Changeset

6203:131b8bfbefb4

mod_pubsub_forgejo: new module for forgejo webhooks
author nicoco <nicoco@nicoco.fr>
date Mon, 17 Feb 2025 23:28:05 +0100
parents 6202:6d5a19bdd718
children 6204:119c0eb65bf3
files mod_pubsub_forgejo/.hgignore mod_pubsub_forgejo/.prettierignore mod_pubsub_forgejo/README.md mod_pubsub_forgejo/format.lib.lua mod_pubsub_forgejo/mod_pubsub_forgejo.lua mod_pubsub_forgejo/style.yaml mod_pubsub_forgejo/templates.lib.lua mod_pubsub_forgejo/test.lua mod_pubsub_forgejo/webhook-examples/create/content.json mod_pubsub_forgejo/webhook-examples/create/headers mod_pubsub_forgejo/webhook-examples/pull_request/content.json mod_pubsub_forgejo/webhook-examples/pull_request/headers mod_pubsub_forgejo/webhook-examples/pull_request_comment/content.json mod_pubsub_forgejo/webhook-examples/pull_request_comment/headers mod_pubsub_forgejo/webhook-examples/pull_request_rejected/content.json mod_pubsub_forgejo/webhook-examples/pull_request_rejected/headers mod_pubsub_forgejo/webhook-examples/push/content.json mod_pubsub_forgejo/webhook-examples/push/headers mod_pubsub_forgejo/webhook-examples/push/slidge.rss mod_pubsub_forgejo/webhook-examples/push_tag/content.json mod_pubsub_forgejo/webhook-examples/push_tag/headers mod_pubsub_forgejo/webhook-examples/release/content.json mod_pubsub_forgejo/webhook-examples/release/headers
diffstat 23 files changed, 2812 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_pubsub_forgejo/.hgignore	Mon Feb 17 23:28:05 2025 +0100
@@ -0,0 +1,3 @@
+test
+style.yaml
+.prettierignore
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_pubsub_forgejo/.prettierignore	Mon Feb 17 23:28:05 2025 +0100
@@ -0,0 +1,1 @@
+README.md
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_pubsub_forgejo/README.md	Mon Feb 17 23:28:05 2025 +0100
@@ -0,0 +1,150 @@
+---
+labels:
+- "Stage-Beta"
+summary: "Turn forgejo/github/gitlab webhooks into atom-in-pubsub"
+rockspec:
+  build:
+    modules:
+      mod_pubsub_forgejo.templates: templates.lib.lua
+      mod_pubsub_forgejo.format: format.lib.lua
+---
+
+# Introduction
+
+This module accepts Forgejo webhooks and publishes them to a local
+pubsub component as Atom entries for XMPP clients to subscribe to.
+Such entries can be viewed with a pubsub-compatible XMPP client such as
+[movim](https://movim.eu/) or [libervia](https://libervia.org/), or turned
+into chat messages with a bot (cf last section of this document).
+It is a more customisable `mod_pubsub_github`.
+
+It should also work with other forges such as github and gitlab (to be tested).
+
+# Configuration
+
+## Basic setup
+
+Load the module on a pubsub component:
+
+```{.lua}
+Component "pubsub.example.com" "pubsub"
+    modules_enabled = { "pubsub_forgejo" }
+    forgejo_secret = "something-very-secret"  -- copy this in the Forgejo web UI
+```
+
+The "Target URL" to configure in the Forgejo web UI should be either:
+
+- `http://pubsub.example.com:5280/pubsub_forgejo`
+- `https://pubsub.example.com:5281/pubsub_forgejo`
+
+If your HTTP host doesn't match the pubsub component's address, you will
+need to inform Prosody. For more info see Prosody's [HTTP server
+documentation](https://prosody.im/doc/http#virtual_hosts).
+
+## Advanced setup
+
+### Publishing settings
+
+#### Pubsub actor
+
+By default, `forgejo_actor` is unset; this results in nodes being created by the prosody superuser.
+Change this if you set up access control and you know what you are doing.
+
+#### Pubsub node
+
+By default, all events are published in the same pubsub node named "forgejo".
+This can be changed by setting `forgejo_node` to a different value.
+
+Another option is to used different nodes based on which repository emitted the webhook.
+This is useful if you configured the webhook at the user (or organisation) level instead of repository-level.
+To set this up, define `forgejo_node_prefix` and `forgejo_node_mapping`.
+`forgejo_node_mapping` must be a key in the the webhook "repository" payload, e.g., "full*name". Example: with `forge_node_prefix = "forgejo---"` and `forgejo_node_mapping = "full_name"`, webhooks emitted by the repository \_repo-name* in the _org-name_ organisation will be published in the node _forgejo---org-name/repo-name_.
+
+### Customizing the atom entry
+
+#### Pushes with no commits
+
+By default, pushes without commits (i.e., pushing tags) are ignored, because it leads
+to weird entries like "romeo pushed 0 commit(s) to repo".
+This behaviour can be changed by setting `forgejo_skip_commitless_push = false`.
+
+#### Atom entry templates
+
+By default, 3 webhooks events are handled (push, pull_request and release),
+and the payload is turned into a atom entry by
+using [util.interpolation](https://prosody.im/doc/developers/util/interpolation) templates.
+The default templates can be viewed in the source of this module, in the `templates.lib.lua`
+file.
+
+You can customise them using by setting `forgejo_templates`, which is merged with the default
+templates.
+In this table, keys are forgejo event names (`x-forgejo-template` request header).
+Values of this table are tables too, where keys are atom elements and values are the templates
+passed to [util.interpolation](https://prosody.im/doc/developers/util/interpolation).
+
+A few filters are provided:
+
+- `|shorten` strips the last 32 characters: useful to get a short commit hash
+- `|firstline` only keeps the first line: useful to get a commit "title"
+- `|branch` strips the first 12 characters: useful to get a branch name from `data.ref`
+- `|tag` strips the first 11 characters: useful to get a tag name from `data.ref`
+
+Example:
+
+```{.lua}
+forgejo_templates = {
+	pull_request = nil,  -- suppress handling of `pull_request` events
+	release = {          -- data is the JSON payload of the webhook
+		title = "{data.sender.username} {data.action} a release for {data.repository.name}",
+		content = "{data.release.name}",
+		id = "release-{data.release.tag_name}",
+		link = "{data.release.html_url}"
+	}
+}
+```
+
+Examples payloads are provided in the `webhook-examples`
+
+# Publishing in a MUC
+
+You can use a bot that listen to pubsub events and converts them to MUC messages.
+MattJ's [riddim](https://matthewwild.co.uk/projects/riddim/) is well suited for that.
+
+Example config, single pubsub node:
+
+```{.lua}
+jid = "forgejo-bot@example.com"
+password = "top-secret-stuff"
+room = "room@rooms.example.com"
+autojoin = room
+
+pubsub2room = {
+  "pubsub.example.com#forgejo" = {
+    room = room,
+    template = "${title}\n${content}\n${link@href}"
+  }
+}
+```
+
+Example with several nodes:
+
+```{.lua}
+local nodes = {"forgejo---org/repo1", "forgejo---org/repo2"}
+pubsub2room = {}
+
+for _, node in ipairs(slidge_repos) do
+  pubsub2room = ["pubsub.example.com#" .. node] = {
+    room = room,
+    template = "${title}\n${content}\n${link@href}"
+  }
+end
+```
+
+# TODO
+
+- Default templates for all event types
+- (x)html content
+
+# Compatibility
+
+Works with prosody 0.12
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_pubsub_forgejo/format.lib.lua	Mon Feb 17 23:28:05 2025 +0100
@@ -0,0 +1,44 @@
+local st = require "util.stanza";
+local datetime = require "util.datetime";
+
+local function shorten(x) return string.sub(x, 1, -32) end
+
+local function firstline(x) return x:match("^[^\r\n]*") end
+
+local function branch(x) return string.sub(x, 12) end
+
+local function tag(x) return string.sub(x, 11) end
+
+local function noop(x) return x end
+
+local filters = {
+	shorten = shorten,
+	firstline = firstline,
+	branch = branch,
+	tag = tag
+}
+
+local render = require"util.interpolation".new("%b{}", noop, filters);
+
+local function get_item(data, templates)
+	local function render_tpl(name) return render(templates[name], {data = data}) end
+
+	local now = datetime.datetime()
+	local id = render(templates["id"], {data = data})
+	-- LuaFormatter off
+	return st.stanza("item", {id = id, xmlns = "http://jabber.org/protocol/pubsub"})
+		:tag("entry", {xmlns = "http://www.w3.org/2005/Atom"})
+			:tag("id"):text(id):up()
+			:tag("title"):text(render_tpl("title")):up()
+			:tag("content", {type = "text"}):text(render_tpl("content")):up()
+			:tag("link", {rel = "alternate", href = render_tpl("link")}):up()
+			:tag("published"):text(now):up()
+			:tag("updated"):text(now):up()
+			:tag("author")
+			:tag("name")
+				:text(data.sender.username):up()
+				:tag("email"):text(data.sender.email)
+    -- LuaFormatter on
+end
+
+return get_item
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_pubsub_forgejo/mod_pubsub_forgejo.lua	Mon Feb 17 23:28:05 2025 +0100
@@ -0,0 +1,115 @@
+module:depends("http")
+local pubsub_service = module:depends("pubsub").service
+
+local st = require "util.stanza"
+local json = require "util.json"
+local hashes = require "util.hashes"
+local from_hex = require"util.hex".from
+local hmacs = {
+	sha1 = hashes.hmac_sha1,
+	sha256 = hashes.hmac_sha256,
+	sha384 = hashes.hmac_sha384,
+	sha512 = hashes.hmac_sha512
+}
+
+local format = module:require "format"
+local default_templates = module:require "templates"
+
+-- configuration
+local forgejo_secret = module:get_option("forgejo_secret")
+
+local default_node = module:get_option("forgejo_node", "forgejo")
+local node_prefix = module:get_option_string("forgejo_node_prefix", "forgejo/")
+local node_mapping = module:get_option_string("forgejo_node_mapping")
+local forgejo_actor = module:get_option_string("forgejo_actor") or true
+
+local skip_commitless_push = module:get_option_boolean(
+				                             "forgejo_skip_commitless_push", true)
+local custom_templates = module:get_option("forgejo_templates")
+
+local forgejo_templates = default_templates
+
+if custom_templates ~= nil then
+	for k, v in pairs(custom_templates) do forgejo_templates[k] = v end
+end
+
+-- used for develoment, should never be set in prod!
+local insecure = module:get_option_boolean("forgejo_insecure", false)
+-- validation
+if not insecure then assert(forgejo_secret, "Please set 'forgejo_secret'") end
+
+local error_mapping = {
+	["forbidden"] = 403,
+	["item-not-found"] = 404,
+	["internal-server-error"] = 500,
+	["conflict"] = 409
+}
+
+local function verify_signature(secret, body, signature)
+	if insecure then return true end
+	if not signature then return false end
+	local algo, digest = signature:match("^([^=]+)=(%x+)")
+	if not algo then return false end
+	local hmac = hmacs[algo]
+	if not algo then return false end
+	return hmac(secret, body) == from_hex(digest)
+end
+
+function handle_POST(event)
+	local request, response = event.request, event.response
+
+	if not verify_signature(forgejo_secret, request.body,
+	                        request.headers.x_hub_signature) then
+		module:log("debug", "Signature validation failed")
+		return 401
+	end
+
+	local data = json.decode(request.body)
+	if not data then
+		response.status_code = 400
+		return "Invalid JSON. From you of all people..."
+	end
+
+	local forgejo_event = request.headers.x_forgejo_event or data.object_kind
+
+	if skip_commitless_push and forgejo_event == "push" and data.total_commits == 0 then
+		module:log("debug", "Skipping push event with 0 commits")
+		return 501
+	end
+
+	if forgejo_templates[forgejo_event] == nil then
+		module:log("debug", "Unsupported forgejo event %q", forgejo_event)
+		return 501
+	end
+
+	local item = format(data, forgejo_templates[forgejo_event])
+
+	if item == nil then
+		module:log("debug", "Formatter returned nil for event %q", forgejo_event)
+		return 501
+	end
+
+	local node = default_node
+	if node_mapping then node = node_prefix .. data.repository[node_mapping] end
+
+	create_node(node)
+
+	local ok, err = pubsub_service:publish(node, forgejo_actor, item.attr.id, item)
+	if not ok then return error_mapping[err] or 500 end
+
+	response.status_code = 202
+	return "Thank you forgejo.\n" .. tostring(item:indent(1, " "))
+end
+
+module:provides("http", {route = {POST = handle_POST}})
+
+function create_node(node)
+	if not pubsub_service.nodes[node] then
+		local ok, err = pubsub_service:create(node, true)
+		if not ok then
+			module:log("error", "Error creating node: %s", err)
+		else
+			module:log("debug", "Node %q created", node)
+		end
+	end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_pubsub_forgejo/style.yaml	Mon Feb 17 23:28:05 2025 +0100
@@ -0,0 +1,2 @@
+indent_width: 1
+use_tab: true
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_pubsub_forgejo/templates.lib.lua	Mon Feb 17 23:28:05 2025 +0100
@@ -0,0 +1,22 @@
+local module = {
+	push = {
+		title = "{data.sender.username} pushed {data.total_commits} commit(s) to {data.repository.name}",
+		content = "{data.commits#- {item.message|firstline} ({item.author.name})\n}",
+		id = "push-{data.head_commit.id}",
+		link = "{data.repository.html_url}/compare/{data.before|shorten}..{data.after|shorten}"
+	},
+	pull_request = {
+		title = "{data.sender.username} {data.action} a pull request for {data.repository.name}",
+		content = "{data.pull_request.title}",
+		id = "pull-{data.pull_request.number}-{data.pull_request.updated_at}",
+		link = "{data.pull_request.html_url}"
+	},
+	release = {
+		title = "{data.sender.username} {data.action} a release for {data.repository.name}",
+		content = "{data.release.name}",
+		id = "release-{data.release.tag_name}",
+		link = "{data.release.html_url}"
+	}
+}
+
+return module
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_pubsub_forgejo/test.lua	Mon Feb 17 23:28:05 2025 +0100
@@ -0,0 +1,40 @@
+-- CLI script to ease templates writing
+-- must be launched with `lua test.lua` after setting the following env vars,
+-- (assuming prosody has been clone in ../../prosody-0.12)
+-- LUA_CPATH=../../prosody-0.12/\?.so
+-- LUA_PATH=../../prosody-0.12/\?.lua\;\?.lua
+-- allow loading ".lib.lua" modules
+local function loadlib(modulename)
+	local filename = modulename .. ".lib.lua"
+	local file = io.open(filename, "rb")
+	if file then
+		return load(file:read("a")), modulename
+	else
+		return filename .. " not found"
+	end
+end
+
+table.insert(package.searchers, loadlib)
+
+local json = require "util.json"
+local format = require "format"
+local templates = require "templates"
+
+local function read_json(fname)
+	local f = io.open(fname)
+	assert(f ~= nil, fname)
+	local data = json.decode(f:read("a"))
+	f:close()
+	return data
+end
+
+local function read_payload(dirname)
+	return read_json("./webhook-examples/" .. dirname .. "/content.json")
+end
+
+local function pprint(stanza) print(stanza:indent(1, "  "):pretty_print()) end
+
+pprint(format(read_payload("push"), templates.push))
+pprint(format(read_payload("pull_request"), templates.pull_request))
+-- pprint(format(read_payload("push_tag"), templates.push))  -- this is a push with 0 commits. It's ugly!
+pprint(format(read_payload("release"), templates.release))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_pubsub_forgejo/webhook-examples/create/content.json	Mon Feb 17 23:28:05 2025 +0100
@@ -0,0 +1,134 @@
+{
+  "sha": "4c87e2dfed00747a28d8602692ba3d73871790cb",
+  "ref": "refs/tags/v0.2.0",
+  "ref_type": "tag",
+  "repository": {
+    "id": 301855,
+    "owner": {
+      "id": 205842,
+      "login": "slidge",
+      "login_name": "",
+      "source_id": 0,
+      "full_name": "",
+      "email": "slidge@noreply.codeberg.org",
+      "avatar_url": "https://codeberg.org/avatars/aa0eeeb5fe173938bdc665be92bf605efa1e8f4a905bc3327e4d5c7eddf584e5",
+      "html_url": "https://codeberg.org/slidge",
+      "language": "",
+      "is_admin": false,
+      "last_login": "0001-01-01T00:00:00Z",
+      "created": "2024-08-30T14:16:14Z",
+      "restricted": false,
+      "active": false,
+      "prohibit_login": false,
+      "location": "",
+      "pronouns": "",
+      "website": "https://slidge.im",
+      "description": "",
+      "visibility": "public",
+      "followers_count": 5,
+      "following_count": 0,
+      "starred_repos_count": 0,
+      "username": "slidge"
+    },
+    "name": "slidgnal",
+    "full_name": "slidge/slidgnal",
+    "description": "A feature-rich Signal to XMPP gateway",
+    "empty": false,
+    "private": false,
+    "fork": false,
+    "template": false,
+    "parent": null,
+    "mirror": false,
+    "size": 471,
+    "language": "",
+    "languages_url": "https://codeberg.org/api/v1/repos/slidge/slidgnal/languages",
+    "html_url": "https://codeberg.org/slidge/slidgnal",
+    "url": "https://codeberg.org/api/v1/repos/slidge/slidgnal",
+    "link": "",
+    "ssh_url": "git@codeberg.org:slidge/slidgnal.git",
+    "clone_url": "https://codeberg.org/slidge/slidgnal.git",
+    "original_url": "",
+    "website": "",
+    "stars_count": 0,
+    "forks_count": 0,
+    "watchers_count": 6,
+    "open_issues_count": 5,
+    "open_pr_counter": 0,
+    "release_counter": 0,
+    "default_branch": "main",
+    "archived": false,
+    "created_at": "2025-01-10T14:37:52Z",
+    "updated_at": "2025-02-09T06:26:00Z",
+    "archived_at": "1970-01-01T00:00:00Z",
+    "permissions": {
+      "admin": false,
+      "push": false,
+      "pull": false
+    },
+    "has_issues": true,
+    "internal_tracker": {
+      "enable_time_tracker": true,
+      "allow_only_contributors_to_track_time": true,
+      "enable_issue_dependencies": true
+    },
+    "has_wiki": false,
+    "wiki_branch": "main",
+    "globally_editable_wiki": false,
+    "has_pull_requests": true,
+    "has_projects": false,
+    "has_releases": true,
+    "has_packages": true,
+    "has_actions": false,
+    "ignore_whitespace_conflicts": false,
+    "allow_merge_commits": true,
+    "allow_rebase": true,
+    "allow_rebase_explicit": true,
+    "allow_squash_merge": true,
+    "allow_fast_forward_only_merge": true,
+    "allow_rebase_update": true,
+    "default_delete_branch_after_merge": false,
+    "default_merge_style": "merge",
+    "default_allow_maintainer_edit": false,
+    "default_update_style": "merge",
+    "avatar_url": "",
+    "internal": false,
+    "mirror_interval": "",
+    "object_format_name": "sha1",
+    "mirror_updated": "0001-01-01T00:00:00Z",
+    "repo_transfer": null,
+    "topics": [
+      "bridge",
+      "gateway",
+      "instant-messaging",
+      "signal",
+      "slidge",
+      "xmpp"
+    ]
+  },
+  "sender": {
+    "id": 64076,
+    "login": "nicoco",
+    "login_name": "",
+    "source_id": 0,
+    "full_name": "",
+    "email": "nicoco@noreply.codeberg.org",
+    "avatar_url": "https://codeberg.org/avatars/32a918ca7a66e4e484ee2ccc625dc6451da728355c060ed0ed54fa69d89224a5",
+    "html_url": "https://codeberg.org/nicoco",
+    "language": "",
+    "is_admin": false,
+    "last_login": "0001-01-01T00:00:00Z",
+    "created": "2022-09-12T11:13:13Z",
+    "restricted": false,
+    "active": false,
+    "prohibit_login": false,
+    "location": "Nice, France",
+    "pronouns": "",
+    "website": "https://nicoco.fr",
+    "description": "wannabe-hacker",
+    "visibility": "public",
+    "followers_count": 2,
+    "following_count": 0,
+    "starred_repos_count": 4,
+    "username": "nicoco"
+  }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_pubsub_forgejo/webhook-examples/create/headers	Mon Feb 17 23:28:05 2025 +0100
@@ -0,0 +1,18 @@
+Content-Type: application/json
+X-Forgejo-Delivery: 2e3badae-779d-4627-94c7-505deeee8b55
+X-Forgejo-Event: create
+X-Forgejo-Event-Type: create
+X-Forgejo-Signature: 99f6adf0a156c379057735690dcfae3f483a026dde78ace6252b3da60b0e6bad
+X-GitHub-Delivery: 2e3badae-779d-4627-94c7-505deeee8b55
+X-GitHub-Event: create
+X-GitHub-Event-Type: create
+X-Gitea-Delivery: 2e3badae-779d-4627-94c7-505deeee8b55
+X-Gitea-Event: create
+X-Gitea-Event-Type: create
+X-Gitea-Signature: 99f6adf0a156c379057735690dcfae3f483a026dde78ace6252b3da60b0e6bad
+X-Gogs-Delivery: 2e3badae-779d-4627-94c7-505deeee8b55
+X-Gogs-Event: create
+X-Gogs-Event-Type: create
+X-Gogs-Signature: 99f6adf0a156c379057735690dcfae3f483a026dde78ace6252b3da60b0e6bad
+X-Hub-Signature: sha1=a6edaf56463683b675cbc3a081292369a143c2c4
+X-Hub-Signature-256: sha256=99f6adf0a156c379057735690dcfae3f483a026dde78ace6252b3da60b0e6bad
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_pubsub_forgejo/webhook-examples/pull_request/content.json	Mon Feb 17 23:28:05 2025 +0100
@@ -0,0 +1,501 @@
+{
+  "action": "opened",
+  "number": 38,
+  "pull_request": {
+    "id": 270810,
+    "url": "https://codeberg.org/slidge/slidge/pulls/38",
+    "number": 38,
+    "user": {
+      "id": 64076,
+      "login": "nicoco",
+      "login_name": "",
+      "source_id": 0,
+      "full_name": "",
+      "email": "nicoco@noreply.codeberg.org",
+      "avatar_url": "https://codeberg.org/avatars/32a918ca7a66e4e484ee2ccc625dc6451da728355c060ed0ed54fa69d89224a5",
+      "html_url": "https://codeberg.org/nicoco",
+      "language": "",
+      "is_admin": false,
+      "last_login": "0001-01-01T00:00:00Z",
+      "created": "2022-09-12T11:13:13Z",
+      "restricted": false,
+      "active": false,
+      "prohibit_login": false,
+      "location": "Nice, France",
+      "pronouns": "",
+      "website": "https://nicoco.fr",
+      "description": "wannabe-hacker",
+      "visibility": "public",
+      "followers_count": 1,
+      "following_count": 0,
+      "starred_repos_count": 4,
+      "username": "nicoco"
+    },
+    "title": "dev",
+    "body": "test\r\n\r\nblabla",
+    "labels": [],
+    "milestone": null,
+    "assignee": null,
+    "assignees": null,
+    "requested_reviewers": null,
+    "requested_reviewers_teams": null,
+    "state": "open",
+    "draft": false,
+    "is_locked": false,
+    "comments": 0,
+    "review_comments": 0,
+    "additions": 338,
+    "deletions": 211,
+    "changed_files": 10,
+    "html_url": "https://codeberg.org/slidge/slidge/pulls/38",
+    "diff_url": "https://codeberg.org/slidge/slidge/pulls/38.diff",
+    "patch_url": "https://codeberg.org/slidge/slidge/pulls/38.patch",
+    "mergeable": true,
+    "merged": false,
+    "merged_at": null,
+    "merge_commit_sha": null,
+    "merged_by": null,
+    "allow_maintainer_edit": true,
+    "base": {
+      "label": "main",
+      "ref": "main",
+      "sha": "84bdc13530ef5fdd001eb55eb4ff9a4545fd1182",
+      "repo_id": 298187,
+      "repo": {
+        "id": 298187,
+        "owner": {
+          "id": 205842,
+          "login": "slidge",
+          "login_name": "",
+          "source_id": 0,
+          "full_name": "",
+          "email": "",
+          "avatar_url": "https://codeberg.org/avatars/aa0eeeb5fe173938bdc665be92bf605efa1e8f4a905bc3327e4d5c7eddf584e5",
+          "html_url": "https://codeberg.org/slidge",
+          "language": "",
+          "is_admin": false,
+          "last_login": "0001-01-01T00:00:00Z",
+          "created": "2024-08-30T14:16:14Z",
+          "restricted": false,
+          "active": false,
+          "prohibit_login": false,
+          "location": "",
+          "pronouns": "",
+          "website": "https://slidge.im",
+          "description": "",
+          "visibility": "public",
+          "followers_count": 3,
+          "following_count": 0,
+          "starred_repos_count": 0,
+          "username": "slidge"
+        },
+        "name": "slidge",
+        "full_name": "slidge/slidge",
+        "description": "An XMPP/other chat networks gateway framework in python",
+        "empty": false,
+        "private": false,
+        "fork": false,
+        "template": false,
+        "parent": null,
+        "mirror": false,
+        "size": 7536,
+        "language": "",
+        "languages_url": "https://codeberg.org/api/v1/repos/slidge/slidge/languages",
+        "html_url": "https://codeberg.org/slidge/slidge",
+        "url": "https://codeberg.org/api/v1/repos/slidge/slidge",
+        "link": "",
+        "ssh_url": "git@codeberg.org:slidge/slidge.git",
+        "clone_url": "https://codeberg.org/slidge/slidge.git",
+        "original_url": "",
+        "website": "https://slidge.im/",
+        "stars_count": 1,
+        "forks_count": 1,
+        "watchers_count": 4,
+        "open_issues_count": 26,
+        "open_pr_counter": 0,
+        "release_counter": 0,
+        "default_branch": "main",
+        "archived": false,
+        "created_at": "2025-01-02T05:57:37Z",
+        "updated_at": "2025-01-16T10:44:09Z",
+        "archived_at": "1970-01-01T00:00:00Z",
+        "permissions": {
+          "admin": false,
+          "push": false,
+          "pull": true
+        },
+        "has_issues": true,
+        "internal_tracker": {
+          "enable_time_tracker": true,
+          "allow_only_contributors_to_track_time": true,
+          "enable_issue_dependencies": true
+        },
+        "has_wiki": false,
+        "wiki_branch": "main",
+        "globally_editable_wiki": false,
+        "has_pull_requests": true,
+        "has_projects": false,
+        "has_releases": true,
+        "has_packages": true,
+        "has_actions": false,
+        "ignore_whitespace_conflicts": false,
+        "allow_merge_commits": true,
+        "allow_rebase": true,
+        "allow_rebase_explicit": true,
+        "allow_squash_merge": true,
+        "allow_fast_forward_only_merge": true,
+        "allow_rebase_update": true,
+        "default_delete_branch_after_merge": true,
+        "default_merge_style": "rebase",
+        "default_allow_maintainer_edit": true,
+        "avatar_url": "",
+        "internal": false,
+        "mirror_interval": "",
+        "object_format_name": "sha1",
+        "mirror_updated": "0001-01-01T00:00:00Z",
+        "repo_transfer": null,
+        "topics": [
+          "bridge",
+          "chat",
+          "gateway",
+          "instant-messaging",
+          "python",
+          "xmpp"
+        ]
+      }
+    },
+    "head": {
+      "label": "dev",
+      "ref": "dev",
+      "sha": "333f86a71bc5dbcf27de4dfaf9006890b86dee34",
+      "repo_id": 303404,
+      "repo": {
+        "id": 303404,
+        "owner": {
+          "id": 64076,
+          "login": "nicoco",
+          "login_name": "",
+          "source_id": 0,
+          "full_name": "",
+          "email": "nicoco@noreply.codeberg.org",
+          "avatar_url": "https://codeberg.org/avatars/32a918ca7a66e4e484ee2ccc625dc6451da728355c060ed0ed54fa69d89224a5",
+          "html_url": "https://codeberg.org/nicoco",
+          "language": "",
+          "is_admin": false,
+          "last_login": "0001-01-01T00:00:00Z",
+          "created": "2022-09-12T11:13:13Z",
+          "restricted": false,
+          "active": false,
+          "prohibit_login": false,
+          "location": "Nice, France",
+          "pronouns": "",
+          "website": "https://nicoco.fr",
+          "description": "wannabe-hacker",
+          "visibility": "public",
+          "followers_count": 1,
+          "following_count": 0,
+          "starred_repos_count": 4,
+          "username": "nicoco"
+        },
+        "name": "slidge",
+        "full_name": "nicoco/slidge",
+        "description": "An XMPP/other chat networks framework in python",
+        "empty": false,
+        "private": false,
+        "fork": true,
+        "template": false,
+        "parent": {
+          "id": 298187,
+          "owner": {
+            "id": 205842,
+            "login": "slidge",
+            "login_name": "",
+            "source_id": 0,
+            "full_name": "",
+            "email": "",
+            "avatar_url": "https://codeberg.org/avatars/aa0eeeb5fe173938bdc665be92bf605efa1e8f4a905bc3327e4d5c7eddf584e5",
+            "html_url": "https://codeberg.org/slidge",
+            "language": "",
+            "is_admin": false,
+            "last_login": "0001-01-01T00:00:00Z",
+            "created": "2024-08-30T14:16:14Z",
+            "restricted": false,
+            "active": false,
+            "prohibit_login": false,
+            "location": "",
+            "pronouns": "",
+            "website": "https://slidge.im",
+            "description": "",
+            "visibility": "public",
+            "followers_count": 3,
+            "following_count": 0,
+            "starred_repos_count": 0,
+            "username": "slidge"
+          },
+          "name": "slidge",
+          "full_name": "slidge/slidge",
+          "description": "An XMPP/other chat networks gateway framework in python",
+          "empty": false,
+          "private": false,
+          "fork": false,
+          "template": false,
+          "parent": null,
+          "mirror": false,
+          "size": 7536,
+          "language": "",
+          "languages_url": "https://codeberg.org/api/v1/repos/slidge/slidge/languages",
+          "html_url": "https://codeberg.org/slidge/slidge",
+          "url": "https://codeberg.org/api/v1/repos/slidge/slidge",
+          "link": "",
+          "ssh_url": "git@codeberg.org:slidge/slidge.git",
+          "clone_url": "https://codeberg.org/slidge/slidge.git",
+          "original_url": "",
+          "website": "https://slidge.im/",
+          "stars_count": 1,
+          "forks_count": 1,
+          "watchers_count": 4,
+          "open_issues_count": 26,
+          "open_pr_counter": 1,
+          "release_counter": 0,
+          "default_branch": "main",
+          "archived": false,
+          "created_at": "2025-01-02T05:57:37Z",
+          "updated_at": "2025-01-16T10:44:09Z",
+          "archived_at": "1970-01-01T00:00:00Z",
+          "permissions": {
+            "admin": false,
+            "push": false,
+            "pull": true
+          },
+          "has_issues": true,
+          "internal_tracker": {
+            "enable_time_tracker": true,
+            "allow_only_contributors_to_track_time": true,
+            "enable_issue_dependencies": true
+          },
+          "has_wiki": false,
+          "wiki_branch": "main",
+          "globally_editable_wiki": false,
+          "has_pull_requests": true,
+          "has_projects": false,
+          "has_releases": true,
+          "has_packages": true,
+          "has_actions": false,
+          "ignore_whitespace_conflicts": false,
+          "allow_merge_commits": true,
+          "allow_rebase": true,
+          "allow_rebase_explicit": true,
+          "allow_squash_merge": true,
+          "allow_fast_forward_only_merge": true,
+          "allow_rebase_update": true,
+          "default_delete_branch_after_merge": true,
+          "default_merge_style": "rebase",
+          "default_allow_maintainer_edit": true,
+          "avatar_url": "",
+          "internal": false,
+          "mirror_interval": "",
+          "object_format_name": "sha1",
+          "mirror_updated": "0001-01-01T00:00:00Z",
+          "repo_transfer": null,
+          "topics": [
+            "bridge",
+            "chat",
+            "gateway",
+            "instant-messaging",
+            "python",
+            "xmpp"
+          ]
+        },
+        "mirror": false,
+        "size": 6562,
+        "language": "",
+        "languages_url": "https://codeberg.org/api/v1/repos/nicoco/slidge/languages",
+        "html_url": "https://codeberg.org/nicoco/slidge",
+        "url": "https://codeberg.org/api/v1/repos/nicoco/slidge",
+        "link": "",
+        "ssh_url": "git@codeberg.org:nicoco/slidge.git",
+        "clone_url": "https://codeberg.org/nicoco/slidge.git",
+        "original_url": "",
+        "website": "",
+        "stars_count": 0,
+        "forks_count": 0,
+        "watchers_count": 1,
+        "open_issues_count": 0,
+        "open_pr_counter": 0,
+        "release_counter": 0,
+        "default_branch": "main",
+        "archived": false,
+        "created_at": "2025-01-14T15:21:10Z",
+        "updated_at": "2025-01-17T14:04:37Z",
+        "archived_at": "1970-01-01T00:00:00Z",
+        "permissions": {
+          "admin": false,
+          "push": false,
+          "pull": true
+        },
+        "has_issues": false,
+        "has_wiki": false,
+        "globally_editable_wiki": false,
+        "has_pull_requests": true,
+        "has_projects": false,
+        "has_releases": false,
+        "has_packages": false,
+        "has_actions": false,
+        "ignore_whitespace_conflicts": false,
+        "allow_merge_commits": true,
+        "allow_rebase": true,
+        "allow_rebase_explicit": true,
+        "allow_squash_merge": true,
+        "allow_fast_forward_only_merge": true,
+        "allow_rebase_update": true,
+        "default_delete_branch_after_merge": false,
+        "default_merge_style": "merge",
+        "default_allow_maintainer_edit": false,
+        "avatar_url": "",
+        "internal": false,
+        "mirror_interval": "",
+        "object_format_name": "sha1",
+        "mirror_updated": "0001-01-01T00:00:00Z",
+        "repo_transfer": null,
+        "topics": null
+      }
+    },
+    "merge_base": "84bdc13530ef5fdd001eb55eb4ff9a4545fd1182",
+    "due_date": null,
+    "created_at": "2025-01-17T14:04:59Z",
+    "updated_at": "2025-01-17T14:04:59Z",
+    "closed_at": null,
+    "pin_order": 0
+  },
+  "requested_reviewer": null,
+  "repository": {
+    "id": 298187,
+    "owner": {
+      "id": 205842,
+      "login": "slidge",
+      "login_name": "",
+      "source_id": 0,
+      "full_name": "",
+      "email": "",
+      "avatar_url": "https://codeberg.org/avatars/aa0eeeb5fe173938bdc665be92bf605efa1e8f4a905bc3327e4d5c7eddf584e5",
+      "html_url": "https://codeberg.org/slidge",
+      "language": "",
+      "is_admin": false,
+      "last_login": "0001-01-01T00:00:00Z",
+      "created": "2024-08-30T14:16:14Z",
+      "restricted": false,
+      "active": false,
+      "prohibit_login": false,
+      "location": "",
+      "pronouns": "",
+      "website": "https://slidge.im",
+      "description": "",
+      "visibility": "public",
+      "followers_count": 3,
+      "following_count": 0,
+      "starred_repos_count": 0,
+      "username": "slidge"
+    },
+    "name": "slidge",
+    "full_name": "slidge/slidge",
+    "description": "An XMPP/other chat networks gateway framework in python",
+    "empty": false,
+    "private": false,
+    "fork": false,
+    "template": false,
+    "parent": null,
+    "mirror": false,
+    "size": 7536,
+    "language": "",
+    "languages_url": "https://codeberg.org/api/v1/repos/slidge/slidge/languages",
+    "html_url": "https://codeberg.org/slidge/slidge",
+    "url": "https://codeberg.org/api/v1/repos/slidge/slidge",
+    "link": "",
+    "ssh_url": "git@codeberg.org:slidge/slidge.git",
+    "clone_url": "https://codeberg.org/slidge/slidge.git",
+    "original_url": "",
+    "website": "https://slidge.im/",
+    "stars_count": 1,
+    "forks_count": 1,
+    "watchers_count": 4,
+    "open_issues_count": 26,
+    "open_pr_counter": 0,
+    "release_counter": 0,
+    "default_branch": "main",
+    "archived": false,
+    "created_at": "2025-01-02T05:57:37Z",
+    "updated_at": "2025-01-16T10:44:09Z",
+    "archived_at": "1970-01-01T00:00:00Z",
+    "permissions": {
+      "admin": true,
+      "push": true,
+      "pull": true
+    },
+    "has_issues": true,
+    "internal_tracker": {
+      "enable_time_tracker": true,
+      "allow_only_contributors_to_track_time": true,
+      "enable_issue_dependencies": true
+    },
+    "has_wiki": false,
+    "wiki_branch": "main",
+    "globally_editable_wiki": false,
+    "has_pull_requests": true,
+    "has_projects": false,
+    "has_releases": true,
+    "has_packages": true,
+    "has_actions": false,
+    "ignore_whitespace_conflicts": false,
+    "allow_merge_commits": true,
+    "allow_rebase": true,
+    "allow_rebase_explicit": true,
+    "allow_squash_merge": true,
+    "allow_fast_forward_only_merge": true,
+    "allow_rebase_update": true,
+    "default_delete_branch_after_merge": true,
+    "default_merge_style": "rebase",
+    "default_allow_maintainer_edit": true,
+    "avatar_url": "",
+    "internal": false,
+    "mirror_interval": "",
+    "object_format_name": "sha1",
+    "mirror_updated": "0001-01-01T00:00:00Z",
+    "repo_transfer": null,
+    "topics": [
+      "bridge",
+      "chat",
+      "gateway",
+      "instant-messaging",
+      "python",
+      "xmpp"
+    ]
+  },
+  "sender": {
+    "id": 64076,
+    "login": "nicoco",
+    "login_name": "",
+    "source_id": 0,
+    "full_name": "",
+    "email": "nicoco@noreply.codeberg.org",
+    "avatar_url": "https://codeberg.org/avatars/32a918ca7a66e4e484ee2ccc625dc6451da728355c060ed0ed54fa69d89224a5",
+    "html_url": "https://codeberg.org/nicoco",
+    "language": "",
+    "is_admin": false,
+    "last_login": "0001-01-01T00:00:00Z",
+    "created": "2022-09-12T11:13:13Z",
+    "restricted": false,
+    "active": false,
+    "prohibit_login": false,
+    "location": "Nice, France",
+    "pronouns": "",
+    "website": "https://nicoco.fr",
+    "description": "wannabe-hacker",
+    "visibility": "public",
+    "followers_count": 1,
+    "following_count": 0,
+    "starred_repos_count": 4,
+    "username": "nicoco"
+  },
+  "commit_id": "",
+  "review": null
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_pubsub_forgejo/webhook-examples/pull_request/headers	Mon Feb 17 23:28:05 2025 +0100
@@ -0,0 +1,18 @@
+Content-Type: application/json
+X-Forgejo-Delivery: d9ef04ca-cba6-4b43-bd81-81f7b10a3415
+X-Forgejo-Event: pull_request
+X-Forgejo-Event-Type: pull_request
+X-Forgejo-Signature: 7963b65afbfaae02376cb5f44c4534bd28b34000e2c410badd1e3ebd922dbe4b
+X-GitHub-Delivery: d9ef04ca-cba6-4b43-bd81-81f7b10a3415
+X-GitHub-Event: pull_request
+X-GitHub-Event-Type: pull_request
+X-Gitea-Delivery: d9ef04ca-cba6-4b43-bd81-81f7b10a3415
+X-Gitea-Event: pull_request
+X-Gitea-Event-Type: pull_request
+X-Gitea-Signature: 7963b65afbfaae02376cb5f44c4534bd28b34000e2c410badd1e3ebd922dbe4b
+X-Gogs-Delivery: d9ef04ca-cba6-4b43-bd81-81f7b10a3415
+X-Gogs-Event: pull_request
+X-Gogs-Event-Type: pull_request
+X-Gogs-Signature: 7963b65afbfaae02376cb5f44c4534bd28b34000e2c410badd1e3ebd922dbe4b
+X-Hub-Signature: sha1=57534fff7c23b977b2be006e66797faf31a1021b
+X-Hub-Signature-256: sha256=7963b65afbfaae02376cb5f44c4534bd28b34000e2c410badd1e3ebd922dbe4b
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_pubsub_forgejo/webhook-examples/pull_request_comment/content.json	Mon Feb 17 23:28:05 2025 +0100
@@ -0,0 +1,232 @@
+{
+  "action": "created",
+  "issue": {
+    "id": 949460,
+    "url": "https://codeberg.org/api/v1/repos/slidge/slidge/issues/38",
+    "html_url": "https://codeberg.org/slidge/slidge/pulls/38",
+    "number": 38,
+    "user": {
+      "id": 64076,
+      "login": "nicoco",
+      "login_name": "",
+      "source_id": 0,
+      "full_name": "",
+      "email": "nicoco@nicoco.fr",
+      "avatar_url": "https://codeberg.org/avatars/32a918ca7a66e4e484ee2ccc625dc6451da728355c060ed0ed54fa69d89224a5",
+      "html_url": "https://codeberg.org/nicoco",
+      "language": "en-US",
+      "is_admin": false,
+      "last_login": "2025-01-16T17:43:26Z",
+      "created": "2022-09-12T11:13:13Z",
+      "restricted": false,
+      "active": true,
+      "prohibit_login": false,
+      "location": "Nice, France",
+      "pronouns": "",
+      "website": "https://nicoco.fr",
+      "description": "wannabe-hacker",
+      "visibility": "public",
+      "followers_count": 1,
+      "following_count": 0,
+      "starred_repos_count": 4,
+      "username": "nicoco"
+    },
+    "original_author": "",
+    "original_author_id": 0,
+    "title": "dev",
+    "body": "test\r\n\r\nblabla",
+    "ref": "",
+    "assets": [],
+    "labels": [],
+    "milestone": null,
+    "assignee": null,
+    "assignees": null,
+    "state": "open",
+    "is_locked": false,
+    "comments": 0,
+    "created_at": "2025-01-17T14:04:59Z",
+    "updated_at": "2025-01-17T14:52:25Z",
+    "closed_at": null,
+    "due_date": null,
+    "pull_request": {
+      "merged": false,
+      "merged_at": null,
+      "draft": false,
+      "html_url": "https://codeberg.org/slidge/slidge/pulls/38"
+    },
+    "repository": {
+      "id": 298187,
+      "name": "slidge",
+      "owner": "slidge",
+      "full_name": "slidge/slidge"
+    },
+    "pin_order": 0
+  },
+  "comment": {
+    "id": 2592486,
+    "html_url": "https://codeberg.org/slidge/slidge/pulls/38#issuecomment-2592486",
+    "pull_request_url": "https://codeberg.org/slidge/slidge/pulls/38",
+    "issue_url": "",
+    "user": {
+      "id": 64076,
+      "login": "nicoco",
+      "login_name": "",
+      "source_id": 0,
+      "full_name": "",
+      "email": "nicoco@noreply.codeberg.org",
+      "avatar_url": "https://codeberg.org/avatars/32a918ca7a66e4e484ee2ccc625dc6451da728355c060ed0ed54fa69d89224a5",
+      "html_url": "https://codeberg.org/nicoco",
+      "language": "",
+      "is_admin": false,
+      "last_login": "0001-01-01T00:00:00Z",
+      "created": "2022-09-12T11:13:13Z",
+      "restricted": false,
+      "active": false,
+      "prohibit_login": false,
+      "location": "Nice, France",
+      "pronouns": "",
+      "website": "https://nicoco.fr",
+      "description": "wannabe-hacker",
+      "visibility": "public",
+      "followers_count": 1,
+      "following_count": 0,
+      "starred_repos_count": 4,
+      "username": "nicoco"
+    },
+    "original_author": "",
+    "original_author_id": 0,
+    "body": "a comment",
+    "assets": [],
+    "created_at": "2025-01-17T14:52:25Z",
+    "updated_at": "2025-01-17T14:52:25Z"
+  },
+  "repository": {
+    "id": 298187,
+    "owner": {
+      "id": 205842,
+      "login": "slidge",
+      "login_name": "",
+      "source_id": 0,
+      "full_name": "",
+      "email": "",
+      "avatar_url": "https://codeberg.org/avatars/aa0eeeb5fe173938bdc665be92bf605efa1e8f4a905bc3327e4d5c7eddf584e5",
+      "html_url": "https://codeberg.org/slidge",
+      "language": "",
+      "is_admin": false,
+      "last_login": "0001-01-01T00:00:00Z",
+      "created": "2024-08-30T14:16:14Z",
+      "restricted": false,
+      "active": false,
+      "prohibit_login": false,
+      "location": "",
+      "pronouns": "",
+      "website": "https://slidge.im",
+      "description": "",
+      "visibility": "public",
+      "followers_count": 3,
+      "following_count": 0,
+      "starred_repos_count": 0,
+      "username": "slidge"
+    },
+    "name": "slidge",
+    "full_name": "slidge/slidge",
+    "description": "An XMPP/other chat networks gateway framework in python",
+    "empty": false,
+    "private": false,
+    "fork": false,
+    "template": false,
+    "parent": null,
+    "mirror": false,
+    "size": 7536,
+    "language": "",
+    "languages_url": "https://codeberg.org/api/v1/repos/slidge/slidge/languages",
+    "html_url": "https://codeberg.org/slidge/slidge",
+    "url": "https://codeberg.org/api/v1/repos/slidge/slidge",
+    "link": "",
+    "ssh_url": "git@codeberg.org:slidge/slidge.git",
+    "clone_url": "https://codeberg.org/slidge/slidge.git",
+    "original_url": "",
+    "website": "https://slidge.im/",
+    "stars_count": 1,
+    "forks_count": 1,
+    "watchers_count": 4,
+    "open_issues_count": 26,
+    "open_pr_counter": 1,
+    "release_counter": 0,
+    "default_branch": "main",
+    "archived": false,
+    "created_at": "2025-01-02T05:57:37Z",
+    "updated_at": "2025-01-16T10:44:09Z",
+    "archived_at": "1970-01-01T00:00:00Z",
+    "permissions": {
+      "admin": true,
+      "push": true,
+      "pull": true
+    },
+    "has_issues": true,
+    "internal_tracker": {
+      "enable_time_tracker": true,
+      "allow_only_contributors_to_track_time": true,
+      "enable_issue_dependencies": true
+    },
+    "has_wiki": false,
+    "wiki_branch": "main",
+    "globally_editable_wiki": false,
+    "has_pull_requests": true,
+    "has_projects": false,
+    "has_releases": true,
+    "has_packages": true,
+    "has_actions": false,
+    "ignore_whitespace_conflicts": false,
+    "allow_merge_commits": true,
+    "allow_rebase": true,
+    "allow_rebase_explicit": true,
+    "allow_squash_merge": true,
+    "allow_fast_forward_only_merge": true,
+    "allow_rebase_update": true,
+    "default_delete_branch_after_merge": true,
+    "default_merge_style": "rebase",
+    "default_allow_maintainer_edit": true,
+    "avatar_url": "",
+    "internal": false,
+    "mirror_interval": "",
+    "object_format_name": "sha1",
+    "mirror_updated": "0001-01-01T00:00:00Z",
+    "repo_transfer": null,
+    "topics": [
+      "bridge",
+      "chat",
+      "gateway",
+      "instant-messaging",
+      "python",
+      "xmpp"
+    ]
+  },
+  "sender": {
+    "id": 64076,
+    "login": "nicoco",
+    "login_name": "",
+    "source_id": 0,
+    "full_name": "",
+    "email": "nicoco@noreply.codeberg.org",
+    "avatar_url": "https://codeberg.org/avatars/32a918ca7a66e4e484ee2ccc625dc6451da728355c060ed0ed54fa69d89224a5",
+    "html_url": "https://codeberg.org/nicoco",
+    "language": "",
+    "is_admin": false,
+    "last_login": "0001-01-01T00:00:00Z",
+    "created": "2022-09-12T11:13:13Z",
+    "restricted": false,
+    "active": false,
+    "prohibit_login": false,
+    "location": "Nice, France",
+    "pronouns": "",
+    "website": "https://nicoco.fr",
+    "description": "wannabe-hacker",
+    "visibility": "public",
+    "followers_count": 1,
+    "following_count": 0,
+    "starred_repos_count": 4,
+    "username": "nicoco"
+  },
+  "is_pull": true
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_pubsub_forgejo/webhook-examples/pull_request_comment/headers	Mon Feb 17 23:28:05 2025 +0100
@@ -0,0 +1,18 @@
+Content-Type: application/json
+X-Forgejo-Delivery: 99fb308a-ed68-46d7-a8b5-4bf935ae294f
+X-Forgejo-Event: issue_comment
+X-Forgejo-Event-Type: pull_request_comment
+X-Forgejo-Signature: 5fc374c851a20a0e18b65d32d9901c5f962cce950452cebc81bb5d6b0c023123
+X-GitHub-Delivery: 99fb308a-ed68-46d7-a8b5-4bf935ae294f
+X-GitHub-Event: issue_comment
+X-GitHub-Event-Type: pull_request_comment
+X-Gitea-Delivery: 99fb308a-ed68-46d7-a8b5-4bf935ae294f
+X-Gitea-Event: issue_comment
+X-Gitea-Event-Type: pull_request_comment
+X-Gitea-Signature: 5fc374c851a20a0e18b65d32d9901c5f962cce950452cebc81bb5d6b0c023123
+X-Gogs-Delivery: 99fb308a-ed68-46d7-a8b5-4bf935ae294f
+X-Gogs-Event: issue_comment
+X-Gogs-Event-Type: pull_request_comment
+X-Gogs-Signature: 5fc374c851a20a0e18b65d32d9901c5f962cce950452cebc81bb5d6b0c023123
+X-Hub-Signature: sha1=ea62c579c31ae99001c1e9b769d4a71e4dbc4b7f
+X-Hub-Signature-256: sha256=5fc374c851a20a0e18b65d32d9901c5f962cce950452cebc81bb5d6b0c023123
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_pubsub_forgejo/webhook-examples/pull_request_rejected/content.json	Mon Feb 17 23:28:05 2025 +0100
@@ -0,0 +1,468 @@
+{
+  "action": "reviewed",
+  "number": 3591,
+  "pull_request": {
+    "id": 289137,
+    "url": "https://codeberg.org/poezio/slixmpp/pulls/3591",
+    "number": 3591,
+    "user": {
+      "id": 102461,
+      "login": "mathieui",
+      "login_name": "",
+      "source_id": 0,
+      "full_name": "",
+      "email": "mathieui@noreply.codeberg.org",
+      "avatar_url": "https://codeberg.org/avatars/921cd14ed81b453b5eab40840af709a256e4fcb2d0c19ff47a7a33948978c3c4",
+      "html_url": "https://codeberg.org/mathieui",
+      "language": "",
+      "is_admin": false,
+      "last_login": "0001-01-01T00:00:00Z",
+      "created": "2023-06-17T14:34:13Z",
+      "restricted": false,
+      "active": false,
+      "prohibit_login": false,
+      "location": "",
+      "pronouns": "",
+      "website": "",
+      "description": "",
+      "visibility": "public",
+      "followers_count": 1,
+      "following_count": 0,
+      "starred_repos_count": 1,
+      "username": "mathieui"
+    },
+    "title": "XEP-0410: add support",
+    "body": "This commit introduces an automated monitoring of MUC presence based on\r\nXEP-0410. An invisible background job is tasked with pinging inactive\r\nMUCs.",
+    "labels": [],
+    "milestone": null,
+    "assignee": null,
+    "assignees": null,
+    "requested_reviewers": [
+      {
+        "id": 64076,
+        "login": "nicoco",
+        "login_name": "",
+        "source_id": 0,
+        "full_name": "",
+        "email": "nicoco@noreply.codeberg.org",
+        "avatar_url": "https://codeberg.org/avatars/32a918ca7a66e4e484ee2ccc625dc6451da728355c060ed0ed54fa69d89224a5",
+        "html_url": "https://codeberg.org/nicoco",
+        "language": "",
+        "is_admin": false,
+        "last_login": "0001-01-01T00:00:00Z",
+        "created": "2022-09-12T11:13:13Z",
+        "restricted": false,
+        "active": false,
+        "prohibit_login": false,
+        "location": "Nice, France",
+        "pronouns": "",
+        "website": "https://nicoco.fr",
+        "description": "wannabe-hacker",
+        "visibility": "public",
+        "followers_count": 2,
+        "following_count": 0,
+        "starred_repos_count": 4,
+        "username": "nicoco"
+      }
+    ],
+    "requested_reviewers_teams": null,
+    "state": "open",
+    "draft": false,
+    "is_locked": false,
+    "comments": 0,
+    "review_comments": 1,
+    "additions": 407,
+    "deletions": 0,
+    "changed_files": 7,
+    "html_url": "https://codeberg.org/poezio/slixmpp/pulls/3591",
+    "diff_url": "https://codeberg.org/poezio/slixmpp/pulls/3591.diff",
+    "patch_url": "https://codeberg.org/poezio/slixmpp/pulls/3591.patch",
+    "mergeable": true,
+    "merged": false,
+    "merged_at": null,
+    "merge_commit_sha": null,
+    "merged_by": null,
+    "allow_maintainer_edit": false,
+    "base": {
+      "label": "master",
+      "ref": "master",
+      "sha": "e344947180bbfd667ac572176727f799349815db",
+      "repo_id": 123634,
+      "repo": {
+        "id": 123634,
+        "owner": {
+          "id": 102463,
+          "login": "poezio",
+          "login_name": "",
+          "source_id": 0,
+          "full_name": "",
+          "email": "",
+          "avatar_url": "https://codeberg.org/avatars/b9b08e0933aaa3579fb616166718292652ae8b70457e50de3a46c1e412f99631",
+          "html_url": "https://codeberg.org/poezio",
+          "language": "",
+          "is_admin": false,
+          "last_login": "0001-01-01T00:00:00Z",
+          "created": "2023-06-17T14:35:46Z",
+          "restricted": false,
+          "active": false,
+          "prohibit_login": false,
+          "location": "cyberspace",
+          "pronouns": "",
+          "website": "",
+          "description": "Organization for poezio \u0026 slixmpp",
+          "visibility": "public",
+          "followers_count": 3,
+          "following_count": 0,
+          "starred_repos_count": 0,
+          "username": "poezio"
+        },
+        "name": "slixmpp",
+        "full_name": "poezio/slixmpp",
+        "description": "Modern python XMPP library using asyncio.",
+        "empty": false,
+        "private": false,
+        "fork": false,
+        "template": false,
+        "parent": null,
+        "mirror": false,
+        "size": 12914,
+        "language": "",
+        "languages_url": "https://codeberg.org/api/v1/repos/poezio/slixmpp/languages",
+        "html_url": "https://codeberg.org/poezio/slixmpp",
+        "url": "https://codeberg.org/api/v1/repos/poezio/slixmpp",
+        "link": "",
+        "ssh_url": "git@codeberg.org:poezio/slixmpp.git",
+        "clone_url": "https://codeberg.org/poezio/slixmpp.git",
+        "original_url": "https://lab.louiz.org/poezio/slixmpp",
+        "website": "",
+        "stars_count": 14,
+        "forks_count": 9,
+        "watchers_count": 6,
+        "open_issues_count": 51,
+        "open_pr_counter": 12,
+        "release_counter": 10,
+        "default_branch": "master",
+        "archived": false,
+        "created_at": "2023-07-06T09:10:25Z",
+        "updated_at": "2025-02-13T22:17:56Z",
+        "archived_at": "1970-01-01T00:00:00Z",
+        "permissions": {
+          "admin": true,
+          "push": true,
+          "pull": true
+        },
+        "has_issues": true,
+        "internal_tracker": {
+          "enable_time_tracker": true,
+          "allow_only_contributors_to_track_time": true,
+          "enable_issue_dependencies": true
+        },
+        "has_wiki": true,
+        "wiki_branch": "master",
+        "globally_editable_wiki": false,
+        "has_pull_requests": true,
+        "has_projects": true,
+        "has_releases": true,
+        "has_packages": true,
+        "has_actions": false,
+        "ignore_whitespace_conflicts": false,
+        "allow_merge_commits": true,
+        "allow_rebase": true,
+        "allow_rebase_explicit": true,
+        "allow_squash_merge": true,
+        "allow_fast_forward_only_merge": false,
+        "allow_rebase_update": true,
+        "default_delete_branch_after_merge": false,
+        "default_merge_style": "merge",
+        "default_allow_maintainer_edit": false,
+        "default_update_style": "merge",
+        "avatar_url": "",
+        "internal": false,
+        "mirror_interval": "",
+        "object_format_name": "sha1",
+        "mirror_updated": "0001-01-01T00:00:00Z",
+        "repo_transfer": null,
+        "topics": [
+          "python",
+          "library",
+          "xmpp",
+          "jabber",
+          "asyncio"
+        ]
+      }
+    },
+    "head": {
+      "label": "add-xep-0410",
+      "ref": "add-xep-0410",
+      "sha": "d768dfdc4b2f52570adfd0cdfc5480eafd5b258b",
+      "repo_id": 123634,
+      "repo": {
+        "id": 123634,
+        "owner": {
+          "id": 102463,
+          "login": "poezio",
+          "login_name": "",
+          "source_id": 0,
+          "full_name": "",
+          "email": "",
+          "avatar_url": "https://codeberg.org/avatars/b9b08e0933aaa3579fb616166718292652ae8b70457e50de3a46c1e412f99631",
+          "html_url": "https://codeberg.org/poezio",
+          "language": "",
+          "is_admin": false,
+          "last_login": "0001-01-01T00:00:00Z",
+          "created": "2023-06-17T14:35:46Z",
+          "restricted": false,
+          "active": false,
+          "prohibit_login": false,
+          "location": "cyberspace",
+          "pronouns": "",
+          "website": "",
+          "description": "Organization for poezio \u0026 slixmpp",
+          "visibility": "public",
+          "followers_count": 3,
+          "following_count": 0,
+          "starred_repos_count": 0,
+          "username": "poezio"
+        },
+        "name": "slixmpp",
+        "full_name": "poezio/slixmpp",
+        "description": "Modern python XMPP library using asyncio.",
+        "empty": false,
+        "private": false,
+        "fork": false,
+        "template": false,
+        "parent": null,
+        "mirror": false,
+        "size": 12914,
+        "language": "",
+        "languages_url": "https://codeberg.org/api/v1/repos/poezio/slixmpp/languages",
+        "html_url": "https://codeberg.org/poezio/slixmpp",
+        "url": "https://codeberg.org/api/v1/repos/poezio/slixmpp",
+        "link": "",
+        "ssh_url": "git@codeberg.org:poezio/slixmpp.git",
+        "clone_url": "https://codeberg.org/poezio/slixmpp.git",
+        "original_url": "https://lab.louiz.org/poezio/slixmpp",
+        "website": "",
+        "stars_count": 14,
+        "forks_count": 9,
+        "watchers_count": 6,
+        "open_issues_count": 51,
+        "open_pr_counter": 12,
+        "release_counter": 10,
+        "default_branch": "master",
+        "archived": false,
+        "created_at": "2023-07-06T09:10:25Z",
+        "updated_at": "2025-02-13T22:17:56Z",
+        "archived_at": "1970-01-01T00:00:00Z",
+        "permissions": {
+          "admin": true,
+          "push": true,
+          "pull": true
+        },
+        "has_issues": true,
+        "internal_tracker": {
+          "enable_time_tracker": true,
+          "allow_only_contributors_to_track_time": true,
+          "enable_issue_dependencies": true
+        },
+        "has_wiki": true,
+        "wiki_branch": "master",
+        "globally_editable_wiki": false,
+        "has_pull_requests": true,
+        "has_projects": true,
+        "has_releases": true,
+        "has_packages": true,
+        "has_actions": false,
+        "ignore_whitespace_conflicts": false,
+        "allow_merge_commits": true,
+        "allow_rebase": true,
+        "allow_rebase_explicit": true,
+        "allow_squash_merge": true,
+        "allow_fast_forward_only_merge": false,
+        "allow_rebase_update": true,
+        "default_delete_branch_after_merge": false,
+        "default_merge_style": "merge",
+        "default_allow_maintainer_edit": false,
+        "default_update_style": "merge",
+        "avatar_url": "",
+        "internal": false,
+        "mirror_interval": "",
+        "object_format_name": "sha1",
+        "mirror_updated": "0001-01-01T00:00:00Z",
+        "repo_transfer": null,
+        "topics": [
+          "python",
+          "library",
+          "xmpp",
+          "jabber",
+          "asyncio"
+        ]
+      }
+    },
+    "merge_base": "e344947180bbfd667ac572176727f799349815db",
+    "due_date": null,
+    "created_at": "2025-02-13T22:18:10Z",
+    "updated_at": "2025-02-14T16:33:20Z",
+    "closed_at": null,
+    "pin_order": 0
+  },
+  "requested_reviewer": {
+    "id": 64076,
+    "login": "nicoco",
+    "login_name": "",
+    "source_id": 0,
+    "full_name": "",
+    "email": "nicoco@noreply.codeberg.org",
+    "avatar_url": "https://codeberg.org/avatars/32a918ca7a66e4e484ee2ccc625dc6451da728355c060ed0ed54fa69d89224a5",
+    "html_url": "https://codeberg.org/nicoco",
+    "language": "",
+    "is_admin": false,
+    "last_login": "0001-01-01T00:00:00Z",
+    "created": "2022-09-12T11:13:13Z",
+    "restricted": false,
+    "active": false,
+    "prohibit_login": false,
+    "location": "Nice, France",
+    "pronouns": "",
+    "website": "https://nicoco.fr",
+    "description": "wannabe-hacker",
+    "visibility": "public",
+    "followers_count": 2,
+    "following_count": 0,
+    "starred_repos_count": 4,
+    "username": "nicoco"
+  },
+  "repository": {
+    "id": 123634,
+    "owner": {
+      "id": 102463,
+      "login": "poezio",
+      "login_name": "",
+      "source_id": 0,
+      "full_name": "",
+      "email": "",
+      "avatar_url": "https://codeberg.org/avatars/b9b08e0933aaa3579fb616166718292652ae8b70457e50de3a46c1e412f99631",
+      "html_url": "https://codeberg.org/poezio",
+      "language": "",
+      "is_admin": false,
+      "last_login": "0001-01-01T00:00:00Z",
+      "created": "2023-06-17T14:35:46Z",
+      "restricted": false,
+      "active": false,
+      "prohibit_login": false,
+      "location": "cyberspace",
+      "pronouns": "",
+      "website": "",
+      "description": "Organization for poezio \u0026 slixmpp",
+      "visibility": "public",
+      "followers_count": 3,
+      "following_count": 0,
+      "starred_repos_count": 0,
+      "username": "poezio"
+    },
+    "name": "slixmpp",
+    "full_name": "poezio/slixmpp",
+    "description": "Modern python XMPP library using asyncio.",
+    "empty": false,
+    "private": false,
+    "fork": false,
+    "template": false,
+    "parent": null,
+    "mirror": false,
+    "size": 12914,
+    "language": "",
+    "languages_url": "https://codeberg.org/api/v1/repos/poezio/slixmpp/languages",
+    "html_url": "https://codeberg.org/poezio/slixmpp",
+    "url": "https://codeberg.org/api/v1/repos/poezio/slixmpp",
+    "link": "",
+    "ssh_url": "git@codeberg.org:poezio/slixmpp.git",
+    "clone_url": "https://codeberg.org/poezio/slixmpp.git",
+    "original_url": "https://lab.louiz.org/poezio/slixmpp",
+    "website": "",
+    "stars_count": 14,
+    "forks_count": 9,
+    "watchers_count": 6,
+    "open_issues_count": 51,
+    "open_pr_counter": 12,
+    "release_counter": 10,
+    "default_branch": "master",
+    "archived": false,
+    "created_at": "2023-07-06T09:10:25Z",
+    "updated_at": "2025-02-13T22:17:56Z",
+    "archived_at": "1970-01-01T00:00:00Z",
+    "permissions": {
+      "admin": true,
+      "push": true,
+      "pull": true
+    },
+    "has_issues": true,
+    "internal_tracker": {
+      "enable_time_tracker": true,
+      "allow_only_contributors_to_track_time": true,
+      "enable_issue_dependencies": true
+    },
+    "has_wiki": true,
+    "wiki_branch": "master",
+    "globally_editable_wiki": false,
+    "has_pull_requests": true,
+    "has_projects": true,
+    "has_releases": true,
+    "has_packages": true,
+    "has_actions": false,
+    "ignore_whitespace_conflicts": false,
+    "allow_merge_commits": true,
+    "allow_rebase": true,
+    "allow_rebase_explicit": true,
+    "allow_squash_merge": true,
+    "allow_fast_forward_only_merge": false,
+    "allow_rebase_update": true,
+    "default_delete_branch_after_merge": false,
+    "default_merge_style": "merge",
+    "default_allow_maintainer_edit": false,
+    "default_update_style": "merge",
+    "avatar_url": "",
+    "internal": false,
+    "mirror_interval": "",
+    "object_format_name": "sha1",
+    "mirror_updated": "0001-01-01T00:00:00Z",
+    "repo_transfer": null,
+    "topics": [
+      "python",
+      "library",
+      "xmpp",
+      "jabber",
+      "asyncio"
+    ]
+  },
+  "sender": {
+    "id": 64076,
+    "login": "nicoco",
+    "login_name": "",
+    "source_id": 0,
+    "full_name": "",
+    "email": "nicoco@noreply.codeberg.org",
+    "avatar_url": "https://codeberg.org/avatars/32a918ca7a66e4e484ee2ccc625dc6451da728355c060ed0ed54fa69d89224a5",
+    "html_url": "https://codeberg.org/nicoco",
+    "language": "",
+    "is_admin": false,
+    "last_login": "0001-01-01T00:00:00Z",
+    "created": "2022-09-12T11:13:13Z",
+    "restricted": false,
+    "active": false,
+    "prohibit_login": false,
+    "location": "Nice, France",
+    "pronouns": "",
+    "website": "https://nicoco.fr",
+    "description": "wannabe-hacker",
+    "visibility": "public",
+    "followers_count": 2,
+    "following_count": 0,
+    "starred_repos_count": 4,
+    "username": "nicoco"
+  },
+  "commit_id": "",
+  "review": {
+    "type": "pull_request_review_rejected",
+    "content": "I spotted one typo, that review was not completely useless, YEAH!\r\n\r\nYou mentioned oob that you'd like more integration tests, maybe that plugin is a good candidate for them? Testing silent kick is probably near-impossible without a custom muc component, but the \"you're still joined\" path looks like it would be doable?"
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_pubsub_forgejo/webhook-examples/pull_request_rejected/headers	Mon Feb 17 23:28:05 2025 +0100
@@ -0,0 +1,18 @@
+Content-Type: application/json
+X-Forgejo-Delivery: 55de0e6c-593b-41d1-b208-ea00efe818c6
+X-Forgejo-Event: pull_request_rejected
+X-Forgejo-Event-Type: pull_request_review_rejected
+X-Forgejo-Signature: 52c66875ee76c8aba327c8bad7122e109f0b24ddeb0c291f57e071637dc60ccc
+X-GitHub-Delivery: 55de0e6c-593b-41d1-b208-ea00efe818c6
+X-GitHub-Event: pull_request_rejected
+X-GitHub-Event-Type: pull_request_review_rejected
+X-Gitea-Delivery: 55de0e6c-593b-41d1-b208-ea00efe818c6
+X-Gitea-Event: pull_request_rejected
+X-Gitea-Event-Type: pull_request_review_rejected
+X-Gitea-Signature: 52c66875ee76c8aba327c8bad7122e109f0b24ddeb0c291f57e071637dc60ccc
+X-Gogs-Delivery: 55de0e6c-593b-41d1-b208-ea00efe818c6
+X-Gogs-Event: pull_request_rejected
+X-Gogs-Event-Type: pull_request_review_rejected
+X-Gogs-Signature: 52c66875ee76c8aba327c8bad7122e109f0b24ddeb0c291f57e071637dc60ccc
+X-Hub-Signature: sha1=ccb5a848dc0447d8a84b10308d5cc94d6a8dbad1
+X-Hub-Signature-256: sha256=52c66875ee76c8aba327c8bad7122e109f0b24ddeb0c291f57e071637dc60ccc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_pubsub_forgejo/webhook-examples/push/content.json	Mon Feb 17 23:28:05 2025 +0100
@@ -0,0 +1,268 @@
+{
+  "ref": "refs/heads/dev",
+  "before": "3f8b7a2f69c6c891d8ab07700f113103db73faa3",
+  "after": "dd7c6923c788b38312fbc27cffba3b7007d0126e",
+  "compare_url": "https://codeberg.org/slidge/slidgram/compare/3f8b7a2f69c6c891d8ab07700f113103db73faa3...dd7c6923c788b38312fbc27cffba3b7007d0126e",
+  "commits": [
+    {
+      "id": "dd7c6923c788b38312fbc27cffba3b7007d0126e",
+      "message": "build: update pre-commit hooks\n",
+      "url": "https://codeberg.org/slidge/slidgram/commit/dd7c6923c788b38312fbc27cffba3b7007d0126e",
+      "author": {
+        "name": "nicoco",
+        "email": "nicoco@nicoco.fr",
+        "username": "nicoco"
+      },
+      "committer": {
+        "name": "nicoco",
+        "email": "nicoco@nicoco.fr",
+        "username": "nicoco"
+      },
+      "verification": null,
+      "timestamp": "2025-01-16T13:33:08+01:00",
+      "added": [],
+      "removed": [],
+      "modified": [
+        ".pre-commit-config.yaml"
+      ]
+    },
+    {
+      "id": "790116333966f395cc268b032c7a6173486955f4",
+      "message": "chore: update lockfile\n\nbecause we must",
+      "url": "https://codeberg.org/slidge/slidgram/commit/790116333966f395cc268b032c7a6173486955f4",
+      "author": {
+        "name": "nicoco",
+        "email": "nicoco@nicoco.fr",
+        "username": "nicoco"
+      },
+      "committer": {
+        "name": "nicoco",
+        "email": "nicoco@nicoco.fr",
+        "username": "nicoco"
+      },
+      "verification": null,
+      "timestamp": "2025-01-13T15:47:16+01:00",
+      "added": [
+        "uv.lock"
+      ],
+      "removed": [],
+      "modified": []
+    },
+    {
+      "id": "776de82113f8a860408f78c22e8539e73306ac87",
+      "message": "ci: use woodpecker\n",
+      "url": "https://codeberg.org/slidge/slidgram/commit/776de82113f8a860408f78c22e8539e73306ac87",
+      "author": {
+        "name": "nicoco",
+        "email": "nicoco@nicoco.fr",
+        "username": "nicoco"
+      },
+      "committer": {
+        "name": "nicoco",
+        "email": "nicoco@nicoco.fr",
+        "username": "nicoco"
+      },
+      "verification": null,
+      "timestamp": "2025-01-11T15:57:59+01:00",
+      "added": [
+        ".woodpecker/container-ci.yaml",
+        ".woodpecker/container.yaml",
+        ".woodpecker/docs.yaml",
+        ".woodpecker/package.yaml",
+        ".woodpecker/test.yaml"
+      ],
+      "removed": [
+        ".builds/ci.yml",
+        ".builds/container.yml",
+        ".copier-answers.yml"
+      ],
+      "modified": [
+        ".gitignore",
+        "Dockerfile",
+        "README.md",
+        "docs/Makefile",
+        "docs/source/conf.py",
+        "pyproject.toml"
+      ]
+    }
+  ],
+  "total_commits": 3,
+  "head_commit": {
+    "id": "dd7c6923c788b38312fbc27cffba3b7007d0126e",
+    "message": "build: update pre-commit hooks\n",
+    "url": "https://codeberg.org/slidge/slidgram/commit/dd7c6923c788b38312fbc27cffba3b7007d0126e",
+    "author": {
+      "name": "nicoco",
+      "email": "nicoco@nicoco.fr",
+      "username": "nicoco"
+    },
+    "committer": {
+      "name": "nicoco",
+      "email": "nicoco@nicoco.fr",
+      "username": "nicoco"
+    },
+    "verification": null,
+    "timestamp": "2025-01-16T13:33:08+01:00",
+    "added": [],
+    "removed": [],
+    "modified": [
+      ".pre-commit-config.yaml"
+    ]
+  },
+  "repository": {
+    "id": 301703,
+    "owner": {
+      "id": 205842,
+      "login": "slidge",
+      "login_name": "",
+      "source_id": 0,
+      "full_name": "",
+      "email": "",
+      "avatar_url": "https://codeberg.org/avatars/aa0eeeb5fe173938bdc665be92bf605efa1e8f4a905bc3327e4d5c7eddf584e5",
+      "html_url": "https://codeberg.org/slidge",
+      "language": "",
+      "is_admin": false,
+      "last_login": "0001-01-01T00:00:00Z",
+      "created": "2024-08-30T14:16:14Z",
+      "restricted": false,
+      "active": false,
+      "prohibit_login": false,
+      "location": "",
+      "pronouns": "",
+      "website": "https://slidge.im",
+      "description": "",
+      "visibility": "public",
+      "followers_count": 3,
+      "following_count": 0,
+      "starred_repos_count": 0,
+      "username": "slidge"
+    },
+    "name": "slidgram",
+    "full_name": "slidge/slidgram",
+    "description": "A feature-rich Telegram to XMPP gateway",
+    "empty": false,
+    "private": false,
+    "fork": false,
+    "template": false,
+    "parent": null,
+    "mirror": false,
+    "size": 1561,
+    "language": "",
+    "languages_url": "https://codeberg.org/api/v1/repos/slidge/slidgram/languages",
+    "html_url": "https://codeberg.org/slidge/slidgram",
+    "url": "https://codeberg.org/api/v1/repos/slidge/slidgram",
+    "link": "",
+    "ssh_url": "git@codeberg.org:slidge/slidgram.git",
+    "clone_url": "https://codeberg.org/slidge/slidgram.git",
+    "original_url": "",
+    "website": "",
+    "stars_count": 0,
+    "forks_count": 1,
+    "watchers_count": 3,
+    "open_issues_count": 7,
+    "open_pr_counter": 0,
+    "release_counter": 0,
+    "default_branch": "main",
+    "archived": false,
+    "created_at": "2025-01-10T09:19:40Z",
+    "updated_at": "2025-01-16T12:15:22Z",
+    "archived_at": "1970-01-01T00:00:00Z",
+    "permissions": {
+      "admin": true,
+      "push": true,
+      "pull": true
+    },
+    "has_issues": true,
+    "internal_tracker": {
+      "enable_time_tracker": true,
+      "allow_only_contributors_to_track_time": true,
+      "enable_issue_dependencies": true
+    },
+    "has_wiki": false,
+    "wiki_branch": "main",
+    "globally_editable_wiki": false,
+    "has_pull_requests": true,
+    "has_projects": false,
+    "has_releases": true,
+    "has_packages": true,
+    "has_actions": false,
+    "ignore_whitespace_conflicts": false,
+    "allow_merge_commits": true,
+    "allow_rebase": true,
+    "allow_rebase_explicit": true,
+    "allow_squash_merge": true,
+    "allow_fast_forward_only_merge": true,
+    "allow_rebase_update": true,
+    "default_delete_branch_after_merge": false,
+    "default_merge_style": "merge",
+    "default_allow_maintainer_edit": false,
+    "avatar_url": "",
+    "internal": false,
+    "mirror_interval": "",
+    "object_format_name": "sha1",
+    "mirror_updated": "0001-01-01T00:00:00Z",
+    "repo_transfer": null,
+    "topics": [
+      "bridge",
+      "chat",
+      "gateway",
+      "instant-messaging",
+      "slidge",
+      "telegram",
+      "telegram-userbot",
+      "xmpp"
+    ]
+  },
+  "pusher": {
+    "id": 64076,
+    "login": "nicoco",
+    "login_name": "",
+    "source_id": 0,
+    "full_name": "",
+    "email": "nicoco@noreply.codeberg.org",
+    "avatar_url": "https://codeberg.org/avatars/32a918ca7a66e4e484ee2ccc625dc6451da728355c060ed0ed54fa69d89224a5",
+    "html_url": "https://codeberg.org/nicoco",
+    "language": "",
+    "is_admin": false,
+    "last_login": "0001-01-01T00:00:00Z",
+    "created": "2022-09-12T11:13:13Z",
+    "restricted": false,
+    "active": false,
+    "prohibit_login": false,
+    "location": "Nice, France",
+    "pronouns": "",
+    "website": "https://nicoco.fr",
+    "description": "wannabe-hacker",
+    "visibility": "public",
+    "followers_count": 1,
+    "following_count": 0,
+    "starred_repos_count": 3,
+    "username": "nicoco"
+  },
+  "sender": {
+    "id": 64076,
+    "login": "nicoco",
+    "login_name": "",
+    "source_id": 0,
+    "full_name": "",
+    "email": "nicoco@noreply.codeberg.org",
+    "avatar_url": "https://codeberg.org/avatars/32a918ca7a66e4e484ee2ccc625dc6451da728355c060ed0ed54fa69d89224a5",
+    "html_url": "https://codeberg.org/nicoco",
+    "language": "",
+    "is_admin": false,
+    "last_login": "0001-01-01T00:00:00Z",
+    "created": "2022-09-12T11:13:13Z",
+    "restricted": false,
+    "active": false,
+    "prohibit_login": false,
+    "location": "Nice, France",
+    "pronouns": "",
+    "website": "https://nicoco.fr",
+    "description": "wannabe-hacker",
+    "visibility": "public",
+    "followers_count": 1,
+    "following_count": 0,
+    "starred_repos_count": 3,
+    "username": "nicoco"
+  }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_pubsub_forgejo/webhook-examples/push/headers	Mon Feb 17 23:28:05 2025 +0100
@@ -0,0 +1,18 @@
+Content-Type: application/json
+X-Forgejo-Delivery: eec91577-9e9a-4914-929e-b7870f77fd57
+X-Forgejo-Event: push
+X-Forgejo-Event-Type: push
+X-Forgejo-Signature: 0bd1c7325306a6f469df72d9966b2e89cebcf1fb790a1926ef9ff094141eed26
+X-GitHub-Delivery: eec91577-9e9a-4914-929e-b7870f77fd57
+X-GitHub-Event: push
+X-GitHub-Event-Type: push
+X-Gitea-Delivery: eec91577-9e9a-4914-929e-b7870f77fd57
+X-Gitea-Event: push
+X-Gitea-Event-Type: push
+X-Gitea-Signature: 0bd1c7325306a6f469df72d9966b2e89cebcf1fb790a1926ef9ff094141eed26
+X-Gogs-Delivery: eec91577-9e9a-4914-929e-b7870f77fd57
+X-Gogs-Event: push
+X-Gogs-Event-Type: push
+X-Gogs-Signature: 0bd1c7325306a6f469df72d9966b2e89cebcf1fb790a1926ef9ff094141eed26
+X-Hub-Signature: sha1=eacf3469341d9deecfcfbb3a63a4f1d55e60226c
+X-Hub-Signature-256: sha256=0bd1c7325306a6f469df72d9966b2e89cebcf1fb790a1926ef9ff094141eed26
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_pubsub_forgejo/webhook-examples/push/slidge.rss	Mon Feb 17 23:28:05 2025 +0100
@@ -0,0 +1,341 @@
+<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
+  <channel>
+    <title>Feed of &#34;slidge&#34;</title>
+    <link>https://codeberg.org/slidge</link>
+    <description></description>
+    <pubDate>Fri, 17 Jan 2025 12:15:21 +0000</pubDate>
+    <item>
+      <title>aereaux commented on issue slidge/slidge#36</title>
+      <link>https://codeberg.org/slidge/slidge/issues/36#issuecomment-2590246</link>
+      <description>Error on startup&#xA;&#xA;&lt;p dir=&#34;auto&#34;&gt;It does continue to run, but it doesn&amp;#39;t set my avatar.  I do seem to see my own avatar in group chats, at least in cheogram.&lt;/p&gt;&#xA;</description>
+      <content:encoded><![CDATA[Error on startup
+
+<p dir="auto">It does continue to run, but it doesn&#39;t set my avatar.  I do seem to see my own avatar in group chats, at least in cheogram.</p>
+]]></content:encoded>
+      <author>aereaux</author>
+      <guid isPermaLink="false">22108577: https://codeberg.org/slidge/slidge/issues/36#issuecomment-2590246</guid>
+      <pubDate>Thu, 16 Jan 2025 15:32:41 +0000</pubDate>
+    </item>
+    <item>
+      <title>nicoco commented on issue slidge/slidge#36</title>
+      <link>https://codeberg.org/slidge/slidge/issues/36#issuecomment-2590128</link>
+      <description>Error on startup&#xA;&#xA;&lt;p dir=&#34;auto&#34;&gt;I&amp;#39;m not sure why this happens but it is non fatal, right? I suspect you don&amp;#39;t see your own avatar in group chats though.&lt;/p&gt;&#xA;</description>
+      <content:encoded><![CDATA[Error on startup
+
+<p dir="auto">I&#39;m not sure why this happens but it is non fatal, right? I suspect you don&#39;t see your own avatar in group chats though.</p>
+]]></content:encoded>
+      <author>nicoco</author>
+      <guid isPermaLink="false">22106314: https://codeberg.org/slidge/slidge/issues/36#issuecomment-2590128</guid>
+      <pubDate>Thu, 16 Jan 2025 14:28:29 +0000</pubDate>
+    </item>
+    <item>
+      <title>nicoco pushed to dev at slidge/slidgram</title>
+      <link>/slidge/slidgram/compare/3f8b7a2f69c6c891d8ab07700f113103db73faa3...dd7c6923c788b38312fbc27cffba3b7007d0126e</link>
+      <description>&lt;a href=&#34;https://codeberg.org/slidge/slidgram/commit/dd7c6923c788b38312fbc27cffba3b7007d0126e&#34;&gt;dd7c6923c788b38312fbc27cffba3b7007d0126e&lt;/a&gt;&#xA;build: update pre-commit hooks&#xA;&#xA;&lt;a href=&#34;https://codeberg.org/slidge/slidgram/commit/790116333966f395cc268b032c7a6173486955f4&#34;&gt;790116333966f395cc268b032c7a6173486955f4&lt;/a&gt;&#xA;chore: update lockfile&#xA;&#xA;&lt;a href=&#34;https://codeberg.org/slidge/slidgram/commit/776de82113f8a860408f78c22e8539e73306ac87&#34;&gt;776de82113f8a860408f78c22e8539e73306ac87&lt;/a&gt;&#xA;ci: use woodpecker</description>
+      <content:encoded><![CDATA[<a href="https://codeberg.org/slidge/slidgram/commit/dd7c6923c788b38312fbc27cffba3b7007d0126e" rel="nofollow">dd7c6923c788b38312fbc27cffba3b7007d0126e</a>
+build: update pre-commit hooks
+
+<a href="https://codeberg.org/slidge/slidgram/commit/790116333966f395cc268b032c7a6173486955f4" rel="nofollow">790116333966f395cc268b032c7a6173486955f4</a>
+chore: update lockfile
+
+<a href="https://codeberg.org/slidge/slidgram/commit/776de82113f8a860408f78c22e8539e73306ac87" rel="nofollow">776de82113f8a860408f78c22e8539e73306ac87</a>
+ci: use woodpecker]]></content:encoded>
+      <author>nicoco</author>
+      <guid isPermaLink="false">22100038: /slidge/slidgram/compare/3f8b7a2f69c6c891d8ab07700f113103db73faa3...dd7c6923c788b38312fbc27cffba3b7007d0126e</guid>
+      <pubDate>Thu, 16 Jan 2025 12:40:29 +0000</pubDate>
+    </item>
+    <item>
+      <title>nicoco pushed to dev at slidge/slidgram</title>
+      <link>https://codeberg.org/slidge/slidgram/commit/3f8b7a2f69c6c891d8ab07700f113103db73faa3</link>
+      <description>&lt;a href=&#34;https://codeberg.org/slidge/slidgram/commit/3f8b7a2f69c6c891d8ab07700f113103db73faa3&#34;&gt;3f8b7a2f69c6c891d8ab07700f113103db73faa3&lt;/a&gt;&#xA;fixup! ci: use woodpecker</description>
+      <content:encoded><![CDATA[<a href="https://codeberg.org/slidge/slidgram/commit/3f8b7a2f69c6c891d8ab07700f113103db73faa3" rel="nofollow">3f8b7a2f69c6c891d8ab07700f113103db73faa3</a>
+fixup! ci: use woodpecker]]></content:encoded>
+      <author>nicoco</author>
+      <guid isPermaLink="false">22099622: https://codeberg.org/slidge/slidgram/commit/3f8b7a2f69c6c891d8ab07700f113103db73faa3</guid>
+      <pubDate>Thu, 16 Jan 2025 12:15:22 +0000</pubDate>
+    </item>
+    <item>
+      <title>nicoco pushed to dev at slidge/slidgram</title>
+      <link>https://codeberg.org/slidge/slidgram/commit/6a1c45d27839cc1d4db0edd0fbe074171b89fcc0</link>
+      <description>&lt;a href=&#34;https://codeberg.org/slidge/slidgram/commit/6a1c45d27839cc1d4db0edd0fbe074171b89fcc0&#34;&gt;6a1c45d27839cc1d4db0edd0fbe074171b89fcc0&lt;/a&gt;&#xA;fixup! ci: use woodpecker</description>
+      <content:encoded><![CDATA[<a href="https://codeberg.org/slidge/slidgram/commit/6a1c45d27839cc1d4db0edd0fbe074171b89fcc0" rel="nofollow">6a1c45d27839cc1d4db0edd0fbe074171b89fcc0</a>
+fixup! ci: use woodpecker]]></content:encoded>
+      <author>nicoco</author>
+      <guid isPermaLink="false">22099101: https://codeberg.org/slidge/slidgram/commit/6a1c45d27839cc1d4db0edd0fbe074171b89fcc0</guid>
+      <pubDate>Thu, 16 Jan 2025 11:53:46 +0000</pubDate>
+    </item>
+    <item>
+      <title>nicoco pushed to dev at slidge/slidgram</title>
+      <link>/slidge/slidgram/compare/2dfc9cc3916e91cf1db677771bc1a41ba55f50aa...d818b8518ce676361e3d694ff9b14ec86cb7e31c</link>
+      <description>&lt;a href=&#34;https://codeberg.org/slidge/slidgram/commit/d818b8518ce676361e3d694ff9b14ec86cb7e31c&#34;&gt;d818b8518ce676361e3d694ff9b14ec86cb7e31c&lt;/a&gt;&#xA;fixup! ci: use woodpecker&#xA;&#xA;&lt;a href=&#34;https://codeberg.org/slidge/slidgram/commit/2e52e2170caeb1a2ac28635dd766e32a79dcbae9&#34;&gt;2e52e2170caeb1a2ac28635dd766e32a79dcbae9&lt;/a&gt;&#xA;chore: update lockfile&#xA;&#xA;&lt;a href=&#34;https://codeberg.org/slidge/slidgram/commit/31d0ee7595b08be34255add47e2b569ba1b05ac4&#34;&gt;31d0ee7595b08be34255add47e2b569ba1b05ac4&lt;/a&gt;&#xA;fixup! ci: use woodpecker&#xA;&#xA;&lt;a href=&#34;https://codeberg.org/slidge/slidgram/commit/e8530a185c5a783b1a98a3183326b1954f92997b&#34;&gt;e8530a185c5a783b1a98a3183326b1954f92997b&lt;/a&gt;&#xA;chore: update lockfile&#xA;&#xA;&lt;a href=&#34;https://codeberg.org/slidge/slidgram/commit/acf27d0ca32c4e193451fcb5025db0e9820380f8&#34;&gt;acf27d0ca32c4e193451fcb5025db0e9820380f8&lt;/a&gt;&#xA;ci: use woodpecker</description>
+      <content:encoded><![CDATA[<a href="https://codeberg.org/slidge/slidgram/commit/d818b8518ce676361e3d694ff9b14ec86cb7e31c" rel="nofollow">d818b8518ce676361e3d694ff9b14ec86cb7e31c</a>
+fixup! ci: use woodpecker
+
+<a href="https://codeberg.org/slidge/slidgram/commit/2e52e2170caeb1a2ac28635dd766e32a79dcbae9" rel="nofollow">2e52e2170caeb1a2ac28635dd766e32a79dcbae9</a>
+chore: update lockfile
+
+<a href="https://codeberg.org/slidge/slidgram/commit/31d0ee7595b08be34255add47e2b569ba1b05ac4" rel="nofollow">31d0ee7595b08be34255add47e2b569ba1b05ac4</a>
+fixup! ci: use woodpecker
+
+<a href="https://codeberg.org/slidge/slidgram/commit/e8530a185c5a783b1a98a3183326b1954f92997b" rel="nofollow">e8530a185c5a783b1a98a3183326b1954f92997b</a>
+chore: update lockfile
+
+<a href="https://codeberg.org/slidge/slidgram/commit/acf27d0ca32c4e193451fcb5025db0e9820380f8" rel="nofollow">acf27d0ca32c4e193451fcb5025db0e9820380f8</a>
+ci: use woodpecker]]></content:encoded>
+      <author>nicoco</author>
+      <guid isPermaLink="false">22099034: /slidge/slidgram/compare/2dfc9cc3916e91cf1db677771bc1a41ba55f50aa...d818b8518ce676361e3d694ff9b14ec86cb7e31c</guid>
+      <pubDate>Thu, 16 Jan 2025 11:49:45 +0000</pubDate>
+    </item>
+    <item>
+      <title>nicoco pushed to dev at slidge/slidge</title>
+      <link>/slidge/slidge/compare/f2700e77b26f3a1391a3bcecc0db758398b9db73...9440989ef8c9b751eb4153f1e20e55627f8f548e</link>
+      <description>&lt;a href=&#34;https://codeberg.org/slidge/slidge/commit/9440989ef8c9b751eb4153f1e20e55627f8f548e&#34;&gt;9440989ef8c9b751eb4153f1e20e55627f8f548e&lt;/a&gt;&#xA;docs: readme: freshen up&#xA;&#xA;&lt;a href=&#34;https://codeberg.org/slidge/slidge/commit/43ea9bf36f1d74e09154e0a9d46e5f67e778fb74&#34;&gt;43ea9bf36f1d74e09154e0a9d46e5f67e778fb74&lt;/a&gt;&#xA;ci: factorisation, cron jobs, manual pipelines</description>
+      <content:encoded><![CDATA[<a href="https://codeberg.org/slidge/slidge/commit/9440989ef8c9b751eb4153f1e20e55627f8f548e" rel="nofollow">9440989ef8c9b751eb4153f1e20e55627f8f548e</a>
+docs: readme: freshen up
+
+<a href="https://codeberg.org/slidge/slidge/commit/43ea9bf36f1d74e09154e0a9d46e5f67e778fb74" rel="nofollow">43ea9bf36f1d74e09154e0a9d46e5f67e778fb74</a>
+ci: factorisation, cron jobs, manual pipelines]]></content:encoded>
+      <author>nicoco</author>
+      <guid isPermaLink="false">22097690: /slidge/slidge/compare/f2700e77b26f3a1391a3bcecc0db758398b9db73...9440989ef8c9b751eb4153f1e20e55627f8f548e</guid>
+      <pubDate>Thu, 16 Jan 2025 10:42:40 +0000</pubDate>
+    </item>
+    <item>
+      <title>nicoco deleted branch buildx from slidge/slidge</title>
+      <link>https://codeberg.org/slidge/slidge</link>
+      <description></description>
+      <author>nicoco</author>
+      <guid isPermaLink="false">22096886: https://codeberg.org/slidge/slidge</guid>
+      <pubDate>Thu, 16 Jan 2025 10:13:13 +0000</pubDate>
+    </item>
+    <item>
+      <title>nicoco pushed to dev at slidge/slidge</title>
+      <link>https://codeberg.org/slidge/slidge/commit/f2700e77b26f3a1391a3bcecc0db758398b9db73</link>
+      <description>&lt;a href=&#34;https://codeberg.org/slidge/slidge/commit/f2700e77b26f3a1391a3bcecc0db758398b9db73&#34;&gt;f2700e77b26f3a1391a3bcecc0db758398b9db73&lt;/a&gt;&#xA;ci: cache virtualenv; factorisation</description>
+      <content:encoded><![CDATA[<a href="https://codeberg.org/slidge/slidge/commit/f2700e77b26f3a1391a3bcecc0db758398b9db73" rel="nofollow">f2700e77b26f3a1391a3bcecc0db758398b9db73</a>
+ci: cache virtualenv; factorisation]]></content:encoded>
+      <author>nicoco</author>
+      <guid isPermaLink="false">22096805: https://codeberg.org/slidge/slidge/commit/f2700e77b26f3a1391a3bcecc0db758398b9db73</guid>
+      <pubDate>Thu, 16 Jan 2025 10:08:14 +0000</pubDate>
+    </item>
+    <item>
+      <title>nicoco pushed to dev at slidge/slidge</title>
+      <link>https://codeberg.org/slidge/slidge/commit/b25334774b74b5d88572694a10ecf451282639b4</link>
+      <description>&lt;a href=&#34;https://codeberg.org/slidge/slidge/commit/b25334774b74b5d88572694a10ecf451282639b4&#34;&gt;b25334774b74b5d88572694a10ecf451282639b4&lt;/a&gt;&#xA;fixup! ci</description>
+      <content:encoded><![CDATA[<a href="https://codeberg.org/slidge/slidge/commit/b25334774b74b5d88572694a10ecf451282639b4" rel="nofollow">b25334774b74b5d88572694a10ecf451282639b4</a>
+fixup! ci]]></content:encoded>
+      <author>nicoco</author>
+      <guid isPermaLink="false">22096514: https://codeberg.org/slidge/slidge/commit/b25334774b74b5d88572694a10ecf451282639b4</guid>
+      <pubDate>Thu, 16 Jan 2025 09:51:06 +0000</pubDate>
+    </item>
+    <item>
+      <title>c3p0-slidge pushed to main at slidge/pages</title>
+      <link>https://codeberg.org/slidge/pages/commit/4688fe65212fee04717f4b466185ba05ac0ccba0</link>
+      <description>&lt;a href=&#34;https://codeberg.org/slidge/pages/commit/4688fe65212fee04717f4b466185ba05ac0ccba0&#34;&gt;4688fe65212fee04717f4b466185ba05ac0ccba0&lt;/a&gt;&#xA;deploy docs for 03adac6ec948d70934756626ce9183b9321523dc</description>
+      <content:encoded><![CDATA[<a href="https://codeberg.org/slidge/pages/commit/4688fe65212fee04717f4b466185ba05ac0ccba0" rel="nofollow">4688fe65212fee04717f4b466185ba05ac0ccba0</a>
+deploy docs for 03adac6ec948d70934756626ce9183b9321523dc]]></content:encoded>
+      <author>c3p0-slidge</author>
+      <guid isPermaLink="false">22096350: https://codeberg.org/slidge/pages/commit/4688fe65212fee04717f4b466185ba05ac0ccba0</guid>
+      <pubDate>Thu, 16 Jan 2025 09:40:36 +0000</pubDate>
+    </item>
+    <item>
+      <title>c3p0-slidge pushed to main at slidge/pages</title>
+      <link>https://codeberg.org/slidge/pages/commit/c9ae413a9966ff51af5d4d4281e74001a1e8677d</link>
+      <description>&lt;a href=&#34;https://codeberg.org/slidge/pages/commit/c9ae413a9966ff51af5d4d4281e74001a1e8677d&#34;&gt;c9ae413a9966ff51af5d4d4281e74001a1e8677d&lt;/a&gt;&#xA;deploy docs for 03adac6ec948d70934756626ce9183b9321523dc</description>
+      <content:encoded><![CDATA[<a href="https://codeberg.org/slidge/pages/commit/c9ae413a9966ff51af5d4d4281e74001a1e8677d" rel="nofollow">c9ae413a9966ff51af5d4d4281e74001a1e8677d</a>
+deploy docs for 03adac6ec948d70934756626ce9183b9321523dc]]></content:encoded>
+      <author>c3p0-slidge</author>
+      <guid isPermaLink="false">22096294: https://codeberg.org/slidge/pages/commit/c9ae413a9966ff51af5d4d4281e74001a1e8677d</guid>
+      <pubDate>Thu, 16 Jan 2025 09:38:07 +0000</pubDate>
+    </item>
+    <item>
+      <title>nicoco pushed to dev at slidge/slidge</title>
+      <link>https://codeberg.org/slidge/slidge/commit/03adac6ec948d70934756626ce9183b9321523dc</link>
+      <description>&lt;a href=&#34;https://codeberg.org/slidge/slidge/commit/03adac6ec948d70934756626ce9183b9321523dc&#34;&gt;03adac6ec948d70934756626ce9183b9321523dc&lt;/a&gt;&#xA;fixup! ci</description>
+      <content:encoded><![CDATA[<a href="https://codeberg.org/slidge/slidge/commit/03adac6ec948d70934756626ce9183b9321523dc" rel="nofollow">03adac6ec948d70934756626ce9183b9321523dc</a>
+fixup! ci]]></content:encoded>
+      <author>nicoco</author>
+      <guid isPermaLink="false">22096251: https://codeberg.org/slidge/slidge/commit/03adac6ec948d70934756626ce9183b9321523dc</guid>
+      <pubDate>Thu, 16 Jan 2025 09:36:07 +0000</pubDate>
+    </item>
+    <item>
+      <title>nicoco pushed to dev at slidge/slidge</title>
+      <link>https://codeberg.org/slidge/slidge/commit/6e945b3eae118271d19c5d94aa1bdbe8006b0267</link>
+      <description>&lt;a href=&#34;https://codeberg.org/slidge/slidge/commit/6e945b3eae118271d19c5d94aa1bdbe8006b0267&#34;&gt;6e945b3eae118271d19c5d94aa1bdbe8006b0267&lt;/a&gt;&#xA;fixup! ci</description>
+      <content:encoded><![CDATA[<a href="https://codeberg.org/slidge/slidge/commit/6e945b3eae118271d19c5d94aa1bdbe8006b0267" rel="nofollow">6e945b3eae118271d19c5d94aa1bdbe8006b0267</a>
+fixup! ci]]></content:encoded>
+      <author>nicoco</author>
+      <guid isPermaLink="false">22095628: https://codeberg.org/slidge/slidge/commit/6e945b3eae118271d19c5d94aa1bdbe8006b0267</guid>
+      <pubDate>Thu, 16 Jan 2025 09:17:04 +0000</pubDate>
+    </item>
+    <item>
+      <title>nicoco pushed to dev at slidge/slidge</title>
+      <link>https://codeberg.org/slidge/slidge/commit/e27df12d6d6a950c4d6ba28c568f6d45fb7456ea</link>
+      <description>&lt;a href=&#34;https://codeberg.org/slidge/slidge/commit/e27df12d6d6a950c4d6ba28c568f6d45fb7456ea&#34;&gt;e27df12d6d6a950c4d6ba28c568f6d45fb7456ea&lt;/a&gt;&#xA;fixup! ci</description>
+      <content:encoded><![CDATA[<a href="https://codeberg.org/slidge/slidge/commit/e27df12d6d6a950c4d6ba28c568f6d45fb7456ea" rel="nofollow">e27df12d6d6a950c4d6ba28c568f6d45fb7456ea</a>
+fixup! ci]]></content:encoded>
+      <author>nicoco</author>
+      <guid isPermaLink="false">22095366: https://codeberg.org/slidge/slidge/commit/e27df12d6d6a950c4d6ba28c568f6d45fb7456ea</guid>
+      <pubDate>Thu, 16 Jan 2025 09:02:26 +0000</pubDate>
+    </item>
+    <item>
+      <title>nicoco pushed to dev at slidge/slidge</title>
+      <link>https://codeberg.org/slidge/slidge/commit/f2c028957dbf2447ae54a0e7d9e94b7145d856d1</link>
+      <description>&lt;a href=&#34;https://codeberg.org/slidge/slidge/commit/f2c028957dbf2447ae54a0e7d9e94b7145d856d1&#34;&gt;f2c028957dbf2447ae54a0e7d9e94b7145d856d1&lt;/a&gt;&#xA;fixup! ci</description>
+      <content:encoded><![CDATA[<a href="https://codeberg.org/slidge/slidge/commit/f2c028957dbf2447ae54a0e7d9e94b7145d856d1" rel="nofollow">f2c028957dbf2447ae54a0e7d9e94b7145d856d1</a>
+fixup! ci]]></content:encoded>
+      <author>nicoco</author>
+      <guid isPermaLink="false">22094639: https://codeberg.org/slidge/slidge/commit/f2c028957dbf2447ae54a0e7d9e94b7145d856d1</guid>
+      <pubDate>Thu, 16 Jan 2025 08:34:20 +0000</pubDate>
+    </item>
+    <item>
+      <title>nicoco pushed to buildx at slidge/slidge</title>
+      <link>https://codeberg.org/slidge/slidge/commit/6c6a79b6b74c684585fca23910d29859253741e4</link>
+      <description>&lt;a href=&#34;https://codeberg.org/slidge/slidge/commit/6c6a79b6b74c684585fca23910d29859253741e4&#34;&gt;6c6a79b6b74c684585fca23910d29859253741e4&lt;/a&gt;&#xA;ci: fix auth</description>
+      <content:encoded><![CDATA[<a href="https://codeberg.org/slidge/slidge/commit/6c6a79b6b74c684585fca23910d29859253741e4" rel="nofollow">6c6a79b6b74c684585fca23910d29859253741e4</a>
+ci: fix auth]]></content:encoded>
+      <author>nicoco</author>
+      <guid isPermaLink="false">22094450: https://codeberg.org/slidge/slidge/commit/6c6a79b6b74c684585fca23910d29859253741e4</guid>
+      <pubDate>Thu, 16 Jan 2025 08:26:14 +0000</pubDate>
+    </item>
+    <item>
+      <title>nicoco pushed to buildx at slidge/slidge</title>
+      <link>https://codeberg.org/slidge/slidge/commit/af5cb61652c159ea382a3df7cc0711df82e2c6f3</link>
+      <description>&lt;a href=&#34;https://codeberg.org/slidge/slidge/commit/af5cb61652c159ea382a3df7cc0711df82e2c6f3&#34;&gt;af5cb61652c159ea382a3df7cc0711df82e2c6f3&lt;/a&gt;&#xA;ci: just build all the time</description>
+      <content:encoded><![CDATA[<a href="https://codeberg.org/slidge/slidge/commit/af5cb61652c159ea382a3df7cc0711df82e2c6f3" rel="nofollow">af5cb61652c159ea382a3df7cc0711df82e2c6f3</a>
+ci: just build all the time]]></content:encoded>
+      <author>nicoco</author>
+      <guid isPermaLink="false">22094328: https://codeberg.org/slidge/slidge/commit/af5cb61652c159ea382a3df7cc0711df82e2c6f3</guid>
+      <pubDate>Thu, 16 Jan 2025 08:18:02 +0000</pubDate>
+    </item>
+    <item>
+      <title>nicoco pushed to buildx at slidge/slidge</title>
+      <link>/slidge/slidge/compare/6e77ed7510a1dfd23f35b81affc90ded895515e6...3efecf7655b24623a122170b872782ac9cab9344</link>
+      <description>&lt;a href=&#34;https://codeberg.org/slidge/slidge/commit/3efecf7655b24623a122170b872782ac9cab9344&#34;&gt;3efecf7655b24623a122170b872782ac9cab9344&lt;/a&gt;&#xA;feat: allow multiple values for --cache-from&#xA;&#xA;&lt;a href=&#34;https://codeberg.org/slidge/slidge/commit/d689954ee5afee840d9c3ccb37eeb434c5554325&#34;&gt;d689954ee5afee840d9c3ccb37eeb434c5554325&lt;/a&gt;&#xA;chore(deps): update docker.io/mstruebing/editorconfig-checker docker tag to v3.1.2&#xA;&#xA;&lt;a href=&#34;https://codeberg.org/slidge/slidge/commit/3e6c614b92c907987cf50029eb4512dba44dadd2&#34;&gt;3e6c614b92c907987cf50029eb4512dba44dadd2&lt;/a&gt;&#xA;fix(deps): update module github.com/go-git/go-git/v5 to v5.13.1&#xA;&#xA;&lt;a href=&#34;https://codeberg.org/slidge/slidge/commit/9ff0fa678dccd7fc4be7cace72073b8d5fefd60a&#34;&gt;9ff0fa678dccd7fc4be7cace72073b8d5fefd60a&lt;/a&gt;&#xA;chore(deps): update davidanson/markdownlint-cli2 docker tag to v0.17.1&#xA;&#xA;&lt;a href=&#34;https://codeberg.org/slidge/slidge/commit/c8666a5831ecafa5c60450128f459191cd94c88a&#34;&gt;c8666a5831ecafa5c60450128f459191cd94c88a&lt;/a&gt;&#xA;fix(deps): update module github.com/go-git/go-git/v5 to v5.13.0</description>
+      <content:encoded><![CDATA[<a href="https://codeberg.org/slidge/slidge/commit/3efecf7655b24623a122170b872782ac9cab9344" rel="nofollow">3efecf7655b24623a122170b872782ac9cab9344</a>
+feat: allow multiple values for --cache-from
+
+<a href="https://codeberg.org/slidge/slidge/commit/d689954ee5afee840d9c3ccb37eeb434c5554325" rel="nofollow">d689954ee5afee840d9c3ccb37eeb434c5554325</a>
+chore(deps): update docker.io/mstruebing/editorconfig-checker docker tag to v3.1.2
+
+<a href="https://codeberg.org/slidge/slidge/commit/3e6c614b92c907987cf50029eb4512dba44dadd2" rel="nofollow">3e6c614b92c907987cf50029eb4512dba44dadd2</a>
+fix(deps): update module github.com/go-git/go-git/v5 to v5.13.1
+
+<a href="https://codeberg.org/slidge/slidge/commit/9ff0fa678dccd7fc4be7cace72073b8d5fefd60a" rel="nofollow">9ff0fa678dccd7fc4be7cace72073b8d5fefd60a</a>
+chore(deps): update davidanson/markdownlint-cli2 docker tag to v0.17.1
+
+<a href="https://codeberg.org/slidge/slidge/commit/c8666a5831ecafa5c60450128f459191cd94c88a" rel="nofollow">c8666a5831ecafa5c60450128f459191cd94c88a</a>
+fix(deps): update module github.com/go-git/go-git/v5 to v5.13.0]]></content:encoded>
+      <author>nicoco</author>
+      <guid isPermaLink="false">22094312: /slidge/slidge/compare/6e77ed7510a1dfd23f35b81affc90ded895515e6...3efecf7655b24623a122170b872782ac9cab9344</guid>
+      <pubDate>Thu, 16 Jan 2025 08:16:32 +0000</pubDate>
+    </item>
+    <item>
+      <title>nicoco created branch buildx in slidge/slidge</title>
+      <link>https://codeberg.org/slidge/slidge/src/branch/buildx</link>
+      <description></description>
+      <author>nicoco</author>
+      <guid isPermaLink="false">22094307: https://codeberg.org/slidge/slidge/src/branch/buildx</guid>
+      <pubDate>Thu, 16 Jan 2025 08:16:31 +0000</pubDate>
+    </item>
+    <item>
+      <title>nicoco pushed to dev at slidge/slidge</title>
+      <link>https://codeberg.org/slidge/slidge/commit/97a2c6d48ec1fc2fd085c3e4ff49ac60a17a76f9</link>
+      <description>&lt;a href=&#34;https://codeberg.org/slidge/slidge/commit/97a2c6d48ec1fc2fd085c3e4ff49ac60a17a76f9&#34;&gt;97a2c6d48ec1fc2fd085c3e4ff49ac60a17a76f9&lt;/a&gt;&#xA;fixup! ci</description>
+      <content:encoded><![CDATA[<a href="https://codeberg.org/slidge/slidge/commit/97a2c6d48ec1fc2fd085c3e4ff49ac60a17a76f9" rel="nofollow">97a2c6d48ec1fc2fd085c3e4ff49ac60a17a76f9</a>
+fixup! ci]]></content:encoded>
+      <author>nicoco</author>
+      <guid isPermaLink="false">22077338: https://codeberg.org/slidge/slidge/commit/97a2c6d48ec1fc2fd085c3e4ff49ac60a17a76f9</guid>
+      <pubDate>Wed, 15 Jan 2025 21:03:37 +0000</pubDate>
+    </item>
+    <item>
+      <title>nicoco pushed to dev at slidge/slidge</title>
+      <link>https://codeberg.org/slidge/slidge/commit/3100ed625b7c10eac11161b0aa89f8947ed93df9</link>
+      <description>&lt;a href=&#34;https://codeberg.org/slidge/slidge/commit/3100ed625b7c10eac11161b0aa89f8947ed93df9&#34;&gt;3100ed625b7c10eac11161b0aa89f8947ed93df9&lt;/a&gt;&#xA;fixup! ci</description>
+      <content:encoded><![CDATA[<a href="https://codeberg.org/slidge/slidge/commit/3100ed625b7c10eac11161b0aa89f8947ed93df9" rel="nofollow">3100ed625b7c10eac11161b0aa89f8947ed93df9</a>
+fixup! ci]]></content:encoded>
+      <author>nicoco</author>
+      <guid isPermaLink="false">22077169: https://codeberg.org/slidge/slidge/commit/3100ed625b7c10eac11161b0aa89f8947ed93df9</guid>
+      <pubDate>Wed, 15 Jan 2025 20:57:50 +0000</pubDate>
+    </item>
+    <item>
+      <title>nicoco pushed to dev at slidge/slidge</title>
+      <link>https://codeberg.org/slidge/slidge/commit/f1b58dc97cd7ecef06d4a37466021854ff32d801</link>
+      <description>&lt;a href=&#34;https://codeberg.org/slidge/slidge/commit/f1b58dc97cd7ecef06d4a37466021854ff32d801&#34;&gt;f1b58dc97cd7ecef06d4a37466021854ff32d801&lt;/a&gt;&#xA;fixup! ci</description>
+      <content:encoded><![CDATA[<a href="https://codeberg.org/slidge/slidge/commit/f1b58dc97cd7ecef06d4a37466021854ff32d801" rel="nofollow">f1b58dc97cd7ecef06d4a37466021854ff32d801</a>
+fixup! ci]]></content:encoded>
+      <author>nicoco</author>
+      <guid isPermaLink="false">22076086: https://codeberg.org/slidge/slidge/commit/f1b58dc97cd7ecef06d4a37466021854ff32d801</guid>
+      <pubDate>Wed, 15 Jan 2025 20:22:45 +0000</pubDate>
+    </item>
+    <item>
+      <title>nicoco pushed to dev at slidge/slidge</title>
+      <link>https://codeberg.org/slidge/slidge/commit/c3445fd943872f214535621e901aaa364ed7f195</link>
+      <description>&lt;a href=&#34;https://codeberg.org/slidge/slidge/commit/c3445fd943872f214535621e901aaa364ed7f195&#34;&gt;c3445fd943872f214535621e901aaa364ed7f195&lt;/a&gt;&#xA;fixup! ci</description>
+      <content:encoded><![CDATA[<a href="https://codeberg.org/slidge/slidge/commit/c3445fd943872f214535621e901aaa364ed7f195" rel="nofollow">c3445fd943872f214535621e901aaa364ed7f195</a>
+fixup! ci]]></content:encoded>
+      <author>nicoco</author>
+      <guid isPermaLink="false">22075973: https://codeberg.org/slidge/slidge/commit/c3445fd943872f214535621e901aaa364ed7f195</guid>
+      <pubDate>Wed, 15 Jan 2025 20:15:09 +0000</pubDate>
+    </item>
+    <item>
+      <title>nicoco pushed to dev at slidge/slidge</title>
+      <link>https://codeberg.org/slidge/slidge/commit/ff2e19117bc440b1de05acc78a4c6f0c6f072262</link>
+      <description>&lt;a href=&#34;https://codeberg.org/slidge/slidge/commit/ff2e19117bc440b1de05acc78a4c6f0c6f072262&#34;&gt;ff2e19117bc440b1de05acc78a4c6f0c6f072262&lt;/a&gt;&#xA;fixup! ci</description>
+      <content:encoded><![CDATA[<a href="https://codeberg.org/slidge/slidge/commit/ff2e19117bc440b1de05acc78a4c6f0c6f072262" rel="nofollow">ff2e19117bc440b1de05acc78a4c6f0c6f072262</a>
+fixup! ci]]></content:encoded>
+      <author>nicoco</author>
+      <guid isPermaLink="false">22075651: https://codeberg.org/slidge/slidge/commit/ff2e19117bc440b1de05acc78a4c6f0c6f072262</guid>
+      <pubDate>Wed, 15 Jan 2025 19:59:07 +0000</pubDate>
+    </item>
+    <item>
+      <title>nicoco pushed to dev at slidge/slidge</title>
+      <link>https://codeberg.org/slidge/slidge/commit/c18cdbb3238b81ba30b259c5e31de843e0014d6f</link>
+      <description>&lt;a href=&#34;https://codeberg.org/slidge/slidge/commit/c18cdbb3238b81ba30b259c5e31de843e0014d6f&#34;&gt;c18cdbb3238b81ba30b259c5e31de843e0014d6f&lt;/a&gt;&#xA;fixup! ci</description>
+      <content:encoded><![CDATA[<a href="https://codeberg.org/slidge/slidge/commit/c18cdbb3238b81ba30b259c5e31de843e0014d6f" rel="nofollow">c18cdbb3238b81ba30b259c5e31de843e0014d6f</a>
+fixup! ci]]></content:encoded>
+      <author>nicoco</author>
+      <guid isPermaLink="false">22075168: https://codeberg.org/slidge/slidge/commit/c18cdbb3238b81ba30b259c5e31de843e0014d6f</guid>
+      <pubDate>Wed, 15 Jan 2025 19:45:33 +0000</pubDate>
+    </item>
+    <item>
+      <title>c3p0-slidge pushed to main at slidge/pages</title>
+      <link>https://codeberg.org/slidge/pages/commit/40966a722929d092bb3eebc4729dfa73d47fde6d</link>
+      <description>&lt;a href=&#34;https://codeberg.org/slidge/pages/commit/40966a722929d092bb3eebc4729dfa73d47fde6d&#34;&gt;40966a722929d092bb3eebc4729dfa73d47fde6d&lt;/a&gt;&#xA;deploy docs for dfa0cd369d50fac8a4afb121a0a84dd43b310da7</description>
+      <content:encoded><![CDATA[<a href="https://codeberg.org/slidge/pages/commit/40966a722929d092bb3eebc4729dfa73d47fde6d" rel="nofollow">40966a722929d092bb3eebc4729dfa73d47fde6d</a>
+deploy docs for dfa0cd369d50fac8a4afb121a0a84dd43b310da7]]></content:encoded>
+      <author>c3p0-slidge</author>
+      <guid isPermaLink="false">22068872: https://codeberg.org/slidge/pages/commit/40966a722929d092bb3eebc4729dfa73d47fde6d</guid>
+      <pubDate>Wed, 15 Jan 2025 15:54:01 +0000</pubDate>
+    </item>
+    <item>
+      <title>nicoco pushed to dev at slidge/slidge</title>
+      <link>https://codeberg.org/slidge/slidge/commit/dfa0cd369d50fac8a4afb121a0a84dd43b310da7</link>
+      <description>&lt;a href=&#34;https://codeberg.org/slidge/slidge/commit/dfa0cd369d50fac8a4afb121a0a84dd43b310da7&#34;&gt;dfa0cd369d50fac8a4afb121a0a84dd43b310da7&lt;/a&gt;&#xA;fixup! ci</description>
+      <content:encoded><![CDATA[<a href="https://codeberg.org/slidge/slidge/commit/dfa0cd369d50fac8a4afb121a0a84dd43b310da7" rel="nofollow">dfa0cd369d50fac8a4afb121a0a84dd43b310da7</a>
+fixup! ci]]></content:encoded>
+      <author>nicoco</author>
+      <guid isPermaLink="false">22063456: https://codeberg.org/slidge/slidge/commit/dfa0cd369d50fac8a4afb121a0a84dd43b310da7</guid>
+      <pubDate>Wed, 15 Jan 2025 14:27:05 +0000</pubDate>
+    </item>
+    <item>
+      <title>nicoco pushed to dev at slidge/slidge</title>
+      <link>https://codeberg.org/slidge/slidge/commit/3686603bae3b714f831e11714e0774707c26562b</link>
+      <description>&lt;a href=&#34;https://codeberg.org/slidge/slidge/commit/3686603bae3b714f831e11714e0774707c26562b&#34;&gt;3686603bae3b714f831e11714e0774707c26562b&lt;/a&gt;&#xA;fixup! ci</description>
+      <content:encoded><![CDATA[<a href="https://codeberg.org/slidge/slidge/commit/3686603bae3b714f831e11714e0774707c26562b" rel="nofollow">3686603bae3b714f831e11714e0774707c26562b</a>
+fixup! ci]]></content:encoded>
+      <author>nicoco</author>
+      <guid isPermaLink="false">22063164: https://codeberg.org/slidge/slidge/commit/3686603bae3b714f831e11714e0774707c26562b</guid>
+      <pubDate>Wed, 15 Jan 2025 14:23:05 +0000</pubDate>
+    </item>
+    <item>
+      <title>nicoco pushed to dev at slidge/slidge</title>
+      <link>https://codeberg.org/slidge/slidge/commit/c6387ee4f1303de3cc3d1d1d0857c9062cbae795</link>
+      <description>&lt;a href=&#34;https://codeberg.org/slidge/slidge/commit/c6387ee4f1303de3cc3d1d1d0857c9062cbae795&#34;&gt;c6387ee4f1303de3cc3d1d1d0857c9062cbae795&lt;/a&gt;&#xA;fixup! ci</description>
+      <content:encoded><![CDATA[<a href="https://codeberg.org/slidge/slidge/commit/c6387ee4f1303de3cc3d1d1d0857c9062cbae795" rel="nofollow">c6387ee4f1303de3cc3d1d1d0857c9062cbae795</a>
+fixup! ci]]></content:encoded>
+      <author>nicoco</author>
+      <guid isPermaLink="false">22063114: https://codeberg.org/slidge/slidge/commit/c6387ee4f1303de3cc3d1d1d0857c9062cbae795</guid>
+      <pubDate>Wed, 15 Jan 2025 14:20:08 +0000</pubDate>
+    </item>
+  </channel>
+</rss>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_pubsub_forgejo/webhook-examples/push_tag/content.json	Mon Feb 17 23:28:05 2025 +0100
@@ -0,0 +1,185 @@
+{
+  "ref": "refs/tags/v0.2.0",
+  "before": "0000000000000000000000000000000000000000",
+  "after": "add5585f1b71de317aac5c54163ddb76c71856c6",
+  "compare_url": "https://codeberg.org/slidge/slidcord/compare/0000000000000000000000000000000000000000...add5585f1b71de317aac5c54163ddb76c71856c6",
+  "commits": [],
+  "total_commits": 0,
+  "head_commit": {
+    "id": "add5585f1b71de317aac5c54163ddb76c71856c6",
+    "message": "docs(readme): fix broken link to docs\n",
+    "url": "https://codeberg.org/slidge/slidcord/commit/add5585f1b71de317aac5c54163ddb76c71856c6",
+    "author": {
+      "name": "nicoco",
+      "email": "nicoco@nicoco.fr",
+      "username": "nicoco"
+    },
+    "committer": {
+      "name": "nicoco",
+      "email": "nicoco@nicoco.fr",
+      "username": "nicoco"
+    },
+    "verification": null,
+    "timestamp": "2025-02-08T14:07:44+01:00",
+    "added": [],
+    "removed": [],
+    "modified": [
+      "README.md"
+    ]
+  },
+  "repository": {
+    "id": 301856,
+    "owner": {
+      "id": 205842,
+      "login": "slidge",
+      "login_name": "",
+      "source_id": 0,
+      "full_name": "",
+      "email": "",
+      "avatar_url": "https://codeberg.org/avatars/aa0eeeb5fe173938bdc665be92bf605efa1e8f4a905bc3327e4d5c7eddf584e5",
+      "html_url": "https://codeberg.org/slidge",
+      "language": "",
+      "is_admin": false,
+      "last_login": "0001-01-01T00:00:00Z",
+      "created": "2024-08-30T14:16:14Z",
+      "restricted": false,
+      "active": false,
+      "prohibit_login": false,
+      "location": "",
+      "pronouns": "",
+      "website": "https://slidge.im",
+      "description": "",
+      "visibility": "public",
+      "followers_count": 4,
+      "following_count": 0,
+      "starred_repos_count": 0,
+      "username": "slidge"
+    },
+    "name": "slidcord",
+    "full_name": "slidge/slidcord",
+    "description": "A feature-rich Discord to XMPP gateway",
+    "empty": false,
+    "private": false,
+    "fork": false,
+    "template": false,
+    "parent": null,
+    "mirror": false,
+    "size": 847,
+    "language": "",
+    "languages_url": "https://codeberg.org/api/v1/repos/slidge/slidcord/languages",
+    "html_url": "https://codeberg.org/slidge/slidcord",
+    "url": "https://codeberg.org/api/v1/repos/slidge/slidcord",
+    "link": "",
+    "ssh_url": "git@codeberg.org:slidge/slidcord.git",
+    "clone_url": "https://codeberg.org/slidge/slidcord.git",
+    "original_url": "",
+    "website": "",
+    "stars_count": 0,
+    "forks_count": 0,
+    "watchers_count": 6,
+    "open_issues_count": 5,
+    "open_pr_counter": 0,
+    "release_counter": 0,
+    "default_branch": "main",
+    "archived": false,
+    "created_at": "2025-01-10T14:38:08Z",
+    "updated_at": "2025-02-04T19:37:54Z",
+    "archived_at": "1970-01-01T00:00:00Z",
+    "permissions": {
+      "admin": true,
+      "push": true,
+      "pull": true
+    },
+    "has_issues": true,
+    "internal_tracker": {
+      "enable_time_tracker": true,
+      "allow_only_contributors_to_track_time": true,
+      "enable_issue_dependencies": true
+    },
+    "has_wiki": false,
+    "wiki_branch": "main",
+    "globally_editable_wiki": false,
+    "has_pull_requests": true,
+    "has_projects": false,
+    "has_releases": true,
+    "has_packages": true,
+    "has_actions": false,
+    "ignore_whitespace_conflicts": false,
+    "allow_merge_commits": true,
+    "allow_rebase": true,
+    "allow_rebase_explicit": true,
+    "allow_squash_merge": true,
+    "allow_fast_forward_only_merge": true,
+    "allow_rebase_update": true,
+    "default_delete_branch_after_merge": false,
+    "default_merge_style": "merge",
+    "default_allow_maintainer_edit": false,
+    "default_update_style": "merge",
+    "avatar_url": "",
+    "internal": false,
+    "mirror_interval": "",
+    "object_format_name": "sha1",
+    "mirror_updated": "0001-01-01T00:00:00Z",
+    "repo_transfer": null,
+    "topics": [
+      "bridge",
+      "discord",
+      "gateway",
+      "instant-messaging",
+      "slidge",
+      "xmpp"
+    ]
+  },
+  "pusher": {
+    "id": 64076,
+    "login": "nicoco",
+    "login_name": "",
+    "source_id": 0,
+    "full_name": "",
+    "email": "nicoco@noreply.codeberg.org",
+    "avatar_url": "https://codeberg.org/avatars/32a918ca7a66e4e484ee2ccc625dc6451da728355c060ed0ed54fa69d89224a5",
+    "html_url": "https://codeberg.org/nicoco",
+    "language": "",
+    "is_admin": false,
+    "last_login": "0001-01-01T00:00:00Z",
+    "created": "2022-09-12T11:13:13Z",
+    "restricted": false,
+    "active": false,
+    "prohibit_login": false,
+    "location": "Nice, France",
+    "pronouns": "",
+    "website": "https://nicoco.fr",
+    "description": "wannabe-hacker",
+    "visibility": "public",
+    "followers_count": 2,
+    "following_count": 0,
+    "starred_repos_count": 4,
+    "username": "nicoco"
+  },
+  "sender": {
+    "id": 64076,
+    "login": "nicoco",
+    "login_name": "",
+    "source_id": 0,
+    "full_name": "",
+    "email": "nicoco@noreply.codeberg.org",
+    "avatar_url": "https://codeberg.org/avatars/32a918ca7a66e4e484ee2ccc625dc6451da728355c060ed0ed54fa69d89224a5",
+    "html_url": "https://codeberg.org/nicoco",
+    "language": "",
+    "is_admin": false,
+    "last_login": "0001-01-01T00:00:00Z",
+    "created": "2022-09-12T11:13:13Z",
+    "restricted": false,
+    "active": false,
+    "prohibit_login": false,
+    "location": "Nice, France",
+    "pronouns": "",
+    "website": "https://nicoco.fr",
+    "description": "wannabe-hacker",
+    "visibility": "public",
+    "followers_count": 2,
+    "following_count": 0,
+    "starred_repos_count": 4,
+    "username": "nicoco"
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_pubsub_forgejo/webhook-examples/push_tag/headers	Mon Feb 17 23:28:05 2025 +0100
@@ -0,0 +1,18 @@
+Content-Type: application/json
+X-Forgejo-Delivery: 82ddd28b-377f-487d-b0f7-e494db9cd763
+X-Forgejo-Event: push
+X-Forgejo-Event-Type: push
+X-Forgejo-Signature: 1cd5728a0b2e218c8dce3d706a2bdaaddcd9c32a0bad383d3762b95e2eef33c4
+X-GitHub-Delivery: 82ddd28b-377f-487d-b0f7-e494db9cd763
+X-GitHub-Event: push
+X-GitHub-Event-Type: push
+X-Gitea-Delivery: 82ddd28b-377f-487d-b0f7-e494db9cd763
+X-Gitea-Event: push
+X-Gitea-Event-Type: push
+X-Gitea-Signature: 1cd5728a0b2e218c8dce3d706a2bdaaddcd9c32a0bad383d3762b95e2eef33c4
+X-Gogs-Delivery: 82ddd28b-377f-487d-b0f7-e494db9cd763
+X-Gogs-Event: push
+X-Gogs-Event-Type: push
+X-Gogs-Signature: 1cd5728a0b2e218c8dce3d706a2bdaaddcd9c32a0bad383d3762b95e2eef33c4
+X-Hub-Signature: sha1=c8cc4aae9018b74a97f46c8766f9d5b85530baa1
+X-Hub-Signature-256: sha256=1cd5728a0b2e218c8dce3d706a2bdaaddcd9c32a0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_pubsub_forgejo/webhook-examples/release/content.json	Mon Feb 17 23:28:05 2025 +0100
@@ -0,0 +1,180 @@
+{
+  "action": "published",
+  "release": {
+    "id": 2802402,
+    "tag_name": "v0.2.0",
+    "target_commitish": "main",
+    "name": "broken",
+    "body": "It's broken! But I want to test the release webhook.",
+    "url": "https://codeberg.org/api/v1/repos/slidge/slidgnal/releases/2802402",
+    "html_url": "https://codeberg.org/slidge/slidgnal/releases/tag/v0.2.0",
+    "tarball_url": "https://codeberg.org/slidge/slidgnal/archive/v0.2.0.tar.gz",
+    "zipball_url": "https://codeberg.org/slidge/slidgnal/archive/v0.2.0.zip",
+    "hide_archive_links": false,
+    "upload_url": "https://codeberg.org/api/v1/repos/slidge/slidgnal/releases/2802402/assets",
+    "draft": false,
+    "prerelease": false,
+    "created_at": "2025-02-10T23:19:23Z",
+    "published_at": "2025-02-10T23:19:23Z",
+    "author": {
+      "id": 64076,
+      "login": "nicoco",
+      "login_name": "",
+      "source_id": 0,
+      "full_name": "",
+      "email": "nicoco@noreply.codeberg.org",
+      "avatar_url": "https://codeberg.org/avatars/32a918ca7a66e4e484ee2ccc625dc6451da728355c060ed0ed54fa69d89224a5",
+      "html_url": "https://codeberg.org/nicoco",
+      "language": "",
+      "is_admin": false,
+      "last_login": "0001-01-01T00:00:00Z",
+      "created": "2022-09-12T11:13:13Z",
+      "restricted": false,
+      "active": false,
+      "prohibit_login": false,
+      "location": "Nice, France",
+      "pronouns": "",
+      "website": "https://nicoco.fr",
+      "description": "wannabe-hacker",
+      "visibility": "public",
+      "followers_count": 2,
+      "following_count": 0,
+      "starred_repos_count": 4,
+      "username": "nicoco"
+    },
+    "assets": [],
+    "archive_download_count": {
+      "zip": 0,
+      "tar_gz": 0
+    }
+  },
+  "repository": {
+    "id": 301855,
+    "owner": {
+      "id": 205842,
+      "login": "slidge",
+      "login_name": "",
+      "source_id": 0,
+      "full_name": "",
+      "email": "",
+      "avatar_url": "https://codeberg.org/avatars/aa0eeeb5fe173938bdc665be92bf605efa1e8f4a905bc3327e4d5c7eddf584e5",
+      "html_url": "https://codeberg.org/slidge",
+      "language": "",
+      "is_admin": false,
+      "last_login": "0001-01-01T00:00:00Z",
+      "created": "2024-08-30T14:16:14Z",
+      "restricted": false,
+      "active": false,
+      "prohibit_login": false,
+      "location": "",
+      "pronouns": "",
+      "website": "https://slidge.im",
+      "description": "",
+      "visibility": "public",
+      "followers_count": 5,
+      "following_count": 0,
+      "starred_repos_count": 0,
+      "username": "slidge"
+    },
+    "name": "slidgnal",
+    "full_name": "slidge/slidgnal",
+    "description": "A feature-rich Signal to XMPP gateway",
+    "empty": false,
+    "private": false,
+    "fork": false,
+    "template": false,
+    "parent": null,
+    "mirror": false,
+    "size": 471,
+    "language": "",
+    "languages_url": "https://codeberg.org/api/v1/repos/slidge/slidgnal/languages",
+    "html_url": "https://codeberg.org/slidge/slidgnal",
+    "url": "https://codeberg.org/api/v1/repos/slidge/slidgnal",
+    "link": "",
+    "ssh_url": "git@codeberg.org:slidge/slidgnal.git",
+    "clone_url": "https://codeberg.org/slidge/slidgnal.git",
+    "original_url": "",
+    "website": "",
+    "stars_count": 0,
+    "forks_count": 0,
+    "watchers_count": 6,
+    "open_issues_count": 5,
+    "open_pr_counter": 0,
+    "release_counter": 1,
+    "default_branch": "main",
+    "archived": false,
+    "created_at": "2025-01-10T14:37:52Z",
+    "updated_at": "2025-02-09T06:26:00Z",
+    "archived_at": "1970-01-01T00:00:00Z",
+    "permissions": {
+      "admin": true,
+      "push": true,
+      "pull": true
+    },
+    "has_issues": true,
+    "internal_tracker": {
+      "enable_time_tracker": true,
+      "allow_only_contributors_to_track_time": true,
+      "enable_issue_dependencies": true
+    },
+    "has_wiki": false,
+    "wiki_branch": "main",
+    "globally_editable_wiki": false,
+    "has_pull_requests": true,
+    "has_projects": false,
+    "has_releases": true,
+    "has_packages": true,
+    "has_actions": false,
+    "ignore_whitespace_conflicts": false,
+    "allow_merge_commits": true,
+    "allow_rebase": true,
+    "allow_rebase_explicit": true,
+    "allow_squash_merge": true,
+    "allow_fast_forward_only_merge": true,
+    "allow_rebase_update": true,
+    "default_delete_branch_after_merge": false,
+    "default_merge_style": "merge",
+    "default_allow_maintainer_edit": false,
+    "default_update_style": "merge",
+    "avatar_url": "",
+    "internal": false,
+    "mirror_interval": "",
+    "object_format_name": "sha1",
+    "mirror_updated": "0001-01-01T00:00:00Z",
+    "repo_transfer": null,
+    "topics": [
+      "bridge",
+      "gateway",
+      "instant-messaging",
+      "signal",
+      "slidge",
+      "xmpp"
+    ]
+  },
+  "sender": {
+    "id": 64076,
+    "login": "nicoco",
+    "login_name": "",
+    "source_id": 0,
+    "full_name": "",
+    "email": "nicoco@noreply.codeberg.org",
+    "avatar_url": "https://codeberg.org/avatars/32a918ca7a66e4e484ee2ccc625dc6451da728355c060ed0ed54fa69d89224a5",
+    "html_url": "https://codeberg.org/nicoco",
+    "language": "",
+    "is_admin": false,
+    "last_login": "0001-01-01T00:00:00Z",
+    "created": "2022-09-12T11:13:13Z",
+    "restricted": false,
+    "active": false,
+    "prohibit_login": false,
+    "location": "Nice, France",
+    "pronouns": "",
+    "website": "https://nicoco.fr",
+    "description": "wannabe-hacker",
+    "visibility": "public",
+    "followers_count": 2,
+    "following_count": 0,
+    "starred_repos_count": 4,
+    "username": "nicoco"
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_pubsub_forgejo/webhook-examples/release/headers	Mon Feb 17 23:28:05 2025 +0100
@@ -0,0 +1,18 @@
+Content-Type: application/json
+X-Forgejo-Delivery: a5a5e377-23e0-4333-bd03-d6dac1f3e75c
+X-Forgejo-Event: release
+X-Forgejo-Event-Type: release
+X-Forgejo-Signature: 4a2ee940e19a1c16281ce219d23be37b7cf8c5ca45107658e560ab73e958b69b
+X-GitHub-Delivery: a5a5e377-23e0-4333-bd03-d6dac1f3e75c
+X-GitHub-Event: release
+X-GitHub-Event-Type: release
+X-Gitea-Delivery: a5a5e377-23e0-4333-bd03-d6dac1f3e75c
+X-Gitea-Event: release
+X-Gitea-Event-Type: release
+X-Gitea-Signature: 4a2ee940e19a1c16281ce219d23be37b7cf8c5ca45107658e560ab73e958b69b
+X-Gogs-Delivery: a5a5e377-23e0-4333-bd03-d6dac1f3e75c
+X-Gogs-Event: release
+X-Gogs-Event-Type: release
+X-Gogs-Signature: 4a2ee940e19a1c16281ce219d23be37b7cf8c5ca45107658e560ab73e958b69b
+X-Hub-Signature: sha1=c8e893eadcd047e87270bdf930088149948c5b1b
+X-Hub-Signature-256: sha256=4a2ee940e19a1c16281ce219d23be37b7cf8c5ca45107658e560ab73e958b69b
\ No newline at end of file