跳转至

入站和出站 Telegram 消息如何被路由、预览、确认和投递

运行时行为

  • Telegram 消息处理在网关进程内运行。
  • 路由是确定性的:Telegram 入站消息回复回 Telegram(模型不会选择通道)。
  • 入站消息会规范化为共享通道信封,其中包含回复元数据、媒体占位符,以及网关观察到的回复的持久化回复链上下文。
  • 群组会话按群组 ID 隔离。论坛主题会追加 :topic:<threadId>。
  • 当机器人加入允许的群组或超级群组时,它会发布一条基于可用房间元数据的介绍:群组标题、描述和置顶消息。Telegram Bot API 无法读取机器人加入之前的群组消息,因此介绍绝不会声称使用先前的聊天历史。介绍默认启用,绝不在私聊中运行,可以通过 channels.telegram.joinIntro: false 禁用,或通过 channels.telegram.accounts.<accountId>.joinIntro 按账户覆盖。参见 群组加入介绍 了解每房间一次的行为和不可信内容处理。
  • 私信消息可以携带 message_thread_id;OpenClaw 会为回复保留它。仅当 Telegram getMe 报告该机器人的 has_topics_enabled: true 时,私信主题会话才会拆分;否则私信保持在扁平会话中。
  • 长轮询在隔离的工作进程中运行。更新会保存到持久队列中,并按每个聊天和主题的顺序进行处理。
  • 多账户启动限制了并发的 getMe 探测,因此大型机器人集群不会一次性扇出所有账户探测。
  • 每个网关进程守护长轮询,因此同一时间只有一个活动的轮询器可以使用一个机器人令牌。持续的 getUpdates 409 冲突表明另一个 OpenClaw 网关、脚本或外部轮询器正在使用同一令牌。
  • 如果在 120 秒内没有完成 getUpdates 活跃性检查,轮询看门狗就会重启。
  • Telegram Bot API 不支持已读回执(sendReadReceipts 不适用)。

Note

升级说明:Telegram 的默认预览在 2026.8.1 中已更改。 当 channels.telegram.streaming 未设置时,Telegram 在回合期间保留一个可编辑的状态草稿(代理的当前状态及其工具行),并将最终答案作为普通消息发送。它之前会将答案文本本身流式传输到预览中。不会有任何配置失效,也无需运行 doctor --fix;要保持先前的行为,请设置:

{ channels: { telegram: { streaming: { mode: "partial" } } } }

Note

channels.telegram.dm.threadReplies 和 channels.telegram.direct.<chatId>.threadReplies 已被移除。升级后,如果您的配置仍然包含这些键,请运行 openclaw doctor --fix。私信主题路由现在遵循 Telegram 的 getMe.has_topics_enabled(由 BotFather 线程模式控制):启用主题的机器人在 Telegram 发送 message_thread_id 时使用线程范围的私信会话;其他私信保持在扁平会话中。

对 replyToMode、streaming 和 textChunkLimit 的更改会在无需重新连接 Telegram 的情况下应用于下一个组装的回合,包括账户覆盖。 活动回合会保留其捕获的投递设置。

入站文本批处理

Telegram 默认会将来自同一发送者的快速文本消息批量合并为一个代理回合。普通文本等待 300ms 的静默窗口;至少 4000 个字符的非转发消息允许最多 1500ms 以等待可能的长粘贴续文。

  • 短文本和长文本共享一个批次,因此简短介绍、长粘贴和简短跟进可以作为一个回合到达。消息 ID 不需要连续。
  • 批次按机器人账户、发送者、聊天和主题保持隔离。回复元数据和源消息 ID 会被保留。
  • messages.inbound.byChannel.telegram 覆盖 messages.inbound.debounceMs,后者覆盖 300ms 的普通文本默认值。显式的 0 会禁用普通突发批处理,但保留自动长粘贴组装。
  • 控制命令绕过批处理并立即分发。停止/中止命令会取消其目标对话的待处理文本。
  • 转发消息使用单独的 80ms 收集窗口,但共享发送者的分发队列,因此它们不能超越较早的文本。

