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 <noreply@anthropic.com>
isActive() was a local flag set at start() time, and start() fired playStream
without awaiting the actual stream connection, so 'broadcasting' was reported
even when nothing reached Discord (auto-start log + ffmpeg/NVENC running are not
proof of transmission).
start() now waits for the streaming library's real readiness signal (the stream
connection's WebRTC reaching "connected") before declaring live. On timeout it
logs a compact connection-state diagnostic, tears the local ffmpeg pipeline down
immediately, and returns an explicit failure. isActive() reports the real live
state. Timeout is config-driven (STREAM_READY_TIMEOUT_MS, default 25s). Adds a
test for the timeout/teardown path and updates the existing leak-teardown test.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The Go-Live broadcast looked badly choppy: video and scrolling stuttered while
the cursor stayed smooth. Root cause is TigerVNC: it only refreshes its
framebuffer while a VNC client is attached, but the broadcast reads that
framebuffer with x11grab (not as a VNC client). With no viewer attached the
captured screen idled at ~1.5 fps (measured 3/30 distinct frames); the cursor
looked smooth only because x11grab overlays the live cursor on every frame.
- Add a headless RFB keepalive (vnc-keepalive.ts) that stays connected for the
life of the stream and requests incremental framebuffer updates at the stream
framerate. SelfbotStreamer starts it on broadcast start and tears it down on
stop/self-end. Measured 3/30 -> 57/60 distinct frames at 60 fps. Fail-open;
authenticates with VNC_PASSWORD or the ~/.config/tigervnc/passwd file.
- Fix a resource leak: when the Go-Live ended on its own, only the active flag
was cleared, leaving the x11grab->nvenc ffmpeg running forever (pinning a CPU
core while no media was transmitted, with only the gateway TCP left and no UDP
media). The self-end path now tears down capture, keepalive and voice like
stop() does.
- Tests for both paths (self-end teardown; keepalive DES auth, port mapping,
password resolution). Add @types/bun so bun:test typechecks; document the
keepalive and recommended Chrome flags in README and .env.example.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>