Last active 1 month ago

tg.ts Raw
1/**
2 * TG — thin dispatcher for TG-related operations.
3 * Orchestrates Score, Attendance, WRank, and Bringer.
4 *
5 * Usage:
6 * import { TG } from "@systems/tg";
7 *
8 * TG.currentWeek()
9 * TG.resetWeek()
10 * TG.getAttendance({ historyKey })
11 * TG.getScore({ character, slot })
12 * TG.getWeeklySummary({ character })
13 */
14
15 import { Nation, Character, UserKey, SlotHour, TGScore } from "@types";
16 import { WRank } from "@systems/wrank";
17 import { Bringer } from "@systems/bringer";
18 import { Score, WeeklySummary } from "@systems/score";
19 import { Attendance } from "@systems/attendance";
20 import { Config } from "@systems/config";
21 import { TGKey } from "@systems/tg-key";
22
23 export const TG = {
24 // ── Week ──────────────────────────────────────────────────────────────────
25
26 currentWeek() {
27 return WRank.currentWeek();
28 },
29
30 /**
31 * Reset to a new week — carry Bringer forward, clear ranks.
32 * Called every Monday 00:00 by the Scheduler.
33 */
34 resetWeek(): void {
35 const now = new Date();
36 const newWeekKey = WRank.weekKey(now);
37 const prevWeekKey = WRank.weekKey(new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000));
38
39 // Access internal data via WRank
40 const prevWeek = WRank.weekFromKey(prevWeekKey);
41 const newWeek = WRank.currentWeek(); // ensures new week exists
42
43 if (prevWeek) {
44 const goal = Config.get({ section: "wrank", key: "goal" });
45 for (const nation of [Nation.Capella, Nation.Procyon]) {
46 const entries = prevWeek.entries[nation];
47 const rank1 = entries.find((e) => e.currentRank === 1);
48
49 // Rank 1 with >= goal TGs becomes Bringer — no exceptions
50 // Officers use Bringer.override() for manual adjustments
51 if (rank1 && rank1.tgCount >= goal) {
52 newWeek.bringer[nation] = rank1.characterName;
53 } else {
54 newWeek.bringer[nation] = null;
55 }
56
57 // Overrides do NOT carry forward — each week starts clean
58 delete (newWeek.bringer as any)[`${nation}Override`];
59 }
60 }
61
62 WRank.save();
63 console.log(`[TG] Week reset to ${newWeekKey}. Bringer:`, newWeek.bringer);
64 },
65
66 // ── Attendance ────────────────────────────────────────────────────────────
67
68 getAttendance({ historyKey, nation }: {
69 historyKey: TGKey;
70 nation?: Nation;
71 }): UserKey[] {
72 const players = Attendance.players(historyKey);
73 if (!nation) return players;
74 // Filter by nation requires character lookup — caller handles if needed
75 return players;
76 },
77
78 allSubmitted(historyKey: TGKey): boolean {
79 return Attendance.allSubmitted(historyKey);
80 },
81
82 // ── Score ─────────────────────────────────────────────────────────────────
83
84 getScore({ character, slot, date }: {
85 character: Character;
86 slot: SlotHour;
87 date?: string;
88 }): TGScore | null {
89 return Score.get({ character, slot });
90 },
91
92 getWeeklySummary({ character }: { character: Character }): WeeklySummary {
93 return Score.getWeeklySummary({ character });
94 },
95
96 submitScore(params: Parameters<typeof Score.submit>[0]): void {
97 Score.submit(params);
98 },
99
100 // ── Bringer ───────────────────────────────────────────────────────────────
101
102 getBringer({ nation }: { nation: Nation }): string | null {
103 return Bringer.get({ nation });
104 },
105
106 // ── W.Rank ────────────────────────────────────────────────────────────────
107
108 getEntry(character: Character) {
109 return WRank.entry(character.name, character.nation);
110 },
111 };