最後活躍 1698355949

Github webhook proxy server (only Issues implemented)

修訂 07635c49433e7bc7a937a67712463cf916a2d9f6

__main__.py 原始檔案
1import hashlib
2import hmac
3import json
4import os
5from ipaddress import ip_address, ip_network
6
7import requests
8from dotenv import load_dotenv
9from flask import Flask, abort, request
10
11
12def create_app():
13 app = Flask(__name__)
14 load_dotenv()
15 app.config["GITHUB_SECRET"] = os.getenv("GITHUB_SECRET")
16 app.config["DISCORD_WEBHOOK"] = os.getenv("DISCORD_WEBHOOK")
17
18 return app
19
20
21app = create_app()
22
23
24def verify_src_ip(src_ip):
25 allowed_ips = requests.get("https://api.github.com/meta").json()["hooks"]
26 return any(src_ip in ip_network(valid_ip) for valid_ip in allowed_ips)
27
28
29def verify_hmac_hash(data, signature):
30 github_secret = bytes(app.config["GITHUB_SECRET"], "ascii")
31 mac = hmac.new(github_secret, msg=data, digestmod=hashlib.sha1)
32 return hmac.compare_digest("sha1=" + mac.hexdigest(), signature)
33
34
35@app.route("/github-payload", methods=["POST"])
36def github_payload():
37 src_ip = ip_address(request.headers.get("X-Real-IP", , request.access_route[0]))
38 if not verify_src_ip(src_ip):
39 app.logger.debug(f"invalid source ip: {src_ip}... aborting request")
40 abort(403)
41
42 signature = request.headers.get("X-Hub-Signature")
43 data = request.data
44 if not verify_hmac_hash(data, signature):
45 app.logger.debug("failed to verify signature!")
46 abort(403)
47
48 payload = request.get_json()
49 match event_type := request.headers.get("X-GitHub-Event"):
50 case "ping":
51 return json.dumps({"msg": "pong"})
52 case "issues":
53 issues_handler(payload)
54 case _:
55 app.logger.debug(f"no action defined for event {event_type}... continuing")
56 return ("", 200, None)
57
58
59def issues_handler(payload):
60 embed = {
61 "author": {
62 "name": payload["sender"]["login"],
63 "icon_url": payload["sender"]["avatar_url"],
64 },
65 "title": f"[{payload['repository']['full_name']}] Issue {payload['action'].capitalize()}: #{payload['issue']['number']} {payload['issue']['title']}",
66 "url": payload["issue"]["html_url"],
67 "description": payload["issue"].get("body"),
68 }
69 data = {
70 "username": "Github-OpenGist",
71 "embeds": [embed],
72 }
73 send_discord_webhook(app.config["DISCORD_WEBHOOK"], data=data)
74
75
76def send_discord_webhook(webhook_url, data):
77 requests.post(webhook_url, json=data)
78
79
80if __name__ == "__main__":
81 app.run(debug=True, port=5000)
82