普通文本批次限制为 12 条消息和 50,000 个字符。它们的收集截止时间为自第一条消息起 7.5 秒;如果配置的静默窗口更长,则使用该窗口。批次刷新后到达的消息无法加入该批次。这种启发式方法不保证 Telegram 会将每个粘贴片段一起投递。有关热重载行为,请参阅 入站防抖。

消息行为

实时流预览(消息编辑)
OpenClaw 在私聊、群组和主题中实时流式传输部分回复:发送一条预览消息,然后反复执行 `editMessageText`,就地完成。

- `channels.telegram.streaming` 为 `off | partial | block | progress`(默认:`progress`);设置 `mode: "partial"` 可将答案文本流式传输到预览中,而不是状态草稿
- 简短的最初答案预览会进行防抖,然后在有界延迟后具体化(如果运行仍然活动)
- `progress` 保留一个可编辑的状态草稿,当答案活动先于工具进度到达时显示稳定的状态标签,在完成时清除它,并将最终答案作为普通消息发送。默认情况下草稿是安静的:状态标题、评论、计划里程碑和审批请求。中间工具失败和非零命令退出会被隐藏;终端任务错误仍使用正常的错误投递。`streaming.progress.toolProgress: true` 会添加滚动的工具日志,包括工具失败。
- `streaming.preview.toolProgress` 控制工具/进度更新是否在 `partial` 和 `block` 模式下重用同一编辑后的预览消息(默认:预览流式传输活动时为 `true`)
- `streaming.preview.commandText` 控制这些行内的命令/执行细节:`status`(默认,仅工具标签)或 `raw`(显式命令文本)
- 默认情况下,已完成的助手前言会更新状态标题;新的前言会保留先前可读的状态直到其完成
- `streaming.progress.commentary`(默认:`false`)将这些前言显示为交错的评论行,而不是标题;评论在计划步骤旁边保持可见
- 成功的后台进程轮询和内部等待不会进入进度日志;失败仍遵循所选的工具进度策略,`/verbose` 保留诊断摘要
- 会检测遗留的 `channels.telegram.streamMode`、布尔值 `streaming` 和已退役的原生草稿预览键;运行 `openclaw doctor --fix` 以迁移它们

工具进度行是工具运行时显示的简短状态更新(命令执行、文件读取、计划更新、补丁摘要、应用服务器模式下的 Codex 前言/评论)。partial 和 block 预览默认显示这些状态;progress 草稿仅在 streaming.progress.toolProgress: true 时显示。压缩状态遵循相同的设置,并会在压缩开始时立即出现,包括在首个模型输出之前。

保留答案预览编辑,但隐藏工具进度行:

{
  "channels": {
    "telegram": {
      "streaming": {
        "mode": "partial",
        "preview": { "toolProgress": false }
      }
    }
  }
}

保持工具进度可见,但隐藏命令/执行文本:

{
  "channels": {
    "telegram": {
      "streaming": {
        "mode": "partial",
        "preview": { "commandText": "status" }
      }
    }
  }
}

progress 模式可以显示工具日志,而无需将最终答案编辑到该消息中。设置 toolProgress: true 即可启用,并将命令文本策略放在 streaming.progress 下:

{
  "channels": {
    "telegram": {
      "streaming": {
        "mode": "progress",
        "progress": {
          "toolProgress": true,
          "commandText": "status"
        }
      }
    }
  }
}

streaming.mode: "off" 禁用预览编辑,并抑制通用的工具/进度消息,而不是将其作为独立状态消息发送;审批提示、媒体和错误仍通过正常最终投递路由。streaming.preview.toolProgress: false 仅保留答案预览编辑。

Note

