SpringBoot 实现自定义钉钉机器人

简介: SpringBoot 实现自定义钉钉机器人

背景


我们在系统使用过程中,常常会有报错日志,以及商品购物等信息,那么我们如何做到信息实时通知,让使用者方便直观查看,让程序员快速定位呢?接下来介绍下钉钉机器人



开发环境


开发语言:java 开发工具:idea


钉钉自定义机器人文档


创建钉钉群聊+添加自定义机器人


记住 Webhook 和 加签的key


选择自定义机器人

记住这两个参数


开发阶段


将下载的sdk 生成到maven

通过idea工具执行下面命令:

mvn install:install-file -Dfile=D:\download\dingtalk-sdk-java\sdk.jar -DgroupId=com.dingtalk -DartifactId=com-dingtalk-api
-Dversion=1.0.0 -Dpackaging=jar

-Dfile=D:\download\dingtalk-sdk-java\sdk.jar  这个指你下载sdk文件的路径


pom.xml 依赖:

<dependency>
      <groupId>com.aliyun</groupId>
      <artifactId>dingtalk</artifactId>
      <version>1.3.23</version>
</dependency>
<dependency>
      <groupId>com.aliyun</groupId>
      <artifactId>alibaba-dingtalk-service-sdk</artifactId>
      <version>2.0.0</version>
</dependency>

自定义机器人安全设置


目前有3种安全设置方式,请根据需要选择一种。

自定义关键词

最多可以设置10个关键词,消息中至少包含其中1个关键词才可以发送成功。

例如添加了一个自定义关键词:监控报警,则这个机器人所发送的消息,必须包含监控报警这个词,才能发送成功。


加签

  1. timestamp+"\n"+密钥当做签名字符串,使用HmacSHA256算法计算签名,然后进行Base64 encode,最后再把签名参数再进行urlEncode,得到最终的签名(需要使用UTF-8字符集)。

参数

说明

timestamp

当前时间戳,单位是毫秒,与请求调用时间误差不能超过1小时。

secret

密钥,机器人安全设置页面,加签一栏下面显示的SEC开头的字符串。

1.签名计算示例代码(Java)

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import java.net.URLEncoder;
 
public class Test{
    public static void main(String[] args) throws Exception{
        Long timestamp = System.currentTimeMillis();
        String secret = "this is secret";
 
        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");
        System.out.println(sign);
    }
 
}

把 timestamp和第一步得到的签名值拼接到URL中。

https://oapi.dingtalk.com/robot/send?access_token=XXXXXX&timestamp=XXX&sign=XXX

参数

说明

timestamp

第一步使用到的时间戳。

sign

第一步得到的签名值。


实现代码:


 
import com.aliyun.credentials.utils.StringUtils;
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.ApiException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
 
@Slf4j
public class DingTalkHelper {
    /**
     * 钉钉群设置 webhook, 支持重置
     */
    private static final String ACCESS_TOKEN = "https://oapi.dingtalk.com/robot/send?access_token=d2151db0cfd7db492b83cd3c49b7b9e36xxxxxxxxxxxxxxxxx";
    /**
     * 消息类型
     */
    private static final String MSG_TYPE_TEXT = "text";
    private static final String MSG_TYPE_LINK = "link";
    private static final String MSG_TYPE_MARKDOWN = "markdown";
    private static final String MSG_TYPE_ACTION_CARD = "actionCard";
    private static final String MSG_TYPE_FEED_CARD = "feedCard";
 
    /**
     * 客户端实例
     */
    public static DingTalkClient client;
 
    static {
        try {
            client = new DefaultDingTalkClient(ACCESS_TOKEN + sign());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        }
    }
 
 
    /**
     * @description: 官方演示示例
     * title 是消息列表下透出的标题
     * text 是进入群后看到的消息内容
     * @author: niaonao
     * @date: 2019/7/6
     */
    public static void sdkDemoJava() {
        DingTalkClient client = DingTalkHelper.client;
        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("15556465933"));
        request.setAt(at);
 
