141 lines
5.1 KiB
Python
141 lines
5.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Gitea to ntfy Webhook Bridge - Translates Gitea events to ntfy notifications"""
|
|
|
|
import os
|
|
import sys
|
|
import json
|
|
import urllib.request
|
|
from http.server import HTTPServer, BaseHTTPRequestHandler
|
|
|
|
# Force unbuffered output
|
|
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', buffering=1)
|
|
sys.stderr = os.fdopen(sys.stderr.fileno(), 'w', buffering=1)
|
|
|
|
NTFY_URL = os.environ.get("NTFY_URL", "https://ntfy.vish.gg")
|
|
NTFY_TOPIC = os.environ.get("NTFY_TOPIC", "homelab-alerts")
|
|
|
|
class WebhookHandler(BaseHTTPRequestHandler):
|
|
def do_GET(self):
|
|
"""Health check endpoint"""
|
|
self.send_response(200)
|
|
self.send_header("Content-type", "text/plain")
|
|
self.end_headers()
|
|
self.wfile.write(b"Gitea-ntfy bridge OK\n")
|
|
print(f"Health check from {self.client_address[0]}", flush=True)
|
|
|
|
def do_POST(self):
|
|
content_length = int(self.headers.get("Content-Length", 0))
|
|
body = self.rfile.read(content_length)
|
|
|
|
try:
|
|
data = json.loads(body) if body else {}
|
|
event_type = self.headers.get("X-Gitea-Event", "unknown")
|
|
|
|
print(f"Received {event_type} event from {self.client_address[0]}", flush=True)
|
|
|
|
title, message, tags, priority = self.format_message(event_type, data)
|
|
|
|
if title and message:
|
|
print(f"Sending notification: {title}", flush=True)
|
|
self.send_ntfy(title, message, tags, priority)
|
|
self.send_response(200)
|
|
else:
|
|
print(f"Ignoring event type: {event_type}", flush=True)
|
|
self.send_response(204) # No content to send
|
|
except Exception as e:
|
|
print(f"Error processing webhook: {e}", flush=True)
|
|
self.send_response(500)
|
|
|
|
self.end_headers()
|
|
|
|
def format_message(self, event_type, data):
|
|
"""Format Gitea event into ntfy message"""
|
|
repo = data.get("repository", {}).get("full_name", "unknown")
|
|
sender = data.get("sender", {}).get("login", "unknown")
|
|
|
|
title = None
|
|
message = None
|
|
tags = "git"
|
|
priority = "default"
|
|
|
|
if event_type == "push":
|
|
commits = data.get("commits", [])
|
|
branch = data.get("ref", "").replace("refs/heads/", "")
|
|
count = len(commits)
|
|
title = f"Push to {repo}"
|
|
message = f"{sender} pushed {count} commit(s) to {branch}"
|
|
if commits:
|
|
message += f"\n\n* {commits[0].get('message', '').split(chr(10))[0]}"
|
|
if count > 1:
|
|
message += f"\n* ... and {count - 1} more"
|
|
tags = "package"
|
|
|
|
elif event_type == "pull_request":
|
|
action = data.get("action", "")
|
|
pr = data.get("pull_request", {})
|
|
pr_title = pr.get("title", "")
|
|
pr_num = pr.get("number", "")
|
|
title = f"PR #{pr_num} {action}"
|
|
message = f"{repo}: {pr_title}\nBy: {sender}"
|
|
tags = "twisted_rightwards_arrows"
|
|
if action == "opened":
|
|
priority = "high"
|
|
|
|
elif event_type == "issues":
|
|
action = data.get("action", "")
|
|
issue = data.get("issue", {})
|
|
issue_title = issue.get("title", "")
|
|
issue_num = issue.get("number", "")
|
|
title = f"Issue #{issue_num} {action}"
|
|
message = f"{repo}: {issue_title}\nBy: {sender}"
|
|
tags = "clipboard"
|
|
|
|
elif event_type == "release":
|
|
action = data.get("action", "")
|
|
release = data.get("release", {})
|
|
tag = release.get("tag_name", "")
|
|
title = f"Release {tag}"
|
|
message = f"{repo}: New release {action}\n{release.get('name', tag)}"
|
|
tags = "rocket"
|
|
priority = "high"
|
|
|
|
elif event_type == "create":
|
|
ref_type = data.get("ref_type", "")
|
|
ref = data.get("ref", "")
|
|
title = f"New {ref_type}: {ref}"
|
|
message = f"{repo}\nCreated by: {sender}"
|
|
tags = "sparkles"
|
|
|
|
elif event_type == "delete":
|
|
ref_type = data.get("ref_type", "")
|
|
ref = data.get("ref", "")
|
|
title = f"Deleted {ref_type}: {ref}"
|
|
message = f"{repo}\nDeleted by: {sender}"
|
|
tags = "wastebasket"
|
|
|
|
return title, message, tags, priority
|
|
|
|
def send_ntfy(self, title, message, tags="git", priority="default"):
|
|
"""Send notification to ntfy"""
|
|
url = f"{NTFY_URL}/{NTFY_TOPIC}"
|
|
headers = {
|
|
"Title": title,
|
|
"Tags": tags,
|
|
"Priority": priority,
|
|
}
|
|
|
|
req = urllib.request.Request(url, data=message.encode('utf-8'), headers=headers, method="POST")
|
|
try:
|
|
with urllib.request.urlopen(req, timeout=10) as resp:
|
|
print(f"Sent: {title} -> {resp.status}")
|
|
except Exception as e:
|
|
print(f"Failed to send ntfy: {e}")
|
|
|
|
def log_message(self, format, *args):
|
|
print(f"[{self.log_date_time_string()}] {format % args}")
|
|
|
|
if __name__ == "__main__":
|
|
server = HTTPServer(("0.0.0.0", 8095), WebhookHandler)
|
|
print(f"Gitea-ntfy bridge running on :8095 -> {NTFY_URL}/{NTFY_TOPIC}")
|
|
server.serve_forever()
|