From 43004fc44ae7386cd5d85a46951a838e581d20f4 Mon Sep 17 00:00:00 2001 From: saopig1 Date: Mon, 30 Mar 2026 21:50:04 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=81=9C=E6=AD=A2=E6=9C=BA=E5=99=A8?= =?UTF-8?q?=E4=BA=BA=E5=90=8EUI=E6=B6=88=E5=A4=B1=20&=20UDP=20send=20error?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E5=88=B7=E5=B1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - botDisconnected事件改为更新状态而非删除,停止机器人后正确显示离线状态 - 节流UDP send error日志,2秒内重复错误只输出一条汇总 Co-Authored-By: Claude Opus 4.6 (1M context) --- src/ts-protocol/client.ts | 24 +++++++++++++++++++++++- src/web/websocket.ts | 6 +++++- web/src/composables/useWebSocket.ts | 16 +++++++++++++++- 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/ts-protocol/client.ts b/src/ts-protocol/client.ts index 761dd27..312eddf 100644 --- a/src/ts-protocol/client.ts +++ b/src/ts-protocol/client.ts @@ -56,11 +56,33 @@ export class TS3Client extends EventEmitter { const addr = `${this.options.host}:${this.options.port}`; this.logger.info({ addr }, "Connecting to TeamSpeak server (full client protocol)"); + // Throttle repeated "udp send error" warnings (fires every 20ms during playback if UDP breaks) + let udpErrorCount = 0; + let udpErrorTimer: ReturnType | null = null; + const throttledWarn = (msg: string, ...args: unknown[]) => { + if (typeof msg === "string" && msg.includes("udp send error")) { + udpErrorCount++; + if (udpErrorCount === 1) { + this.logger.warn(msg); + // After 2 seconds, log a summary and reset + udpErrorTimer = setTimeout(() => { + if (udpErrorCount > 1) { + this.logger.warn(`udp send error (repeated ${udpErrorCount} times, connection may be lost)`); + } + udpErrorCount = 0; + udpErrorTimer = null; + }, 2000); + } + return; + } + this.logger.warn(msg); + }; + this.client = new TS3FullClient(this.identity, addr, this.options.nickname, { logger: { debug: (msg) => this.logger.debug(msg), info: (msg) => this.logger.info(msg), - warn: (msg) => this.logger.warn(msg), + warn: throttledWarn, error: (msg) => this.logger.error(msg), }, }); diff --git a/src/web/websocket.ts b/src/web/websocket.ts index ec00d5e..916f820 100644 --- a/src/web/websocket.ts +++ b/src/web/websocket.ts @@ -65,7 +65,11 @@ export function setupWebSocket( }; const onDisconnected = () => { - broadcast({ type: "botDisconnected", botId: bot.id }); + broadcast({ + type: "botDisconnected", + botId: bot.id, + status: bot.getStatus(), + }); }; bot.on("stateChange", onStateChange); diff --git a/web/src/composables/useWebSocket.ts b/web/src/composables/useWebSocket.ts index b800363..b343cc2 100644 --- a/web/src/composables/useWebSocket.ts +++ b/web/src/composables/useWebSocket.ts @@ -40,7 +40,21 @@ export function useWebSocket() { store.updateBotStatus(data.botId, data.status); break; case 'botDisconnected': - store.removeBotStatus(data.botId); + // Bot disconnected from TS3 but still exists — update status, don't remove + if (data.status) { + store.updateBotStatus(data.botId, data.status); + } else { + const existing = store.bots.find((b) => b.id === data.botId); + if (existing) { + store.updateBotStatus(data.botId, { + ...existing, + connected: false, + playing: false, + paused: false, + currentSong: null, + }); + } + } break; } };