aboutsummaryrefslogtreecommitdiffstats
path: root/contrib
diff options
context:
space:
mode:
authorRobin Jarry <robin@jarry.cc>2023-06-25 13:33:44 +0200
committerRobin Jarry <robin@jarry.cc>2023-07-01 18:26:35 +0200
commit91dbd6078192e017b88bf061bb730e9d16ed5088 (patch)
treeedc2f7fcf65b699cc048b5aa5476afea02ae2aed /contrib
parent69094e332779a71ddd47b88bced0992c290d67e7 (diff)
downloadaerc-91dbd6078192e017b88bf061bb730e9d16ed5088.tar.gz
contrib: add irc bot stuff
Add a small script to install a sourcehut webhook that triggers on patchset reception. Add a limnoria (supybot fork) plugin to receive the webhook requests and send IRC NOTICE messages on the proper channels. Signed-off-by: Robin Jarry <robin@jarry.cc> Acked-by: Bence Ferdinandy <bence@ferdinandy.com>
Diffstat (limited to 'contrib')
-rw-r--r--contrib/ircbot/Sourcehut/README.md1
-rw-r--r--contrib/ircbot/Sourcehut/__init__.py21
-rw-r--r--contrib/ircbot/Sourcehut/config.py14
-rw-r--r--contrib/ircbot/Sourcehut/local/__init__.py1
-rw-r--r--contrib/ircbot/Sourcehut/plugin.py80
-rw-r--r--contrib/ircbot/Sourcehut/setup.py3
-rwxr-xr-xcontrib/ircbot/install-webhook.sh36
-rw-r--r--contrib/ircbot/nginx.conf36
-rw-r--r--contrib/ircbot/supybot.conf40
-rw-r--r--contrib/ircbot/supybot.service19
10 files changed, 251 insertions, 0 deletions
diff --git a/contrib/ircbot/Sourcehut/README.md b/contrib/ircbot/Sourcehut/README.md
new file mode 100644
index 00000000..dbc4311d
--- /dev/null
+++ b/contrib/ircbot/Sourcehut/README.md
@@ -0,0 +1 @@
+Supybot plugin to receive Sourcehut webhooks
diff --git a/contrib/ircbot/Sourcehut/__init__.py b/contrib/ircbot/Sourcehut/__init__.py
new file mode 100644
index 00000000..39a9beef
--- /dev/null
+++ b/contrib/ircbot/Sourcehut/__init__.py
@@ -0,0 +1,21 @@
+"""
+Sourcehut: Supybot plugin to receive Sourcehut webhooks
+"""
+
+import sys
+import supybot
+
+__version__ = "0.1"
+__author__ = supybot.authors.unknown
+__contributors__ = {}
+__url__ = ''
+
+from . import config
+from . import plugin
+from importlib import reload
+
+reload(config)
+reload(plugin)
+
+Class = plugin.Class
+configure = config.configure
diff --git a/contrib/ircbot/Sourcehut/config.py b/contrib/ircbot/Sourcehut/config.py
new file mode 100644
index 00000000..38e55427
--- /dev/null
+++ b/contrib/ircbot/Sourcehut/config.py
@@ -0,0 +1,14 @@
+from supybot import conf, registry
+try:
+ from supybot.i18n import PluginInternationalization
+ _ = PluginInternationalization('Sourcehut')
+except:
+ _ = lambda x: x
+
+
+def configure(advanced):
+ from supybot.questions import expect, anything, something, yn
+ conf.registerPlugin('Sourcehut', True)
+
+
+Sourcehut = conf.registerPlugin('Sourcehut')
diff --git a/contrib/ircbot/Sourcehut/local/__init__.py b/contrib/ircbot/Sourcehut/local/__init__.py
new file mode 100644
index 00000000..e86e97b8
--- /dev/null
+++ b/contrib/ircbot/Sourcehut/local/__init__.py
@@ -0,0 +1 @@
+# Stub so local is a module, used for third-party modules
diff --git a/contrib/ircbot/Sourcehut/plugin.py b/contrib/ircbot/Sourcehut/plugin.py
new file mode 100644
index 00000000..d66c1029
--- /dev/null
+++ b/contrib/ircbot/Sourcehut/plugin.py
@@ -0,0 +1,80 @@
+import json
+
+from supybot import ircmsgs, callbacks, httpserver, log, world
+from supybot.ircutils import bold, italic, underline
+
+
+class Sourcehut(callbacks.Plugin):
+ """
+ Supybot plugin to receive Sourcehut webhooks
+ """
+ def __init__(self, irc):
+ super().__init__(irc)
+ httpserver.hook("sourcehut", SourcehutServerCallback(self))
+
+ def die(self):
+ httpserver.unhook("sourcehut")
+ super().die()
+
+ def announce(self, channel, message):
+ libera = world.getIrc("libera")
+ if libera is None:
+ print("error: no irc libera")
+ return
+ if channel not in libera.state.channels:
+ print(f"error: not in {channel} channel")
+ return
+ libera.sendMsg(ircmsgs.notice(channel, message))
+
+
+class SourcehutServerCallback(httpserver.SupyHTTPServerCallback):
+ name = "Sourcehut"
+ defaultResponse = "Bad request\n"
+
+ def __init__(self, plugin: Sourcehut):
+ super().__init__()
+ self.plugin = plugin
+
+ SUBJECT = "[PATCH {prefix} v{version}] {subject}"
+ URL = "https://lists.sr.ht/{list[owner][canonicalName]}/{list[name]}/patches/{id}"
+ CHANS = {
+ "#public-inbox": "##rjarry",
+ "#aerc-devel": "#aerc",
+ }
+
+ def doPost(self, handler, path, form=None):
+ if hasattr(form, "decode"):
+ form = form.decode("utf-8")
+ print(f"POST {path} {form}")
+ try:
+ body = json.loads(form)
+ hook = body["data"]["webhook"]
+ if hook["event"] == "PATCHSET_RECEIVED":
+ patchset = hook["patchset"]
+ subject = self.SUBJECT.format(**patchset)
+ url = self.URL.format(**patchset)
+ if not url.startswith("https://lists.sr.ht/~rjarry/"):
+ raise ValueError("unknown list")
+ channel = f"#{patchset['list']['name']}"
+ channel = self.CHANS.get(channel, channel)
+ submitter = patchset["submitter"]["canonicalName"]
+ msg = f"received {bold(subject)} from {italic(submitter)}: {underline(url)}"
+ self.plugin.announce(channel, msg)
+ handler.send_response(200)
+ handler.end_headers()
+ handler.wfile.write(b"")
+ return
+
+ raise ValueError("unsupported webhook: %r" % hook)
+
+ except Exception as e:
+ print("ERROR", e)
+ handler.send_response(400)
+ handler.end_headers()
+ handler.wfile.write(b"Bad request\n")
+
+ def log_message(self, format, *args):
+ pass
+
+
+Class = Sourcehut
diff --git a/contrib/ircbot/Sourcehut/setup.py b/contrib/ircbot/Sourcehut/setup.py
new file mode 100644
index 00000000..11ba8772
--- /dev/null
+++ b/contrib/ircbot/Sourcehut/setup.py
@@ -0,0 +1,3 @@
+from supybot.setup import plugin_setup
+
+plugin_setup('Sourcehut')
diff --git a/contrib/ircbot/install-webhook.sh b/contrib/ircbot/install-webhook.sh
new file mode 100755
index 00000000..4d737f5b
--- /dev/null
+++ b/contrib/ircbot/install-webhook.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+set -xe
+
+hut lists webhook create "https://lists.sr.ht/~rjarry/aerc-devel" \
+ --stdin -e patchset_received \
+ -u https://bot.diabeteman.com/sourcehut/ <<EOF
+query {
+ webhook {
+ uuid
+ event
+ date
+ ... on PatchsetEvent {
+ patchset {
+ id
+ subject
+ version
+ prefix
+ list {
+ name
+ owner {
+ ... on User {
+ canonicalName
+ }
+ }
+ }
+ submitter {
+ ... on User {
+ canonicalName
+ }
+ }
+ }
+ }
+ }
+}
+EOF
diff --git a/contrib/ircbot/nginx.conf b/contrib/ircbot/nginx.conf
new file mode 100644
index 00000000..b68f860a
--- /dev/null
+++ b/contrib/ircbot/nginx.conf
@@ -0,0 +1,36 @@
+limit_req_zone $binary_remote_addr zone=aercbot:1m rate=1r/s;
+
+server {
+ listen 443 ssl;
+ listen [::]:443 ssl;
+ server_name bot.diabeteman.com;
+
+ ssl_certificate /etc/dehydrated/certs/diabeteman.com/fullchain.pem;
+ ssl_certificate_key /etc/dehydrated/certs/diabeteman.com/privkey.pem;
+
+ client_max_body_size 150K;
+ limit_req zone=aercbot burst=10 nodelay;
+
+ location / {
+ allow 173.195.146.144;
+ allow 2604:bf00:710:0:5054:ff:fec4:6bfb;
+ allow 2a01:cb00:f8b:9700:84d0:c0d6:72c3:677c;
+ deny all;
+ proxy_http_version 1.1;
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_redirect off;
+ proxy_buffering off;
+ proxy_request_buffering off;
+ proxy_pass http://127.0.0.1:7777;
+ }
+}
+
+server {
+ listen 80;
+ listen [::]:80;
+ server_name bot.diabeteman.com;
+ return 301 https://$host$request_uri;
+}
diff --git a/contrib/ircbot/supybot.conf b/contrib/ircbot/supybot.conf
new file mode 100644
index 00000000..f7cff21d
--- /dev/null
+++ b/contrib/ircbot/supybot.conf
@@ -0,0 +1,40 @@
+supybot.commands.allowShell = False
+supybot.ident = aercbot
+supybot.log.format = %(name)s: %(message)s
+supybot.log.plugins.format = %(message)s
+supybot.log.stdout.colorized = False
+supybot.log.stdout.format = %(message)s
+supybot.log.stdout.level = INFO
+supybot.log.stdout.wrap = False
+supybot.log.stdout = True
+supybot.networks.libera.channels = #aerc
+supybot.networks.libera.requireStarttls = False
+supybot.networks.libera.sasl.password = ********************
+supybot.networks.libera.sasl.required = True
+supybot.networks.libera.sasl.username = aercbot
+supybot.networks.libera.servers = irc.libera.chat:6697
+supybot.networks.libera.ssl = True
+supybot.networks = libera
+supybot.nick.alternates = %s` %s_
+supybot.nick = aercbot
+supybot.plugins.Channel.partMsg = KTHXBYE
+supybot.plugins.Karma.allowSelfRating = False
+supybot.plugins.Karma.allowUnaddressedKarma = True
+supybot.plugins.Karma.decrementChars = --
+supybot.plugins.Karma.incrementChars = ++
+supybot.plugins.Karma.mostDisplay = 25
+supybot.plugins.Karma.onlyNicks = False
+supybot.plugins.Karma.public = True
+supybot.plugins.Karma.rankingDisplay = 3
+supybot.plugins.Karma.response = True
+supybot.plugins.Karma.simpleOutput = True
+supybot.plugins.Karma = True
+supybot.plugins.Sourcehut.public = False
+supybot.plugins.Sourcehut = True
+supybot.servers.http.hosts4 = 127.0.0.1
+supybot.servers.http.hosts6 = ::1
+supybot.servers.http.keepAlive = False
+supybot.servers.http.port = 7777
+supybot.servers.http.publicUrl = https://bot.diabeteman.com/
+supybot.servers.http.singleStack = True
+supybot.user = aerc's IRC bot
diff --git a/contrib/ircbot/supybot.service b/contrib/ircbot/supybot.service
new file mode 100644
index 00000000..dd127850
--- /dev/null
+++ b/contrib/ircbot/supybot.service
@@ -0,0 +1,19 @@
+[Unit]
+Description=IRC bot
+After=network.target auditd.service
+
+[Service]
+ExecStart=/usr/bin/supybot /var/lib/supybot/supybot.conf
+User=supybot
+Group=supybot
+WorkingDirectory=/var/lib/supybot
+ProtectHome=true
+ProtectSystem=strict
+ReadWritePaths=/var/lib/supybot /tmp
+PrivateTmp=true
+SyslogIdentifier=supybot
+StandardOutput=journal
+StandardError=journal
+
+[Install]
+WantedBy=multi-user.target