Files
openclaw/docs/channels/telegram.md

915 lines
32 KiB
Markdown
Raw Normal View History

---
summary: "Telegram bot support status, capabilities, and configuration"
read_when:
- Working on Telegram features or webhooks
title: "Telegram"
---
2026-01-31 21:13:13 +09:00
# Telegram (Bot API)
2026-01-07 00:25:16 +01:00
Status: production-ready for bot DMs + groups via grammY. Long polling is the default mode; webhook mode is optional.
2026-01-07 00:25:16 +01:00
<CardGroup cols={3}>
<Card title="Pairing" icon="link" href="/channels/pairing">
Default DM policy for Telegram is pairing.
</Card>
<Card title="Channel troubleshooting" icon="wrench" href="/channels/troubleshooting">
Cross-channel diagnostics and repair playbooks.
</Card>
<Card title="Gateway configuration" icon="settings" href="/gateway/configuration">
Full channel config patterns and examples.
</Card>
</CardGroup>
2026-02-01 18:51:44 +00:00
## Quick setup
<Steps>
<Step title="Create the bot token in BotFather">
Open Telegram and chat with **@BotFather** (confirm the handle is exactly `@BotFather`).
2026-01-31 21:13:13 +09:00
Run `/newbot`, follow prompts, and save the token.
</Step>
2026-01-31 21:13:13 +09:00
<Step title="Configure token and DM policy">
2026-01-07 00:25:16 +01:00
```json5
{
channels: {
telegram: {
enabled: true,
botToken: "123:abc",
dmPolicy: "pairing",
2026-01-31 21:13:13 +09:00
groups: { "*": { requireMention: true } },
},
},
}
```
2026-01-07 00:25:16 +01:00
Env fallback: `TELEGRAM_BOT_TOKEN=...` (default account only).
Telegram does **not** use `openclaw channels login telegram`; configure token in config/env, then start gateway.
</Step>
<Step title="Start gateway and approve first DM">
2026-01-08 09:33:35 +00:00
```bash
openclaw gateway
openclaw pairing list telegram
openclaw pairing approve telegram <CODE>
```
2026-01-08 09:33:35 +00:00
Pairing codes expire after 1 hour.
2026-01-31 21:13:13 +09:00
</Step>
2026-01-08 09:33:35 +00:00
<Step title="Add the bot to a group">
Add the bot to your group, then set `channels.telegram.groups` and `groupPolicy` to match your access model.
</Step>
</Steps>
2026-01-31 21:13:13 +09:00
<Note>
Token resolution order is account-aware. In practice, config values win over env fallback, and `TELEGRAM_BOT_TOKEN` only applies to the default account.
</Note>
2026-01-31 21:13:13 +09:00
## Telegram side settings
2026-01-08 09:33:35 +00:00
<AccordionGroup>
<Accordion title="Privacy mode and group visibility">
Telegram bots default to **Privacy Mode**, which limits what group messages they receive.
2026-01-08 09:33:35 +00:00
If the bot must see all group messages, either:
2026-01-31 21:13:13 +09:00
- disable privacy mode via `/setprivacy`, or
- make the bot a group admin.
2026-01-07 00:25:16 +01:00
When toggling privacy mode, remove + re-add the bot in each group so Telegram applies the change.
2026-01-31 21:13:13 +09:00
</Accordion>
2026-01-07 00:25:16 +01:00
<Accordion title="Group permissions">
Admin status is controlled in Telegram group settings.
Admin bots receive all group messages, which is useful for always-on group behavior.
</Accordion>
<Accordion title="Helpful BotFather toggles">
- `/setjoingroups` to allow/deny group adds
- `/setprivacy` for group visibility behavior
</Accordion>
</AccordionGroup>
2026-01-31 21:13:13 +09:00
## Access control and activation
<Tabs>
<Tab title="DM policy">
`channels.telegram.dmPolicy` controls direct message access:
2026-01-31 21:13:13 +09:00
- `pairing` (default)
- `allowlist` (requires at least one sender ID in `allowFrom`)
- `open` (requires `allowFrom` to include `"*"`)
- `disabled`
`channels.telegram.allowFrom` accepts numeric Telegram user IDs. `telegram:` / `tg:` prefixes are accepted and normalized.
`dmPolicy: "allowlist"` with empty `allowFrom` blocks all DMs and is rejected by config validation.
The onboarding wizard accepts `@username` input and resolves it to numeric IDs.
If you upgraded and your config contains `@username` allowlist entries, run `openclaw doctor --fix` to resolve them (best-effort; requires a Telegram bot token).
If you previously relied on pairing-store allowlist files, `openclaw doctor --fix` can recover entries into `channels.telegram.allowFrom` in allowlist flows (for example when `dmPolicy: "allowlist"` has no explicit IDs yet).
For one-owner bots, prefer `dmPolicy: "allowlist"` with explicit numeric `allowFrom` IDs to keep access policy durable in config (instead of depending on previous pairing approvals).
### Finding your Telegram user ID
Safer (no third-party bot):
1. DM your bot.
2. Run `openclaw logs --follow`.
3. Read `from.id`.
Official Bot API method:
```bash
curl "https://api.telegram.org/bot<bot_token>/getUpdates"
```
Third-party method (less private): `@userinfobot` or `@getidsbot`.
</Tab>
<Tab title="Group policy and allowlists">
Two controls apply together:
2026-01-31 21:13:13 +09:00
1. **Which groups are allowed** (`channels.telegram.groups`)
- no `groups` config:
- with `groupPolicy: "open"`: any group can pass group-ID checks
- with `groupPolicy: "allowlist"` (default): groups are blocked until you add `groups` entries (or `"*"`)
- `groups` configured: acts as allowlist (explicit IDs or `"*"`)
2026-01-08 04:02:04 +01:00
2. **Which senders are allowed in groups** (`channels.telegram.groupPolicy`)
- `open`
- `allowlist` (default)
- `disabled`
`groupAllowFrom` is used for group sender filtering. If not set, Telegram falls back to `allowFrom`.
`groupAllowFrom` entries should be numeric Telegram user IDs (`telegram:` / `tg:` prefixes are normalized).
Non-numeric entries are ignored for sender authorization.
Security boundary (`2026.2.25+`): group sender auth does **not** inherit DM pairing-store approvals.
Pairing stays DM-only. For groups, set `groupAllowFrom` or per-group/per-topic `allowFrom`.
Runtime note: if `channels.telegram` is completely missing, runtime defaults to fail-closed `groupPolicy="allowlist"` unless `channels.defaults.groupPolicy` is explicitly set.
Example: allow any member in one specific group:
```json5
{
channels: {
telegram: {
groups: {
"-1001234567890": {
groupPolicy: "open",
requireMention: false,
},
2026-01-31 21:13:13 +09:00
},
},
},
}
```
</Tab>
<Tab title="Mention behavior">
Group replies require mention by default.
2026-01-31 21:13:13 +09:00
Mention can come from:
- native `@botusername` mention, or
- mention patterns in:
- `agents.list[].groupChat.mentionPatterns`
- `messages.groupChat.mentionPatterns`
Session-level command toggles:
- `/activation always`
- `/activation mention`
These update session state only. Use config for persistence.
Persistent config example:
2026-01-31 21:13:13 +09:00
```json5
{
channels: {
telegram: {
groups: {
"*": { requireMention: false },
2026-01-31 21:13:13 +09:00
},
},
},
}
```
Getting the group chat ID:
- forward a group message to `@userinfobot` / `@getidsbot`
- or read `chat.id` from `openclaw logs --follow`
- or inspect Bot API `getUpdates`
</Tab>
</Tabs>
## Runtime behavior
- Telegram is owned by the gateway process.
- Routing is deterministic: Telegram inbound replies back to Telegram (the model does not pick channels).
- Inbound messages normalize into the shared channel envelope with reply metadata and media placeholders.
- Group sessions are isolated by group ID. Forum topics append `:topic:<threadId>` to keep topics isolated.
- DM messages can carry `message_thread_id`; OpenClaw routes them with thread-aware session keys and preserves thread ID for replies.
- Long polling uses grammY runner with per-chat/per-thread sequencing. Overall runner sink concurrency uses `agents.defaults.maxConcurrent`.
- Telegram Bot API has no read-receipt support (`sendReadReceipts` does not apply).
## Feature reference
<AccordionGroup>
<Accordion title="Live stream preview (native drafts + message edits)">
OpenClaw can stream partial replies in real time:
- direct chats: Telegram native draft streaming via `sendMessageDraft`
- groups/topics: preview message + `editMessageText`
Requirement:
- `channels.telegram.streaming` is `off | partial | block | progress` (default: `partial`)
- `progress` maps to `partial` on Telegram (compat with cross-channel naming)
- legacy `channels.telegram.streamMode` and boolean `streaming` values are auto-mapped
Telegram enabled `sendMessageDraft` for all bots in Bot API 9.5 (March 1, 2026).
For text-only replies:
- DM: OpenClaw updates the draft in place (no extra preview message)
- group/topic: OpenClaw keeps the same preview message and performs a final edit in place (no second message)
For complex replies (for example media payloads), OpenClaw falls back to normal final delivery and then cleans up the preview message.
Preview streaming is separate from block streaming. When block streaming is explicitly enabled for Telegram, OpenClaw skips the preview stream to avoid double-streaming.
If native draft transport is unavailable/rejected, OpenClaw automatically falls back to `sendMessage` + `editMessageText`.
Telegram-only reasoning stream:
2026-01-31 21:13:13 +09:00
- `/reasoning stream` sends reasoning to the live preview while generating
- final answer is sent without reasoning text
</Accordion>
2026-01-31 21:13:13 +09:00
<Accordion title="Formatting and HTML fallback">
Outbound text uses Telegram `parse_mode: "HTML"`.
- Markdown-ish text is rendered to Telegram-safe HTML.
- Raw model HTML is escaped to reduce Telegram parse failures.
- If Telegram rejects parsed HTML, OpenClaw retries as plain text.
Link previews are enabled by default and can be disabled with `channels.telegram.linkPreview: false`.
</Accordion>
<Accordion title="Native commands and custom commands">
Telegram command menu registration is handled at startup with `setMyCommands`.
Native command defaults:
- `commands.native: "auto"` enables native commands for Telegram
Add custom command menu entries:
2026-01-31 21:13:13 +09:00
```json5
{
channels: {
telegram: {
customCommands: [
{ command: "backup", description: "Git backup" },
{ command: "generate", description: "Create an image" },
],
},
},
}
```
Rules:
- names are normalized (strip leading `/`, lowercase)
- valid pattern: `a-z`, `0-9`, `_`, length `1..32`
- custom commands cannot override native commands
- conflicts/duplicates are skipped and logged
Notes:
- custom commands are menu entries only; they do not auto-implement behavior
- plugin/skill commands can still work when typed even if not shown in Telegram menu
If native commands are disabled, built-ins are removed. Custom/plugin commands may still register if configured.
Common setup failure:
- `setMyCommands failed` usually means outbound DNS/HTTPS to `api.telegram.org` is blocked.
2026-01-31 21:13:13 +09:00
### Device pairing commands (`device-pair` plugin)
2026-01-31 21:13:13 +09:00
When the `device-pair` plugin is installed:
1. `/pair` generates setup code
2. paste code in iOS app
3. `/pair approve` approves latest pending request
2026-01-07 11:08:11 +01:00
More details: [Pairing](/channels/pairing#pair-via-telegram-recommended-for-ios).
</Accordion>
<Accordion title="Inline buttons">
Configure inline keyboard scope:
```json5
{
2026-01-31 21:13:13 +09:00
channels: {
telegram: {
capabilities: {
inlineButtons: "allowlist",
},
},
},
}
```
Per-account override:
2026-01-31 21:13:13 +09:00
```json5
{
2026-01-31 21:13:13 +09:00
channels: {
telegram: {
accounts: {
main: {
capabilities: {
inlineButtons: "allowlist",
},
},
},
},
},
}
```
Scopes:
2026-01-16 20:16:35 +00:00
- `off`
- `dm`
- `group`
- `all`
- `allowlist` (default)
2026-01-16 20:16:35 +00:00
Legacy `capabilities: ["inlineButtons"]` maps to `inlineButtons: "all"`.
Message action example:
```json5
{
2026-01-31 21:13:13 +09:00
action: "send",
channel: "telegram",
to: "123456789",
message: "Choose an option:",
buttons: [
[
2026-01-31 21:13:13 +09:00
{ text: "Yes", callback_data: "yes" },
{ text: "No", callback_data: "no" },
],
2026-01-31 21:13:13 +09:00
[{ text: "Cancel", callback_data: "cancel" }],
],
}
```
Callback clicks are passed to the agent as text:
`callback_data: <value>`
2026-01-31 21:13:13 +09:00
</Accordion>
2026-01-31 21:13:13 +09:00
<Accordion title="Telegram message actions for agents and automation">
Telegram tool actions include:
- `sendMessage` (`to`, `content`, optional `mediaUrl`, `replyToMessageId`, `messageThreadId`)
- `react` (`chatId`, `messageId`, `emoji`)
- `deleteMessage` (`chatId`, `messageId`)
- `editMessage` (`chatId`, `messageId`, `content`)
- `createForumTopic` (`chatId`, `name`, optional `iconColor`, `iconCustomEmojiId`)
2026-01-31 21:13:13 +09:00
Channel message actions expose ergonomic aliases (`send`, `react`, `delete`, `edit`, `sticker`, `sticker-search`, `topic-create`).
Gating controls:
- `channels.telegram.actions.sendMessage`
- `channels.telegram.actions.deleteMessage`
- `channels.telegram.actions.reactions`
- `channels.telegram.actions.sticker` (default: disabled)
2026-01-31 21:13:13 +09:00
Note: `edit` and `topic-create` are currently enabled by default and do not have separate `channels.telegram.actions.*` toggles.
Reaction removal semantics: [/tools/reactions](/tools/reactions)
2026-01-07 00:25:16 +01:00
</Accordion>
<Accordion title="Reply threading tags">
Telegram supports explicit reply threading tags in generated output:
- `[[reply_to_current]]` replies to the triggering message
- `[[reply_to:<id>]]` replies to a specific Telegram message ID
2026-01-31 21:13:13 +09:00
`channels.telegram.replyToMode` controls handling:
- `off` (default)
- `first`
- `all`
2026-01-31 21:13:13 +09:00
Note: `off` disables implicit reply threading. Explicit `[[reply_to_*]]` tags are still honored.
</Accordion>
<Accordion title="Forum topics and thread behavior">
Forum supergroups:
fix(telegram): accept messages from group members in allowlisted groups (#9775) * fix(telegram): accept messages from group members in allowlisted groups Issue #4559: Telegram bot was silently dropping messages from non-paired users in allowlisted group chats due to overly strict sender filtering. The fix adds a check to distinguish between: 1. Group itself is allowlisted → accept messages from any member 2. Group is NOT allowlisted → only accept from allowlisted senders Changes: - Check if group ID is in the allowlist (or allowlist is wildcard) - Only reject sender if they're not in allowlist AND group is not allowlisted - Improved logging to indicate the actual reason for rejection This preserves security controls while fixing the UX issue where group members couldn't participate unless individually allowlisted. Backwards compatible: existing allowlists continue to work as before. * style: format telegram fix for oxfmt compliance * refactor(telegram): clarify group allowlist semantics in fix for #4559 Changes: - Rename 'isGroupInAllowlist' to 'isGroupChatIdInAllowlist' for clarity - Expand comments to explain the semantic distinction: * Group chat ID in allowlist -> accept any group member (fixes #4559) * Group chat ID NOT in allowlist -> enforce sender allowlist (preserves security) - This addresses concerns about config semantics raised in code review The fix maintains backward compatibility: - 'groupAllowFrom' with group chat IDs now correctly acts as group enablement - 'groupAllowFrom' with sender IDs continues to work as sender allowlist - Operators should use group chat IDs for group enablement, sender IDs for sender control Note: If operators were using 'groupAllowFrom' with group IDs expecting sender-level filtering, they should migrate to a separate sender allowlist config. This is the intended behavior per issue #4559. * Telegram: allow per-group groupPolicy overrides * Telegram: support per-group groupPolicy overrides (#9775) (thanks @nicolasstanley) --------- Co-authored-by: George Pickett <gpickett00@gmail.com>
2026-02-05 23:45:45 +01:00
- topic session keys append `:topic:<threadId>`
- replies and typing target the topic thread
- topic config path:
`channels.telegram.groups.<chatId>.topics.<threadId>`
2026-01-31 21:13:13 +09:00
General topic (`threadId=1`) special-case:
2026-01-07 00:25:16 +01:00
- message sends omit `message_thread_id` (Telegram rejects `sendMessage(...thread_id=1)`)
- typing actions still include `message_thread_id`
2026-01-31 21:13:13 +09:00
Topic inheritance: topic entries inherit group settings unless overridden (`requireMention`, `allowFrom`, `skills`, `systemPrompt`, `enabled`, `groupPolicy`).
`agentId` is topic-only and does not inherit from group defaults.
**Per-topic agent routing**: Each topic can route to a different agent by setting `agentId` in the topic config. This gives each topic its own isolated workspace, memory, and session. Example:
```json5
{
channels: {
telegram: {
groups: {
"-1001234567890": {
topics: {
"1": { agentId: "main" }, // General topic → main agent
"3": { agentId: "zu" }, // Dev topic → zu agent
"5": { agentId: "coder" } // Code review → coder agent
}
}
}
}
}
}
```
Each topic then has its own session key: `agent:zu:telegram:group:-1001234567890:topic:3`
2026-01-31 21:13:13 +09:00
ACP: add persistent Discord channel and Telegram topic bindings (#34873) * docs: add ACP persistent binding experiment plan * docs: align ACP persistent binding spec to channel-local config * docs: scope Telegram ACP bindings to forum topics only * docs: lock bound /new and /reset behavior to in-place ACP reset * ACP: add persistent discord/telegram conversation bindings * ACP: fix persistent binding reuse and discord thread parent context * docs: document channel-specific persistent ACP bindings * ACP: split persistent bindings and share conversation id helpers * ACP: defer configured binding init until preflight passes * ACP: fix discord thread parent fallback and explicit disable inheritance * ACP: keep bound /new and /reset in-place * ACP: honor configured bindings in native command flows * ACP: avoid configured fallback after runtime bind failure * docs: refine ACP bindings experiment config examples * acp: cut over to typed top-level persistent bindings * ACP bindings: harden reset recovery and native command auth * Docs: add ACP bound command auth proposal * Tests: normalize i18n registry zh-CN assertion encoding * ACP bindings: address review findings for reset and fallback routing * ACP reset: gate hooks on success and preserve /new arguments * ACP bindings: fix auth and binding-priority review findings * Telegram ACP: gate ensure on auth and accepted messages * ACP bindings: fix session-key precedence and unavailable handling * ACP reset/native commands: honor fallback targets and abort on bootstrap failure * Config schema: validate ACP binding channel and Telegram topic IDs * Discord ACP: apply configured DM bindings to native commands * ACP reset tails: dispatch through ACP after command handling * ACP tails/native reset auth: fix target dispatch and restore full auth * ACP reset detection: fallback to active ACP keys for DM contexts * Tests: type runTurn mock input in ACP dispatch test * ACP: dedup binding route bootstrap and reset target resolution * reply: align ACP reset hooks with bound session key * docs: replace personal discord ids with placeholders * fix: add changelog entry for ACP persistent bindings (#34873) (thanks @dutifulbob) --------- Co-authored-by: Onur <2453968+osolmaz@users.noreply.github.com>
2026-03-05 09:38:12 +01:00
**Persistent ACP topic binding**: Forum topics can pin ACP harness sessions through top-level typed ACP bindings:
- `bindings[]` with `type: "acp"` and `match.channel: "telegram"`
Example:
```json5
{
agents: {
list: [
{
id: "codex",
runtime: {
type: "acp",
acp: {
agent: "codex",
backend: "acpx",
mode: "persistent",
cwd: "/workspace/openclaw",
},
},
},
],
},
bindings: [
{
type: "acp",
agentId: "codex",
match: {
channel: "telegram",
accountId: "default",
peer: { kind: "group", id: "-1001234567890:topic:42" },
},
},
],
channels: {
telegram: {
groups: {
"-1001234567890": {
topics: {
"42": {
requireMention: false,
},
},
},
},
},
},
}
```
This is currently scoped to forum topics in groups and supergroups.
**Thread-bound ACP spawn from chat**:
- `/acp spawn <agent> --thread here|auto` can bind the current Telegram topic to a new ACP session.
- Follow-up topic messages route to the bound ACP session directly (no `/acp steer` required).
- OpenClaw pins the spawn confirmation message in-topic after a successful bind.
- Requires `channels.telegram.threadBindings.spawnAcpSessions=true`.
Template context includes:
2026-01-07 00:25:16 +01:00
- `MessageThreadId`
- `IsForum`
2026-01-31 21:13:13 +09:00
DM thread behavior:
2026-01-07 00:25:16 +01:00
- private chats with `message_thread_id` keep DM routing but use thread-aware session keys/reply targets.
2026-01-31 21:13:13 +09:00
</Accordion>
<Accordion title="Audio, video, and stickers">
### Audio messages
2026-01-31 21:13:13 +09:00
Telegram distinguishes voice notes vs audio files.
- default: audio file behavior
- tag `[[audio_as_voice]]` in agent reply to force voice-note send
Message action example:
```json5
{
2026-01-31 21:13:13 +09:00
action: "send",
channel: "telegram",
to: "123456789",
media: "https://example.com/voice.ogg",
asVoice: true,
}
```
### Video messages
Telegram distinguishes video files vs video notes.
Message action example:
```json5
{
action: "send",
channel: "telegram",
to: "123456789",
media: "https://example.com/video.mp4",
asVideoNote: true,
}
```
Video notes do not support captions; provided message text is sent separately.
### Stickers
Inbound sticker handling:
2026-01-31 21:13:13 +09:00
- static WEBP: downloaded and processed (placeholder `<media:sticker>`)
- animated TGS: skipped
- video WEBM: skipped
Sticker context fields:
- `Sticker.emoji`
- `Sticker.setName`
- `Sticker.fileId`
- `Sticker.fileUniqueId`
- `Sticker.cachedDescription`
Sticker cache file:
- `~/.openclaw/telegram/sticker-cache.json`
Stickers are described once (when possible) and cached to reduce repeated vision calls.
Enable sticker actions:
```json5
{
channels: {
telegram: {
actions: {
2026-01-31 21:13:13 +09:00
sticker: true,
},
},
},
}
```
Send sticker action:
```json5
{
2026-01-31 21:13:13 +09:00
action: "sticker",
channel: "telegram",
to: "123456789",
fileId: "CAACAgIAAxkBAAI...",
}
```
Search cached stickers:
```json5
{
2026-01-31 21:13:13 +09:00
action: "sticker-search",
channel: "telegram",
query: "cat waving",
limit: 5,
}
```
</Accordion>
2026-01-31 21:13:13 +09:00
<Accordion title="Reaction notifications">
Telegram reactions arrive as `message_reaction` updates (separate from message payloads).
When enabled, OpenClaw enqueues system events like:
- `Telegram reaction added: 👍 by Alice (@alice) on msg 42`
Config:
- `channels.telegram.reactionNotifications`: `off | own | all` (default: `own`)
- `channels.telegram.reactionLevel`: `off | ack | minimal | extensive` (default: `minimal`)
Notes:
- `own` means user reactions to bot-sent messages only (best-effort via sent-message cache).
- Reaction events still respect Telegram access controls (`dmPolicy`, `allowFrom`, `groupPolicy`, `groupAllowFrom`); unauthorized senders are dropped.
- Telegram does not provide thread IDs in reaction updates.
- non-forum groups route to group chat session
- forum groups route to the group general-topic session (`:topic:1`), not the exact originating topic
`allowed_updates` for polling/webhook include `message_reaction` automatically.
</Accordion>
<Accordion title="Ack reactions">
`ackReaction` sends an acknowledgement emoji while OpenClaw is processing an inbound message.
Resolution order:
- `channels.telegram.accounts.<accountId>.ackReaction`
- `channels.telegram.ackReaction`
- `messages.ackReaction`
- agent identity emoji fallback (`agents.list[].identity.emoji`, else "👀")
Notes:
- Telegram expects unicode emoji (for example "👀").
- Use `""` to disable the reaction for a channel or account.
</Accordion>
<Accordion title="Config writes from Telegram events and commands">
Channel config writes are enabled by default (`configWrites !== false`).
Telegram-triggered writes include:
- group migration events (`migrate_to_chat_id`) to update `channels.telegram.groups`
- `/config set` and `/config unset` (requires command enablement)
Disable:
```json5
{
channels: {
telegram: {
configWrites: false,
},
},
}
```
</Accordion>
2026-01-31 21:13:13 +09:00
<Accordion title="Long polling vs webhook">
Default: long polling.
2026-01-07 11:08:11 +01:00
Webhook mode:
2026-01-31 21:13:13 +09:00
- set `channels.telegram.webhookUrl`
- set `channels.telegram.webhookSecret` (required when webhook URL is set)
- optional `channels.telegram.webhookPath` (default `/telegram-webhook`)
- optional `channels.telegram.webhookHost` (default `127.0.0.1`)
- optional `channels.telegram.webhookPort` (default `8787`)
2026-01-07 11:08:11 +01:00
Default local listener for webhook mode binds to `127.0.0.1:8787`.
2026-01-31 21:13:13 +09:00
If your public endpoint differs, place a reverse proxy in front and point `webhookUrl` at the public URL.
Set `webhookHost` (for example `0.0.0.0`) when you intentionally need external ingress.
2026-01-07 11:08:11 +01:00
</Accordion>
<Accordion title="Limits, retry, and CLI targets">
- `channels.telegram.textChunkLimit` default is 4000.
- `channels.telegram.chunkMode="newline"` prefers paragraph boundaries (blank lines) before length splitting.
- `channels.telegram.mediaMaxMb` (default 100) caps inbound and outbound Telegram media size.
- `channels.telegram.timeoutSeconds` overrides Telegram API client timeout (if unset, grammY default applies).
- group context history uses `channels.telegram.historyLimit` or `messages.groupChat.historyLimit` (default 50); `0` disables.
- DM history controls:
- `channels.telegram.dmHistoryLimit`
- `channels.telegram.dms["<user_id>"].historyLimit`
- `channels.telegram.retry` config applies to Telegram send helpers (CLI/tools/actions) for recoverable outbound API errors.
2026-01-31 21:13:13 +09:00
CLI send target can be numeric chat ID or username:
2026-01-07 11:08:11 +01:00
```bash
openclaw message send --channel telegram --target 123456789 --message "hi"
openclaw message send --channel telegram --target @name --message "hi"
```
2026-01-31 21:13:13 +09:00
Telegram polls use `openclaw message poll` and support forum topics:
```bash
openclaw message poll --channel telegram --target 123456789 \
--poll-question "Ship it?" --poll-option "Yes" --poll-option "No"
openclaw message poll --channel telegram --target -1001234567890:topic:42 \
--poll-question "Pick a time" --poll-option "10am" --poll-option "2pm" \
--poll-duration-seconds 300 --poll-public
```
Telegram-only poll flags:
- `--poll-duration-seconds` (5-600)
- `--poll-anonymous`
- `--poll-public`
- `--thread-id` for forum topics (or use a `:topic:` target)
Action gating:
- `channels.telegram.actions.sendMessage=false` disables outbound Telegram messages, including polls
- `channels.telegram.actions.poll=false` disables Telegram poll creation while leaving regular sends enabled
</Accordion>
</AccordionGroup>
2026-01-07 17:48:19 +00:00
## Troubleshooting
2026-01-31 21:13:13 +09:00
<AccordionGroup>
<Accordion title="Bot does not respond to non mention group messages">
2026-01-07 04:10:13 +01:00
- If `requireMention=false`, Telegram privacy mode must allow full visibility.
- BotFather: `/setprivacy` -> Disable
- then remove + re-add bot to group
- `openclaw channels status` warns when config expects unmentioned group messages.
- `openclaw channels status --probe` can check explicit numeric group IDs; wildcard `"*"` cannot be membership-probed.
- quick session test: `/activation always`.
</Accordion>
<Accordion title="Bot not seeing group messages at all">
- when `channels.telegram.groups` exists, group must be listed (or include `"*"`)
- verify bot membership in group
- review logs: `openclaw logs --follow` for skip reasons
</Accordion>
2026-01-31 21:13:13 +09:00
<Accordion title="Commands work partially or not at all">
- authorize your sender identity (pairing and/or numeric `allowFrom`)
- command authorization still applies even when group policy is `open`
- `setMyCommands failed` usually indicates DNS/HTTPS reachability issues to `api.telegram.org`
</Accordion>
<Accordion title="Polling or network instability">
2026-01-31 21:13:13 +09:00
- Node 22+ + custom fetch/proxy can trigger immediate abort behavior if AbortSignal types mismatch.
- Some hosts resolve `api.telegram.org` to IPv6 first; broken IPv6 egress can cause intermittent Telegram API failures.
- If logs include `TypeError: fetch failed` or `Network request for 'getUpdates' failed!`, OpenClaw now retries these as recoverable network errors.
- On VPS hosts with unstable direct egress/TLS, route Telegram API calls through `channels.telegram.proxy`:
```yaml
channels:
telegram:
proxy: socks5://user:pass@proxy-host:1080
```
- Node 22+ defaults to `autoSelectFamily=true` (except WSL2) and `dnsResultOrder=ipv4first`.
- If your host is WSL2 or explicitly works better with IPv4-only behavior, force family selection:
```yaml
channels:
telegram:
network:
autoSelectFamily: false
```
- Environment overrides (temporary):
- `OPENCLAW_TELEGRAM_DISABLE_AUTO_SELECT_FAMILY=1`
- `OPENCLAW_TELEGRAM_ENABLE_AUTO_SELECT_FAMILY=1`
- `OPENCLAW_TELEGRAM_DNS_RESULT_ORDER=ipv4first`
- Validate DNS answers:
```bash
dig +short api.telegram.org A
dig +short api.telegram.org AAAA
```
</Accordion>
</AccordionGroup>
2026-01-31 21:13:13 +09:00
More help: [Channel troubleshooting](/channels/troubleshooting).
## Telegram config reference pointers
2026-01-31 21:13:13 +09:00
Primary reference:
2026-01-07 00:25:16 +01:00
- `channels.telegram.enabled`: enable/disable channel startup.
- `channels.telegram.botToken`: bot token (BotFather).
- `channels.telegram.tokenFile`: read token from file path.
- `channels.telegram.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing).
- `channels.telegram.allowFrom`: DM allowlist (numeric Telegram user IDs). `allowlist` requires at least one sender ID. `open` requires `"*"`. `openclaw doctor --fix` can resolve legacy `@username` entries to IDs and can recover allowlist entries from pairing-store files in allowlist migration flows.
- `channels.telegram.actions.poll`: enable or disable Telegram poll creation (default: enabled; still requires `sendMessage`).
- `channels.telegram.defaultTo`: default Telegram target used by CLI `--deliver` when no explicit `--reply-to` is provided.
- `channels.telegram.groupPolicy`: `open | allowlist | disabled` (default: allowlist).
- `channels.telegram.groupAllowFrom`: group sender allowlist (numeric Telegram user IDs). `openclaw doctor --fix` can resolve legacy `@username` entries to IDs. Non-numeric entries are ignored at auth time. Group auth does not use DM pairing-store fallback (`2026.2.25+`).
- Multi-account precedence:
- When two or more account IDs are configured, set `channels.telegram.defaultAccount` (or include `channels.telegram.accounts.default`) to make default routing explicit.
- If neither is set, OpenClaw falls back to the first normalized account ID and `openclaw doctor` warns.
- `channels.telegram.accounts.default.allowFrom` and `channels.telegram.accounts.default.groupAllowFrom` apply only to the `default` account.
- Named accounts inherit `channels.telegram.allowFrom` and `channels.telegram.groupAllowFrom` when account-level values are unset.
- Named accounts do not inherit `channels.telegram.accounts.default.allowFrom` / `groupAllowFrom`.
- `channels.telegram.groups`: per-group defaults + allowlist (use `"*"` for global defaults).
- `channels.telegram.groups.<id>.groupPolicy`: per-group override for groupPolicy (`open | allowlist | disabled`).
- `channels.telegram.groups.<id>.requireMention`: mention gating default.
- `channels.telegram.groups.<id>.skills`: skill filter (omit = all skills, empty = none).
- `channels.telegram.groups.<id>.allowFrom`: per-group sender allowlist override.
- `channels.telegram.groups.<id>.systemPrompt`: extra system prompt for the group.
- `channels.telegram.groups.<id>.enabled`: disable the group when `false`.
- `channels.telegram.groups.<id>.topics.<threadId>.*`: per-topic overrides (group fields + topic-only `agentId`).
- `channels.telegram.groups.<id>.topics.<threadId>.agentId`: route this topic to a specific agent (overrides group-level and binding routing).
- `channels.telegram.groups.<id>.topics.<threadId>.groupPolicy`: per-topic override for groupPolicy (`open | allowlist | disabled`).
- `channels.telegram.groups.<id>.topics.<threadId>.requireMention`: per-topic mention gating override.
ACP: add persistent Discord channel and Telegram topic bindings (#34873) * docs: add ACP persistent binding experiment plan * docs: align ACP persistent binding spec to channel-local config * docs: scope Telegram ACP bindings to forum topics only * docs: lock bound /new and /reset behavior to in-place ACP reset * ACP: add persistent discord/telegram conversation bindings * ACP: fix persistent binding reuse and discord thread parent context * docs: document channel-specific persistent ACP bindings * ACP: split persistent bindings and share conversation id helpers * ACP: defer configured binding init until preflight passes * ACP: fix discord thread parent fallback and explicit disable inheritance * ACP: keep bound /new and /reset in-place * ACP: honor configured bindings in native command flows * ACP: avoid configured fallback after runtime bind failure * docs: refine ACP bindings experiment config examples * acp: cut over to typed top-level persistent bindings * ACP bindings: harden reset recovery and native command auth * Docs: add ACP bound command auth proposal * Tests: normalize i18n registry zh-CN assertion encoding * ACP bindings: address review findings for reset and fallback routing * ACP reset: gate hooks on success and preserve /new arguments * ACP bindings: fix auth and binding-priority review findings * Telegram ACP: gate ensure on auth and accepted messages * ACP bindings: fix session-key precedence and unavailable handling * ACP reset/native commands: honor fallback targets and abort on bootstrap failure * Config schema: validate ACP binding channel and Telegram topic IDs * Discord ACP: apply configured DM bindings to native commands * ACP reset tails: dispatch through ACP after command handling * ACP tails/native reset auth: fix target dispatch and restore full auth * ACP reset detection: fallback to active ACP keys for DM contexts * Tests: type runTurn mock input in ACP dispatch test * ACP: dedup binding route bootstrap and reset target resolution * reply: align ACP reset hooks with bound session key * docs: replace personal discord ids with placeholders * fix: add changelog entry for ACP persistent bindings (#34873) (thanks @dutifulbob) --------- Co-authored-by: Onur <2453968+osolmaz@users.noreply.github.com>
2026-03-05 09:38:12 +01:00
- top-level `bindings[]` with `type: "acp"` and canonical topic id `chatId:topic:topicId` in `match.peer.id`: persistent ACP topic binding fields (see [ACP Agents](/tools/acp-agents#channel-specific-settings)).
- `channels.telegram.direct.<id>.topics.<threadId>.agentId`: route DM topics to a specific agent (same behavior as forum topics).
- `channels.telegram.capabilities.inlineButtons`: `off | dm | group | all | allowlist` (default: allowlist).
- `channels.telegram.accounts.<account>.capabilities.inlineButtons`: per-account override.
- `channels.telegram.commands.nativeSkills`: enable/disable Telegram native skills commands.
- `channels.telegram.replyToMode`: `off | first | all` (default: `off`).
- `channels.telegram.textChunkLimit`: outbound chunk size (chars).
- `channels.telegram.chunkMode`: `length` (default) or `newline` to split on blank lines (paragraph boundaries) before length chunking.
- `channels.telegram.linkPreview`: toggle link previews for outbound messages (default: true).
- `channels.telegram.streaming`: `off | partial | block | progress` (live stream preview; default: `partial`; `progress` maps to `partial`; `block` is legacy preview mode compatibility). In DMs, `partial` uses native `sendMessageDraft` when available.
- `channels.telegram.mediaMaxMb`: inbound/outbound Telegram media cap (MB, default: 100).
- `channels.telegram.retry`: retry policy for Telegram send helpers (CLI/tools/actions) on recoverable outbound API errors (attempts, minDelayMs, maxDelayMs, jitter).
- `channels.telegram.network.autoSelectFamily`: override Node autoSelectFamily (true=enable, false=disable). Defaults to enabled on Node 22+, with WSL2 defaulting to disabled.
- `channels.telegram.network.dnsResultOrder`: override DNS result order (`ipv4first` or `verbatim`). Defaults to `ipv4first` on Node 22+.
- `channels.telegram.proxy`: proxy URL for Bot API calls (SOCKS/HTTP).
- `channels.telegram.webhookUrl`: enable webhook mode (requires `channels.telegram.webhookSecret`).
- `channels.telegram.webhookSecret`: webhook secret (required when webhookUrl is set).
- `channels.telegram.webhookPath`: local webhook path (default `/telegram-webhook`).
- `channels.telegram.webhookHost`: local webhook bind host (default `127.0.0.1`).
- `channels.telegram.webhookPort`: local webhook bind port (default `8787`).
- `channels.telegram.actions.reactions`: gate Telegram tool reactions.
- `channels.telegram.actions.sendMessage`: gate Telegram tool message sends.
- `channels.telegram.actions.deleteMessage`: gate Telegram tool message deletes.
- `channels.telegram.actions.sticker`: gate Telegram sticker actions — send and search (default: false).
- `channels.telegram.reactionNotifications`: `off | own | all` — control which reactions trigger system events (default: `own` when not set).
- `channels.telegram.reactionLevel`: `off | ack | minimal | extensive` — control agent's reaction capability (default: `minimal` when not set).
- [Configuration reference - Telegram](/gateway/configuration-reference#telegram)
Telegram-specific high-signal fields:
- startup/auth: `enabled`, `botToken`, `tokenFile`, `accounts.*`
ACP: add persistent Discord channel and Telegram topic bindings (#34873) * docs: add ACP persistent binding experiment plan * docs: align ACP persistent binding spec to channel-local config * docs: scope Telegram ACP bindings to forum topics only * docs: lock bound /new and /reset behavior to in-place ACP reset * ACP: add persistent discord/telegram conversation bindings * ACP: fix persistent binding reuse and discord thread parent context * docs: document channel-specific persistent ACP bindings * ACP: split persistent bindings and share conversation id helpers * ACP: defer configured binding init until preflight passes * ACP: fix discord thread parent fallback and explicit disable inheritance * ACP: keep bound /new and /reset in-place * ACP: honor configured bindings in native command flows * ACP: avoid configured fallback after runtime bind failure * docs: refine ACP bindings experiment config examples * acp: cut over to typed top-level persistent bindings * ACP bindings: harden reset recovery and native command auth * Docs: add ACP bound command auth proposal * Tests: normalize i18n registry zh-CN assertion encoding * ACP bindings: address review findings for reset and fallback routing * ACP reset: gate hooks on success and preserve /new arguments * ACP bindings: fix auth and binding-priority review findings * Telegram ACP: gate ensure on auth and accepted messages * ACP bindings: fix session-key precedence and unavailable handling * ACP reset/native commands: honor fallback targets and abort on bootstrap failure * Config schema: validate ACP binding channel and Telegram topic IDs * Discord ACP: apply configured DM bindings to native commands * ACP reset tails: dispatch through ACP after command handling * ACP tails/native reset auth: fix target dispatch and restore full auth * ACP reset detection: fallback to active ACP keys for DM contexts * Tests: type runTurn mock input in ACP dispatch test * ACP: dedup binding route bootstrap and reset target resolution * reply: align ACP reset hooks with bound session key * docs: replace personal discord ids with placeholders * fix: add changelog entry for ACP persistent bindings (#34873) (thanks @dutifulbob) --------- Co-authored-by: Onur <2453968+osolmaz@users.noreply.github.com>
2026-03-05 09:38:12 +01:00
- access control: `dmPolicy`, `allowFrom`, `groupPolicy`, `groupAllowFrom`, `groups`, `groups.*.topics.*`, top-level `bindings[]` (`type: "acp"`)
- command/menu: `commands.native`, `commands.nativeSkills`, `customCommands`
- threading/replies: `replyToMode`
- streaming: `streaming` (preview), `blockStreaming`
- formatting/delivery: `textChunkLimit`, `chunkMode`, `linkPreview`, `responsePrefix`
- media/network: `mediaMaxMb`, `timeoutSeconds`, `retry`, `network.autoSelectFamily`, `proxy`
- webhook: `webhookUrl`, `webhookSecret`, `webhookPath`, `webhookHost`
- actions/capabilities: `capabilities.inlineButtons`, `actions.sendMessage|editMessage|deleteMessage|reactions|sticker`
- reactions: `reactionNotifications`, `reactionLevel`
- writes/history: `configWrites`, `historyLimit`, `dmHistoryLimit`, `dms.*.historyLimit`
## Related
- [Pairing](/channels/pairing)
- [Channel routing](/channels/channel-routing)
2026-02-17 14:27:52 -06:00
- [Multi-agent routing](/concepts/multi-agent)
- [Troubleshooting](/channels/troubleshooting)