机器人SDK 封装预警消息发送工具"/>
钉钉机器人SDK 封装预警消息发送工具
文章目录
- 1 群机器人
- 2 自定义机器人
- 2.1 创建机器人
- 2.2 Postman 通过webhook 发送消息演示
- 3 创建项目引入钉钉机器人SDK
- 3.1 创建Maven 项目引入SDK
- 3.2 预警消息发送工具类封装
- 3.2.1 工具类说明
- 3.3.2 测试发送消息
- 3.3 项目源码
1 群机器人
(1) 引言
钉钉聊天群内支持的群机器人, 类似QQ 群机器人, 可以发天气, 讲笑话那样;
钉钉群机器人支持自定义机器人, 允许开发者管理机器人做预警消息通知;
(2) 限制
- 6 个机器人/每群, 20条消息/每分钟
- 不支持应答模式, 仅做群消息通知
(3) 消息格式
支持普通文本消息, 链接消息, markdown 格式(注意仅部分语法支持)文本, 图片及链接, 支持FeedCard, ActionCard 等消息
(4) 官方开发文档
2 自定义机器人
2.1 创建机器人
自定义机器人依赖于钉钉群, 首先创建钉钉群(群主有权限查看access_token), 拿到webhook 信息, 通过网络发送请求, 钉钉开放平台响应请求发送预警消息到指定access_token 的钉钉群
钉钉群创建方式是在聊天页面, 点击"+"添加其他人来创建一个钉钉群.
进入群, 打开设置, 选中群机器人, 创建自定义机器人, 完善机器人基本信息后创建完成, 机器人自动推送一条欢迎语到钉钉群.
支持配置安全设置,来保障信息安全,设置方式包括自定义关键词校验、加签密钥校验、IP地址白名单校验;此处以加签为例。
2.2 Postman 通过webhook 发送消息演示
此处使用Postman 模拟请求, 如下所示, 发送普通文本消息后, 钉钉群会收到机器人的预警消息.(此处为旧版本未加签时的钉钉消息发送测试图,仅作演示参考)
3 创建项目引入钉钉机器人SDK
本机开发环境win10, 已创建钉钉群, 并获取到webhook, 本机使用的IDEA, 下载的Java SDK
3.1 创建Maven 项目引入SDK
(1)Java SDK 下载
下载SDK: , 官方支持以下版本, 此处使用Java SDK;
- JAVA版本
- PHP版本
- .NET版
- Python版
解压后是官方直接打好的jar 包, 也包括源码包
(2)创建Maven 项目引入Jar
创建Maven 项目引入打好的jar 包: 本机jar 包地址: D:\download\dingtalk-sdk-java\taobao-sdk-java-auto_1479188381469-20190704.jar
- mvn 命令
mvn install:install-file
-Dfile=D:\download\dingtalk-sdk-java\taobao-sdk-java-auto_1479188381469-20190704.jar -DgroupId=com.dingtalk -DartifactId=com-dingtalk-api
-Dversion=1.0.0 -Dpackaging=jar
- 参数说明
-Dfile jar包所在路径,需要包含jar包名.此处D:\download\dingtalk-sdk-java\taobao-sdk-java-auto_1479188381469-20190704.jar
-DgroupId 指定导入jar时的groupid,可以自定义,此处com.dingtalk
-DartifactId指定导入jar时的artifactId,可以自定义,此处com-dingtalk-api
-Dversion 指定导入jar时的版本号,可以自定义,此处1.0.0
-Dpackaging 指定文件类型 ,由于这里是jar包的形式,所以这里得是jar
3.2 预警消息发送工具类封装
3.2.1 工具类说明
项目成功引入SDK, 支持通过SDK 指定自定义机器人去发送群消息, 前面有说明群机器人支持发送多种类型的消息, 也支持消息带跳转链接, 或者图片带跳转链接等;
工具类创建客户端client 实例需要拥有正确的webhook, 就是图2-5. 群机器人管理页面图中群设置中可以查看或者重置的hook 标识.
此处安全设置了加签,需要对请求服务链接做一次封装;维护时间戳及签名;签名通过创建机器人时的密钥进行加密转换获得。密钥如图2-3. 创建自定义机器人图中加签设置下以SEC开头的密钥串。
=xxx×tamp=xxx&sign=xxx
加密方法如下
/*** 钉钉群设置 webhook, 支持重置*/private static final String ACCESS_TOKEN = "=40d2e1ef8c83b0ade5c7d2ae43553988c68373c1fa0901dcd701b0c2f5f90c59";/*** 加签密钥,支持重置*/private static final String SECRET = "SECc90f8dac81401632962362d280f79a86e2875d8fa2282c7ee80249385be76b38";/*** 安全设置:是否加签*/private static boolean isSign = true;/*** 客户端实例*/public static DingTalkClient client = new DefaultDingTalkClient(getServerUrl(ACCESS_TOKEN, SECRET));/*** @param: accessToken token* @param: secret 加签密钥* @description: 获取 serverUrl* @return: java.lang.String* @author: niaonao*/private static String getServerUrl(String accessToken, String secret) {StringBuilder serverUrl = new StringBuilder(accessToken);// isSign 是否加签if (!isSign) {return serverUrl.toString();}try {Long timestamp = System.currentTimeMillis();String stringToSign = timestamp + "\n" + secret;Mac mac = Mac.getInstance("HmacSHA256");mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256"));byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));String sign = URLEncoder.encode(new String(Base64.encodeBase64(signData)),"UTF-8");serverUrl.append("×tamp=").append(timestamp).append("&sign=").append(sign);} catch (Exception e) {e.printStackTrace();}return serverUrl.toString();}
util 下即封装的工具类RobotHelperUtil.java, 其中test 包下有做单元测试
3.3.2 测试发送消息
此处单元测试测试方法sendMessageByMarkdown(), sendMessageByActionCardMulti(), sendMessageByFeedCard() 等方法, 群机器人会发送群消息, 其中方法参数title 效果如图中左侧(联系人列表页面)透出的消息内容, text 是进入群后的真正的消息.;
此处发送的支持单独跳转的消息, 初始化了三个按钮附带跳转链接, 按钮默认纵向排列. 点击按钮即可跳转指定链接查看详细内容.
此处发送的支持多个Link 的消息, 初始化了三个link , 指定标题导图及跳转链接; 首个link 效果更突出, 这个还挺好玩的.
钉钉消息支持配置内容、图片跳转链接,多为钉钉内部打开查看;同时有提供钉钉打开外部链接的消息类型 msgType:link,测试类的方法 sendMessageByLink,运行结果如下图所示。
3.3 项目源码
- 枚举类 MsgTypeEnum.java
package pers.niaonao.dingtalkrobot.enums;/*** @className: MsgTypeEnum* @description: 消息类型枚举嘞* @date: 2021/6/15* @author niaonao**/
public enum MsgTypeEnum {MSG_TYPE_TEXT("text", "MSG_TYPE_TEXT"),MSG_TYPE_LINK("link", "MSG_TYPE_LINK"),MSG_TYPE_MARKDOWN("markdown", "MSG_TYPE_MARKDOWN"),MSG_TYPE_ACTION_CARD("actionCard", "MSG_TYPE_ACTION_CARD"),MSG_TYPE_FEED_CARD("feedCard", "MSG_TYPE_FEED_CARD");private String value;private String code;MsgTypeEnum(String value, String code) {this.code = code;this.value = value;}public static MsgTypeEnum getEnumValue(String value){for (MsgTypeEnum constants : values()) {if (constants.getValue() == value) {return constants;}}return null;}public String getValue() {return value;}public String getCode() {return code;}
}
- 工具类 DataValidUtil.java:
package pers.niaonao.dingtalkrobot.util;import lombok.extern.slf4j.Slf4j;import java.lang.reflect.Field;/*** @className: DataValidUtil* @description: 数据基础校验工具* @author: niaonao* @date: 2019/7/6**/
@Slf4j
public class DataValidUtil {/*** @description: 判空方法* @param obj* @return: boolean* @author: niaonao* @date: 2019/7/6*/public static boolean checkNotEmpty(Object... obj) {for (Object object : obj) {if (null == object) {return false;}}return true;}/*** @description: 校验接口入参对象方法* 校验object 对象是否全部属性都不为null* @param object* @return: boolean* @author: niaonao* @date: 2019/7/6*/public static boolean checkExistEmpty(Object object) {if (null == object) {return false;}boolean flag = false;Class clazz = object.getClass();Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {//设置权限, 获取private的属性field.setAccessible(true);Object fieldValue = null;try {fieldValue = field.get(object);} catch (IllegalArgumentException | IllegalAccessException e) {log.error("对象属性值为空校验, 异常捕获{}", e.getMessage());}//只要有一个属性值不为null 就返回false 表示对象不为nullif (null == fieldValue) {flag = true;break;}}return flag;}
}
钉钉消息发送工具类 RobotHelperUtil.java
package pers.niaonao.dingtalkrobot.util;import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiRobotSendRequest ;
import com.dingtalk.api.response.OapiRobotSendResponse;
import com.taobao.api.TaobaoRequest;
import com.taobao.api.TaobaoResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;import java.util.Arrays;
import java.util.List;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apachemons.codec.binary.Base64;
import pers.niaonao.dingtalkrobot.enums.MsgTypeEnum;import java.URLEncoder;
/*** @className: RobotHelperUtil* @description: 机器人工具类* 每个机器人每分钟最多发送20条* 限制6 个机器人/群* * @author: niaonao* @date: 2019/7/6**/
@Slf4j
public class RobotHelperUtil {/*** 钉钉群设置 webhook, 支持重置*/private static final String ACCESS_TOKEN = "=40d2e1ef8c83b0ade5c7d2ae43553988c68373c1fa0901dcd701b0c2f5f90c59";/*** 加签密钥,支持重置*/private static final String SECRET = "SECc90f8dac81401632962362d280f79a86e2875d8fa2282c7ee80249385be76b38";/*** 安全设置:是否加签*/private static boolean isSign = true;/*** 客户端实例*/public static DingTalkClient client = new DefaultDingTalkClient(getServerUrl(ACCESS_TOKEN, SECRET));/*** @description: 官方演示示例* title 是消息列表下透出的标题* text 是进入群后看到的消息内容** @author: niaonao* @date: 2019/7/6*/public static void sdkDemoJava() {OapiRobotSendRequest request = new OapiRobotSendRequest();request.setMsgtype("text");OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text();text.setContent("测试文本消息");request.setText(text);OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();at.setAtMobiles(Arrays.asList("13261303345"));request.setAt(at);request.setMsgtype("link");OapiRobotSendRequest.Link link = new OapiRobotSendRequest.Link();link.setMessageUrl("/");link.setPicUrl("");link.setTitle("时代的火车向前开");link.setText("这个即将发布的新版本,创始人陈航(花名“无招”)称它为“红树林”。\n" +"而在此之前,每当面临重大升级,产品经理们都会取一个应景的代号,这一次,为什么是“红树林");request.setLink(link);request.setMsgtype("markdown");OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();markdown.setTitle("杭州天气");markdown.setText("#### 杭州天气 @156xxxx8827\n" +"> 9度,西北风1级,空气良89,相对温度73%\n\n" +"> ![screenshot](.png)\n" +"> ###### 10点20分发布 [天气](/) \n");request.setMarkdown(markdown);requestExecute(request);}/*** @description: 发送普通文本消息* @param content 文本消息* @param mobileList 指定@ 联系人* @param isAtAll 是否@ 全部联系人* @return: com.dingtalk.api.response.OapiRobotSendResponse* @author: niaonao* @date: 2019/7/6*/public static OapiRobotSendResponse sendMessageByText(String content, List<String> mobileList, boolean isAtAll) {if (StringUtils.isEmpty(content)) {return null;}OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();at.setIsAtAll(isAtAll);//atMobiles 被@人的手机号if (!CollectionUtils.isEmpty(mobileList)) {at.setAtMobiles(mobileList);if (!isAtAll) {content = content + "\n";for (String mobile : mobileList) {content = content + "@" + mobile;}}}//参数 参数类型 必须 说明OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text();text.setContent(content);OapiRobotSendRequest request = new OapiRobotSendRequest();request.setAt(at);request.setMsgtype(MsgTypeEnum.MSG_TYPE_TEXT.getValue());request.setText(text);OapiRobotSendResponse response = (OapiRobotSendResponse) requestExecute(request);return response;}/*** @description: 发送link 类型消息,点击标题实现在钉钉外部打开链接* @param title 消息标题* @param text 消息内容* @param messageUrl 点击消息后跳转的url* @param picUrl 插入图片的url* @return: com.dingtalk.api.response.OapiRobotSendResponse* @author: niaonao* @date: 2019/7/6*/public static OapiRobotSendResponse sendMessageByLink(String title, String text, String messageUrl, String picUrl) {if (!DataValidUtil.checkNotEmpty(title, text, messageUrl)) {return null;}OapiRobotSendRequest.Link link = new OapiRobotSendRequest.Link();link.setTitle(title);link.setText(text);link.setMessageUrl(messageUrl);link.setPicUrl(picUrl);OapiRobotSendRequest request = new OapiRobotSendRequest();request.setMsgtype(MsgTypeEnum.MSG_TYPE_LINK.getValue());request.setLink(link);OapiRobotSendResponse response = (OapiRobotSendResponse) requestExecute(request);return response;}/*** @description: 发送Markdown 编辑格式的消息* @param title 标题* @param markdownText 支持markdown 编辑格式的文本信息* @param mobileList 消息@ 联系人* @param isAtAll 是否@ 全部* @return: com.dingtalk.api.response.OapiRobotSendResponse* @author: niaonao* @date: 2019/7/6*/public static OapiRobotSendResponse sendMessageByMarkdown(String title, String markdownText, List<String> mobileList, boolean isAtAll) {if (!DataValidUtil.checkNotEmpty(title, markdownText)) {return null;}OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();at.setIsAtAll(isAtAll);if (!CollectionUtils.isEmpty(mobileList)) {at.setAtMobiles(mobileList);if (!isAtAll) {markdownText = markdownText + "\n";for (String mobile : mobileList) {markdownText = markdownText + "@" + mobile;}}}OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();markdown.setTitle(title);markdown.setText(markdownText);OapiRobotSendRequest request = new OapiRobotSendRequest();request.setMsgtype(MsgTypeEnum.MSG_TYPE_MARKDOWN.getValue());request.setMarkdown(markdown);request.setAt(at);OapiRobotSendResponse response = (OapiRobotSendResponse) requestExecute(request);return response;}/*** @description: 整体跳转ActionCard类型的消息发送* @param title 消息标题, 会话消息会展示标题* @param markdownText markdown格式的消息* @param singleTitle 单个按钮的标题* @param singleURL 单个按钮的跳转链接* @param btnOrientation 是否横向排列(true 横向排列, false 纵向排列)* 参数 类型 必选 说明* msgtype string true 此消息类型为固定actionCard* title string true 首屏会话透出的展示内容* text string true markdown格式的消息* singleTitle string true 单个按钮的方案。(设置此项和singleURL后btns无效)* singleURL string true 点击singleTitle按钮触发的URL* btnOrientation string false 0-按钮竖直排列,1-按钮横向排列* @return: com.dingtalk.api.response.OapiRobotSendResponse* @author: niaonao* @date: 2019/7/6*/public static OapiRobotSendResponse sendMessageByActionCardSingle(String title, String markdownText, String singleTitle, String singleURL, boolean btnOrientation) {if (!DataValidUtil.checkNotEmpty(title, markdownText)) {return null;}OapiRobotSendRequest.Actioncard actionCard = new OapiRobotSendRequest.Actioncard();actionCard.setTitle(title);actionCard.setText(markdownText);actionCard.setSingleTitle(singleTitle);actionCard.setSingleURL(singleURL);// 此处默认为0actionCard.setBtnOrientation(btnOrientation ? "1" : "0");OapiRobotSendRequest request = new OapiRobotSendRequest();request.setMsgtype(MsgTypeEnum.MSG_TYPE_ACTION_CARD.getValue());request.setActionCard(actionCard);OapiRobotSendResponse response = (OapiRobotSendResponse) requestExecute(request);return response;}/*** @description: 独立跳转ActionCard类型 消息发送* @param title 标题* @param markdownText 文本* @param btns 按钮列表* @param btnOrientation 是否横向排列(true 横向排列, false 纵向排列)* 参数 类型 必选 说明* msgtype string true 此消息类型为固定actionCard* title string true 首屏会话透出的展示内容* text string true markdown格式的消息* btns array true 按钮的信息:title-按钮方案,actionURL-点击按钮触发的URL* btnOrientation string false 0-按钮竖直排列,1-按钮横向排列* @return: com.dingtalk.api.response.OapiRobotSendResponse* @author: niaonao* @date: 2019/7/6*/public static OapiRobotSendResponse sendMessageByActionCardMulti(String title, String markdownText, List<OapiRobotSendRequest.Btns> btns, boolean btnOrientation) {if (!DataValidUtil.checkNotEmpty(title, markdownText) || CollectionUtils.isEmpty(btns)) {return null;}OapiRobotSendRequest.Actioncard actionCard = new OapiRobotSendRequest.Actioncard();actionCard.setTitle(title);actionCard.setText(markdownText);// 此处默认为0actionCard.setBtnOrientation(btnOrientation ? "1" : "0");actionCard.setBtns(btns);OapiRobotSendRequest request = new OapiRobotSendRequest();request.setMsgtype(MsgTypeEnum.MSG_TYPE_ACTION_CARD.getValue());request.setActionCard(actionCard);OapiRobotSendResponse response = (OapiRobotSendResponse) requestExecute(request);return response;}/** * @description: 发送FeedCard类型消息* msgtype string true 此消息类型为固定feedCard* title string true 单条信息文本* messageURL string true 点击单条信息到跳转链接(在钉钉内部打开,不能跳转到外部)* picURL string true 单条信息后面图片的URL* @param links* @return: com.dingtalk.api.response.OapiRobotSendResponse* @author: niaonao* @date: 2019/7/6*/public static OapiRobotSendResponse sendMessageByFeedCard(List<OapiRobotSendRequest.Links> links) {if (CollectionUtils.isEmpty(links)) {return null;}OapiRobotSendRequest.Feedcard feedcard = new OapiRobotSendRequest.Feedcard();feedcard.setLinks(links);OapiRobotSendRequest request = new OapiRobotSendRequest();request.setMsgtype(MsgTypeEnum.MSG_TYPE_FEED_CARD.getValue());request.setFeedCard(feedcard);OapiRobotSendResponse response = (OapiRobotSendResponse) requestExecute(request);return response;}/*** SDK 请求*/public static TaobaoResponse requestExecute(TaobaoRequest request){TaobaoResponse response = null;try {response = RobotHelperUtil.client.execute(request);} catch (Exception e) {log.error("钉钉监控机器人发送消息失败, 异常捕获{}", e.getMessage());}return response;}/*** @param: accessToken token* @param: secret 加签密钥* @description: 获取 serverUrl* @return: java.lang.String* @author: niaonao*/private static String getServerUrl(String accessToken, String secret) {StringBuilder serverUrl = new StringBuilder(accessToken);// isSign 是否加签if (!isSign) {return serverUrl.toString();}try {Long timestamp = System.currentTimeMillis();String stringToSign = timestamp + "\n" + secret;Mac mac = Mac.getInstance("HmacSHA256");mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256"));byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));String sign = URLEncoder.encode(new String(Base64.encodeBase64(signData)),"UTF-8");serverUrl.append("×tamp=").append(timestamp).append("&sign=").append(sign);} catch (Exception e) {e.printStackTrace();}return serverUrl.toString();}
}
- 项目源码:
更多推荐
钉钉机器人SDK 封装预警消息发送工具
发布评论