/** * TG — thin dispatcher for TG-related operations. * Orchestrates Score, Attendance, WRank, and Bringer. * * Usage: * import { TG } from "@systems/tg"; * * TG.currentWeek() * TG.resetWeek() * TG.getAttendance({ historyKey }) * TG.getScore({ character, slot }) * TG.getWeeklySummary({ character }) */ import { Nation, Character, UserKey, SlotHour, TGScore } from "@types"; import { WRank } from "@systems/wrank"; import { Bringer } from "@systems/bringer"; import { Score, WeeklySummary } from "@systems/score"; import { Attendance } from "@systems/attendance"; import { Config } from "@systems/config"; import { TGKey } from "@systems/tg-key"; export const TG = { // ── Week ────────────────────────────────────────────────────────────────── currentWeek() { return WRank.currentWeek(); }, /** * Reset to a new week — carry Bringer forward, clear ranks. * Called every Monday 00:00 by the Scheduler. */ resetWeek(): void { const now = new Date(); const newWeekKey = WRank.weekKey(now); const prevWeekKey = WRank.weekKey(new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000)); // Access internal data via WRank const prevWeek = WRank.weekFromKey(prevWeekKey); const newWeek = WRank.currentWeek(); // ensures new week exists if (prevWeek) { const goal = Config.get({ section: "wrank", key: "goal" }); for (const nation of [Nation.Capella, Nation.Procyon]) { const entries = prevWeek.entries[nation]; const rank1 = entries.find((e) => e.currentRank === 1); // Rank 1 with >= goal TGs becomes Bringer — no exceptions // Officers use Bringer.override() for manual adjustments if (rank1 && rank1.tgCount >= goal) { newWeek.bringer[nation] = rank1.characterName; } else { newWeek.bringer[nation] = null; } // Overrides do NOT carry forward — each week starts clean delete (newWeek.bringer as any)[`${nation}Override`]; } } WRank.save(); console.log(`[TG] Week reset to ${newWeekKey}. Bringer:`, newWeek.bringer); }, // ── Attendance ──────────────────────────────────────────────────────────── getAttendance({ historyKey, nation }: { historyKey: TGKey; nation?: Nation; }): UserKey[] { const players = Attendance.players(historyKey); if (!nation) return players; // Filter by nation requires character lookup — caller handles if needed return players; }, allSubmitted(historyKey: TGKey): boolean { return Attendance.allSubmitted(historyKey); }, // ── Score ───────────────────────────────────────────────────────────────── getScore({ character, slot, date }: { character: Character; slot: SlotHour; date?: string; }): TGScore | null { return Score.get({ character, slot }); }, getWeeklySummary({ character }: { character: Character }): WeeklySummary { return Score.getWeeklySummary({ character }); }, submitScore(params: Parameters[0]): void { Score.submit(params); }, // ── Bringer ─────────────────────────────────────────────────────────────── getBringer({ nation }: { nation: Nation }): string | null { return Bringer.get({ nation }); }, // ── W.Rank ──────────────────────────────────────────────────────────────── getEntry(character: Character) { return WRank.entry(character.name, character.nation); }, };