diff --git a/.hermes/plans/2026-09-02_selfbot-manual-video-watch-spec.md b/.hermes/plans/2026-09-02_selfbot-manual-video-watch-spec.md new file mode 100644 index 00000000..f7efc84f --- /dev/null +++ b/.hermes/plans/2026-09-02_selfbot-manual-video-watch-spec.md @@ -0,0 +1,81 @@ +# Spec: Selfbot-Viable Video Capture — manual screen-share watch command (Phase D) + +Status: PLANNED (not yet built) +Date: 2026-09-02 +Author: Hermes +Related: `.hermes/plans/2026-08-31_video-receive-phaseC-spec.md` (auto-receive, superseded +for selfbot), `gmw-ops/references/selfbot-presence-detection-limits.md`, +`gmw-ops/references/discord-voice-fork-video-receive.md` + +## TL;DR — the decisive finding (verified live 2026-09-02) + +User insists on keeping the **selfbot** (no bot-token migration). Live diagnostics prove +a selfbot CANNOT auto-detect other members' camera/share because: +- It never receives `VOICE_STATE_UPDATE` for other members (only its own). +- `guild.members.fetch()` → 403, `GET /channels/{id}/voice-states` → 404. +- No `GUILD_CREATE`, no `READY.broadcaster_user_ids` presence. +- `scanExistingStreamers` + `handleVoiceStateUpdate` (the only two `startStreamWatch` + triggers) are therefore both **dead on a selfbot**. +- No manual watch command exists today, so even on-demand capture is impossible. + +→ The ONE selfbot-viable path is a **manual, operator-initiated STREAM_WATCH** on a + member known to be screen-sharing. Gateway op 20 (STREAM_WATCH) is **NOT gated on + bot-vs-user**; the DAVE handshake to Ready+MLS was already verified live in earlier + sessions. The receive/mux/segment/upload pipeline (`streamWatchReceiver.ts`) is already + built and only lacks a real streamer to produce its first `.mp4`. + +Camera-of-others is NOT viable on a selfbot even with `unknown-ssrc` fallback: +`@discordjs/voice` `parsePacket` calls `daveSession.decrypt(packet, userId)` keyed per +REAL userId (vendor fork dist/index.js:2143), so a fake id selects no MLS decryptor → +garbage, not H264. (The uncommitted `unknown-ssrc` change was reverted this session.) + +Selfbot CAN capture the OWNER's own video (its own VOICE_STATE_UPDATE + fork op12 +videoSSRC are attributable), but `videoRecorder.ts` hard-skips its own id — parameterized +self-capture is a follow-up, not the default. + +## Goal + +Add a **manual watch command** so an operator can say "record 's screen share" +and the gateway `startStreamWatch`s that member → DAVE watch → per-burst `.mp4` segments +(mirroring voice silence split) → upload → DB `voice_recordings` → dashboard `