Stage 15: dual Telegram bots & language-gated variants
Tests · Go / test (push) Successful in 9s
Tests · Integration / integration (push) Successful in 10s
Tests · UI / test (push) Successful in 20s
Tests · Go / test (pull_request) Successful in 8s
Tests · Integration / integration (pull_request) Successful in 11s
Tests · UI / test (pull_request) Successful in 19s
Tests · Go / test (push) Successful in 9s
Tests · Integration / integration (push) Successful in 10s
Tests · UI / test (push) Successful in 20s
Tests · Go / test (pull_request) Successful in 8s
Tests · Integration / integration (pull_request) Successful in 11s
Tests · UI / test (pull_request) Successful in 19s
Service-agnostic refinement of the owner's idea: the sign-in service returns a set of supported game languages with the user identity, and the lobby gates the New Game variant choice by it (en -> English; ru -> Russian + Эрудит). - Connector hosts two bots in one container (one per service language, each its own token + game channel; the same telegram_id spans both). ValidateInitData tries each token and returns the validating bot's service_language + supported_languages. Per-language config (TELEGRAM_BOT_TOKEN_EN/_RU, channels). - supported_languages rides the Session (fbs, session-scoped, not persisted); the UI offers only the matching variants on New Game — gating only the START of a new game (auto-match + friend invite), not accept/open/play; backend does not enforce. - service_language persisted (accounts.service_language, migration 00010, written every login, last-login-wins) and routes the user-facing Notify push back through the right bot (push-target coalesces with preferred_language). - Admin SendToUser/SendToGameChannel gain an operator-chosen language selector in the console (unrelated to ValidateInitData). - Non-Telegram logins carry the gateway default set (GATEWAY_DEFAULT_SUPPORTED_LANGUAGES, all variants). Wire (committed regen): ValidateInitDataResponse +service_language +supported_languages; Session +supported_languages; SendToUser/SendToGameChannel +language. Docs (ARCHITECTURE/FUNCTIONAL/_ru/READMEs) + PLAN updated; stage marked done.
This commit is contained in:
+25
-5
@@ -45,8 +45,10 @@ Three executables plus per-platform side-services:
|
||||
mode). The visual/interaction design system is documented in
|
||||
[`UI_DESIGN.md`](UI_DESIGN.md).
|
||||
- **`platform/telegram`** — the Telegram side-service (the "connector", module
|
||||
`scrabble/platform/telegram`). It is the only component holding the bot token: it
|
||||
runs the Bot API long-poll loop (Mini App launch + `/start` deep-links) and serves
|
||||
`scrabble/platform/telegram`). It is the only component holding the bot tokens — **one
|
||||
bot per service language** (`en`/`ru`), each its own token + game channel, the same
|
||||
Telegram user id spanning both (§3). It
|
||||
runs a Bot API long-poll loop per bot (Mini App launch + `/start` deep-links) and serves
|
||||
a gRPC API (`pkg/proto/telegram/v1`) that `gateway` (Mini App initData validation
|
||||
and out-of-app push) and `backend` (operator broadcasts) call over the
|
||||
trusted internal network. Its generic delivery methods are **platform-agnostic**
|
||||
@@ -119,6 +121,20 @@ arrive from a platform rather than completing a mandatory registration).
|
||||
bootstrap — then mints a **thin opaque server session token** (`session_id`). First
|
||||
Telegram contact seeds the new account's language (from the launch `language_code`)
|
||||
and display name (§4).
|
||||
- **Service language & variant gating (Stage 15).** The connector hosts **one bot per
|
||||
service language** (`en`/`ru`), each its own token + game channel; the same Telegram
|
||||
user id spans both. `ValidateInitData` tries each token in turn and returns the
|
||||
validating bot's **service language** and its **supported-languages set**. The set
|
||||
rides the **`Session`** (FlatBuffers, session-scoped, not persisted): the UI offers
|
||||
only the variants those languages support on New Game (`en` → English; `ru` → Russian
|
||||
+ Эрудит). **Starting** a new game is the only gated action — opening and playing
|
||||
existing games of any language is unrestricted, and the backend does not enforce the
|
||||
gate (it is a product affordance, not a trust boundary). The service language is
|
||||
**persisted** per account (`accounts.service_language`, updated on every Telegram
|
||||
login — last-login-wins) and routes the user's out-of-app push back through the right
|
||||
bot (§10); it is distinct from `preferred_language` (the interface language) and from
|
||||
a game's variant language. Non-Telegram logins (web / email / guest) carry the
|
||||
gateway's default set (`GATEWAY_DEFAULT_SUPPORTED_LANGUAGES`, all variants by default).
|
||||
- The client holds `session_id` in memory for the app session (browser/OS
|
||||
storage is optional and may be unavailable; losing it means re-login).
|
||||
- The gateway caches `session → user_id` and injects `X-User-ID`. Session
|
||||
@@ -447,12 +463,16 @@ open and on focus as well as re-polling on the `notify` event — covering a pus
|
||||
missed while the app was hidden. **Out-of-app platform push** (Stage 9) is a fallback
|
||||
the **gateway** routes from the same firehose: for an event whose recipient has **no
|
||||
live in-app stream** it resolves the backend `/internal/push-target` (their Telegram
|
||||
`external_id`, language, and the `notifications_in_app_only` flag) and asks the
|
||||
`external_id`, the **service language** — the bot they last signed in through, falling
|
||||
back to the interface language — and the `notifications_in_app_only` flag) and asks the
|
||||
**Telegram connector** to deliver a localized message with a Mini App deep-link
|
||||
button — only when the recipient has a Telegram identity and has not confined
|
||||
notifications to the app, so the two channels never duplicate. The out-of-app set is
|
||||
notifications to the app, so the two channels never duplicate. The connector routes by
|
||||
that language to the matching bot and renders the message in it. The out-of-app set is
|
||||
your-turn, nudge, match-found and the invitation / friend-request notify sub-kinds;
|
||||
the connector renders the message and skips the rest. Session-revocation events and
|
||||
the connector renders the message and skips the rest. Operator broadcasts
|
||||
(`SendToUser` / `SendToGameChannel`, §10 admin) instead pick the bot by an
|
||||
**operator-chosen** language in the console, unrelated to the recipient's login. Session-revocation events and
|
||||
cursor-based stream resume stay deferred (single-instance MVP).
|
||||
|
||||
A separate **announcements channel** feeds the client's one-line banner (UI_DESIGN.md).
|
||||
|
||||
Reference in New Issue
Block a user