aboutsummaryrefslogtreecommitdiffstats
path: root/jeeves/admin.py
diff options
context:
space:
mode:
authorAhmed <git@gumx.cc>2026-06-14 01:46:29 +0300
committerAhmed <git@gumx.cc>2026-06-14 01:46:29 +0300
commit5a8d568931d9b23ce0df1265d05259a7012081c9 (patch)
tree4ea19b8bb1763a38246f342f172c285f6579e09b /jeeves/admin.py
init: mostly vibed
Diffstat (limited to 'jeeves/admin.py')
-rw-r--r--jeeves/admin.py185
1 files changed, 185 insertions, 0 deletions
diff --git a/jeeves/admin.py b/jeeves/admin.py
new file mode 100644
index 0000000..8738025
--- /dev/null
+++ b/jeeves/admin.py
@@ -0,0 +1,185 @@
+"""
+Sopel plugin: jeeves admin
+
+Owner commands for bot management and follow mode.
+
+ !join #channel - join a channel
+ !part [#channel] - leave a channel (defaults to current)
+ !chans - list joined channels
+ !follow <on|off> - follow owner into channels
+ !unfollow - leave all follow-joined channels immediately
+ !pause - pause keyword responses
+ !resume - resume keyword responses
+"""
+
+import os as _os, sys as _sys
+_d = _os.path.dirname(_os.path.abspath(__file__))
+if _d not in _sys.path:
+ _sys.path.insert(0, _d)
+import jv_core as jv
+
+from sopel import plugin
+from sopel.tools import events
+
+
+def setup(bot):
+ jv.ensure_setup(bot)
+ bot.memory.setdefault("jv_follow_enabled", False)
+ bot.memory.setdefault("jv_follow_joined", set())
+ bot.memory.setdefault("jv_whois_got_channels", False)
+ bot.memory.setdefault("jv_paused", False)
+
+
+def _configured_channels(bot):
+ chans = bot.settings.core.channels or []
+ return {c.split()[0].lower() for c in chans}
+
+
+@plugin.event("PART")
+def on_owner_part(bot, trigger):
+ if not jv.is_owner(bot, trigger):
+ return
+ channel = str(trigger.sender)
+ if channel.lower() in _configured_channels(bot):
+ return
+ if channel in {str(c) for c in bot.channels}:
+ bot.part(channel)
+ bot.memory.get("jv_follow_joined", set()).discard(channel.lower())
+
+
+@plugin.event("QUIT")
+def on_owner_quit(bot, trigger):
+ if not jv.is_owner(bot, trigger):
+ return
+ configured = _configured_channels(bot)
+ for chan in list(bot.channels):
+ if str(chan).lower() not in configured:
+ bot.part(chan)
+ bot.memory["jv_follow_joined"] = set()
+
+
+@plugin.command("join")
+def cmd_join(bot, trigger):
+ if not jv.is_owner(bot, trigger):
+ return
+ channel = (trigger.group(2) or "").strip()
+ if not channel.startswith("#"):
+ bot.notice("usage: !join #channel", trigger.nick)
+ return
+ bot.join(channel)
+
+
+@plugin.command("part")
+def cmd_part(bot, trigger):
+ if not jv.is_owner(bot, trigger):
+ return
+ channel = (trigger.group(2) or "").strip() or str(trigger.sender)
+ bot.part(channel)
+
+
+@plugin.command("chans")
+def cmd_chans(bot, trigger):
+ if not jv.is_owner(bot, trigger):
+ return
+ chans = sorted(str(c) for c in bot.channels)
+ bot.notice(("in: " + " ".join(chans)) if chans else "not in any channels", trigger.nick)
+
+
+@plugin.command("follow")
+def cmd_follow(bot, trigger):
+ if not jv.is_owner(bot, trigger):
+ return
+ arg = (trigger.group(2) or "").strip().lower()
+ if arg == "on":
+ bot.memory["jv_follow_enabled"] = True
+ bot.notice("follow on - tracking your channels every 30s", trigger.nick)
+ elif arg == "off":
+ bot.memory["jv_follow_enabled"] = False
+ bot.notice("follow off", trigger.nick)
+ else:
+ state = "on" if bot.memory.get("jv_follow_enabled") else "off"
+ bot.notice(f"follow is {state} — usage: !follow <on|off>", trigger.nick)
+
+
+@plugin.command("unfollow")
+def cmd_unfollow(bot, trigger):
+ if not jv.is_owner(bot, trigger):
+ return
+ joined = set(bot.memory.get("jv_follow_joined", set()))
+ if not joined:
+ bot.notice("not in any follow-joined channels", trigger.nick)
+ return
+ for chan in joined:
+ bot.part(chan)
+ bot.memory["jv_follow_joined"] = set()
+ bot.notice(f"left {len(joined)} channel(s)", trigger.nick)
+
+
+@plugin.command("pause")
+def cmd_pause(bot, trigger):
+ if not jv.is_owner(bot, trigger):
+ return
+ bot.memory["jv_paused"] = True
+ bot.notice("keyword responses paused", trigger.nick)
+
+
+@plugin.command("resume")
+def cmd_resume(bot, trigger):
+ if not jv.is_owner(bot, trigger):
+ return
+ bot.memory["jv_paused"] = False
+ bot.notice("keyword responses resumed", trigger.nick)
+
+
+@plugin.interval(30)
+def _follow_tick(bot):
+ if not bot.memory.get("jv_follow_enabled"):
+ return
+ bot.memory["jv_whois_got_channels"] = False
+ bot.write(("WHOIS", bot.settings.core.owner))
+
+
+@plugin.rule(r"(.*)")
+@plugin.event(events.RPL_WHOISCHANNELS)
+@plugin.priority("high")
+def _whois_channels(bot, trigger):
+ if not bot.memory.get("jv_follow_enabled"):
+ return
+ owner = bot.settings.core.owner
+ if len(trigger.args) < 2 or trigger.args[1] != owner:
+ return
+
+ bot.memory["jv_whois_got_channels"] = True
+ raw = (trigger.group(1) or "").strip()
+ owner_chans = set()
+ for part in raw.split():
+ chan = part.lstrip("@+~&%")
+ if chan.startswith("#"):
+ owner_chans.add(chan.lower())
+
+ current_chans = {str(c).lower() for c in bot.channels}
+ follow_joined = bot.memory.get("jv_follow_joined", set())
+
+ for chan in owner_chans:
+ if chan not in current_chans:
+ bot.join(chan)
+ follow_joined.add(chan)
+
+ bot.memory["jv_follow_joined"] = follow_joined
+
+
+@plugin.rule(r"(.*)")
+@plugin.event(events.RPL_ENDOFWHOIS)
+@plugin.priority("high")
+def _whois_end(bot, trigger):
+ if not bot.memory.get("jv_follow_enabled"):
+ return
+ owner = bot.settings.core.owner
+ if len(trigger.args) < 2 or trigger.args[1] != owner:
+ return
+ if bot.memory.get("jv_whois_got_channels"):
+ return
+ follow_joined = set(bot.memory.get("jv_follow_joined", set()))
+ for chan in follow_joined:
+ bot.part(chan)
+ bot.memory["jv_follow_joined"] = set()