Commit Graph
6 Commits
Author SHA1 Message Date
Leonid PershinandClaude Opus 5 d4880e757e Fix voice join against LiveKit 0.13+ and tidy up command messages
revoice.js reads `room.isConnected()`, but @livekit/rtc-node turned that
into a getter, so every join threw "this.room.isConnected is not a
function" before the bot ever reached the channel. The player no longer
touches that getter: readiness comes from the connection's own join /
roomfetched events and is cleared when it reports the offline state.

Also delete the invoking chat message once a command is recognised
(DELETE_COMMAND_MESSAGES, on by default) so channels stay readable;
failures are non-fatal since it needs ManageMessages.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 23:34:39 +03:00
Leonid PershinandClaude Opus 5 2b999bf2a0 Document YouTube cookie auth and warn when the file is unusable
Cookies were already wired up but only mentioned in passing, and the
non-obvious parts were undocumented: yt-dlp has no username/password
support for YouTube, it rewrites the cookie file to persist rotated
cookies (so a read-only file expires early), and the export has to
happen in a private window that is logged out before closing.

Startup now reports whether the cookie file is usable, missing, or
read-only, and YTDLP_EXTRACTOR_ARGS is passed through for the cases
where YouTube blocks a server IP outright.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 23:26:48 +03:00
Leonid PershinandClaude Opus 5 1ac99b4a10 Document cloning into /opt as root-owned directory
Cloning straight into /opt fails with "Permission denied", and a clone
left owned by root would also make the ./data bind mount unwritable for
the container's node user.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 23:18:56 +03:00
Leonid PershinandClaude Opus 5 62d5291cd9 Run the panel as its own compose project behind the host Caddy
The instance's internal Caddy is only for Stoat itself, so the panel no
longer goes through it: the bot ships its own compose project, publishes
3005 on loopback, and the host's external Caddy gives it a domain.

extra_hosts pins the instance domain to host-gateway, so the bot reaches
the API, gateway and LiveKit through the external proxy with a valid
certificate instead of depending on router NAT loopback. The old
in-project layout stays available as a fallback example, together with a
step-by-step guide for when voice fails to connect.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 23:16:04 +03:00
Leonid PershinandClaude Opus 5 a9b7ccdd16 Add music bot for self-hosted Stoat with web control panel
Plays audio into Stoat voice channels over LiveKit and exposes the same
player through both chat commands and a browser panel, so the two never
drift apart: everything routes through a single MusicManager.

- core: per-server GuildPlayer (queue, loop, shuffle, seek, volume,
  idle auto-leave) driving revoice.js/@livekit/rtc-node and ffmpeg
- sources: yt-dlp for YouTube/SoundCloud, direct media URLs and internet
  radio, optional local library with path-traversal guards
- bot: 18 chat commands with aliases, plus !panel one-time login links
- api: Fastify REST + WebSocket, sessions authenticated against the
  instance's own /auth/session/login (TOTP supported), permissions
  re-checked against Stoat membership and roles on every request
- web: React panel with search, queue editing, seek and volume
- deploy: Dockerfile, compose.override.yml and Caddyfile snippets for
  dropping the service into an existing /opt/stoat stack

Verified with npm run typecheck, both builds, and scripts/smoke-api.mjs
(9 API checks). Voice playback itself needs a live instance to test.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 23:12:43 +03:00
mrleo1nid d9d0e9f6bf Initial commit 2026-09-08 19:37:00 +00:00