开发企业微信群机器人,实现定时提醒
大家好,我是鱼皮,今天分享一个用程序解决生活工作问题的真实案例。
说来惭愧,事情是这样的,在我们公司,每天都要轮流安排一名员工(当然也包括我)去楼层中间一个很牛的饮水机那里接水。但由于大家每天都有自己的工作,经常出现忘记接水的情况,导致大家口渴难耐。
怎么解决这个问题呢?
我想到了几种方法:
1)每天大家轮流提醒。但是别说提醒别人了,自己都不记得什么时候轮到自己接水。
2)由一个员工负责提醒大家接水,必要时招募一个 “接水提醒员”。
3)在企业微信的日历功能给员工安排接水日程,就像下面这样:
但问题是我们的人数和天数不是完全对应的、反复安排日程也很麻烦。
你觉得上面哪种方案好呢?其实我觉得第二个方案是最好的 —— 招募一个 “接水提醒员”。
别笑,我认真的!
只不过这个 “接水提醒员” 何必是人?
没错,作为一名程序员,我们可以搞一个机器人,让它在企业微信群聊中每天提醒不同的员工去接水即可。
其实这个功能和员工排班打卡系统是很类似的,只不过更轻量一些。我也调研了很多排班系统,但是都要收费,索性自己开发一个好了。
在企业微信中接入机器人其实非常简单,因为企业微信官方就支持群聊机器人功能,所以这次的任务我就安排给了实习生,他很快就完成了,所以我相信大家应该也都能学会~
企微群聊机器人开发
学习开发第三方应用时,一定要先完整阅读官方文档,比如企业微信群机器人配置文档。
指路:https://developer.work.weixin.qq.com/document/path/99110
设计 SDK 结构
虽然我们的目标是做一个提醒接水机器人,但是企业微信群聊机器人其实是一个通用的功能,所以我们决定开发一个企微机器人 SDK,以后公司其他业务需要时都能够快速复用。(比如开发一个定时喝水提醒机器人)
设计好 SDK 是需要一定技巧的,之前给大家分享过:如何设计一个优秀的 SDK ,可以阅读参考。
在查阅企微机器人文档后,了解到企业微信机器人支持发送多种类型的消息,包括文本、 Markdown 、图片、图文、文件、语音和模块卡片等,文档中对每一种类型的请求参数和字段含义都做了详尽的解释。
吐槽一下,跟微信开发者文档比起来,企微机器人的文档写得清晰多了!
由于每种消息最终都是要转换成 JSON 格式作为 HTTP 请求的参数的,所以我们可以设计一个基础的消息类(Message)来存放公共参数,然后定义各种不同的具体消息类来集成它(比如文本消息 TextMessage、Markdown 消息 MarkdownMessage 等)。
为了简化开发者使用 SDK 来发送消息,定义统一的 MessageSender 类,在类中提供发送消息的方法(比如发送文本消息 sendText),可以接受 Message 并发送到企业微信服务器。
最终,客户端只需调用统一的消息发送方法即可。SDK 的整体结构如下图所示:
值得一提的是,如果要制作更通用的消息发送 SDK。可以将 MessageSender 定义成接口,编写不同的子类比如飞书 MessageSender、短信 MessageSender 等。
开发 SDK
做好设计之后,接下来就可以开始开发 SDK 了。
步骤如下:
-
获取 webhook -
创建 SDK 项目 -
编写代码 -
SDK 打包 -
调用 SDK
1、获取 webhook
首先,必须在企业微信群聊中创建一个企业微信机器人,并获取机器人的 webhook。
webhook 是一个 url 地址,用于接受我们开发者自己服务器的请求,从而控制企业微信机器人。后续所有的开发过程,都需要通过 webhook 才可以实现。
复制并保存好这个 Webhook 地址,注意不要泄露该地址!
2、创建 SDK 项目
SDK 通常是一个很干净的项目,此处我们使用 Maven 来构建一个空的项目,并在 pom.xml 文件中配置项目信息。
需要特别注意的是,既然我们正在创建一个 SDK,这意味着它将被更多的开发者使用。因此,在配置 groupId 和 artifactId 时,我们应当遵循以下规范:
-
groupId:它是项目组织或项目开发者的唯一标识符,其实际对应的是 main 目录下的 Java 目录结构。 -
artifactId:它是项目的唯一标识符,对应的是项目名称,即项目的根目录名称。通常,它应当为纯小写,并且多个词之间使用中划线(-)隔开。 -
version:它指定了项目的当前版本。其中,SNAPSHOT 表示该项目仍在开发中,是一个不稳定的版本。
以下是我们配置好的项目信息:
<groupId>com.yupi</groupId>
<artifactId>rtx-robot</artifactId>
<version>1.0-SNAPSHOT</version>
为了让我们的项目更加易用,我们还要能做到让开发者通过配置文件来传入配置(比如 webhook),而不是通过硬编码重复配置各种信息。
所以此处我们把项目只作为 Spring Boot 的 starter,需要在 pom.xml 文件中引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
最后,我们还需要添加一个配置,配置项 <skip>true</skip> 表示跳过执行该插件的默认行为:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
这样,一个 SDK 项目的初始依赖就配置好了。
3、编写配置类
现在我们就可以按照之前设计的结构开发了。
首先,我们要写一个配置类,用来接受开发者在配置文件中写入的 webhook。
同时,我们可以在配置类中,将需要被调用的 MessageSender 对象 Bean 自动注入到 IOC 容器中,不用让开发者自己 new 对象了。
示例代码如下:
@Configuration
@ConfigurationProperties(prefix = "wechatwork-bot")
@ComponentScan
@Data
public class WebhookConfig {
private String webhook;
@Bean
public RtxRobotMessageSender rtxRobotMessageSender() {
return new RtxRobotMessageSender(webhook);
}
}
接下来,为了让 Spring Boot 项目在启动时能自动识别并应用配置类,需要把配置类写入到 resources/META-INF/spring.factories 文件中,示例代码如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.yupi.rtxrobot.config.WebhookConfig
4、编写消息类
接下来,我们要按照官方文档的请求参数把几种类型的消息对象编写好。
由于每个消息类都有一个固定的字段 msgtype,所以我们定义一个基类 Message,方便后续将不同类型的消息传入统一的方法:
public class Message {
/**
* 消息类型
**/
String msgtype;
}
接下来编写具体的消息类,比如纯文本类型消息 TextMessage,示例代码如下:
@Data
public class TextMessage extends Message {
/**
* 消息内容
*/
private String content;
/**
* 被提及者userId列表
*/
private List<String> mentionedList;
/**
* 被提及者电话号码列表
*/
private List<String> mentionedMobileList;
/**
* 提及全体
*/
private Boolean mentionAll = false;
public TextMessage(String content, List<String> mentionedList, List<String> mentionedMobileList, Boolean mentionAll) {
this.content = content;
this.mentionedList = mentionedList;
this.mentionedMobileList = mentionedMobileList;
this.mentionAll = mentionAll;
if (mentionAll) {
if (CollUtil.isNotEmpty(this.mentionedList) || CollUtil.isNotEmpty(this.mentionedMobileList)) {
if (CollUtil.isNotEmpty(mentionedList)) {
this.mentionedList.add("@all");
} else {
this.mentionedList = CollUtil.newArrayList("@all");
}
} else {
this.mentionedList = CollUtil.newArrayList("@all");
}
}
}
public TextMessage(String content) {
this(content, null, null, false);
}
}
上面的代码中,有个代码优化小细节,官方文档是使用 “@all” 字符串来表示 @全体成员的,但 “@all” 是一个魔法值,为了简化调用,我们将其封装为 mentionAll 布尔类型字段,并且在构造函数中自动转换为实际请求需要的字段。
5、编写消息发送类
接下来,我们将编写一个消息发送类。在这个类中,定义了用于发送各种类型消息的方法,并且所有的方法都会依赖调用底层的 send 方法。send 方法的作用是通过向企微机器人的 webhook 地址发送请求,从而驱动企微机器人发送消息。
以下是示例代码,有很多编码细节:
/**
* 微信机器人消息发送器
* @author yuyuanweb
*/
@Slf4j
@Data
public class RtxRobotMessageSender {
private final String webhook;
public WebhookConfig webhookConfig;
public RtxRobotMessageSender(String webhook) {
this.webhook = webhook;
}
/**
* 支持自定义消息发送
*/
public void sendMessage(Message message) throws Exception {
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage) message;
send(textMessage);
} else if (message instanceof MarkdownMessage) {
MarkdownMessage markdownMessage = (MarkdownMessage) message;
send(markdownMessage);
} else {
throw new RuntimeException("Unsupported message type");
}
}
/**
* 发送文本(简化调用)
*/
public void sendText(String content) throws Exception {
sendText(content, null, null, false);
}
public void sendText(String content, List<String> mentionedList, List<String> mentionedMobileList) throws Exception {
TextMessage textMessage = new TextMessage(content, mentionedList, mentionedMobileList, false);
send(textMessage);
}
/**
* 发送消息的公共依赖底层代码
*/
private void send(Message message) throws Exception {
String webhook = this.webhook;
String messageJsonObject = JSONUtil.toJsonStr(message);
// 未传入配置,降级为从配置文件中寻找
if (StrUtil.isBlank(this.webhook)) {
try {
webhook = webhookConfig.getWebhook();
} catch (Exception e) {
log.error("没有找到配置项中的webhook,请检查:1.是否在application.yml中填写webhook 2.是否在spring环境下运行");
throw new RuntimeException(e);
}
}
OkHttpClient client = new OkHttpClient();
RequestBody body = RequestBody.create(
MediaType.get("application/json; charset=utf-8"),
messageJsonObject);
Request request = new Request.Builder()
.url(webhook)
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
if (response.isSuccessful()) {
log.info("消息发送成功");
} else {
log.error("消息发送失败,响应码:{}", response.code());
throw new Exception("消息发送失败,响应码:" + response.code());
}
} catch (IOException e) {
log.error("发送消息时发生错误:" + e);
throw new Exception("发送消息时发生错误", e);
}
}
}
代码部分就到这里,是不是也没有很复杂?
6、SDK 打包
接下来就可以对 SDK 进行打包,然后本地使用或者上传到远程仓库了。
SDK 的打包非常简单,通过 Maven 的 install 命令即可,SDK 的 jar 包就会被导入到你的本地仓库中。
在打包前建议先执行 clean 来清理垃圾文件。
7、调用 SDK
最后我们来调用自己写的 SDK,首先将你的 SDK 作为依赖引入到项目中,比如我们的接水提醒应用。
引入代码如下:
<dependency>
<groupId>com.yupi</groupId>
<artifactId>rtx-robot</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
然后将之前复制的 webhook 写入到 Spring Boot 的配置文件中:
wechatwork-bot:
webhook: 你的webhook地址
随后你就可以用依赖注入的方式得到一个消息发送者对象了:
@Resource
public RtxRobotMessageSender rtxRobotMessageSender;
当然你也可以选择在一个非 Spring 环境中手动创建对象,自己传入 webhook:
String webhook = "你的webhook地址";
RtxRobotMessageSender rtxRobotMessageSender = new RtxRobotMessageSender(webhook);
现在,就可以轻松实现我们之前提到的提醒接水工具了。
这里我们就用最简单的方式,定义一个员工数组,分别对应到每周 X,然后用定时任务每日执行消息发送。
示例代码如下:
@Component
public class WaterReminderTask {
@Resource
public RtxRobotMessageSender rtxRobotMessageSender;
private String[] names = {"员工a", "员工b", "员工c", "员工d", "员工e"};
@Scheduled(cron = "0 55 9 * * MON-FRI")
public void remindToGetWater() {
LocalDate today = LocalDate.now();
DayOfWeek dayOfWeek = today.getDayOfWeek();
String nameToRemind;
switch (dayOfWeek) {
case MONDAY:
nameToRemind = names[0];
break;
case TUESDAY:
nameToRemind = names[1];
break;
case WEDNESDAY:
nameToRemind = names[2];
break;
case THURSDAY:
nameToRemind = names[3];
break;
case FRIDAY:
nameToRemind = names[4];
break;
default:
return;
}
String message = "提醒:" + nameToRemind + ",是你接水的时间了!";
rtxRobotMessageSender.sendText(message);
}
}
好了,现在大家每天都有水喝了,真不错 👍🏻
最后
虽然开发企微机器人 SDK 并不难,但想做一个完善的、易用的 SDK 还是需要两把刷子的,而且沉淀 SDK 对自己未来做项目帮助会非常大。
希望本文对大家有帮助,学会的话 点个赞 或 在看 吧,谢谢大家~
相关文章:
开发企业微信群机器人,实现定时提醒
大家好,我是鱼皮,今天分享一个用程序解决生活工作问题的真实案例。 说来惭愧,事情是这样的,在我们公司,每天都要轮流安排一名员工(当然也包括我)去楼层中间一个很牛的饮水机那里接水。但由于大…...
剑指 Offer 06. 从尾到头打印链表
title: 剑指 Offer 06. 从尾到头打印链表 tags: 链表递归迭代 categories:算法剑指 Offer 题目描述 输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。 示例 1: 输入:head [1,3,2] 输出&#…...
深度学习之基于Pytorch服装图像分类识别系统
欢迎大家点赞、收藏、关注、评论啦 ,由于篇幅有限,只展示了部分核心代码。 文章目录 一项目简介系统组成1. 数据集准备2. 数据预处理3. 模型构建4. 模型训练5. 模型评估 PyTorch的优势 二、功能三、系统四. 总结 一项目简介 深度学习在计算机视觉领域的…...
串口通讯:
一、 1.在用ReadFile和WriteFile读写串口时,既可以同步执行,也可以重叠执行: 在同步执行时,函数直到操作完成后才返回。这意味着同步执行时线程会被阻塞,从而导致效率下降。 在重叠执行时,即使操作…...
批量重命名软件推荐 A Better Finder Rename 12最新 for mac
A Better Finder Rename的大量重命名选项被组织成15个直观的类别,涵盖了一个伟大的文件重命名器所期望的所有文本,字符,位置,转换和截断功能。 除此之外,A Better Finder Rename提供了更多高级功能,可以满…...
【2013年数据结构真题】
highlight: a11y-dark 41题 王道解析: 算法的策略是从前向后扫描数组元素,标记出一个可能成为主元素的元素Num 。然后重新计数,确认Num是否是主元素。算法可分为以下两步: 选取候选的主元素:依次扫描所给数组中的每个…...
csrf学习笔记总结
跨站请求伪造csrf csrf概述 掌握CSRF 漏洞原理 掌握CSRF 漏洞场景 掌握CSRF 漏洞验证 csrf原理 跨站请求伪造(Cross Site Request Forgery,CSRF)是一种攻击,它强制浏览器客户端用户在当前对其进行身份验证后的Web 应用程…...
【kafka】windows安装启动
1.zookeeper的安装与启动 快速打开window powershell: windowx,选 2.kafka下载 —注意kafka和zookeeper需要版本匹配 安装路径 注意,kafka安装目录不能有空格。文件下载到: D:\Program_Files\kafka_2.12-3.6.0新建logs文件 修改c…...
redis的基本命令,并用netty操作redis(不使用springboot或者spring框架)就单纯的用netty搞。
大家如果对使用netty搞这些http请求什么的感兴趣的,可以参观我自己创建的这个项目。 nanshaws/nettyWeb: 复习一下netty,并打算做一个web项目出来 (github.com) Redis的基本命令包括: SET key value:设置指定key的值。 GET key…...
《白帽子讲web安全》笔记
第八章 文件上传漏洞 文件上传漏洞是指用户上传了一个可执行的脚本文件,并通过此脚本文件获得了执行服务器端命令的能力 文件上传后导致的常见安全问题一般有: ❍ 上传文件是Web脚本语言,服务器的Web容器解释并执行了用户上传的脚本…...
unity UGUI无限循环滚动居中
最近在做一个ui循环滚动的功能,网上找了半天脚本感觉都和我实际需求不太符合,自己花费一些时间完成了这个功能记录一下。下面开始正题 ,我是采用unity自带组件Scroll View来完成,首先设置Scroll View如下图 面板层级结构如下 然…...
人工智能与新能源电动车的融合——技术创新引领未来交通革命
人工智能与新能源电动车的融合——技术创新引领未来交通革命 摘要:本文探讨了人工智能与新能源电动车领域的技术融合,分析了其在智能驾驶、电池技术、充电设施等方面的应用与创新。文章指出,这两大技术的结合将重塑交通产业,为我…...
交换机堆叠 配置(H3C)堆叠中一台故障如何替换
交换机堆叠 配置(H3C)堆叠中一台故障如何替换 堆叠用来干什么?配置两台成员设备的 IRF(堆叠)Switch01配置Switch02配置 如何替换堆叠中坏掉的一台交换机 堆叠用来干什么? 一台交换机网口有限,无…...
2024年软考有哪些考试科目?具体考什么内容?
软考分为三个考试层次,软考初级、中级和高级,每个层次的考试科目,其考试内容都是不一样的。报考时先选层次,再选科目。选好科目后,再看自己需要学习哪些内容。 一、软考初级科目 1.程序员: 考核内容&…...
2023.11.12 hive中分区表,分桶表与区别概念
1.分区表 分区表的本质就是在分目录 当Hive表对应的数据量大、文件多时,为了避免查询时全表扫描数据。比如把一整年的数据根据月份划分12个月(12个分区),后续就可以查询指定月份分区的数据,尽可能避免了全表扫描查询。…...
Pass-中间件管理
中间件管理是指对应用软件和操作系统之间的软件层进行管理和调度的过程,以优化应用性能和提高系统可靠性。 中间件管理是什么? 中间件管理是软件开发过程中不可或缺的一部分,它主要负责管理应用程序与操作系统之间的交互。中间件࿰…...
什么是GIL锁,有什么作用?python的垃圾回收机制是什么样的?解释为什么计算密集型用多进程,io密集型用多线程。
1 什么是gil锁,有什么作用? 2 python的垃圾回收机制是什么样的? 3 解释为什么计算密集型用多进程,io密集型用多线程。 1 什么是gil锁,有什么作用? 1 GIL:Global Interpreter Lock又称全局解释器…...
Postman如何发送Https请求
Postman如果想要发送Https请求,需要从设置中将SSL安全认证禁用...
Redis集群启动
配置项 # 允许Redis监听所有网络接口的IP地址,即0.0.0.0。这意味着Redis可以接受来自任何网络接口的连接。 bind 0.0.0.0 # 关闭保护模式。在保护模式下,Redis只接受来自本机的连接。关闭保护模式后,Redis可以接受来自任何网络接口的连接。 protected-mode no # 在后…...
使用proxy把后端返回的图片域名替换成目标域名
proxy 对象用于创建一个对象的代理,是在目标对象之前架设一个拦截,外界对该对象的访问,都必须先通过这个拦截。通过这种机制,就可以对外界的访问进行过滤和改写。 ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例。…...
C++中显示与隐式加载dll的使用与区别
一、什么是 DLL?DLL(Dynamic Link Library) 是 Windows 下的动态链接库,包含可被多个程序共享的函数、资源或类。使用 DLL 可以实现代码复用、模块化设计和插件机制。在 C 中,调用 DLL 中的函数有两种主要方式…...
2026在线测评系统十大量表对比:信效度与场景全解析
【30s 核心摘要】2026 年在线测评成人才管理刚需,信效度与场景适配成选型核心。本文聚焦十大量表,从信度、效度、适配场景等维度深度对比,重点解析问卷星、北森、金数据等主流平台的量表能力与落地效果,为企业、高校及机构提供科学…...
亚马逊卖家公开信息数据提取:反爬攻防战与 Python 批量采集实战
摘要: 批量获取亚马逊(Amazon)第三方卖家的商业名称、信用代码和注册地址等信息,对于跨境 B2B 拓客和供应链分析具有重要意义。然而,亚马逊的 Cloudflare 盾和 Robot 验证码构成了极高的反爬门槛。本文将深度解析亚马逊…...
基于LM22678的树莓派硬盘专用电源设计:解决供电不稳与电流冲击
1. 项目概述:为什么我们需要一个“专用”电源?如果你正在用树莓派搭配一块机械硬盘搭建一个家庭服务器或者个人云存储,可能已经遇到了一个不大不小的麻烦:供电不稳。树莓派官方推荐的5V/3A电源,单独带树莓派4B跑满负载…...
BLE四大广播模式详解:可连接/不可连接/定向/周期广播
一、前言在低功耗蓝牙(BLE)开发中,广播(Advertising)是设备发现、连接建立、数据广播、设备重连的核心基石,所有BLE交互流程均始于广播报文的收发。不同于传统经典蓝牙,BLE所有广播行为标准化、…...
2605.VGGT-Omega 论文解读: 3D重建的Scaling Law, Register Attention效率革命 | Oxford+Meta CVPR26 Oral
VGGT-Omega: Scaling Feed-Forward 3D Reconstruction Jianyuan Wang, Minghao Chen, Shangzhan Zhang, Nikita Karaev, Johannes Schonberger, et al. Visual Geometry Group, Oxford Meta AI | CVPR 2026 Oral | arXiv 2605.15195 Paper | Project Page 一句话总结 VGGT-Om…...
从入门到实践:EEG公开数据集分类与应用场景全解析
1. EEG公开数据集入门指南刚接触脑电信号分析的研究者,常常会被一个问题困扰:"我应该从哪里获取可靠的EEG数据?"作为一个在这个领域摸爬滚打多年的研究者,我完全理解这种困惑。记得我第一次接触EEG研究时,光…...
独立站内容分层:一层给 SEO,一层给 GEO
你的内容在喂两个完全不同的"阅读者" 你的博客文章,从来都不只有一个读者。 传统认知里,独立站内容的读者只有两类:真人访客和搜索引擎爬虫。SEO 优化的一切工作,本质上都是在讨好后者,顺带服务前者。 但…...
MongoDB Limit 与 Skip 方法详解
MongoDB Limit 与 Skip 方法详解 引言 MongoDB 是一个高性能、可伸缩的文档存储系统,它提供了强大的数据存储和查询功能。在处理大量数据时,Limit 与 Skip 方法是 MongoDB 中常用的查询优化工具。本文将详细介绍 MongoDB 中的 Limit 与 Skip 方法,包括其基本用法、性能影响…...
INT8量化下TVA注意力对齐精度保障方案
重磅预告:本专栏将独家连载系列丛书《智能体视觉技术与应用》部分精华内容,该书是世界首套系统阐述“因式智能体”视觉理论与实践的专著,特邀美国 TypeOne 公司首席科学家、斯坦福大学博士 Bohan 担任技术顾问。Bohan先生师从美国三院院士、“…...
