diff --git a/src/modules/lolschedule/README.md b/src/modules/lolschedule/README.md index 51b5692..80e6719 100644 --- a/src/modules/lolschedule/README.md +++ b/src/modules/lolschedule/README.md @@ -7,7 +7,7 @@ LoL esports match schedule via the **lolesports.com** esports-api (the data feed | Command | Description | |---|---| | `/lolschedule_today` | Today's matches (ICT), grouped by league. Scores for played + live, times for upcoming. | -| `/lolschedule_week` | Next 7 days, grouped by day → league. | +| `/lolschedule_week` | Next 7 days, grouped by league → day. | | `/lolschedule_subscribe` | Opt the current chat into the daily 08:00 ICT digest. | | `/lolschedule_unsubscribe` | Stop receiving the digest. | @@ -63,7 +63,7 @@ Cache-first with KV. Key is `matches:{fromIso}:{toIso}`. ## Grouping - `/lolschedule_today` — one section per league (header + match lines). -- `/lolschedule_week` — one section per ICT day; within each day, leagues are sub-grouped. +- `/lolschedule_week` — one section per league; within each league, matches are sub-grouped by ICT day (italic date headers). - League ordering follows `LEAGUE_ORDER` in `format.js` (worlds / msi / first_stand first, then LCK / LPL / LEC / LCS, then the rest). ## Subscribers diff --git a/src/modules/lolschedule/format.js b/src/modules/lolschedule/format.js index 5004d18..c6252cd 100644 --- a/src/modules/lolschedule/format.js +++ b/src/modules/lolschedule/format.js @@ -151,7 +151,9 @@ export function renderToday(events, day) { } /** - * Render week reply — grouped by ICT day → league. + * Render week reply — grouped by league → ICT day. The league ordering is + * determined by LEAGUE_ORDER; within each league, days appear chronologically + * and each day section lists its matches. * * @param {ScheduleEvent[]} events * @param {Date} from @@ -164,24 +166,25 @@ export function renderWeek(events, from, to) { const header = `LoL — ${fromLbl} → ${toLbl} (ICT)`; if (events.length === 0) return `${header}\nNo matches this week.`; - /** @type {Map} */ - const days = new Map(); - for (const event of events) { - const d = new Date(event.startTime); - const key = ictDayKey(d); - let g = days.get(key); - if (!g) { - g = { label: formatIctDayLabel(d), events: [] }; - days.set(key, g); + const leagueBlocks = groupByLeague(events).map((league) => { + /** @type {Map} */ + const daysInLeague = new Map(); + for (const event of league.events) { + const d = new Date(event.startTime); + const key = ictDayKey(d); + let g = daysInLeague.get(key); + if (!g) { + g = { label: formatIctDayLabel(d), lines: [] }; + daysInLeague.set(key, g); + } + g.lines.push(formatEventLine(event)); } - g.events.push(event); - } + const daySections = [...daysInLeague.keys()].sort().map((key) => { + const day = daysInLeague.get(key); + return `${escapeHtml(day.label)}\n${day.lines.join("\n")}`; + }); + return `${escapeHtml(league.name)}\n${daySections.join("\n")}`; + }); - const dayBlocks = []; - for (const key of [...days.keys()].sort()) { - const day = days.get(key); - const leagueSections = groupByLeague(day.events).map(renderLeagueSection); - dayBlocks.push(`${escapeHtml(day.label)}\n${leagueSections.join("\n")}`); - } - return `${header}\n\n${dayBlocks.join("\n\n")}`; + return `${header}\n\n${leagueBlocks.join("\n\n")}`; } diff --git a/tests/modules/lolschedule/format.test.js b/tests/modules/lolschedule/format.test.js index 89cec78..238a512 100644 --- a/tests/modules/lolschedule/format.test.js +++ b/tests/modules/lolschedule/format.test.js @@ -153,12 +153,8 @@ describe("renderWeek", () => { ).toContain("No matches this week."); }); - it("nests leagues under each ICT day in chronological order", () => { + it("groups by league first then nests days chronologically", () => { const events = [ - evt({ - startTime: "2026-04-21T09:00:00Z", - league: { name: "LCK", slug: "lck" }, - }), evt({ startTime: "2026-04-22T09:00:00Z", league: { name: "LPL", slug: "lpl" }, @@ -167,15 +163,22 @@ describe("renderWeek", () => { startTime: "2026-04-22T11:00:00Z", league: { name: "LCK", slug: "lck" }, }), + evt({ + startTime: "2026-04-21T09:00:00Z", + league: { name: "LCK", slug: "lck" }, + }), ]; const out = renderWeek( events, new Date("2026-04-21T00:00:00Z"), new Date("2026-04-28T00:00:00Z"), ); - expect(out.indexOf("Apr 21")).toBeLessThan(out.indexOf("Apr 22")); - // Apr 22: LCK section appears before LPL (per LEAGUE_ORDER) - const apr22Block = out.split("Apr 22")[1] || ""; - expect(apr22Block.indexOf("LCK")).toBeLessThan(apr22Block.indexOf("LPL")); + // Top-level: LCK section appears before LPL (per LEAGUE_ORDER). + expect(out.indexOf("LCK")).toBeLessThan(out.indexOf("LPL")); + // Inside LCK: Apr 21 before Apr 22. + const lckBlock = out.split("LCK")[1].split("LPL")[0]; + expect(lckBlock.indexOf("Apr 21")).toBeLessThan(lckBlock.indexOf("Apr 22")); + // Day labels use italic inside league sections (distinct from league bold). + expect(lckBlock).toContain("Tue Apr 21"); }); });