钉钉企业机器人单聊消息发送实践-大数据平台(XSailboat)消息中心消息推送

1. 背景

在笔者开发的大数据平台XSailboat中有 消息中心 模块,用来全平台的消息收集,整理分拆、订阅发送等功能。消息推送方式支持钉钉群聊、钉钉单聊、短信通知。现记录一下企业机器人消息单聊推送的实现过程。

2. 钉钉开发文档

这是官方的开发文档地址:《机器人发送、查询和撤回单聊消息》

将其中的主要过程抄录一下:

步骤一:登录开发者后台,点击应用开发-企业内部开发,创建机器人。
步骤二:点击机器人应用-基础信息,获取AppKey和AppSecret。
步骤三:添加接口调用权限,点击“机器人”,申请企业内机器人发送消息权限。申请权限无需审批,默认开通。
步骤四:上线机器人。进入版本管理与发布页面,点击上线,机器人的状态变更为已发布。
步骤五:获取应用访问凭证获取企业内部应用的accessToken。调用接口时,通过accessToken鉴权调用者身份。
步骤六:调用机器人单聊相关的API:

  • 调用服务端API-批量发送单聊消息,获取消息processQueryKey。
  • 根据消息processQueryKey,调用服务端API-批量查询单聊机器人是否已读,查询机器人发送的单聊消息对方是否已读。
  • 根据消息processQueryKey,调用服务端API-批量撤回单聊消息,撤回机器人发送的单聊消息。

3. 记录自己的操作过程

  1. 上企业的 开发者后台 创建应用(应用名:大数据平台-消息中心)。
  2. 创建了应用之后,再点进应用,创建机器人,创建好了之后发布。
    在这里插入图片描述
  3. 申请“机器人”里面的 “企业内机器人发送消息权限” 和“通讯录管理”中的 “根据手机号姓名获取成员信息的接口访问权限”
  4. 根绝Appkey和AppSecret获取token。
  • 《获取企业内部应用的access_token》
  1. 定向向某人发送钉钉消息时需要指定userId,而消息中心配置的消息订阅人设置的是手机号。所以需要将手机号翻译成userId。
  • 《根据手机号查询企业账号用户》
  1. 发送消息的API。
  • 《批量发送人与机器人会话中机器人消息》

4. 示例代码

