mirror of
https://github.com/harivansh-afk/pi-telegram-webhook.git
synced 2026-04-15 06:04:43 +00:00
initial: webhook telegram adapter for pi with streaming replies
- webhook server with secret validation, rate limiting, body guards - streaming replies via sendMessage + editMessageText throttled loop - RPC session management for persistent conversations - 15/15 tests passing
This commit is contained in:
parent
809e9b1df5
commit
ce9abc2a8e
18 changed files with 6991 additions and 1 deletions
162
README.md
162
README.md
|
|
@ -1,2 +1,162 @@
|
|||
# pi-telegram-webhook
|
||||
Pi extension for Telegram webhook ingress and streaming message delivery
|
||||
|
||||
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<YOUR_TOKEN>/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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue