dingtalk-message
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinese钉钉消息技能
DingTalk Messaging Skill
负责钉钉消息发送的所有操作。本文件为策略指南,仅包含决策逻辑和工作流程。完整 API 请求格式见文末的 查阅索引。
references/api.mdResponsible for all operations related to DingTalk message sending. This document is a Strategy Guide that only includes decision logic and workflows. For complete API request formats, refer to the lookup index in at the end of this document.
references/api.md四种消息通道概览
Overview of Four Messaging Channels
| 通道 | 适用场景 | 认证方式 | 特点 |
|---|---|---|---|
| Webhook 机器人 | 往指定群发通知 | 无需 token,URL 自带凭证 | 最简单;支持加签安全模式 |
| 企业内部应用机器人 | 单聊私信 / 群聊消息 | 新版 accessToken | 可撤回、查已读;需 userId 或 openConversationId |
| 工作通知 | 以应用身份推送到"工作通知"会话 | 旧版 access_token + agentId | 可推全员/部门;出现在工作通知而非聊天 |
| sessionWebhook | 回调中直接回复当前对话 | 无需任何认证 | 回调消息自带临时 URL,约 1.5 小时有效 |
| Channel | Use Case | Authentication Method | Features |
|---|---|---|---|
| Webhook Bot | Send notifications to designated groups | No token required, URL includes credentials | Simplest to use; supports signature-based security mode |
| Internal Enterprise App Bot | One-on-one private messages / group chat messages | New version accessToken | Supports message recall and read status query; requires userId or openConversationId |
| Work Notification | Push to "Work Notification" session as an app identity | Legacy access_token + agentId | Can push to all users/departments; appears in Work Notifications instead of chats |
| sessionWebhook | Directly reply to the current conversation in callbacks | No authentication required | Callback messages include a temporary URL, valid for approximately 1.5 hours |
工作流程(每次执行前)
Workflow (Before Each Execution)
- 先理解用户意图 → 判断属于哪个消息通道(见下方「场景路由」)
- 读取配置 → 用一条 命令一次性读取该通道所需的全部键值,不要分多次查询。(跨会话保留,所有 dingtalk-skills 共用同一文件)具体需要哪些配置,见[各通道所需配置](### 各通道所需配置)表格
grep -E '^KEY1=|^KEY2=' - 仅收集该通道所需的缺失配置 → 一次性询问,不要逐条问
- 持久化 → 写入 config,后续无需再问
- 执行任务
- Understand User Intent First → Determine which messaging channel it belongs to (see "Scenario Routing" below)
- Read Configuration → Use a single command to read all required key-values for the channel at once, instead of querying multiple times. (Retained across sessions, shared by all dingtalk-skills) For specific required configurations, see the "Configurations Required for Each Channel" table below
grep -E '^KEY1=|^KEY2=' - Collect Only Missing Configurations for the Channel → Ask all missing configurations at once, not one by one
- Persistence → Write to config, no need to ask again in subsequent operations
- Execute Task
各通道所需配置
Configurations Required for Each Channel
| 通道 | 所需配置 | 来源说明 |
|---|---|---|
| Webhook | | 群设置 → 智能群助手 → 添加自定义机器人 |
| Webhook(加签) | 额外 | 创建机器人时选择"加签"模式获得 |
| 机器人消息 | | 开放平台 → 应用管理 → 凭证信息 |
| 工作通知 | | agentId 在应用管理 → 基本信息 |
=robotCode(完全一致,无需额外配置)appKey- 凭证禁止在输出中完整打印,确认时仅显示前 4 位 +
****
| Channel | Required Configurations | Source Description |
|---|---|---|
| Webhook | | Group Settings → Smart Group Assistant → Add Custom Bot → Copy URL |
| Webhook (with Signature) | Additional | Obtained when selecting "Signature" mode during bot creation |
| Bot Messaging | | Open Platform → App Management → Credential Information |
| Work Notification | | agentId is located in App Management → Basic Information |
=robotCode(exactly the same, no additional configuration required)appKey- Credentials must not be printed in full in outputs; only show the first 4 characters +
during confirmation****
执行规范(推荐)
Execution Specifications (Recommended)
始终使用脚本文件执行:凡是包含变量替换()、管道()或多行逻辑的命令,一律用 写到 再 执行,不要内联到终端。内联命令会被终端工具截断或污染,导致变量读取失败。
$(...)|create_file/tmp/<task>.shbash /tmp/<task>.sh禁止 heredoc(),会被工具截断。
<<'EOF'典型脚本模板(读取配置 → 获取 token(带缓存)→ 执行 API):
bash
#!/bin/bash
set -e
CONFIG=~/.dingtalk-skills/configAlways Execute via Script Files: For commands involving variable substitution (), pipes () or multi-line logic, always use to write to then execute with . Do not inline them in the terminal. Inlined commands may be truncated or contaminated by terminal tools, leading to variable reading failures.
$(...)|create_file/tmp/<task>.shbash /tmp/<task>.shProhibit heredoc (), as it will be truncated by tools.
<<'EOF'Typical Script Template (Read Config → Get Token (with Cache) → Execute API):
bash
#!/bin/bash
set -e
CONFIG=~/.dingtalk-skills/config一次性读取所有所需配置
Read all required configurations at once
APP_KEY=$(grep '^DINGTALK_APP_KEY=' "$CONFIG" | cut -d= -f2-)
APP_SECRET=$(grep '^DINGTALK_APP_SECRET=' "$CONFIG" | cut -d= -f2-)
APP_KEY=$(grep '^DINGTALK_APP_KEY=' "$CONFIG" | cut -d= -f2-)
APP_SECRET=$(grep '^DINGTALK_APP_SECRET=' "$CONFIG" | cut -d= -f2-)
Token 缓存:有效期内复用,避免重复请求
Token Cache: Reuse within validity period to avoid repeated requests
CACHED_TOKEN=$(grep '^DINGTALK_ACCESS_TOKEN=' "$CONFIG" 2>/dev/null | cut -d= -f2-)
TOKEN_EXPIRY=$(grep '^DINGTALK_TOKEN_EXPIRY=' "$CONFIG" 2>/dev/null | cut -d= -f2-)
NOW=$(date +%s)
if [ -n "$CACHED_TOKEN" ] && [ -n "$TOKEN_EXPIRY" ] && [ "$NOW" -lt "$TOKEN_EXPIRY" ]; then
TOKEN=$CACHED_TOKEN
else
RESP=$(curl -s -X POST https://api.dingtalk.com/v1.0/oauth2/accessToken
-H 'Content-Type: application/json'
-d "{"appKey":"$APP_KEY","appSecret":"$APP_SECRET"}") TOKEN=$(echo "$RESP" | grep -o '"accessToken":"[^"]*"' | cut -d'"' -f4) EXPIRY=$((NOW + 7000)) # token 有效期 2h,提前约 13 分钟过期
-H 'Content-Type: application/json'
-d "{"appKey":"$APP_KEY","appSecret":"$APP_SECRET"}") TOKEN=$(echo "$RESP" | grep -o '"accessToken":"[^"]*"' | cut -d'"' -f4) EXPIRY=$((NOW + 7000)) # token 有效期 2h,提前约 13 分钟过期
更新缓存(先删旧值再追加)
sed -i '/^DINGTALK_ACCESS_TOKEN=/d;/^DINGTALK_TOKEN_EXPIRY=/d' "$CONFIG"
echo "DINGTALK_ACCESS_TOKEN=$TOKEN" >> "$CONFIG"
echo "DINGTALK_TOKEN_EXPIRY=$EXPIRY" >> "$CONFIG"
fi
CACHED_TOKEN=$(grep '^DINGTALK_ACCESS_TOKEN=' "$CONFIG" 2>/dev/null | cut -d= -f2-)
TOKEN_EXPIRY=$(grep '^DINGTALK_TOKEN_EXPIRY=' "$CONFIG" 2>/dev/null | cut -d= -f2-)
NOW=$(date +%s)
if [ -n "$CACHED_TOKEN" ] && [ -n "$TOKEN_EXPIRY" ] && [ "$NOW" -lt "$TOKEN_EXPIRY" ]; then
TOKEN=$CACHED_TOKEN
else
RESP=$(curl -s -X POST https://api.dingtalk.com/v1.0/oauth2/accessToken
-H 'Content-Type: application/json'
-d "{"appKey":"$APP_KEY","appSecret":"$APP_SECRET"}") TOKEN=$(echo "$RESP" | grep -o '"accessToken":"[^"]*"' | cut -d'"' -f4) EXPIRY=$((NOW + 7000)) # Token validity is 2 hours, set to expire about 13 minutes early
-H 'Content-Type: application/json'
-d "{"appKey":"$APP_KEY","appSecret":"$APP_SECRET"}") TOKEN=$(echo "$RESP" | grep -o '"accessToken":"[^"]*"' | cut -d'"' -f4) EXPIRY=$((NOW + 7000)) # Token validity is 2 hours, set to expire about 13 minutes early
Update cache (delete old values first then append)
sed -i '/^DINGTALK_ACCESS_TOKEN=/d;/^DINGTALK_TOKEN_EXPIRY=/d' "$CONFIG"
echo "DINGTALK_ACCESS_TOKEN=$TOKEN" >> "$CONFIG"
echo "DINGTALK_TOKEN_EXPIRY=$EXPIRY" >> "$CONFIG"
fi
在此追加具体 API 调用
Append specific API calls here
JSON 字段提取:`grep -o '"key":"[^"]*"' | cut -d'"' -f4`
JSON Field Extraction: `grep -o '"key":"[^"]*"' | cut -d'"' -f4`消息内容缺失时的处理
Handling Missing Message Content
用户未指定消息内容时,不要自行编造,应先询问用户想发送什么内容。仅当用户明确表示"随便发一条测试"时,才使用默认内容:
这是一条来自钉钉机器人的测试消息。If the user does not specify message content, do not fabricate it yourself; first ask the user what content they want to send. Only when the user clearly states "send a random test message" should you use the default content:
This is a test message from a DingTalk bot.场景路由(收到用户请求后的判断逻辑)
Scenario Routing (Decision Logic After Receiving User Request)
用户想发消息
├─ 发到群里?
│ ├─ 通用群消息(含 @某人)→ 询问用户选择发送方式(见下方「群消息发送方式选择」)
│ ├─ 明确需要撤回或查已读 → 企业机器人群聊(直接跳过询问)
│ └─ 正在处理机器人回调,直接回复 → sessionWebhook
├─ 发给个人?
│ ├─ 以机器人身份发私信 → 企业机器人单聊
│ └─ 以应用身份推工作通知 → 工作通知
├─ 撤回/查已读?
│ ├─ 机器人消息 → 企业机器人的撤回/已读 API
│ └─ 工作通知 → 工作通知的查询/撤回 API
└─ 回复机器人收到的消息? → sessionWebhookUser wants to send a message
├─ Send to a group?
│ ├─ General group message (including @someone) → Ask the user to select a sending method (see "Group Message Sending Method Selection" below)
│ ├─ Explicitly needs recall or read status query → Enterprise Bot Group Chat (skip the inquiry directly)
│ └─ Processing bot callback, direct reply → sessionWebhook
├─ Send to an individual?
│ ├─ Send private message as a bot → Enterprise Bot One-on-One Chat
│ └─ Push work notification as an app identity → Work Notification
├─ Recall message / check read status?
│ ├─ Bot message → Enterprise Bot's recall/read status API
│ └─ Work notification → Work notification query/recall API
└─ Reply to messages received by the bot? → sessionWebhook群消息发送方式选择
Group Message Sending Method Selection
用户发起群消息请求时,必须先询问他们选择哪种方式,并说明各自需要什么配置:
请问您想通过哪种方式发这条群消息?
方式 需要提供 如何获取 说明 Webhook 机器人 WEBHOOK_URL群设置 → 智能群助手 → 添加自定义机器人 → 复制 URL 无需应用权限,配置最简单;支持 @某人( )at.atUserIds企业内部应用机器人 (群会话 ID)openConversationId机器人收到群消息时,回调体的 字段即为该值conversationId需要 /APP_KEY;支持撤回、查已读APP_SECRET推荐 Webhook,只需一个 URL 即可,无需任何应用权限。
收到用户选择后按以下方式收集配置:
- 选 Webhook:收集 (若启用加签还需
DINGTALK_WEBHOOK_URL),持久化后执行DINGTALK_WEBHOOK_SECRET - 选 企业机器人:收集 ,复用已有的
openConversationId/APP_KEY(若未配置则一并收集),调用APP_SECRETgroupMessages/send
执行 API 前按通道获取对应 token:
| 通道 | token 类型 | 获取方式 | 使用方式 |
|---|---|---|---|
| 机器人消息 | 新版 accessToken | | 请求头 |
| 工作通知 | 旧版 access_token | | URL 参数 |
| Webhook | 无需 token | — | 直接 POST 到 Webhook URL |
| sessionWebhook | 无需 token | — | 直接 POST 到回调中的 sessionWebhook URL |
token 有效期均为 2 小时,遇 401 重新获取即可。具体请求/返回格式见 api.md 对应章节。
When a user initiates a group message request, must first ask them to select a method and explain the required configurations for each:
Which method would you like to use to send this group message?
Method Required Information How to Obtain Description Webhook Bot WEBHOOK_URLGroup Settings → Smart Group Assistant → Add Custom Bot → Copy URL No app permissions required, simplest configuration; supports @someone ( )at.atUserIdsInternal Enterprise App Bot (group session ID)openConversationIdWhen the bot receives a group message, the field in the callback body is this valueconversationIdRequires /APP_KEY; supports message recall and read status queryAPP_SECRETWebhook is recommended, as it only requires a URL and no app permissions.
After receiving the user's selection, collect configurations as follows:
- If Webhook is selected: Collect (if signature is enabled, also collect
DINGTALK_WEBHOOK_URL), then execute after persistenceDINGTALK_WEBHOOK_SECRET - If Enterprise Bot is selected: Collect , reuse existing
openConversationId/APP_KEY(collect them together if not configured), then callAPP_SECRETgroupMessages/send
Obtain the corresponding token by channel before executing the API:
| Channel | Token Type | Obtaining Method | Usage Method |
|---|---|---|---|
| Bot Messaging | New version accessToken | | Request header |
| Work Notification | Legacy access_token | | URL parameter |
| Webhook | No token required | — | Direct POST to Webhook URL |
| sessionWebhook | No token required | — | Direct POST to the sessionWebhook URL in the callback |
All tokens are valid for 2 hours. Re-obtain them when encountering 401 errors. For specific request/response formats, refer to the corresponding sections in api.md.
身份标识(关键决策知识)
Identity Identification (Key Decision Knowledge)
所有消息发送 API 均只接受 userId(staffId),不接受 unionId。这一点已通过实际 API 调用验证。
| 标识 | 作用域 | 能否用于发消息 |
|---|---|---|
| 单个企业内唯一 | ✅ 唯一接受的 ID |
| 跨组织唯一 | ❌ 会被判定无效用户 |
All message sending APIs only accept userId (staffId), not unionId. This has been verified through actual API calls.
| Identifier | Scope | Usable for Sending Messages |
|---|---|---|
| Unique within a single enterprise | ✅ The only accepted ID |
| Unique across organizations | ❌ Will be judged as an invalid user |
如何获取 userId
How to Obtain userId
- 机器人回调(最常用):消息体的 字段
senderStaffId - unionId → userId:
POST /topapi/user/getbyunionid - 手机号 → userId:
POST /topapi/v2/user/getbymobile - 管理后台:PC 端钉钉 → 工作台 → 管理后台 → 通讯录
- Bot Callback (Most commonly used): field in the message body
senderStaffId - unionId → userId:
POST /topapi/user/getbyunionid - Mobile Number → userId:
POST /topapi/v2/user/getbymobile - Admin Backend: PC DingTalk → Workbench → Admin Backend → Address Book
回调消息中的身份字段
Identity Fields in Callback Messages
| 字段 | 含义 | 可靠性 |
|---|---|---|
| 发送者 userId | 企业内部群始终存在;外部群中外部用户可能为空 |
| 发送者 unionId | 始终存在 |
userId ↔ unionId 互转的 API 细节:注意grep -A 8 "^#### userId ↔ unionId" references/api.md(无下划线)有值,result.unionid(有下划线)在部分企业中为空。result.union_id
| Field | Meaning | Reliability |
|---|---|---|
| Sender's userId | Always exists in internal enterprise groups; may be empty for external users in external groups |
| Sender's unionId | Always exists |
For details on userId ↔ unionId conversion APIs:Note thatgrep -A 8 "^#### userId ↔ unionId" references/api.md(no underscore) has a value, whileresult.unionid(with underscore) may be empty in some enterprises.result.union_id
消息类型速查
Quick Reference for Message Types
Webhook 消息类型
Webhook Message Types
直接在请求 body 的 字段指定: | | | |
msgtypetextmarkdownactionCardlinkfeedCard各类型完整 JSON 格式:(将grep -A 30 "^#### 文本消息" references/api.md替换为文本消息、Markdown 消息等)ActionCard
Specify directly in the field of the request body: | | | |
msgtypetextmarkdownactionCardlinkfeedCardComplete JSON formats for each type:(replace "Text Message" with "Markdown Message", "ActionCard", etc.)grep -A 30 "^#### Text Message" references/api.md
机器人消息类型
Bot Message Types
通过 + (JSON 字符串)指定:
msgKeymsgParam| msgKey | 类型 | msgParam 关键字段 |
|---|---|---|
| 文本 | |
| Markdown | |
| ActionCard | |
| 链接 | |
| 图片 | |
重要:必须是 JSON 字符串,不是对象。完整格式:msgParamgrep -A 16 "^### 消息类型" references/api.md
Specify via + (JSON string):
msgKeymsgParam| msgKey | Type | Key Fields in msgParam |
|---|---|---|
| Text | |
| Markdown | |
| ActionCard | |
| Link | |
| Image | |
Important:must be a JSON string, not an object. For complete formats:msgParamgrep -A 16 "^### Message Types" references/api.md
工作通知消息类型
Work Notification Message Types
在 对象的 字段指定: | |
msgmsgtypetextmarkdownaction_card注意工作通知的用下划线(不同于 Webhook 的action_card)。完整格式:actionCardgrep -A 62 "^### 工作通知消息类型" references/api.md
Specify in the field of the object: | |
msgtypemsgtextmarkdownaction_cardNote thatuses an underscore for work notifications (different fromaction_cardfor Webhook). For complete formats:actionCardgrep -A 62 "^### Work Notification Message Types" references/api.md
典型场景
Typical Scenarios
"发个群消息通知大家" / "@某人的群消息"
"Send a group message to notify everyone" / "Group message @someone"
→ 先按「群消息发送方式选择」询问用户用 Webhook 还是企业机器人,收集对应配置后执行。
Webhook 支持 @某人:body 中加(用户 ID 数组),正文同时写at.atUserIds以高亮显示。@userId
→ First ask the user to select between Webhook or Enterprise Bot per "Group Message Sending Method Selection", then execute after collecting corresponding configurations.
Webhook supports @someone: Add(array of user IDs) in the body, and writeat.atUserIdsin the body content to highlight the user.@userId
"用 Markdown 发个部署通知到群里"
"Send a deployment notification to the R&D group using Markdown"
→ 先询问发送方式(同上),确认选 Webhook 后用 构造 body;若选企业机器人则用 。
msgtype: markdownmsgKey: sampleMarkdown→ First ask the sending method (same as above). If Webhook is selected, construct the body with ; if Enterprise Bot is selected, use .
msgtype: markdownmsgKey: sampleMarkdown"给张三发条消息"
"Send a message to Zhang San"
→ 机器人单聊。需 / + 张三的 userId。调用 。
APP_KEYAPP_SECREToToMessages/batchSend→ Enterprise Bot One-on-One Chat. Requires / + Zhang San's userId. Call .
APP_KEYAPP_SECREToToMessages/batchSend"往研发群发个带按钮的消息"
"Send a message with buttons to the R&D group"
→ 先按「群消息发送方式选择」询问用户用 Webhook 还是企业机器人,收集对应配置后执行。
Webhook 用;企业机器人用msgtype: actionCard。msgKey: sampleActionCard
→ First ask the user to select between Webhook or Enterprise Bot per "Group Message Sending Method Selection", then execute after collecting corresponding configurations.
Webhook uses; Enterprise Bot usesmsgtype: actionCard.msgKey: sampleActionCard
"发工作通知给全员"
"Send a work notification to all staff"
→ 工作通知。需 //。。
APP_KEYAPP_SECRETAGENT_IDto_all_user: true→ Work Notification. Requires //. Set .
APP_KEYAPP_SECRETAGENT_IDto_all_user: true"撤回刚才那条消息"
"Recall the message I just sent"
→ 找到上一次发送返回的 (机器人)或 (工作通知),调用对应撤回 API。
processQueryKeytask_id→ Find the (for bots) or (for work notifications) returned from the last send, then call the corresponding recall API.
processQueryKeytask_id"回复机器人收到的消息"
"Reply to messages received by the bot"
→ sessionWebhook。从回调取 URL,直接 POST,无需任何认证。
sessionWebhook→ sessionWebhook. Retrieve the URL from the callback, then directly POST to it without any authentication.
sessionWebhook错误处理速查
Quick Reference for Error Handling
| 场景 | 错误特征 | 处理 |
|---|---|---|
| Webhook | | 需包含自定义关键词 |
| Webhook | | 检查签名计算和 timestamp |
| Webhook | | 限 20 条/分钟,等待重试 |
| 机器人 | | userId 无效,确认在组织内 |
| 机器人 | | 限流,稍候重试 |
| 工作通知 | | agentId 错误 |
| 工作通知 | | access_token 过期 |
| 通用 | 401 / 403 | token 过期 / 权限不足 |
完整错误码表:grep -A 33 "^## 错误码" references/api.md
| Scenario | Error Characteristics | Handling |
|---|---|---|
| Webhook | | Must include custom keywords |
| Webhook | | Check signature calculation and timestamp |
| Webhook | | Limited to 20 messages/minute, wait and retry |
| Bot | | userId is invalid, confirm the user is in the organization |
| Bot | | Rate-limited, retry later |
| Work Notification | | Incorrect agentId |
| Work Notification | | access_token expired |
| General | 401 / 403 | Token expired / Insufficient permissions |
Complete error code table:grep -A 33 "^## Error Codes" references/api.md
所需应用权限
Required App Permissions
| 功能 | 权限 |
|---|---|
| 机器人单聊 | |
| 机器人群聊 | |
| 消息已读查询 | |
| 消息撤回 | |
| 工作通知 | |
| Webhook / sessionWebhook | 无需应用权限 |
| Function | Permission |
|---|---|
| Bot One-on-One Chat | |
| Bot Group Chat | |
| Message Read Status Query | |
| Message Recall | |
| Work Notification | |
| Webhook / sessionWebhook | No app permissions required |
references/api.md 查阅索引
references/api.md Lookup Index
确定好要做什么之后,用以下命令从 中提取对应章节的完整 API 细节(请求格式、参数表、返回值示例):
references/api.mdbash
grep -A 196 "^## 一、群自定义 Webhook 机器人" references/api.md
grep -A 19 "^### 加签计算" references/api.md
grep -A 192 "^## 二、企业内部应用机器人" references/api.md
grep -A 36 "^#### 钉钉身份标识体系" references/api.md
grep -A 16 "^### 消息类型" references/api.md
grep -A 145 "^## 三、工作通知" references/api.md
grep -A 60 "^## 四、sessionWebhook" references/api.md
grep -A 33 "^## 错误码" references/api.md
grep -A 30 "^#### 文本消息" references/api.md
grep -A 30 "^### 批量发送单聊消息" references/api.md
grep -A 30 "^### 发送工作通知" references/api.mdAfter confirming the operation, use the following commands to extract complete API details (request formats, parameter tables, return value examples) from the corresponding sections in :
references/api.mdbash
grep -A 196 "^## 一、群自定义 Webhook 机器人" references/api.md
grep -A 19 "^### 加签计算" references/api.md
grep -A 192 "^## 二、企业内部应用机器人" references/api.md
grep -A 36 "^#### 钉钉身份标识体系" references/api.md
grep -A 16 "^### 消息类型" references/api.md
grep -A 145 "^## 三、工作通知" references/api.md
grep -A 60 "^## 四、sessionWebhook" references/api.md
grep -A 33 "^## 错误码" references/api.md
grep -A 30 "^#### 文本消息" references/api.md
grep -A 30 "^### 批量发送单聊消息" references/api.md
grep -A 30 "^### 发送工作通知" references/api.md