diff --git a/src/app/api/telegram/webhook/__tests__/route.test.ts b/src/app/api/telegram/webhook/__tests__/route.test.ts index b9ccdb6ab..d0dd4e058 100644 --- a/src/app/api/telegram/webhook/__tests__/route.test.ts +++ b/src/app/api/telegram/webhook/__tests__/route.test.ts @@ -566,6 +566,9 @@ describe("Telegram webhook — text-message dispatch", () => { ["ayuda", 213], ["aiuto", 214], ["Pomoc", 215], + ["hulp", 217], + ["도움말", 218], + ["Ajuda", 219], ])("answers the bare help word %s", async (word, updateId) => { const res = await POST( tgRequest({ @@ -602,6 +605,9 @@ describe("Telegram webhook — text-message dispatch", () => { ["Hola", 221], ["ciao", 222], ["cześć", 223], + ["Hoi", 224], + ["안녕하세요", 225], + ["Olá", 226], ])("answers the greeting %s", async (word, updateId) => { const res = await POST( tgRequest({ diff --git a/src/lib/telegram-bare-words.ts b/src/lib/telegram-bare-words.ts new file mode 100644 index 000000000..567df26b0 --- /dev/null +++ b/src/lib/telegram-bare-words.ts @@ -0,0 +1,47 @@ +/** + * Bare-word aliases the Telegram bot answers, per app language. + * + * The bot's real commands are slash commands (`/help`, `/start`, `/add`) plus + * the `taken ` text command, and `telegram.helpBody` documents exactly + * those in every bundle; none of that depends on the reader's language. What + * did depend on it were the two undocumented courtesies below, which knew + * English and German: "aide", "ayuda", "aiuto" and "pomoc" reached the intake + * keyword instead, failed it, and the handler returned without replying at all. + * Silence is the worst answer to someone asking for help. + * + * These are ordinary words, not app labels, so there is nothing in the bundles + * to derive them from. One entry per locale, so a new language cannot be + * forgotten here. The bot matches the union, whatever the reader's language, + * on the FOLDED text, so "cześć" and "czesc" are one entry and a stray capital + * or accent cannot miss. + */ +import type { Locale } from "@/lib/i18n/config"; + +interface BareWords { + /** "Help". */ + readonly help: readonly string[]; + /** Greetings the bot answers with a wave. */ + readonly greeting: readonly string[]; +} + +const BARE_WORDS: Readonly> = { + en: { help: ["help"], greeting: ["hi", "hello", "hey"] }, + de: { help: ["hilfe"], greeting: ["hallo", "moin"] }, + fr: { help: ["aide"], greeting: ["bonjour", "salut"] }, + es: { help: ["ayuda"], greeting: ["hola", "buenas"] }, + it: { help: ["aiuto"], greeting: ["ciao", "salve"] }, + pl: { help: ["pomoc"], greeting: ["czesc", "witaj", "dzien dobry"] }, + ko: { help: ["도움말", "도움"], greeting: ["안녕", "안녕하세요"] }, + nl: { help: ["hulp"], greeting: ["hoi", "hallo", "goedemorgen"] }, + "pt-BR": { help: ["ajuda"], greeting: ["oi", "ola", "bom dia"] }, +}; + +/** "Help", in every language the app ships. */ +export const HELP_WORDS: readonly string[] = Object.values(BARE_WORDS).flatMap( + (words) => words.help, +); + +/** Greetings, in every language the app ships. */ +export const GREETING_WORDS: readonly string[] = Object.values( + BARE_WORDS, +).flatMap((words) => words.greeting); diff --git a/src/lib/telegram-webhook-handlers.ts b/src/lib/telegram-webhook-handlers.ts index faea70a20..7b7bdf7c2 100644 --- a/src/lib/telegram-webhook-handlers.ts +++ b/src/lib/telegram-webhook-handlers.ts @@ -37,6 +37,7 @@ import { // dose (the /add list, the "taken" keyword, the reminder buttons) leaves a // medication kept as a record out, as the web and the today list do. import { TRACKED_INTAKE_WHERE } from "@/lib/medications/intake-tracking"; +import { GREETING_WORDS, HELP_WORDS } from "@/lib/telegram-bare-words"; /** * Telegram webhook update handlers, split out of @@ -48,54 +49,6 @@ import { TRACKED_INTAKE_WHERE } from "@/lib/medications/intake-tracking"; * from the linked chat via `findTelegramUser` — never from the payload. */ -/* ────────────────────────────────────────────────────────────────────────── - * Bare-word aliases. - * - * The bot's real commands are slash commands (`/help`, `/start`, `/add`) plus - * the `taken ` text command, and `telegram.helpBody` documents exactly - * those in all six bundles — none of that depends on the reader's language. - * What did depend on it were the two undocumented courtesies below, which knew - * English and German: "aide", "ayuda", "aiuto" and "pomoc" reached the intake - * keyword instead, failed it, and the handler returned without replying at all. - * Silence is the worst answer to someone asking for help. - * - * These are ordinary words, not app labels, so there is nothing in the bundles - * to derive them from. They are matched on the FOLDED text so "cześć" and - * "czesc" are one entry and a stray capital or accent cannot miss. - * ────────────────────────────────────────────────────────────────────────── */ - -/** "Help", in every language the app ships. */ -const HELP_WORDS: readonly string[] = [ - "help", - "hilfe", - "aide", - "ayuda", - "aiuto", - "pomoc", - "ajuda", -]; - -/** Greetings the bot answers with a wave, in every language the app ships. */ -const GREETING_WORDS: readonly string[] = [ - "hi", - "hello", - "hey", - "hallo", - "moin", - "bonjour", - "salut", - "hola", - "buenas", - "ciao", - "salve", - "czesc", - "witaj", - "dzien dobry", - "oi", - "ola", - "bom dia", -]; - /** * True when the whole message is one of `words`. Folded on both sides, so the * comparison is case-, accent- and punctuation-insensitive; anchored to the