import { Client, GatewayIntentBits, Events } from 'discord.js' import { createPrismaClient } from '@afis/db' import { loadEnv } from './env.js' import { writeHeartbeat } from './heartbeat.js' /** * Rollen-Abgleich-Worker. Haengt am Discord-Gateway und haelt die * Guild-Mitgliedschaft in Postgres aktuell, damit die Web-App nie live bei * Discord anfragen muss. Siehe docs/02-architektur.md. * * Phase 0 baut das Geruest: Verbindung, Lebenszeichen, sauberes Herunterfahren. * Der eigentliche Abgleich (Ereignisse, Voll-Abgleich, pg_notify) kommt in Phase 1. */ async function main(): Promise { const env = loadEnv() const db = createPrismaClient(env.databaseUrl) const client = new Client({ // Server Members Intent muss im Developer Portal aktiviert sein, sonst // liefert der Bot keine Rollen. Siehe docs/03-auth-und-rechte.md. intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers], }) let gatewayReady = false client.once(Events.ClientReady, (ready) => { gatewayReady = true console.log(`[worker] am Gateway als ${ready.user.tag}`) }) client.on(Events.ShardDisconnect, (_event, shardId) => { gatewayReady = false console.warn(`[worker] Verbindung zu Shard ${shardId} verloren`) }) client.on(Events.ShardResume, () => { gatewayReady = true console.log('[worker] Verbindung wiederhergestellt') }) client.on(Events.Error, (error) => { console.error('[worker] Gateway-Fehler', error) }) const heartbeat = setInterval(() => { void writeHeartbeat( db, gatewayReady ? 'up' : 'degraded', gatewayReady ? undefined : 'Gateway nicht verbunden', ).catch((error: unknown) => console.error('[worker] Lebenszeichen fehlgeschlagen', error)) }, env.heartbeatIntervalMs) async function shutdown(signal: string): Promise { console.log(`[worker] ${signal}, fahre herunter`) clearInterval(heartbeat) await writeHeartbeat(db, 'down', 'planmäßig beendet').catch(() => undefined) await client.destroy() await db.$disconnect() process.exit(0) } process.on('SIGTERM', () => void shutdown('SIGTERM')) process.on('SIGINT', () => void shutdown('SIGINT')) await writeHeartbeat(db, 'degraded', 'Start läuft') await client.login(env.discordBotToken) } main().catch((error: unknown) => { console.error('[worker] Start fehlgeschlagen', error) process.exit(1) })