开发企业微信群机器人,实现定时提醒

大家好,我是鱼皮,今天分享一个用程序解决生活工作问题的真实案例。

说来惭愧,事情是这样的,在我们公司,每天都要轮流安排一名员工(当然也包括我)去楼层中间一个很牛的饮水机那里接水。但由于大家每天都有自己的工作,经常出现忘记接水的情况,导致大家口渴难耐。

怎么解决这个问题呢?

我想到了几种方法:

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 了。

步骤如下:

  1. 获取 webhook
  2. 创建 SDK 项目
  3. 编写代码
  4. SDK 打包
  5. 调用 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, nullnullfalse);
    }
}

上面的代码中,有个代码优化小细节,官方文档是使用 “@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, nullnullfalse);
    }
  
    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 对自己未来做项目帮助会非常大。

希望本文对大家有帮助,学会的话 点个赞在看 吧,谢谢大家~

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深耕智慧物流夯实竞争优势,极智嘉(Geek+)斩获多项大奖

全球仓储机器人引领者极智嘉(Geek)&#xff0c;一直以来致力于为仓储与制造场景提供高效柔性的智能物流解决方案&#xff0c;强劲的发展实力备受认可。而近段时间&#xff0c;极智嘉更是频频传来好消息&#xff0c;不仅斩获了英国机器人与自动化仓储创新大奖&#xff0c;获评胡…

RT-DETR算法优化改进:一种新颖的动态稀疏注意力(BiLevelRoutingAttention) | CVPR2023

💡💡💡本文独家改进: 提出了一种新颖的动态稀疏注意力(BiLevelRoutingAttention),以实现更灵活的计算分配和内容感知,使其具备动态的查询感知稀疏性 1)代替RepC3进行使用; 2)BiLevelRoutingAttention直接作为注意力进行使用; 推荐指数:五星 RT-DETR魔术师专栏介…

睿登RD6012P数控可调电源简单开箱评测

睿登RD6012P恒压恒流数控直流可调电源简单开箱评测&#xff0c;测试了一下 空载功耗和效率测试、恒流输出精度和电流回读精度测试、恒压输出精度和电压回读精度测试、输出纹波&#xff0c;还有简单分析了一下用到的芯片&#xff08;背面可看到的芯片&#xff0c;没有拆开看里面…

Flutter实践二:repository模式

1.repository 几乎所有的APP&#xff0c;从简单的到最复杂的&#xff0c;在它们的架构里几乎都包括状态管理和数据源这两部分。状态管理常见的有Bloc、Cubit、Provider、ViewModel等&#xff0c;数据源则是一些直接和数据库或者网络客户端进行交互&#xff0c;取得相应的数据&…

Spring的Redis客户端

如何在Spring中操作redis 在创建springboot项目的时候引入redis的依赖. 在配置文件里指定redis主机的地址和端口,此处我们配置了ssh隧道,所以连接的就是本机的8888端口. 创建一个controller类,注入操作redis的对象. 前面使用jedis,是通过jedis对象里的各种方法来操作redis的,此…

python用pychart库,实现将经纬度信息在地图上显示

python使用pyecharts对给到的经纬度数据进行位置标注&#xff0c;下面是批量更新。给入数据&#xff0c;将地图生成。实验数据在下面附件。 from pyecharts import options as opts from pyecharts.charts import Geo import osfolder_path F:\\GPS file_names os.listdir(f…

腾讯小程序音视频 TRTC live-pusher 黑屏等各种问题

微信小程序进行音视频开发, 主要会用到live-player live-pusher,这两个媒体组件. 在开发的过程中,会遇到各种各样的问题,其中最直接的就是黑屏问题, 以下就这个问题进行整理. 文档: https://developers.weixin.qq.com/miniprogram/dev/component/live-player.html https://dev…

【Python】jupyter notebook(学习笔记)

Jupyter Notebook初见 1、Jupyter Notebook介绍 web版的ipython 编程、写文档、记笔记、展示 格式.ipynb 2、为什么使用Jupyter Notebook? 画图方面的优势&#xff1a;图像的生成不会堵塞后面代码的执行数据展示方面的优势&#xff1a;生成的数据可以保存在文件中 3、J…

Springboot细节补充

一、Bean是怎么装配的&#xff1f; 1、bean扫描 在之前的ssm中&#xff0c;spring要么用标签的形式来扫描包&#xff0c;要么使用注解ComponentScan来扫描 但是在Springboot中&#xff0c;启动类上默认有一个注解SpringBootApplication&#xff0c;里面就包含了ComponentScan…

