最終更新 3 weeks ago

nuno revised this gist 3 weeks ago. Go to revision

1 file changed, 108 insertions

gistfile1.txt(file created)

@@ -0,0 +1,108 @@
1 + /**
2 + * Scheduler — plugin-based cron job manager.
3 + *
4 + * Drop a file exporting `job: ScheduledJob` in this directory
5 + * and it will be automatically scheduled.
6 + *
7 + * Slot-specific jobs (poll open/lock/close) are registered separately
8 + * via Scheduler.scheduleSlots() since they depend on runtime config.
9 + */
10 +
11 + import cron from "node-cron";
12 + import { Client, TextChannel } from "discord.js";
13 + import { ScheduledJob } from "./types";
14 + import { Config } from "@systems/config";
15 + import { TGSlot } from "@types";
16 +
17 + // Import all jobs
18 + import { job as weeklyReset } from "@scheduler/weekly-reset";
19 + import { job as midnightCleanup } from "@scheduler/midnight-cleanup";
20 + import { job as midnightSnapshot } from "@scheduler/midnight-snapshot";
21 +
22 + const STATIC_JOBS: ScheduledJob[] = [
23 + weeklyReset,
24 + midnightCleanup,
25 + midnightSnapshot
26 + ];
27 +
28 + type PollCallback = (slot: TGSlot) => Promise<void>;
29 + type LockCallback = (slot: TGSlot) => Promise<void>;
30 + type CloseCallback = (slot: TGSlot) => Promise<void>;
31 +
32 + let _tasks: cron.ScheduledTask[] = [];
33 +
34 + function stopAll(): void {
35 + _tasks.forEach((t) => t.stop());
36 + _tasks = [];
37 + }
38 +
39 + export const Scheduler = {
40 + /**
41 + * Schedule all jobs — static crons + slot-based polls.
42 + */
43 + schedule(
44 + client: Client,
45 + onPollOpen: PollCallback,
46 + onPollLock: LockCallback,
47 + onPollClose: CloseCallback,
48 + ): void {
49 + stopAll();
50 +
51 + const tz = process.env.TZ ?? "Etc/GMT-2";
52 + const slots = Config.get({ section: "poll", key: "slots" }).filter((s) => s.active);
53 +
54 + console.log(`[Scheduler] Weekly reset scheduled: "0 0 * * 1" in ${tz}`);
55 +
56 + // Static jobs
57 + for (const job of STATIC_JOBS) {
58 + _tasks.push(cron.schedule(
59 + job.cron,
60 + () => job.run(client),
61 + { timezone: job.timezone ?? tz }
62 + ));
63 + console.log(`[Scheduler] Registered: ${job.name} (${job.cron})`);
64 + }
65 +
66 + // Slot-based jobs
67 + for (const slot of slots) {
68 + const [openHour, openMin] = slot.pollOpens.split(":").map(Number);
69 +
70 + _tasks.push(cron.schedule(
71 + `${openMin} ${openHour} * * *`,
72 + () => onPollOpen(slot),
73 + { timezone: tz }
74 + ));
75 +
76 + _tasks.push(cron.schedule(
77 + `0 ${slot.tgHour} * * *`,
78 + () => onPollLock(slot),
79 + { timezone: tz }
80 + ));
81 +
82 + const closeMinTotal = slot.tgHour * 60 + slot.closesAfter;
83 + const closeHour = Math.floor(closeMinTotal / 60) % 24;
84 + const closeMin = closeMinTotal % 60;
85 + _tasks.push(cron.schedule(
86 + `${closeMin} ${closeHour} * * *`,
87 + () => onPollClose(slot),
88 + { timezone: tz }
89 + ));
90 + }
91 +
92 + console.log(`[Scheduler] ${STATIC_JOBS.length} static jobs + ${slots.length} slot(s) scheduled.`);
93 + },
94 +
95 + reschedule(
96 + client: Client,
97 + onPollOpen: PollCallback,
98 + onPollLock: LockCallback,
99 + onPollClose: CloseCallback,
100 + ): void {
101 + Scheduler.schedule(client, onPollOpen, onPollLock, onPollClose);
102 + },
103 +
104 + stop(): void {
105 + stopAll();
106 + console.log(`[Scheduler] All jobs stopped.`);
107 + },
108 + };
Newer Older