        request.setMsgtype("link");
        OapiRobotSendRequest.Link link = new OapiRobotSendRequest.Link();
        link.setMessageUrl("https://www.dingtalk.com/");
        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](https://gw.alipayobjects.com/zos/skylark-tools/public/files/84111bbeba74743d2771ed4f062d1f25.png)\n" +
                "> ###### 10点20分发布 [天气](http://www.thinkpage.cn/) \n");
        request.setMarkdown(markdown);
        try {
            client.execute(request);
        } catch (ApiException e) {
            log.error("[ApiException]: 消息发送演示示例, 异常捕获{}", e.getMessage());
        }
    }
 
    /**
     * @param content    文本消息
     * @param mobileList 指定@ 联系人
     * @param isAtAll    是否@ 全部联系人
     * @description: 发送普通文本消息
     * @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;
        }
 
        //参数  参数类型  必须  说明
        //msgtype String  是 消息类型,此时固定为:text
        //content String  是 消息内容
        //atMobiles Array 否 被@人的手机号(在content里添加@人的手机号)
        //isAtAll bool  否 @所有人时:true,否则为:false
        OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text();
        text.setContent(content);
        OapiRobotSendRequest request = new OapiRobotSendRequest();
        if (!CollectionUtils.isEmpty(mobileList)) {
            // 发送消息并@ 以下手机号联系人
            OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
            at.setAtMobiles(mobileList);
            at.setIsAtAll(isAtAll);
            request.setAt(at);
        }
        request.setMsgtype(DingTalkHelper.MSG_TYPE_TEXT);
        request.setText(text);
 
        OapiRobotSendResponse response = new OapiRobotSendResponse();
        try {
            response = DingTalkHelper.client.execute(request);
        } catch (ApiException e) {
            log.error("[发送普通文本消息]: 发送消息失败, 异常捕获{}", e.getMessage());
        }
        return response;
    }
 
    /**
     * @param title      消息标题
     * @param text       消息内容
     * @param messageUrl 点击消息后跳转的url
     * @param picUrl     插入图片的url
     * @description: 发送link 类型消息
     * @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 (StringUtils.isEmpty(title) || StringUtils.isEmpty(text) || StringUtils.isEmpty(messageUrl)) {
            return null;
        }
        //参数  参数类型  必须  说明
        //msgtype String  是 消息类型,此时固定为:link
        //title String  是 消息标题
        //text  String  是 消息内容。如果太长只会部分展示
        //messageUrl  String  是 点击消息跳转的URL
        //picUrl  String  否 图片URL
        OapiRobotSendRequest.Link link = new OapiRobotSendRequest.Link();
        link.setTitle(title);
        link.setText(text);
        link.setMessageUrl(messageUrl);
        link.setPicUrl(picUrl);
 
        OapiRobotSendRequest request = new OapiRobotSendRequest();
        request.setMsgtype(DingTalkHelper.MSG_TYPE_LINK);
        request.setLink(link);
 
        OapiRobotSendResponse response = new OapiRobotSendResponse();
        try {
            response = DingTalkHelper.client.execute(request);
        } catch (ApiException e) {
            log.error("[发送link 类型消息]: 发送消息失败, 异常捕获{}", e.getMessage());
        }
        return response;
    }
 
 
    /**
     * @param title        标题
     * @param markdownText 支持markdown 编辑格式的文本信息
     * @param mobileList   消息@ 联系人
     * @param isAtAll      是否@ 全部
     * @description: 发送Markdown 编辑格式的消息
     * @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 (StringUtils.isEmpty(title) || StringUtils.isEmpty(markdownText)) {
            return null;
        }
        //参数  类型  必选  说明
        //msgtype String  是 此消息类型为固定markdown
        //title String  是 首屏会话透出的展示内容
        //text  String  是 markdown格式的消息
        //atMobiles Array 否 被@人的手机号(在text内容里要有@手机号)
        //isAtAll bool  否 @所有人时:true,否则为:false
        OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();
        markdown.setTitle(title);
        markdown.setText(markdownText);
 
        OapiRobotSendRequest request = new OapiRobotSendRequest();
        request.setMsgtype(DingTalkHelper.MSG_TYPE_MARKDOWN);
        request.setMarkdown(markdown);
        if (!CollectionUtils.isEmpty(mobileList)) {
            OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
            at.setIsAtAll(isAtAll);
            at.setAtMobiles(mobileList);
            request.setAt(at);
        }
 
        OapiRobotSendResponse response = new OapiRobotSendResponse();
        try {
            response = DingTalkHelper.client.execute(request);
        } catch (ApiException e) {
            log.error("[发送link 类型消息]: 发送消息失败, 异常捕获{}", e.getMessage());
        }
        return response;
    }
 
    /**
     * @param title          消息标题, 会话消息会展示标题
     * @param markdownText   markdown格式的消息
     * @param singleTitle    单个按钮的标题
     * @param singleURL      单个按钮的跳转链接
     * @param btnOrientation 是否横向排列(true 横向排列, false 纵向排列)
     * @param hideAvatar     是否隐藏发消息者头像(true 隐藏头像, false 不隐藏)
     * @description: 整体跳转ActionCard类型的消息发送
     * @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, boolean hideAvatar) {
        if (StringUtils.isEmpty(title) || StringUtils.isEmpty(markdownText)) {
            return null;
        }
        //参数  类型  必选  说明
        //    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-按钮横向排列
        //    hideAvatar  string  false 0-正常发消息者头像,1-隐藏发消息者头像
        OapiRobotSendRequest.Actioncard actionCard = new OapiRobotSendRequest.Actioncard();
        actionCard.setTitle(title);
        actionCard.setText(markdownText);
        actionCard.setSingleTitle(singleTitle);
        actionCard.setSingleURL(singleURL);
        // 此处默认为0
        actionCard.setBtnOrientation(btnOrientation ? "1" : "0");
        // 此处默认为0
        actionCard.setHideAvatar(hideAvatar ? "1" : "0");
 
        OapiRobotSendRequest request = new OapiRobotSendRequest();
        request.setMsgtype(DingTalkHelper.MSG_TYPE_ACTION_CARD);
        request.setActionCard(actionCard);
        OapiRobotSendResponse response = new OapiRobotSendResponse();
        try {
            response = DingTalkHelper.client.execute(request);
        } catch (ApiException e) {
            log.error("[发送ActionCard 类型消息]: 整体跳转ActionCard类型的发送消息失败, 异常捕获{}", e.getMessage());
        }
        return response;
    }
 
    /**
     * @param title          标题
     * @param markdownText   文本
     * @param btns           按钮列表
     * @param btnOrientation 是否横向排列(true 横向排列, false 纵向排列)
     * @param hideAvatar     是否隐藏发消息者头像(true 隐藏头像, false 不隐藏)
     * @description: 独立跳转ActionCard类型 消息发送
     * @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, boolean hideAvatar) {
        if (StringUtils.isEmpty(title) || StringUtils.isEmpty(markdownText) || CollectionUtils.isEmpty(btns)) {
            return null;
        }
        //参数  类型  必选  说明
        //msgtype string  true  此消息类型为固定actionCard
        //title string  true  首屏会话透出的展示内容
        //text  string  true  markdown格式的消息
        //btns  array true  按钮的信息:title-按钮方案,actionURL-点击按钮触发的URL
        //btnOrientation  string  false 0-按钮竖直排列,1-按钮横向排列
        //hideAvatar  string  false 0-正常发消息者头像,1-隐藏发消息者头像
        OapiRobotSendRequest.Actioncard actionCard = new OapiRobotSendRequest.Actioncard();
        actionCard.setTitle(title);
        actionCard.setText(markdownText);
        // 此处默认为0
        actionCard.setBtnOrientation(btnOrientation ? "1" : "0");
        // 此处默认为0
        actionCard.setHideAvatar(hideAvatar ? "1" : "0");
 
        actionCard.setBtns(btns);
 
        OapiRobotSendRequest request = new OapiRobotSendRequest();
        request.setMsgtype(DingTalkHelper.MSG_TYPE_ACTION_CARD);
        request.setActionCard(actionCard);
        OapiRobotSendResponse response = new OapiRobotSendResponse();
        try {
            response = DingTalkHelper.client.execute(request);
        } catch (ApiException e) {
            log.error("[发送ActionCard 类型消息]: 独立跳转ActionCard类型发送消息失败, 异常捕获{}", e.getMessage());
        }
        return response;
    }
 
    /**
     * @param links
     * @description: 发送FeedCard类型消息
     * @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;
        }
 
        //msgtype string  true  此消息类型为固定feedCard
        //title string  true  单条信息文本
        //messageURL  string  true  点击单条信息到跳转链接
        //picURL  string  true  单条信息后面图片的URL
        OapiRobotSendRequest.Feedcard feedcard = new OapiRobotSendRequest.Feedcard();
        feedcard.setLinks(links);
        OapiRobotSendRequest request = new OapiRobotSendRequest();
        request.setMsgtype(DingTalkHelper.MSG_TYPE_FEED_CARD);
        request.setFeedCard(feedcard);
        OapiRobotSendResponse response = new OapiRobotSendResponse();
        try {
            response = DingTalkHelper.client.execute(request);
        } catch (ApiException e) {
            log.error("[发送ActionCard 类型消息]: 独立跳转ActionCard类型发送消息失败, 异常捕获{}", e.getMessage());
        }
        return response;
    }
 
    public static void main(String args[]) {
        //sdkDemoJava();
        List<String> list = new ArrayList<>();
        list.add("15556599999");
        sendMessageByText("代码测试", list, true);
    }
 
    public static String sign() throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException {
        Long timestamp = System.currentTimeMillis();
        String secret = "SEC1ef8d0e6b7d4b6d5065bb25c9f40d06006xxxxxxxxxxxxxxxxx";
        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");
        return "&timestamp=" + timestamp + "&sign=" + sign;
    }
}
 

运行


 public static void main(String args[]) {
        //sdkDemoJava();
        List<String> list = new ArrayList<>();
        list.add("15556565933");
        sendMessageByText("代码测试", list, true);
    }

相关文章
|
6月前
|
安全 机器人 API
简单几步,钉钉机器人秒变通义千问对话机器人
通过阿里云计算巢AppFlow平台,无需编码,只需简单几步,即可将钉钉机器人转化为通义千问对话机器人。首先在灵积模型服务平台获取API Key,然后在AppFlow中配置连接器,授权并保存Webhook Url。在钉钉中创建自定义机器人,选择Outgoing功能,填写签名和Webhook地址。最后,@机器人即可开始对话。此外,还提供了通过钉钉开放平台创建机器人的步骤。AppFlow简化了集成过程,加速了企业自动化服务流程。
|
3月前
|
存储 机器人 API
如何使用渐变块创建自定义聊天机器人
本文是一篇使用Gradio库的Blocks API创建自定义聊天机器人界面的教程,涵盖了从基础聊天机器人到支持流式响应、用户反馈(喜欢/不喜欢)以及Markdown、图像、音频和视频等多媒体内容的高级功能实现方法。
如何使用渐变块创建自定义聊天机器人
|
5月前
|
数据管理 机器人 BI
数据管理DMS产品使用合集之如何让报表自动更新推送到钉钉机器人
阿里云数据管理DMS提供了全面的数据管理、数据库运维、数据安全、数据迁移与同步等功能,助力企业高效、安全地进行数据库管理和运维工作。以下是DMS产品使用合集的详细介绍。
81 3
|
5月前
|
运维 机器人 开发者
使用阿里云百炼通过appflow模板,组合钉钉机器人搭建个人知识库评测与感想
尝试构建个人助手机制,用阿里云百炼+AppFlow+钉钉机器人,花费两午休时间解决配置问题。百炼appid复制时多出空格致错,文档未提及,耗时排查。应用创建时模型选项限于max, plus, turbo,性价比高的qwen-long未上线。期望尽快修复bug和上线新模型以降低成本。附故障排查截图。
184 1
|
6月前
|
存储 数据建模 索引
来自钉钉群的问题——Elasticsearch 如何实现文件名自定义排序?
来自钉钉群的问题——Elasticsearch 如何实现文件名自定义排序?
55 0
|
6月前
|
Serverless Go API
Serverless 应用引擎产品使用之在阿里云Serverless中,我想在钉钉机器人中使用函数计算的签名认证如何解决
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
|
1月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,包括版本兼容性、安全性、性能调优等方面。
147 1
|
20天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。首先,创建并配置 Spring Boot 项目,实现后端 API;然后,使用 Ant Design Pro Vue 创建前端项目,配置动态路由和菜单。通过具体案例,展示了如何快速搭建高效、易维护的项目框架。
95 62
|
18天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,帮助开发者提高开发效率和应用的可维护性。
36 2