选中的引用回复是例外。当 replyToMode 为 first、all 或 batched,且入站消息包含选中的引用文本时,OpenClaw 会通过 Telegram 的原生引用回复路径发送最终答案,并跳过该轮次的草稿预览。没有选中引用文本的当前消息回复仍然流式输出。启用回复线程后,它们的预览会携带自动引用摘录,并在原位置完成最终答案时保留这些摘录。当工具进度可见性比原生引用回复更重要时,请将 replyToMode 设置为 "off"。要保留原生引用回复并隐藏工具进度行,请在 progress 模式下使用 streaming.progress.toolProgress: false,或在 partial 和 block 模式下使用 streaming.preview.toolProgress: false。

对于纯文本回复:短预览会在原位置进行最终编辑;拆分为多条消息的长最终回复会将预览复用为第一块,然后仅发送剩余部分;progress 模式的最终回复会清除状态草稿并使用正常最终投递;如果在确认完成前最终编辑失败,OpenClaw 会回退到正常最终投递并清理过时的预览。对于复杂回复(媒体负载),OpenClaw 始终回退到正常最终投递并清理预览。

预览流式输出和块流式输出是互斥的。显式的非 off 预览模式会覆盖继承的 agents.defaults.blockStreamingDefault: "on";显式的 streaming.block.enabled: true 会覆盖预览。如果某一轮无法使用预览,继承的块投递仍然适用。

推理:/reasoning stream 在生成时将推理流式输出到实时预览中,然后在最终投递后删除推理预览(使用 /reasoning on 保持其可见)。最终答案发送时不包含推理文本。

原生命令与自定义命令

Telegram 的命令菜单在启动时通过 setMyCommands 注册。commands.native: "auto" 为 Telegram 启用原生命令。

添加自定义命令菜单条目:

{
  channels: {
    telegram: {
      customCommands: [
        { command: "backup", description: "Git backup" },
        { command: "generate", description: "Create an image" },
      ],
    },
  },
}

规则:名称会被规范化(去除开头的 /,转为小写);有效模式为 a-z、0-9、_,长度 1-32;自定义命令不能覆盖原生命令;冲突/重复项会被跳过并记录日志。

当 Telegram 菜单限制需要裁剪时,配置的自定义命令优先,除非省略的按技能条目由前置的 /skill 回退项替换。

自定义命令仅是菜单条目——它们不会自动实现行为。插件/技能命令即使在 Telegram 菜单中未显示,输入时仍然可以工作。如果原生命令被禁用,内置命令会被移除;自定义/插件命令如果已配置,仍可能注册。

常见设置失败:

  • setMyCommands failed 并带有 BOT_COMMANDS_TOO_MUCH,在裁剪重试后表示菜单仍然溢出;减少插件/技能/自定义命令,或禁用 channels.telegram.commands.native。
  • deleteWebhook、deleteMyCommands 或 setMyCommands 在直接 Bot API curl 命令正常工作时却失败并返回 404: Not Found,通常意味着 channels.telegram.apiRoot 被设置为完整的 /bot<TOKEN> 端点。apiRoot 必须是 Bot API 根路径;openclaw doctor --fix 会移除意外添加的尾部 /bot<TOKEN>。
  • getMe returned 401 表示 Telegram 拒绝了配置的机器人令牌。使用当前的 BotFather 令牌更新 botToken、tokenFile 或 TELEGRAM_BOT_TOKEN(默认账户);OpenClaw 会在轮询之前停止,因此这不会被报告为 webhook 清理失败。
  • setMyCommands failed 并出现网络/获取错误,通常意味着到 api.telegram.org 的出站 DNS/HTTPS 被阻止。

设备配对命令(device-pair 插件)

安装后:

  1. /pair 生成设置代码
  2. 将代码粘贴到 iOS 应用中
  3. /pair pending 列出待处理请求(包括角色/作用域)
  4. 批准:/pair approve <requestId>、/pair approve(仅待处理请求)或 /pair approve latest

