/** * Scheduler — plugin-based cron job manager. * * Drop a file exporting `job: ScheduledJob` in this directory * and it will be automatically scheduled. * * Slot-specific jobs (poll open/lock/close) are registered separately * via Scheduler.scheduleSlots() since they depend on runtime config. */ import cron from "node-cron"; import { Client, TextChannel } from "discord.js"; import { ScheduledJob } from "./types"; import { Config } from "@systems/config"; import { TGSlot } from "@types"; // Import all jobs import { job as weeklyReset } from "@scheduler/weekly-reset"; import { job as midnightCleanup } from "@scheduler/midnight-cleanup"; import { job as midnightSnapshot } from "@scheduler/midnight-snapshot"; const STATIC_JOBS: ScheduledJob[] = [ weeklyReset, midnightCleanup, midnightSnapshot ]; type PollCallback = (slot: TGSlot) => Promise; type LockCallback = (slot: TGSlot) => Promise; type CloseCallback = (slot: TGSlot) => Promise; let _tasks: cron.ScheduledTask[] = []; function stopAll(): void { _tasks.forEach((t) => t.stop()); _tasks = []; } export const Scheduler = { /** * Schedule all jobs — static crons + slot-based polls. */ schedule( client: Client, onPollOpen: PollCallback, onPollLock: LockCallback, onPollClose: CloseCallback, ): void { stopAll(); const tz = process.env.TZ ?? "Etc/GMT-2"; const slots = Config.get({ section: "poll", key: "slots" }).filter((s) => s.active); console.log(`[Scheduler] Weekly reset scheduled: "0 0 * * 1" in ${tz}`); // Static jobs for (const job of STATIC_JOBS) { _tasks.push(cron.schedule( job.cron, () => job.run(client), { timezone: job.timezone ?? tz } )); console.log(`[Scheduler] Registered: ${job.name} (${job.cron})`); } // Slot-based jobs for (const slot of slots) { const [openHour, openMin] = slot.pollOpens.split(":").map(Number); _tasks.push(cron.schedule( `${openMin} ${openHour} * * *`, () => onPollOpen(slot), { timezone: tz } )); _tasks.push(cron.schedule( `0 ${slot.tgHour} * * *`, () => onPollLock(slot), { timezone: tz } )); const closeMinTotal = slot.tgHour * 60 + slot.closesAfter; const closeHour = Math.floor(closeMinTotal / 60) % 24; const closeMin = closeMinTotal % 60; _tasks.push(cron.schedule( `${closeMin} ${closeHour} * * *`, () => onPollClose(slot), { timezone: tz } )); } console.log(`[Scheduler] ${STATIC_JOBS.length} static jobs + ${slots.length} slot(s) scheduled.`); }, reschedule( client: Client, onPollOpen: PollCallback, onPollLock: LockCallback, onPollClose: CloseCallback, ): void { Scheduler.schedule(client, onPollOpen, onPollLock, onPollClose); }, stop(): void { stopAll(); console.log(`[Scheduler] All jobs stopped.`); }, };