Nightly: Partymode-Status global per SSE /api/events Broadcast; Panic/Stop/Start senden Status an alle Clients

This commit is contained in:
vibe-bot 2025-08-09 23:20:13 +02:00
parent 23b90b5923
commit 21b4e9bd0c
3 changed files with 51 additions and 1 deletions

View file

@ -152,6 +152,14 @@ const guildAudioState = new Map<string, GuildAudioState>();
// Partymode: serverseitige Steuerung (global pro Guild)
const partyTimers = new Map<string, NodeJS.Timeout>();
const partyActive = new Set<string>();
// SSE-Klienten für Broadcasts (z.B. Partymode Status)
const sseClients = new Set<Response>();
function sseBroadcast(payload: any) {
const data = `data: ${JSON.stringify(payload)}\n\n`;
for (const res of sseClients) {
try { res.write(data); } catch {}
}
}
async function playFilePath(guildId: string, channelId: string, filePath: string, volume?: number, relativeKey?: string): Promise<void> {
const guild = client.guilds.cache.get(guildId);
@ -871,6 +879,7 @@ app.post('/api/stop', (req: Request, res: Response) => {
if (t) clearTimeout(t);
partyTimers.delete(guildId);
partyActive.delete(guildId);
sseBroadcast({ type: 'party', guildId, active: false });
} catch {}
return res.json({ ok: true });
} catch (e: any) {
@ -921,6 +930,8 @@ function schedulePartyPlayback(guildId: string, channelId: string) {
// Start: sofort spielen und nächste planen
partyActive.add(guildId);
void loop();
// Broadcast Status
sseBroadcast({ type: 'party', guildId, active: true, channelId });
}
app.post('/api/party/start', async (req: Request, res: Response) => {
@ -946,6 +957,7 @@ app.post('/api/party/stop', (req: Request, res: Response) => {
const t = partyTimers.get(id); if (t) clearTimeout(t);
partyTimers.delete(id);
partyActive.delete(id);
sseBroadcast({ type: 'party', guildId: id, active: false });
return res.json({ ok: true });
} catch (e: any) {
console.error('party/stop error', e);
@ -953,6 +965,23 @@ app.post('/api/party/stop', (req: Request, res: Response) => {
}
});
// Server-Sent Events Endpoint
app.get('/api/events', (req: Request, res: Response) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
res.flushHeaders?.();
// Snapshot senden
try { res.write(`data: ${JSON.stringify({ type: 'snapshot', party: Array.from(partyActive) })}\n\n`); } catch {}
sseClients.add(res);
req.on('close', () => {
sseClients.delete(res);
try { res.end(); } catch {}
});
});
// Static Frontend ausliefern (Vite build)
const webDistPath = path.resolve(__dirname, '../../web/dist');
if (fs.existsSync(webDistPath)) {