From 5a8d568931d9b23ce0df1265d05259a7012081c9 Mon Sep 17 00:00:00 2001 From: Ahmed Date: Sun, 14 Jun 2026 01:46:29 +0300 Subject: init: mostly vibed --- alfred/soju.py | 193 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 alfred/soju.py (limited to 'alfred/soju.py') diff --git a/alfred/soju.py b/alfred/soju.py new file mode 100644 index 0000000..02428a5 --- /dev/null +++ b/alfred/soju.py @@ -0,0 +1,193 @@ +""" +Sopel plugin: soju IRC bouncer management + +All commands are owner-only and work in channel or PM. + + !irc - show usage + + !irc user list - list all bouncer users + !irc user add - create a new user + !irc user remove - delete a user + !irc user passwd - change a user's password + !irc user enable - re-enable a disabled user + !irc user disable - disable a user + + !irc net list - list networks for a user + !irc net status - show network connection status + !irc net add - add a network using a preset + !irc net add - add a network with a custom address + !irc net remove - remove a network + !irc net quote - send raw IRC command to a network + !irc net presets - list available network presets + + !irc bouncer - show bouncer-wide stats + !irc notice - broadcast a notice to all users +""" + +import subprocess + +from sopel import plugin +from sopel.config.types import FilenameAttribute, StaticSection +import os as _os, sys as _sys +_alfred_dir = _os.path.dirname(_os.path.abspath(__file__)) +if _alfred_dir not in _sys.path: + _sys.path.insert(0, _alfred_dir) +import helpstrings as h + + +NETWORK_PRESETS = { + "libera": "ircs://irc.libera.chat:6697", + "hackint": "ircs://irc.hackint.org:6697", + "oftc": "ircs://irc.oftc.net:6697", + "rizon": "ircs://irc.rizon.net:6697", + "efnet": "ircs://irc.efnet.org:6697", +} + + +class SojuSection(StaticSection): + config_path = FilenameAttribute("config_path", relative=False) + + +def setup(bot): + bot.settings.define_section("soju", SojuSection) + + +def _is_owner(bot, trigger): + return trigger.nick == bot.settings.core.owner + + +def _sojuctl(bot, *args, timeout=15): + cmd = ["sojuctl"] + cfg = bot.settings.soju.config_path + if cfg: + cmd += ["-config", cfg] + cmd += list(args) + try: + result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout) + out = (result.stdout + result.stderr).strip() + return out[:400] if out else "(no output)" + except subprocess.TimeoutExpired: + return "(timed out)" + except Exception as e: + return f"(error: {e})" + + +def _send(bot, text): + for line in text.splitlines(): + if line.strip(): + bot.say(line.strip()) + + +# --------------------------------------------------------------------------- +# !irc +# --------------------------------------------------------------------------- + +@plugin.command("irc") +def cmd_irc(bot, trigger): + if not _is_owner(bot, trigger): + return + + args = (trigger.group(2) or "").split() + + if not args: + bot.say(h.IRC_TOPIC) + return + + sub = args[0] + + if sub == "bouncer": + _send(bot, _sojuctl(bot, "server", "status")) + + elif sub == "notice": + if len(args) < 2: + bot.say("usage: !irc notice ") + return + _send(bot, _sojuctl(bot, "server", "notice", " ".join(args[1:]))) + + elif sub == "user": + _cmd_user(bot, args[1:]) + + elif sub == "net": + _cmd_net(bot, args[1:]) + + else: + bot.say("unknown subcommand - type !irc for usage") + + +# --------------------------------------------------------------------------- +# user subcommands +# --------------------------------------------------------------------------- + +def _cmd_user(bot, args): + if not args: + bot.say("usage: !irc user |remove |passwd |enable |disable >") + return + + sub = args[0] + + if sub == "list": + _send(bot, _sojuctl(bot, "user", "status")) + + elif sub == "add" and len(args) == 3: + _send(bot, _sojuctl(bot, "user", "create", + "-username", args[1], "-password", args[2])) + + elif sub == "remove" and len(args) == 2: + _send(bot, _sojuctl(bot, "user", "delete", args[1])) + + elif sub == "passwd" and len(args) == 3: + _send(bot, _sojuctl(bot, "user", "update", args[1], "-password", args[2])) + + elif sub == "enable" and len(args) == 2: + _send(bot, _sojuctl(bot, "user", "update", args[1], "-enabled", "true")) + + elif sub == "disable" and len(args) == 2: + _send(bot, _sojuctl(bot, "user", "update", args[1], "-enabled", "false")) + + else: + bot.say("usage: !irc user |remove |passwd |enable |disable >") + + +# --------------------------------------------------------------------------- +# net subcommands +# --------------------------------------------------------------------------- + +def _cmd_net(bot, args): + if not args: + bot.say("usage: !irc net |status |add [addr]|remove |quote |presets>") + return + + sub = args[0] + + if sub == "presets": + bot.say("presets: " + ", ".join(f"{k} ({v})" for k, v in NETWORK_PRESETS.items())) + + elif sub == "list" and len(args) == 2: + _send(bot, _sojuctl(bot, "user", "run", args[1], "network", "status")) + + elif sub == "status" and len(args) == 2: + _send(bot, _sojuctl(bot, "user", "run", args[1], "network", "status")) + + elif sub == "add" and len(args) == 3: + name = args[2] + addr = NETWORK_PRESETS.get(name) + if not addr: + bot.say(f"unknown preset '{name}' - use !irc net presets or provide an address") + return + _send(bot, _sojuctl(bot, "user", "run", args[1], + "network", "create", "-name", name, "-addr", addr)) + + elif sub == "add" and len(args) == 4: + _send(bot, _sojuctl(bot, "user", "run", args[1], + "network", "create", "-name", args[2], "-addr", args[3])) + + elif sub == "remove" and len(args) == 3: + _send(bot, _sojuctl(bot, "user", "run", args[1], + "network", "delete", args[2])) + + elif sub == "quote" and len(args) >= 4: + _send(bot, _sojuctl(bot, "user", "run", args[1], + "network", "quote", args[2], " ".join(args[3:]))) + + else: + bot.say("usage: !irc net |status |add [addr]|remove |quote |presets>") -- cgit v1.2.3