Commit graph

21 commits

Author SHA1 Message Date
Daniel
dd71d763cd Feat: Show member count in soundboard channel dropdown
Add voice channel member count (non-bot users) to soundboard
channel API response and display it in the dropdown, matching
the radio plugin's existing behavior.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 20:02:00 +01:00
Daniel
9aefc3d470 Harden state persistence: atomic writes + backup fallback
- Atomic save: write to .tmp file then rename (prevents corruption
  if container is killed mid-write)
- Backup: .bak copy created on successful load, used as fallback
  if main file is corrupted
- Startup log shows loaded keys (verifies favorites survived)

Ensures radio_favorites and radio_volumes survive container
updates, crashes, and forced restarts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 19:58:23 +01:00
Daniel
b821ad9615 Fix: Reuse voice connection when switching radio stations
Instead of destroying and recreating the voice connection on every
station change, now checks if the bot is already in the target channel.
If same channel: only stops ffmpeg/player, spawns new stream, reuses
the existing connection (no reconnect flicker).
If different channel: full disconnect + reconnect as before.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 11:25:42 +01:00
Daniel
63afc55836 Fix: Tile proxy for black globe + radio voicestats modal
- Globe was black because Radio Garden CDN (rg-tiles.b-cdn.net) returns
  403 without Referer: radio.garden header. Added server-side tile proxy
  /api/radio/tile/:z/:x/:y with in-memory cache (max 500 tiles).
- Added radio_voicestats SSE broadcast (every 5s) with voice ping,
  gateway ping, status, channel name, and connected-since timestamp.
