Commit Graph

9 Commits

Author SHA1 Message Date
EJClaw
d3cf4e01b5 feat(bot+dashboard): bot info, server/voice-channel picker, participants, speaker
Adds a dashboard<->bot control plane (bot pushes state + polls commands, keeping
the bot's single outbound-HTTP direction):
- New bot_control.BotControl + endpoints: GET /api/bot/state, /api/bot/commands;
  POST /api/bot/report, /api/bot/select.
- Dashboard header bar: bot identity/connection, server dropdown (top "없음"),
  voice-channel dropdown (top "없음"), and live participant list.
- Turns record who spoke (Turn.speaker, via X-User-Name on the voice-turn POST).
- dave/bot.mjs: reports identity/guilds/voice-channels/members, polls join/leave
  commands and joins dynamically, and sends the speaker's display name.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-08-22 11:43:48 +09:00
EJClaw
2c4c9ad82c feat(bot): reach host voice-server + throttle DAVE decrypt error bursts
- Log voice connection errors instead of letting them surface silently.
- Collapse bursty repeated receive-stream errors (DAVE E2EE group-transition
  decrypt failures) into one line + a suppressed-count summary, so a member
  joining/leaving no longer floods the log.

Deploy: voice-server now runs as the wsai-voice.service user unit (STT+Claude
Haiku brain+TTS on GPU); the bot container reaches it via
host.docker.internal:8787.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-08-21 23:22:21 +09:00
EJClaw
95e2d4472b fix(bot): tolerate slow DAVE join + never crash on a receive-stream error
- Raise the voice-Ready ceiling 20s->40s: the DAVE/MLS handshake cycles
  signalling<->connecting and can take ~25s, so 20s spuriously failed the join.
- Handle AudioReceiveStream 'error' (e.g. a DAVE decrypt/UDP GenericFailure on
  one packet): log and free the speaker slot instead of letting the unhandled
  'error' event crash the whole bot process.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-08-18 22:05:57 +09:00
EJClaw
0f94245d5f feat(voice): real Discord listen+speak loop (STT->echo->TTS)
The bot (dave/bot.mjs) previously only joined the channel and counted audio
frames — it never fed STT or spoke back. Wire the real loop:

- Node bot: buffer each speaker's Opus->PCM utterance until AfterSilence,
  wrap as WAV, POST to the Python voice-turn endpoint, then play the returned
  reply wav into the channel via an AudioPlayer (ffmpeg->Opus). Skips its own
  audio, dedupes overlapping subscriptions, and ignores sub-0.35s noise.
- Python: new `python -m wsai --voice-server` serves /api/voice-turn — decode
  the uploaded utterance, GPU faster-whisper STT, produce a reply (echo of what
  was heard for now), GPU MeloTTS synth, return the reply wav (recognised/reply
  text ride along as X-Heard/X-Reply headers). Both engines pre-warmed; turns
  show in the dashboard feed. MeloTTS.synth() extracted for direct wav reuse.

Echo mode verifies listening+speaking+GPU recognition entirely in Discord; the
Claude brain is the next slice. Verified the endpoint round-trip: utterance wav
-> correct Korean X-Heard/X-Reply + a WAVE reply on device=cuda. 12 tests pass,
node --check clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-08-18 21:59:01 +09:00
EJClaw
27e449f9f1 feat(discord): migrate voice join from selfbot to official bot (bot.mjs)
Replace the user-token selfbot voice path with an official Discord bot using
discord.js 14 + @discordjs/voice 0.19. The bot logs in with the stored testbot
token, joins the target voice channel, passes the DAVE/MLS E2EE handshake, and
receives per-user Opus audio via VoiceReceiver (the STT input path). ToS-safe.

Live-verified: bot joined guild "사지방" / channel "일반" and reached Ready.
Selfbot (gate.mjs/join.mjs) kept only for the deferred screenshare-video track,
which official bots cannot receive. Docs updated (README/PLAN); M1 done on bot path.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-08-16 18:32:44 +09:00
EJClaw
5327f8ec7c feat(dave): join with mic on (self_mute=false) so bot can speak and hear
Previously the selfbot joined self_mute=true (listen-only). Un-mute so both
directions are enabled at the Discord voice-state level for live testing.
2026-08-15 21:01:37 +09:00
EJClaw
cfad568029 fix(dave): hard RUN_MS time-box ceiling armed at startup, not on ready gate
The RUN_MS auto-leave was only armed inside announceReady(), which requires a
fully successful join (DAVE/MLS op29/op30 -> mlsReady). When the E2EE handshake
stalls after op26 key_package, announceReady never fires, so the "time-boxed"
selfbot ran unbounded in a live channel. Arm RUN_MS at process startup instead,
independent of handshake state, and make leaveAndExit idempotent so the ceiling,
ready timer, and signal handlers can't double-fire.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-08-15 19:28:14 +09:00
EJClaw
9f3a57d8a0 feat(discord): M1 persistent selfbot voice joiner + record confirmed build decisions
join.mjs joins the target voice channel over the proven DAVE handshake and stays
connected, mapping SPEAKING->ssrc and tallying incoming RTP (foundation for M2
audio decrypt). README records GPU=on, shared OAuth brain, natural-but-<=1s TTS,
Discord-voice STT input, and the M1..M6 milestones.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-08-11 20:51:24 +09:00
EJClaw
375e1e5539 feat(dave): pass arbiter gate 1 — selfbot joins DAVE/MLS E2EE voice group
Fail-fast checkpoint the arbiter mandated before committing to option A
(protocol-level selfbot stream receive). dave/gate.mjs proves, live against
Discord, that a user token can pass the voice DAVE/MLS handshake:

- voice GW v8 IDENTIFY with max_dave_protocol_version=1 -> NO close 4017
- dave_protocol_version=1 negotiated (E2EE active on the channel)
- @snazzah/davey drives full MLS membership: op25 external_sender -> op26
  key_package -> op27 proposals -> op28 commit_welcome -> op29 announce_commit
  -> MLS session ready=true, stable 5s, voicePrivacyCode derived

Confirms option A is viable: the selfbot can join the E2EE group as a full
member, which is the prerequisite for receiving+decrypting the video RTP.
PLAN.md updated with gate result, exact binary framing, and next A steps.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-08-09 21:33:55 +09:00