OpenClaw与dingding交互逻辑
2026.2.28
版本情况:OpenClaw26.2.26,插件: @openclaw-china/dingtalk@0.1.38 + dingtalk-stream@2.1.
来自🦞 OpenClaw China — China IM Channels
结论先说:
- 本机 OpenClaw -> 钉钉 走的是 Stream 模式,底层是 HTTPS + WebSocket(实际建连地址来自钉钉网关返回的 endpoint,通常是 wss)。
- 心跳是两层:
- dingtalk-stream 的 WebSocket ping/pong 心跳,默认 8 秒。
- 钉钉 SYSTEM 消息里的 KEEPALIVE/ping。
- 发起方向:
- OpenClaw 主动发起建连和 ws ping。
- 钉钉回复 pong,并主动下发 KEEPALIVE/ping/消息回调。
- OpenClaw 对钉钉回调消息主动 ACK(避免服务端重试)。
关键证据与调用链:
- OpenClaw 启动钉钉账户连接:startAccount -> monitorDingtalkProvider dist/index.js:9818
- 创建客户端时明确 keepAlive: true、autoReconnect: false(重连由插件自己的 loop 管) dist/index.js:9259
- dingtalk-stream 先 POST 钉钉网关拿 endpoint + ticket,再 new WebSocket(endpoint?ticket) client.mjs:97 client.mjs:133 constants.mjs:1
- 回调 topic 订阅和消息 ACK client.mjs:66 dist/index.js:8975 dist/index.js:8938 client.mjs:291
心跳细节(关注重点):
- 默认间隔:heartbeat_interval = 8e3(8000ms) client.mjs:29
- 发起方与逻辑:
- OpenClaw 侧定时 socket.ping()(每 8s) client.mjs:139 client.mjs:148
- 收到 pong 后标记存活 client.mjs:152
- 若一个周期内未恢复存活,客户端 terminate() 主动断开 client.mjs:141
- 钉钉侧系统心跳:SYSTEM/KEEPALIVE、SYSTEM/ping 下发给客户端;客户端分别 heartbeat() 或回 code:200 client.mjs:227 client.mjs:231
- OpenClaw 额外连接守护(非 WS 心跳):5s 轮询、30s connect/register 超时、15s 断连宽限、指数退避重连 dist/index.js:9008 dist/index.js:9009 dist/index.js:9010 dist/index.js:9011 dist/index.js:9139
补充一个容易混淆点:
- openclaw.plugin.json 里声明了 connectionMode: stream|webhook,但运行时代码 schema 实际没有用这个字段,实际实现是 dingtalk-stream 路线(即 stream)。 openclaw.plugin.json:14 dist/index.js:4218 dist/index.js:9845
运行态佐证(2026-02-28 本机日志):
- 有 state idle -> connecting -> connected、socket connected、registration not confirmed after 30000ms、state connected -> running reason=traffic confirmed。 /tmp/openclaw/openclaw-2026-02-28.log:735 /tmp/openclaw/openclaw-2026-02-28.log:744 /tmp/openclaw/openclaw-2026-02-28.log:790 /tmp/openclaw/openclaw-2026-02-28.log:811
如果你要,我可以下一步把这条链路画成一张时序图(建连、收消息、ACK、心跳、断线重连)。
一、OpenClaw 连接 DingTalk:方式、协议与心跳机制
更新时间:2026-02-28
适用环境:本机已安装 @openclaw-china/dingtalk@0.1.38 + dingtalk-stream@2.1.4
1. 总体结论
- 连接方式:
OpenClaw dingtalk 插件通过dingtalk-stream SDK连接钉钉 Stream 网关。 - 协议栈:先走 HTTPS 获取连接参数,再走 WebSocket 长连接(通常是
wss://...)。 - 当前本机是 Stream 路线,不是 webhook 入站路由。
- 心跳是双通道:
- 客户端 WS ping/pong(默认 8 秒,客户端发起 ping)。
- 钉钉 SYSTEM 消息 KEEPALIVE/ping(服务端下发,客户端应答/标记存活)。
2. 连接建立时序图
sequenceDiagram
participant GW as OpenClaw Gateway
participant PL as dingtalk 插件
participant SDK as dingtalk-stream(DWClient)
participant DTAPI as DingTalk HTTPS API
participant DTS as DingTalk Stream WS
GW->>PL: startAccount(ctx)
PL->>PL: monitorDingtalkProvider()
PL->>PL: runGatewayLoop()
PL->>SDK: createDingtalkClientFromConfig(keepAlive=true, autoReconnect=false)
SDK->>DTAPI: POST /v1.0/gateway/connections/open
DTAPI-->>SDK: endpoint + ticket
SDK->>DTS: WebSocket connect(endpoint?ticket)
DTS-->>SDK: SYSTEM.CONNECTED
DTS-->>SDK: SYSTEM.REGISTERED
SDK-->>PL: client.connected=true / client.registered=true
PL-->>GW: 状态连接成功(connected/running)
3. 消息交互与 ACK 时序图
sequenceDiagram
participant DTS as DingTalk Stream WS
participant SDK as dingtalk-stream(DWClient)
participant PL as dingtalk 插件
participant GW as OpenClaw Gateway/Agent
participant DTAPI as DingTalk OpenAPI
PL->>SDK: registerCallbackListener(TOPIC_ROBOT)
DTS-->>SDK: CALLBACK /v1.0/im/bot/messages/get
SDK-->>PL: payload(messageId, data)
PL->>SDK: socketCallBackResponse(messageId,{success:true})
Note over PL,SDK: 先 ACK,避免钉钉在超时窗口内重试推送
PL->>GW: handleDingtalkMessage() 路由到会话/模型
GW-->>PL: 回复内容
PL->>DTAPI: 发送文本/媒体/卡片回复
4. 心跳机制(重点)
4.1 WS ping/pong 心跳
- 默认周期:
8000ms。 - 发起方:客户端(OpenClaw 侧的
dingtalk-stream)。 - 过程:
- 每 8 秒客户端执行一次
socket.ping()。 - 若收到
pong,标记连接存活。 - 若一个心跳周期内未恢复存活,客户端主动
terminate()断开,交给上层重连逻辑处理。
- 每 8 秒客户端执行一次
sequenceDiagram
participant SDK as dingtalk-stream Client
participant DTS as DingTalk Stream WS
loop Every 8s
SDK->>DTS: WS ping
DTS-->>SDK: WS pong
SDK->>SDK: isAlive=true
end
alt 未在周期内收到有效心跳
SDK->>SDK: terminate socket
end
4.2 钉钉 SYSTEM 心跳
- 触发方:服务端(DingTalk)。
- 事件:
SYSTEM/KEEPALIVE:客户端调用heartbeat()标记活跃。SYSTEM/ping:客户端返回code=200应答。
5. 断线检测与重连(插件层)
WATCHDOG_INTERVAL_MS = 5000(5 秒轮询连接状态)。CONNECT_TIMEOUT_MS = 30000(30 秒建连超时)。REGISTER_TIMEOUT_MS = 30000(30 秒注册确认超时,先告警但保持连接观察)。DISCONNECT_GRACE_MS = 15000(断连宽限 15 秒)。- 重连退避:
- 基础延迟
1000ms,指数增长,最大60000ms,带±20%抖动。
- 基础延迟
- 设计要点:SDK 自动重连被关闭(
autoReconnect=false),由插件统一管理重连节奏和状态机。
6. “谁调用谁”清单
- OpenClaw Gateway 调用 dingtalk 插件
startAccount()。 - 插件调用
DWClient.connect()发起连接。 DWClient先调用钉钉 HTTPS 网关接口拿endpoint/ticket,再发起 WebSocket。- 钉钉服务端向客户端下发 CALLBACK / SYSTEM 消息。
- 插件收到 CALLBACK 后主动调用
socketCallBackResponse()进行 ACK。 - 心跳上,客户端主动发 WS ping;服务端返回 pong 并可下发 SYSTEM KEEPALIVE/ping。
7. 关键实现定位(本机)
- 插件入口与连接启动:
/root/.openclaw/extensions/dingtalk/dist/index.js:9818
- 客户端创建参数(
keepAlive=true,autoReconnect=false):/root/.openclaw/extensions/dingtalk/dist/index.js:9259
- 消息回调注册与 ACK:
/root/.openclaw/extensions/dingtalk/dist/index.js:8975/root/.openclaw/extensions/dingtalk/dist/index.js:8938
- SDK 协议核心(获取 endpoint + 建立 WS):
/root/.openclaw/extensions/dingtalk/node_modules/dingtalk-stream/dist/client.mjs:97/root/.openclaw/extensions/dingtalk/node_modules/dingtalk-stream/dist/client.mjs:133
- SDK 心跳实现(8 秒 ping/pong):
/root/.openclaw/extensions/dingtalk/node_modules/dingtalk-stream/dist/client.mjs:29/root/.openclaw/extensions/dingtalk/node_modules/dingtalk-stream/dist/client.mjs:139/root/.openclaw/extensions/dingtalk/node_modules/dingtalk-stream/dist/client.mjs:152
- SDK 系统心跳与 ping 处理:
/root/.openclaw/extensions/dingtalk/node_modules/dingtalk-stream/dist/client.mjs:227/root/.openclaw/extensions/dingtalk/node_modules/dingtalk-stream/dist/client.mjs:231
- 网关地址常量:
/root/.openclaw/extensions/dingtalk/node_modules/dingtalk-stream/dist/constants.mjs:1
8. 运行态佐证(本机日志)
- 已出现连接状态迁移与 socket connected:
/tmp/openclaw/openclaw-2026-02-28.log:735/tmp/openclaw/openclaw-2026-02-28.log:744
- 出现注册超时告警(30s)后继续保活:
/tmp/openclaw/openclaw-2026-02-28.log:790
- 出现 connected -> running(traffic confirmed):
/tmp/openclaw/openclaw-2026-02-28.log:811
如果后续要做抓包或压测,建议优先观测三类指标:ACK 成功率、pong 丢失率、reconnect reason 分布。