pymicrocosm/src/microcosm/webmentions.py

131 lines
3.6 KiB
Python
Raw Normal View History

import json
import base64
import giteapy
import os
2024-11-16 14:31:21 +00:00
import clientapi_forgejo as forgejo
from flask import request, Blueprint
from urllib.parse import urlparse
from datetime import datetime
webhook_bp = Blueprint("webmention", __name__)
2024-11-16 14:31:21 +00:00
@webhook_bp.route("/webmentions", methods=["POST"])
def webmention_hook():
"""Accept web mention webhook request"""
body = request.get_json()
2024-11-16 14:31:21 +00:00
print(f"Incoming webmention {body}")
# webmention should always have a json body
if body is None:
2024-11-16 14:31:21 +00:00
return {"error": "invalid_request"}, 400
# assert that secret matches
2024-11-16 14:31:21 +00:00
if body.get("secret") != os.environ.get("WEBMENTION_SECRET", "changeme"):
return {"error": "invalid_secret"}, 401
# get existing mentions from gitea
from microcosm import get_api_client
api = get_api_client()
old_sha = None
try:
2024-11-16 14:31:21 +00:00
mentions_meta = api.repo_get_contents(
os.environ.get("GITEA_REPO_OWNER"),
os.environ.get("GITEA_REPO_NAME"),
os.environ.get("WEBMENTIONS_JSON_FILE"),
)
old_sha = mentions_meta.sha
2024-11-16 14:31:21 +00:00
mentions = json.loads(base64.decodebytes(mentions_meta.content.encode("utf8")))
2024-11-16 14:31:21 +00:00
except forgejo.rest.ApiException as e:
if e.status == 404:
print("no mentions yet, create new mentions file")
mentions = {}
# parse target url and get path on server
2024-11-16 14:31:21 +00:00
url_path = urlparse(body.get("target")).path
# create mention entry if needed
if url_path not in mentions:
mentions[url_path] = []
# if the mention is already processed, do nothing.
for entry in mentions[url_path]:
2024-11-16 14:31:21 +00:00
if entry["source"] == body["source"]:
return {"message": "mention already processed, no action taken."}
activity_types = {
"in-reply-to": "reply",
"like-of": "like",
"repost-of": "repost",
"bookmark-of": "bookmark",
"mention-of": "mention",
2024-11-16 14:31:21 +00:00
"rsvp": "rsvp",
}
# format the mention like the data from webmention.io api
new_mention = {
2024-11-16 14:31:21 +00:00
"id": body["post"]["wm-id"],
"source": body["source"],
"target": body["target"],
"activity": {"type": activity_types[body["post"]["wm-property"]]},
"verified_date": body.get("wm-received", datetime.now().isoformat()),
"data": {
"author": body["post"]["author"],
"content": body["post"].get("content", {}).get("html", None),
"published": body["post"].get("published"),
},
}
# append the new mention
mentions[url_path].append(new_mention)
2024-11-16 14:31:21 +00:00
content = base64.b64encode(json.dumps(mentions, indent=2).encode("utf8")).decode(
"utf8"
)
# store to repo
if old_sha:
print(f"Update {os.environ.get('WEBMENTIONS_JSON_FILE')}")
2024-11-16 14:31:21 +00:00
body = forgejo.UpdateFileOptions(content=content, sha=old_sha)
try:
2024-11-16 14:31:21 +00:00
r = api.repo_update_file(
os.environ.get("GITEA_REPO_OWNER"),
os.environ.get("GITEA_REPO_NAME"),
os.environ.get("WEBMENTIONS_JSON_FILE"),
body,
)
2024-11-16 14:31:21 +00:00
return {"message": "ok"}
except Exception as e:
return {"error": str(e)}, 500
2024-11-16 14:31:21 +00:00
else:
print(f"Create {os.environ.get('WEBMENTIONS_JSON_FILE')}")
body = giteapy.CreateFileOptions(content=content)
try:
2024-11-16 14:31:21 +00:00
r = api.repo_create_file(
os.environ.get("GITEA_REPO_OWNER"),
os.environ.get("GITEA_REPO_NAME"),
os.environ.get("WEBMENTIONS_JSON_FILE"),
body,
)
2024-11-16 14:31:21 +00:00
return {"message": "ok"}
except Exception as e:
2024-11-16 14:31:21 +00:00
return {"error": str(e)}, 500