140 lines
4.3 KiB
JavaScript
140 lines
4.3 KiB
JavaScript
/**
|
|
* =============================================================================
|
|
* SpeakerBotClient
|
|
* =============================================================================
|
|
* Author: Rodrigo Emanuel (VortisRD)
|
|
* Created: 2025-08-10
|
|
* Description:
|
|
* WebSocket client for connecting to Speaker.bot, sending TTS (text-to-speech)
|
|
* messages, and handling reconnection logic automatically.
|
|
*
|
|
* Usage Example:
|
|
* -----------------------------------------------------------------------------
|
|
* const speakerBot = new SpeakerBotClient({
|
|
* host: '127.0.0.1',
|
|
* port: 7580,
|
|
* voiceAlias: 'Joanna',
|
|
*
|
|
* onConnect: () => console.log('Connected!'),
|
|
* onDisconnect: () => console.log('Disconnected!'),
|
|
* onError: (err) => console.error(err),
|
|
* onMessage: (msg) => console.log('SpeakerBot says:', msg)
|
|
* });
|
|
*
|
|
* speakerBot.speak("Hello World!");
|
|
* -----------------------------------------------------------------------------
|
|
*
|
|
* Parameters:
|
|
* host - IP or hostname of the Speaker.bot server.
|
|
* port - Port number for the WebSocket connection.
|
|
* reconnectDelay - Time in ms before attempting reconnection.
|
|
* voiceAlias - Preferred TTS voice name (string or null).
|
|
* onConnect - Callback fired when connection is established.
|
|
* onDisconnect - Callback fired when connection is closed.
|
|
* onError - Callback fired on connection error.
|
|
* onMessage - Callback fired when receiving a message from Speaker.bot.
|
|
*
|
|
* Dependencies:
|
|
* None (uses native WebSocket API)
|
|
*
|
|
* Notes:
|
|
* - Messages sent while disconnected are queued and sent after reconnection.
|
|
* - Bad word filter is enabled by default in speak() payload.
|
|
*
|
|
* License:
|
|
* MIT License
|
|
* =============================================================================
|
|
*/
|
|
|
|
|
|
class SpeakerBotClient {
|
|
constructor({
|
|
host = '127.0.0.1',
|
|
port = 7580,
|
|
reconnectDelay = 10000,
|
|
voiceAlias = null,
|
|
onConnect = () => {},
|
|
onDisconnect = () => {},
|
|
onError = () => {},
|
|
onMessage = () => {}
|
|
} = {}) {
|
|
this.host = host;
|
|
this.port = port;
|
|
this.reconnectDelay = reconnectDelay;
|
|
this.voiceAlias = voiceAlias;
|
|
this.onConnect = onConnect;
|
|
this.onDisconnect = onDisconnect;
|
|
this.onError = onError;
|
|
this.onMessage = onMessage;
|
|
|
|
this.ws = null;
|
|
this.queue = [];
|
|
this._manualClose = false; // inicia como false
|
|
|
|
this.connect();
|
|
}
|
|
|
|
get url() {
|
|
return `ws://${this.host}:${this.port}/`;
|
|
}
|
|
|
|
get readyState() {
|
|
return this.ws ? this.ws.readyState : WebSocket.CLOSED;
|
|
}
|
|
|
|
connect() {
|
|
this._manualClose = false; // reset da flag
|
|
console.log('[SpeakerBot] Connecting to Speaker.bot...');
|
|
this.ws = new WebSocket(this.url);
|
|
|
|
this.ws.onopen = () => {
|
|
console.log('[SpeakerBot] Connected to Speaker.bot!');
|
|
this.onConnect();
|
|
while (this.queue.length > 0) {
|
|
this.ws.send(this.queue.shift());
|
|
}
|
|
};
|
|
|
|
this.ws.onmessage = (event) => {
|
|
this.onMessage(event.data);
|
|
};
|
|
|
|
this.ws.onerror = (error) => {
|
|
//console.warn(`[SpeakerBot] Connection error. Reconnecting in ${Math.floor(this.reconnectDelay / 1000)}s...`, error);
|
|
//this.onError(error);
|
|
};
|
|
|
|
this.ws.onclose = () => {
|
|
this.onDisconnect();
|
|
if (!this._manualClose) {
|
|
setTimeout(() => this.connect(), this.reconnectDelay);
|
|
}
|
|
};
|
|
}
|
|
|
|
speak(message) {
|
|
const payload = {
|
|
id: `speak-${Date.now()}`,
|
|
request: 'Speak',
|
|
message: message,
|
|
voice: this.voiceAlias,
|
|
badWordFilter: true
|
|
};
|
|
|
|
const json = JSON.stringify(payload);
|
|
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
this.ws.send(json);
|
|
} else {
|
|
console.warn(`[SpeakerBot] Not connected yet. Queuing message...`);
|
|
this.queue.push(json);
|
|
}
|
|
}
|
|
|
|
disconnect() {
|
|
this._manualClose = true; // flag para evitar reconexão automática
|
|
if (this.ws) {
|
|
this.ws.close();
|
|
}
|
|
}
|
|
}
|