export interface BotConfig { token: string; guildId: string; appUrl: string; opsChannelId: string | null; announceChannelId: string | null; staffRoleIds: string[]; sideOpsChannelId: string | null; communityZeusRoleId: string | null; attendancePollMs: number; notificationPollMs: number; evaluationPollMs: number; readyReminderPollMs: number; imminentReminderPollMs: number; sideOpsNoticePollMs: number; } const required = (name: string): string => { const value = process.env[name]?.trim(); if (!value) { throw new Error(`[Bot] Missing required environment variable: ${name}`); } return value; }; const optional = (name: string): string | null => { const value = process.env[name]?.trim(); return value ? value : null; }; const pollMs = (name: string, fallback: number): number => { const raw = process.env[name]?.trim(); if (!raw) { return fallback; } const value = Number.parseInt(raw, 10); if (!Number.isInteger(value) || value <= 0) { throw new Error(`[Bot] Invalid ${name}: expected a positive integer, got "${raw}"`); } return value; }; export const botConfig: BotConfig = { token: required("DISCORD_TOKEN"), guildId: required("DISCORD_GUILD_ID"), appUrl: required("APP_URL"), opsChannelId: optional("DISCORD_OPS_CHANNEL_ID"), announceChannelId: optional("DISCORD_ANNOUNCE_CHANNEL_ID"), staffRoleIds: (optional("DISCORD_STAFF_ROLE_IDS") ?? "") .split(",") .map((id) => id.trim()) .filter((id) => id.length > 0), sideOpsChannelId: optional("DISCORD_SIDE_OPS_CHANNEL_ID"), communityZeusRoleId: optional("DISCORD_COMMUNITY_ZEUS_ROLE_ID"), attendancePollMs: pollMs("DISCORD_ATTENDANCE_POLL_MS", 60_000), notificationPollMs: pollMs("DISCORD_NOTIFICATION_POLL_MS", 20_000), evaluationPollMs: pollMs("DISCORD_EVALUATION_POLL_MS", 60_000), readyReminderPollMs: pollMs("DISCORD_READY_REMINDER_POLL_MS", 60_000), imminentReminderPollMs: pollMs("DISCORD_IMMINENT_POLL_MS", 60_000), sideOpsNoticePollMs: pollMs("DISCORD_SIDE_OPS_POLL_MS", 1_800_000), };