diff --git a/docs/zh-CN/channels/telegram.md b/docs/zh-CN/channels/telegram.md index 4bfbde510..e369cb656 100644 --- a/docs/zh-CN/channels/telegram.md +++ b/docs/zh-CN/channels/telegram.md @@ -1,42 +1,42 @@ --- read_when: - - 处理 Telegram 功能或 webhook + - 处理 Telegram 功能或网络钩子 summary: Telegram 机器人支持状态、能力和配置 title: Telegram x-i18n: - generated_at: "2026-05-03T15:57:48Z" + generated_at: "2026-05-03T19:29:14Z" model: gpt-5.5 provider: openai - source_hash: 730b26df661d5faacdc393b1a017ac36ee38371b9cee6a6dffe8b627566ce2e2 + source_hash: 06252b1540d5794aa5d8fe1066bf9d121e682a0df969a265d890445e97d5d647 source_path: channels/telegram.md workflow: 16 --- -可用于生产环境中的机器人私信和群组,基于 grammY。长轮询是默认模式;webhook 模式是可选的。 +Production-ready for bot DMs and groups via grammY. Long polling is the default mode; webhook mode is optional. - - Telegram 的默认私信策略是配对。 + + Default DM policy for Telegram is pairing. - - 跨渠道诊断和修复手册。 + + Cross-channel diagnostics and repair playbooks. - - 完整的渠道配置模式和示例。 + + Full channel config patterns and examples. -## 快速设置 +## Quick setup - - 打开 Telegram,并与 **@BotFather** 聊天(确认用户名完全是 `@BotFather`)。 + + Open Telegram and chat with **@BotFather** (confirm the handle is exactly `@BotFather`). - 运行 `/newbot`,按照提示操作,并保存令牌。 + Run `/newbot`, follow prompts, and save the token. - + ```json5 { @@ -51,12 +51,12 @@ x-i18n: } ``` - 环境变量回退:`TELEGRAM_BOT_TOKEN=...`(仅默认账户)。 - Telegram **不**使用 `openclaw channels login telegram`;请在配置/环境变量中配置令牌,然后启动 Gateway 网关。 + Env fallback: `TELEGRAM_BOT_TOKEN=...` (default account only). + Telegram does **not** use `openclaw channels login telegram`; configure token in config/env, then start gateway. - + ```bash openclaw gateway @@ -64,119 +64,119 @@ openclaw pairing list telegram openclaw pairing approve telegram ``` - 配对码会在 1 小时后过期。 + Pairing codes expire after 1 hour. - - 将机器人添加到你的群组,然后设置 `channels.telegram.groups` 和 `groupPolicy`,使其与你的访问模型匹配。 + + Add the bot to your group, then set `channels.telegram.groups` and `groupPolicy` to match your access model. -令牌解析顺序感知账户。实践中,配置值优先于环境变量回退,而 `TELEGRAM_BOT_TOKEN` 只适用于默认账户。 +Token resolution order is account-aware. In practice, config values win over env fallback, and `TELEGRAM_BOT_TOKEN` only applies to the default account. -## Telegram 端设置 +## Telegram side settings - - Telegram 机器人默认使用**隐私模式**,这会限制它们能接收哪些群组消息。 + + Telegram bots default to **Privacy Mode**, which limits what group messages they receive. - 如果机器人必须看到所有群组消息,可以: + If the bot must see all group messages, either: - - 通过 `/setprivacy` 禁用隐私模式,或 - - 将机器人设为群组管理员。 + - disable privacy mode via `/setprivacy`, or + - make the bot a group admin. - 切换隐私模式时,请在每个群组中移除并重新添加机器人,以便 Telegram 应用更改。 + When toggling privacy mode, remove + re-add the bot in each group so Telegram applies the change. - - 管理员状态在 Telegram 群组设置中控制。 + + Admin status is controlled in Telegram group settings. - 管理员机器人会接收所有群组消息,这对始终在线的群组行为很有用。 + Admin bots receive all group messages, which is useful for always-on group behavior. - + - - `/setjoingroups` 用于允许/拒绝加入群组 - - `/setprivacy` 用于群组可见性行为 + - `/setjoingroups` to allow/deny group adds + - `/setprivacy` for group visibility behavior -## 访问控制和激活 +## Access control and activation - - `channels.telegram.dmPolicy` 控制直接消息访问: + + `channels.telegram.dmPolicy` controls direct message access: - - `pairing`(默认) - - `allowlist`(要求 `allowFrom` 中至少有一个发送者 ID) - - `open`(要求 `allowFrom` 包含 `"*"`) + - `pairing` (default) + - `allowlist` (requires at least one sender ID in `allowFrom`) + - `open` (requires `allowFrom` to include `"*"`) - `disabled` - `dmPolicy: "open"` 搭配 `allowFrom: ["*"]` 会让任何找到或猜到机器人用户名的 Telegram 账户都能指挥机器人。只应将其用于有严格受限工具的有意公开机器人;单所有者机器人应使用带数字用户 ID 的 `allowlist`。 + `dmPolicy: "open"` with `allowFrom: ["*"]` lets any Telegram account that finds or guesses the bot username command the bot. Use it only for intentionally public bots with tightly restricted tools; one-owner bots should use `allowlist` with numeric user IDs. - `channels.telegram.allowFrom` 接受数字 Telegram 用户 ID。`telegram:` / `tg:` 前缀会被接受并规范化。 - 在多账户配置中,限制性的顶层 `channels.telegram.allowFrom` 会被视为安全边界:账户级 `allowFrom: ["*"]` 条目不会让该账户公开,除非合并后的有效账户允许名单仍包含显式通配符。 - `dmPolicy: "allowlist"` 搭配空的 `allowFrom` 会阻止所有私信,并会被配置验证拒绝。 - 设置过程只会询问数字用户 ID。 - 如果你已升级且配置中包含 `@username` 允许名单条目,请运行 `openclaw doctor --fix` 来解析它们(尽力而为;需要 Telegram 机器人令牌)。 - 如果你之前依赖配对存储的允许名单文件,`openclaw doctor --fix` 可以在允许名单流程中将条目恢复到 `channels.telegram.allowFrom`(例如当 `dmPolicy: "allowlist"` 尚无显式 ID 时)。 + `channels.telegram.allowFrom` accepts numeric Telegram user IDs. `telegram:` / `tg:` prefixes are accepted and normalized. + In multi-account configs, a restrictive top-level `channels.telegram.allowFrom` is treated as a safety boundary: account-level `allowFrom: ["*"]` entries do not make that account public unless the effective account allowlist still contains an explicit wildcard after merging. + `dmPolicy: "allowlist"` with empty `allowFrom` blocks all DMs and is rejected by config validation. + Setup asks for numeric user IDs only. + 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). - 对于单所有者机器人,优先使用 `dmPolicy: "allowlist"` 并配置显式数字 `allowFrom` ID,以便让访问策略在配置中保持持久(而不是依赖之前的配对批准)。 + 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). - 常见困惑:私信配对批准并不表示“这个发送者在所有地方都已授权”。 - 配对授予私信访问权限。如果还没有命令所有者,第一次获批的配对还会设置 `commands.ownerAllowFrom`,让仅所有者命令和执行批准拥有明确的操作员账户。 - 群组发送者授权仍来自显式配置允许名单。 - 如果你想要“我授权一次,私信和群组命令都能用”,请将你的数字 Telegram 用户 ID 放入 `channels.telegram.allowFrom`;对于仅所有者命令,请确保 `commands.ownerAllowFrom` 包含 `telegram:`。 + Common confusion: DM pairing approval does not mean "this sender is authorized everywhere". + Pairing grants DM access. If no command owner exists yet, the first approved pairing also sets `commands.ownerAllowFrom` so owner-only commands and exec approvals have an explicit operator account. + Group sender authorization still comes from explicit config allowlists. + If you want "I am authorized once and both DMs and group commands work", put your numeric Telegram user ID in `channels.telegram.allowFrom`; for owner-only commands, make sure `commands.ownerAllowFrom` contains `telegram:`. - ### 查找你的 Telegram 用户 ID + ### Finding your Telegram user ID - 更安全的方式(无需第三方机器人): + Safer (no third-party bot): - 1. 向你的机器人发送私信。 - 2. 运行 `openclaw logs --follow`。 - 3. 读取 `from.id`。 + 1. DM your bot. + 2. Run `openclaw logs --follow`. + 3. Read `from.id`. - 官方 Bot API 方法: + Official Bot API method: ```bash curl "https://api.telegram.org/bot/getUpdates" ``` - 第三方方法(隐私性较低):`@userinfobot` 或 `@getidsbot`。 + Third-party method (less private): `@userinfobot` or `@getidsbot`. - - 两项控制会一起生效: + + Two controls apply together: - 1. **允许哪些群组**(`channels.telegram.groups`) - - 没有 `groups` 配置: - - 使用 `groupPolicy: "open"`:任何群组都可以通过群组 ID 检查 - - 使用 `groupPolicy: "allowlist"`(默认):群组会被阻止,直到你添加 `groups` 条目(或 `"*"`) - - 已配置 `groups`:作为允许名单生效(显式 ID 或 `"*"`) + 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 `"*"`) - 2. **群组中允许哪些发送者**(`channels.telegram.groupPolicy`) + 2. **Which senders are allowed in groups** (`channels.telegram.groupPolicy`) - `open` - - `allowlist`(默认) + - `allowlist` (default) - `disabled` - `groupAllowFrom` 用于群组发送者过滤。如果未设置,Telegram 会回退到 `allowFrom`。 - `groupAllowFrom` 条目应为数字 Telegram 用户 ID(`telegram:` / `tg:` 前缀会被规范化)。 - 不要将 Telegram 群组或超级群组聊天 ID 放入 `groupAllowFrom`。负数聊天 ID 属于 `channels.telegram.groups`。 - 非数字条目会在发送者授权中被忽略。 - 安全边界(`2026.2.25+`):群组发送者认证**不会**继承私信配对存储批准。 - 配对仅适用于私信。对于群组,请设置 `groupAllowFrom` 或按群组/按话题设置 `allowFrom`。 - 如果未设置 `groupAllowFrom`,Telegram 会回退到配置 `allowFrom`,而不是配对存储。 - 单所有者机器人的实用模式:在 `channels.telegram.allowFrom` 中设置你的用户 ID,不设置 `groupAllowFrom`,并在 `channels.telegram.groups` 下允许目标群组。 - 运行时说明:如果完全缺少 `channels.telegram`,运行时默认采用故障关闭的 `groupPolicy="allowlist"`,除非显式设置了 `channels.defaults.groupPolicy`。 + `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). + Do not put Telegram group or supergroup chat IDs in `groupAllowFrom`. Negative chat IDs belong under `channels.telegram.groups`. + 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`. + If `groupAllowFrom` is unset, Telegram falls back to config `allowFrom`, not the pairing store. + Practical pattern for one-owner bots: set your user ID in `channels.telegram.allowFrom`, leave `groupAllowFrom` unset, and allow the target groups under `channels.telegram.groups`. + 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 { @@ -193,7 +193,7 @@ curl "https://api.telegram.org/bot/getUpdates" } ``` - 示例:只允许一个特定群组内的特定用户: + Example: allow only specific users inside one specific group: ```json5 { @@ -211,34 +211,34 @@ curl "https://api.telegram.org/bot/getUpdates" ``` - 常见错误:`groupAllowFrom` 不是 Telegram 群组允许名单。 + Common mistake: `groupAllowFrom` is not a Telegram group allowlist. - - 将像 `-1001234567890` 这样的负数 Telegram 群组或超级群组聊天 ID 放在 `channels.telegram.groups` 下。 - - 当你想限制允许群组内哪些人可以触发机器人时,将像 `8734062810` 这样的 Telegram 用户 ID 放在 `groupAllowFrom` 下。 - - 只有在你希望允许群组中的任何成员都能与机器人对话时,才使用 `groupAllowFrom: ["*"]`。 + - Put negative Telegram group or supergroup chat IDs like `-1001234567890` under `channels.telegram.groups`. + - Put Telegram user IDs like `8734062810` under `groupAllowFrom` when you want to limit which people inside an allowed group can trigger the bot. + - Use `groupAllowFrom: ["*"]` only when you want any member of an allowed group to be able to talk to the bot. - - 群组回复默认需要提及。 + + Group replies require mention by default. - 提及可以来自: + Mention can come from: - - 原生 `@botusername` 提及,或 - - 以下位置中的提及模式: + - 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: ```json5 { @@ -252,44 +252,44 @@ curl "https://api.telegram.org/bot/getUpdates" } ``` - 获取群组聊天 ID: + Getting the group chat ID: - - 将群组消息转发给 `@userinfobot` / `@getidsbot` - - 或从 `openclaw logs --follow` 读取 `chat.id` - - 或检查 Bot API `getUpdates` + - forward a group message to `@userinfobot` / `@getidsbot` + - or read `chat.id` from `openclaw logs --follow` + - or inspect Bot API `getUpdates` -## 运行时行为 +## Runtime behavior -- Telegram 由 Gateway 网关进程拥有。 -- 路由是确定性的:Telegram 入站会回复到 Telegram(模型不会选择渠道)。 -- 入站消息会规范化为共享渠道信封,并包含回复元数据和媒体占位符。 -- 群组会话按群组 ID 隔离。论坛话题会追加 `:topic:`,以保持话题隔离。 -- 私信消息可以携带 `message_thread_id`;OpenClaw 会为回复保留线程 ID,但默认将私信保持在扁平会话上。当你有意想要私信话题会话隔离时,请配置 `channels.telegram.dm.threadReplies: "inbound"`、`channels.telegram.direct..threadReplies: "inbound"`、`requireTopic: true`,或匹配的话题配置。 -- 长轮询使用 grammY runner,并按聊天/按线程排序。整体 runner sink 并发使用 `agents.defaults.maxConcurrent`。 -- 长轮询在每个 Gateway 网关进程内都有保护,因此同一时间只有一个活动 poller 可以使用一个机器人令牌。如果你仍然看到 `getUpdates` 409 冲突,可能有另一个 OpenClaw Gateway 网关、脚本或外部 poller 正在使用同一令牌。 -- 默认情况下,长轮询 watchdog 会在 120 秒内没有完成的 `getUpdates` 存活性信号后触发重启。只有当你的部署在长时间运行工作期间仍看到误报的轮询停滞重启时,才增加 `channels.telegram.pollingStallThresholdMs`。该值以毫秒为单位,允许范围为 `30000` 到 `600000`;支持按账户覆盖。 -- Telegram Bot API 不支持已读回执(`sendReadReceipts` 不适用)。 +- 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:` to keep topics isolated. +- DM messages can carry `message_thread_id`; OpenClaw preserves the thread ID for replies but keeps DMs on the flat session by default. Configure `channels.telegram.dm.threadReplies: "inbound"`, `channels.telegram.direct..threadReplies: "inbound"`, `requireTopic: true`, or a matching topic config when you intentionally want DM topic session isolation. +- Long polling uses grammY runner with per-chat/per-thread sequencing. Overall runner sink concurrency uses `agents.defaults.maxConcurrent`. +- Long polling is guarded inside each gateway process so only one active poller can use a bot token at a time. If you still see `getUpdates` 409 conflicts, another OpenClaw gateway, script, or external poller is likely using the same token. +- Long-polling watchdog restarts trigger after 120 seconds without completed `getUpdates` liveness by default. Increase `channels.telegram.pollingStallThresholdMs` only if your deployment still sees false polling-stall restarts during long-running work. The value is in milliseconds and is allowed from `30000` to `600000`; per-account overrides are supported. +- Telegram Bot API has no read-receipt support (`sendReadReceipts` does not apply). -## 功能参考 +## Feature reference - - OpenClaw 可以实时流式传输部分回复: + + OpenClaw can stream partial replies in real time: - - 直接聊天:预览消息 + `editMessageText` - - 群组/话题:预览消息 + `editMessageText` + - direct chats: preview message + `editMessageText` + - groups/topics: preview message + `editMessageText` - 要求: + Requirement: - - `channels.telegram.streaming` 为 `off | partial | block | progress`(默认:`partial`) - - 在 Telegram 上,`progress` 映射到 `partial`(兼容跨渠道命名) - - `streaming.preview.toolProgress` 控制工具/进度更新是否复用同一条已编辑的预览消息(默认:预览流式传输处于活动状态时为 `true`) - - 会检测旧版 `channels.telegram.streamMode` 和布尔值 `streaming`;运行 `openclaw doctor --fix` 将它们迁移到 `channels.telegram.streaming.mode` + - `channels.telegram.streaming` is `off | partial | block | progress` (default: `partial`) + - `progress` maps to `partial` on Telegram (compat with cross-channel naming) + - `streaming.preview.toolProgress` controls whether tool/progress updates reuse the same edited preview message (default: `true` when preview streaming is active) + - legacy `channels.telegram.streamMode` and boolean `streaming` values are detected; run `openclaw doctor --fix` to migrate them to `channels.telegram.streaming.mode` - 工具进度预览更新是在工具运行时显示的短“Working...”行,例如命令执行、文件读取、规划更新或补丁摘要。Telegram 默认保持这些更新启用,以匹配 `v2026.4.22` 及之后版本发布的 OpenClaw 行为。若要保留用于答案文本的已编辑预览,但隐藏工具进度行,请设置: + Tool-progress preview updates are the short "Working..." lines shown while tools run, for example command execution, file reads, planning updates, or patch summaries. Telegram keeps these enabled by default to match released OpenClaw behavior from `v2026.4.22` and later. To keep the edited preview for answer text but hide tool-progress lines, set: ```json { @@ -306,21 +306,21 @@ curl "https://api.telegram.org/bot/getUpdates" } ``` - Use `streaming.mode: "off"` 仅在你想要只发送最终内容时使用:Telegram 预览编辑会被禁用,通用工具/进度闲聊会被抑制,而不是作为独立的 “Working...” 消息发送。审批提示、媒体载荷和错误仍会通过正常的最终发送路径。仅当你想保留答案预览编辑、同时隐藏工具进度状态行时,使用 `streaming.preview.toolProgress: false`。 + 仅在你希望仅最终交付时使用 `streaming.mode: "off"`:Telegram 预览编辑会被禁用,通用工具/进度闲聊会被抑制,而不是作为独立的 “Working...” 消息发送。审批提示、媒体载荷和错误仍会通过正常的最终交付路径路由。仅当你希望保留答案预览编辑,同时隐藏工具进度状态行时,使用 `streaming.preview.toolProgress: false`。 - Telegram 选中引用回复是例外。当 `replyToMode` 为 `"first"`、`"all"` 或 `"batched"`,并且入站消息包含选中的引用文本时,OpenClaw 会通过 Telegram 的原生引用回复路径发送最终答案,而不是编辑答案预览,因此 `streaming.preview.toolProgress` 无法为该轮显示短的 “Working...” 行。不带选中引用文本的当前消息回复仍会保留预览流式传输。当工具进度可见性比原生引用回复更重要时,设置 `replyToMode: "off"`;或设置 `streaming.preview.toolProgress: false` 来确认这一权衡。 + Telegram 选中引用回复是例外。当 `replyToMode` 为 `"first"`、`"all"` 或 `"batched"`,且入站消息包含选中的引用文本时,OpenClaw 会通过 Telegram 原生引用回复路径发送最终答案,而不是编辑答案预览,因此 `streaming.preview.toolProgress` 无法为该轮显示简短的 “Working...” 行。没有选中引用文本的当前消息回复仍会保留预览流式传输。当工具进度可见性比原生引用回复更重要时,设置 `replyToMode: "off"`;或者设置 `streaming.preview.toolProgress: false` 以确认这一权衡。 对于纯文本回复: - - 短私信/群组/topic 预览:OpenClaw 会保留同一条预览消息并在原处执行最终编辑,除非预览出现后又发送了一条可见的非预览消息 - - 预览之后跟随可见的非预览输出:OpenClaw 会将完成后的回复作为新的最终消息发送,并清理较早的预览,因此最终答案会出现在中间输出之后 - - 超过约一分钟的预览:OpenClaw 会将完成后的回复作为新的最终消息发送,然后清理预览,因此 Telegram 的可见时间戳会反映完成时间,而不是预览创建时间 + - 简短的私信/群组/topic 预览:OpenClaw 会保留同一条预览消息,并在原位置执行最终编辑,除非预览出现后发送过可见的非预览消息 + - 预览后跟随可见的非预览输出:OpenClaw 会将完成后的回复作为新的最终消息发送,并清理较旧的预览,因此最终答案会出现在中间输出之后 + - 超过约一分钟的预览:OpenClaw 会将完成后的回复作为新的最终消息发送,然后清理预览,因此 Telegram 的可见时间戳反映的是完成时间,而不是预览创建时间 - 对于复杂回复(例如媒体载荷),OpenClaw 会回退到正常的最终发送路径,然后清理预览消息。 + 对于复杂回复(例如媒体载荷),OpenClaw 会回退到正常的最终交付,然后清理预览消息。 - 预览流式传输与分块流式传输是分开的。当 Telegram 显式启用分块流式传输时,OpenClaw 会跳过预览流,以避免重复流式传输。 + 预览流式传输独立于分块流式传输。当为 Telegram 显式启用分块流式传输时,OpenClaw 会跳过预览流,以避免双重流式传输。 仅限 Telegram 的推理流: @@ -329,7 +329,7 @@ curl "https://api.telegram.org/bot/getUpdates" - + 出站文本使用 Telegram `parse_mode: "HTML"`。 - 类 Markdown 文本会渲染为 Telegram 安全的 HTML。 @@ -369,23 +369,23 @@ curl "https://api.telegram.org/bot/getUpdates" - 自定义命令不能覆盖原生命令 - 冲突/重复项会被跳过并记录日志 - 注意: + 注意事项: - 自定义命令只是菜单项;它们不会自动实现行为 - - plugin/skill 命令即使没有显示在 Telegram 菜单中,输入时仍然可以工作 + - plugin/skill 命令即使未显示在 Telegram 菜单中,输入时仍可生效 - 如果禁用原生命令,内置命令会被移除。自定义/plugin 命令在已配置时仍可注册。 + 如果原生命令被禁用,内置命令会被移除。自定义/plugin 命令在配置后仍可能注册。 常见设置失败: - - `setMyCommands failed` 并带有 `BOT_COMMANDS_TOO_MUCH` 表示 Telegram 菜单在裁剪后仍然溢出;减少 plugin/skill/自定义命令,或禁用 `channels.telegram.commands.native`。 - - 当直接 Bot API curl 命令可用,但 `deleteWebhook`、`deleteMyCommands` 或 `setMyCommands` 失败并带有 `404: Not Found` 时,可能表示 `channels.telegram.apiRoot` 被设置成了完整的 `/bot` 端点。`apiRoot` 必须只是 Bot API 根地址,`openclaw doctor --fix` 会移除意外追加的 `/bot`。 - - `getMe returned 401` 表示 Telegram 拒绝了已配置的 bot token。使用当前 BotFather token 更新 `botToken`、`tokenFile` 或 `TELEGRAM_BOT_TOKEN`;OpenClaw 会在轮询前停止,因此这不会被报告为 webhook 清理失败。 - - `setMyCommands failed` 并带有网络/fetch 错误,通常表示到 `api.telegram.org` 的出站 DNS/HTTPS 被阻止。 + - `setMyCommands failed` 搭配 `BOT_COMMANDS_TOO_MUCH` 表示 Telegram 菜单在裁剪后仍然溢出;减少 plugin/skill/自定义命令,或禁用 `channels.telegram.commands.native`。 + - 当直接的 Bot API curl 命令可用,但 `deleteWebhook`、`deleteMyCommands` 或 `setMyCommands` 失败并显示 `404: Not Found` 时,可能表示 `channels.telegram.apiRoot` 被设置成了完整的 `/bot` 端点。`apiRoot` 必须只是 Bot API 根地址,并且 `openclaw doctor --fix` 会移除意外附加的尾部 `/bot`。 + - `getMe returned 401` 表示 Telegram 拒绝了配置的机器人 token。使用当前 BotFather token 更新 `botToken`、`tokenFile` 或 `TELEGRAM_BOT_TOKEN`;OpenClaw 会在轮询前停止,因此这不会被报告为 webhook 清理失败。 + - `setMyCommands failed` 搭配网络/fetch 错误通常表示到 `api.telegram.org` 的出站 DNS/HTTPS 被阻断。 ### 设备配对命令(`device-pair` 插件) - 当安装了 `device-pair` 插件时: + 安装 `device-pair` 插件后: 1. `/pair` 生成设置代码 2. 在 iOS 应用中粘贴代码 @@ -395,9 +395,9 @@ curl "https://api.telegram.org/bot/getUpdates" - 只有一个待处理请求时使用 `/pair approve` - `/pair approve latest` 用于最近的请求 - 设置代码携带一个短生命周期的引导 token。内置引导交接会将主节点 token 保持在 `scopes: []`;任何被交接的 operator token 都会限制在 `operator.approvals`、`operator.read`、`operator.talk.secrets` 和 `operator.write` 范围内。引导作用域检查带有角色前缀,因此该 operator 允许列表只满足 operator 请求;非 operator 角色仍需要位于其自身角色前缀下的作用域。 + 设置代码携带一个短期有效的 bootstrap token。内置 bootstrap 交接会将主节点 token 保持在 `scopes: []`;任何被交接的 operator token 都限制在 `operator.approvals`、`operator.read`、`operator.talk.secrets` 和 `operator.write`。Bootstrap 作用域检查带有角色前缀,因此该 operator 允许列表只满足 operator 请求;非 operator 角色仍需要位于自身角色前缀下的作用域。 - 如果设备使用变更后的认证详情重试(例如角色/作用域/公钥),之前的待处理请求会被取代,新请求会使用不同的 `requestId`。批准前请重新运行 `/pair pending`。 + 如果设备使用变更后的认证详情重试(例如角色/作用域/公钥),先前的待处理请求会被取代,新请求会使用不同的 `requestId`。批准前请重新运行 `/pair pending`。 更多详情:[配对](/zh-CN/channels/pairing#pair-via-telegram-recommended-for-ios)。 @@ -418,7 +418,7 @@ curl "https://api.telegram.org/bot/getUpdates" } ``` - 单账号覆盖: + 按账户覆盖: ```json5 { @@ -478,24 +478,24 @@ curl "https://api.telegram.org/bot/getUpdates" - `editMessage`(`chatId`、`messageId`、`content`) - `createForumTopic`(`chatId`、`name`、可选 `iconColor`、`iconCustomEmojiId`) - 渠道消息操作暴露符合人体工学的别名(`send`、`react`、`delete`、`edit`、`sticker`、`sticker-search`、`topic-create`)。 + 渠道消息操作提供易用别名(`send`、`react`、`delete`、`edit`、`sticker`、`sticker-search`、`topic-create`)。 - 门控控制: + 门控控制项: - `channels.telegram.actions.sendMessage` - `channels.telegram.actions.deleteMessage` - `channels.telegram.actions.reactions` - `channels.telegram.actions.sticker`(默认:禁用) - 注意:`edit` 和 `topic-create` 当前默认启用,并且没有单独的 `channels.telegram.actions.*` 开关。 - 运行时发送会使用活动的配置/secret 快照(启动/重载),因此操作路径不会在每次发送时执行临时 SecretRef 重新解析。 + 注意:`edit` 和 `topic-create` 目前默认启用,且没有单独的 `channels.telegram.actions.*` 开关。 + 运行时发送使用当前有效的配置/secret 快照(启动/重载),因此操作路径不会在每次发送时执行临时 SecretRef 重新解析。 移除回应的语义:[/tools/reactions](/zh-CN/tools/reactions) - Telegram 支持在生成输出中使用显式回复线程标签: + Telegram 支持在生成的输出中使用显式回复线程标签: - `[[reply_to_current]]` 回复触发消息 - `[[reply_to:]]` 回复特定的 Telegram 消息 ID @@ -506,29 +506,29 @@ curl "https://api.telegram.org/bot/getUpdates" - `first` - `all` - 当回复线程启用且原始 Telegram 文本或标题可用时,OpenClaw 会自动包含原生 Telegram 引用摘录。Telegram 将原生引用文本限制为 1024 个 UTF-16 码元,因此更长的消息会从开头引用;如果 Telegram 拒绝该引用,则回退到普通回复。 + 启用回复线程且原始 Telegram 文本或说明可用时,OpenClaw 会自动包含一段原生 Telegram 引用摘录。Telegram 将原生引用文本限制为 1024 个 UTF-16 代码单元,因此较长消息会从开头开始引用;如果 Telegram 拒绝该引用,则回退为普通回复。 - 注意:`off` 会禁用隐式回复线程。显式 `[[reply_to_*]]` 标签仍会生效。 + 注意:`off` 会禁用隐式回复线程。显式 `[[reply_to_*]]` 标签仍会被遵循。 - + 论坛超级群组: - topic 会话键追加 `:topic:` - - 回复和输入状态会指向 topic 线程 + - 回复和正在输入状态会指向 topic 线程 - topic 配置路径: `channels.telegram.groups..topics.` 常规 topic(`threadId=1`)特殊情况: - 消息发送会省略 `message_thread_id`(Telegram 会拒绝 `sendMessage(...thread_id=1)`) - - 输入状态操作仍会包含 `message_thread_id` + - 正在输入操作仍会包含 `message_thread_id` - topic 继承:topic 条目会继承群组设置,除非被覆盖(`requireMention`、`allowFrom`、`skills`、`systemPrompt`、`enabled`、`groupPolicy`)。 - `agentId` 仅适用于 topic,并且不会从群组默认值继承。 + Topic 继承:topic 条目会继承群组设置,除非被覆盖(`requireMention`、`allowFrom`、`skills`、`systemPrompt`、`enabled`、`groupPolicy`)。 + `agentId` 仅限 topic,不会从群组默认值继承。 - **按 topic 路由智能体**:每个 topic 都可以通过在 topic 配置中设置 `agentId` 路由到不同的智能体。这让每个 topic 都拥有自己的隔离工作区、记忆和会话。示例: + **按 topic 的智能体路由**:每个 topic 都可以通过在 topic 配置中设置 `agentId` 路由到不同智能体。这让每个 topic 都拥有自己的隔离工作区、记忆和会话。示例: ```json5 { @@ -548,26 +548,24 @@ curl "https://api.telegram.org/bot/getUpdates" } ``` - 然后每个 topic 都有自己的会话键:`agent:zu:telegram:group:-1001234567890:topic:3` + 随后每个 topic 都会有自己的会话键:`agent:zu:telegram:group:-1001234567890:topic:3` - **持久 ACP topic 绑定**:论坛 topic 可以通过顶层类型化 ACP 绑定固定 ACP harness 会话(`bindings[]`,其中 `type: "acp"`、`match.channel: "telegram"`、`peer.kind: "group"`,并使用类似 `-1001234567890:topic:42` 的 topic 限定 ID)。目前作用域限定为群组/超级群组中的论坛 topic。参见 [ACP 智能体](/zh-CN/tools/acp-agents)。 + **持久 ACP topic 绑定**:论坛 topic 可以通过顶层 typed ACP 绑定固定 ACP harness 会话(`bindings[]`,其中 `type: "acp"`、`match.channel: "telegram"`、`peer.kind: "group"`,以及类似 `-1001234567890:topic:42` 的 topic 限定 id)。目前作用域限于群组/超级群组中的论坛 topic。参见 [ACP Agents](/zh-CN/tools/acp-agents)。 - **从聊天生成线程绑定 ACP**:`/acp spawn --thread here|auto` 会将当前 topic 绑定到新的 ACP 会话;后续消息会直接路由到那里。OpenClaw 会在 topic 内固定生成确认消息。要求 `channels.telegram.threadBindings.spawnSessions` 保持启用(默认:`true`)。 + **从聊天派生线程绑定 ACP**:`/acp spawn --thread here|auto` 会将当前 topic 绑定到新的 ACP 会话;后续消息会直接路由到那里。OpenClaw 会在 topic 内固定派生确认消息。需要保持启用 `channels.telegram.threadBindings.spawnSessions`(默认:`true`)。 - 模板上下文会暴露 `MessageThreadId` 和 `IsForum`。带有 `message_thread_id` 的私信聊天默认会在扁平会话上保留私信路由和回复元数据;只有当配置了 `threadReplies: "inbound"`、`threadReplies: "always"`、`requireTopic: true` 或匹配的 topic 配置时,它们才会使用线程感知会话键。使用顶层 `channels.telegram.dm.threadReplies` 设置账号默认值,或使用 `direct..threadReplies` 设置单个私信。 + 模板上下文公开 `MessageThreadId` 和 `IsForum`。带有 `message_thread_id` 的私信聊天默认保留私信路由,并在扁平会话上保留回复元数据;只有在配置了 `threadReplies: "inbound"`、`threadReplies: "always"`、`requireTopic: true` 或匹配的 topic 配置时,才会使用线程感知的会话键。使用顶层 `channels.telegram.dm.threadReplies` 作为账户默认值,或使用 `direct..threadReplies` 配置单个私信。 ### 音频消息 - Telegram 区分语音便笺和音频文件。 + Telegram 会区分语音消息和音频文件。 - 默认:音频文件行为 - - 在智能体回复中使用标签 `[[audio_as_voice]]` 可强制作为语音便笺发送 - - 入站语音便笺转录会在智能体上下文中被标记为机器生成的、 - 不受信任文本;提及检测仍会使用原始 - 转录,因此受提及门控的语音消息会继续工作。 + - 在智能体回复中使用标签 `[[audio_as_voice]]` 以强制作为语音消息发送 + - 入站语音消息转写会在智能体上下文中被框定为机器生成的、不受信任文本;提及检测仍使用原始转写,因此需要提及门控的语音消息会继续工作。 消息操作示例: @@ -583,9 +581,9 @@ curl "https://api.telegram.org/bot/getUpdates" ### 视频消息 - Telegram 会区分视频文件和视频消息。 + Telegram 区分视频文件和圆形视频消息。 - 消息操作示例: + 消息 action 示例: ```json5 { @@ -597,14 +595,14 @@ curl "https://api.telegram.org/bot/getUpdates" } ``` - 视频消息不支持说明文字;提供的消息文本会单独发送。 + 圆形视频消息不支持说明文字;提供的消息文本会单独发送。 ### 贴纸 入站贴纸处理: - 静态 WEBP:下载并处理(占位符 ``) - - 动画 TGS:跳过 + - 动态 TGS:跳过 - 视频 WEBM:跳过 贴纸上下文字段: @@ -619,9 +617,9 @@ curl "https://api.telegram.org/bot/getUpdates" - `~/.openclaw/telegram/sticker-cache.json` - 贴纸会被描述一次(可行时),并缓存以减少重复的视觉调用。 + 贴纸会被描述一次(如果可能)并缓存,以减少重复的视觉调用。 - 启用贴纸操作: + 启用贴纸 action: ```json5 { @@ -635,7 +633,7 @@ curl "https://api.telegram.org/bot/getUpdates" } ``` - 发送贴纸操作: + 发送贴纸 action: ```json5 { @@ -660,7 +658,7 @@ curl "https://api.telegram.org/bot/getUpdates" - Telegram 回应会以 `message_reaction` 更新到达(与消息载荷分开)。 + Telegram 回应会作为 `message_reaction` 更新到达(与消息负载分离)。 启用后,OpenClaw 会将如下系统事件加入队列: @@ -671,15 +669,15 @@ curl "https://api.telegram.org/bot/getUpdates" - `channels.telegram.reactionNotifications`:`off | own | all`(默认:`own`) - `channels.telegram.reactionLevel`:`off | ack | minimal | extensive`(默认:`minimal`) - 注意事项: + 注意: - - `own` 表示仅用户对机器人发送消息的回应(通过已发送消息缓存尽力实现)。 + - `own` 表示仅用户对机器人发送消息的回应(通过已发送消息缓存尽力判断)。 - 回应事件仍会遵守 Telegram 访问控制(`dmPolicy`、`allowFrom`、`groupPolicy`、`groupAllowFrom`);未授权发送者会被丢弃。 - Telegram 不会在回应更新中提供话题 ID。 - - 非论坛群组会路由到群聊会话 - - 论坛群组会路由到群组通用话题会话(`:topic:1`),而不是准确的原始话题 + - 非论坛群组路由到群聊会话 + - 论坛群组路由到群组通用话题会话(`:topic:1`),而不是确切的原始话题 - 轮询/webhook 的 `allowed_updates` 会自动包含 `message_reaction`。 + 用于轮询/webhook 的 `allowed_updates` 会自动包含 `message_reaction`。 @@ -691,19 +689,19 @@ curl "https://api.telegram.org/bot/getUpdates" - `channels.telegram.accounts..ackReaction` - `channels.telegram.ackReaction` - `messages.ackReaction` - - 智能体身份 emoji 回退(`agents.list[].identity.emoji`,否则为 "👀") + - agent 身份 emoji 回退(`agents.list[].identity.emoji`,否则为 "👀") - 注意事项: + 注意: - Telegram 需要 unicode emoji(例如 "👀")。 - - 使用 `""` 可为某个渠道或账号禁用回应。 + - 使用 `""` 可为某个 channel 或账户禁用该回应。 - 渠道配置写入默认启用(`configWrites !== false`)。 + channel 配置写入默认启用(`configWrites !== false`)。 - Telegram 触发的写入包括: + 由 Telegram 触发的写入包括: - 群组迁移事件(`migrate_to_chat_id`),用于更新 `channels.telegram.groups` - `/config set` 和 `/config unset`(需要启用命令) @@ -723,28 +721,29 @@ curl "https://api.telegram.org/bot/getUpdates" - 默认使用长轮询。对于 webhook 模式,设置 `channels.telegram.webhookUrl` 和 `channels.telegram.webhookSecret`;可选 `webhookPath`、`webhookHost`、`webhookPort`(默认值为 `/telegram-webhook`、`127.0.0.1`、`8787`)。 + 默认使用长轮询。对于 webhook 模式,请设置 `channels.telegram.webhookUrl` 和 `channels.telegram.webhookSecret`;可选项包括 `webhookPath`、`webhookHost`、`webhookPort`(默认值为 `/telegram-webhook`、`127.0.0.1`、`8787`)。 - 本地监听器绑定到 `127.0.0.1:8787`。对于公网入口,可以在本地端口前放置反向代理,或有意设置 `webhookHost: "0.0.0.0"`。 + 本地监听器绑定到 `127.0.0.1:8787`。对于公开入口,要么在本地端口前放置反向代理,要么有意设置 `webhookHost: "0.0.0.0"`。 - Webhook 模式会在向 Telegram 返回 `200` 前校验请求防护、Telegram 密钥令牌和 JSON 正文。 - 然后 OpenClaw 会通过长轮询使用的同一组按聊天/按话题的机器人通道异步处理该更新,因此较慢的智能体轮次不会阻塞 Telegram 的投递确认。 + Webhook 模式会先验证请求防护、Telegram secret token 和 JSON 正文,然后再向 Telegram 返回 `200`。 + 随后 OpenClaw 会通过与长轮询相同的按聊天/按话题机器人通道异步处理更新,因此缓慢的 agent 回合不会阻塞 Telegram 的投递 ACK。 - `channels.telegram.textChunkLimit` 默认值为 4000。 - - `channels.telegram.chunkMode="newline"` 会在按长度拆分前优先使用段落边界(空行)。 - - `channels.telegram.mediaMaxMb`(默认 100)会限制入站和出站 Telegram 媒体大小。 - - `channels.telegram.timeoutSeconds` 会覆盖 Telegram API 客户端超时(如果未设置,则使用 grammY 默认值)。机器人客户端会将配置值限制在 60 秒出站文本/输入状态请求防护以内,这样 grammY 不会在 OpenClaw 的传输防护和回退运行前中止可见回复投递。长轮询仍使用 45 秒的 `getUpdates` 请求防护,因此空闲轮询不会被无限期遗弃。 + - `channels.telegram.chunkMode="newline"` 在按长度拆分前优先使用段落边界(空行)。 + - `channels.telegram.mediaMaxMb`(默认 100)限制入站和出站 Telegram 媒体大小。 + - `channels.telegram.mediaGroupFlushMs`(默认 500)控制在 OpenClaw 将 Telegram 相册/媒体组作为一条入站消息分发前缓冲多久。如果相册部分到达较晚,请增大该值;如果要降低相册回复延迟,请减小该值。 + - `channels.telegram.timeoutSeconds` 会覆盖 Telegram API 客户端超时(如果未设置,则使用 grammY 默认值)。机器人客户端会将配置值限制在 60 秒出站文本/正在输入请求防护以下,以便 grammY 不会在 OpenClaw 的传输防护和回退运行前中止可见回复投递。长轮询仍使用 45 秒 `getUpdates` 请求防护,因此空闲轮询不会被无限期放弃。 - `channels.telegram.pollingStallThresholdMs` 默认值为 `120000`;仅在误报轮询停滞重启时,在 `30000` 到 `600000` 之间调整。 - 群组上下文历史使用 `channels.telegram.historyLimit` 或 `messages.groupChat.historyLimit`(默认 50);`0` 表示禁用。 - - 回复/引用/转发补充上下文目前会按接收内容传递。 - - Telegram 允许列表主要控制谁可以触发智能体,而不是完整的补充上下文脱敏边界。 + - 回复/引用/转发的补充上下文目前按接收内容传递。 + - Telegram 允许列表主要控制谁可以触发 agent,而不是完整的补充上下文删减边界。 - 私信历史控制: - `channels.telegram.dmHistoryLimit` - `channels.telegram.dms[""].historyLimit` - - `channels.telegram.retry` 配置适用于 Telegram 发送辅助函数(CLI/工具/操作),用于可恢复的出站 API 错误。入站最终回复投递也会对 Telegram 预连接失败使用有界安全发送重试,但不会重试可能导致可见消息重复的模糊发送后网络包络。 + - `channels.telegram.retry` 配置适用于 Telegram 发送辅助方法(CLI/工具/action),用于可恢复的出站 API 错误。入站最终回复投递也会对 Telegram 预连接失败使用有界安全发送重试,但不会重试可能导致可见消息重复的不明确发送后网络信封。 CLI 发送目标可以是数字聊天 ID 或用户名: @@ -763,7 +762,7 @@ openclaw message poll --channel telegram --target -1001234567890:topic:42 \ --poll-duration-seconds 300 --poll-public ``` - 仅限 Telegram 的投票标志: + 仅 Telegram 支持的投票标志: - `--poll-duration-seconds`(5-600) - `--poll-anonymous` @@ -772,48 +771,48 @@ openclaw message poll --channel telegram --target -1001234567890:topic:42 \ Telegram 发送还支持: - - 当 `channels.telegram.capabilities.inlineButtons` 允许时,使用带有 `buttons` 块的 `--presentation` 来创建内联键盘 - - 当机器人可在该聊天中置顶时,使用 `--pin` 或 `--delivery '{"pin":true}'` 请求置顶投递 - - 使用 `--force-document` 将出站图片和 GIF 作为文档发送,而不是压缩照片或动画媒体上传 + - 当 `channels.telegram.capabilities.inlineButtons` 允许时,使用带有 `buttons` 块的 `--presentation` 来生成内联键盘 + - 当机器人可以在该聊天中置顶时,使用 `--pin` 或 `--delivery '{"pin":true}'` 请求置顶投递 + - 使用 `--force-document` 将出站图片和 GIF 作为文档发送,而不是作为压缩照片或动态媒体上传 - 操作门控: + Action 门控: - `channels.telegram.actions.sendMessage=false` 会禁用出站 Telegram 消息,包括投票 - - `channels.telegram.actions.poll=false` 会禁用 Telegram 投票创建,同时保留常规发送功能 + - `channels.telegram.actions.poll=false` 会禁用 Telegram 投票创建,但保留常规发送启用 - - Telegram 支持在审批者私信中进行执行审批,并且可以选择在原始聊天或话题中发布提示。审批者必须是数字 Telegram 用户 ID。 + + Telegram 支持在审批者私信中进行 exec 审批,并可选择在原始聊天或话题中发布提示。审批者必须是数字 Telegram 用户 ID。 配置路径: - - `channels.telegram.execApprovals.enabled`(至少有一个审批者可解析时自动启用) - - `channels.telegram.execApprovals.approvers`(回退到来自 `commands.ownerAllowFrom` 的数字所有者 ID) + - `channels.telegram.execApprovals.enabled`(当至少一个审批者可解析时自动启用) + - `channels.telegram.execApprovals.approvers`(回退到 `commands.ownerAllowFrom` 中的数字所有者 ID) - `channels.telegram.execApprovals.target`:`dm`(默认)| `channel` | `both` - `agentFilter`、`sessionFilter` - `channels.telegram.allowFrom`、`groupAllowFrom` 和 `defaultTo` 控制谁可以与机器人对话,以及机器人向哪里发送普通回复。它们不会让某人成为执行审批者。当还没有命令所有者时,第一次获批的私信配对会引导设置 `commands.ownerAllowFrom`,因此单所有者设置仍可工作,无需在 `execApprovals.approvers` 下重复 ID。 + `channels.telegram.allowFrom`、`groupAllowFrom` 和 `defaultTo` 控制谁可以与机器人对话,以及机器人将普通回复发送到哪里。它们不会让某人成为 exec 审批者。当还没有命令所有者时,首次获批的私信配对会引导写入 `commands.ownerAllowFrom`,因此单所有者设置仍然无需在 `execApprovals.approvers` 下重复 ID 即可工作。 - 渠道投递会在聊天中显示命令文本;仅在可信群组/话题中启用 `channel` 或 `both`。当提示落在论坛话题中时,OpenClaw 会为审批提示和后续消息保留该话题。执行审批默认 30 分钟后过期。 + channel 投递会在聊天中显示命令文本;仅在可信群组/话题中启用 `channel` 或 `both`。当提示落在论坛话题中时,OpenClaw 会为审批提示和后续消息保留该话题。Exec 审批默认在 30 分钟后过期。 - 内联审批按钮还要求 `channels.telegram.capabilities.inlineButtons` 允许目标表面(`dm`、`group` 或 `all`)。带有 `plugin:` 前缀的审批 ID 会通过插件审批解析;其他 ID 会先通过执行审批解析。 + 内联审批按钮还要求 `channels.telegram.capabilities.inlineButtons` 允许目标表面(`dm`、`group` 或 `all`)。以 `plugin:` 为前缀的审批 ID 会通过插件审批解析;其他 ID 会先通过 exec 审批解析。 - 请参阅[执行审批](/zh-CN/tools/exec-approvals)。 + 参见 [Exec 审批](/zh-CN/tools/exec-approvals)。 ## 错误回复控制 -当智能体遇到投递或提供商错误时,Telegram 可以回复错误文本,也可以抑制该错误。两个配置键控制此行为: +当 agent 遇到投递或提供商错误时,Telegram 可以回复错误文本,也可以抑制该回复。两个配置键控制此行为: -| 键 | 值 | 默认值 | 描述 | -| ----------------------------------- | ----------------- | ------- | -------------------------------------------------------------------------------------------------- | -| `channels.telegram.errorPolicy` | `reply`, `silent` | `reply` | `reply` 会向聊天发送友好的错误消息。`silent` 会完全抑制错误回复。 | -| `channels.telegram.errorCooldownMs` | number (ms) | `60000` | 向同一聊天发送错误回复之间的最短时间。可防止中断期间出现错误刷屏。 | +| 键 | 值 | 默认值 | 描述 | +| ----------------------------------- | ----------------- | ------- | ---------------------------------------------------------------------------------------- | +| `channels.telegram.errorPolicy` | `reply`, `silent` | `reply` | `reply` 会向聊天发送友好的错误消息。`silent` 会完全抑制错误回复。 | +| `channels.telegram.errorCooldownMs` | number (ms) | `60000` | 向同一聊天发送错误回复之间的最短时间。防止故障期间产生错误刷屏。 | -支持按账号、按群组和按话题覆盖(继承方式与其他 Telegram 配置键相同)。 +支持按账户、按群组和按话题覆盖(继承方式与其他 Telegram 配置键相同)。 ```json5 { @@ -834,22 +833,22 @@ openclaw message poll --channel telegram --target -1001234567890:topic:42 \ ## 故障排除 - + - 如果 `requireMention=false`,Telegram 隐私模式必须允许完整可见性。 - BotFather:`/setprivacy` -> Disable - 然后将机器人从群组移除并重新添加 - - 当配置预期未提及的群组消息时,`openclaw channels status` 会发出警告。 - - `openclaw channels status --probe` 可以检查显式数字群组 ID;通配符 `"*"` 不能进行成员探测。 + - 当配置预期接收未提及的群组消息时,`openclaw channels status` 会发出警告。 + - `openclaw channels status --probe` 可以检查显式数字群组 ID;通配符 `"*"` 无法进行成员身份探测。 - 快速会话测试:`/activation always`。 - - 当 `channels.telegram.groups` 存在时,群组必须列出(或包含 `"*"`) - - 验证机器人是群组成员 - - 查看日志:`openclaw logs --follow` 了解跳过原因 + - 当存在 `channels.telegram.groups` 时,必须列出该群组(或包含 `"*"`) + - 验证机器人在群组中的成员身份 + - 查看日志:`openclaw logs --follow` 以了解跳过原因 @@ -857,32 +856,32 @@ openclaw message poll --channel telegram --target -1001234567890:topic:42 \ - 授权你的发送者身份(配对和/或数字 `allowFrom`) - 即使群组策略为 `open`,命令授权仍然适用 - - `setMyCommands failed` 携带 `BOT_COMMANDS_TOO_MUCH` 表示原生命令菜单条目过多;减少插件/skill/自定义命令,或禁用原生命令菜单 - - `deleteMyCommands` / `setMyCommands` 启动调用和 `sendChatAction` 输入状态调用是有界的,并会在请求超时时通过 Telegram 的传输回退重试一次。持续的网络/fetch 错误通常表示到 `api.telegram.org` 的 DNS/HTTPS 可达性问题 + - `setMyCommands failed` 并带有 `BOT_COMMANDS_TOO_MUCH` 表示原生命令菜单条目过多;减少插件/skill/自定义命令,或禁用原生命令菜单 + - 启动时的 `deleteMyCommands` / `setMyCommands` 调用以及 `sendChatAction` 正在输入调用都是有界的,并会在请求超时时通过 Telegram 的传输回退重试一次。持续的网络/fetch 错误通常表示到 `api.telegram.org` 的 DNS/HTTPS 可达性问题 - + - - `getMe returned 401` 是已配置 bot token 的 Telegram 身份验证失败。 - - 在 BotFather 中重新复制或重新生成 bot token,然后为默认账号更新 `channels.telegram.botToken`、`channels.telegram.tokenFile`、`channels.telegram.accounts..botToken` 或 `TELEGRAM_BOT_TOKEN`。 - - 启动期间的 `deleteWebhook 401 Unauthorized` 也是身份验证失败;将它当作“没有 webhook 存在”只会把同一个错误 token 失败推迟到后续 API 调用中。 + - `getMe returned 401` 是配置的机器人令牌发生 Telegram 身份验证失败。 + - 在 BotFather 中重新复制或重新生成机器人令牌,然后为默认账户更新 `channels.telegram.botToken`、`channels.telegram.tokenFile`、`channels.telegram.accounts..botToken` 或 `TELEGRAM_BOT_TOKEN`。 + - 启动期间的 `deleteWebhook 401 Unauthorized` 也是身份验证失败;把它当作“不存在 webhook”只会把同一个无效令牌失败推迟到后续 API 调用。 - - Node 22+ + 自定义 fetch/proxy 在 AbortSignal 类型不匹配时可能触发立即中止行为。 - - 有些主机会先将 `api.telegram.org` 解析为 IPv6;损坏的 IPv6 出站可能导致间歇性的 Telegram API 失败。 - - 如果日志包含 `TypeError: fetch failed` 或 `Network request for 'getUpdates' failed!`,OpenClaw 现在会将这些错误作为可恢复的网络错误进行重试。 - - 在轮询启动期间,OpenClaw 会为 grammY 复用成功的启动 `getMe` 探测,因此运行器在第一次 `getUpdates` 前不需要第二次 `getMe`。 + - Node 22+ + 自定义 fetch/proxy 可能会在 AbortSignal 类型不匹配时触发立即中止行为。 + - 某些主机会先把 `api.telegram.org` 解析到 IPv6;损坏的 IPv6 出站连接可能导致间歇性的 Telegram API 失败。 + - 如果日志包含 `TypeError: fetch failed` 或 `Network request for 'getUpdates' failed!`,OpenClaw 现在会将这些作为可恢复的网络错误重试。 + - 在轮询启动期间,OpenClaw 会为 grammY 复用成功的启动 `getMe` 探测,因此运行器在第一次 `getUpdates` 之前不需要第二次 `getMe`。 - 如果 `deleteWebhook` 在轮询启动期间因瞬时网络错误失败,OpenClaw 会继续进入长轮询,而不是再发起一次轮询前控制平面调用。仍处于活动状态的 webhook 会表现为 `getUpdates` 冲突;随后 OpenClaw 会重建 Telegram 传输并重试 webhook 清理。 - - 如果 Telegram 套接字按很短的固定周期回收,请检查 `channels.telegram.timeoutSeconds` 是否过低;bot 客户端会把低于出站和 `getUpdates` 请求保护值的配置值夹紧,但旧版本在该值低于这些保护值时,可能会中止每次轮询或回复。 - - 如果日志包含 `Polling stall detected`,默认情况下,OpenClaw 会在 120 秒内没有完成长轮询存活性检查后重启轮询并重建 Telegram 传输。 - - 当正在运行的轮询账号在启动宽限期后仍未完成 `getUpdates`、正在运行的 webhook 账号在启动宽限期后仍未完成 `setWebhook`,或最后一次成功的轮询传输活动已过期时,`openclaw channels status --probe` 和 `openclaw doctor` 会发出警告。 - - 只有在长时间运行的 `getUpdates` 调用健康,但你的主机仍报告误报的轮询停滞重启时,才增大 `channels.telegram.pollingStallThresholdMs`。持续停滞通常指向主机与 `api.telegram.org` 之间的代理、DNS、IPv6 或 TLS 出站问题。 - - Telegram 还会遵循 Bot API 传输的进程代理环境变量,包括 `HTTP_PROXY`、`HTTPS_PROXY`、`ALL_PROXY` 及其小写变体。`NO_PROXY` / `no_proxy` 仍然可以绕过 `api.telegram.org`。 - - 如果服务环境通过 `OPENCLAW_PROXY_URL` 配置了 OpenClaw 托管代理,且不存在标准代理环境变量,Telegram 也会将该 URL 用于 Bot API 传输。 + - 如果 Telegram 套接字按很短的固定周期回收,请检查是否有过低的 `channels.telegram.timeoutSeconds`;机器人客户端会将低于出站和 `getUpdates` 请求保护阈值的配置值钳制到保护阈值以上,但旧版本在该值设置低于这些保护阈值时,可能会中止每次轮询或回复。 + - 如果日志包含 `Polling stall detected`,默认情况下,OpenClaw 会在 120 秒内没有完成长轮询活性检查后重启轮询并重建 Telegram 传输。 + - 当正在运行的轮询账户在启动宽限期后尚未完成 `getUpdates`、正在运行的 webhook 账户在启动宽限期后尚未完成 `setWebhook`,或上一次成功的轮询传输活动已经陈旧时,`openclaw channels status --probe` 和 `openclaw doctor` 会发出警告。 + - 只有当长时间运行的 `getUpdates` 调用是健康的,但你的主机仍然报告误判的轮询停滞重启时,才增加 `channels.telegram.pollingStallThresholdMs`。持续停滞通常指向主机与 `api.telegram.org` 之间的代理、DNS、IPv6 或 TLS 出站问题。 + - Telegram 还会遵循进程代理环境变量用于 Bot API 传输,包括 `HTTP_PROXY`、`HTTPS_PROXY`、`ALL_PROXY` 及其小写变体。`NO_PROXY` / `no_proxy` 仍可绕过 `api.telegram.org`。 + - 如果 OpenClaw 托管代理在服务环境中通过 `OPENCLAW_PROXY_URL` 配置,且不存在标准代理环境变量,Telegram 也会将该 URL 用于 Bot API 传输。 - 在直接出站/TLS 不稳定的 VPS 主机上,通过 `channels.telegram.proxy` 路由 Telegram API 调用: ```yaml @@ -891,8 +890,8 @@ channels: proxy: socks5://:@proxy-host:1080 ``` - - Node 22+ 默认使用 `autoSelectFamily=true`(WSL2 除外)。Telegram DNS 结果顺序会依次遵循 `OPENCLAW_TELEGRAM_DNS_RESULT_ORDER`、`channels.telegram.network.dnsResultOrder`,再到进程默认值,例如 `NODE_OPTIONS=--dns-result-order=ipv4first`;如果都不适用,Node 22+ 会回退到 `ipv4first`。 - - 如果你的主机是 WSL2,或明确在仅 IPv4 行为下运行更好,请强制选择地址族: + - Node 22+ 默认使用 `autoSelectFamily=true`(WSL2 除外)。Telegram DNS 结果顺序依次遵循 `OPENCLAW_TELEGRAM_DNS_RESULT_ORDER`、`channels.telegram.network.dnsResultOrder`,再到进程默认值,例如 `NODE_OPTIONS=--dns-result-order=ipv4first`;如果都不适用,Node 22+ 会回退到 `ipv4first`。 + - 如果你的主机是 WSL2,或明确更适合仅 IPv4 行为,请强制选择地址族: ```yaml channels: @@ -901,7 +900,7 @@ channels: autoSelectFamily: false ``` - - 默认情况下,Telegram 媒体下载已允许 RFC 2544 基准测试范围的应答(`198.18.0.0/15`)。如果受信任的 fake-IP 或透明代理在媒体下载期间将 `api.telegram.org` 重写为其他私有/内部/特殊用途地址,你可以选择启用仅限 Telegram 的绕过: + - 默认情况下,Telegram 媒体下载已允许 RFC 2544 基准测试范围地址(`198.18.0.0/15`)。如果可信的 fake-IP 或透明代理在媒体下载期间把 `api.telegram.org` 重写为其他私有/内部/特殊用途地址,你可以选择启用仅限 Telegram 的绕过: ```yaml channels: @@ -910,19 +909,20 @@ channels: dangerouslyAllowPrivateNetwork: true ``` - - 同一个可选启用项也可按账号设置在 - `channels.telegram.accounts..network.dangerouslyAllowPrivateNetwork`。 - - 如果你的代理将 Telegram 媒体主机解析为 `198.18.x.x`,请先保持危险标志关闭。默认情况下,Telegram 媒体已允许 RFC 2544 基准测试范围。 + - 同一个选择启用项也可按账户在 + `channels.telegram.accounts..network.dangerouslyAllowPrivateNetwork` 配置。 + - 如果你的代理将 Telegram 媒体主机解析为 `198.18.x.x`,请先保持危险标志关闭。Telegram 媒体默认已允许 RFC 2544 基准测试范围。 - `channels.telegram.network.dangerouslyAllowPrivateNetwork` 会削弱 Telegram 媒体 SSRF 防护。仅在受信任、由操作方控制的代理环境中使用它,例如 Clash、Mihomo 或 Surge fake-IP 路由,并且这些环境会合成 RFC 2544 基准测试范围之外的私有或特殊用途应答。正常公共互联网 Telegram 访问请保持关闭。 + `channels.telegram.network.dangerouslyAllowPrivateNetwork` 会削弱 Telegram + 媒体 SSRF 防护。仅在可信、由操作员控制的代理环境中使用它,例如 Clash、Mihomo 或 Surge fake-IP 路由,且它们会在 RFC 2544 基准测试范围之外合成私有或特殊用途答案。对于正常的公网 Telegram 访问,请保持关闭。 - 环境覆盖(临时): - `OPENCLAW_TELEGRAM_DISABLE_AUTO_SELECT_FAMILY=1` - `OPENCLAW_TELEGRAM_ENABLE_AUTO_SELECT_FAMILY=1` - `OPENCLAW_TELEGRAM_DNS_RESULT_ORDER=ipv4first` - - 验证 DNS 应答: + - 验证 DNS 答案: ```bash dig +short api.telegram.org A @@ -940,25 +940,25 @@ dig +short api.telegram.org AAAA -- 启动/身份验证:`enabled`、`botToken`、`tokenFile`、`accounts.*`(`tokenFile` 必须指向普通文件;符号链接会被拒绝) +- 启动/身份验证:`enabled`、`botToken`、`tokenFile`、`accounts.*`(`tokenFile` 必须指向常规文件;符号链接会被拒绝) - 访问控制:`dmPolicy`、`allowFrom`、`groupPolicy`、`groupAllowFrom`、`groups`、`groups.*.topics.*`、顶层 `bindings[]`(`type: "acp"`) -- exec 审批:`execApprovals`、`accounts.*.execApprovals` +- 执行批准:`execApprovals`、`accounts.*.execApprovals` - 命令/菜单:`commands.native`、`commands.nativeSkills`、`customCommands` - 线程/回复:`replyToMode`、`dm.threadReplies`、`direct.*.threadReplies` - 流式传输:`streaming`(预览)、`streaming.preview.toolProgress`、`blockStreaming` - 格式化/投递:`textChunkLimit`、`chunkMode`、`linkPreview`、`responsePrefix` -- 媒体/网络:`mediaMaxMb`、`timeoutSeconds`、`pollingStallThresholdMs`、`retry`、`network.autoSelectFamily`、`network.dangerouslyAllowPrivateNetwork`、`proxy` -- 自定义 API 根路径:`apiRoot`(仅 Bot API 根路径;不要包含 `/bot`) +- 媒体/网络:`mediaMaxMb`、`mediaGroupFlushMs`、`timeoutSeconds`、`pollingStallThresholdMs`、`retry`、`network.autoSelectFamily`、`network.dangerouslyAllowPrivateNetwork`、`proxy` +- 自定义 API 根:`apiRoot`(仅 Bot API 根;不要包含 `/bot`) - webhook:`webhookUrl`、`webhookSecret`、`webhookPath`、`webhookHost` - 操作/能力:`capabilities.inlineButtons`、`actions.sendMessage|editMessage|deleteMessage|reactions|sticker` -- reactions:`reactionNotifications`、`reactionLevel` +- 表情回应:`reactionNotifications`、`reactionLevel` - 错误:`errorPolicy`、`errorCooldownMs` - 写入/历史:`configWrites`、`historyLimit`、`dmHistoryLimit`、`dms.*.historyLimit` -多账号优先级:配置两个或更多账号 ID 时,设置 `channels.telegram.defaultAccount`(或包含 `channels.telegram.accounts.default`)以明确默认路由。否则,OpenClaw 会回退到第一个规范化账号 ID,并且 `openclaw doctor` 会发出警告。命名账号会继承 `channels.telegram.allowFrom` / `groupAllowFrom`,但不会继承 `accounts.default.*` 值。 +多账户优先级:配置两个或更多账户 ID 时,请设置 `channels.telegram.defaultAccount`(或包含 `channels.telegram.accounts.default`)以明确默认路由。否则 OpenClaw 会回退到第一个规范化账户 ID,且 `openclaw doctor` 会发出警告。命名账户会继承 `channels.telegram.allowFrom` / `groupAllowFrom`,但不会继承 `accounts.default.*` 值。 ## 相关内容 diff --git a/docs/zh-CN/plugins/hooks.md b/docs/zh-CN/plugins/hooks.md index cff3c2dbb..2c33bc1b6 100644 --- a/docs/zh-CN/plugins/hooks.md +++ b/docs/zh-CN/plugins/hooks.md @@ -1,26 +1,26 @@ --- read_when: - 你正在构建一个需要 before_tool_call、before_agent_reply、消息钩子或生命周期钩子的插件 - - 你需要阻止、重写或要求审批来自插件的工具调用 - - 你正在决定使用内部钩子还是插件钩子 + - 你需要阻止、重写或要求批准插件发起的工具调用 + - 你正在内部钩子和插件钩子之间做选择 summary: 插件钩子:拦截智能体、工具、消息、会话和 Gateway 网关生命周期事件 title: 插件钩子 x-i18n: - generated_at: "2026-05-02T03:10:25Z" + generated_at: "2026-05-03T19:29:30Z" model: gpt-5.5 provider: openai - source_hash: 4efb07c6211debb5a7915d63678b1695946a91600c54d31faa0edf7025fbabf0 + source_hash: 2c4ed060f1b89917e1f2f46d2da9448cd562edbcd6ce03bc9b1a83da3ed9a591 source_path: plugins/hooks.md workflow: 16 --- 插件钩子是 OpenClaw 插件的进程内扩展点。当插件需要检查或更改智能体运行、工具调用、消息流、会话生命周期、子智能体路由、安装或 Gateway 网关启动时使用它们。 -如果你想为 `/new`、`/reset`、`/stop`、`agent:bootstrap` 或 `gateway:startup` 等命令和 Gateway 网关事件使用一个由操作员安装的小型 `HOOK.md` 脚本,请改用[内部钩子](/zh-CN/automation/hooks)。 +当你想要一个由操作者安装的小型 `HOOK.md` 脚本,用于 `/new`、`/reset`、`/stop`、`agent:bootstrap` 或 `gateway:startup` 等命令和 Gateway 网关事件时,请改用 [内部钩子](/zh-CN/automation/hooks)。 ## 快速开始 -从你的插件入口使用 `api.on(...)` 注册类型化插件钩子: +从你的插件入口使用 `api.on(...)` 注册带类型的插件钩子: ```typescript import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry"; @@ -52,34 +52,56 @@ export default definePluginEntry({ }); ``` -钩子处理程序会按 `priority` 降序依次运行。相同优先级的钩子保持注册顺序。 +钩子处理程序会按 `priority` 降序顺序运行。相同优先级的钩子保持注册顺序。 `api.on(name, handler, opts?)` 接受: -- `priority` — 处理程序排序(数值越高越先运行)。 -- `timeoutMs` — 可选的单钩子预算。设置后,钩子运行器会在预算耗尽后中止该处理程序并继续下一个,而不是让缓慢的设置或召回工作消耗调用方配置的模型超时时间。省略它则使用钩子运行器通用应用的默认观察/决策超时。 +- `priority` — 处理程序排序(值越高越先运行)。 +- `timeoutMs` — 可选的单钩子预算。设置后,钩子运行器会在预算耗尽后中止该处理程序并继续下一个,而不是让缓慢的设置或回忆工作消耗调用方配置的模型超时。省略它则使用钩子运行器通用应用的默认观察/决策超时。 -每个钩子都会收到 `event.context.pluginConfig`,也就是注册该处理程序的插件的已解析配置。需要当前插件选项来做钩子决策时使用它;OpenClaw 会按处理程序注入它,而不会改变其他插件看到的共享事件对象。 +操作者也可以在不修补插件代码的情况下设置钩子预算: + +```json +{ + "plugins": { + "entries": { + "my-plugin": { + "hooks": { + "timeoutMs": 30000, + "timeouts": { + "before_prompt_build": 90000, + "agent_end": 60000 + } + } + } + } + } +} +``` + +`hooks.timeouts.` 会覆盖 `hooks.timeoutMs`,后者会覆盖插件作者提供的 `api.on(..., { timeoutMs })` 值。每个配置值都必须是正整数,且不大于 600000 毫秒。对于已知较慢的钩子,优先使用单钩子覆盖,这样一个插件就不会在所有位置都获得更长预算。 + +每个钩子都会接收 `event.context.pluginConfig`,也就是注册该处理程序的插件解析后的配置。将它用于需要当前插件选项的钩子决策;OpenClaw 会按处理程序注入它,而不会改变其他插件看到的共享事件对象。 ## 钩子目录 -钩子按其扩展的表面分组。**粗体**名称接受决策结果(阻止、取消、覆盖或要求批准);其他所有钩子仅用于观察。 +钩子按它们扩展的表面分组。**粗体**名称接受决策结果(阻止、取消、覆盖或要求批准);其他所有钩子仅用于观察。 **智能体轮次** - `before_model_resolve` — 在会话消息加载前覆盖提供商或模型 -- `agent_turn_prepare` — 消费排队的插件轮次注入,并在提示钩子前添加同轮上下文 -- `before_prompt_build` — 在模型调用前添加动态上下文或系统提示文本 -- `before_agent_start` — 仅兼容用的组合阶段;优先使用上面的两个钩子 -- **`before_agent_reply`** — 用合成回复或静默短路模型轮次 -- **`before_agent_finalize`** — 检查自然的最终答案并请求再进行一次模型传递 +- `agent_turn_prepare` — 消耗排队的插件轮次注入,并在提示词钩子前添加同轮次上下文 +- `before_prompt_build` — 在模型调用前添加动态上下文或系统提示词文本 +- `before_agent_start` — 仅兼容用的组合阶段;优先使用上面两个钩子 +- **`before_agent_reply`** — 使用合成回复或静默来短路模型轮次 +- **`before_agent_finalize`** — 检查自然最终答案并请求再进行一次模型传递 - `agent_end` — 观察最终消息、成功状态和运行时长 -- `heartbeat_prompt_contribution` — 为后台监控器和生命周期插件添加仅 Heartbeat 使用的上下文 +- `heartbeat_prompt_contribution` — 为后台监控和生命周期插件添加仅 Heartbeat 的上下文 **对话观察** -- `model_call_started` / `model_call_ended` — 观察经过清理的提供商/模型调用元数据、计时、结果,以及不含提示或响应内容的有界请求 ID 哈希 -- `llm_input` — 观察提供商输入(系统提示、提示、历史) +- `model_call_started` / `model_call_ended` — 在不包含提示词或响应内容的情况下,观察经过清理的提供商/模型调用元数据、计时、结果和有界请求 ID 哈希 +- `llm_input` — 观察提供商输入(系统提示词、提示词、历史记录) - `llm_output` — 观察提供商输出 **工具** @@ -87,16 +109,16 @@ export default definePluginEntry({ - **`before_tool_call`** — 重写工具参数、阻止执行或要求批准 - `after_tool_call` — 观察工具结果、错误和时长 - **`tool_result_persist`** — 重写由工具结果生成的助手消息 -- **`before_message_write`** — 检查或阻止进行中的消息写入(少见) +- **`before_message_write`** — 检查或阻止正在进行的消息写入(少见) **消息和投递** - **`inbound_claim`** — 在智能体路由前认领入站消息(合成回复) -- `message_received` — 观察入站内容、发送方、会话串和元数据 +- `message_received` — 观察入站内容、发送者、线程和元数据 - **`message_sending`** — 重写出站内容或取消投递 - `message_sent` — 观察出站投递成功或失败 -- **`before_dispatch`** — 在渠道交接前检查或重写出站派发 -- **`reply_dispatch`** — 参与最终回复派发流水线 +- **`before_dispatch`** — 在渠道移交前检查或重写出站分发 +- **`reply_dispatch`** — 参与最终回复分发流水线 **会话和压缩** @@ -110,13 +132,13 @@ export default definePluginEntry({ **生命周期** -- `gateway_start` / `gateway_stop` — 随 Gateway 网关启动或停止插件拥有的服务 -- `cron_changed` — 观察 Gateway 网关拥有的 cron 生命周期变化(已添加、已更新、已移除、已启动、已完成、已调度) -- **`before_install`** — 检查技能或插件安装扫描,并可选择阻止 +- `gateway_start` / `gateway_stop` — 随 Gateway 网关启动或停止插件自有服务 +- `cron_changed` — 观察 Gateway 网关自有 cron 生命周期变化(已添加、已更新、已移除、已启动、已完成、已调度) +- **`before_install`** — 检查 Skills 或插件安装扫描并可选阻止 ## 工具调用策略 -`before_tool_call` 会收到: +`before_tool_call` 接收: - `event.toolName` - `event.params` @@ -148,42 +170,42 @@ type BeforeToolCallResult = { 规则: - `block: true` 是终止性决策,并会跳过较低优先级的处理程序。 -- `block: false` 会被视为无决策。 +- `block: false` 会被视为没有决策。 - `params` 会重写用于执行的工具参数。 -- `requireApproval` 会暂停智能体运行,并通过插件批准请求用户确认。`/approve` 命令可以同时批准 exec 和插件批准。 -- 在较高优先级钩子请求批准后,较低优先级的 `block: true` 仍然可以阻止。 -- `onResolution` 会收到已解析的批准决策 — `allow-once`、`allow-always`、`deny`、`timeout` 或 `cancelled`。 +- `requireApproval` 会暂停智能体运行,并通过插件批准询问用户。`/approve` 命令可以同时批准 exec 和插件批准。 +- 即使较高优先级的钩子已请求批准,较低优先级的 `block: true` 仍然可以阻止。 +- `onResolution` 会接收已解析的批准决策 — `allow-once`、`allow-always`、`deny`、`timeout` 或 `cancelled`。 -需要主机级策略的内置插件可以使用 `api.registerTrustedToolPolicy(...)` 注册受信工具策略。这些策略会在普通 `before_tool_call` 钩子和外部插件决策之前运行。仅将其用于主机信任的门禁,例如工作区策略、预算执行或保留工作流安全。外部插件应使用普通 `before_tool_call` 钩子。 +需要主机级策略的内置插件可以用 `api.registerTrustedToolPolicy(...)` 注册受信任的工具策略。这些策略会在普通 `before_tool_call` 钩子之前、外部插件决策之前运行。只应将它们用于主机信任的门控,例如工作区策略、预算执行或保留工作流安全。外部插件应使用普通 `before_tool_call` 钩子。 ### 工具结果持久化 -工具结果可以包含结构化 `details`,用于 UI 渲染、诊断、媒体路由或插件拥有的元数据。将 `details` 视为运行时元数据,而不是提示内容: +工具结果可以包含结构化的 `details`,用于 UI 渲染、诊断、媒体路由或插件自有元数据。将 `details` 视为运行时元数据,而不是提示词内容: -- OpenClaw 会在提供商重放和压缩输入前剥离 `toolResult.details`,因此元数据不会成为模型上下文。 -- 持久化的会话条目只保留有界的 `details`。超大的 details 会被替换为紧凑摘要和 `persistedDetailsTruncated: true`。 -- `tool_result_persist` 和 `before_message_write` 会在最终持久化上限之前运行。钩子仍应保持返回的 `details` 很小,并避免只把与提示相关的文本放在 `details` 中;将模型可见的工具输出放入 `content`。 +- OpenClaw 会在提供商重放和压缩输入前剥离 `toolResult.details`,这样元数据就不会成为模型上下文。 +- 持久化的会话条目只保留有界的 `details`。过大的 details 会被替换为紧凑摘要和 `persistedDetailsTruncated: true`。 +- `tool_result_persist` 和 `before_message_write` 会在最终持久化上限前运行。钩子仍应保持返回的 `details` 较小,并避免只把与提示词相关的文本放在 `details` 中;请将模型可见的工具输出放在 `content` 中。 -## 提示和模型钩子 +## 提示词和模型钩子 新插件请使用特定阶段的钩子: -- `before_model_resolve`:只接收当前提示和附件元数据。返回 `providerOverride` 或 `modelOverride`。 -- `agent_turn_prepare`:接收当前提示、已准备好的会话消息,以及为此会话清空的任何恰好一次排队注入。返回 `prependContext` 或 `appendContext`。 -- `before_prompt_build`:接收当前提示和会话消息。返回 `prependContext`、`appendContext`、`systemPrompt`、`prependSystemContext` 或 `appendSystemContext`。 -- `heartbeat_prompt_contribution`:仅在 Heartbeat 轮次中运行,并返回 `prependContext` 或 `appendContext`。它面向需要汇总当前状态而不改变用户发起轮次的后台监控器。 +- `before_model_resolve`:仅接收当前提示词和附件元数据。返回 `providerOverride` 或 `modelOverride`。 +- `agent_turn_prepare`:接收当前提示词、准备好的会话消息,以及为此会话排空的任何仅一次排队注入。返回 `prependContext` 或 `appendContext`。 +- `before_prompt_build`:接收当前提示词和会话消息。返回 `prependContext`、`appendContext`、`systemPrompt`、`prependSystemContext` 或 `appendSystemContext`。 +- `heartbeat_prompt_contribution`:仅针对 Heartbeat 轮次运行,并返回 `prependContext` 或 `appendContext`。它适用于需要在不改变用户发起轮次的情况下汇总当前状态的后台监控。 -`before_agent_start` 保留用于兼容。优先使用上面的显式钩子,避免你的插件依赖旧版组合阶段。 +`before_agent_start` 仍保留用于兼容。优先使用上面的显式钩子,这样你的插件就不会依赖旧版组合阶段。 -当 OpenClaw 可以识别活跃运行时,`before_agent_start` 和 `agent_end` 会包含 `event.runId`。同一值也可在 `ctx.runId` 上取得。cron 驱动的运行还会暴露 `ctx.jobId`(来源 cron 作业 ID),以便插件钩子可以将指标、副作用或状态限定到特定定时任务。 +当 OpenClaw 能识别活跃运行时,`before_agent_start` 和 `agent_end` 会包含 `event.runId`。同一个值也可通过 `ctx.runId` 访问。cron 驱动的运行还会暴露 `ctx.jobId`(来源 cron 作业 ID),以便插件钩子可以把指标、副作用或状态限定到特定定时任务。 -对于源自渠道的运行,`ctx.messageProvider` 是 `discord` 或 `telegram` 等提供商表面,而当 OpenClaw 可以从会话键或投递元数据推导时,`ctx.channelId` 是对话目标标识符。 +对于源自渠道的运行,`ctx.messageProvider` 是 `discord` 或 `telegram` 这样的提供商表面,而 `ctx.channelId` 是当 OpenClaw 可以从会话键或投递元数据推导时得到的对话目标标识符。 -`agent_end` 是观察钩子,会在轮次之后以即发即忘方式运行。钩子运行器会应用 30 秒超时,避免卡住的插件或嵌入端点让钩子 promise 永久挂起。超时会被记录,OpenClaw 会继续;除非插件也使用自己的中止信号,否则它不会取消插件拥有的网络工作。 +`agent_end` 是观察钩子,会在轮次之后即发即忘地运行。钩子运行器会应用 30 秒超时,这样卡住的插件或嵌入端点就不会让钩子 Promise 永久挂起。超时会被记录,OpenClaw 会继续;除非插件也使用自己的中止信号,否则它不会取消插件自有的网络工作。 -如果提供商调用遥测不应接收原始提示、历史、响应、标头、请求正文或提供商请求 ID,请使用 `model_call_started` 和 `model_call_ended`。这些钩子包含稳定的元数据,例如 `runId`、`callId`、`provider`、`model`、可选的 `api`/`transport`、终止状态 `durationMs`/`outcome`,以及 OpenClaw 可以推导有界提供商请求 ID 哈希时的 `upstreamRequestIdHash`。 +使用 `model_call_started` 和 `model_call_ended` 来做不应接收原始提示词、历史记录、响应、标头、请求正文或提供商请求 ID 的提供商调用遥测。这些钩子包含稳定元数据,例如 `runId`、`callId`、`provider`、`model`、可选的 `api`/`transport`、终止态 `durationMs`/`outcome`,以及当 OpenClaw 能推导有界提供商请求 ID 哈希时的 `upstreamRequestIdHash`。 -`before_agent_finalize` 只会在 harness 即将接受自然的最终助手答案时运行。它不是 `/stop` 取消路径,并且不会在用户中止某个轮次时运行。返回 `{ action: "revise", reason }` 可要求 harness 在最终确定前再进行一次模型传递,返回 `{ action: "finalize", reason? }` 可强制最终确定,或省略结果以继续。Codex 原生 `Stop` 钩子会作为 OpenClaw `before_agent_finalize` 决策中继到此钩子。 +`before_agent_finalize` 仅在 harness 即将接受自然最终助手答案时运行。它不是 `/stop` 取消路径,并且在用户中止轮次时不会运行。返回 `{ action: "revise", reason }` 可请求 harness 在最终化前再进行一次模型传递,返回 `{ action: "finalize", reason? }` 可强制最终化,或省略结果以继续。Codex 原生 `Stop` 钩子会作为 OpenClaw `before_agent_finalize` 决策转发到此钩子中。 需要 `llm_input`、`llm_output`、`before_agent_finalize` 或 `agent_end` 的非内置插件必须设置: @@ -201,73 +223,71 @@ type BeforeToolCallResult = { } ``` -可以按插件通过 `plugins.entries..hooks.allowPromptInjection=false` 禁用会改变提示的钩子和持久的下一轮注入。 +可以按插件用 `plugins.entries..hooks.allowPromptInjection=false` 禁用提示词变更钩子和持久的下一轮注入。 ### 会话扩展和下一轮注入 -工作流插件可以用 `api.registerSessionExtension(...)` 持久化小型 JSON 兼容会话状态,并通过 Gateway 网关 `sessions.pluginPatch` 方法更新它。会话行会通过 `pluginExtensions` 投影已注册的扩展状态,让 Control UI 和其他客户端无需了解插件内部机制即可渲染插件拥有的 Status。 +工作流插件可以用 `api.registerSessionExtension(...)` 持久化小型 JSON 兼容会话状态,并通过 Gateway 网关 `sessions.pluginPatch` 方法更新它。会话行会通过 `pluginExtensions` 投影已注册的扩展状态,让 Control UI 和其他客户端无需了解插件内部机制即可渲染插件自有状态。 -当插件需要让持久上下文恰好一次到达下一次模型轮次时,使用 `api.enqueueNextTurnInjection(...)`。OpenClaw 会在提示钩子前清空排队注入,丢弃已过期的注入,并按每个插件的 `idempotencyKey` 去重。对于应在下一轮对模型可见但不应成为永久系统提示文本的批准恢复、策略摘要、后台监控器增量和命令延续,这是合适的扩展点。 +使用 `api.enqueueNextTurnInjection(...)`,让插件在需要持久上下文时只精确一次地到达下一次模型回合。OpenClaw 会在提示词钩子之前排空队列中的注入,丢弃已过期的注入,并按每个插件的 `idempotencyKey` 去重。对于审批恢复、策略摘要、后台监控增量,以及应该在下一回合对模型可见但不应成为永久系统提示词文本的命令续接,这是合适的接口边界。 -清理语义是契约的一部分。会话扩展清理和运行时生命周期清理回调会收到 `reset`、`delete`、`disable` 或 `restart`。对于 reset/delete/disable,主机会移除所属插件的持久会话扩展状态和待处理的下一轮注入;restart 会保留持久会话状态,同时清理回调让插件释放旧运行时世代的调度器作业、运行上下文和其他带外资源。 +清理语义是契约的一部分。会话扩展清理和运行时生命周期清理回调会收到 `reset`、`delete`、`disable` 或 `restart`。对于重置/删除/禁用,宿主会移除所属插件的持久会话扩展状态和待处理的下一回合注入;重启会保留持久会话状态,同时清理回调让插件释放旧运行时代次的调度器作业、运行上下文和其他带外资源。 ## 消息钩子 -使用消息钩子来处理渠道级路由和投递策略: +使用消息钩子处理渠道级路由和投递策略: -- `message_received`:观察入站内容、发送者、`threadId`、`messageId`、 - `senderId`、可选的运行/会话关联以及元数据。 -- `message_sending`:重写 `content`,或返回 `{ cancel: true }`。 +- `message_received`:观察传入内容、发送者、`threadId`、`messageId`、`senderId`、可选的运行/会话关联,以及元数据。 +- `message_sending`:重写 `content` 或返回 `{ cancel: true }`。 - `message_sent`:观察最终成功或失败。 -对于仅音频 TTS 回复,即使渠道载荷没有可见文本/说明文字,`content` 也可能包含隐藏的朗读文本。重写该 -`content` 只会更新钩子可见的文本;它不会渲染为媒体说明文字。 +对于仅音频的 TTS 回复,即使渠道载荷没有可见文本/说明文字,`content` 也可能包含隐藏的朗读转录文本。重写该 `content` 只会更新钩子可见的转录文本;它不会渲染为媒体说明文字。 消息钩子上下文会在可用时暴露稳定的关联字段: `ctx.sessionKey`、`ctx.runId`、`ctx.messageId`、`ctx.senderId`、`ctx.trace`、 -`ctx.traceId`、`ctx.spanId`、`ctx.parentSpanId` 和 `ctx.callDepth`。在读取旧版元数据前,优先使用这些一等字段。 +`ctx.traceId`、`ctx.spanId`、`ctx.parentSpanId` 和 `ctx.callDepth`。优先使用这些一等字段,再读取旧版元数据。 -在使用渠道特定元数据前,优先使用有类型的 `threadId` 和 `replyToId` 字段。 +优先使用类型化的 `threadId` 和 `replyToId` 字段,再使用渠道特定元数据。 决策规则: -- 带有 `cancel: true` 的 `message_sending` 是终止性的。 -- 带有 `cancel: false` 的 `message_sending` 会被视为无决策。 +- 带有 `cancel: true` 的 `message_sending` 是终止决策。 +- 带有 `cancel: false` 的 `message_sending` 会视为未作决策。 - 重写后的 `content` 会继续传递给较低优先级的钩子,除非后续钩子取消投递。 ## 安装钩子 -`before_install` 在内置 Skills 和插件安装扫描之后运行。返回额外发现,或返回 `{ block: true, blockReason }` 以停止安装。 +`before_install` 会在内置 Skills 和插件安装扫描之后运行。返回额外发现,或返回 `{ block: true, blockReason }` 以停止安装。 -`block: true` 是终止性的。`block: false` 会被视为无决策。 +`block: true` 是终止决策。`block: false` 会视为未作决策。 ## Gateway 网关生命周期 -对需要 Gateway 网关拥有的状态的插件服务使用 `gateway_start`。上下文会暴露 `ctx.config`、`ctx.workspaceDir` 和 `ctx.getCron?.()`,用于 cron 检查和更新。使用 `gateway_stop` 清理长期运行的资源。 +对于需要 Gateway 网关所拥有状态的插件服务,请使用 `gateway_start`。上下文会暴露 `ctx.config`、`ctx.workspaceDir` 和用于 cron 检查与更新的 `ctx.getCron?.()`。使用 `gateway_stop` 清理长期运行的资源。 -不要依赖内部 `gateway:startup` 钩子来运行插件拥有的运行时服务。 +不要依赖内部 `gateway:startup` 钩子来实现插件自有的运行时服务。 -`cron_changed` 会针对 Gateway 网关拥有的 cron 生命周期事件触发,并带有有类型的事件载荷,涵盖 `added`、`updated`、`removed`、`started`、`finished` 和 `scheduled` 原因。该事件携带一个 `PluginHookGatewayCronJob` 快照(包括存在时的 `state.nextRunAtMs`、`state.lastRunStatus` 和 `state.lastError`),以及值为 `not-requested` | `delivered` | `not-delivered` | `unknown` 的 `PluginHookGatewayCronDeliveryStatus`。移除事件仍会携带已删除的作业快照,以便外部调度器协调状态。同步外部唤醒调度器时,使用运行时上下文中的 `ctx.getCron?.()` 和 `ctx.config`,并将 OpenClaw 保持为到期检查和执行的事实来源。 +`cron_changed` 会针对 Gateway 网关所拥有的 cron 生命周期事件触发,带有类型化事件载荷,涵盖 `added`、`updated`、`removed`、`started`、`finished` 和 `scheduled` 原因。事件携带一个 `PluginHookGatewayCronJob` 快照(包括 `state.nextRunAtMs`、`state.lastRunStatus`,以及存在时的 `state.lastError`)和一个 `PluginHookGatewayCronDeliveryStatus`,其值为 `not-requested` | `delivered` | `not-delivered` | `unknown`。移除事件仍会携带已删除的作业快照,以便外部调度器协调状态。同步外部唤醒调度器时,请使用运行时上下文中的 `ctx.getCron?.()` 和 `ctx.config`,并让 OpenClaw 作为到期检查和执行的事实来源。 -## 即将弃用的内容 +## 即将弃用 -少数与钩子相邻的表面已弃用,但仍受支持。请在下一个主版本发布前迁移: +少数与钩子相邻的接口已弃用,但仍受支持。请在下一个主版本发布前迁移: -- **明文渠道信封**,位于 `inbound_claim` 和 `message_received` - 处理器中。请读取 `BodyForAgent` 和结构化用户上下文块,而不是解析扁平信封文本。请参阅 - [明文渠道信封 → BodyForAgent](/zh-CN/plugins/sdk-migration#active-deprecations)。 -- **`before_agent_start`** 保留用于兼容性。新插件应使用 +- **纯文本渠道信封**,位于 `inbound_claim` 和 `message_received` + 处理器中。读取 `BodyForAgent` 和结构化用户上下文块,而不是解析扁平信封文本。参见 + [纯文本渠道信封 → BodyForAgent](/zh-CN/plugins/sdk-migration#active-deprecations)。 +- **`before_agent_start`** 会保留用于兼容。新插件应使用 `before_model_resolve` 和 `before_prompt_build`,而不是组合阶段。 -- **`before_tool_call` 中的 `onResolution`** 现在使用有类型的 +- **`before_tool_call` 中的 `onResolution`** 现在使用类型化 `PluginApprovalResolution` 联合类型(`allow-once` / `allow-always` / `deny` / - `timeout` / `cancelled`),而不是自由形式的 `string`。 + `timeout` / `cancelled`),而不是自由格式的 `string`。 -完整列表包括记忆能力注册、提供商思考配置、外部身份验证提供商、提供商发现类型、任务运行时访问器,以及 `command-auth` → `command-status` 重命名,请参阅 -[插件 SDK 迁移 → 活跃弃用项](/zh-CN/plugins/sdk-migration#active-deprecations)。 +完整列表包括记忆能力注册、提供商思考配置文件、外部认证提供商、提供商发现类型、任务运行时访问器,以及 `command-auth` → `command-status` 重命名,请参见 +[插件 SDK 迁移 → 当前弃用项](/zh-CN/plugins/sdk-migration#active-deprecations)。 ## 相关内容 -- [插件 SDK 迁移](/zh-CN/plugins/sdk-migration) — 活跃弃用项和移除时间线 +- [插件 SDK 迁移](/zh-CN/plugins/sdk-migration) — 当前弃用项和移除时间线 - [构建插件](/zh-CN/plugins/building-plugins) - [插件 SDK 概览](/zh-CN/plugins/sdk-overview) - [插件入口点](/zh-CN/plugins/sdk-entrypoints)