# 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 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 {}.".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( "{} {}".format(cond, ", ".join(str(user) for user in users)) for cond, users in conds.iteritems() ) if existing: status = f"Current part conditions: {existing}." else: status = "No part conditions set for {}.".format( "this channel" if channel == data.chan else channel ) self.reply( data, f"{status} Usage: !{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 {} when {} joins.".format( "the channel" if channel == data.chan else channel, cond ), ) else: self.reply(data, f"Unknown event: {event} (valid events: join).") 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( f"Parting {data.chan} because {str(user)} met join condition {str(cond)}" ) self.part(data.chan, f"Requested to leave when {data.nick} joined") 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)