From 945e998516e9bd6f2bd305ab8fe0e2f95c59ea8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Thu, 22 Oct 2026 15:16:42 +0200 Subject: [PATCH] refactor(telegram): keep the bare help and greeting words in a per-locale table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The two word lists lived inside the webhook handler and had no entry for Dutch or Korean. They move to their own module, keyed by locale so the compiler names a language that has none, and gain "hulp", "hoi", "goedemorgen", "도움말", "도움", "안녕" and "안녕하세요" beside the Portuguese words. The bot still matches the union on the folded text. --- .../telegram/webhook/__tests__/route.test.ts | 6 +++ src/lib/telegram-bare-words.ts | 47 ++++++++++++++++++ src/lib/telegram-webhook-handlers.ts | 49 +------------------ 3 files changed, 54 insertions(+), 48 deletions(-) create mode 100644 src/lib/telegram-bare-words.ts 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