企业微信、飞书、钉钉机器人消息发送工具类
1、实例化WebClient对象
其实你也可以使用RestTemplate,我这里主要是用到了webflux框架,所以需要实例化客户端请求对象
@Bean
public WebClient webClient(){HttpClient httpClient = getHttpClient();return WebClient.builder().clientConnector(new ReactorClientHttpConnector(httpClient)).build();
}private HttpClient getHttpClient() {ConnectionProvider provider = ConnectionProvider.builder("你爱咋咋的,一般用你项目名即可").maxConnections(500).maxIdleTime(Duration.ofSeconds(10)).maxLifeTime(Duration.ofSeconds(20)).pendingAcquireTimeout(Duration.ofSeconds(30)).pendingAcquireTimer((r, d) -> {Timeout t = wheel.newTimeout(timeout -> r.run(), d.toMillis(), TimeUnit.MILLISECONDS);return () -> t.cancel();}).fifo().build();HttpClient httpClient = HttpClient.create(provider);return httpClient;
}
2、发送及有效性测试工具类
import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.tomcat.util.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Map;import static com.paratera.console.notice.utils.Constants.*;/*** 机器人发送工具类(微信,飞书,钉钉)** @author huxiang*/
@Component
@Slf4j
public class RobotUtil {@Autowiredprivate WebClient webClient;/*** 机器人发送消息(markdown格式)** @param robotUrl 机器人地址* @param type 类型:1微信,2飞书,3钉钉* @param context markdown文本内容* @param signSecret 签名校验(飞书,钉钉使用,可为空,具体要根据用户是否启用签名)*/public void sendMsg(String robotUrl, Integer type, String context, String signSecret) {JSONObject msgObj = JSONUtil.createObj();JSONObject mdObj = JSONUtil.createObj();switch (type) {case ROBOT_TYPE_WX:msgObj.set("msgtype", "markdown");mdObj.set("content", context);msgObj.set("markdown", mdObj);break;case ROBOT_TYPE_FS:if (StringUtils.isNotEmpty(signSecret)) {msgObj = getFsSignObj(signSecret);}msgObj.set("msg_type", "interactive");mdObj.set("tag", "lark_md");mdObj.set("content", context);JSONArray elements = JSONUtil.createArray();JSONObject wrapObj = JSONUtil.createObj();wrapObj.set("tag", "div");wrapObj.set("text", mdObj);elements.put(wrapObj);JSONObject cardObj = JSONUtil.createObj();cardObj.set("elements", elements);msgObj.set("card", cardObj);break;case ROBOT_TYPE_DD:if (StringUtils.isNotEmpty(signSecret)) {robotUrl = getDdRobotURL(robotUrl, signSecret);}msgObj.set("msgtype", "markdown");mdObj.set("title", "通知");mdObj.set("text", context);msgObj.set("markdown", mdObj);break;}webClient.post().uri(robotUrl).contentType(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(msgObj)).retrieve().bodyToMono(String.class).subscribe(result -> {log.info("机器人通知发送结果:" + result);});}/*** 机器人有效性测试 0表示成功,其他表示失败** @param robotUrl 机器人地址* @param type 类型:1微信,2飞书,3钉钉* @param signSecret 签名校验(飞书,钉钉使用,可为空,具体要根据用户是否启用签名)* @return*/public Integer sendTestMsg(String robotUrl, Integer type, String signSecret) {JSONObject msgObj = JSONUtil.createObj();JSONObject mdObj = JSONUtil.createObj();ObjectMapper mapper = new ObjectMapper();switch (type) {case ROBOT_TYPE_WX:msgObj.set("msgtype", "markdown");mdObj.set("content", "机器人有效性测试!");msgObj.set("markdown", mdObj);break;case ROBOT_TYPE_FS:if (StringUtils.isNotEmpty(signSecret)) {msgObj = getFsSignObj(signSecret);}msgObj.set("msg_type", "interactive");mdObj.set("tag", "lark_md");mdObj.set("content", "机器人有效性测试!");JSONArray elements = JSONUtil.createArray();JSONObject wrapObj = JSONUtil.createObj();wrapObj.set("tag", "div");wrapObj.set("text", mdObj);elements.put(wrapObj);JSONObject cardObj = JSONUtil.createObj();cardObj.set("elements", elements);msgObj.set("card", cardObj);break;case ROBOT_TYPE_DD:if (StringUtils.isNotEmpty(signSecret)) {robotUrl = getDdRobotURL(robotUrl, signSecret);}msgObj.set("msgtype", "markdown");mdObj.set("title", "通知");mdObj.set("text", "机器人有效性测试!");msgObj.set("markdown", mdObj);break;}Mono<String> mono = webClient.post().uri(robotUrl).contentType(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(msgObj)).retrieve().bodyToMono(String.class);String result = mono.block();try {Map res = mapper.readValue(result, Map.class);return (Integer) (res.containsKey("errcode") ? res.get("errcode") : res.get("code"));} catch (JsonProcessingException e) {log.error("类型转换异常-RobotUtil.sendTestMsg:" + e);}return -1;}/*** 飞书获取带签名的消息体** @return*/public JSONObject getFsSignObj(String signSecret) {JSONObject signObj = JSONUtil.createObj();Long timestamp = DateUtil.currentSeconds();String sign = null;try {//把timestamp+"\n"+密钥当做签名字符串String stringToSign = timestamp + "\n" + signSecret;//使用HmacSHA256算法计算签名Mac mac = Mac.getInstance("HmacSHA256");mac.init(new SecretKeySpec(stringToSign.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));byte[] signData = mac.doFinal(new byte[]{});sign = Base64.encodeBase64String(signData);} catch (Exception e) {log.error("飞书获取签名失败:" + e);}if (StringUtils.isEmpty(sign)) {return null;}signObj.set("timestamp", timestamp);signObj.set("sign", sign);return signObj;}/*** 钉钉获取带签名的机器人推送URL** @return*/public String getDdRobotURL(String robotUrl, String signSecret) {Long timestamp = System.currentTimeMillis();String stringToSign = timestamp + "\n" + signSecret;String sign = null;try {Mac mac = Mac.getInstance("HmacSHA256");mac.init(new SecretKeySpec(signSecret.getBytes("UTF-8"), "HmacSHA256"));byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));sign = URLEncoder.encode(Base64.encodeBase64String(signData), "UTF-8");} catch (Exception e) {log.error("钉钉获取签名失败:" + e);}if (StringUtils.isEmpty(sign)) {return null;}return robotUrl + "×tamp=" + timestamp + "&sign=" + sign;}
}相关文章:
企业微信、飞书、钉钉机器人消息发送工具类
1、实例化WebClient对象 其实你也可以使用RestTemplate,我这里主要是用到了webflux框架,所以需要实例化客户端请求对象 Bean public WebClient webClient(){HttpClient httpClient getHttpClient();return WebClient.builder().clientConnector(new R…...
手撕 视觉slam14讲 ch7 / pose_estimation_3d2d.cpp (1)
首先理清我们需要实现什么功能,怎么实现,提供一份整体逻辑:包括主函数和功能函数 主函数逻辑: 1. 读图,两张rgb(cv::imread) 2. 找到两张rgb图中的特征点匹配对 2.1定义所需要的参数:keypoints…...
Mac安装Dart时,Homebrew报错 Error: Failure while executing
前言: 最近准备开发Flutter项目时,在安装环境时,安装Homebew时遇到了以下报错信息,在这里分享一下。 报错信息: ~ % brew tap dart-lang/dart > Tapping dart-lang/dart Cloning into /opt/homebrew/Library/Tap…...
SSM整合~
构建并配置项目: 第一步:创建maven项目 第二步:配置pom.xml文件 设置打包方式: 为了方便部署,我们通常情况下,将项目打包为WAR,因为WAR文件是一种可执行的压缩文件,它可以将项目…...
Self-supervised 3D Human Pose Estimation from a Single Image
基于单幅图像的自监督三维人体姿态估计 主页: https://josesosajs.github.io/ imagepose/ 源码:未开源 摘要 我们提出了一种新的自我监督的方法预测三维人体姿势从一个单一的图像。预测网络是从描绘处于典型姿势的人的未标记图像的数据集和一组未配对…...
ubuntu下cups部分场景
第一章:部分操作指令 在计算机领域中,cups 是“通用UNIX打印系统”(Common UNIX Printing System)的缩写,它是一种用于在UNIX-like操作系统上管理打印任务的开源打印系统。cups 提供了一个框架,允许用户和…...
通过geoserver imageMosic发布多张tif数据
通过geoserver imageMosic发布多张tif数据 reference: https://zhuanlan.zhihu.com/p/132388558 https://zhuanlan.zhihu.com/p/103674876 https://docs.geoserver.org/latest/en/user/tutorials/imagemosaic_timeseries/imagemosaic_timeseries.html 步骤 下载数据 http…...
输出图元(四)8-2 OpenGL画点函数、OpenGL画线函数
4.3 OpenGL画点函数 要描述一个点的几何要素,我们只需在世界坐标系中指定一个位置。然后该坐标位置和场景中已有的其他几何描述一起被传递给观察子程序。除非指定其他属性值,OpenGL 图元按默认的大小和颜色来显示。默认的图元颜色是白色&#x…...
java八股文
6. 如何保证消息的可靠性? 在RabbitMq的整个消息投递过程中,有三种情况下,会存在消息丢失的问题: 6. RabbitMq如何保证消息的可靠性? 所以从这三个维度保证消息的可靠性去可靠性传递就可以了,从生产者发送…...
算法通关村——解析堆的应用
在数组中找第K大的元素 LeetCode21 Medium 我们要找第 K 大的元素,如果我们找使用大堆的话那么就会造成这个堆到底需要多大的,而且哪一个是第 K 的的元素我们不知道是哪一个索引,我们更想要的结果就是根节点就是我们要找的值,所以…...
爬虫源码---爬取小猫猫交易网站
前言: 本片文章主要对爬虫爬取网页数据来进行一个简单的解答,对与其中的数据来进行一个爬取。 一:环境配置 Python版本:3.7.3 IDE:PyCharm 所需库:requests ,parsel 二:网站页面 我们需要…...
Python的由来和基础语法(一)
目录 一、Python 背景知识 1.1Python 是咋来的? 1.2Python 都能干啥? 1.3Python 的优缺点 二、基础语法 2.1常量和表达式 2.2变量和类型 变量的语法 (1) 定义变量 (2) 使用变量 变量的类型 (1) 整数 (2) 浮点数(小数) (3) 字符串 (4) 布尔 (5) 其他 动态类型…...
使用maven创建springboot项目
创建maven快速启动项目 命令行或者idea、eclipse快捷创建也可以 pom.xml下project项目下导入springboot 父工程 <!--导入springboot 父工程--> <parent><artifactId>spring-boot-starter-parent</artifactId><groupId>org.springframework.bo…...
MySQL 基本操作1
目录 Create insert 插入跟新 1 插入跟新 2 Retrive select where 子句查询 1.查找数学成绩小于 80 的同学。 2.查询数学成绩等于90分的同学。 3.查询总分大于240 的学生 4.查询空值或者非空值 5.查询语文成绩在70~80之间的同学 6.查询英语成绩是99 和 93 和 19 和…...
linux内网yum源服务器搭建
1.nginx: location / {root /usr/local/Kylin-Server-V10-SP3-General-Release-2303-X86_64;autoindex on;autoindex_localtime on;autoindex_exact_size off; } 注:指定到镜像的包名 2.修改yum源地址 cd /etc/yum.repos.d/vim kylin_x86_64.repo 注: --enabled设置为1 3.重…...
机器学习与数据分析
【数据清洗】 异常检测 孤立森林(Isolation Forest)从原理到实践 效果评估:F-score 【1】 保护隐私的时间序列异常检测架构 概率后缀树 PST – (异常检测) 【1】 UEBA架构设计之路5: 概率后缀树模型 【…...
项目总结知识点记录-文件上传下载(三)
(1)文件上传 代码: RequestMapping(value "doUpload", method RequestMethod.POST)public String doUpload(ModelAttribute BookHelper bookHelper, Model model, HttpSession session) throws IllegalStateException, IOExcepti…...
基于LinuxC语言实现的TCP多线程/进程服务器
多进程并发服务器 设计流程 框架一(使用信号回收僵尸进程) void handler(int sig) {while(waitpid(-1, NULL, WNOHANG) > 0); }int main() {//回收僵尸进程siganl(17, handler);//创建服务器监听套接字 serverserver socket();//给服务器地址信息…...
浅谈JVM垃圾回收机制
一、HotSpot VM中的GC分为两大类 1.部分收集(Partial GC): 新生代收集(Minor GC/Young GC):只对新生代进行垃圾收集老年代收集(Major GC/Old GC):只队老年代进行垃圾收集混合收集(Mixed GC):对整个新生代和老年代进行垃圾收集 2.整堆收集(Full GC) 收集整个Java堆和方法区 …...
【80天学习完《深入理解计算机系统》】第十二天3.6数组和结构体
专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客,如有问题交流,欢迎评论区留言,一定尽快回复!(大家可以去看我的专栏,是所有文章的目录) 文章字体风格: 红色文字表示&#…...
通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
python报错No module named ‘tensorflow.keras‘
是由于不同版本的tensorflow下的keras所在的路径不同,结合所安装的tensorflow的目录结构修改from语句即可。 原语句: from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后: from tensorflow.python.keras.lay…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...
安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)
船舶制造装配管理现状:装配工作依赖人工经验,装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书,但在实际执行中,工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...
【JavaSE】多线程基础学习笔记
多线程基础 -线程相关概念 程序(Program) 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存…...
