fix(bot): auto-start broadcast on voice join in userbot mode
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>
This commit is contained in:
70
bot/src/broadcast.test.ts
Normal file
70
bot/src/broadcast.test.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { test, expect } from "bun:test";
|
||||
|
||||
// broadcast.ts pulls in the runtime `config`, which requires DISCORD_GUILD_ID.
|
||||
// Set it before the dynamic import so the module loads without a real .env.
|
||||
process.env.DISCORD_GUILD_ID ||= "test-guild";
|
||||
const { coupleBroadcast } = await import("./broadcast.ts");
|
||||
|
||||
/** Minimal in-memory ScreenStreamer that records start/stop calls. */
|
||||
function fakeStreamer(active = false) {
|
||||
const calls: string[] = [];
|
||||
let on = active;
|
||||
return {
|
||||
calls,
|
||||
kind: "selfbot" as const,
|
||||
isActive: () => on,
|
||||
async start() {
|
||||
calls.push("start");
|
||||
on = true;
|
||||
return "ok";
|
||||
},
|
||||
async stop() {
|
||||
calls.push("stop");
|
||||
on = false;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const ctx = { guildId: "g", voiceChannelId: "c" };
|
||||
|
||||
test("auto-starts the broadcast when joining with the stream idle", async () => {
|
||||
const s = fakeStreamer(false);
|
||||
const session: any = {};
|
||||
await coupleBroadcast(session, s as any, ctx);
|
||||
expect(s.calls).toEqual(["start"]);
|
||||
expect(session.getBroadcasting()).toBe(true);
|
||||
});
|
||||
|
||||
test("does not restart a broadcast that is already live", async () => {
|
||||
const s = fakeStreamer(true);
|
||||
const session: any = {};
|
||||
await coupleBroadcast(session, s as any, ctx);
|
||||
expect(s.calls).toEqual([]);
|
||||
expect(session.getBroadcasting()).toBe(true);
|
||||
});
|
||||
|
||||
test("the brain can toggle the stream by voice via onBroadcastAction", async () => {
|
||||
const s = fakeStreamer(false);
|
||||
const session: any = {};
|
||||
await coupleBroadcast(session, s as any, ctx); // auto-start fires
|
||||
await session.onBroadcastAction("start"); // already live -> no-op
|
||||
await session.onBroadcastAction("stop"); // stops
|
||||
expect(session.getBroadcasting()).toBe(false);
|
||||
await session.onBroadcastAction("start"); // restarts
|
||||
expect(session.getBroadcasting()).toBe(true);
|
||||
expect(s.calls).toEqual(["start", "stop", "start"]);
|
||||
});
|
||||
|
||||
test("an auto-start failure is swallowed but the toggle hooks are still wired", async () => {
|
||||
const s: any = {
|
||||
isActive: () => false,
|
||||
async start() {
|
||||
throw new Error("boom");
|
||||
},
|
||||
async stop() {},
|
||||
};
|
||||
const session: any = {};
|
||||
await coupleBroadcast(session, s, ctx);
|
||||
expect(typeof session.getBroadcasting).toBe("function");
|
||||
expect(typeof session.onBroadcastAction).toBe("function");
|
||||
});
|
||||
Reference in New Issue
Block a user