feat(signature): make host configurable and add WS reconnect
- New optional env SIGNATURE_HOST overrides the hardcoded 192.168.10.5:2967 (defaults preserved for back-compat). - WebSocket now reconnects with exponential backoff (1s, 2s, 4s ... capped at 30s) on close/error. Previously a dropped signature server connection silently disabled signature playback until the bot was restarted.
This commit is contained in:
@@ -4,6 +4,7 @@ import WebSocket from "ws";
|
|||||||
import ffmpeg from "fluent-ffmpeg";
|
import ffmpeg from "fluent-ffmpeg";
|
||||||
import { existsSync, mkdirSync, readdirSync, readFileSync, rmdirSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
|
import { existsSync, mkdirSync, readdirSync, readFileSync, rmdirSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
|
||||||
import { Readable } from "node:stream";
|
import { Readable } from "node:stream";
|
||||||
|
import { Config } from "../utils/Config";
|
||||||
|
|
||||||
interface SignatureItem {
|
interface SignatureItem {
|
||||||
name: string[];
|
name: string[];
|
||||||
@@ -17,10 +18,13 @@ mkdirSync(filePath, { recursive: true });
|
|||||||
|
|
||||||
export class SignatureClient {
|
export class SignatureClient {
|
||||||
private readonly myClientId = "bot-"+Math.random().toString(36).slice(2, 9);
|
private readonly myClientId = "bot-"+Math.random().toString(36).slice(2, 9);
|
||||||
private readonly host = `192.168.10.5:2967`;
|
private readonly host = Config.signatureHost;
|
||||||
private readonly wsUrl = `ws://${this.host}?clientId=${this.myClientId}`;
|
private readonly wsUrl = `ws://${this.host}?clientId=${this.myClientId}`;
|
||||||
private ws = new WebSocket(this.wsUrl);
|
private ws!: WebSocket;
|
||||||
private signatureList: SignatureItem[] = [];
|
private signatureList: SignatureItem[] = [];
|
||||||
|
private reconnectDelayMs = 1000;
|
||||||
|
private readonly reconnectMaxMs = 30_000;
|
||||||
|
private reconnectTimer?: NodeJS.Timeout;
|
||||||
|
|
||||||
get list() {
|
get list() {
|
||||||
return this.signatureList;
|
return this.signatureList;
|
||||||
@@ -36,8 +40,14 @@ export class SignatureClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
this.connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
private connect() {
|
||||||
|
this.ws = new WebSocket(this.wsUrl);
|
||||||
this.ws.on("open", () => {
|
this.ws.on("open", () => {
|
||||||
Logger.ready("[WS] 서버 연결 성공");
|
Logger.ready("[WS] 서버 연결 성공");
|
||||||
|
this.reconnectDelayMs = 1000; // reset backoff on successful connect
|
||||||
const requestMessage = JSON.stringify({ type: "GET_LIST" });
|
const requestMessage = JSON.stringify({ type: "GET_LIST" });
|
||||||
this.ws.send(requestMessage);
|
this.ws.send(requestMessage);
|
||||||
});
|
});
|
||||||
@@ -54,12 +64,27 @@ export class SignatureClient {
|
|||||||
});
|
});
|
||||||
this.ws.on("close", () => {
|
this.ws.on("close", () => {
|
||||||
Logger.info("[WS] 서버 연결 종료");
|
Logger.info("[WS] 서버 연결 종료");
|
||||||
|
this.scheduleReconnect();
|
||||||
});
|
});
|
||||||
this.ws.on("error", (error) => {
|
this.ws.on("error", (error) => {
|
||||||
Logger.error(`[WS] 오류 발생: ${String(error)}`);
|
Logger.error(`[WS] 오류 발생: ${String(error)}`);
|
||||||
|
// 'error' is typically followed by 'close' on the ws lib, but call
|
||||||
|
// out reconnect anyway in case the socket never closes cleanly.
|
||||||
|
try { this.ws.terminate(); } catch {}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private scheduleReconnect() {
|
||||||
|
if (this.reconnectTimer) return; // already scheduled
|
||||||
|
const delay = this.reconnectDelayMs;
|
||||||
|
this.reconnectDelayMs = Math.min(this.reconnectDelayMs * 2, this.reconnectMaxMs);
|
||||||
|
Logger.info(`[WS] ${delay}ms 후 재연결 시도`);
|
||||||
|
this.reconnectTimer = setTimeout(() => {
|
||||||
|
this.reconnectTimer = undefined;
|
||||||
|
this.connect();
|
||||||
|
}, delay);
|
||||||
|
}
|
||||||
|
|
||||||
async changeVolume(buffer: Buffer, volume: number) {
|
async changeVolume(buffer: Buffer, volume: number) {
|
||||||
try {
|
try {
|
||||||
const inputStream = Readable.from(buffer);
|
const inputStream = Readable.from(buffer);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export const Config = {
|
|||||||
nid_ses: process.env.CHZZK_NID_SES?.trim(),
|
nid_ses: process.env.CHZZK_NID_SES?.trim(),
|
||||||
},
|
},
|
||||||
_ttsPath: process.env.TTSPATH?.trim(),
|
_ttsPath: process.env.TTSPATH?.trim(),
|
||||||
|
_signatureHost: process.env.SIGNATURE_HOST?.trim() || "192.168.10.5:2967",
|
||||||
debug: process.env.DEBUG?.trim()?.toLocaleLowerCase() === "true",
|
debug: process.env.DEBUG?.trim()?.toLocaleLowerCase() === "true",
|
||||||
dev: process.env.DEV?.trim()?.toLocaleLowerCase() === "true",
|
dev: process.env.DEV?.trim()?.toLocaleLowerCase() === "true",
|
||||||
replaceObj: { ...def_replaceObj, ...JSON.parse(process.env.REPLACETEXT?.trim() || "[{}]")[0] },
|
replaceObj: { ...def_replaceObj, ...JSON.parse(process.env.REPLACETEXT?.trim() || "[{}]")[0] },
|
||||||
@@ -48,6 +49,9 @@ export const Config = {
|
|||||||
get ttsPath() {
|
get ttsPath() {
|
||||||
if (!this._ttsPath) throw new ReferenceError("TTSPATH is missing");
|
if (!this._ttsPath) throw new ReferenceError("TTSPATH is missing");
|
||||||
return this._ttsPath;
|
return this._ttsPath;
|
||||||
|
},
|
||||||
|
get signatureHost() {
|
||||||
|
return this._signatureHost;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user