Proven approach: the conversation (hear+speak) runs on @discordjs/voice; the
Go-Live broadcast is a SEPARATE stream connection created on the SAME selfbot
session (exactly like a real Discord client), so ONE account hears, speaks, AND
broadcasts — no second login, no self_deaf, no voice conflict.
- voice.ts captures its own voice session_id (adapter wrap) and exposes
getSharedSession() {client, guildId, channelId, sessionId, botId}.
- broadcast.ts threads it into the StreamContext.
- selfbot.ts: when a shared session is present, build the Streamer on the
conversation client and create the stream on its session_id (no login/joinVoice/
humanPause); teardown only stops the stream (never leaves voice/destroys the
shared client). Falls back to the dedicated-account path otherwise.
Verified live: Go-Live connected in ~7s while the conversation voice stayed
ready, and the broadcast was visible in Discord — all on one account.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Makes the Go-Live auto-start verifiable from the container logs (the reviewer
flagged that a successful broadcast left no log record), alongside the existing
loud failure log.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Address review of the noise/broadcast fixes:
- STT now refuses to run Whisper on non-speech. transcribe() runs the Silero
VAD (bundled with faster-whisper, no new dep) BEFORE the model, so noise or a
brief loud blip with no real speech never reaches STT and can't be
hallucinated into a transcript. The no_speech_prob/avg_logprob post-filter
stays as a second line of defence (a clap the VAD lets through is still killed
by Whisper's own no_speech_prob). VAD is env-tunable (VAD_THRESHOLD,
VAD_MIN_SPEECH_MS, VAD_ENABLED) and fail-open so a VAD error never swallows a
real utterance. Validated on real audio: synthesised Korean speech passes;
silence, a 50ms blip and white noise are rejected.
- Broadcast auto-start no longer blocks the voice join and no longer silently
swallows failures: wiring is synchronous, the Go-Live start runs in the
background with a bounded retry and a loud final-failure log.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The "couple broadcast to voice" feature only wired auto-start into the
normal-bot join handler. Userbot mode (the only mode that can actually Go
Live) was added later with Go-Live deferred to a "stage 2" that never
landed, so the running deployment had no path to start a broadcast.
Extract the coupling into a shared bot/src/broadcast.ts (auto-start on
join, report live state to the brain each turn, voice toggle) and wire it
into both index.ts and userbot.ts so both modes behave identically. Add a
behavioural test for the auto-start + toggle contract.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>