Some checks failed
Docker Build / build-and-push-image (push) Has been cancelled
Brings the project to a TDD/BDD-friendly state — apologies for shipping Phase 1.5 + Phase 2 code-first, that violated feedback_tdd_first_bdd_required. What's added : - helpers_test.go : FakeTelegram (httptest server that records sendMessage / deleteMessage / setWebhook / etc.), miniredis bootstrap, MakeUpdate / PostWebhook helpers. The same harness simulates 'a user DMing the bot' end-to-end without hitting Telegram cloud — answer to the user question. - 43 tests covering : allowlist parsing, telegram type helpers (UserID / ChatID / Text / messageID), secret_token constant-time compare, Backoff schedule, Auth (login wrong/right/logout/TTL/nil-receiver), EchoHandler, HTTPHandler (forward / timeout / non-2xx / empty body), AuthHandler (start / auth / whoami / logout / replay defense delete), Server (bad secret 401, unknown bot 404, allowlist drop, gated bot prompt, full /auth → echo → /logout flow, healthz/readyz). - All tests pass with -race in 1.6s, no external deps (miniredis + httptest in-process). Infra : - Updated .gitea/workflows/dockerimage.yaml : new 'test' job (go vet + go test -race) gates the build-and-push-image job. CI now also runs on pull_request. - docker-compose.yml : redis + postgres for full local stack. - Makefile : test-race, compose-up/down targets. - README updated with test + local-dev sections. Refs ~/.claude/plans/pour-les-notifications-on-inherited-seal.md § Phase 2.
111 lines
4.4 KiB
Markdown
111 lines
4.4 KiB
Markdown
# telegram-gateway
|
|
|
|
Telegram **webhook gateway** for the Arcodange home lab. Replaces polling-based
|
|
bots (e.g. those scheduled in Cowork) with direct webhook delivery from
|
|
Telegram, routed to per-bot handlers running on the k3s cluster.
|
|
|
|
> Phase 1 (MVP): single sync `echo` handler, end-to-end flow validated.
|
|
> Phase 2 (planned): `http` forward handler + Postgres-backed durable queue.
|
|
> Phase 3 (planned): async `shell` / `script` / `ollama` handlers.
|
|
|
|
See the design doc at `~/.claude/plans/pour-les-notifications-on-inherited-seal.md`.
|
|
|
|
## Architecture (current)
|
|
|
|
```
|
|
Telegram → Cloudflare Tunnel (tg.arcodange.fr) → Service telegram-gateway:8080
|
|
→ /bot/<slug> → secret_token check → handler dispatch → Bot API sendMessage
|
|
```
|
|
|
|
## Routes
|
|
|
|
| Method | Path | Description |
|
|
|--------|------------------|--------------------------------------------|
|
|
| GET | `/healthz` | Liveness probe |
|
|
| GET | `/readyz` | Readiness probe |
|
|
| POST | `/bot/{slug}` | Telegram webhook entry (validates secret) |
|
|
|
|
## Local dev
|
|
|
|
Pour le dev local complet (Redis pour l'auth + Postgres pour la queue) :
|
|
|
|
```bash
|
|
make compose-up # docker compose up -d --wait : redis + postgres
|
|
export REDIS_URL=redis://localhost:6379/0
|
|
export DATABASE_URL=postgres://gateway:gateway@localhost:5432/gateway?sslmode=disable
|
|
export AUTH_SECRET=$(openssl rand -hex 16)
|
|
export ALLOWED_USERS=<your-tg-user-id>
|
|
export BOT_FACTORY_TOKEN='8737289837:…' # from @BotFather
|
|
export BOT_FACTORY_SECRET=$(openssl rand -hex 32)
|
|
make run # uses bots.example.yaml
|
|
```
|
|
|
|
Smoke d'un webhook (sans Telegram cloud) :
|
|
|
|
```bash
|
|
curl -X POST -H "X-Telegram-Bot-Api-Secret-Token: $BOT_FACTORY_SECRET" \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{"update_id":1,"message":{"message_id":1,"from":{"id":<tg-id>},"chat":{"id":<tg-id>},"text":"hi"}}' \
|
|
http://localhost:8080/bot/factory
|
|
```
|
|
|
|
## Tests
|
|
|
|
```bash
|
|
make test # unit + integration (in-process miniredis + httptest mock Telegram)
|
|
make test-race # avec race detector
|
|
make vet
|
|
```
|
|
|
|
43 tests couvrent : allowlist parsing, secret comparison, auth flow (login/logout/whoami/replay-defense), echo handler (plain/slash/empty), http handler (forward/timeout/non-2xx/empty), webhook dispatch (bad secret 401, unknown bot 404, allowlist drop, gated bot prompt, full /auth → echo flow).
|
|
|
|
Pour des smokes locaux contre une vraie API Telegram, voir la section "Local dev" ci-dessus.
|
|
|
|
## Set / delete webhook
|
|
|
|
```bash
|
|
# Once the gateway is reachable at https://tg.arcodange.fr:
|
|
export BOT_FACTORY_TOKEN=…
|
|
export BOT_FACTORY_SECRET=…
|
|
make setwebhook SLUG=factory BASE_URL=https://tg.arcodange.fr
|
|
make deletewebhook SLUG=factory
|
|
```
|
|
|
|
## Configuration
|
|
|
|
- **Routing** (non-secret): YAML at `$CONFIG_PATH` (default
|
|
`/etc/telegram-gateway/bots.yaml`, mounted from a ConfigMap in cluster).
|
|
- **Secrets**: per-bot env vars `BOT_<UPPER_SLUG>_TOKEN`,
|
|
`BOT_<UPPER_SLUG>_SECRET`. Sourced from Vault path
|
|
`kvv2/telegram-gateway/config` via Vault Secrets Operator.
|
|
|
|
## Cluster deploy
|
|
|
|
- Image: `gitea.arcodange.lab/arcodange/telegram-gateway:<tag>`
|
|
- Helm chart: `chart/`
|
|
- ArgoCD app: `telegram-gateway` (in `factory/argocd/values.yaml`)
|
|
- Public URL: `https://tg.arcodange.fr` (Cloudflare déjà configuré pour
|
|
router `*.arcodange.fr` vers le home lab → Traefik route par Host)
|
|
- Secrets Phase 1 : `kubectl create secret generic telegram-gateway-bots …`
|
|
(sans Vault). Migration vers Vault Secrets Operator en Phase 2+ via
|
|
`vault.enabled: true` dans `chart/values.yaml`.
|
|
|
|
Voir `DEPLOY.md` pour la procédure end-to-end.
|
|
|
|
## Layout
|
|
|
|
```
|
|
.
|
|
├── main.go # bootstrap, subcommand dispatch
|
|
├── server.go # HTTP routes
|
|
├── middleware.go # secret validation, recover, access log
|
|
├── handlers.go # Handler interface + Registry
|
|
├── handler_echo.go # echo handler
|
|
├── telegram.go # Telegram Bot API client
|
|
├── telegram_types.go # Update / Message structs
|
|
├── config.go # YAML routing config + per-bot env merge
|
|
├── setwebhook.go # CLI subcommands (setwebhook / deletewebhook)
|
|
├── chart/ # Helm chart
|
|
└── .gitea/workflows/ # CI: docker build → gitea registry
|
|
```
|