钉钉(DingTalk)¶
适用范围:独立 Python Runtime。 桌面端操作见 Desktop 使用指南。
概述¶
钉钉通道通过 Stream Mode(流模式)与钉钉开放平台通信。Stream Mode 采用回调注册 + WebSocket 长轮询机制,无需暴露公网端点,适合内网部署或无固定公网 IP 的场景。
通道支持单聊(1:1)和群聊两种消息类型,通过 conversation_type 元数据自动区分聊天类型,并分别调用不同的发送接口。
Tip
Stream Mode 是钉钉推荐的机器人接入方式,免去了 Webhook 回调地址配置和 SSL 证书管理的复杂度。
配置示例¶
channels:
dingtalk:
enabled: true
app_key: "your-app-key"
app_secret: "your-app-secret"
robot_code: "your-robot-code"
allow_from:
- "user1"
- "user2"
| 字段 | 必填 | 说明 |
|---|---|---|
app_key |
是 | 企业内部应用的 AppKey |
app_secret |
是 | 企业内部应用的 AppSecret |
robot_code |
是 | 机器人唯一标识码 |
allow_from |
否 | 允许发送消息的用户白名单,为空则不限制 |
凭证获取¶
- 登录 钉钉开放平台
- 进入「应用开发」→「企业内部应用」→ 点击「创建应用」
- 在应用信息页获取 AppKey 和 AppSecret
- 进入「机器人与消息推送」配置页面
- 启用机器人功能,获取 robot_code
- 在机器人设置中启用 Stream Mode(消息接收模式选择「Stream 模式」)
Warning
AppSecret 仅在创建时显示一次,请妥善保存。如遗失需重新生成,届时旧密钥立即失效。
回调 / Webhook 设置¶
Stream Mode 下无需配置公网回调地址。连接流程如下:
- 应用启动时使用 AppKey + AppSecret 向钉钉 API 注册回调
- 钉钉返回 WebSocket 连接地址
- 应用建立 WebSocket 长连接,持续接收消息推送
- 连接断开时自动重连
┌─────────┐ 注册回调 ┌──────────────┐
│ Agent │ ────────────→ │ DingTalk API │
│ │ ←──────────── │ │
│ │ WS endpoint │ │
│ │ ═══════════════│ │
│ │ WebSocket 长连接(消息推送) │
└─────────┘ └──────────────┘
Tip
由于使用 WebSocket 出站连接,部署在 NAT 或防火墙后的服务也可正常工作,仅需允许出站 HTTPS/WSS 流量。
能力矩阵¶
| 能力 | 支持情况 | 备注 |
|---|---|---|
| 消息编辑 | ❌ | 钉钉 API 不支持修改已发送消息 |
| 表情回应 | ❌ | 不支持 |
| 文件发送 | ✅ | 本地文件/图片上传,支持单聊和群聊,最大 20MB |
| 实时消息 | ✅ | Stream Mode WebSocket 推送 |
| 群聊 | ✅ | 通过 openConversationId 路由 |
| 单聊 | ✅ | 通过 staffId 路由 |
消息发送接口区分:
- 单聊:
POST /v1.0/robot/oToMessages/batchSend - 群聊:
POST /v1.0/robot/groupMessages/send(需要openConversationId)
文件发送¶
使用 gateway 已有的 send_file 工具即可发送本地附件:
{
"channel": "dingtalk",
"chat_id": "cid你的群会话ID==",
"file_path": "/absolute/path/report.docx",
"caption": "您的报告"
}
- 单聊
chat_id使用企业用户userId(入站回调的senderStaffId),不是个人钉钉号。 - 群聊使用回调的
conversationId(通常以cid开头),不是客户端显示的群号。工具事件没有conversation_type时,通道按cid前缀识别群聊;显式元数据优先。 - 文件支持
xlsx、pdf、zip、rar、doc、docx;图片支持jpg、gif、png、bmp,按图片模板发送。其他格式请先打包成 ZIP。仅接受本地非空文件,每个文件最大 20MB。 - 应用需开通「企业内机器人发送消息权限」,机器人需加入目标群聊。媒体上传权限默认开通。
通道先检查文件,再通过 POST https://oapi.dingtalk.com/media/upload 上传,取得 media_id 后使用 sampleFile(图片为 sampleImageMsg)发送。caption 会作为独立文本先发送。上传失败、发送失败、接收人无效或被限流都会返回失败,不会仅因文本发出就报告文件成功;发送回执不代表用户已经阅读或下载文件。
图片的 .jpeg 后缀按 JPEG 图片处理。显式指定 as_image: false 时不会静默改成图片;平台文件模板不接受的格式会明确提示打包为 ZIP。
长回复与投递恢复¶
长回复按真实 JSON 编码预算拆分为编号消息,保留原文和空白。同一聊天的回复不会交错;不同聊天可以继续处理。钉钉、Telegram、Discord、Slack、飞书和微信共用分段检查点,已确认的段在同一投递重试时跳过。钉钉还记录说明文字及附件的投递,重试不会重复发送已确认的文件。
完整服务使用 SQLite 保存检查点和原始发送内容。普通最终回复与流式失败回退共用投递身份;节流期间取消不会把尚未发送的段标成结果未知。明确的拒绝允许续发;网络超时、服务端异常或缺少确认则保留未知状态,避免盲目重放。重启将旧的发送中记录转为待核对,不自动重发未知内容。
管理员可在管理端「通道 → 消息投递恢复」查看完整消息、附件名称和已确认段数。明确未送达的内容可以继续发送;未知结果必须先在目标聊天核对,再选择「确认未送达后继续」或「已核对收到完整消息」。恢复操作使用管理员权限,并保存核对记录。对应接口为 GET /api/v1/deliveries、GET /api/v1/deliveries/{id}、POST /api/v1/deliveries/{id}/recover,实际前缀遵循 Gateway 配置。
检查点不自动淘汰未知或已确认的身份,上限十万条;容量用尽时停止新投递并明确返回错误。没有持久存储的轻量适配器仅保留当前实例的检查点;钉钉长回复要求持久存储。
恢复附件需要原文件仍位于原路径且内容未改变。文件已修改或删除时,请恢复原文件后继续,或作为新消息重新发送;系统会拒绝用旧投递身份发送不同内容。
澄清问题与回答¶
问题确认投递后才成为待答状态,并保存在会话中。关联任务显示为等待回答,回复选项后继续同一任务、计划及文档交付流程;问题发送失败时保留任务等待投递恢复,避免误判任务失败。同批中提问之后的工具不会提前执行。选项仅去除明确带标点的编号,保留「A 股」等合法正文。
飞书和 Telegram 的群聊待答期间,只为提问者放行无需再次 @ 的回复,其他成员仍遵循原有群聊规则。Slack 普通私聊沿用同一会话;线程仍独立。问题有效期遵循 session.im_clarify_pending_ttl_seconds,显式引用其他消息优先于隐式续接。
接口依据:上传媒体文件、企业机器人消息类型、单聊发送、群聊发送。
认证机制¶
钉钉采用 AppKey + AppSecret 换取 access_token 的认证方式:
POST https://api.dingtalk.com/v1.0/oauth2/accessToken
{
"appKey": "your-app-key",
"appSecret": "your-app-secret"
}
Token 有效期为 2 小时,通道层自动刷新,无需手动管理。
常见问题¶
群聊会话 ID 从哪里来?
无需额外调用 API,也不需要为此单独申请权限。会话 ID 直接取自入站回调的 conversationId 字段,回复时以 openConversationId 原样带回。
单聊(conversationType 为 1)用发送者 ID 作为会话标识,群聊用 conversationId。因此只要机器人能收到消息回调,回复所需的会话 ID 就已经在手。
Q: 触发了速率限制怎么办?
文本发送之间至少间隔 3 秒。平台明确拒绝时保留成功回执,管理员可从「消息投递恢复」继续未送达内容;超时或缺少回执时必须先核对收件情况。通道不会盲目重发未知结果。频繁触发限流时建议:
- 合并短时间内的多条回复为单条消息
- 检查是否有异常的重复消息发送
Q: Stream Mode 连接不上?
- 确认 AppKey / AppSecret / robot_code 三项配置均正确
- 确认机器人设置中已启用 Stream Mode
- 检查出站网络是否允许 WSS 连接(端口 443)
- 查看日志中是否有 token 获取失败的错误
Q: 消息在群聊中没有回复?
- 确认
allow_from未将发送者排除在外 - 确认机器人已被添加到目标群聊
- 检查
conversation_type是否正确识别为群聊 - 群聊需要 @机器人 才能触发响应(取决于配置)
Q: 如何区分单聊和群聊消息?
通道通过消息中的 conversationType 字段自动区分:
- 1 = 单聊
- 2 = 群聊
不同类型的消息会自动路由到对应的发送接口。