GithubWebhook解析服务

Webhook

Webhook是一种基于HTTP的回调函数,可在两个应用编程接口(API)之间实现轻量级的事件驱动通信。客户端向服务器API提供唯一URL,并指定它想要知道的事件。设置webhook后,客户端不再需要轮询服务器;发生指定的事件时,服务器会自动将相关的有效负载发送到客户端的Webhook URL。

Webhook可以是实现自动化的重要一环,而且实现简单(仅一次http请求,可以嵌到任何地方),国内飞书、钉钉和企业微信等都支持Webhook推送消息。

以上是好处,那么坏处就是http请求的参数不统一,就是因为它太简单了,没有协议规范,一个平台一个样,比如在某平台上消息叫做”msg”,在其它平台可能就是”message”或”text”。因而想要真正发挥Webhook的作用,需要搭建一个自己的Webhook解析处理服务,好在这并不复杂。本文将以Github Webhook为例,基于Flask进行解析并推送到企业微信。

然而,如果您没有高度自定义的需求,且仅想使用Github Webhook而不使用其它平台的,可以使用Dingling,它可以将GIthub Webhook转发到企业微信。DIngling只支能推送Markdown格式的消息(不能在微信中直接查看)是我放弃它的原因。

CloudFlare Webhook

CloudFlare Webhook 的消息体非常简单,如下:

{"text":"CloudFlare简直是业界良心!"}

使用Flask解析,并将消息转发到企业微信:

from flask import Flask, request, jsonify,abort
import requests
from config import config
app = Flask(__name__)

@app.route('/cloudflare', methods=['POST'])
def cloudflare():
  if request.method == 'POST':
    print(request.json)
    text = request.json['text']
    data = {
        "msgtype": "text",
        "text": {
            "content": text
        }
        }
    r = requests.post('https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key='+config['API_KEY_3'], json=data)
    print(r.json())
    return 'success', 200
  else:
    abort(400)
 
if __name__ == '__main__':
    app.run(port=5700,host='0.0.0.0',debug=True)

将Flask进程部署到服务器上,并在Cloudflare上填写地址,如下图

image-20231026224635480

保存并测试之后,应该就能在企业微信中收到一条简短的测试消息了。

Github Webhook

如果说CloudFlare的Webhook是一条简短的消息,那么Github恨不得把整个网站的信息全都传给你,参数有几十个,但是没有一句是自然语言,因而需要根据自己的需求解析。下段代码的逻辑是先判断事件类型,再根据事件类型提取需要的信息转发出去。下面解析的内容包括push,release,issue,pull等行为。

