From a4fe1f9722567873109c4b671a73dbbf74d518f3 Mon Sep 17 00:00:00 2001 From: Ben Kurtovic Date: Mon, 25 Jan 2021 00:48:53 -0500 Subject: [PATCH] Add !partwhen command --- commands/partwhen.py | 131 +++++++++++++++++++++++++++++++++++++++++++++++++ commands/rc_monitor.py | 1 + 2 files changed, 132 insertions(+) create mode 100644 commands/partwhen.py diff --git a/commands/partwhen.py b/commands/partwhen.py new file mode 100644 index 0000000..77145a5 --- /dev/null +++ b/commands/partwhen.py @@ -0,0 +1,131 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2021 Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import base64 +import cPickle as pickle +import re + +from earwigbot.commands import Command +from earwigbot.config.permissions import User + +class PartWhen(Command): + """Ask the bot to part the channel when a condition is met.""" + name = "partwhen" + commands = ["partwhen", "unpartwhen"] + hooks = ["join", "msg"] + + def setup(self): + self._conds = self._load_conditions() + + def check(self, data): + if data.is_command and data.command in self.commands: + return True + try: + if data.line[1] == "JOIN": + if data.chan in self._conds and "join" in self._conds[data.chan]: + return True + except IndexError: + pass + return False + + def process(self, data): + if data.line[1] == "JOIN": + self._handle_join(data) + return + + if not self.config.irc["permissions"].is_admin(data): + self.reply(data, "You must be a bot admin to use this command.") + return + + channel = data.chan + args = data.args + if args and args[0].startswith("#"): + # "!partwhen #channel ..." + channel = args[0] + args = args[1:] + + if data.command == "unpartwhen": + if self._conds.get(channel): + del self._conds[channel] + self._save_conditions() + self.reply(data, "Cleared part conditions for {0}.".format( + "this channel" if channel == data.chan else channel)) + else: + self.reply(data, "No part conditions set.") + return + + if not args: + conds = self._conds.get(channel, {}) + existing = "; ".join("{0} {1}".format(cond, ", ".join(str(user) for user in users)) + for cond, users in conds.iteritems()) + if existing: + status = "Current part conditions: {0}.".format(existing) + else: + status = "No part conditions set for {0}.".format( + "this channel" if channel == data.chan else channel) + self.reply(data, "{0} Usage: !{1} [] ...".format( + status, data.command)) + return + + event = args[0] + args = args[1:] + if event == "join": + if not args: + self.reply(data, "Join event requires an argument for the user joining, " + "in nick!ident@host syntax.") + return + cond = args[0] + match = re.match(r"(.*?)!(.*?)@(.*?)$", cond) + if not match: + self.reply(data, "User join pattern is invalid; should use " + "nick!ident@host syntax.") + return + conds = self._conds.setdefault(channel, {}).setdefault("join", []) + conds.append(User(match.group(1), match.group(2), match.group(3))) + self._save_conditions() + self.reply(data, "Okay, I will leave {0} when {1} joins.".format( + "the channel" if channel == data.chan else channel, cond)) + else: + self.reply(data, "Unknown event: {0} (valid events: join).".format(event)) + + def _handle_join(self, data): + user = User(data.nick, data.ident, data.host) + conds = self._conds.get(data.chan, {}).get("join", {}) + for cond in conds: + if user in cond: + self.logger.info("Parting {0} because {1} met join condition {2}".format( + data.chan, str(user), str(cond))) + self.part(data.chan, "Requested to leave when {0} joined".format(data.nick)) + break + + def _load_conditions(self): + permdb = self.config.irc["permissions"] + try: + raw = permdb.get_attr("command:partwhen", "data") + except KeyError: + return {} + return pickle.loads(base64.b64decode(raw)) + + def _save_conditions(self): + permdb = self.config.irc["permissions"] + raw = base64.b64encode(pickle.dumps(self._conds, pickle.HIGHEST_PROTOCOL)) + permdb.set_attr("command:partwhen", "data", raw) diff --git a/commands/rc_monitor.py b/commands/rc_monitor.py index 3f28eff..19c4659 100644 --- a/commands/rc_monitor.py +++ b/commands/rc_monitor.py @@ -49,6 +49,7 @@ class RCMonitor(Command): log = ('Cannot use without a report channel set as ' 'config.commands["{0}"]["channel"]') self.logger.warn(log.format(self.name)) + return self._stats = { "start": datetime.utcnow(),