webhook-handler / main.py
Rolaficus's picture
Update main.py
79eb0b7 verified
Raw History Blame Contribute Delete
3.32 kB
from fastapi import FastAPI, Request, Header, HTTPException
from pydantic import BaseModel
from typing import Optional, Any, Dict
import requests
import os
app = FastAPI()
# --- Configuration ---
DISCORD_WEBHOOK_URL = os.environ.get("DISCORD_WEBHOOK_URL")
WEBHOOK_SECRET = os.environ.get("WEBHOOK_SECRET")
# --- Models ---
class HFRepo(BaseModel):
type: str
name: str
id: str
url: Dict[str, str]
class HFEvent(BaseModel):
action: str
scope: str
class HFPayload(BaseModel):
repo: HFRepo
event: HFEvent
discussion: Optional[Dict[str, Any]] = None
comment: Optional[Dict[str, Any]] = None
webhook: Optional[Dict[str, Any]] = None
@app.get("/")
def home():
return {"status": "Webhook listener is running"}
@app.post("/webhook")
def handle_webhook(
payload: HFPayload,
x_webhook_secret: Optional[str] = Header(None)
):
# 1. Security Check
if WEBHOOK_SECRET:
if not x_webhook_secret or x_webhook_secret.strip() != WEBHOOK_SECRET.strip():
print("Auth Failed")
raise HTTPException(status_code=401, detail="Invalid Secret")
# 2. Build Embed
embed = build_discord_embed(payload)
# 3. Send to Discord
if DISCORD_WEBHOOK_URL:
# Clean the URL just in case
url = DISCORD_WEBHOOK_URL.strip().strip("'").strip('"')
try:
print(f"Sending to Discord: {payload.repo.name} -> {payload.event.action}")
# requests is synchronous and handles DNS/Proxies much better in containers
resp = requests.post(url, json={"embeds": [embed]}, timeout=10)
resp.raise_for_status()
print("Success: Message sent.")
except Exception as e:
print(f"Error sending to Discord: {e}")
return {"message": "Error sending to Discord", "error": str(e)}
return {"message": "Processed"}
def build_discord_embed(data: HFPayload):
# Basic data extraction
repo_name = data.repo.name
repo_url = data.repo.url.get('web', '')
event_scope = data.event.scope
event_action = data.event.action
# Colors
COLOR_BLUE = 0x3498db
COLOR_GREEN = 0x2ecc71
COLOR_GREY = 0x95a5a6
color = COLOR_GREY
title = f"Update on {repo_name}"
description = f"Action: {event_action}\nScope: {event_scope}"
# Smart Formatting
if "repo.content" in event_scope:
color = COLOR_BLUE
title = f"New Commit to {repo_name}"
description = f"The repository content has been updated.\n[View Changes]({repo_url})"
elif "discussion" in event_scope:
color = COLOR_GREEN
disc_title = data.discussion.get('title', 'Discussion') if data.discussion else "Discussion"
title = f"Discussion: {disc_title}"
if data.comment:
content = data.comment.get('content', '')
if len(content) > 200:
content = content[:200] + "..."
description = f"**New Comment:**\n{content}\n\n[View Discussion]({repo_url})"
else:
description = f"Discussion status updated: **{event_action}**"
return {
"title": title,
"description": description,
"url": repo_url,
"color": color,
"footer": {"text": "Hugging Face Webhooks"}
}