钉钉机器人实战5分钟搞定卡片消息推送附完整代码最近在做一个内部效率工具需要把一些关键的业务状态通知给项目组的同学。最开始用的是邮件但响应速度太慢后来试了试普通的文本消息信息量又不够直观。直到用上了钉钉的互动卡片消息才算真正找到了“对”的感觉。一张卡片能把状态、操作按钮、关键数据都整合在一起点击就能处理体验非常流畅。如果你也在为团队通知的效率和体验发愁或者想快速上手钉钉机器人的高级功能这篇文章就是为你准备的。我们不谈复杂的理论直接从零开始手把手带你跑通从创建机器人到发送一张精美卡片消息的全流程并提供可直接复用的代码。1. 环境准备与基础概念扫盲在开始敲代码之前我们需要先把“舞台”搭好并理解几个核心概念。这能让你在后续的步骤中知其然也知其所以然遇到问题时不至于抓瞎。首先你需要一个钉钉开发者账号。如果你所在的企业已经使用钉钉通常可以直接用你的工作账号登录钉钉开放平台。如果只是个人学习也可以注册一个测试企业。这是所有操作的前提。接下来我们要搞清楚几个关键术语企业内部应用这是我们创建机器人的载体。你可以把它理解为一个“容器”机器人能力、消息推送权限、访问令牌等都挂载在这个应用下。机器人它是应用的一种能力体现负责在群聊或单聊中接收和发送消息。我们最终要用的“卡片消息推送”接口就是由这个机器人来调用的。互动卡片这是一种富媒体消息格式远不止于文本。它可以包含标题、图片、按钮、表单、进度条等多种组件用户可以直接在卡片上点击按钮进行交互无需跳转到其他页面。AccessToken这是调用钉钉开放平台几乎所有API的“钥匙”。每次请求都需要带上它以证明你的应用有权限进行操作。它有一定有效期需要定时刷新。Webhook机器人回调地址当你希望机器人能响应用户它的消息时就需要配置一个Webhook地址。钉钉会把用户发送的消息推送到你这个地址你的服务处理后再决定回复什么。对于单纯的主动推送场景可以暂时不配置。为了完成本教程请确保你的开发环境已安装以下基础工具Node.js (版本 14 或以上) 或 Python 3.8。本文将提供 Node.js 版本的示例代码逻辑清晰易于移植。一个代码编辑器如 VS Code。Postman 或类似的 API 测试工具用于调试接口非必需但强烈推荐。注意钉钉开放平台的界面和部分流程可能会更新但核心逻辑和API基本保持稳定。如果遇到步骤差异以官方最新文档为准。2. 五步创建你的第一个钉钉机器人理论说完了我们立刻动手。整个过程就像组装乐高步骤清晰一步一坑我都帮你标出来了。2.1 第一步创建企业内部应用登录钉钉开放平台后在控制台找到“应用开发” - “企业内部开发”。点击“创建应用”。应用类型选择“H5微应用”或“小程序”均可它们都支持添加机器人能力。这里我们以“H5微应用”为例。应用名称起一个容易识别的名字例如“业务监控机器人”。开发模式选择“企业自助开发”。 填写完基本信息后点击创建。应用创建成功后你会进入应用的管理页面。请务必记下页面上的两个关键信息AppKeyAppSecret这组密钥相当于你应用的“账号密码”后续获取AccessToken全靠它们。请像保护密码一样保管好尤其不要泄露AppSecret。2.2 第二步为应用添加机器人能力创建应用后它还是个“空壳”。我们需要为其添加“机器人”能力。在应用管理页面的左侧菜单栏找到“能力列表”或“功能列表”。点击“添加能力”在列表中找到“机器人”。添加成功后页面会跳转到机器人的配置页。这里你需要配置两个关键信息消息接收模式如果你需要机器人能回复用户的消息选择“Webhook”。如果只需要主动推送消息本文主要场景选择“消息推送”即可。为了功能完整我们先选择“Webhook”。Webhook地址填写你部署好的服务端API地址用于接收钉钉转发过来的用户消息。由于我们此刻还在开发调试没有公网服务器可以先填一个占位符如https://your-server.com/dingtalk/callback后续在本地用内网穿透工具如 ngrok、localtunnel生成临时地址再回来修改。点击“完成”或“保存”。保存后钉钉会生成一个用于消息加解密的Token和AES_KEY请一并保存好。2.3 第三步搭建你的第一张互动卡片模板卡片消息之所以强大在于其可定制的模板。我们需要先在钉钉的卡片搭建平台上设计好模板获取一个唯一的template_id才能在代码中调用。进入钉钉互动卡片平台。点击“创建新模板”。你会看到一个可视化的拖拽编辑器左侧是组件库中间是画布右侧是属性面板。搭建一个简单的状态通知卡片。例如从左侧拖入一个“文本”组件作为标题再拖入一个“多列布局”在里面放几个“文本”组件显示关键数据最后拖入一个“按钮”组件。设计时务必关注每个组件的“属性”面板特别是“唯一标识”即id。这个id将在后续服务端动态更新卡片内容时起到关键作用。例如将显示状态的文本组件id设为status_text按钮的id设为confirm_btn。设计完成后点击右上角的“保存”并“发布”。发布成功后在模板列表页就能看到你的模板及其对应的template_id。复制这个ID我们下一步就要用到它。为了更直观这里用一个简单的表格对比一下卡片模板的两种使用方式特性静态卡片动态卡片内容内容在创建模板时完全固定。模板仅定义结构和占位符内容由服务端API动态填充。适用场景内容永不变化的通知如固定欢迎语。状态更新、待办任务、数据报表等需要实时变化的内容。本次使用不采用。灵活性差。主要采用。通过cardData参数动态注入数据。2.4 第四步获取访问令牌 (AccessToken)万事俱备只欠“令牌”。所有调用钉钉服务端API的请求都必须在URL中携带有效的access_token。获取access_token的API非常简单是一个GET请求GET https://oapi.dingtalk.com/gettoken?appkeyYOUR_APPKEYappsecretYOUR_APPSECRET我们使用 Node.js 的axios库来实现。首先在项目目录下初始化并安装依赖npm init -y npm install axios然后创建一个getToken.js文件const axios require(axios); const APP_KEY 你的AppKey; const APP_SECRET 你的AppSecret; const GET_TOKEN_URL https://oapi.dingtalk.com/gettoken; async function getAccessToken() { try { const response await axios.get(GET_TOKEN_URL, { params: { appkey: APP_KEY, appsecret: APP_SECRET } }); if (response.data.errcode 0) { const accessToken response.data.access_token; console.log(获取AccessToken成功:, accessToken); // 在实际项目中你需要将这个token缓存起来如存入Redis并在过期前刷新。 return accessToken; } else { console.error(获取AccessToken失败:, response.data.errmsg); return null; } } catch (error) { console.error(请求失败:, error.message); return null; } } // 执行函数 getAccessToken();运行node getToken.js如果控制台打印出一长串字符串恭喜你钥匙拿到了请记住这个token默认有效期为7200秒2小时生产环境一定要做好缓存和刷新机制。2.5 第五步调试与发布机器人在正式编写推送代码前我们还需要在钉钉客户端里“启用”这个机器人。回到开放平台应用管理页面找到“版本管理与发布”。点击“创建版本”填写版本号等信息。在“可用范围”里选择你希望机器人能生效的部门或人员为了测试可以先选择你自己所在部门或仅自己。提交后需要企业管理员在钉钉手机端或管理后台审批通过该应用。审批通过后你就可以在钉钉群或单聊中通过“群设置” - “智能群助手” - “添加机器人”找到你刚创建的应用并将其添加到群里。至此你的机器人已经“物理上线”具备了接收和发送消息的能力。接下来就是最激动人心的环节用代码让它“说话”。3. 核心代码实战发送互动卡片消息现在我们将利用前面获取的access_token和template_id编写发送卡片消息的核心代码。钉钉提供了两个主要接口发送到群聊和发送到单人。我们先从最常用的群聊开始。3.1 发送群聊卡片消息发送群卡片需要先获取群的openConversationId。一个简单的方式是在添加了机器人的群里机器人会自动收到一条“机器人已添加”的消息这条消息体里就包含了这个ID。更通用的方式是通过chat.createAPI创建群或通过chat.get获取已存在的群信息。为了简化我们假设你已经有了目标群的openConversationId。核心的发送接口是im/v1.0/robot/interactiveCards/send。创建一个sendGroupCard.js文件const axios require(axios); const ACCESS_TOKEN 上一步获取到的AccessToken; const SEND_CARD_URL https://api.dingtalk.com/v1.0/robot/interactiveCards/send; // 卡片模板ID (从互动卡片平台获取) const TEMPLATE_ID 你的卡片模板ID; // 群聊的开放会话ID const OPEN_CONVERSATION_ID 钉钉群对应的openConversationId; // 接收者用户ID列表在群内特定人时使用非必需 const RECEIVER_USER_ID_LIST [userid1, userid2]; async function sendInteractiveCard() { // 动态卡片数据对应模板中组件的id const cardData { cardTemplateId: TEMPLATE_ID, openConversationId: OPEN_CONVERSATION_ID, receiverUserIdList: RECEIVER_USER_ID_LIST, cardBizId: unique_biz_id_${Date.now()}, // 用于幂等控制的业务ID data: { // 这里的内容对应卡片模板里定义的组件id status_text: { // 文本组件的值 value: 任务执行成功 }, data_field_1: { value: 完成率: 98% }, data_field_2: { value: 耗时: 2.5s } }, callbackUrl: https://your-server.com/dingtalk/card_callback, // 用户点击卡片按钮后的回调地址 callbackType: STREAM // 回调类型STREAM为流式更新可实时刷新卡片 }; try { const response await axios.post(SEND_CARD_URL, cardData, { headers: { Content-Type: application/json, x-acs-dingtalk-access-token: ACCESS_TOKEN // 注意新版API的鉴权头 } }); if (response.data.success) { console.log(卡片消息发送成功!, response.data); console.log(卡片任务ID:, response.data.processQueryKey); } else { console.error(发送失败:, response.data); } } catch (error) { console.error(请求异常:, error.response?.data || error.message); } } sendInteractiveCard();代码关键点解析鉴权方式注意我们使用了新的请求头x-acs-dingtalk-access-token来传递令牌这是钉钉新版API的规范。cardBizId这是一个业务唯一ID用于保证消息发送的幂等性。如果你在短时间内重复发送相同cardBizId的消息钉钉只会处理第一条。data 对象这是卡片内容的灵魂。其键名必须与你在卡片模板中为组件设置的“唯一标识”id完全一致。值对象中的value字段就是最终渲染的内容。callbackUrl当用户点击卡片上的按钮时钉钉会将点击事件推送到这个地址。你的服务端可以据此更新卡片状态如将“处理中”按钮变为“已处理”或触发后续业务逻辑。运行这段代码如果一切顺利你的钉钉群内就会出现一张精美的互动卡片了3.2 发送单人卡片消息发送给单人相对更简单因为不需要群的openConversationId直接使用用户的staffId(即userid) 即可。接口是im/v1.0/robot/interactiveCards/sendToSingle。假设你已经通过手机号或其它方式获取到了目标用户的useridconst axios require(axios); const ACCESS_TOKEN 你的AccessToken; const SEND_TO_SINGLE_URL https://api.dingtalk.com/v1.0/robot/interactiveCards/sendToSingle; const TEMPLATE_ID 你的卡片模板ID; const TARGET_USER_ID 接收者的userid; async function sendCardToSingleUser() { const cardData { cardTemplateId: TEMPLATE_ID, receiverUserId: TARGET_USER_ID, // 注意这里是单个字符串不是数组 cardBizId: personal_msg_${Date.now()}, data: { title: { value: 您的待办事项 }, task_content: { value: 请审核项目预算报告 }, deadline: { value: 今天 18:00前 } }, callbackUrl: https://your-server.com/dingtalk/card_callback }; try { const response await axios.post(SEND_TO_SINGLE_URL, cardData, { headers: { Content-Type: application/json, x-acs-dingtalk-access-token: ACCESS_TOKEN } }); console.log(单人卡片发送结果:, response.data); } catch (error) { console.error(发送失败:, error.response?.data || error.message); } } sendCardToSingleUser();4. 进阶技巧与避坑指南掌握了基础发送功能后我们来看看如何让机器人变得更智能、更稳定。这里分享几个实战中总结的进阶技巧和常见坑点。4.1 处理卡片回调与动态更新用户点击卡片按钮后真正的魔法才开始。钉钉会将点击事件以POST请求的形式发送到你预设的callbackUrl。你的服务需要处理这个请求并返回卡片更新指令。一个典型的回调请求体包含outTrackId卡片实例唯一ID、userId点击者ID和action按钮动作信息。你需要验证请求签名钉钉会在请求头中携带签名你必须验证此签名以确保请求来源合法防止伪造请求。官方SDK提供了验签方法。处理业务逻辑根据outTrackId和action执行相应操作如更新数据库状态。返回更新指令向钉钉返回一个JSON响应告诉它如何更新卡片。例如将按钮置灰并修改文本。// 一个简化的回调处理示例 (使用 Express.js 框架) app.post(/dingtalk/card_callback, async (req, res) { const { outTrackId, userId, action } req.body; // 1. 此处应进行签名验证 // if (!verifySignature(req)) { return res.status(403).send(Invalid signature); } // 2. 根据action处理业务 if (action.value confirm) { await db.updateTaskStatus(outTrackId, confirmed, userId); } // 3. 构造更新响应 const updateResponse { cardUpdateOptions: { updateCardDataByKey: true // 按key更新 }, cardData: { // 更新卡片上特定组件 confirm_btn: { title: 已确认, disabled: true // 禁用按钮 }, status_text: { value: 已由 ${userId} 确认 } } }; // 4. 返回更新指令 res.json({ success: true, ...updateResponse }); });4.2 消息安全与权限管理IP白名单在开放平台机器人设置中强烈建议配置服务器的出口IP白名单。这样只有来自你信任服务器的请求才能调用机器人接口增加一层安全防护。Token管理AccessToken务必在服务端缓存如使用Redis并设置合理的过期前刷新策略例如在有效期还剩600秒时主动刷新。切勿在客户端代码或页面中硬编码AppSecret和AccessToken。回调验签如前所述处理卡片回调或机器人Webhook消息时验签是必须步骤。忽略这一步可能导致严重的安全漏洞。4.3 性能优化与监控异步与非阻塞发送消息、处理回调等IO密集型操作应使用异步方式避免阻塞主线程。Node.js的异步特性在此很有优势。错误处理与重试网络请求可能失败。对于重要的通知消息需要实现简单的重试机制例如使用指数退避策略重试2-3次并记录失败日志。监控关键指标监控消息发送成功率、回调处理延迟、Token获取失败率等。这能帮助你及时发现服务异常。4.4 几个常见的“坑”invalid token错误99%的情况是AccessToken已过期。检查你的缓存和刷新逻辑。卡片发送成功但收不到检查机器人的“可用范围”是否包含了接收者所在的部门或人员检查群聊是否已成功添加该机器人。回调收不到或验签失败确认callbackUrl是公网可访问的HTTPS地址检查服务端验签算法是否正确特别是时间戳的校验钉钉要求请求时间与服务器时间不能相差超过1小时。卡片样式错乱检查data对象中的键名是否与模板组件ID完全一致包括大小写确认值的格式是否符合组件要求如按钮的disabled字段是布尔值。把这些代码片段组合起来加上错误处理和业务逻辑一个健壮、高效的钉钉机器人通知系统就初具雏形了。从我自己的经验来看最花时间的部分往往不是调用API而是设计出体验良好的卡片模板以及规划清晰的消息推送与回调处理流程。多思考用户的使用场景让每一张卡片都传递有效信息并提供便捷操作这才是技术最终要服务的价值。