diff --git a/src/modules/lolschedule/README.md b/src/modules/lolschedule/README.md index d487a4a..51b5692 100644 --- a/src/modules/lolschedule/README.md +++ b/src/modules/lolschedule/README.md @@ -66,13 +66,18 @@ Cache-first with KV. Key is `matches:{fromIso}:{toIso}`. - `/lolschedule_week` — one section per ICT day; within each day, leagues are sub-grouped. - League ordering follows `LEAGUE_ORDER` in `format.js` (worlds / msi / first_stand first, then LCK / LPL / LEC / LCS, then the rest). +## Subscribers + +`/lolschedule_subscribe` adds `ctx.chat.id` to the module's `subscribers` KV key (JSON array). `/lolschedule_unsubscribe` removes it. Both are idempotent and reply with the new state. The daily cron reads this list; empty list means the cron skips cleanly. + ## Time zone -All rendering is in **ICT (UTC+7)**. `startTime` is UTC ISO; day boundaries for the `/lol_today` and `/lol_week` windows are anchored to ICT midnight. +All rendering is in **ICT (UTC+7)**. `startTime` is UTC ISO; day boundaries for the `/lolschedule_today` and `/lolschedule_week` windows are anchored to ICT midnight. ## Files - `index.js` — module contract - `api-client.js` — getSchedule client with pagination + cache - `format.js` — pure renderers (`formatEventLine`, `renderToday`, `renderWeek`) -- `handlers.js` — grammY command handlers + ICT day-boundary helpers +- `handlers.js` — grammY command handlers, ICT day boundaries, cron fan-out +- `subscribers.js` — KV-backed add/list/remove for the daily-push list diff --git a/src/modules/lolschedule/format.js b/src/modules/lolschedule/format.js index 68248a5..5004d18 100644 --- a/src/modules/lolschedule/format.js +++ b/src/modules/lolschedule/format.js @@ -73,38 +73,34 @@ function teamLabel(team) { } /** - * Render one event line (no leading newline). By default the league name is - * omitted because events are rendered under a league header; pass - * `{ showLeague: true }` to include it for flat lists. + * Render one event line (no leading newline). The league name is omitted + * because events render under a league header. * * @param {ScheduleEvent} event - * @param {{ showLeague?: boolean }} [opts] * @returns {string} escaped HTML */ -export function formatEventLine(event, { showLeague = false } = {}) { +export function formatEventLine(event) { const teams = event?.match?.teams || []; const t1Label = escapeHtml(teamLabel(teams[0])); const t2Label = escapeHtml(teamLabel(teams[1])); const block = event?.blockName ? ` (${escapeHtml(event.blockName)})` : ""; const bestOf = event?.match?.strategy?.count; const bo = bestOf ? ` · Bo${bestOf}` : ""; - const leagueSuffix = - showLeague && event?.league?.name ? ` · ${escapeHtml(event.league.name)}` : ""; if (event?.state === "completed") { const w1 = teams[0]?.result?.gameWins ?? 0; const w2 = teams[1]?.result?.gameWins ?? 0; const l = teams[0]?.result?.outcome === "win" ? `${t1Label}` : t1Label; const r = teams[1]?.result?.outcome === "win" ? `${t2Label}` : t2Label; - return `✅ ${l} ${w1}–${w2} ${r}${bo}${leagueSuffix}${block}`; + return `✅ ${l} ${w1}–${w2} ${r}${bo}${block}`; } if (event?.state === "inProgress") { const w1 = teams[0]?.result?.gameWins ?? 0; const w2 = teams[1]?.result?.gameWins ?? 0; - return `🔴 LIVE ${t1Label} ${w1}–${w2} ${t2Label}${bo}${leagueSuffix}${block}`; + return `🔴 LIVE ${t1Label} ${w1}–${w2} ${t2Label}${bo}${block}`; } const time = formatIctTime(new Date(event.startTime)); - return `🕒 ${time} ${t1Label} vs ${t2Label}${bo}${leagueSuffix}${block}`; + return `🕒 ${time} ${t1Label} vs ${t2Label}${bo}${block}`; } /** diff --git a/src/modules/lolschedule/handlers.js b/src/modules/lolschedule/handlers.js index d4577a1..4a6c63c 100644 --- a/src/modules/lolschedule/handlers.js +++ b/src/modules/lolschedule/handlers.js @@ -3,8 +3,8 @@ * * Day boundaries are defined in ICT (UTC+7). Data comes from lolesports.com * via a cache-first fetcher; no cron pre-warm is needed because the upstream - * API is rate-limit friendly. A daily cron also pushes today's schedule to a - * configured chat. + * API is rate-limit friendly. A daily cron fans today's schedule out to every + * chat opted in via /lolschedule_subscribe. */ import { getEventsCached } from "./api-client.js"; diff --git a/tests/modules/lolschedule/format.test.js b/tests/modules/lolschedule/format.test.js index ffc81f0..89cec78 100644 --- a/tests/modules/lolschedule/format.test.js +++ b/tests/modules/lolschedule/format.test.js @@ -62,14 +62,10 @@ describe("formatIctTime / formatIctDayLabel", () => { }); describe("formatEventLine", () => { - it("omits league name by default (renders under league header)", () => { + it("omits league name — renders under a league header", () => { expect(formatEventLine(evt())).not.toContain("LCK"); }); - it("includes league name when showLeague is true", () => { - expect(formatEventLine(evt(), { showLeague: true })).toContain("LCK"); - }); - it("renders completed with bolded winner + score", () => { const line = formatEventLine(completed); expect(line.startsWith("✅")).toBe(true);