钉钉开发机器人单聊业务实现

  • 背景
    • 钉钉待办
    • 钉钉卡片
      • 卡片类型的工作通知
      • 创建并投放卡片
      • 机器人发送互动卡片
  • 实现
    • 过程搭建
      • 1. 授权
      • 2. 创建机器人
      • 3. 创建卡片
        • 变量创建
        • 链接问题
      • 4. 卡片投放测试
      • 5. web端调用机器人发送卡片接口
      • 6. 生成待办并封装接口
    • 新发现
  • 总结

背景

特定业务场景下的对具体业务实现方式的取舍

公司OA系统升级, 需要将OA系统内的消息和系统外的消息联系. 为此产品提出需求. 当用户在OA系统收到消息时,可同时在钉钉收到待办消息提醒,提醒用户前往OA系统查看消息,处理相关事宜. 因此着手做了钉钉待办. 但是钉钉待办实现后效果并不理想.因此采用了钉钉卡片进行通知. 而采用卡片通知又分群聊和单聊场景. 并且卡片通知整个流程也挺曲折, 下面我将 把我走过的坑重新梳理下, 让更多需要的人更快搭建此场景.

钉钉待办

待办是钉钉的一个协同办公产品,帮助企业员工更高效的进行事项(工作任务)管理。钉钉待办提供了强大的开放能力,各类业务系统或企业自建应用可低成本的接入。因为主要介绍的是钉钉机器人单聊发送卡片业务实现, 所以在这里简单提一下.

钉钉创建待办案例代码地址


经过自己自测发现, 钉钉现在做逻辑是, 如果在待办中有配置pc端和app的跳转url. 那么给你创建的就是工作待办.
这种待办提醒比较醒目(会有红点, 以及待办数提示)


但是如果在代码中没有加上双端应用跳转地址, 则不会创建工作类型的待办, 而是创建个人类型的待办.
个人类型的待办不会再钉钉待办框提示, 并且只有手动点进去才能进行提醒,
这样的话体验效果就较差, 谁能每次会主动进入待办中去查看个人待办的东西呢?


你会觉得钉钉那边肯定有考虑了, 一定有参数可以设置待办类型的.
目前的情况是, 就其提供的现有参数是无法创建没有跳转的工作待办的.
如果你可以实现, 也欢迎大家尝试来告诉下我, 让我也来学习下.
因为以上的原因, 我们觉得采用钉钉卡片, 以微应用或机器人的形式进行单聊发送.
进而实现将OA系统消息推送到钉钉的这样一个需求


钉钉卡片

钉钉互动卡片是一种即时交互、多人协同、数据驱动的轻量卡片,它能够将原本复杂的应用解构成一个个轻量级的卡片在钉钉的各个场域(场域的解释请参考下文名词解释部分)上运行。用户可以在卡片上完成互动协同,提高用户的沟通效率,同时帮助业务更好地触达用户。钉钉的互动卡片官方介绍地址,

习惯啃官方文档的可以看这个. 里面的内容也就是我今天想讲的, 现在我把我对这块的理解以及我踩过的坑来给大家分享一下.

在我花费漫长的时间阅读官方文档以及调试中发现, 钉钉互动卡片的发送不止一种, 而是达到了惊人的三种!
分别是发送卡片类型的工作通知, 机器人发送互动卡片(普通版), 以及直接创建并投放卡片.
下面我来简单介绍下三种创建方式的区别.

卡片类型的工作通知

发送卡片类型的工作通知


此种方式创建的卡片, 格式固定, 自由度较低, 无法自己编辑通知卡片格式

创建并投放卡片

  • 创建并投放卡片

    卡片的创建十分复杂. 主要步骤分为:

    1. 开启卡片权限机器人权限(单聊)
    2. 创建卡片模板(编辑模板, 绑定参数)
    3. 为卡片模板新增场域创建卡片实例
    4. 配置场域并投放测试
    5. web接口页面测试
    6. 生成相关代码
    7. 封装api并使用

    并且我在成功之后, 进行web接口页面测试接口发下, 不仅需要n多个参数, 而且参数的解释也不全,
    花了很长时间请求之后, 接口请求成功, 但是没有显示投放结果并且钉钉上未收到卡片信息.
    而且, 就接口请求相关问题去问钉钉客服, 只能通过工单,
    而我提的工单, 截止到发文已经两个工作日都没有回复了…

