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
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:
+21
-25
@@ -1,22 +1,20 @@
|
||||
# scrabble/platform/telegram — Telegram connector
|
||||
|
||||
The Telegram platform side-service. It is the **only** component that holds the bot
|
||||
tokens: it runs a Bot API long-poll loop **per service language** (Mini App launch +
|
||||
deep-links) and serves the connector gRPC API that the gateway and backend call over
|
||||
the trusted internal network. See
|
||||
[`docs/ARCHITECTURE.md`](../../docs/ARCHITECTURE.md) §1/§3/§10/§12.
|
||||
token: it runs a Bot API long-poll loop (Mini App launch + deep-links) and serves
|
||||
the connector gRPC API that the gateway and backend call over the trusted internal
|
||||
network. See [`docs/ARCHITECTURE.md`](../../docs/ARCHITECTURE.md) §1/§3/§10/§12.
|
||||
|
||||
## Service languages (dual bots)
|
||||
## Single bot
|
||||
|
||||
The connector hosts **one bot per service language** (`en`, `ru`) — each its own
|
||||
token + game channel, configured by the `*_EN` / `*_RU` env vars; at least one is
|
||||
required. The **same Telegram user id spans both bots**. `ValidateInitData` tries
|
||||
each bot's token in turn (none validates ⇒ invalid) and reports which bot validated:
|
||||
its **`service_language`** (persisted by the backend to route the user's later push)
|
||||
and its **`supported_languages`** set (which the UI gates the New Game variant choice
|
||||
by — `en` → English, `ru` → Russian + Эрудит). The user-facing `Notify` routes by the
|
||||
recipient's persisted service language; the admin `SendToUser` / `SendToGameChannel`
|
||||
route by an **operator-chosen** `language` (unrelated to login).
|
||||
The connector hosts **one unified bot** — one token plus one optional game channel,
|
||||
configured by `TELEGRAM_BOT_TOKEN` and `TELEGRAM_GAME_CHANNEL_ID`. `ValidateInitData`
|
||||
validates `initData` against that single token (it does not validate ⇒ invalid) and
|
||||
returns only the Telegram user identity — there is no per-bot service language and no
|
||||
supported-languages set. Every message the bot sends is rendered in the **recipient's
|
||||
interface language**: the user-facing `Notify` renders in the request's `language` (the
|
||||
recipient's interface language); the admin `SendToUser` / `SendToGameChannel` render in
|
||||
an **operator-chosen** `language`. No call routes between bots — there is only one.
|
||||
|
||||
## Responsibilities
|
||||
|
||||
@@ -26,17 +24,17 @@ route by an **operator-chosen** `language` (unrelated to login).
|
||||
backend internal API — so the bot token never leaves this process.
|
||||
- **Out-of-app push.** `Notify` renders a backend push event (your_turn, nudge,
|
||||
match_found, and the invitation / friend_request notify sub-kinds) into a
|
||||
localized message with a Mini App launch button and sends it **through the bot for
|
||||
the request's `language`** (the recipient's service language). The gateway calls it
|
||||
**only** for a recipient with no live in-app stream and the
|
||||
localized message with a Mini App launch button and sends it through the **single
|
||||
bot**, rendered in the request's `language` (the recipient's interface language).
|
||||
The gateway calls it **only** for a recipient with no live in-app stream and the
|
||||
`notifications_in_app_only` flag off, so the platform push never duplicates in-app
|
||||
delivery.
|
||||
- **Bot chat.** `/start <payload>` (and the chat menu button) reply with a Mini App
|
||||
launch button; a deep-link payload routes the launch to a game / invitation /
|
||||
friend code.
|
||||
- **Admin messaging.** `SendToUser` and `SendToGameChannel` send
|
||||
arbitrary text to one user or a game channel through the bot the request selects by
|
||||
`language` (an operator choice in the admin console).
|
||||
arbitrary text to one user or a game channel through the single bot, rendered in the
|
||||
`language` the operator chooses in the admin console.
|
||||
|
||||
The generic methods (`Notify`, `SendToUser`, `SendToGameChannel`) address a
|
||||
recipient by the identity `external_id` (as in the backend `identities` table), so a
|
||||
@@ -71,11 +69,9 @@ The bot turns a `/start <payload>` or a notification target into a launch-button
|
||||
|
||||
| Env var | Default | Meaning |
|
||||
| --- | --- | --- |
|
||||
| `TELEGRAM_BOT_TOKEN_EN` | — | English bot's API token + initData HMAC secret |
|
||||
| `TELEGRAM_BOT_TOKEN_RU` | — | Russian bot's API token + initData HMAC secret (≥ 1 of EN/RU required) |
|
||||
| `TELEGRAM_GAME_CHANNEL_ID_EN` | — | English bot's game channel chat id for `SendToGameChannel` |
|
||||
| `TELEGRAM_GAME_CHANNEL_ID_RU` | — | Russian bot's game channel chat id |
|
||||
| `TELEGRAM_MINIAPP_URL` | — (required) | Mini App HTTPS origin, shared by all bots (BotFather-registered) |
|
||||
| `TELEGRAM_BOT_TOKEN` | — (required) | The bot's API token + initData HMAC secret |
|
||||
| `TELEGRAM_GAME_CHANNEL_ID` | — | The bot's game channel chat id for `SendToGameChannel` |
|
||||
| `TELEGRAM_MINIAPP_URL` | — (required) | Mini App HTTPS origin (BotFather-registered) |
|
||||
| `TELEGRAM_GRPC_ADDR` | `:9091` | connector gRPC listen address |
|
||||
| `TELEGRAM_API_BASE_URL` | `https://api.telegram.org` | Bot API host override (mock / self-hosted) |
|
||||
| `TELEGRAM_TEST_ENV` | `false` | route to the Bot API **test environment** (`/bot<token>/test/METHOD`) |
|
||||
@@ -93,7 +89,7 @@ builds `<host>/bot<token>/<method>`).
|
||||
```sh
|
||||
go build ./platform/telegram/...
|
||||
go test ./platform/telegram/... # unit tests use an httptest fake Bot API
|
||||
go run ./platform/telegram/cmd/telegram # needs a real TELEGRAM_BOT_TOKEN_EN or _RU
|
||||
go run ./platform/telegram/cmd/telegram # needs a real TELEGRAM_BOT_TOKEN
|
||||
```
|
||||
|
||||
## Deploy
|
||||
|
||||
Reference in New Issue
Block a user