zoukankan      html  css  js  c++  java
  • 拉仇恨!webhook + 企业微信给同事做了个代码提交监听工具

    本文案例收录在 https://github.com/chengxy-nds/Springboot-Notebook

    大家好,我是小富~

    最近接个任务,用webhook做了个代码提交监听功能,就是有人向远程仓库提交代码后,会在企业微信群内发送一条消息,类似 @XXX 在XXX时间,向XXX项目提交 XXXX 代码 这样的文案。

    至于为啥要做这么个工具,没办法官大一级压死人,其实我内心是拒绝的,总像是被监视一样感觉怪怪的。难不成是发现了我平时偷偷提代码,悄无声息的修Bug?

    webhook

    webhook也就是我们经常说的钩子,如果对钩子不熟悉,没关系那我们换一个概念,回调URL应该听说过吧,例如:微信支付这类的三方平台都支持配置回调URL,通知支付状态。

    当一些事件触发,例如:"push代码到远程仓库",或者"提一个issue"等,源网站可以发起一个HTTP请求到webhook配置的URL。

    下图是这个工具的工作流程,开发者向GitHub项目提交代码,会触发GitHub的pull event,紧接着向GitHub webhook中配置的三方URL发送一个POST请求,这个三方平台可以是钉钉、飞书、企业微信这类平台。

    下面我们以 GitHub + 企业微信 来实现代码提交监听,自动向企业微信群组推送消息。

    配置GitHub webhook

    首先进入GitHub对应项目的 Settings,做webhook的基础配置。

    主要配置四部分:

    Payload URL 回调服务的地址;

    Content type 回调请求头,建议JSON格式;

    Secret 为了做安全校验,设置后会在请求 header 中增加如下两个属性,用来区分请求的来源,避免暴露的请求被恶意访问;

    X-Hub-Signature: sha1=2478e400758f6114aa18abc4380ef8fad0b16fb9
    X-Hub-Signature-256: sha256=68bde5bee18bc36fd95c9b71b4a89f238cb01ab3bf92fd67de3a1de12b4f5c72
    

    最后我们选择由哪些事件来触发webhook回调,push event(代码推送事件)、everything(所有事件)、某些特定事件三种。

    我们可以在 Recent Deliveries 查看webhook回调记录,以及完整的请求和参数数据,还可以redelivery模拟发送请求。

    配置企业微信

    企业微信的配置其实更简单,我们先创建一个群组,在群组右键有个添加机器人选项,添加成功后会生成webhook地址。我们只要向这个地址发送POST请求,群组内就会收到推送消息。

    消息内容支持文本(text)、markdown(markdown)、图片(image)、图文(news)四种消息类型,而且还支持在群内@群成员,下边以文本格式做示范。

       curl 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=145a516a-dd15-421f-97a3-ba3bf1479369' 
       -H 'Content-Type: application/json' 
       -d '
       {
            "msgtype": "text",
            "text": {
                "content": "你好,我是程序员内点事"
            }
       }'
    
    

    直接请求 url 发现消息推送成功,说明配置的没问题。

    但是到这大家发现一个问题没,GitHub企业微信一个只管往出发请求,一个只管接受固定数据格式的请求,两个接口的数据根本无法兼容啊?

    请求转发

    既然他们之间不兼容,没办法,那就只能我们自己在中间做一层适配,谁让两边都惹不起呢!

    转发的逻辑也比较简单,只需接受GitHub回调过来的请求数据,稍加修改组装成企业微信要求的数据格式,直接发送就可以了。

    GitHub推送过来的数据包括,仓库、作者、提交者、提交内容等信息,基本上够用。

    代码实现比较粗糙,将就看下吧

    @Slf4j
    @RestController
    public class WebhookController {
    
        private static String WECHAT_URL = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=145a516a-dd15-421f-97a3-ba3bf1479369";
    
        private static String GITHUB_API = "https://api.github.com/users/";
    
        /**
         * @param webhook webhook
         * @author 程序员内点事
         * @Description: github 回调
         * @date 2021/05/19
         */
        @PostMapping("/webhook")
        public String webhookGithub(@RequestBody GithubWebhookPullVo webhook) {
    
            log.info("webhook 入参接收 weChatWebhook {}", JSON.toJSONString(webhook));
            // 仓库名
            String name = webhook.getRepository().getName();
            SimpleDateFormat simpleFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String now = simpleFormatter.format(new Date());
            String content = null;
            if (webhook.getCommits().size() > 0) {
                GithubWebhookPullVo.CommitsDTO commitsDTO = webhook.getCommits().get(0);
    
                content = "[" + commitsDTO.getCommitter().getName() + "]" +
                        "于:" + now + "," +
                        "向作者:[" + commitsDTO.getAuthor().getName() + "]的,远程仓库" + name + "推送代码" +
                        "详情:";
    
                List<String> addeds = commitsDTO.getAdded();
                if (addeds.size() > 0) {
                    content += "添加文件:";
                    for (int i = 0; i < addeds.size(); i++) {
                        content = (i + 1) + content + addeds.get(i);
                    }
                }
                List<String> modifieds = commitsDTO.getModified();
                if (modifieds.size() > 0) {
                    content += "修改文件:";
                    for (int i = 0; i < modifieds.size(); i++) {
                        content = (i + 1) + content + modifieds.get(i);
                    }
                }
                List<String> removeds = commitsDTO.getRemoved();
                if (removeds.size() > 0) {
                    content += "删除文件:";
                    for (int i = 0; i < removeds.size(); i++) {
                        content = (i + 1) + content + removeds.get(i);
                    }
                }
            }
            log.info(content);
    
            WeChatWebhook weChatWebhook = new WeChatWebhook();
            weChatWebhook.setMsgtype("text");
            WeChatWebhook.TextDTO textDTO = new WeChatWebhook.TextDTO();
            textDTO.setContent(content);
            textDTO.setMentionedList(Arrays.asList("@all"));
            textDTO.setMentionedMobileList(Arrays.asList("@all"));
            weChatWebhook.setText(textDTO);
    
            /**
             * 组装参数后向企业微信发送webhook请求
             */
            log.info("企业微信发送参数 {}", JSON.toJSONString(weChatWebhook));
            String post = HttpUtil.sendPostJsonBody(WECHAT_URL, JSON.toJSONString(weChatWebhook));
            log.info("企业微信发送结果 post {}", post);
            return JSON.toJSONString(post);
        }
    }
    

    这里要提醒一下,GitHub webhook 回调过来的数据有些并不能直接拿来用,某些场景还是要调用GitHub API来换取一些数据的。

    文档地址:https://docs.github.com/en/rest/reference

    上边的配置工作完成,再将转发的代码部署到服务器,测试下整个链路看看效果,故意修改pom.xml文件提交,发现提交代码后成功向企业微信发送了消息,和我们预期的效果一致。

    源码地址:https://github.com/chengxy-nds/Springboot-Notebook/

    这个工程包含我过往文章里所有的案例,比如:抖音去水印工具源码人脸识别项目源码、以及redisSeataMQ等中间件的各种问题解决案例,感兴趣的同学可以Star个,实际开发一定会用得到。

  • 相关阅读:
    2.6
    20、算法的复杂度
    SVN的部署及分支等方法
    19、数据库设计的三大范式
    2.ViewBag、ViewData、TempData之间的区别
    1、MVC和EF中的 Model First 和 Code First
    19、lambda表达式树
    12、c#中事务及回滚
    11、Linq的使用
    18、(番外)匿名方法+lambda表达式
  • 原文地址:https://www.cnblogs.com/chengxy-nds/p/14840266.html
Copyright © 2011-2022 走看看