机器人发送互动卡片

  • 机器人发送互动卡片(普通版)
    因为之前主要就是研究创建并投放卡片的接口, 去立即并测试相关参数. 机
    器人单聊和场域相关的参数好不容易补充好, 结果最终还是请求失败.
    但是在阅读官方文档的时, 无意中发现, 在机器人这一章节, 有发送卡片的功能以及接口.
    因此就在这里测试了. 结果没花几分钟就搞好了.

此种方式创建卡片通知, 参数简单, 调用方便, 并且支持调用卡片模板和变量传入
所以, 最终还是敲定使用此方案进行业务功能实现


实现

基于机器人发送互动卡片的实现以及搭建过程和踩坑介绍

过程搭建

整个流程可以分为:

  • 授权
  • 创建机器人
  • 创建卡片
  • 卡片投放测试
  • web端调用机器人发送卡片接口
  • 生成待办并封装接口
  • 调用测试

下面, 我们来按照上面流程来介绍实现过程

1. 授权

这里授权主要是为了方便之后接口调用时, 没有权限导致的无法调用的问题

在钉钉开放平台-> 应用管理 -> 权限管理中, 搜索机器人, 卡片. 寻找对应的权限并开启

2. 创建机器人

配置用于后续进行发送卡片的单聊机器人的相关信息. 这里主要是获取到RobotCode, 用于后续接口测试时使用

在钉钉开放平台-> 应用管理->消息和推送中创建机器人

3. 创建卡片

创建卡片的目的主要就是开发人员根据需求去创建符合自己业务环境的卡片. 通过变量来绑定自己业务数据, 通过按钮来绑定自己跳转链接 卡片支持的交互组件

卡片的话, 可以参照钉钉卡片这一模块的案例介绍. 慢慢摸索总能够做好.
这里主要提两点: 变量创建和链接问题.

变量创建

在卡片左侧数据源这里, 编辑后面需要的变量, 在卡片设计中, 通过el表达式进行填充. 主要格式就是 ${变量名}. 我们可以填写好Mock数据来进行模板的渲染效果的展示

链接问题

互动卡片支持链接的跳转。默认情况下,如果给卡片配置的是一个普通的 HTTP 协议的链接,如 https://dingtalk.com ,那么它会以新窗口的方式打开(在 PC 端以系统默认浏览器打开,移动端以普通 H5 页面打开)。

但钉钉除了常规的新窗口打开之外,还支持其他多种打开方式,如侧边栏打开、半浮层打开等。通过合理使用钉钉的统一跳转协议,即可实现这些跳转效果。但钉钉除了常规的新窗口打开之外,还支持其他多种打开方式,如侧边栏打开、半浮层打开等。通过合理使用钉钉的统一跳转协议,即可实现这些跳转效果。钉钉链接跳转规范.


这里主要介绍一下弹窗方式打开链接

格式:
dingtalk://dingtalkclient/page/link?popup_wnd=true&url=${url}&title=${title}&width=${width}&height=${height}
参数介绍:

根据上面要求, 我将某个连接以窗口形式加载, 并且窗口名为test, 链接应构造成
dingtalk://dingtalkclient/page/link?popup_wnd=true&url=https://open.dingtalk.com/document/orgapp/link-jump-specification&title=%E9%92%89%E9%92%89%E9%93%BE%E6%8E%A5%E8%A7%84%E8%8C%83&width=1000&height=800

不过经过测试, 目前弹窗的标题和弹窗的宽高配置之后都未生效, 已经向钉钉提交了相关问题, 希望能够早日处理吧

4. 卡片投放测试

