feat(telegram,game): single bot + per-user variant preferences
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 13s
CI / ui (pull_request) Successful in 54s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 57s

Collapse the two per-language Telegram bots into one unified bot and
replace language-based variant gating with explicit per-user variant
preferences.

- Telegram: one bot; drop service_language and the supported_languages
  set everywhere (DB, account, auth, FlatBuffers Session wire, gateway,
  connector proto). The single bot renders chat and out-of-app push in
  the recipient's preferred_language; remove the game-language push
  routing override (notify Intent.Language / push Event.language).
- Preferences: new accounts.variant_preferences (text[], DB default
  {erudit_ru}, CHECK non-empty + subset of the three variants). Gates
  the New Game picker, vs-AI and the friend invitation the player
  creates, enforced server-side (HTTP 400 otherwise); an invited friend
  may still accept any variant. Edited on the Settings screen; variants
  are Erudit-first everywhere.
- Admin: drop the per-bot language selectors (broadcast / send-to-user)
  and the feedback channel_lang column/field.
- Env/CI: collapse TELEGRAM_BOT_TOKEN_{EN,RU}, TELEGRAM_GAME_CHANNEL_ID_{EN,RU},
  VITE_TELEGRAM_LINK{,_EN,_RU} and VITE_TELEGRAM_GAME_CHANNEL_NAME_{EN,RU}
  to single unsuffixed names; drop GATEWAY_DEFAULT_SUPPORTED_LANGUAGES.
- Docs updated (ARCHITECTURE, FUNCTIONAL + _ru, platform/telegram, gateway,
  backend, ui, UI_DESIGN, PRERELEASE).

The migration squash is deferred to a follow-up PR.
This commit is contained in:
Ilia Denisov
2026-06-20 14:08:27 +02:00
parent 1933849dba
commit 57c778f9b2
99 changed files with 1006 additions and 1256 deletions
+4 -6
View File
@@ -117,15 +117,13 @@ archive, preview the per-variant word diff, then install + activate — `interna
`engine.DiffWords` / `Registry.LoadAvailable`, written to per-version subdirectories of the
`BACKEND_DICT_DIR` volume with the active version persisted in `dictionary_state`), and operator **broadcasts** via a
backend Telegram-connector client (`internal/connector`, `BACKEND_CONNECTOR_ADDR`) — each
broadcast picks the delivering bot by an operator-chosen language. `accounts.service_language`
holds the language tag of the bot a Telegram
user last signed in through, written on every login and returned by
`/internal/push-target` (falling back to `preferred_language`) so out-of-app push routes
to the right bot. The console also manages the **advertising banner** (`/_gm/banners` +
broadcast renders through the single bot in an operator-chosen language. There is one bot,
so `/internal/push-target` returns the recipient's `preferred_language` as the render
language for out-of-app push; no per-bot routing remains. The console also manages the **advertising banner** (`/_gm/banners` +
`/_gm/banner-settings`, `internal/ads`): operator campaigns with a percent weight, an optional
window and bilingual messages, plus the global display timings. `GET /api/v1/user/profile` attaches
the resolved, weighted campaign feed for an **eligible** viewer (`!paid_account && hint_balance == 0
&& !no_banner` role, the message language picked by `service_language`); changing those inputs
&& !no_banner` role, the message language picked by `preferred_language`); changing those inputs
publishes a `notify` `banner` re-poll signal so the client shows/hides it in place. The shared wire
contracts live in the sibling [`../pkg`](../pkg) module.