package com.cimstech.sailboat.ms.msg;import com.cimstech.sailboat.ms.msg.sender.Letter;
import com.cimstech.xfront.common.http.HttpClient;
import com.cimstech.xfront.common.http.Request;
import com.cimstech.xfront.common.json.JSONArray;
import com.cimstech.xfront.common.json.JSONObject;
import com.cimstech.xfront.common.text.XString;
import com.cimstech.xfront.common.time.XTime;public class Test
{/*** 获取token,查询userId的服务地址*/static final String sDingService = "https://oapi.dingtalk.com" ;/*** 发送钉钉消息的服务地址*/static final String sDingSendService = "https://api.dingtalk.com" ;/*** 凭借appKey和appSecret获取token*/static final String sPath_GetToken = "/gettoken" ;/*** 通过手机号获取企业账号用户*/static final String sPath_GetUserByMobile = "/topapi/v2/user/getbymobile" ;static final String sPath_SendMsgOfSingleChat = "/v1.0/robot/oToMessages/batchSend" ;static final String sDetailPtn_n = "[以及另外{}条详情..]({})" ;static final String sDetailPtn_1 = "[详情..]({})" ;static final String sNoLink = "以及另外{}条" ;static String[] sColors = new String[] {"#FF0000", "#ffa500", "#0fdbdb", "#12e512"} ;static String sDetailLink = "http://XXXXX/sailmsg/msgrow?sendBatch={}&totalAmount={}" ;static JSONObject toJSON(Letter aLetter){int level = aLetter.getLevel() ;String color = level < sColors.length? sColors[level]:null ;StringBuilder strBld = new StringBuilder("【消息中心-").append(aLetter.getSubscriberItemName()).append("】").append("-[") ;if(color != null)strBld.append("<font color=").append(color).append(">") ;strBld.append(level).append("级") ;if(color != null)strBld.append("</font>") ;
//		String partition = aLetter.getPartition() ;strBld.append("]") ; // .append(sSiteMap.getOrDefault(partition , partition)) ;String title = strBld.toString() ;strBld.setLength(0);strBld.append("**").append(title).append("**  \n  > ").append("<font color=#000000>").append(aLetter.getContent()).append("</font>").append("  \n  \n  ") ;strBld.append("<font color=#666666>").append(aLetter.getEventTime()).append("</font>").append("&nbsp;&nbsp;&nbsp;&nbsp;") ;if(XString.isEmpty(sDetailLink)){if(aLetter.getReduceAmount() > 0)strBld.append(XString.msgFmt(sNoLink , aLetter.getReduceAmount())) ;}else{if(aLetter.getReduceAmount() > 1){strBld.append(XString.msgFmt(sDetailPtn_n, aLetter.getReduceAmount(), XString.msgFmt(sDetailLink , aLetter.getSendBatch() , aLetter.getReduceAmount()))) ;}else{strBld.append(XString.msgFmt(sDetailPtn_1, XString.msgFmt(sDetailLink , aLetter.getSendBatch() , aLetter.getReduceAmount()))) ;}}String content = strBld.toString() ;return new JSONObject().put("title" , title).put("text", content);}public static void main(String[] args) throws Exception{String appKey = "ddddddddddddddd" ;String appSecret = "ssssssssssssssssssssssssssssssss" ;String mobile = "130XXXXXXXXX" ;String robotCode = "dingXXXXXXXXX" ;// 1.获取AccessTokenHttpClient client = HttpClient.ofUrl(sDingService) ;long expireTime = System.currentTimeMillis() ;/*** {*   "errcode": 0,*   "access_token": "aaaaaaaaaaaaaaaaa",*   "errmsg": "ok",*   "expires_in": 7200* }*/JSONObject jo = client.askJo(Request.GET().path(sPath_GetToken).queryParam("appkey" , appKey).queryParam("appsecret", appSecret)) ;System.out.println("获取到的Token:"+jo) ;String accessToken = jo.optString("access_token") ;expireTime += jo.optLong("expires_in") * 1000 ;/*** {*   "errcode": 0,*   "errmsg": "ok",*   "result":*   {*     "exclusive_account_userid_list": [],*     "userid": "bbbbbbbbbbbbb"*   },*   "request_id": "ccccccc"* }*/// 2. 查询userIdjo = client.askJo(Request.POST().path(sPath_GetUserByMobile).queryParam("access_token" , accessToken).setJsonEntity(new JSONObject().put("mobile" , mobile).put("support_exclusive_account_search" , true))) ;System.out.println("获取到的User信息:"+jo);String userId = jo.pathString(null , "result" , "userid") ;Letter letter = new Letter(null) ;letter.setLevel(5);letter.setContent("单聊测试") ;letter.setEventTime(XTime.current$yyyyMMddHHmmss());letter.setReduceAmount(1) ;letter.setSendBatch("xxxxxxxxxxxxxxx") ;letter.setSubscriberItemName("单聊测试订阅项");String msgParam = toJSON(letter).toJSONString() ;/*** {*   "flowControlledStaffIdList": [],*   "invalidStaffIdList": [],*   "processQueryKey": "xxxxxxxxxxxxxx"* }*/// 3.发送消息jo = HttpClient.ofUrl(sDingSendService).askJo(Request.POST().path(sPath_SendMsgOfSingleChat).header("x-acs-dingtalk-access-token" , accessToken).setJsonEntity(new JSONObject().put("robotCode" , robotCode).put("userIds", new JSONArray().put(userId)).put("msgKey", "sampleMarkdown").put("msgParam" , msgParam))) ;System.out.println("发送消息收到的回复:"+jo);}}

效果:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/646520.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MyBatis中一级缓存是什么?SqlSession一级缓存失效的原因?如何理解一级缓存?

一级缓存是SqlSession级别的&#xff0c;通过同一个SqlSession查询的数据会被缓存&#xff0c;下次查询相同的数据&#xff0c;就 会从缓存中直接获取&#xff0c;不会从数据库重新访问 使一级缓存失效的四种情况&#xff1a; 1) 不同的SqlSession对应不同的一级缓存 2) 同一…

好书推荐丨豆瓣评出9.2高分!Python编程入门就看蟒蛇书

目录 写在前面 内容简介 业内专家推荐 编辑推荐 资源丰富 作者介绍 Q&A 粉丝福利 写在后面 写在前面 在这日新月异的科技新时代&#xff0c;编程如同一把万能钥匙&#xff0c;为无数人打开了通向无限可能的大门。而在众多编程语言中&#xff0c;Python无疑是最耀…

etcd未授权到控制k8s集群

在安装完 K8s 后&#xff0c;默认会安装 etcd 组件&#xff0c;etcd 是一个高可用的 key-value 数据库&#xff0c;它为 k8s 集群提供底层数据存储&#xff0c;保存了整个集群的状态。大多数情形下&#xff0c;数据库中的内容没有加密&#xff0c;因此如果黑客拿下 etcd&#x…

防御课程—华为USG6000V1的配置实验(一)

实验拓扑&#xff1a; 实验分析 由实验拓扑图需求分析可知我们在生产区和办公区需要用到子接口技术 实验配置 在Cloud1上配置 在DMZ区域配置 在server1上配置在server2上配置在防火墙上进行的配置 由实验拓扑图可知防火墙与DMZ区域相连的接口为GigabitEthernet1/0/0接口 …

Pandas.Series.median() 中位数 详解 含代码 含测试数据集 随Pandas版本持续更新

关于Pandas版本&#xff1a; 本文基于 pandas2.2.0 编写。 关于本文内容更新&#xff1a; 随着pandas的stable版本更迭&#xff0c;本文持续更新&#xff0c;不断完善补充。 传送门&#xff1a; Pandas API参考目录 传送门&#xff1a; Pandas 版本更新及新特性 传送门&…

MySQL(基础篇)——SQL

一.SQL分类 二.DDL(数据定义语言) 1.DDL——数据库操作 ① 查询 查询所有数据库 SHOW DATABASES 查询当前所处数据库 SELECT DATABASE() ② 创建 CREATE DATABASE [IF NOT EXISTS] 数据库名(通常以db结尾) [DEFAULT CHARSET 字符集] [COLLATE 排序规则] ③ …

java分布式锁详解

本地锁 浏览器把100w请求由网关随机往下传&#xff0c;在集群情况下&#xff0c;每台服务都放行10w请求过来&#xff0c;这时候每台服务都用的是本地锁是跨JVM的&#xff0c; 列如这些服务都没有49企业&#xff0c;此时有几个服务进行回原了打击在DB上面&#xff0c;那后期把这…

Allegro PCB如何关联原理图?

在用Allegro进行PCB设计时,我们可以点击Orcad原理图上的器件,然后PCB会自动跳转到该器件。那如何操作PCB上的器件点击跳转到原理图呢? 这种方式可以提高设计的效率。具体操作如下。 选择菜单栏Display

【软考问题】-- 2 - 知识精讲 - 项目立项管理

一、基本问题 1&#xff1a;项目投资前时期的四个阶段是什么&#xff1f; a.项目建议与立项申请 (1)定义&#xff1a;项目建设单位向上级主管部门提交项目申请时所必须的文件。(2)特点&#xff1a;项目发展周期的初始阶段、可行性研究的依据。(3)注意&#xff1a;又称项目建议书…

xinput1_3.dll文件的几种修复办法以及修复xinput1_3.dll注意事项

xinput1_3.dll文件是DirectX的一部分&#xff0c;它在Windows系统中负责处理游戏控制器的输入。然而&#xff0c;有时候此文件可能会出现问题&#xff0c;导致游戏无法正常运行或启动。在本文中&#xff0c;将介绍多种解决xinput1_3.dll文件问题的方法&#xff0c;并对它们进行…

【Web前端开发基础】CSS3之空间转换和动画

CSS3之空间转换和动画 目录 CSS3之空间转换和动画一、空间转换1.1 概述1.2 3D转换常用的属性1.3 3D转换&#xff1a;translate3d&#xff08;位移&#xff09;1.4 3D转换&#xff1a;perspective&#xff08;视角&#xff09;1.5 3D转换&#xff1a;rotate3d&#xff08;旋转&a…

vit细粒度图像分类(一)CADF学习笔记

1.摘要&#xff1a; 目的 基于Transformer架构的网络在图像分类中表现出优异的性能。然而&#xff0c;注意力机制往往只关注图像中的显著性特征&#xff0c;而忽略了其他区域的次级显著信息&#xff0c;基于自注意力机制的Transformer也是如此。为了获取更多的有效信息&#…

Spring基于AbstractRoutingDataSource实现MySQL多数据源

目录 多数据源实现 yml配置文件 配置类 业务代码 案例演示 多数据源实现 yml配置文件 spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedatasource1:url: jdbc:mysql://127.0.0.1:3306/datasource1?serverTimezoneUTC&useUnicodetrue&characte…

企业转型:虚拟化对云计算的影响

虚拟化被认为是IT行业最优秀的技术之一。虚拟化提供的灵活性和效率&#xff0c;有助于企业根据不断变化的需求扩展其IT基础设施。虚拟化是云基础设施的基础&#xff0c;允许按需动态分配和管理计算资源。这种适应性对于满足现代企业的多样化需求至关重要&#xff0c;因为现代企…

外汇天眼:美国证券交易委员会(SEC)采纳了一系列规定,以加强与特殊目的收购公司(SPACs)相关的投资者保护

美国证券交易委员会&#xff08;SEC&#xff09;今天通过了一系列新规和修订&#xff0c;以增强特殊目的收购公司&#xff08;SPACs&#xff09;的首次公开募股&#xff08;IPOs&#xff09;中的披露&#xff0c;并在SPACs与目标公司之间的后续业务合并交易&#xff08;de-SPAC…

u盘可以分区吗?怎么分区?分区后不显示出来怎么办

随着科技的不断发展&#xff0c;U盘已经成为人们传输和存储数据的重要设备之一。而针对U盘的分区问题&#xff0c;很多人对此还不太了解。比如&#xff0c;U盘可以分区吗&#xff1f;U盘怎么分区&#xff1f;U盘分区后不显示出来怎么办&#xff1f;下面我们一起来针对这些问题进…

SpringCloud Aliba-Seata【下】-从入门到学废【8】

目录 1.数据库创建 1.seata_account库下建表 2.seata_order库下建表 3.seata_storage库下建表 4.在每个库下创建回滚日志 2.创建订单模块 2.1建工程 2.2加pom 2.3改yml 2.4file.conf 2.5registry.conf 2.6domain 2.7Dao 2.8Service 2.9controller 2.10confi…

对 MODNet 网络结构直接剪枝的探索

文章目录 1 写在前面2 遇到问题3 解决方案4 探索过程4.1 方案一4.2 方案二4.3 方案三 5 疑惑与思考5.1 Q15.2 Q2 1 写在前面 在前面的文章中&#xff0c;笔者与小伙伴们分享了对 MODNet 主干网络部分以及其余分支分别剪枝的探索历程&#xff0c;即先分解、再处理、后融合的手法…

C++:缺省参数函数重载

目录 C/C语言 函数调用的工作原理&#xff1a; 函数调用一般分为两个部分&#xff1a; 缺省参数&#xff1a; 缺省参数的分类&#xff1a; 全缺省参数 半缺省参数 注意事项&#xff1a; 缺省参数与C语言的调用参数对比&#xff1a; 函数重载&#xff1a; 函数重载…

pve8.1 安装、创建centos7虚拟机及配置

之前创建虚拟机centos7时&#xff0c;硬盘分配太大了&#xff0c;做成模板后无法进行修改了&#xff0c;安装完pve8.1后&#xff0c;强迫症犯了重新创建一下顺便记录一下配置过程。由于目前centos7还是生产用的比较多的版本所以本次还是安装centos7.9版本。 一、下载镜像 下载…