From de83bfadcc4807c07ba562f5d0ca599f126e2639 Mon Sep 17 00:00:00 2001 From: Ahmed Date: Thu, 18 Jun 2026 14:45:29 +0300 Subject: fix: mlmmj commands --- alfred/helpstrings.py | 4 ++- alfred/mlmmj.py | 91 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 91 insertions(+), 4 deletions(-) (limited to 'alfred') diff --git a/alfred/helpstrings.py b/alfred/helpstrings.py index 2abaa5f..15362be 100644 --- a/alfred/helpstrings.py +++ b/alfred/helpstrings.py @@ -59,12 +59,14 @@ VPN = { "remove": "vpn remove: remove a peer live and from wg0.conf. usage: !vpn remove name", } -LIST_TOPIC = "list: members subscribe unsubscribe send. type !help list for details" +LIST_TOPIC = "list: members subscribe unsubscribe send ls rm. type !help list for details" LIST = { "members": "list members: show all subscribers. usage: !list members", "subscribe": "list subscribe: subscribe an address. usage: !list subscribe email", "unsubscribe": "list unsubscribe: unsubscribe an address. usage: !list unsubscribe email", "send": "list send: send a message to the list. usage: !list send message", + "ls": "list ls: show latest n archive entries. usage: !list ls or !list ls n", + "rm": "list rm: remove a message from the archive. usage: !list rm id", } GIT_TOPIC = "git: list desc hook remove. type !help git for details" diff --git a/alfred/mlmmj.py b/alfred/mlmmj.py index 88a6333..8c3749b 100644 --- a/alfred/mlmmj.py +++ b/alfred/mlmmj.py @@ -5,20 +5,58 @@ Sopel plugin: mlmmj mailing list management !list unsubscribe - unsubscribe !list members - show all subscribers !list send - send a message to the list as owner + !list ls [n] - show latest n archive entries (default 5) + !list rm - remove a message from the archive immediately """ -import email.utils, glob, os, subprocess +import email.utils, glob, mailbox, os, subprocess +from email.header import decode_header as _decode_hdr +from email.utils import parseaddr, parsedate_to_datetime from sopel import plugin LIST_DIR = "/var/spool/mlmmj/list" LIST_ADDR = "list@gumx.cc" OWNER_ADDR = "ahmed@gumx.cc" +ARCHIVE = os.path.join(LIST_DIR, "archive") +ARCHIVE_GEN = "/home/ahmed/scripts/archive-gen.py" + +try: + with open(os.path.join(LIST_DIR, 'control', 'prefix')) as _f: + LIST_PREFIX = _f.read().strip() +except FileNotFoundError: + LIST_PREFIX = '' def _owner(bot, trigger): return trigger.nick == bot.settings.core.owner +def _decode(s): + if not s: + return '' + parts = _decode_hdr(s) + out = [] + for part, charset in parts: + if isinstance(part, bytes): + out.append(part.decode(charset or 'utf-8', errors='replace')) + else: + out.append(part) + return ''.join(out) + + +def _strip_prefix(subject): + if LIST_PREFIX and subject.startswith(LIST_PREFIX): + return subject[len(LIST_PREFIX):].lstrip() + return subject + + +def _regen(): + subprocess.run( + ["python3", ARCHIVE_GEN, LIST_DIR], + capture_output=True + ) + + @plugin.command("list") def cmd_list(bot, trigger): if not _owner(bot, trigger): @@ -26,7 +64,7 @@ def cmd_list(bot, trigger): args = (trigger.group(2) or "").split() if not args: - bot.say("usage: !list ") + bot.say("usage: !list ") return action = args[0] @@ -87,5 +125,52 @@ def cmd_list(bot, trigger): bot.say("queued" if r.returncode == 0 else f"error: {(r.stderr or r.stdout).strip()}") + elif action == "ls": + n = 5 + if len(args) >= 2: + try: + n = int(args[1]) + except ValueError: + bot.say("usage: !list ls [n]") + return + + items = sorted( + f for f in os.listdir(ARCHIVE) + if os.path.isfile(os.path.join(ARCHIVE, f)) and not f.endswith('.html') + ) + if not items: + bot.say("archive: empty") + return + + for fname in reversed(items[-n:]): + fpath = os.path.join(ARCHIVE, fname) + with open(fpath, 'rb') as fh: + msg = mailbox.Message(fh) + subject = _strip_prefix(_decode(msg.get('Subject', '(no subject)'))) + _, addr = parseaddr(_decode(msg.get('From', ''))) + sender = addr or _decode(msg.get('From', '')) + date_raw = msg.get('Date', '') + try: + date_str = parsedate_to_datetime(date_raw).strftime('%Y-%m-%d %H:%M') + except Exception: + date_str = date_raw[:16] if date_raw else '?' + bot.say(f"{fname}: [{date_str}] {sender}: {subject}") + + elif action == "rm": + if len(args) < 2: + bot.say("usage: !list rm ") + return + fid = args[1].strip() + fpath = os.path.join(ARCHIVE, fid) + if not os.path.isfile(fpath): + bot.say(f"rm: not found: {fid}") + return + os.remove(fpath) + html_path = fpath + '.html' + if os.path.isfile(html_path): + os.remove(html_path) + _regen() + bot.say(f"removed: {fid}") + else: - bot.say("usage: !list ") + bot.say("usage: !list ") -- cgit v1.2.3