# 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 ` (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`, `Notify`, `SendToUser`, `SendToGameChannel`. Generated Go is committed under `pkg`. ## 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` | open that game | | `i` | open that invitation | | `f<6-digit code>` | redeem that friend code | | empty / unknown | the lobby | The bot turns a `/start ` or a notification target into a launch-button URL `?startapp=`. ## 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/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 `/bot/`). ## 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.