diff options
author | Robin Jarry <robin@jarry.cc> | 2023-06-25 13:33:44 +0200 |
---|---|---|
committer | Robin Jarry <robin@jarry.cc> | 2023-07-01 18:26:35 +0200 |
commit | 91dbd6078192e017b88bf061bb730e9d16ed5088 (patch) | |
tree | edc2f7fcf65b699cc048b5aa5476afea02ae2aed /contrib | |
parent | 69094e332779a71ddd47b88bced0992c290d67e7 (diff) | |
download | aerc-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.md | 1 | ||||
-rw-r--r-- | contrib/ircbot/Sourcehut/__init__.py | 21 | ||||
-rw-r--r-- | contrib/ircbot/Sourcehut/config.py | 14 | ||||
-rw-r--r-- | contrib/ircbot/Sourcehut/local/__init__.py | 1 | ||||
-rw-r--r-- | contrib/ircbot/Sourcehut/plugin.py | 80 | ||||
-rw-r--r-- | contrib/ircbot/Sourcehut/setup.py | 3 | ||||
-rwxr-xr-x | contrib/ircbot/install-webhook.sh | 36 | ||||
-rw-r--r-- | contrib/ircbot/nginx.conf | 36 | ||||
-rw-r--r-- | contrib/ircbot/supybot.conf | 40 | ||||
-rw-r--r-- | contrib/ircbot/supybot.service | 19 |
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 |