卡片编辑完成并绑定变量, 参数之后, 可以进行发布. 发布后的卡片可以创建卡片实例.用于对具体场景的投放.
投放后, 我们便可以在钉钉中看到该消息

  1. 发布卡片

  2. 在卡片实例管理中创建卡片实例

    后面创建好卡片实例之后, 就会生成 outTrackId , 而 outTrackId在后面接口调试中会用到

  3. 实例创建是, 类型一般选择静态数据即可.
    此静态数据指的是, 卡片在发送之后, 只会拉取一次数据.
    而动态数据指的是, 卡片在发送之后, 可以按照指定周期去实时动态拉取消息.

  4. 投放卡片实例

    目前投放只能选择单聊场域, 选择投放之后便可进行投放

    投放成功之后, 钉钉内便可以收到此通知, 此步成功说明卡片创建成功

  5. 返回模板列表, 这里的模板ID后面接口调试会用到

5. web端调用机器人发送卡片接口

进入接口调试页面, 输入指定参数后发起调用, 执行成功之后点击示例代码即可获取消息调用的api


下面将分享机器人发送单聊互动卡片的代码

在开发环境运行下面代码需要下载jar包, 在下一章我将进行分享

// java代码
package com.aliyun.sample;import com.aliyun.tea.*;public class Sample {/*** 使用 Token 初始化账号Client* @return Client* @throws Exception*/public static com.aliyun.dingtalkim_1_0.Client createClient() throws Exception {com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config();config.protocol = "https";config.regionId = "central";return new com.aliyun.dingtalkim_1_0.Client(config);}public static void main(String[] args_) throws Exception {java.util.List<String> args = java.util.Arrays.asList(args_);com.aliyun.dingtalkim_1_0.Client client = Sample.createClient();com.aliyun.dingtalkim_1_0.models.SendRobotInteractiveCardHeaders sendRobotInteractiveCardHeaders = new com.aliyun.dingtalkim_1_0.models.SendRobotInteractiveCardHeaders();sendRobotInteractiveCardHeaders.xAcsDingtalkAccessToken = "<your access token>";com.aliyun.dingtalkim_1_0.models.SendRobotInteractiveCardRequest sendRobotInteractiveCardRequest = new com.aliyun.dingtalkim_1_0.models.SendRobotInteractiveCardRequest().setSingleChatReceiver("{\"userId\":\"用户userId\"}").setCardData("卡片模板变量填充的串").setCardBizId("卡片唯一id").setCardTemplateId("卡片模板id").setRobotCode("机器人码RobotCode");try {client.sendRobotInteractiveCardWithOptions(sendRobotInteractiveCardRequest, sendRobotInteractiveCardHeaders, new com.aliyun.teautil.models.RuntimeOptions());} catch (TeaException err) {if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {// err 中含有 code 和 message 属性,可帮助开发定位问题}} catch (Exception _err) {TeaException err = new TeaException(_err.getMessage(), _err);if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {// err 中含有 code 和 message 属性,可帮助开发定位问题}}        }
}
# python代码
import sysfrom typing import Listfrom alibabacloud_dingtalk.im_1_0.client import Client as dingtalkim_1_0Client
from alibabacloud_tea_openapi import models as open_api_models
from alibabacloud_dingtalk.im_1_0 import models as dingtalkim__1__0_models
from alibabacloud_tea_util import models as util_models
from alibabacloud_tea_util.client import Client as UtilClientclass Sample:def __init__(self):pass@staticmethoddef create_client() -> dingtalkim_1_0Client:"""使用 Token 初始化账号Client@return: Client@throws Exception"""config = open_api_models.Config()config.protocol = 'https'config.region_id = 'central'return dingtalkim_1_0Client(config)@staticmethoddef main(args: List[str],) -> None:client = Sample.create_client()send_robot_interactive_card_headers = dingtalkim__1__0_models.SendRobotInteractiveCardHeaders()send_robot_interactive_card_headers.x_acs_dingtalk_access_token = '<your access token>'send_robot_interactive_card_request = dingtalkim__1__0_models.SendRobotInteractiveCardRequest(single_chat_receiver='{"userId":"userId"}',card_data='卡片模板变量填充的json串',card_biz_id='chy123123',card_template_id='卡片唯一id',robot_code='机器人码RobotCode')try:client.send_robot_interactive_card_with_options(send_robot_interactive_card_request, send_robot_interactive_card_headers, util_models.RuntimeOptions())except Exception as err:if not UtilClient.empty(err.code) and not UtilClient.empty(err.message):# err 中含有 code 和 message 属性,可帮助开发定位问题pass@staticmethodasync def main_async(args: List[str],) -> None:client = Sample.create_client()send_robot_interactive_card_headers = dingtalkim__1__0_models.SendRobotInteractiveCardHeaders()send_robot_interactive_card_headers.x_acs_dingtalk_access_token = '<your access token>'send_robot_interactive_card_request = dingtalkim__1__0_models.SendRobotInteractiveCardRequest(single_chat_receiver='{"userId":"01335649490826229549"}',card_data='{   "notifyTitle": "您有一个审核带申请",   "notifyType": "审核通知",   "notifyContent": "王康提交的一个新闻中心待申请",   "handleType": "待审核",   "createTime": "2023-03-15 21:40" }',card_biz_id='chy123123',card_template_id='592fafad-f6ff-4937-b713-73779152c555',robot_code='dingm1iglijknxgpjrvc')try:await client.send_robot_interactive_card_with_options_async(send_robot_interactive_card_request, send_robot_interactive_card_headers, util_models.RuntimeOptions())except Exception as err:if not UtilClient.empty(err.code) and not UtilClient.empty(err.message):# err 中含有 code 和 message 属性,可帮助开发定位问题passif __name__ == '__main__':Sample.main(sys.argv[1:])

6. 生成待办并封装接口

我这里我采用将上面参数通过对象进行封装, 然后配置对象通过配置文件进行配置的方式进行封装

  1. 钉钉sds包的下载 下载页面传送门. 这里我选择下载新版sdk, 且版本号为最新的1.5.55

  2. 实体类的构建

/*** info:卡片通知相关** @Author caoHaiYang* @Date 2023/3/16 14:05*/
public class CartNotifyDTO {/*** 通知标题*/private String notifyTitle;/*** 通知类型*/private String notifyType;/*** 通知内容*/private String notifyContent;/*** 处理类型*/private String handleType;/*** 创建时间*/private String createTime;/*** 卡片接受者的用户id*/@JsonIgnoreprivate String receiveUserId;public String getNotifyTitle() {return notifyTitle;}public void setNotifyTitle(String notifyTitle) {this.notifyTitle = notifyTitle;}public String getNotifyType() {return notifyType;}public void setNotifyType(String notifyType) {this.notifyType = notifyType;}public String getNotifyContent() {return notifyContent;}public void setNotifyContent(String notifyContent) {this.notifyContent = notifyContent;}public String getHandleType() {return handleType;}public void setHandleType(String handleType) {this.handleType = handleType;}public String getCreateTime() {return createTime;}public void setCreateTime(String createTime) {this.createTime = createTime;}public String getReceiveUserId() {return receiveUserId;}public void setReceiveUserId(String receiveUserId) {this.receiveUserId = receiveUserId;}
  1. 单聊机器人发送卡片业务方法
    /*** 使用 Token 初始化账号Client* @return Client* @throws Exception*/public static com.aliyun.dingtalkim_1_0.Client createClient2() throws Exception {com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config();config.protocol = "https";config.regionId = "central";return new com.aliyun.dingtalkim_1_0.Client(config);}@Overridepublic void setCardNotify(CartNotifyDTO cardNotifyDTO) throws Exception {com.aliyun.dingtalkim_1_0.Client client = createClient2();com.aliyun.dingtalkim_1_0.models.SendRobotInteractiveCardHeaders sendRobotInteractiveCardHeaders = new com.aliyun.dingtalkim_1_0.models.SendRobotInteractiveCardHeaders();sendRobotInteractiveCardHeaders.xAcsDingtalkAccessToken = "这里注意accessToken有效期, 需要全局进行定时刷新, 所有的接口请求都要调用获取企业accessToken接口";com.aliyun.dingtalkim_1_0.models.SendRobotInteractiveCardRequest sendRobotInteractiveCardRequest = new com.aliyun.dingtalkim_1_0.models.SendRobotInteractiveCardRequest().setSingleChatReceiver("{\"userId\":\""+cardNotifyDTO.getReceiveUserId()+"\"}").setCardData(JSONObject.toJSONString(cardNotifyDTO)).setCardBizId(UUIDUtil.getUuid()).setCardTemplateId(dingTalkConfig.getCardTemplateId()).setRobotCode(dingTalkConfig.getRobotCode());try {SendRobotInteractiveCardResponse sendRobotInteractiveCardResponse = client.sendRobotInteractiveCardWithOptions(sendRobotInteractiveCardRequest, sendRobotInteractiveCardHeaders, new RuntimeOptions());} catch (TeaException err) {if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {// err 中含有 code 和 message 属性,可帮助开发定位问题log.error(err.code, err.getMessage());}} catch (Exception _err) {TeaException err = new TeaException(_err.getMessage(), _err);if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {// err 中含有 code 和 message 属性,可帮助开发定位问题log.error(err.code, err.getMessage());}}}

新发现

在资料搜寻时, 遇到了一个优秀的博主 Grandpa_Rick, 将钉钉发送互动消息做成了组件. 只需要将组件dingtalk-module项目打包, 然后放到demo项目中, 即可轻松使用, 至今依旧在持续更新. 博主人很棒, 向他请教了很多问题, 都很热心的解答. 感兴趣的也可以看看 DingTalk钉钉机器人单聊互动卡片消息的一次实现(附仓库).

基于该博主的demo代码. 只配置下面三个地方也能实现上述功能(但是需要自己重写一下dingtalk-module组件的DingBotMessageHandler类下的sendInteractiveMsgToIndividual方法, 在方法头部方法参数中新增卡片模板参数实体, 然后在方法中将参数实体转换成map放入到cardData.setCardParamMap中. 调用重写后的方法).

下面是配置修改内容

修改一: 基础信息的修改

修改二: 卡片模板id填入

修改三: 用户手机号(可以根据钉钉提供其他接口改成userId或union来实现单聊机器人卡片推送)


总结

不得不感慨, 阿里钉钉开放平台是一款优秀的企业级应用开发平台,具有便捷易用、功能完善、安全可靠、社区生态等优点,能够满足企业在数字化转型和业务创新方面的需求。其自身善于将钉钉内部开发的功能开放出来, 独立做成功能, 供各企业使用. 并且对各种功能进行组合, 实现业务场景的开发. 各种场景的组合则为企业提供了在管理和运营时的解决方案. 并且, 各个公司提供的钉钉场景解决方案还能在钉钉中进行商业化, 从而更好的促进钉钉更多的场景和解决方案的出现. 这种模式值得广大互联网公司学习.

但是, 仍然美中不足的是. 作为业界标杆, 钉钉在第三方企业进行对接仍存在不少问题. 比如: 接口部分文档描述较差, 跨场景关联性较差, 接口关键参数定义不清晰, 工单响应不及时, 接口sdk分新旧两版, 新版个版本具有哪些功能未说明.
良好的生态是需要有良好开放环境所支持的. 希望钉钉再接再厉, 在商业化的同时也不要忘了对开发者的良性支持. 这样才能让开放平台更加的开放!

钉钉机器人单聊实现互动卡片推送相关推荐

  1. DingTalk钉钉机器人单聊互动卡片消息的一次实现(附仓库)

    DingTalk钉钉机器人单聊互动卡片消息的一次实现 dingtalk-module迭代记录 07-08-22: 完善一下一些定义的接口, 方便理解 07-26-22: 看了下文档, 钉钉更新了两种互 ...

  2. php公众号向多个用户推送消息,如何实现微信公众号给指定互动用户推送多次消息?...

    1.微号帮平台注册账号.登录.授权公众号 2.创建推送信息 进入功能管理后,找到高级功能,选择48小时信息推送,点击添加推送信息,支持微信公众号给指定互动用户推送多次消息. 3.微号帮平台:编辑推送消 ...

  3. python生成QQ机器人爬取百度文库链接推送好友并生成词云

    QQ机器人爬取百度文库链接推送好友并生成词云 一.环境准备 二.实现QQ机器人 1.QQ机器人介绍 2.安装方法 3.实现自己的QQ机器人 三.百度文库内容链接爬取推送好友 代码实现: 思路分析 1. ...

  4. php 微信图文推送,微信单图文、多图文推送、列表中排序

    /** * @author yinhuiying(改于) * @since version - 2014-10-31 * @deprecated version - 2014-10-31 * 腾讯客服 ...

  5. 小程序表单提交,服务端推送模板消息通知

    1.小程序按钮表单,提交formid和openid 注:https://blog.csdn.net/qq_38191191/article/details/80982732 2.发送网络请求(小程序点 ...

  6. 持续集成之群聊机器人消息推送:钉钉 vs 企业微信

    企业微信和钉钉都有面向群的消息推送机器人,两者的使用非常相近,也有一些细节上的特性的区别,这篇文章将结合前面的使用示例进行总结. 使用方式 不同点 企业微信:先创建群,然后在群中添加机器人,可添加多个 ...

  7. 钉钉企业内部机器人python开发(公网部署版本)

    钉钉企业内部机器人开发(公网部署版本) 钉钉给出的开发文档地址如下: https://open.dingtalk.com/document/group/enterprise-created-chatb ...

  8. Jenkins:如何给coding的项目绑定钉钉机器人

    写在前面的话: 之前想弄这个功能来着,但是不巧,钉钉的Webhook正好在维护,就搁置了.于是每次合并代码之类的,我都会对着老大喊一嗓子,同意一下呗.今天喊了一嗓子后,老大说:你快弄下那个机器人提醒吧 ...

  9. 【钉钉-场景化能力包】IoT物联网设备协同

    需求场景 环境预警监控:企业与监控数据实时同步,当设备监测到环境异常时,需要通过机器人发送预警信息到个人或场景群,并呈现预警具体信息. 考勤机数据推送:员工通过考勤机完成一键考勤,考勤成功后通过机器人 ...

最新文章

  1. python使用界面-如何使用Python建立有窗口、按钮之类的图形界面
  2. Distributed locks with Redis--官方
  3. 早上吃燕麦是一个很好的方法
  4. css 图文 上下 居中,CSS垂直居中的6种方法
  5. 二、数据库原理-设计理论
  6. Photoshop脚本 使用ExtendScript编写Ps脚本
  7. Jmeter参数化 CSV Data Set Config界面说明
  8. SQL Server数据库是否会引发恶意?
  9. Oracler的锁概念
  10. just show up失败让我们成长
  11. 2021/8/10 正在F-Droid里下载Termux...
  12. 常用的银行卡验证API接口——银行卡三元素API接口
  13. 12 如何分析kernel panic?
  14. python find_peaks 源码理解
  15. 瑞吉外卖项目学习笔记01
  16. JRebel启动报错:compile error: cannot find constructor org.zeroturnaround.javarebel.integration.spring
  17. 正则表达式匹配英文和法文
  18. 彻底解决win10屏幕亮度无法调节
  19. 重置ubuntu密码
  20. java 内部接口 内部类_Java接口/内部类

热门文章

  1. 2019年12月前端面经及总结(西安,杭州)
  2. 【Ubuntu Ip冲突时通过Netplan修改Ip】
  3. 从十大经典故事中学管理(转…
  4. 央行数字货币DCEP指南
  5. php非默认安装的扩展库,PHP安装新的扩展库
  6. 服务器经常开关机会影响吗,频繁给手机重启/开关机,会对手机产生危害吗?...
  7. 网络开发的两大架构★★★★★
  8. Unity 2017.3 Beta已发布
  9. es - 关于环境配置时limits.conf中nofile和nproc代表的含义
  10. git 导出patch 和应用patch