From 2e21185dc0df48b63da90485b0d0ec20b19bbf0c Mon Sep 17 00:00:00 2001 From: javis-bot Date: Sat, 13 Jun 2026 21:33:50 +0900 Subject: [PATCH] fix(bot): never broadcast on the conversation's account (it deafened STT) Critical regression: in single-account userbot mode the broadcast auto-start logged in a SECOND session of the conversation account and called joinVoice, which the streaming lib always sends with self_deaf:true. Voice state is per-account, so this deafened the CONVERSATION session too and the bot silently stopped hearing the user (STT receive broke). The Go-Live cannot connect on a shared account anyway. start() now refuses early (no joinVoice, no deafen) when there is no dedicated DISCORD_STREAM_TOKEN and no normal-bot token, protecting the conversation. A dedicated stream account re-enables the broadcast. Co-Authored-By: Claude Opus 4.7 --- bot/src/stream/selfbot.test.ts | 4 ++++ bot/src/stream/selfbot.ts | 14 +++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/bot/src/stream/selfbot.test.ts b/bot/src/stream/selfbot.test.ts index e33fa83..94efd3a 100644 --- a/bot/src/stream/selfbot.test.ts +++ b/bot/src/stream/selfbot.test.ts @@ -46,6 +46,8 @@ test("a self-ended stream tears down the capture pipeline (no ffmpeg leak)", asy const { SelfbotStreamer } = await import("./selfbot.ts"); const s = new SelfbotStreamer({ selfbotToken: "token", + streamToken: "stream-token", // dedicated broadcast account -> broadcast allowed + botToken: "", vncDisplay: ":1", vncResolution: "1920x1080", vncFramerate: 60, @@ -104,6 +106,8 @@ test("a Go-Live that never connects is reported as failed, not live, and is torn const { SelfbotStreamer } = await import("./selfbot.ts"); const s = new SelfbotStreamer({ selfbotToken: "token", + streamToken: "stream-token", // dedicated broadcast account -> broadcast allowed + botToken: "", vncDisplay: ":1", vncResolution: "1920x1080", vncFramerate: 60, diff --git a/bot/src/stream/selfbot.ts b/bot/src/stream/selfbot.ts index ec5e387..b7a68b2 100644 --- a/bot/src/stream/selfbot.ts +++ b/bot/src/stream/selfbot.ts @@ -141,12 +141,20 @@ export class SelfbotStreamer implements ScreenStreamer { } const dedicatedStreamAccount = !!this.config.streamToken && this.config.streamToken !== this.config.selfbotToken; + // CRITICAL: in single-account userbot mode (no normal-bot token, no dedicated + // stream token) we must NOT start the broadcast. The broadcaster logs in as a + // SECOND session of the conversation account and joins voice with + // self_deaf:true (the streaming lib always deafens itself). Voice state is + // per-account, so that deafens the CONVERSATION session too and the bot stops + // hearing the user (STT breaks). The Go-Live can't connect on a shared account + // anyway, so refuse early — protecting the conversation is more important than + // a broadcast that cannot work. A dedicated DISCORD_STREAM_TOKEN removes this. if (!dedicatedStreamAccount && !this.config.botToken) { console.warn( - "[selfbot] ⚠️ 방송과 대화가 같은 계정(DISCORD_SELFBOT_TOKEN)을 사용합니다. " + - "유저봇 모드에서는 음성 채널을 동시에 점유할 수 없어 Go-Live가 연결되지 않습니다. " + - "방송 전용 DISCORD_STREAM_TOKEN(별도 버너 계정)을 설정하세요.", + "[selfbot] ⚠️ 방송 전용 DISCORD_STREAM_TOKEN 미설정 — 단일 계정 유저봇에서는 방송이 대화 음성을 " + + "끊으므로(self_deaf) 시작하지 않습니다. 별도 버너 계정 토큰(DISCORD_STREAM_TOKEN)을 설정하세요.", ); + return "방송 전용 계정(DISCORD_STREAM_TOKEN)이 없어 대화 음성 보호를 위해 방송을 시작하지 않았습니다."; } if (!ctx.voiceChannelId) { return "셀프봇 송출은 음성 채널 안에서 호출해야 합니다.";