如果设备以更改后的身份验证详情(角色、权限范围、公钥)重试,则之前的待处理请求会被新的 requestId 取代;批准前请重新运行 /pair pending。

    更多详情:[配对](../pairing.md#pair-via-telegram).
回复线程标签

生成输出中的显式回复线程标签:

  • [[reply_to_current]] — 回复触发消息
  • [[reply_to:<id>]] — 回复特定消息 ID

channels.telegram.replyToMode:off(默认)、first、all。

当启用回复线程且原始文本/说明可用时,OpenClaw 会自动添加原生引用摘录。Telegram 将原生引用文本限制为 1024 个 UTF-16 码元;更长的消息将从开头引用,如果 Telegram 拒绝该引用,则回退为普通回复。

off 仅禁用隐式回复线程;显式 [[reply_to_*]] 标签仍会被遵循。

确认表情回应
`ackReaction` 会在 OpenClaw 处理入站消息时发送一个确认表情。`messages.ackReactionScope` 决定*何时*发送。

**表情解析顺序:**

- `channels.telegram.accounts.<accountId>.ackReaction`
- `channels.telegram.ackReaction`
- `messages.ackReaction`
- 代理身份表情回退(`agents.entries.*.identity.emoji`,否则为 "👀")

Telegram 期望一个 Unicode 表情(例如 "👀");使用 `""` 可为某个渠道或账户禁用该反应。

**作用域(`messages.ackReactionScope`,默认 `"group-mentions"`;无 Telegram 账户或 Telegram 渠道覆盖):**

`all`(私聊 + 群组,包括环境房间事件)、`direct`(仅私聊)、`group-all`(除环境房间事件外的所有群消息,不含私聊)、`group-mentions`(群组中机器人被提及时;**不含私聊** — 默认)、`off` / `none`(禁用)。

Note

默认作用域(group-mentions)不会在私聊或环境房间事件中触发确认表情。私聊请使用 direct 或 all;只有 all 会确认环境房间事件。更改遵循热重载,并应用于后续消息。每个组装的回合保留其捕获的值。

保留的群组历史
Telegram 群组和论坛主题使用近期自动上下文窗口以及显式历史读取。当 `requireMention: true` 时,被允许的未提及消息会被记录,而不会启动代理回合。后续被指向的回合会收到近期上下文,代理在需要更早讨论时可以使用 `message(action="read")`。

- `channels.telegram.historyLimit` 或 `messages.groupChat.historyLimit` 限制自动观察消息窗口(默认 50,硬性上限 200)。`0` 禁用自动历史注入,而不是禁用记录或显式读取。JSON 整数最大值(`9007199254740991`)会选择 50 条消息的默认值。
- 自动上下文在应用主题和发送者权限之前,会检查一个有界的近期片段。当其他主题或被排除的发送者主导该片段时,繁忙的群组可能提供的消息少于配置数量。代理可以通过显式历史读取向前翻页。
- 历史读取始终限定在已授权的账户、聊天和主题内。它们使用 Telegram 消息 ID 进行引用和分页;省略主题时,不得将主题范围的读取扩展到整个群组。
- 代理读取默认每页 50 条消息,最多 100 条。使用返回的 `oldestMessageId` 配合 `before` 读取更早的消息,使用 `after` 读取更新的消息,或使用 `messageId` 进行精确引用。这些读取需要代理已认证的当前群组/主题;它们不会获取 Telegram 服务器历史。
- `/new` 和 `/reset` 重置自动会话上下文,而不是保留的对话。显式历史读取可以检索被允许的更早讨论。
- 现有的 SQLite 插件状态表拥有保留的群消息。成功持久化的记录在 Gateway 重启后仍然存在,并且不会因消息数量而被逐出。私聊缓存行为仍保持有界。
- 首次使用时,现有的群缓存记录会原子地迁移到保留存储中。没有历史准入来源的旧记录(包括嵌入的回复祖先)仍可在现有深度和可见性限制内作为显式回复上下文使用;它们不会被当作经过验证的对话存档。
- 历史仅包含 OpenClaw 收到并被允许记录的消息。它无法恢复此功能之前被逐出的消息、Telegram 未投递的消息,或机器人加入之前的消息。媒体引用不保证附件字节会无限期可用。

无需单独的观察设置。请保持 Telegram [群组可见性](setup.md#privacy-mode-and-group-visibility) 启用,以便机器人接收普通消息。启用历史不会更改显式 `requireMention: false` 的激活方式。

Warning

旧版 OpenClaw 版本应用不同的缓存和插件配额规则。不要将它们用于扩展的保留历史。降级需要兼容的更新前备份;此功能不会添加数据库版本围栏。

限制和 CLI 目标
- `channels.telegram.textChunkLimit` 默认 4000;`streaming.chunkMode="newline"` 在按长度拆分之前优先选择段落边界(空行)。
- `channels.telegram.mediaMaxMb`(默认 100)限制入站和出站媒体大小。
- 当入站附件无法下载且消息继续传递给代理时,其正文会包含 `[media unavailable: ...]` 通知。超大通知会包含有效大小限制;部分相册会包含失败和总附件数量。这也适用于被允许的渠道帖子,即使其单独的聊天警告被抑制。
- 自动群组上下文使用 `channels.telegram.historyLimit` 或 `messages.groupChat.historyLimit`(默认 50);`0` 禁用自动窗口,而不是保留历史。
- 当网关已观察到父消息时,回复/引用/转发的补充上下文会归一化为一个选定的对话上下文窗口;观察消息缓存位于 OpenClaw SQLite 插件状态中。要导入六月前的缓存 sidecar 文件,请[通过 `2026.9.5` 升级](../../install/updating.md#upgrading-very-old-versions),并先运行其 Doctor。Telegram 每个更新仅包含一个浅层 `reply_to_message`,因此早于缓存的链仅限于该负载。
- Telegram 允许列表主要控制谁可以触发代理,而不是完整的补充上下文脱敏边界。
- 私聊历史:`channels.telegram.dmHistoryLimit`、`channels.telegram.dms["<user_id>"].historyLimit`。自动观察私聊上下文默认为 10 条消息,上限为 200;`0` 禁用该额外上下文,JSON 整数最大值会选择默认值。
- 这些渠道/账户/私聊字段还控制**用户回合**中嵌入的会话转录修剪,而不是观察消息。该独立限制器将 `0` 视为不修剪,并保留其现有逐出缓冲。Doctor 会保留有效的已保存限制(包括 JSON 整数最大值),因此不会悄悄更改转录上下文。

CLI 和 message-tool 发送目标接受数字聊天 ID、用户名或论坛主题目标:

```bash
openclaw message send --channel telegram --target 123456789 --message "hi"
openclaw message send --channel telegram --target @name --message "hi"
openclaw message send --channel telegram --target -1001234567890:topic:42 --message "hi topic"
```

    投票使用 `openclaw message poll`,并支持论坛主题:

```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 的投票参数:`--poll-duration-seconds`(5-604800;最长七天)、`--poll-anonymous`、`--poll-public`、`--thread-id`(或 `:topic:` 目标)。`--poll-option` 可重复 2-12 次(Telegram 的选项上限)。

    Telegram 发送还支持 `--presentation`,其中包含 `buttons` 块用于内联键盘(当 `channels.telegram.capabilities.inlineButtons` 允许时)、`--pin` 或 `--delivery '{"pin":true}'` 以在机器人可以在该聊天中置顶消息时请求置顶投递,以及 `--force-document` 以将出站图片、GIF 和视频作为文档发送,而不是作为压缩/动画/视频上传发送。

    操作门控:`channels.telegram.actions.sendMessage=false` 会禁用所有出站消息,包括投票;`channels.telegram.actions.poll=false` 会禁用投票创建,同时保留普通发送功能。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 cl/openclaw