# pi-telegram-webhook Webhook-based Telegram adapter for [pi](https://github.com/badlogic/pi-mono) with streaming replies and persistent RPC sessions. ## Features - **Webhook ingress** (vs polling) — lower latency, better for production - **Security hardened**: - Constant-time secret validation (timing attack resistant) - Fixed-window rate limiter per IP - 1MB body size limit - 30s read timeout - Trusted proxy X-Forwarded-For support - **Streaming replies** — throttled `editMessageText` for live updates (default 1s, 4096 char limit) - **Persistent RPC sessions** — `pi --mode rpc` subprocess per sender, context preserved across messages - **Generation tracking** — prevents new messages from clobbering old edits - **FIFO queues** — per-sender serialization, configurable concurrency - **Bot commands**: `/start`, `/help`, `/new`, `/abort`, `/clear` ## Installation ```bash npm install pi-telegram-webhook ``` ## Configuration Add to `.pi/settings.json`: ```json { "pi-telegram-webhook": { "botToken": "your-telegram-bot-token", "webhookUrl": "https://your-domain.com/telegram-webhook", "webhookPort": 2470, "webhookHost": "127.0.0.1", "webhookPath": "/telegram-webhook", "webhookSecret": "random-secret-token", "allowedChatIds": ["123456789"], "streamingThrottleMs": 1000, "minInitialChars": 50, "trustedProxies": ["127.0.0.1", "10.0.0.0/8"], "maxConcurrent": 2, "timeoutMs": 300000, "idleTimeoutMinutes": 30, "model": "anthropic/claude-sonnet-4", "extensions": ["pi-channels"] } } ``` ### Config Reference | Key | Type | Default | Description | |-----|------|---------|-------------| | `botToken` | `string` | *required* | Telegram Bot API token | | `webhookUrl` | `string` | *required* | Public webhook URL (must be HTTPS in production) | | `webhookSecret` | `string` | *required* | Secret token for webhook validation | | `webhookPort` | `number` | `2470` | Local server port | | `webhookHost` | `string` | `127.0.0.1` | Local server bind address | | `webhookPath` | `string` | `/telegram-webhook` | Webhook endpoint path | | `allowedChatIds` | `string[]` | `undefined` | Whitelist of allowed chat IDs (optional) | | `streamingThrottleMs` | `number` | `1000` | Throttle interval for streaming edits | | `minInitialChars` | `number` | `50` | Min chars before sending first message (debounce) | | `trustedProxies` | `string[]` | `["127.0.0.1"]` | Trusted proxy IPs/CIDRs for X-Forwarded-For | | `maxConcurrent` | `number` | `2` | Max concurrent prompts across all senders | | `timeoutMs` | `number` | `300000` | Prompt timeout (5 min) | | `idleTimeoutMinutes` | `number` | `30` | RPC session idle timeout | | `model` | `string` | `undefined` | Override model for RPC sessions | | `extensions` | `string[]` | `[]` | Additional pi extensions to load | ## Deployment ### Reverse Proxy (nginx) ```nginx location /telegram-webhook { proxy_pass http://127.0.0.1:2470; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; proxy_http_version 1.1; proxy_read_timeout 35s; client_max_body_size 2M; } ``` ### Systemd Service ```ini [Unit] Description=pi Telegram webhook After=network.target [Service] Type=simple User=youruser WorkingDirectory=/path/to/your/project ExecStart=/usr/bin/pi --no-tui Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.target ``` ### Webhook Setup The extension automatically calls `setWebhook` on startup. To manually test: ```bash curl -X POST "https://api.telegram.org/bot/setWebhook" \ -H "Content-Type: application/json" \ -d '{ "url": "https://your-domain.com/telegram-webhook", "secret_token": "your-secret" }' ``` ## Bot Commands - `/start`, `/help` — Show welcome message - `/new` — Start a new conversation (clear context) - `/abort` — Abort the current task - `/clear` — Clear the message queue ## Architecture ``` Telegram → HTTPS webhook → nginx → pi-telegram-webhook ├─ webhook-server (security, rate limiting) ├─ bridge (FIFO queues, concurrency) └─ RPC sessions (streaming replies) ``` ### Flow 1. Incoming webhook → secret validation → rate limit check → body parsing 2. Message enqueued in per-sender FIFO queue 3. Bridge spawns/reuses `pi --mode rpc` subprocess for sender 4. Prompt sent to RPC session, streaming deltas received 5. StreamingReply sends initial message, then throttled edits 6. Generation tracking prevents race conditions ## Development ```bash npm install npm run build npm test ``` ## Security Notes - **Secret validation** uses constant-time comparison (timing attack resistant) - **Rate limiting** applied *before* secret check (prevents brute-force) - **Body size limit** prevents memory exhaustion - **Read timeout** prevents slowloris attacks - **Trusted proxies** must be explicitly configured for X-Forwarded-For ## License MIT