add proxy app to repo
Signed-off-by: disqualifier <dev@disqualifier.me>
This commit is contained in:
parent
030109c08b
commit
fbe66b1473
111
app.py
Normal file
111
app.py
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
"""
|
||||||
|
discord webhook reformat proxy
|
||||||
|
|
||||||
|
drop-in for discord.com: swap the hostname on a gitea (discord-type) webhook
|
||||||
|
so it points here, e.g.
|
||||||
|
https://discord.com/api/webhooks/<id>/<token>
|
||||||
|
becomes
|
||||||
|
https://proxy.example.com/api/webhooks/<id>/<token>
|
||||||
|
|
||||||
|
the proxy receives gitea's native discord embed, rewrites only the embed
|
||||||
|
`description` into github's `[`hash`](url)` backtick style (hash backticked,
|
||||||
|
commit message collapsed to its first line, author kept), and forwards the
|
||||||
|
otherwise-untouched payload to the real discord webhook. everything gitea
|
||||||
|
already handled (title, branches, multiple commits, tags, colors) passes
|
||||||
|
through as-is.
|
||||||
|
|
||||||
|
run:
|
||||||
|
pip install fastapi httpx uvicorn
|
||||||
|
uvicorn discord_reformat_proxy:app --host 127.0.0.1 --port 8000
|
||||||
|
|
||||||
|
then set a gitea webhook (type: discord) to:
|
||||||
|
http://127.0.0.1:8000/api/webhooks/<id>/<token>
|
||||||
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import httpx
|
||||||
|
from fastapi import FastAPI, Request, Response
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
DISCORD_BASE = "https://discord.com"
|
||||||
|
TIMEOUT = 10
|
||||||
|
|
||||||
|
# gitea commit line: [hash](url) <message possibly spanning lines> - author
|
||||||
|
# github commit line: [`hash`](url) <first line of message> - author
|
||||||
|
# match a hash-link, then the message up to an optional trailing " - author",
|
||||||
|
# stopping at the next commit line or end of string.
|
||||||
|
_COMMIT = re.compile(
|
||||||
|
r"\[`?([0-9a-f]{6,40})`?\]\((https?://[^)]+)\)"
|
||||||
|
r"\s+(.*?)"
|
||||||
|
r"(\s-\s[^\n]+?)?"
|
||||||
|
r"(?=\n\[|\Z)",
|
||||||
|
re.DOTALL,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _commit_sub(m: "re.Match") -> str:
|
||||||
|
"""rebuild one commit line github-style: backtick hash, first message line only"""
|
||||||
|
short, url, message, author = m.group(1), m.group(2), m.group(3), m.group(4) or ""
|
||||||
|
first_line = message.strip().split("\n", 1)[0].strip()
|
||||||
|
return f"[`{short}`]({url}) {first_line}{author}"
|
||||||
|
|
||||||
|
|
||||||
|
def _reformat(description: str) -> str:
|
||||||
|
"""rewrite gitea commit lines into github-style (backtick hash, one line each)"""
|
||||||
|
return _COMMIT.sub(_commit_sub, description)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/healthz")
|
||||||
|
async def healthz():
|
||||||
|
"""liveness probe"""
|
||||||
|
return {"ok": True}
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/api/webhooks/{webhook_id}/{token}")
|
||||||
|
async def proxy(webhook_id: str, token: str, request: Request):
|
||||||
|
"""reformat embed descriptions and forward to the real discord webhook"""
|
||||||
|
try:
|
||||||
|
payload = await request.json()
|
||||||
|
except Exception:
|
||||||
|
body = await request.body()
|
||||||
|
async with httpx.AsyncClient(timeout=TIMEOUT) as client:
|
||||||
|
resp = await client.post(
|
||||||
|
f"{DISCORD_BASE}/api/webhooks/{webhook_id}/{token}",
|
||||||
|
content=body,
|
||||||
|
params=dict(request.query_params),
|
||||||
|
headers={"content-type": request.headers.get("content-type", "application/json")},
|
||||||
|
)
|
||||||
|
return _passthrough(resp)
|
||||||
|
|
||||||
|
if isinstance(payload, dict):
|
||||||
|
for embed in payload.get("embeds") or []:
|
||||||
|
if isinstance(embed, dict) and embed.get("description"):
|
||||||
|
embed["description"] = _reformat(embed["description"])
|
||||||
|
|
||||||
|
async with httpx.AsyncClient(timeout=TIMEOUT) as client:
|
||||||
|
resp = await client.post(
|
||||||
|
f"{DISCORD_BASE}/api/webhooks/{webhook_id}/{token}",
|
||||||
|
json=payload,
|
||||||
|
params=dict(request.query_params),
|
||||||
|
)
|
||||||
|
|
||||||
|
return _passthrough(resp)
|
||||||
|
|
||||||
|
|
||||||
|
def _passthrough(resp: httpx.Response) -> Response:
|
||||||
|
"""return discord's response verbatim, preserving rate-limit headers"""
|
||||||
|
forward = {
|
||||||
|
k: v for k, v in resp.headers.items()
|
||||||
|
if k.lower().startswith("x-ratelimit") or k.lower() == "retry-after"
|
||||||
|
}
|
||||||
|
return Response(
|
||||||
|
content=resp.content,
|
||||||
|
status_code=resp.status_code,
|
||||||
|
media_type=resp.headers.get("content-type"),
|
||||||
|
headers=forward,
|
||||||
|
)
|
||||||
Loading…
Reference in New Issue
Block a user