diff options
Diffstat (limited to 'jeeves/admin.py')
| -rw-r--r-- | jeeves/admin.py | 185 |
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() |
