Daily Bluesky bot for AT Mot. Invites players and congratulates yesterday's solvers.
1import { puzzleNumberFor, type Lang } from './config.js';
2import { yesterdayCounts } from './counts.js';
3import { composePost, postMarker } from './compose.js';
4import { createBotSession, alreadyPosted, publishPost } from './post.js';
5
6export interface Env {
7 ATMOT_BOT_IDENTIFIER: string;
8 ATMOT_BOT_APP_PASSWORD: string;
9 /** When "1", compose and log the post but do not authenticate or publish. */
10 DRY_RUN?: string;
11}
12
13/**
14 * Map the firing cron expression to a language. Keyed on the exact expressions
15 * in wrangler.toml (EN 00:10 UTC, FR 00:11 UTC); an unrecognized cron throws so
16 * a trigger misconfiguration fails loudly instead of silently posting English.
17 */
18const CRON_LANG: Record<string, Lang> = {
19 '10 0 * * *': 'en',
20 '11 0 * * *': 'fr',
21};
22
23export function langForCron(cron: string): Lang {
24 const lang = CRON_LANG[cron];
25 if (!lang) throw new Error(`[atmot-bot] unmapped cron expression: ${JSON.stringify(cron)}`);
26 return lang;
27}
28
29export default {
30 async scheduled(controller: ScheduledController, env: Env, _ctx: ExecutionContext): Promise<void> {
31 const lang = langForCron(controller.cron);
32 const todayN = puzzleNumberFor();
33 const yesterdayN = todayN - 1;
34
35 try {
36 const yesterday = yesterdayN >= 1 ? await yesterdayCounts(lang, yesterdayN) : null;
37 const post = composePost({ lang, todayN, yesterday });
38
39 if (env.DRY_RUN === '1') {
40 console.log(`[atmot-bot] DRY_RUN ${lang} #${todayN}:\n${post.text}`);
41 return;
42 }
43
44 const session = await createBotSession(env.ATMOT_BOT_IDENTIFIER, env.ATMOT_BOT_APP_PASSWORD);
45 if (await alreadyPosted(session, postMarker(lang, todayN))) {
46 console.log(`[atmot-bot] ${lang} #${todayN} already posted; skipping`);
47 return;
48 }
49 const uri = await publishPost(session, post);
50 console.log(`[atmot-bot] posted ${lang} #${todayN}: ${uri}`);
51 } catch (err) {
52 console.error(`[atmot-bot] ${lang} #${todayN} failed:`, err);
53 throw err; // surface the failure to Cloudflare's cron logs
54 }
55 },
56};