Files
scrabble-game/platform/telegram/README.md
T
Ilia Denisov 52f898ca6f
Tests · Go / test (push) Successful in 7s
Tests · Integration / integration (push) Successful in 11s
Tests · UI / test (push) Successful in 20s
Tests · Go / test (pull_request) Successful in 6s
Tests · Integration / integration (pull_request) Successful in 11s
Tests · UI / test (pull_request) Successful in 19s
Stage 11: account linking & merge (email + Telegram Login Widget)
Link an email (confirm-code) or Telegram (web Login Widget) to the current
account; if the identity already has its own account, merge the two into the
one in use (the current account is primary, except a guest initiator whose
durable counterpart wins). The merge runs in one transaction
(internal/accountmerge): stats + hint wallet summed, paid_account ORed,
identities/games/chat/complaints transferred, friends/blocks de-duplicated,
the secondary kept as a merged_into tombstone so a shared finished game's
no-cascade FKs hold; a shared active game blocks the merge.

- migration 00009: accounts.paid_account, merged_into, merged_at (+ jetgen)
- internal/link orchestrator; session.RevokeAllForAccount on merge
- connector ValidateLoginWidget RPC + loginwidget HMAC validator
- edge ops link.email.request/confirm/merge, link.telegram.confirm/merge;
  supersedes the Stage 8 email.bind.* surface (request never reveals 'taken'
  before the code is verified, so a probe cannot enumerate addresses)
- UI Profile link section + irreversible-merge dialog; Telegram web sign-in
- focused regression tests (merge core, guest inversion, active-game refusal,
  finished-shared-game kept), gateway transcode + connector + UI codec/e2e
- docs: PLAN, ARCHITECTURE 3/4/9, FUNCTIONAL(+ru), module READMEs
2026-06-04 11:15:14 +02:00

91 lines
4.3 KiB
Markdown

# scrabble/platform/telegram — Telegram connector
The Telegram platform side-service. It is the **only** component that holds the bot
token: it runs the 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.
## Responsibilities
- **Mini App auth.** `ValidateInitData` verifies Telegram Web App `initData` (HMAC
under the bot token) and returns the user identity. The gateway calls it during
the `auth.telegram` edge operation, then provisions the session through the
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. 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** (wired in Stage 10). `SendToUser` and `SendToGameChannel` send
arbitrary text to one user or the configured game channel.
The generic methods (`Notify`, `SendToUser`, `SendToGameChannel`) address a
recipient by the identity `external_id` (as in the backend `identities` table), so a
future VK / MAX connector can implement the same service; only `ValidateInitData` is
Telegram-specific.
## gRPC API
`pkg/proto/telegram/v1`, service `Telegram`: `ValidateInitData`,
`ValidateLoginWidget`, `Notify`, `SendToUser`, `SendToGameChannel`. Generated Go is
committed under `pkg`. `ValidateLoginWidget` (Stage 11) verifies Telegram **Login
Widget** web sign-in data — HMAC under `SHA-256(bot_token)`, distinct from initData
(`internal/loginwidget`) — for attaching a Telegram identity to an account from a
browser.
## Deep-link scheme
Shared verbatim with the UI (`ui/src/lib/deeplink.ts`). A Mini App start parameter
is a one-character kind prefix plus a value:
| Parameter | Destination |
| --- | --- |
| `g<game uuid>` | open that game |
| `i<invitation uuid>` | open that invitation |
| `f<6-digit code>` | redeem that friend code |
| empty / unknown | the lobby |
The bot turns a `/start <payload>` or a notification target into a launch-button URL
`<MiniAppURL>?startapp=<payload>`.
## Configuration
| Env var | Default | Meaning |
| --- | --- | --- |
| `TELEGRAM_BOT_TOKEN` | — (required) | Bot API token + the initData HMAC secret |
| `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`) |
| `TELEGRAM_GAME_CHANNEL_ID` | — | game channel chat id for `SendToGameChannel` |
| `TELEGRAM_LOG_LEVEL` | `info` | zap log level |
The **test environment** is selected by `TELEGRAM_TEST_ENV=true`, which suffixes the
Bot API path with `/test` (the connector appends it to the token, since the client
builds `<host>/bot<token>/<method>`).
## Build, test, run
```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
```
## Deploy
The connector runs in its **own container** with the bot token held only there and
all egress through a VPN sidecar (`deploy/docker-compose.yml`, mirroring
`../../15-puzzle`). It needs no public ingress — it long-polls Telegram and answers
internal gRPC at `telegram:9091` on the shared `edge` network. The host reverse proxy
routes public traffic to the **gateway** port only, which serves the Mini App under
`/telegram/`. The full multi-service deploy lands with Stage 12.
A real end-to-end Telegram smoke needs a BotFather bot, its token, a public HTTPS
Mini App origin, and the connector container; the unit tests cover the wire format,
templates, deep-links and the gRPC handlers without a live bot.