Files
homelab-optimized/hosts/vms/homelab-vm/gitea-ntfy-bridge/bridge.py
Gitea Mirror Bot e7652c8dab
Some checks failed
Documentation / Build Docusaurus (push) Failing after 5m3s
Documentation / Deploy to GitHub Pages (push) Has been skipped
Sanitized mirror from private repository - 2026-04-20 01:32:01 UTC
2026-04-20 01:32:01 +00:00

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()