@app.route('/github', methods=['POST'])
def github():
    githubEvent = request.headers['x-github-event']
    if githubEvent == 'push':
        respority = request.json['repository']['name']
        branch = request.json['ref'].split('/')[-1]
        commits = request.json['commits']
        for commit in commits:
            author = commit['author']['name']
            message = commit['message']
            data = {
                "msgtype": "text",
                "text": {
                    "content": f"{respority} 有人提交了代码\n分支:{branch}\n作者:{author}\n提交信息:{message}"
                }
            }
            r = requests.post('https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key='+config['API_KEY_2'], json=data)
        return 'success', 200
    if githubEvent == 'issues':
        action = request.json['action']
        if action == 'opened':
            respority = request.json['repository']['name']
            issue = request.json['issue']
            title = issue['title']
            body = issue['body']
            user = issue['user']['login']
            data = {
                "msgtype": "text",
                "text": {
                    "content": f"{respority} 有人提交了 issue\n标题:{title}\n内容:{body}\n提交者:{user}"
                }
            }
            r = requests.post('https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key='+config['API_KEY_2'], json=data)
        if action == 'closed':
            respority = request.json['repository']['name']
            issue = request.json['issue']
            title = issue['title']
            body = issue['body']
            user = issue['user']['login']
            data = {
                "msgtype": "text",
                "text": {
                    "content": f"{respority} 有人关闭了 issue\n标题:{title}\n内容:{body}\n提交者:{user}"
                }
            }
            r = requests.post('https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key='+config['API_KEY_2'], json=data)
        if action == 'reopened':
            respority = request.json['repository']['name']
            issue = request.json['issue']
            title = issue['title']
            body = issue['body']
            user = issue['user']['login']
            data = {
                "msgtype": "text",
                "text": {
                    "content": f"{respority} 有人重新打开了 issue\n标题:{title}\n内容:{body}\n提交者:{user}"
                }
            }
            r = requests.post('https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key='+config['API_KEY_2'], json=data)
        return 'success', 200
    if githubEvent == 'pull_request':
        action = request.json['action']
        if action == 'opened':
            respority = request.json['repository']['name']
            pull_request = request.json['pull_request']
            title = pull_request['title']
            body = pull_request['body']
            user = pull_request['user']['login']
            data = {
                "msgtype": "text",
                "text": {
                    "content": f"{respority} 有人提交了 pull request\n标题:{title}\n内容:{body}\n提交者:{user}"
                }
            }
            r = requests.post('https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key='+config['API_KEY_2'], json=data)
        if action == 'closed':
            respority = request.json['repository']['name']
            pull_request = request.json['pull_request']
            title = pull_request['title']
            body = pull_request['body']
            user = pull_request['user']['login']
            data = {
                "msgtype": "text",
                "text": {
                    "content": f"{respority} 有人关闭了 pull request\n标题:{title}\n内容:{body}\n提交者:{user}"
                }
            }
            r = requests.post('https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key='+config['API_KEY_2'], json=data)
        if action == 'reopened':
            respority = request.json['repository']['name']
            pull_request = request.json['pull_request']
            title = pull_request['title']
            body = pull_request['body']
            user = pull_request['user']['login']
            data = {
                "msgtype": "text",
                "text": {
                    "content": f"{respority} 有人重新打开了 pull request\n标题:{title}\n内容:{body}\n提交者:{user}"
                }
            }
            r = requests.post('https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key='+config['API_KEY_2'], json=data)
        return 'success', 200
    if githubEvent == 'release':
        action = request.json['action']
        if action == 'published':
            respority = request.json['repository']['name']
            release = request.json['release']
            tag_name = release['tag_name']
            body = release['body']
            user = release['author']['login']
            data = {
                "msgtype": "text",
                "text": {
                    "content": f"{respority} 有人发布了 release\n版本:{tag_name}\n内容:{body}\n发布者:{user}"
                }
            }
            r = requests.post('https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key='+config['API_KEY_2'], json=data)
        return 'success', 200
    if githubEvent == 'ping':
        data = {
            "msgtype": "text",
            "text": {
                "content": "github webhook test"
            }
        }
        r = requests.post('https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key='+config['API_KEY_2'], json=data)
        return 'success', 200
    if githubEvent == 'create':
        ref_type = request.json['ref_type']
        if ref_type == 'branch':
            respority = request.json['repository']['name']
            branch = request.json['ref']
            user = request.json['sender']['login']
            data = {
                "msgtype": "text",
                "text": {
                    "content": f"{respority} 有人创建了分支\n分支:{branch}\n创建者:{user}"
                }
            }
            r = requests.post('https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key='+config['API_KEY_2'],
                              json=data)
        if ref_type == 'tag':
            respority = request.json['repository']['name']
            tag = request.json['ref']
            user = request.json['sender']['login']
            data = {
                "msgtype": "text",
                "text": {
                    "content": f"{respority} 有人创建了 tag\ntag:{tag}\n创建者:{user}"
                }
            }
            r = requests.post('https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key='+config['API_KEY_2'],
                              json=data)
        return 'success', 200
    if githubEvent == 'delete':
        ref_type = request.json['ref_type']
        if ref_type == 'branch':
            respority = request.json['repository']['name']
            branch = request.json['ref']
            user = request.json['sender']['login']
            data = {
                "msgtype": "text",
                "text": {
                    "content": f"{respority} 有人删除了分支\n分支:{branch}\n删除者:{user}"
                }
            }
            r = requests.post('https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key='+config['API_KEY_2'],
                              json=data)
        if ref_type == 'tag':
            respority = request.json['repository']['name']
            tag = request.json['ref']
            user = request.json['sender']['login']
            data = {
                "msgtype": "text",
                "text": {
                    "content": f"{respority} 有人删除了 tag\ntag:{tag}\n删除者:{user}"
                }
            }
            r = requests.post('https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key='+config['API_KEY_2'],
                              json=data)
        return 'success', 200
    if githubEvent == 'pull_request_review':
        action = request.json['action']
        if action == 'submitted':
            respority = request.json['repository']['name']
            pull_request = request.json['pull_request']
            title = pull_request['title']
            body = pull_request['body']
            user = pull_request['user']['login']
            review = request.json['review']
            state = review['state']
            data = {
                "msgtype": "text",
                "text": {
                    "content": f"{respority} 有人提交了 pull request review\n标题:{title}\n内容:{body}\n提交者:{user}\n状态:{state}"
                }
            }
            r = requests.post(
                'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key='+config['API_KEY_2'],
                json=data)
        return 'success', 200
    if githubEvent == 'pull_request_review_comment':
        action = request.json['action']
        if action == 'created':
            respority = request.json['repository']['name']
            pull_request = request.json['pull_request']
            title = pull_request['title']
            body = pull_request['body']
            user = pull_request['user']['login']
            comment = request.json['comment']
            comment_body = comment['body']
            data = {
                "msgtype": "text",
                "text": {
                    "content": f"{respority} 有人提交了 pull request review comment\n标题:{title}\n内容:{body}\n提交者:{user}\n内容:{comment_body}"
                }
            }
            r = requests.post(
                'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key='+config['API_KEY_2'],
                json=data)
        return 'success', 200
    # if githubEvent == 'check_run':
    #     action = request.json['action']
    #     if action == 'created':
    #         respority = request.json['repository']['name']
    #         check_run = request.json['check_run']
    #         name = check_run['name']
    #         conclusion = check_run['conclusion']
    #         data = {
    #             "msgtype": "text",
    #             "text": {
    #                 "content": f"{respority} 有人提交了 check run\n名称:{name}\n状态:{conclusion}"
    #             }
    #         }
    #         r = requests.post(
    #             'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key='+config['API_KEY_2'],
    #             json=data)
    #     return 'success', 200
    if githubEvent == 'check_suite':
        action = request.json['action']
        if action == 'completed':
            respority = request.json['repository']['name']
            check_suite = request.json['check_suite']
            conclusion = check_suite['conclusion']
            data = {
                "msgtype": "text",
                "text": {
                    "content": f"{respority} 有人提交了 check suite\n状态:{conclusion}"
                }
            }
            r = requests.post(
                'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key='+config['API_KEY_2'],
                json=data)
        return 'success', 200

配置好服务端和Github,可在历史记录中查看详细的推送信息。

image-20231026231232779

参考文档

Github官方文档