拉仇恨!webhook + 企業微信給同事做了個程式碼提交監聽工具

chengxy-nds 發表於 2021-06-02

本文案例收錄在 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請求,這個三方平臺可以是釘釘、飛書、企業微信這類平臺。

拉仇恨!webhook + 企業微信給同事做了個程式碼提交監聽工具

下面我們以 GitHub + 企業微信 來實現程式碼提交監聽,自動向企業微信群組推送訊息。

配置GitHub webhook

首先進入GitHub對應專案的 Settings,做webhook的基礎配置。

拉仇恨!webhook + 企業微信給同事做了個程式碼提交監聽工具

主要配置四部分:

Payload URL 回撥服務的地址;

Content type 回撥請求頭,建議JSON格式;

Secret 為了做安全校驗,設定後會在請求 header 中增加如下兩個屬性,用來區分請求的來源,避免暴露的請求被惡意訪問;

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

最後我們選擇由哪些事件來觸發webhook回撥,push event(程式碼推送事件)、everything(所有事件)、某些特定事件三種。

拉仇恨!webhook + 企業微信給同事做了個程式碼提交監聽工具

我們可以在 Recent Deliveries 檢視webhook回撥記錄,以及完整的請求和引數資料,還可以redelivery模擬傳送請求。

拉仇恨!webhook + 企業微信給同事做了個程式碼提交監聽工具

配置企業微信

企業微信的配置其實更簡單,我們先建立一個群組,在群組右鍵有個新增機器人選項,新增成功後會生成webhook地址。我們只要向這個地址傳送POST請求,群組內就會收到推送訊息。

拉仇恨!webhook + 企業微信給同事做了個程式碼提交監聽工具

訊息內容支援文字(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 發現訊息推送成功,說明配置的沒問題。

拉仇恨!webhook + 企業微信給同事做了個程式碼提交監聽工具

但是到這大家發現一個問題沒,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

拉仇恨!webhook + 企業微信給同事做了個程式碼提交監聽工具

上邊的配置工作完成,再將轉發的程式碼部署到伺服器,測試下整個鏈路看看效果,故意修改pom.xml檔案提交,發現提交程式碼後成功向企業微信傳送了訊息,和我們預期的效果一致。

拉仇恨!webhook + 企業微信給同事做了個程式碼提交監聽工具

原始碼地址:https://github.com/chengxy-nds/Springboot-Notebook/

這個工程包含我過往文章裡所有的案例,比如:抖音去水印工具原始碼人臉識別專案原始碼、以及redisSeataMQ等中介軟體的各種問題解決案例,感興趣的同學可以Star個,實際開發一定會用得到。

拉仇恨!webhook + 企業微信給同事做了個程式碼提交監聽工具