- Added clickable "Verbunden" connection indicator in RadioTab bottom
  bar with live ping display and connection details modal (matching
  soundboard's existing modal pattern).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 11:19:19 +01:00
Daniel
b9a9347356 Fix: Separate voice groups so radio + soundboard play in parallel
Each plugin now uses its own @discordjs/voice group:
- Radio: group='radio'
- Soundboard: group='soundboard'

This prevents joinVoiceChannel from one bot overwriting the
other bot's connection. Both bots can now play simultaneously
in the same voice channel. Removed claimVoice system (not needed
with separate bots).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 11:07:31 +01:00
Daniel
3cd9f6f169 Feat: Multi-bot support - separate Discord bot per plugin
Each plugin gets its own Discord client and token:
- DISCORD_TOKEN_JUKEBOX (fallback: DISCORD_TOKEN) → Soundboard
- DISCORD_TOKEN_RADIO → Radio

discord.ts: factory createClient() instead of singleton
plugin.ts: per-plugin context storage via registerPlugin(p, ctx)
index.ts: creates/logins/shutdowns multiple bots independently

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 10:56:22 +01:00
Daniel
1e4ccfb1f1 Fix: Voice claim system - radio stops when soundboard plays
Plugins now claim voice per guild via claimVoice(). When soundboard
plays a sound, radio's cleanup runs automatically (kills ffmpeg,
broadcasts SSE stop event). Fixes stale "now playing" UI on tab switch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 10:21:11 +01:00
Daniel
e0635b30ef Fix: Radio search results empty + texture quality
- Search API: read title/subtitle/url from _source.page (nested)
- Channel click: extract correct ID from URL (last segment)
- Replace earth texture with higher-res 4096x2048 original

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 10:00:44 +01:00
Daniel
d1ae2db00b feat: Radio volume control - server-wide slider synced via SSE
- Server: inlineVolume on AudioResource, POST /api/radio/volume endpoint
- Volume persisted per guild, broadcast via SSE to all clients
- Frontend: volume slider in bottom bar with debounced API calls
- Volume icon changes based on level (muted/low/normal)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 02:10:02 +01:00
Daniel
971fd2c3fc fix: add missing @snazzah/davey (DAVE) and ws dependencies
Jukebox has @snazzah/davey (Discord Audio Video Encryption) and ws
(WebSocket) as dependencies which were missing from the Gaming Hub.
Without davey, voice connections get stuck at 'signalling' because
Discord's voice servers require DAVE negotiation in @discordjs/voice 0.19.
Also removed unused prism-media (covered by @discordjs/opus).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 01:57:56 +01:00
Daniel
20ea13d71f debug: add connection stateChange + error listeners from creation
Listen for state transitions and errors immediately after joinVoiceChannel,
not just after ensureConnectionReady succeeds. This will show if the
connection transitions at all internally or stays stuck at signalling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 01:49:17 +01:00
Daniel
a13765d5b6 fix: correct voice adapter debug wrapper method direction
Previous wrapper intercepted wrong methods (library→adapter vs adapter→library).
Now correctly wraps:
- sendPayload (adapter→gateway): logs op code and return value
- onVoiceServerUpdate/onVoiceStateUpdate (gateway→library): logs incoming events

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 01:41:34 +01:00
Daniel
cc5e21fe1c debug: add voice adapter wrapper to trace gateway communication
Logs sendPayload calls (op code, result), onVoiceServerUpdate
and onVoiceStateUpdate to identify why connection stays at signalling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 01:27:50 +01:00
Daniel
5faf5139ef feat: add comprehensive logging to soundboard voice/play pipeline
- Log voice state transitions (Signalling → Connecting → Ready etc.)
- Log play requests with sound name, guild, channel, file path
- Log connection creation, rejoin attempts, and failures
- Log AudioPlayer state changes and errors
- All prefixed with [Soundboard] for easy filtering

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 01:22:28 +01:00
Daniel
9ac1034e5e fix: initialize voice encryption libs before first connection
- await sodium.ready + nacl preload (same as original jukebox)
- Add generateDependencyReport() for debugging
- Add type declarations for libsodium-wrappers and tweetnacl

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 01:13:46 +01:00
Daniel
901dfe54be feat: Soundboard Plugin + Radio Globe Fixes
- Add Soundboard plugin (full Jukebox port): server + frontend + CSS
- Fix Radio Globe: swap geo coords (API returns [lng,lat] not [lat,lng])
- Fix Radio stations showing "Unbekannt": use item.page.title + fix channel ID regex
- Add DirectMessages + MessageContent intents for DM commands
- Register SoundboardTab in App.tsx with scoped theme/card-size CSS vars

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 00:51:07 +01:00
Daniel
6696fed9d7 fix: remove privileged intents not needed for radio plugin
Only request Guilds + GuildVoiceStates. GuildMembers, GuildPresences
and MessageContent are privileged and require manual portal activation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 23:56:49 +01:00
Daniel
1df780fe60 feat: add ADMIN_PWD and ALLOWED_GUILD_IDS support
- Add ADMIN_PWD and ALLOWED_GUILD_IDS env vars to config
- Extend PluginContext with adminPwd and allowedGuildIds
- Add adminAuth and guildFilter middleware for plugins
- Add /api/admin/login endpoint

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 23:48:23 +01:00
Daniel
1669af1e91 fix: use Express 5 compatible catch-all route syntax
Change SPA fallback from app.get('*') to app.get('/{*splat}')
as Express 5 uses path-to-regexp v8+ which requires named splat.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 23:39:35 +01:00
Daniel
847c963d86 feat: Radio Plugin – 3D Globe mit weltweiten Radiosendern
- Radio Garden API Client (30K+ Orte, Sender-Suche, Stream-URL Auflösung)
- Discord Voice Streaming via ffmpeg (PCM Pipeline)
- Interactive 3D Globe (globe.gl) mit allen Radiosender-Standorten
- Sender-Panel mit Play/Stop/Favoriten
- Live-Suche nach Sendern und Städten
- Now-Playing Bar mit Equalizer-Animation
- Guild/Voice-Channel Auswahl
- SSE Broadcasting für Live-Updates
- Favoriten-System mit Persistenz
- Responsive Design (Mobile/Tablet/Desktop)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 23:23:52 +01:00
Claude Code
ae1c41f0ae Initial commit: Gaming Hub foundation
Plugin-based Discord bot framework with web frontend:
- Core: Discord.js client, SSE broadcast, JSON persistence
- Plugin system: lifecycle hooks (init, onReady, routes, snapshot, destroy)
- Web: React 19 + Vite 6 + TypeScript, tab-based navigation
- Docker: multi-stage build (Node 24, static ffmpeg, yt-dlp)
- GitLab CI: Kaniko with LAN registry caching

Ready for plugin development.
2026-03-05 22:52:13 +01:00