Last active 4 weeks ago

Revision a7412440ab4b912110234eb78205503887016c2a

scores.ts Raw
1import { TGScore, Nation, ClassKey } from "../types";
2import { cfg } from "./config";
3import { upsertScore, todayString } from "./history";
4import { recordScore } from "./wrank";
5
6// Normalize a slot string to a 24h integer hour
7// Accepts: "20", "8", "8pm", "20:00", "midnight", "midday", "noon"
8export function normalizeSlot(input: string): number | null {
9 const s = input.trim().toLowerCase();
10 if (s === "midnight") return 0;
11 if (s === "midday" || s === "noon") return 12;
12
13 const pmMatch = s.match(/^(\d{1,2})pm$/);
14 if (pmMatch) {
15 const h = parseInt(pmMatch[1]);
16 return h === 12 ? 12 : h + 12;
17 }
18
19 const amMatch = s.match(/^(\d{1,2})am$/);
20 if (amMatch) {
21 const h = parseInt(amMatch[1]);
22 return h === 12 ? 0 : h;
23 }
24
25 const colonMatch = s.match(/^(\d{1,2}):\d{2}$/);
26 if (colonMatch) return parseInt(colonMatch[1]);
27
28 const numMatch = s.match(/^(\d{1,2})$/);
29 if (numMatch) return parseInt(numMatch[1]);
30
31 return null;
32}
33
34// Detect which slot a submission belongs to based on current time
35export function detectSlot(): number | null {
36 const slots = cfg("slots").filter((s) => s.active);
37 const windowMs = cfg("scoreWindowHours") * 60 * 60 * 1000;
38 const durationMs = cfg("tgDurationMinutes") * 60 * 1000;
39 const now = Date.now();
40
41 for (const slot of slots) {
42 const today = new Date();
43 const tgTime = new Date(today);
44 tgTime.setHours(slot.tgHour, 0, 0, 0);
45 const closeTime = tgTime.getTime() + durationMs;
46 const windowEnd = closeTime + windowMs;
47
48 if (now >= closeTime && now <= windowEnd) {
49 return slot.tgHour;
50 }
51 }
52 return null;
53}
54
55export interface ScoreSubmission {
56 userKey: string; // owner's key (score goes here)
57 playedBy?: string; // borrower's key if different from owner
58 characterName: string;
59 cls: ClassKey;
60 nation: Nation;
61 pts: number;
62 k?: number;
63 d?: number;
64 slot: number;
65 date?: string;
66 atk?: number;
67 def?: number;
68 heal?: number;
69 submittedByOfficer: boolean;
70}
71
72export function submitScore(sub: ScoreSubmission): void {
73 const date = sub.date ?? todayString();
74 const historyKey = `${date}-${String(sub.slot).padStart(2, "0")}`;
75
76 const score: TGScore = {
77 userKey: sub.userKey,
78 playedBy: sub.playedBy,
79 characterName: sub.characterName,
80 class: sub.cls,
81 nation: sub.nation,
82 pts: sub.pts,
83 k: sub.k,
84 d: sub.d,
85 atk: sub.atk,
86 def: sub.def,
87 heal: sub.heal,
88 submittedAt: new Date().toISOString(),
89 slot: sub.slot,
90 date,
91 submittedByOfficer: sub.submittedByOfficer,
92 };
93
94 upsertScore(score);
95 recordScore(sub.userKey, sub.characterName, sub.cls, sub.nation, sub.pts, historyKey);
96}
97