diff --git a/lib/api/api.ts b/lib/api/api.ts index bc632a4..233d1a1 100644 --- a/lib/api/api.ts +++ b/lib/api/api.ts @@ -7,7 +7,11 @@ import { makeDailyWebhookPostHandler, makeManualDailyWebhookPostHandler, } from "./dailies.ts"; -import { makeSeasonGetHandler, makeSeasonsGetHandler } from "./seasons.ts"; +import { + makeSeasonGetHandler, + makeSeasonsGetHandler, + makeSeasonTxtGetHandler, +} from "./seasons.ts"; /** * makeAPIRouter creates a router which handles requests on the @@ -67,6 +71,10 @@ export function makeAPIRouter( new URLPattern({ pathname: "/seasons" }), withCORS(makeSeasonsGetHandler(leaderboardClient)), ) + .get( + new URLPattern({ pathname: "/seasons/:season_id.txt" }), + withCORS(makeSeasonTxtGetHandler(leaderboardClient)), + ) .get( new URLPattern({ pathname: "/seasons/:season_id" }), withCORS(makeSeasonGetHandler(leaderboardClient)), diff --git a/lib/api/seasons.ts b/lib/api/seasons.ts index d20e503..bc1947b 100644 --- a/lib/api/seasons.ts +++ b/lib/api/seasons.ts @@ -1,3 +1,4 @@ +import * as api from "lc-dailies/lib/api/mod.ts"; import * as leaderboard from "lc-dailies/lib/leaderboard/mod.ts"; import * as router from "lc-dailies/lib/router/mod.ts"; @@ -33,10 +34,57 @@ export function makeSeasonGetHandler( return new Response("Missing season ID", { status: 400 }); } - const season = - await (seasonID === "latest" - ? leaderboardClient.getLatestSeason() - : leaderboardClient.getSeason(seasonID)); + const season = await getSeasonByIDOrLatest(leaderboardClient, seasonID); return new Response(JSON.stringify(season)); }; } + +/** + * makeSeasonTxtGetHandler makes a handler that returns a plaintext + * representation of a season. + */ +export function makeSeasonTxtGetHandler( + leaderboardClient: leaderboard.LeaderboardClient, +) { + /** + * handleGetSeasonTxt handles GET requests to the season.txt endpoint. + */ + return async function handleGetSeasonTxt( + request: router.RouterRequest, + ): Promise { + const seasonID = request.params["season_id"]; + if (!seasonID) { + return new Response("Missing season ID", { status: 400 }); + } + + const season = await getSeasonByIDOrLatest(leaderboardClient, seasonID); + if (!season) { + return new Response("Season not found", { status: 404 }); + } + + const text = leaderboard.formatScores(season); + return new Response(text, { + headers: { "Content-Type": "text/plain" }, + }); + }; +} + +async function getSeasonByIDOrLatest( + leaderboardClient: leaderboard.LeaderboardClient, + seasonID: string | undefined, +): Promise { + const season = !seasonID || seasonID === "latest" + ? await leaderboardClient.getLatestSeason() + : await leaderboardClient.getSeason(seasonID); + if (season && !season.scores) { + season.scores = await leaderboard.calculateScores( + leaderboard.makeDefaultCalculateScoresOptions( + season.players, + season.questions, + season.submissions, + ), + ); + } + + return season; +}