11 KiB
summary, read_when
| summary | read_when | |
|---|---|---|
| Telegram bot support status, capabilities, and configuration |
|
Telegram (Bot API)
Updated: 2026-01-08
Status: production-ready for bot DMs + groups via grammY. Long-polling by default; webhook optional.
What it is
- A Telegram Bot API provider owned by the Gateway.
- Deterministic routing: replies go back to Telegram; the model never chooses providers.
- DMs share the agent's main session; groups stay isolated (
telegram:group:<chatId>).
Setup (fast path)
- Create a bot with @BotFather and copy the token.
- Configure the token (env or config). Example:
{
telegram: {
enabled: true,
botToken: "123:abc",
dmPolicy: "pairing",
groups: { "*": { requireMention: true } }
}
}
Multi-account support: use telegram.accounts with per-account tokens and optional name. See gateway/configuration for the shared pattern.
- Start the gateway. Telegram starts when a
telegramconfig section exists and a token is resolved. - DM access defaults to pairing. Approve the code when the bot is first contacted.
- For groups: add the bot, disable privacy mode (or make it admin), then set
telegram.groupsto control mention gating + allowlists.
How it works (behavior)
- Inbound messages are normalized into the shared provider envelope with reply context and media placeholders.
- Group replies require a mention by default (native @mention or
routing.groupChat.mentionPatterns). - Replies always route back to the same Telegram chat.
- Long-polling uses grammY runner with per-chat sequencing; overall concurrency is capped by
agent.maxConcurrent.
Formatting (Telegram HTML)
- Outbound Telegram text uses
parse_mode: "HTML"(Telegram’s supported tag subset). - Markdown-ish input is rendered into Telegram-safe HTML (bold/italic/strike/code/links); block elements are flattened to text with newlines/bullets.
- Raw HTML from models is escaped to avoid Telegram parse errors.
- If Telegram rejects the HTML payload, Clawdbot retries the same message as plain text.
Limits
- Outbound text is chunked to
telegram.textChunkLimit(default 4000). - Media downloads/uploads are capped by
telegram.mediaMaxMb(default 5).
Group activation modes
By default, the bot only responds to mentions in groups (@botname or patterns in routing.groupChat.mentionPatterns). To change this behavior:
Via config (recommended)
{
telegram: {
groups: {
"-1001234567890": { requireMention: false } // always respond in this group
}
}
}
Important: Setting telegram.groups creates an allowlist - only listed groups (or "*") will be accepted.
To allow all groups with always-respond:
{
telegram: {
groups: {
"*": { requireMention: false } // all groups, always respond
}
}
}
To keep mention-only for all groups (default behavior):
{
telegram: {
groups: {
"*": { requireMention: true } // or omit groups entirely
}
}
}
Via command (session-level)
Send in the group:
/activation always- respond to all messages/activation mention- require mentions (default)
Note: Commands update session state only. For persistent behavior across restarts, use config.
Getting the group chat ID
Forward any message from the group to @userinfobot or @getidsbot on Telegram to see the chat ID (negative number like -1001234567890).
Topics (forum supergroups)
Telegram forum topics include a message_thread_id per message. Clawdbot:
- Appends
:topic:<threadId>to the Telegram group session key so each topic is isolated. - Sends typing indicators and replies with
message_thread_idso responses stay in the topic. - Exposes
MessageThreadId+IsForumin template context for routing/templating. - Topic-specific configuration is available under
telegram.groups.<chatId>.topics.<threadId>(skills, allowlists, auto-reply, system prompts, disable).
Private topics (DM forum mode) also include message_thread_id. Clawdbot:
- Appends
:topic:<threadId>to DM session keys for isolation. - Uses the thread id for draft streaming + replies.
Access control (DMs + groups)
DM access
- Default:
telegram.dmPolicy = "pairing". Unknown senders receive a pairing code; messages are ignored until approved (codes expire after 1 hour). - Approve via:
clawdbot pairing list --provider telegramclawdbot pairing approve --provider telegram <CODE>
- Pairing is the default token exchange used for Telegram DMs. Details: Pairing
Group access
Two independent controls:
1. Which groups are allowed (group allowlist via telegram.groups):
- No
groupsconfig = all groups allowed - With
groupsconfig = only listed groups or"*"are allowed - Example:
"groups": { "-1001234567890": {}, "*": {} }allows all groups
2. Which senders are allowed (sender filtering via telegram.groupPolicy):
"open"(default) = all senders in allowed groups can message"allowlist"= only senders intelegram.groupAllowFromcan message"disabled"= no group messages accepted at all
Most users want: groupPolicy: "open" + specific groups listed in telegram.groups
Long-polling vs webhook
- Default: long-polling (no public URL required).
- Webhook mode: set
telegram.webhookUrl(optionallytelegram.webhookSecret+telegram.webhookPath).- The local listener binds to
0.0.0.0:8787and servesPOST /telegram-webhookby default. - If your public URL is different, use a reverse proxy and point
telegram.webhookUrlat the public endpoint.
- The local listener binds to
Reply threading
Telegram supports optional threaded replies via tags:
[[reply_to_current]]-- reply to the triggering message.[[reply_to:<id>]]-- reply to a specific message id.
Controlled by telegram.replyToMode:
first(default),all,off.
Audio messages (voice vs file)
Telegram distinguishes voice notes (round bubble) from audio files (metadata card). Clawdbot defaults to audio files for backward compatibility.
To force a voice note bubble in agent replies, include this tag anywhere in the reply:
[[audio_as_voice]]— send audio as a voice note instead of a file.
The tag is stripped from the delivered text. Other providers ignore this tag.
Streaming (drafts)
Telegram can stream draft bubbles while the agent is generating a response.
Clawdbot uses Bot API sendMessageDraft (not real messages) and then sends the
final reply as a normal message.
Requirements (Telegram Bot API 9.3+):
- Private chats with topics enabled (forum topic mode for the bot).
- Incoming messages must include
message_thread_id(private topic thread). - Streaming is ignored for groups/supergroups/channels.
Config:
telegram.streamMode: "off" | "partial" | "block"(default:partial)partial: update the draft bubble with the latest streaming text.block: update the draft bubble in larger blocks (chunked).off: disable draft streaming.
Reasoning stream (Telegram only):
/reasoning streamstreams reasoning into the draft bubble while the reply is generating, then sends the final answer without reasoning.- If
telegram.streamModeisoff, reasoning stream is disabled. More context: Streaming + chunking.
Retry policy
Outbound Telegram API calls retry on transient network/429 errors with exponential backoff and jitter. Configure via telegram.retry. See Retry policy.
Agent tool (messages + reactions)
- Tool:
telegramwithsendMessageaction (to,content, optionalmediaUrl,replyToMessageId,messageThreadId). - Tool:
telegramwithreactaction (chatId,messageId,emoji). - Reaction removal semantics: see /tools/reactions.
- Tool gating:
telegram.actions.reactionsandtelegram.actions.sendMessage(default: enabled).
Delivery targets (CLI/cron)
- Use a chat id (
123456789) or a username (@name) as the target. - Example:
clawdbot send --provider telegram --to 123456789 "hi".
Troubleshooting
Bot doesn't respond to non-mention messages in group:
- Check if group is in
telegram.groupswithrequireMention: false - Or use
"*": { "requireMention": false }to enable for all groups - Test with
/activation alwayscommand (requires config change to persist)
Bot not seeing group messages at all:
- If
telegram.groupsis set, the group must be listed or use"*" - Check Privacy Settings in @BotFather → "Group Privacy" should be OFF
- Verify bot is actually a member (not just an admin with no read access)
- Check gateway logs:
journalctl --user -u clawdbot -f(look for "skipping group message")
Bot responds to mentions but not /activation always:
- The
/activationcommand updates session state but doesn't persist to config - For persistent behavior, add group to
telegram.groupswithrequireMention: false
Commands like /status don't work:
- Make sure your Telegram user ID is authorized (via pairing or
telegram.allowFrom) - Commands require authorization even in groups with
groupPolicy: "open"
Configuration reference (Telegram)
Full configuration: Configuration
Provider options:
telegram.enabled: enable/disable provider startup.telegram.botToken: bot token (BotFather).telegram.tokenFile: read token from file path.telegram.dmPolicy:pairing | allowlist | open | disabled(default: pairing).telegram.allowFrom: DM allowlist (ids/usernames).openrequires"*".telegram.groupPolicy:open | allowlist | disabled(default: open).telegram.groupAllowFrom: group sender allowlist (ids/usernames).telegram.groups: per-group defaults + allowlist (use"*"for global defaults).telegram.groups.<id>.requireMention: mention gating default.telegram.groups.<id>.skills: skill filter (omit = all skills, empty = none).telegram.groups.<id>.allowFrom: per-group sender allowlist override.telegram.groups.<id>.systemPrompt: extra system prompt for the group.telegram.groups.<id>.enabled: disable the group whenfalse.telegram.groups.<id>.topics.<threadId>.*: per-topic overrides (same fields as group).telegram.groups.<id>.topics.<threadId>.requireMention: per-topic mention gating override.
telegram.replyToMode:off | first | all(default:first).telegram.textChunkLimit: outbound chunk size (chars).telegram.streamMode:off | partial | block(draft streaming).telegram.mediaMaxMb: inbound/outbound media cap (MB).telegram.retry: retry policy for outbound Telegram API calls (attempts, minDelayMs, maxDelayMs, jitter).telegram.proxy: proxy URL for Bot API calls (SOCKS/HTTP).telegram.webhookUrl: enable webhook mode.telegram.webhookSecret: webhook secret (optional).telegram.webhookPath: local webhook path (default/telegram-webhook).telegram.actions.reactions: gate Telegram tool reactions.telegram.actions.sendMessage: gate Telegram tool message sends.
Related global options:
routing.groupChat.mentionPatterns(mention gating patterns).commands.native,commands.text,commands.useAccessGroups(command behavior).messages.responsePrefix,messages.ackReaction,messages.ackReactionScope.