2025-12-09 17:51:05 +00:00
---
summary: "Telegram bot support status, capabilities, and configuration"
read_when:
- Working on Telegram features or webhooks
---
2025-12-07 22:46:02 +01:00
# Telegram (Bot API)
Updated: 2025-12-07
2026-01-02 22:23:00 +01:00
Status: ready for bot-mode use with grammY (long-polling by default; webhook supported when configured). Text + media send, mention-gated group replies with per-group overrides, and optional proxy support are implemented.
2025-12-07 22:46:02 +01:00
## Goals
2026-01-04 14:32:47 +00:00
- Let you talk to Clawdbot via a Telegram bot in DMs and groups.
2026-01-02 10:14:58 +01:00
- Share the same `main` session used by WhatsApp/WebChat; groups stay isolated as `telegram:group:<chatId>` .
2026-01-06 18:25:52 +00:00
- Keep transport routing deterministic: replies always go back to the provider they arrived on.
2025-12-07 22:46:02 +01:00
## How it will work (Bot API)
1) Create a bot with @BotFather and grab the token.
2026-01-04 14:32:47 +00:00
2) Configure Clawdbot with `TELEGRAM_BOT_TOKEN` (or `telegram.botToken` in `~/.clawdbot/clawdbot.json` ).
2026-01-02 11:41:08 +00:00
3) Run the gateway; it auto-starts Telegram only when a `telegram` config section exists **and** a bot token is set (unless `telegram.enabled = false` ).
2026-01-04 14:32:47 +00:00
- If you prefer env vars, still add `telegram: { enabled: true }` to `~/.clawdbot/clawdbot.json` and set `TELEGRAM_BOT_TOKEN` .
2025-12-14 00:49:18 +00:00
- **Long-polling** is the default.
- **Webhook mode** is enabled by setting `telegram.webhookUrl` (optionally `telegram.webhookSecret` / `telegram.webhookPath` ).
- The webhook listener currently binds to `0.0.0.0:8787` and serves `POST /telegram-webhook` by default.
- If you need a different public port/host, set `telegram.webhookUrl` to the externally reachable URL and use a reverse proxy to forward to `:8787` .
2026-01-06 17:51:38 +01:00
4) Direct chats: secure by default — unknown senders are gated by `telegram.dmPolicy` (default: `"pairing"` ). The bot responds with a pairing code that the owner must approve before messages are processed. If you really want public inbound DMs: set `telegram.dmPolicy="open"` and `telegram.allowFrom=["*"]` .
2026-01-06 03:30:33 +01:00
5) Groups: add the bot, disable privacy mode (or make it admin) so it can read messages; group threads stay on `telegram:group:<chatId>` . When `telegram.groups` is set, it becomes a group allowlist (use `"*"` to allow all). Mention/command gating defaults come from `telegram.groups` .
2026-01-06 17:51:38 +01:00
6) Allowlist + pairing:
- Direct chats: `telegram.allowFrom` (chat ids) or pairing approvals via `clawdbot pairing approve --provider telegram <code>` (alias: `clawdbot telegram pairing approve <code>` ).
2026-01-06 06:40:42 +00:00
- Groups: set `telegram.groupPolicy = "allowlist"` and list senders in `telegram.groupAllowFrom` (fallback: explicit `telegram.allowFrom` ).
2025-12-07 22:46:02 +01:00
## Capabilities & limits (Bot API)
- Sees only messages sent after it’ s added to a chat; no pre-history access.
- Cannot DM users first; they must initiate. Channels are receive-only unless the bot is an admin poster.
- File size caps follow Telegram Bot API (up to 2 GB for documents; smaller for some media types).
2026-01-02 23:18:41 +01:00
- Typing indicators (`sendChatAction` ) supported; native replies are **off by default** and enabled via `telegram.replyToMode` + reply tags.
2025-12-07 22:46:02 +01:00
## Planned implementation details
2025-12-09 17:51:05 +00:00
- Library: grammY is the only client for send + gateway (fetch fallback removed); grammY throttler is enabled by default to stay under Bot API limits.
2026-01-06 18:25:52 +00:00
- Inbound normalization: maps Bot API updates to `MsgContext` with `Provider: "telegram"` , `ChatType: direct|group` , `SenderName` , `MediaPath` /`MediaType` when attachments arrive, `Timestamp` , and reply-to metadata (`ReplyToId` , `ReplyToBody` , `ReplyToSender` ) when the user replies; reply context is appended to `Body` as a `[Replying to ...]` block (includes `id:` when available); groups require @bot mention or a `routing.groupChat.mentionPatterns` match by default (override per chat in config).
2025-12-07 22:46:02 +01:00
- Outbound: text and media (photo/video/audio/document) with optional caption; chunked to limits. Typing cue sent best-effort.
2026-01-06 17:51:38 +01:00
- Config: `TELEGRAM_BOT_TOKEN` env or `telegram.botToken` required; `telegram.dmPolicy` , `telegram.groups` (group allowlist + mention defaults), `telegram.allowFrom` , `telegram.groupAllowFrom` , `telegram.groupPolicy` , `telegram.mediaMaxMb` , `telegram.replyToMode` , `telegram.proxy` , `telegram.webhookSecret` , `telegram.webhookUrl` , `telegram.webhookPath` supported.
2026-01-06 03:28:35 +00:00
- Ack reactions are controlled globally via `messages.ackReaction` + `messages.ackReactionScope` .
2026-01-02 22:23:00 +01:00
- Mention gating precedence (most specific wins): `telegram.groups.<chatId>.requireMention` → `telegram.groups."*".requireMention` → default `true` .
2025-12-07 22:46:02 +01:00
Example config:
```json5
{
telegram: {
2025-12-26 16:54:53 +00:00
enabled: true,
2025-12-07 22:46:02 +01:00
botToken: "123:abc",
2026-01-06 17:51:38 +01:00
dmPolicy: "pairing", // pairing | allowlist | open | disabled
2026-01-02 23:18:41 +01:00
replyToMode: "off",
2026-01-02 22:23:00 +01:00
groups: {
2026-01-06 03:30:33 +01:00
"*": { requireMention: true }, // allow all groups
2026-01-02 22:23:00 +01:00
"123456789": { requireMention: false } // group chat id
},
2026-01-06 17:51:38 +01:00
allowFrom: ["123456789"], // direct chat ids allowed ("open" requires ["*"])
2026-01-06 06:40:42 +00:00
groupPolicy: "allowlist",
groupAllowFrom: ["tg:123456789", "@alice "],
2025-12-07 22:46:02 +01:00
mediaMaxMb: 5,
proxy: "socks5://localhost:9050",
webhookSecret: "mysecret",
2025-12-14 00:49:18 +00:00
webhookPath: "/telegram-webhook",
2025-12-07 22:46:02 +01:00
webhookUrl: "https://yourdomain.com/telegram-webhook"
}
}
```
- Tests: grammY-based paths in `src/telegram/*.test.ts` cover DM + group gating; add more media and webhook cases as needed.
## Group etiquette
- Keep privacy mode off if you expect the bot to read all messages; with privacy on, it only sees commands/mentions.
- Make the bot an admin if you need it to send in restricted groups or channels.
2026-01-06 03:30:33 +01:00
- Mention the bot (`@yourbot` ) or use a `routing.groupChat.mentionPatterns` trigger; per-group overrides live in `telegram.groups` if you want always-on behavior. If `telegram.groups` is set, add `"*"` to keep existing allow-all behavior.
2025-12-07 22:46:02 +01:00
2026-01-02 23:18:41 +01:00
## Reply tags
To request a threaded reply, the model can include one tag in its output:
- `[[reply_to_current]]` — reply to the triggering Telegram message.
- `[[reply_to:<id>]]` — reply to a specific message id from context.
Current message ids are appended to prompts as `[message_id: …]` ; reply context includes `id:` when available.
Behavior is controlled by `telegram.replyToMode` :
- `off` : ignore tags.
- `first` : only the first outbound chunk/attachment is a reply.
- `all` : every outbound chunk/attachment is a reply.
2025-12-07 22:46:02 +01:00
## Roadmap
- ✅ Design and defaults (this doc)
2025-12-09 17:51:05 +00:00
- ✅ grammY long-poll gateway + text/media send
2025-12-07 22:46:02 +01:00
- ✅ Proxy + webhook helpers (setWebhook/deleteWebhook, health endpoint, optional public URL)
- ⏳ Add more grammY coverage (webhook payloads, media edge cases)
## Safety & ops
2026-01-04 14:32:47 +00:00
- Treat the bot token as a secret (equivalent to account control); prefer `TELEGRAM_BOT_TOKEN` or a locked-down config file (`chmod 600 ~/.clawdbot/clawdbot.json` ).
2025-12-14 00:49:18 +00:00
- Respect Telegram rate limits (429s); grammY throttling is enabled by default.
2025-12-07 22:46:02 +01:00
- Use a test bot for development to avoid hitting production chats.