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>
- 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>
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>
- 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>