CCF ChinaSoft 2023 论坛巡礼 | 云原生与智能化运维论坛

2023年CCF中国软件大会&#xff08;CCF ChinaSoft 2023&#xff09;由CCF主办&#xff0c;CCF系统软件专委会、形式化方法专委会、软件工程专委会以及复旦大学联合承办&#xff0c;将于2023年12月1-3日在上海国际会议中心举行。 本次大会主题是“智能化软件创新推动数字经济与社…

[原创]仅需小小的改变,B++ Builder 12的代码完成提示即可完美工作.

[简介] 常用网名: 猪头三 出生日期: 1981.XX.XXQQ: 643439947 个人网站: 80x86汇编小站 编程生涯: 2001年~至今[共22年] 职业生涯: 20年 开发语言: C/C、80x86ASM、PHP、Perl、Objective-C、Object Pascal、C#、Python 开发工具: Visual Studio、Delphi、XCode、Eclipse、C Bui…

隔离在高可用架构中的使用

写作目的 最近看到了河北王校长隔离的视频&#xff0c;结合自己在工作中的应用&#xff0c;分享常见的隔离落地方案。 隔离落地方案 服务环境隔离 因为我们的项目服务于整个国内的多条产品线&#xff0c;也服务于国外。为了低成本所以使用一套代码。在产品线之间隔离&#…

14.求n!和1!+2!+...+20!和2^1+2^2+……++2^20和2^1+2^3+……++2^19

文章目录 前言一、题目描述 二、题目分析 三、解题 程序运行代码 四、举一反三一、题目描述&#xff1a;求1&#xff01;2&#xff01;...20! 二、题目分析 三、解题 程序运行代码1程序运行代码2 一、题目描述&#xff1a;求求2^1^2^2^……2^20^二、解题 程序运行代码 一、题目…

大数据毕业设计选题推荐-消防监控平台-Hadoop-Spark-Hive

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

通过百度翻译API完成Java中的中英文翻译

因为要做英文文献索引分词&#xff0c;所以对于索引词汇必须得是英文&#xff0c;将表中的中文都转换成英文 这里用到百度的翻译API 首先需要注册成为百度翻译开发者&#xff1a;百度翻译开放平台 注册成为个人开发者就可以&#xff0c; 这里可以完善相关信息&#xff0c;要记…

每日互动(个推)全新推出AITA智选人群工具,助力品牌营销升级

11月9日&#xff0c;在2023年世界互联网大会“新产品新技术特色场景发布活动”上&#xff0c;数据智能服务商每日互动&#xff08;个推&#xff09;全新打造的AITA智选人群工具首次正式对外发布。作为每日互动在品牌营销领域的大模型应用最新成果&#xff0c;AITA智选人群工具将…

《数据结构、算法与应用C++语言描述》-代码实现键值有序链表跳表

跳表 定义 在一个用有序链表描述的 n 个数对的字典中进行查找&#xff0c;至多需要 n 次关键字比较。如果在链表的中部节点加一个指针&#xff0c;则比较次数可以减少到 n/21。这时&#xff0c;为了查找一个数对&#xff0c;首先与中间的数对比较。如果查找的数对关键字比较小…

【数据仓库】数仓分层方法

文章目录 一. 数仓分层的意义1. 清晰数据结构。2. 减少重复开发3. 方便数据血缘追踪4. 把复杂问题简单化5. 屏蔽原始数据的异常6. 数据仓库的可维护性 二. 如何进行数仓分层&#xff1f;1. ODS层2. DW层2.1. DW层分类2.2. DWD层2.3. DWS 3. ADS层 4、层次调用规范 一. 数仓分层…

全域全自主建设,亚信科技AntDB数据库助力广电5G业务上线运行

自2019年6月&#xff0c;中国广电成功获得5G牌照以来&#xff0c;迅速推进网络建设目标&#xff0c;成为5G网络覆盖广、应用场景多、用户体验出色的第四大运营商。其依托全球独有的700MHz频谱资源&#xff0c;具备覆盖能力强、容量足、速率高的优势。通过不断深化和中国移动的共…

【JavaEE】Servlet(创建Maven、引入依赖、创建目录、编写及打包、部署和验证、smart Tomcat)

一、什么是Servlet&#xff1f; Servlet 是一种实现动态页面的技术. 是一组 Tomcat 提供给程序猿的 API, 帮助程序猿简单高效的开发一个 web app 1.1 Servlet能干什么&#xff1f; &#x1f695;允许程序猿注册一个类, 在 Tomcat 收到某个特定的 HTTP 请求的时候, 执行这个类…