chore(i18n): refresh zh-CN translations
This commit is contained in:
parent
e0fc7b18f5
commit
9afa141488
File diff suppressed because it is too large
Load Diff
@ -2,25 +2,25 @@
|
||||
read_when:
|
||||
- 你正在构建一个新的消息渠道插件
|
||||
- 你想将 OpenClaw 连接到一个消息平台
|
||||
- 你需要了解 `ChannelPlugin` 适配器接口 surface
|
||||
- 你需要理解 `ChannelPlugin` 适配器接口。
|
||||
sidebarTitle: Channel Plugins
|
||||
summary: 为 OpenClaw 构建消息渠道插件的分步指南
|
||||
title: 构建渠道插件
|
||||
x-i18n:
|
||||
generated_at: "2026-04-10T20:41:22Z"
|
||||
generated_at: "2026-04-14T16:24:20Z"
|
||||
model: gpt-5.4
|
||||
provider: openai
|
||||
source_hash: 8a026e924f9ae8a3ddd46287674443bcfccb0247be504261522b078e1f440aef
|
||||
source_hash: a7f4c746fe3163a8880e14c433f4db4a1475535d91716a53fb879551d8d62f65
|
||||
source_path: plugins/sdk-channel-plugins.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
# 构建渠道插件
|
||||
|
||||
本指南将带你构建一个将 OpenClaw 连接到消息平台的渠道插件。完成后,你将拥有一个可工作的渠道,具备私信安全、配对、回复线程处理以及出站消息能力。
|
||||
本指南将带你构建一个把 OpenClaw 连接到消息平台的渠道插件。完成后,你将拥有一个可正常工作的渠道,具备私信安全、配对、回复线程以及出站消息能力。
|
||||
|
||||
<Info>
|
||||
如果你之前还没有构建过任何 OpenClaw 插件,请先阅读
|
||||
如果你之前没有构建过任何 OpenClaw 插件,请先阅读
|
||||
[入门指南](/zh-CN/plugins/building-plugins),了解基础包结构和清单设置。
|
||||
</Info>
|
||||
|
||||
@ -28,57 +28,61 @@ x-i18n:
|
||||
|
||||
渠道插件不需要自己的发送/编辑/响应工具。OpenClaw 在核心中保留了一个共享的 `message` 工具。你的插件负责:
|
||||
|
||||
- **配置** — 账号解析和设置向导
|
||||
- **安全** — 私信策略和允许列表
|
||||
- **配对** — 私信批准流程
|
||||
- **会话语法** — 提供商特定的会话 id 如何映射到基础聊天、线程 id 和父级回退
|
||||
- **出站** — 向平台发送文本、媒体和投票
|
||||
- **线程处理** — 回复如何在线程中组织
|
||||
- **配置** —— 账户解析和设置向导
|
||||
- **安全** —— 私信策略和允许列表
|
||||
- **配对** —— 私信批准流程
|
||||
- **会话语法** —— 提供商特定的会话 id 如何映射到基础聊天、线程 id 和父级回退
|
||||
- **出站** —— 向平台发送文本、媒体和投票
|
||||
- **线程** —— 回复如何建立线程
|
||||
|
||||
核心负责共享的消息工具、提示词接线、外层会话键形状、通用 `:thread:` 记录以及分发。
|
||||
|
||||
如果你的平台在会话 id 中存储了额外作用域,请将该解析逻辑保留在插件中,并使用 `messaging.resolveSessionConversation(...)`。这是将 `rawId` 映射为基础会话 id、可选线程 id、显式 `baseConversationId` 以及任意 `parentConversationCandidates` 的规范钩子。
|
||||
当你返回 `parentConversationCandidates` 时,请按从最窄父级到最宽泛/基础会话的顺序排列。
|
||||
如果你的渠道为消息工具添加了携带媒体来源的参数,请通过 `describeMessageTool(...).mediaSourceParams` 暴露这些参数名。核心使用这个显式列表来进行沙箱路径规范化和出站媒体访问策略处理,因此插件不需要为提供商特定的头像、附件或封面图片参数添加共享核心特例。
|
||||
更推荐返回一个按 action 键控的映射,例如
|
||||
`{ "set-profile": ["avatarUrl", "avatarPath"] }`,这样不相关的 action 就不会继承另一个 action 的媒体参数。对于确实要在所有暴露 action 之间共享的参数,扁平数组同样可用。
|
||||
|
||||
需要在渠道注册表启动之前执行相同解析的内置插件,也可以公开一个顶层 `session-key-api.ts` 文件,并提供匹配的 `resolveSessionConversation(...)` 导出。只有在运行时插件注册表尚不可用时,核心才会使用这个可安全用于引导的接口。
|
||||
如果你的平台会在会话 id 中存储额外作用域,请把该解析逻辑保留在插件中,通过 `messaging.resolveSessionConversation(...)` 处理。这是将 `rawId` 映射为基础会话 id、可选线程 id、显式 `baseConversationId` 以及任意 `parentConversationCandidates` 的规范钩子。
|
||||
当你返回 `parentConversationCandidates` 时,请按从最窄父级到最宽/基础会话的顺序排列。
|
||||
|
||||
当插件只需要在通用/原始 id 之上提供父级回退时,`messaging.resolveParentConversationCandidates(...)` 仍然可作为旧版兼容回退方案使用。如果两个钩子都存在,核心会优先使用 `resolveSessionConversation(...).parentConversationCandidates`,只有在规范钩子省略它们时,才会回退到 `resolveParentConversationCandidates(...)`。
|
||||
需要在渠道注册表启动前完成相同解析的内置插件,也可以暴露一个顶层 `session-key-api.ts` 文件,并导出匹配的 `resolveSessionConversation(...)`。只有在运行时插件注册表尚不可用时,核心才会使用这个可安全引导的接口。
|
||||
|
||||
## 批准和渠道能力
|
||||
当插件只需要在通用/原始 id 之上补充父级回退时,`messaging.resolveParentConversationCandidates(...)` 仍可作为旧版兼容回退使用。如果两个钩子都存在,核心会优先使用 `resolveSessionConversation(...).parentConversationCandidates`,只有在规范钩子省略它们时,才会回退到 `resolveParentConversationCandidates(...)`。
|
||||
|
||||
## 批准与渠道能力
|
||||
|
||||
大多数渠道插件不需要特定于批准的代码。
|
||||
|
||||
- 核心负责同一聊天中的 `/approve`、共享的批准按钮负载以及通用回退投递。
|
||||
- 当渠道需要特定于批准的行为时,优先在渠道插件上使用单个 `approvalCapability` 对象。
|
||||
- `ChannelPlugin.approvals` 已移除。请将批准投递/原生/渲染/鉴权相关信息放到 `approvalCapability` 中。
|
||||
- `plugin.auth` 仅用于登录/登出;核心不再从该对象读取批准鉴权钩子。
|
||||
- `approvalCapability.authorizeActorAction` 和 `approvalCapability.getActionAvailabilityState` 是规范的批准鉴权接口。
|
||||
- 对于同一聊天中的批准鉴权可用性,请使用 `approvalCapability.getActionAvailabilityState`。
|
||||
- 如果你的渠道公开原生 exec 批准,请在发起界面/原生客户端状态与同一聊天中的批准鉴权不同时使用 `approvalCapability.getExecInitiatingSurfaceState`。核心使用这个特定于 exec 的钩子来区分 `enabled` 与 `disabled`、判断发起渠道是否支持原生 exec 批准,并在原生客户端回退指南中包含该渠道。`createApproverRestrictedNativeApprovalCapability(...)` 为常见情况提供了这一能力。
|
||||
- 对于渠道特定的负载生命周期行为,例如隐藏重复的本地批准提示,或在投递前发送输入中指示器,请使用 `outbound.shouldSuppressLocalPayloadPrompt` 或 `outbound.beforeDeliverPayload`。
|
||||
- 仅在原生批准路由或回退抑制时使用 `approvalCapability.delivery`。
|
||||
- 对于渠道自有的原生批准事实,请使用 `approvalCapability.nativeRuntime`。在高频渠道入口点上,请通过 `createLazyChannelApprovalNativeRuntimeAdapter(...)` 保持其惰性加载;这样它可以按需导入你的运行时模块,同时仍让核心组装批准生命周期。
|
||||
- 仅当渠道确实需要自定义批准负载而不是共享渲染器时,才使用 `approvalCapability.render`。
|
||||
- 当渠道希望禁用路径回复能够说明启用原生 exec 批准所需的精确配置项时,请使用 `approvalCapability.describeExecApprovalSetup`。该钩子接收 `{ channel, channelLabel, accountId }`;命名账号渠道应渲染带账号作用域的路径,例如 `channels.<channel>.accounts.<id>.execApprovals.*`,而不是顶层默认值。
|
||||
- 如果渠道可以从现有配置中推断出稳定的 owner 类似私信身份,请使用来自 `openclaw/plugin-sdk/approval-runtime` 的 `createResolvedApproverActionAuthAdapter`,以在不添加特定于批准的核心逻辑的情况下限制同一聊天中的 `/approve`。
|
||||
- 如果渠道需要原生批准投递,请让渠道代码专注于目标规范化以及传输/呈现事实。请使用来自 `openclaw/plugin-sdk/approval-runtime` 的 `createChannelExecApprovalProfile`、`createChannelNativeOriginTargetResolver`、`createChannelApproverDmTargetResolver` 和 `createApproverRestrictedNativeApprovalCapability`。将渠道特定事实放在 `approvalCapability.nativeRuntime` 之后,理想情况下通过 `createChannelApprovalNativeRuntimeAdapter(...)` 或 `createLazyChannelApprovalNativeRuntimeAdapter(...)`,这样核心就可以组装处理器并负责请求过滤、路由、去重、过期、Gateway 网关订阅以及“已路由到其他位置”的通知。`nativeRuntime` 被拆分为几个更小的接口:
|
||||
- `availability` — 账号是否已配置,以及请求是否应被处理
|
||||
- `presentation` — 将共享批准视图模型映射为待处理/已解决/已过期的原生负载或最终操作
|
||||
- `transport` — 准备目标,以及发送/更新/删除原生批准消息
|
||||
- `interactions` — 原生按钮或响应的可选绑定/解绑/清除操作钩子
|
||||
- `observe` — 可选的投递诊断钩子
|
||||
- 如果渠道需要运行时自有对象,例如客户端、令牌、Bolt 应用或 webhook 接收器,请通过 `openclaw/plugin-sdk/channel-runtime-context` 注册它们。通用运行时上下文注册表让核心能够从渠道启动状态中引导基于能力的处理器,而无需添加特定于批准的包装胶水代码。
|
||||
- 仅当基于能力的接口还不够有表达力时,才使用更底层的 `createChannelApprovalHandler` 或 `createChannelNativeApprovalRuntime`。
|
||||
- 原生批准渠道必须通过这些辅助工具同时路由 `accountId` 和 `approvalKind`。`accountId` 使多账号批准策略保持在正确的机器人账号作用域内,而 `approvalKind` 让渠道在不在核心中编写硬编码分支的情况下获得 exec 与插件批准行为。
|
||||
- 现在连批准重路由通知也由核心负责。渠道插件不应再从 `createChannelNativeApprovalRuntime` 发送自己的“批准已转到私信/其他渠道”后续消息;相反,应通过共享批准能力辅助工具公开准确的来源和 approver 私信路由,并让核心在向发起聊天回发任何通知前聚合实际投递结果。
|
||||
- 端到端保留已投递的批准 id 类型。原生客户端不应根据渠道本地状态猜测或重写 exec 与插件批准路由。
|
||||
- 不同的批准类型可以有意公开不同的原生界面。
|
||||
当前的内置示例:
|
||||
- Slack 对 exec 和插件 id 都保留了原生批准路由能力。
|
||||
- Matrix 对 exec 和插件批准保留相同的原生私信/渠道路由和 reaction 交互体验,同时仍允许鉴权按批准类型不同。
|
||||
- `createApproverRestrictedNativeApprovalAdapter` 仍然作为兼容包装器存在,但新代码应优先使用能力构建器,并在插件上公开 `approvalCapability`。
|
||||
- 核心负责同一聊天中的 `/approve`、共享的批准按钮负载,以及通用回退投递。
|
||||
- 当渠道需要特定于批准的行为时,优先在渠道插件上使用一个 `approvalCapability` 对象。
|
||||
- `ChannelPlugin.approvals` 已移除。请将批准投递/原生/渲染/认证相关信息放到 `approvalCapability` 上。
|
||||
- `plugin.auth` 仅用于登录/登出;核心不再从该对象读取批准认证钩子。
|
||||
- `approvalCapability.authorizeActorAction` 和 `approvalCapability.getActionAvailabilityState` 是规范的批准认证接口。
|
||||
- 对于同一聊天中的批准认证可用性,请使用 `approvalCapability.getActionAvailabilityState`。
|
||||
- 如果你的渠道暴露原生 exec 批准,请在其与同一聊天批准认证不同时使用 `approvalCapability.getExecInitiatingSurfaceState` 来表示发起界面/原生客户端状态。核心使用这个 exec 专用钩子来区分 `enabled` 与 `disabled`、判断发起渠道是否支持原生 exec 批准,并在原生客户端回退指引中包含该渠道。`createApproverRestrictedNativeApprovalCapability(...)` 可为常见情况完成这部分逻辑。
|
||||
- 对于特定于渠道的负载生命周期行为,例如隐藏重复的本地批准提示或在投递前发送输入中指示器,请使用 `outbound.shouldSuppressLocalPayloadPrompt` 或 `outbound.beforeDeliverPayload`。
|
||||
- 仅在进行原生批准路由或抑制回退时使用 `approvalCapability.delivery`。
|
||||
- 对于渠道自有的原生批准事实,请使用 `approvalCapability.nativeRuntime`。在高频渠道入口点中,使用 `createLazyChannelApprovalNativeRuntimeAdapter(...)` 保持其惰性加载;这样可以按需导入你的运行时模块,同时仍允许核心组装批准生命周期。
|
||||
- 只有当渠道确实需要自定义批准负载而不是共享渲染器时,才使用 `approvalCapability.render`。
|
||||
- 如果渠道希望在禁用路径的回复中说明启用原生 exec 批准所需的精确配置项,请使用 `approvalCapability.describeExecApprovalSetup`。该钩子接收 `{ channel, channelLabel, accountId }`;具名账户渠道应渲染带账户作用域的路径,例如 `channels.<channel>.accounts.<id>.execApprovals.*`,而不是顶层默认值。
|
||||
- 如果渠道可以从现有配置中推断出稳定的类似所有者的私信身份,请使用 `openclaw/plugin-sdk/approval-runtime` 中的 `createResolvedApproverActionAuthAdapter`,以限制同一聊天中的 `/approve`,而无需添加特定于批准的核心逻辑。
|
||||
- 如果渠道需要原生批准投递,请让渠道代码专注于目标规范化以及传输/展示事实。使用 `openclaw/plugin-sdk/approval-runtime` 中的 `createChannelExecApprovalProfile`、`createChannelNativeOriginTargetResolver`、`createChannelApproverDmTargetResolver` 和 `createApproverRestrictedNativeApprovalCapability`。将特定于渠道的事实放在 `approvalCapability.nativeRuntime` 后面,理想情况下通过 `createChannelApprovalNativeRuntimeAdapter(...)` 或 `createLazyChannelApprovalNativeRuntimeAdapter(...)`,这样核心就能组装处理器,并负责请求过滤、路由、去重、过期、Gateway 网关订阅以及“已路由到其他地方”通知。`nativeRuntime` 被拆分为几个更小的接口:
|
||||
- `availability` —— 账户是否已配置,以及请求是否应被处理
|
||||
- `presentation` —— 将共享的批准视图模型映射为待处理/已解决/已过期的原生负载或最终动作
|
||||
- `transport` —— 准备目标,并发送/更新/删除原生批准消息
|
||||
- `interactions` —— 原生按钮或响应的可选 bind/unbind/clear-action 钩子
|
||||
- `observe` —— 可选的投递诊断钩子
|
||||
- 如果渠道需要运行时自有对象,例如客户端、令牌、Bolt 应用或 webhook 接收器,请通过 `openclaw/plugin-sdk/channel-runtime-context` 注册它们。通用 runtime-context 注册表让核心可以从渠道启动状态引导基于能力的处理器,而无需添加特定于批准的包装胶水代码。
|
||||
- 仅当基于能力的接口还不足以表达需求时,才使用更底层的 `createChannelApprovalHandler` 或 `createChannelNativeApprovalRuntime`。
|
||||
- 原生批准渠道必须通过这些辅助函数同时路由 `accountId` 和 `approvalKind`。`accountId` 使多账户批准策略保持在正确的机器人账户作用域内,而 `approvalKind` 则使 exec 与插件批准行为能够在渠道中可用,而无需在核心中写死分支。
|
||||
- 核心现在也负责批准重路由通知。渠道插件不应再从 `createChannelNativeApprovalRuntime` 发送自己的“批准已转到私信/另一个渠道”后续消息;应通过共享批准能力辅助函数暴露准确的来源 + 批准者私信路由,并让核心在向发起聊天发送任何通知前先汇总实际投递情况。
|
||||
- 在端到端流程中保留已投递批准 id 的种类。原生客户端不应根据渠道本地状态去猜测或重写 exec 与插件批准的路由。
|
||||
- 不同批准类型可以有意暴露不同的原生界面。
|
||||
当前内置示例:
|
||||
- Slack 对 exec 和插件 id 都保留原生批准路由能力。
|
||||
- Matrix 对 exec 和插件批准保持相同的原生私信/渠道路由与响应 UX,同时仍允许认证因批准类型而异。
|
||||
- `createApproverRestrictedNativeApprovalAdapter` 仍然存在,作为兼容性包装器,但新代码应优先使用能力构建器,并在插件上暴露 `approvalCapability`。
|
||||
|
||||
对于高频渠道入口点,如果你只需要该系列中的某一部分,请优先使用更窄的运行时子路径:
|
||||
对于高频渠道入口点,如果你只需要这个家族中的某一部分,请优先使用更窄的运行时子路径:
|
||||
|
||||
- `openclaw/plugin-sdk/approval-auth-runtime`
|
||||
- `openclaw/plugin-sdk/approval-client-runtime`
|
||||
@ -90,66 +94,65 @@ x-i18n:
|
||||
- `openclaw/plugin-sdk/approval-reply-runtime`
|
||||
- `openclaw/plugin-sdk/channel-runtime-context`
|
||||
|
||||
同样,如果你不需要更宽泛的总接口,请优先使用 `openclaw/plugin-sdk/setup-runtime`、`openclaw/plugin-sdk/setup-adapter-runtime`、`openclaw/plugin-sdk/reply-runtime`、`openclaw/plugin-sdk/reply-dispatch-runtime`、`openclaw/plugin-sdk/reply-reference` 和 `openclaw/plugin-sdk/reply-chunking`。
|
||||
同样地,当你不需要更宽泛的总入口接口时,请优先使用 `openclaw/plugin-sdk/setup-runtime`、
|
||||
`openclaw/plugin-sdk/setup-adapter-runtime`、
|
||||
`openclaw/plugin-sdk/reply-runtime`、
|
||||
`openclaw/plugin-sdk/reply-dispatch-runtime`、
|
||||
`openclaw/plugin-sdk/reply-reference` 和
|
||||
`openclaw/plugin-sdk/reply-chunking`。
|
||||
|
||||
对于设置,具体如下:
|
||||
|
||||
- `openclaw/plugin-sdk/setup-runtime` 包含运行时安全的设置辅助工具:
|
||||
可安全导入的设置补丁适配器(`createPatchedAccountSetupAdapter`、
|
||||
- `openclaw/plugin-sdk/setup-runtime` 覆盖运行时安全的设置辅助工具:
|
||||
导入安全的设置补丁适配器(`createPatchedAccountSetupAdapter`、
|
||||
`createEnvPatchedAccountSetupAdapter`、
|
||||
`createSetupInputPresenceValidator`)、查找说明输出、
|
||||
`promptResolvedAllowFrom`、`splitSetupEntries` 以及委托式
|
||||
setup-proxy 构建器
|
||||
- `openclaw/plugin-sdk/setup-adapter-runtime` 是用于 `createEnvPatchedAccountSetupAdapter` 的窄型、环境感知适配器接口
|
||||
- `openclaw/plugin-sdk/channel-setup` 包含可选安装设置构建器以及一些设置安全的原语:
|
||||
- `openclaw/plugin-sdk/setup-adapter-runtime` 是用于 `createEnvPatchedAccountSetupAdapter` 的窄范围、环境感知适配器接口
|
||||
- `openclaw/plugin-sdk/channel-setup` 覆盖可选安装的设置构建器以及一些设置安全原语:
|
||||
`createOptionalChannelSetupSurface`、`createOptionalChannelSetupAdapter`、
|
||||
|
||||
如果你的渠道支持由环境驱动的设置或鉴权,并且通用启动/配置流程需要在运行时加载前知道这些环境变量名称,请在插件清单中使用 `channelEnvVars` 声明它们。请将渠道运行时 `envVars` 或本地常量仅保留给面向运维者的文案。
|
||||
如果你的渠道支持由环境变量驱动的设置或认证,并且通用启动/配置流程需要在运行时加载前知道这些环境变量名称,请在插件清单中用 `channelEnvVars` 声明它们。将渠道运行时 `envVars` 或本地常量保留给面向操作者的文案使用。
|
||||
`createOptionalChannelSetupWizard`、`DEFAULT_ACCOUNT_ID`、
|
||||
`createTopLevelChannelDmPolicy`、`setSetupChannelEnabled` 和
|
||||
`splitSetupEntries`
|
||||
|
||||
- 只有在你还需要更重型的共享设置/配置辅助工具时,才使用更宽泛的 `openclaw/plugin-sdk/setup` 接口,例如
|
||||
- 只有当你还需要更重的共享设置/配置辅助工具时,才使用更宽泛的 `openclaw/plugin-sdk/setup` 接口,例如
|
||||
`moveSingleAccountChannelSectionToDefaultAccount(...)`
|
||||
|
||||
如果你的渠道只想在设置界面中提示“请先安装此插件”,优先使用 `createOptionalChannelSetupSurface(...)`。生成的适配器/向导会在配置写入和最终完成时默认关闭,并在校验、完成和文档链接文案中复用同一条“需要安装”的消息。
|
||||
如果你的渠道只想在设置界面中提示“请先安装此插件”,优先使用 `createOptionalChannelSetupSurface(...)`。生成的 adapter/wizard 会在配置写入和最终化时默认关闭,并且会在验证、最终化以及文档链接文案中复用同一条“需要安装”消息。
|
||||
|
||||
对于其他高频渠道路径,请优先使用窄型辅助工具,而不是更宽泛的旧接口:
|
||||
对于其他高频渠道路径,请优先使用窄接口辅助工具,而不是更宽泛的旧版接口:
|
||||
|
||||
- `openclaw/plugin-sdk/account-core`、
|
||||
`openclaw/plugin-sdk/account-id`、
|
||||
`openclaw/plugin-sdk/account-resolution` 和
|
||||
`openclaw/plugin-sdk/account-helpers`,用于多账号配置和
|
||||
默认账号回退
|
||||
`openclaw/plugin-sdk/account-helpers`,用于多账户配置和默认账户回退
|
||||
- `openclaw/plugin-sdk/inbound-envelope` 和
|
||||
`openclaw/plugin-sdk/inbound-reply-dispatch`,用于入站路由/信封以及
|
||||
record-and-dispatch 接线
|
||||
`openclaw/plugin-sdk/inbound-reply-dispatch`,用于入站路由/信封以及记录并分发接线
|
||||
- `openclaw/plugin-sdk/messaging-targets`,用于目标解析/匹配
|
||||
- `openclaw/plugin-sdk/outbound-media` 和
|
||||
`openclaw/plugin-sdk/outbound-runtime`,用于媒体加载以及出站
|
||||
身份/发送委托
|
||||
- `openclaw/plugin-sdk/thread-bindings-runtime`,用于线程绑定生命周期
|
||||
和适配器注册
|
||||
- 仅当仍然需要旧版智能体/媒体负载字段布局时,才使用 `openclaw/plugin-sdk/agent-media-payload`
|
||||
- `openclaw/plugin-sdk/telegram-command-config`,用于 Telegram 自定义命令
|
||||
规范化、重复/冲突校验,以及一个回退稳定的命令
|
||||
配置契约
|
||||
`openclaw/plugin-sdk/outbound-runtime`,用于媒体加载以及出站身份/发送委托
|
||||
- `openclaw/plugin-sdk/thread-bindings-runtime`,用于线程绑定生命周期和 adapter 注册
|
||||
- `openclaw/plugin-sdk/agent-media-payload`,仅在仍需要旧版智能体/媒体负载字段布局时使用
|
||||
- `openclaw/plugin-sdk/telegram-command-config`,用于 Telegram 自定义命令规范化、重复/冲突校验,以及具备稳定回退行为的命令配置契约
|
||||
|
||||
仅鉴权渠道通常可以停留在默认路径:核心处理批准,而插件只公开出站/鉴权能力。像 Matrix、Slack、Telegram 和自定义聊天传输这样的原生批准渠道,应使用共享的原生辅助工具,而不是自行实现批准生命周期。
|
||||
仅认证渠道通常可以停留在默认路径:核心负责批准,而插件只需暴露出站/认证能力。像 Matrix、Slack、Telegram 以及自定义聊天传输这类原生批准渠道,应使用共享的原生辅助工具,而不是自行实现批准生命周期。
|
||||
|
||||
## 入站提及策略
|
||||
|
||||
请将入站提及处理拆分为两层:
|
||||
|
||||
- 插件自有的证据收集
|
||||
- 共享策略评估
|
||||
- 共享的策略评估
|
||||
|
||||
共享层请使用 `openclaw/plugin-sdk/channel-inbound`。
|
||||
|
||||
适合放在插件本地逻辑中的内容:
|
||||
|
||||
- 回复给机器人的检测
|
||||
- 引用机器人的检测
|
||||
- 回复机器人检测
|
||||
- 引用机器人检测
|
||||
- 线程参与检查
|
||||
- 服务/系统消息排除
|
||||
- 用于证明机器人参与的平台原生缓存
|
||||
@ -160,12 +163,12 @@ x-i18n:
|
||||
- 显式提及结果
|
||||
- 隐式提及允许列表
|
||||
- 命令绕过
|
||||
- 最终跳过决策
|
||||
- 最终跳过决定
|
||||
|
||||
推荐流程:
|
||||
|
||||
1. 计算本地提及事实。
|
||||
2. 将这些事实传入 `resolveInboundMentionDecision({ facts, policy })`。
|
||||
2. 将这些事实传给 `resolveInboundMentionDecision({ facts, policy })`。
|
||||
3. 在你的入站门控中使用 `decision.effectiveWasMentioned`、`decision.shouldBypassMention` 和 `decision.shouldSkip`。
|
||||
|
||||
```typescript
|
||||
@ -205,7 +208,7 @@ const decision = resolveInboundMentionDecision({
|
||||
if (decision.shouldSkip) return;
|
||||
```
|
||||
|
||||
`api.runtime.channel.mentions` 为已经依赖运行时注入的内置渠道插件公开了同样的共享提及辅助工具:
|
||||
`api.runtime.channel.mentions` 为已经依赖运行时注入的内置渠道插件暴露了同一组共享提及辅助工具:
|
||||
|
||||
- `buildMentionRegexes`
|
||||
- `matchesMentionPatterns`
|
||||
@ -222,9 +225,8 @@ if (decision.shouldSkip) return;
|
||||
<Steps>
|
||||
<a id="step-1-package-and-manifest"></a>
|
||||
<Step title="包和清单">
|
||||
创建标准插件文件。`package.json` 中的 `channel` 字段
|
||||
让它成为一个渠道插件。有关完整的包元数据接口,
|
||||
请参阅 [插件设置和配置](/zh-CN/plugins/sdk-setup#openclaw-channel):
|
||||
创建标准插件文件。`package.json` 中的 `channel` 字段会将其标记为渠道插件。完整的包元数据接口请参见
|
||||
[插件设置和配置](/zh-CN/plugins/sdk-setup#openclaw-channel):
|
||||
|
||||
<CodeGroup>
|
||||
```json package.json
|
||||
@ -238,7 +240,7 @@ if (decision.shouldSkip) return;
|
||||
"channel": {
|
||||
"id": "acme-chat",
|
||||
"label": "Acme Chat",
|
||||
"blurb": "将 OpenClaw 连接到 Acme Chat。"
|
||||
"blurb": "Connect OpenClaw to Acme Chat."
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -250,7 +252,7 @@ if (decision.shouldSkip) return;
|
||||
"kind": "channel",
|
||||
"channels": ["acme-chat"],
|
||||
"name": "Acme Chat",
|
||||
"description": "Acme Chat 渠道插件",
|
||||
"description": "Acme Chat channel plugin",
|
||||
"configSchema": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
@ -274,8 +276,7 @@ if (decision.shouldSkip) return;
|
||||
</Step>
|
||||
|
||||
<Step title="构建渠道插件对象">
|
||||
`ChannelPlugin` 接口具有许多可选的适配器接口。先从
|
||||
最小集合开始——`id` 和 `setup`——然后按需添加适配器。
|
||||
`ChannelPlugin` 接口有许多可选适配器接口。先从最小集合开始——`id` 和 `setup`——然后按需添加适配器。
|
||||
|
||||
创建 `src/channel.ts`:
|
||||
|
||||
@ -285,7 +286,7 @@ if (decision.shouldSkip) return;
|
||||
createChannelPluginBase,
|
||||
} from "openclaw/plugin-sdk/channel-core";
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/channel-core";
|
||||
import { acmeChatApi } from "./client.js"; // 你的平台 API 客户端
|
||||
import { acmeChatApi } from "./client.js"; // your platform API client
|
||||
|
||||
type ResolvedAccount = {
|
||||
accountId: string | null;
|
||||
@ -326,7 +327,7 @@ if (decision.shouldSkip) return;
|
||||
},
|
||||
}),
|
||||
|
||||
// 私信安全:谁可以给机器人发消息
|
||||
// DM security: who can message the bot
|
||||
security: {
|
||||
dm: {
|
||||
channelKey: "acme-chat",
|
||||
@ -336,21 +337,21 @@ if (decision.shouldSkip) return;
|
||||
},
|
||||
},
|
||||
|
||||
// 配对:新私信联系人的批准流程
|
||||
// Pairing: approval flow for new DM contacts
|
||||
pairing: {
|
||||
text: {
|
||||
idLabel: "Acme Chat 用户名",
|
||||
message: "发送此代码以验证你的身份:",
|
||||
idLabel: "Acme Chat username",
|
||||
message: "Send this code to verify your identity:",
|
||||
notify: async ({ target, code }) => {
|
||||
await acmeChatApi.sendDm(target, `Pairing code: ${code}`);
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// 线程处理:回复如何投递
|
||||
// Threading: how replies are delivered
|
||||
threading: { topLevelReplyToMode: "reply" },
|
||||
|
||||
// 出站:向平台发送消息
|
||||
// Outbound: send messages to the platform
|
||||
outbound: {
|
||||
attachedResults: {
|
||||
sendText: async (params) => {
|
||||
@ -371,17 +372,16 @@ if (decision.shouldSkip) return;
|
||||
```
|
||||
|
||||
<Accordion title="`createChatChannelPlugin` 为你做了什么">
|
||||
你无需手动实现底层适配器接口,
|
||||
而是传入声明式选项,由构建器负责组合它们:
|
||||
你无需手动实现底层适配器接口,而是传入声明式选项,由构建器负责组合它们:
|
||||
|
||||
| 选项 | 接线内容 |
|
||||
| --- | --- |
|
||||
| `security.dm` | 根据配置字段生成带作用域的私信安全解析器 |
|
||||
| `pairing.text` | 基于文本代码交换的私信配对流程 |
|
||||
| `threading` | reply-to 模式解析器(固定、账号作用域或自定义) |
|
||||
| `outbound.attachedResults` | 返回结果元数据(消息 ID)的发送函数 |
|
||||
| `security.dm` | 从配置字段解析带作用域的私信安全解析器 |
|
||||
| `pairing.text` | 基于文本和验证码交换的私信配对流程 |
|
||||
| `threading` | 回复模式解析器(固定、账户作用域或自定义) |
|
||||
| `outbound.attachedResults` | 返回结果元数据的发送函数(消息 ID) |
|
||||
|
||||
如果你需要完全控制,也可以直接传入原始适配器对象,而不是声明式选项。
|
||||
如果你需要完全控制,也可以直接传入原始适配器对象,而不是这些声明式选项。
|
||||
</Accordion>
|
||||
|
||||
</Step>
|
||||
@ -396,20 +396,20 @@ if (decision.shouldSkip) return;
|
||||
export default defineChannelPluginEntry({
|
||||
id: "acme-chat",
|
||||
name: "Acme Chat",
|
||||
description: "Acme Chat 渠道插件",
|
||||
description: "Acme Chat channel plugin",
|
||||
plugin: acmeChatPlugin,
|
||||
registerCliMetadata(api) {
|
||||
api.registerCli(
|
||||
({ program }) => {
|
||||
program
|
||||
.command("acme-chat")
|
||||
.description("Acme Chat 管理");
|
||||
.description("Acme Chat management");
|
||||
},
|
||||
{
|
||||
descriptors: [
|
||||
{
|
||||
name: "acme-chat",
|
||||
description: "Acme Chat 管理",
|
||||
description: "Acme Chat management",
|
||||
hasSubcommands: false,
|
||||
},
|
||||
],
|
||||
@ -422,21 +422,16 @@ if (decision.shouldSkip) return;
|
||||
});
|
||||
```
|
||||
|
||||
将渠道自有的 CLI 描述符放在 `registerCliMetadata(...)` 中,这样 OpenClaw
|
||||
就可以在不激活完整渠道运行时的情况下在根帮助中显示它们,
|
||||
同时正常的完整加载仍会使用同一组描述符进行真实命令
|
||||
注册。请将 `registerFull(...)` 保留给仅运行时的工作。
|
||||
如果 `registerFull(...)` 注册 Gateway 网关 RPC 方法,请使用
|
||||
插件特定前缀。核心管理命名空间(`config.*`、
|
||||
`exec.approvals.*`、`wizard.*`、`update.*`)保持保留,并且始终
|
||||
解析为 `operator.admin`。
|
||||
`defineChannelPluginEntry` 会自动处理注册模式拆分。有关全部
|
||||
选项,请参阅 [入口点](/zh-CN/plugins/sdk-entrypoints#definechannelpluginentry)。
|
||||
将渠道自有的 CLI 描述符放在 `registerCliMetadata(...)` 中,这样 OpenClaw 就可以在不激活完整渠道运行时的情况下,在根帮助中展示它们;而正常的完整加载仍会拾取相同的描述符来进行真实命令注册。`registerFull(...)` 应保留给仅运行时需要的工作。
|
||||
如果 `registerFull(...)` 注册 Gateway 网关 RPC 方法,请使用插件专属前缀。核心管理命名空间(`config.*`、
|
||||
`exec.approvals.*`、`wizard.*`、`update.*`)保留给核心使用,并始终解析到 `operator.admin`。
|
||||
`defineChannelPluginEntry` 会自动处理注册模式拆分。所有选项请参见
|
||||
[入口点](/zh-CN/plugins/sdk-entrypoints#definechannelpluginentry)。
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="添加设置入口">
|
||||
创建 `setup-entry.ts`,用于在新手引导期间进行轻量加载:
|
||||
创建 `setup-entry.ts`,用于新手引导期间的轻量加载:
|
||||
|
||||
```typescript setup-entry.ts
|
||||
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
|
||||
@ -445,27 +440,26 @@ if (decision.shouldSkip) return;
|
||||
export default defineSetupPluginEntry(acmeChatPlugin);
|
||||
```
|
||||
|
||||
当渠道被禁用或尚未配置时,OpenClaw 会加载它而不是完整入口。
|
||||
这样可以避免在设置流程期间拉入重量级运行时代码。
|
||||
详见 [设置和配置](/zh-CN/plugins/sdk-setup#setup-entry)。
|
||||
当渠道被禁用或尚未配置时,OpenClaw 会加载这个入口,而不是完整入口。
|
||||
这样可以避免在设置流程中拉入较重的运行时代码。详见
|
||||
[设置和配置](/zh-CN/plugins/sdk-setup#setup-entry)。
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="处理入站消息">
|
||||
你的插件需要从平台接收消息并将其转发给
|
||||
OpenClaw。典型模式是一个 webhook:它验证请求,然后通过你的渠道入站处理器进行分发:
|
||||
你的插件需要从平台接收消息并将其转发给 OpenClaw。典型模式是使用一个 webhook,对请求进行校验,并通过你渠道的入站处理器进行分发:
|
||||
|
||||
```typescript
|
||||
registerFull(api) {
|
||||
api.registerHttpRoute({
|
||||
path: "/acme-chat/webhook",
|
||||
auth: "plugin", // 插件管理的鉴权(请自行验证签名)
|
||||
auth: "plugin", // plugin-managed auth (verify signatures yourself)
|
||||
handler: async (req, res) => {
|
||||
const event = parseWebhookPayload(req);
|
||||
|
||||
// 你的入站处理器将消息分发到 OpenClaw。
|
||||
// 具体接线方式取决于你的平台 SDK —
|
||||
// 可在内置的 Microsoft Teams 或 Google Chat 插件包中查看真实示例。
|
||||
// Your inbound handler dispatches the message to OpenClaw.
|
||||
// The exact wiring depends on your platform SDK —
|
||||
// see a real example in the bundled Microsoft Teams or Google Chat plugin package.
|
||||
await handleAcmeChatInbound(api, event);
|
||||
|
||||
res.statusCode = 200;
|
||||
@ -477,9 +471,8 @@ if (decision.shouldSkip) return;
|
||||
```
|
||||
|
||||
<Note>
|
||||
入站消息处理是渠道特定的。每个渠道插件都负责
|
||||
自己的入站管线。请查看内置渠道插件
|
||||
(例如 Microsoft Teams 或 Google Chat 插件包)中的真实模式。
|
||||
入站消息处理是渠道特定的。每个渠道插件都负责自己的入站流水线。请查看内置渠道插件
|
||||
(例如 Microsoft Teams 或 Google Chat 插件包)了解真实模式。
|
||||
</Note>
|
||||
|
||||
</Step>
|
||||
@ -493,7 +486,7 @@ if (decision.shouldSkip) return;
|
||||
import { acmeChatPlugin } from "./channel.js";
|
||||
|
||||
describe("acme-chat plugin", () => {
|
||||
it("从配置解析账号", () => {
|
||||
it("resolves account from config", () => {
|
||||
const cfg = {
|
||||
channels: {
|
||||
"acme-chat": { token: "test-token", allowFrom: ["user1"] },
|
||||
@ -503,7 +496,7 @@ if (decision.shouldSkip) return;
|
||||
expect(account.token).toBe("test-token");
|
||||
});
|
||||
|
||||
it("在不实体化密钥的情况下检查账号", () => {
|
||||
it("inspects account without materializing secrets", () => {
|
||||
const cfg = {
|
||||
channels: { "acme-chat": { token: "test-token" } },
|
||||
} as any;
|
||||
@ -512,7 +505,7 @@ if (decision.shouldSkip) return;
|
||||
expect(result.tokenStatus).toBe("available");
|
||||
});
|
||||
|
||||
it("报告缺失的配置", () => {
|
||||
it("reports missing config", () => {
|
||||
const cfg = { channels: {} } as any;
|
||||
const result = acmeChatPlugin.setup!.inspectAccount!(cfg, undefined);
|
||||
expect(result.configured).toBe(false);
|
||||
@ -524,55 +517,53 @@ if (decision.shouldSkip) return;
|
||||
pnpm test -- <bundled-plugin-root>/acme-chat/
|
||||
```
|
||||
|
||||
有关共享测试辅助工具,请参阅 [测试](/zh-CN/plugins/sdk-testing)。
|
||||
关于共享测试辅助工具,请参见 [测试](/zh-CN/plugins/sdk-testing)。
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## 文件结构
|
||||
|
||||
```text
|
||||
```
|
||||
<bundled-plugin-root>/acme-chat/
|
||||
├── package.json # `openclaw.channel` 元数据
|
||||
├── package.json # openclaw.channel 元数据
|
||||
├── openclaw.plugin.json # 带配置 schema 的清单
|
||||
├── index.ts # `defineChannelPluginEntry`
|
||||
├── setup-entry.ts # `defineSetupPluginEntry`
|
||||
├── index.ts # defineChannelPluginEntry
|
||||
├── setup-entry.ts # defineSetupPluginEntry
|
||||
├── api.ts # 公共导出(可选)
|
||||
├── runtime-api.ts # 内部运行时导出(可选)
|
||||
└── src/
|
||||
├── channel.ts # 通过 `createChatChannelPlugin` 创建的 `ChannelPlugin`
|
||||
├── channel.ts # 通过 createChatChannelPlugin 创建的 ChannelPlugin
|
||||
├── channel.test.ts # 测试
|
||||
├── client.ts # 平台 API 客户端
|
||||
└── runtime.ts # 运行时存储(如有需要)
|
||||
└── runtime.ts # 运行时存储(如需要)
|
||||
```
|
||||
|
||||
## 高级主题
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="线程处理选项" icon="git-branch" href="/zh-CN/plugins/sdk-entrypoints#registration-mode">
|
||||
固定、账号作用域或自定义回复模式
|
||||
<Card title="线程选项" icon="git-branch" href="/zh-CN/plugins/sdk-entrypoints#registration-mode">
|
||||
固定、账户作用域或自定义回复模式
|
||||
</Card>
|
||||
<Card title="消息工具集成" icon="puzzle" href="/zh-CN/plugins/architecture#channel-plugins-and-the-shared-message-tool">
|
||||
`describeMessageTool` 和 action 发现
|
||||
describeMessageTool 和 action 发现
|
||||
</Card>
|
||||
<Card title="目标解析" icon="crosshair" href="/zh-CN/plugins/architecture#channel-target-resolution">
|
||||
`inferTargetChatType`、`looksLikeId`、`resolveTarget`
|
||||
inferTargetChatType、looksLikeId、resolveTarget
|
||||
</Card>
|
||||
<Card title="运行时辅助工具" icon="settings" href="/zh-CN/plugins/sdk-runtime">
|
||||
通过 `api.runtime` 使用 TTS、STT、媒体、子智能体
|
||||
通过 api.runtime 使用 TTS、STT、媒体、子智能体
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
<Note>
|
||||
一些内置辅助工具接口仍然存在,用于内置插件的维护和兼容性。
|
||||
对于新的渠道插件,它们不是推荐模式;
|
||||
除非你正在直接维护该内置插件家族,否则请优先使用通用 SDK
|
||||
接口中的 channel/setup/reply/runtime 子路径。
|
||||
一些内置辅助接口仍然保留,用于内置插件维护和兼容性。
|
||||
对于新的渠道插件,它们并不是推荐模式;除非你正在直接维护该内置插件家族,否则请优先使用通用 SDK 接口中的 channel/setup/reply/runtime 子路径。
|
||||
</Note>
|
||||
|
||||
## 后续步骤
|
||||
|
||||
- [提供商插件](/zh-CN/plugins/sdk-provider-plugins) — 如果你的插件也提供模型
|
||||
- [插件 SDK 概览](/zh-CN/plugins/sdk-overview) — 完整的子路径导入参考
|
||||
- [插件 SDK 测试](/zh-CN/plugins/sdk-testing) — 测试工具和契约测试
|
||||
- [插件清单](/zh-CN/plugins/manifest) — 完整的清单 schema
|
||||
- [提供商插件](/zh-CN/plugins/sdk-provider-plugins) —— 如果你的插件也提供模型
|
||||
- [SDK 概览](/zh-CN/plugins/sdk-overview) —— 完整子路径导入参考
|
||||
- [插件 SDK 测试](/zh-CN/plugins/sdk-testing) —— 测试工具和契约测试
|
||||
- [插件清单](/zh-CN/plugins/manifest) —— 完整清单 schema
|
||||
|
||||
Loading…
Reference in New Issue
Block a user