mirror of
https://github.com/ZHANGTIANYAO1/teamspeak-music-bot.git
synced 2026-10-02 04:52:50 +08:00
- Global uncaughtException/unhandledRejection handlers in index.ts - FFmpeg stdout/stderr stream error handlers in player.ts - HTTP server and WebSocket server error handlers in server.ts - Safe WebSocket broadcast with try-catch in websocket.ts - Catch async errors from textMessage handler in instance.ts - Reset voiceFramesSent counter on reconnect in client.ts https://claude.ai/code/session_01EjpEsC2GCsvwbu4n3XC8EE
115 lines
2.9 KiB
TypeScript
115 lines
2.9 KiB
TypeScript
import { WebSocketServer, WebSocket } from "ws";
|
|
import type { BotManager } from "../bot/manager.js";
|
|
import type { BotInstance } from "../bot/instance.js";
|
|
import type { Logger } from "../logger.js";
|
|
|
|
export function setupWebSocket(
|
|
wss: WebSocketServer,
|
|
botManager: BotManager,
|
|
logger: Logger
|
|
): () => void {
|
|
const clients = new Set<WebSocket>();
|
|
|
|
/** Track which bots already have listeners attached */
|
|
const attachedBots = new Map<string, {
|
|
stateChange: () => void;
|
|
connected: () => void;
|
|
disconnected: () => void;
|
|
}>();
|
|
|
|
wss.on("connection", (ws) => {
|
|
clients.add(ws);
|
|
logger.debug("WebSocket client connected");
|
|
|
|
const bots = botManager.getAllBots().map((b) => b.getStatus());
|
|
ws.send(JSON.stringify({ type: "init", bots }));
|
|
|
|
ws.on("close", () => {
|
|
clients.delete(ws);
|
|
logger.debug("WebSocket client disconnected");
|
|
});
|
|
|
|
ws.on("error", (err) => {
|
|
logger.error({ err }, "WebSocket error");
|
|
clients.delete(ws);
|
|
});
|
|
});
|
|
|
|
const broadcast = (data: object) => {
|
|
const message = JSON.stringify(data);
|
|
for (const client of clients) {
|
|
if (client.readyState === WebSocket.OPEN) {
|
|
try {
|
|
client.send(message);
|
|
} catch {
|
|
clients.delete(client);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
function attachBotListener(bot: BotInstance): void {
|
|
if (attachedBots.has(bot.id)) return;
|
|
|
|
const onStateChange = () => {
|
|
broadcast({
|
|
type: "stateChange",
|
|
botId: bot.id,
|
|
status: bot.getStatus(),
|
|
queue: bot.getQueue(),
|
|
});
|
|
};
|
|
|
|
const onConnected = () => {
|
|
broadcast({
|
|
type: "botConnected",
|
|
botId: bot.id,
|
|
status: bot.getStatus(),
|
|
});
|
|
};
|
|
|
|
const onDisconnected = () => {
|
|
broadcast({
|
|
type: "botDisconnected",
|
|
botId: bot.id,
|
|
status: bot.getStatus(),
|
|
});
|
|
};
|
|
|
|
bot.on("stateChange", onStateChange);
|
|
bot.on("connected", onConnected);
|
|
bot.on("disconnected", onDisconnected);
|
|
|
|
attachedBots.set(bot.id, {
|
|
stateChange: onStateChange,
|
|
connected: onConnected,
|
|
disconnected: onDisconnected,
|
|
});
|
|
}
|
|
|
|
/** Attach listeners for any new bots that don't have them yet */
|
|
function ensureAllBotsAttached(): void {
|
|
for (const bot of botManager.getAllBots()) {
|
|
attachBotListener(bot);
|
|
}
|
|
}
|
|
|
|
// Check for newly added bots periodically
|
|
const intervalId = setInterval(ensureAllBotsAttached, 5000);
|
|
ensureAllBotsAttached();
|
|
|
|
return () => {
|
|
clearInterval(intervalId);
|
|
// Clean up named listeners
|
|
for (const bot of botManager.getAllBots()) {
|
|
const listeners = attachedBots.get(bot.id);
|
|
if (listeners) {
|
|
bot.removeListener("stateChange", listeners.stateChange);
|
|
bot.removeListener("connected", listeners.connected);
|
|
bot.removeListener("disconnected", listeners.disconnected);
|
|
}
|
|
}
|
|
attachedBots.clear();
|
|
};
|
|
}
|