diff --git a/modules/commands/__init__.py b/modules/commands/__init__.py index 2b03dee1..78cad2e8 100644 --- a/modules/commands/__init__.py +++ b/modules/commands/__init__.py @@ -90,10 +90,9 @@ class Module(ModuleManager.BaseModule): return True return False - def message(self, event, command, args_index=1): - args_split = event["message_split"][args_index:] + def _find_command_hook(self, server, command, is_channel): if not self.has_command(command): - aliases = self._get_aliases(event["server"]) + aliases = self._get_aliases(server) if command.lower() in aliases: command, _, new_args = aliases[command.lower()].partition(" ") @@ -102,12 +101,8 @@ class Module(ModuleManager.BaseModule): except IndexError: return + hook = None if self.has_command(command): - if self._is_ignored(event["server"], event["user"], command): - return - - hook = None - target = None for potential_hook in self.get_hooks(command): alias_of = self._get_alias_of(potential_hook) if alias_of: @@ -118,97 +113,92 @@ class Module(ModuleManager.BaseModule): "'%s' is an alias of unknown command '%s'" % (command.lower(), alias_of.lower())) - is_channel = "channel" in event if not is_channel and potential_hook.kwargs.get("channel_only"): continue if is_channel and potential_hook.kwargs.get("private_only"): continue hook = potential_hook - target = event["user"] if not is_channel else event["channel"] break - if not hook: - return + return hook - module_name = self._get_prefix(hook) or "" - if not module_name and hasattr(hook.function, "__self__"): - module_name = hook.function.__self__._name + def command(self, server, target, is_channel, user, command, args_split, + tags, statusmsg, hook, **kwargs): + if self._is_ignored(server, user, command): + return False - msgid = MSGID_TAG.get_value(event["tags"]) - statusmsg = "".join(event.get("statusmsg", [])) - stdout = outs.StdOut(event["server"], module_name, target, msgid, - statusmsg) - stderr = outs.StdErr(event["server"], module_name, target, msgid, - statusmsg) - command_method = self._command_method(target, event["server"]) + module_name = self._get_prefix(hook) or "" + if not module_name and hasattr(hook.function, "__self__"): + module_name = hook.function.__self__._name - if hook.kwargs.get("remove_empty", True): - args_split = list(filter(None, args_split)) + msgid = MSGID_TAG.get_value(tags) + stdout = outs.StdOut(server, module_name, target, msgid, statusmsg) + stderr = outs.StdErr(server, module_name, target, msgid, statusmsg) + command_method = self._command_method(target, server) - min_args = hook.kwargs.get("min_args") - if min_args and len(args_split) < min_args: - command_prefix = "" - if is_channel: - command_prefix = self._command_prefix(event["server"], - target) - usage = self._get_usage(hook, command, command_prefix) - if usage: - stderr.write("Not enough arguments, usage: %s" % - usage).send(command_method) - else: - stderr.write("Not enough arguments (minimum: %d)" % - min_args).send(command_method) + if hook.kwargs.get("remove_empty", True): + args_split = list(filter(None, args_split)) + + target.buffer.skip_next() + + min_args = hook.kwargs.get("min_args") + if min_args and len(args_split) < min_args: + command_prefix = "" + if is_channel: + command_prefix = self._command_prefix(server, target) + usage = self._get_usage(hook, command, command_prefix) + if usage: + stderr.write("Not enough arguments, usage: %s" % + usage).send(command_method) else: - returns = self.events.on("preprocess.command").call_unsafe( - hook=hook, user=event["user"], server=event["server"], - target=target, is_channel=is_channel, tags=event["tags"], - args_split=args_split) + stderr.write("Not enough arguments (minimum: %d)" % + min_args).send(command_method) + else: + returns = self.events.on("preprocess.command").call_unsafe( + hook=hook, user=user, server=server, target=target, + is_channel=is_channel, tags=tags, args_split=args_split, + command=command) - hard_fail = False - force_success = False - error = None - for returned in returns: - if returned == utils.consts.PERMISSION_HARD_FAIL: - hard_fail = True - break - elif returned == utils.consts.PERMISSION_FORCE_SUCCESS: - force_success = True - elif returned: - error = returned + hard_fail = False + force_success = False + error = None + for returned in returns: + if returned == utils.consts.PERMISSION_HARD_FAIL: + hard_fail = True + break + elif returned == utils.consts.PERMISSION_FORCE_SUCCESS: + force_success = True + elif returned: + error = returned - if hard_fail or (not force_success and error): - if error: - stderr.write(error).send(command_method) - target.buffer.skip_next() - return + if hard_fail or (not force_success and error): + if error: + stderr.write(error).send(command_method) + target.buffer.skip_next() + return True - args = " ".join(args_split) - server = event["server"] - user = event["user"] + args = " ".join(args_split) - new_event = self.events.on("received.command").on(command - ).make_event(user=user, server=server, target=target, - args=args, tags=event["tags"], args_split=args_split, - stdout=stdout, stderr=stderr, command=command.lower(), - is_channel=is_channel) + new_event = self.events.on(hook.event_name).make_event(user=user, + server=server, target=target, args=args, tags=tags, + args_split=args_split, stdout=stdout, stderr=stderr, + is_channel=is_channel, command=command, **kwargs) - self.log.trace("calling command '%s': %s", - [command, new_event.kwargs]) - try: - hook.call(new_event) - except utils.EventError as e: - stderr.write(str(e)) + self.log.trace("calling command '%s': %s", + [command, new_event.kwargs]) + try: + hook.call(new_event) + except utils.EventError as e: + stderr.write(str(e)) - if not hook.kwargs.get("skip_out", False): - command_method = self._command_method( - target, event["server"]) - stdout.send(command_method) - stderr.send(command_method) - target.last_stdout = stdout - target.last_stderr = stderr - target.buffer.skip_next() - event.eat() + if not hook.kwargs.get("skip_out", False): + command_method = self._command_method(target, server) + stdout.send(command_method) + stderr.send(command_method) + target.last_stdout = stdout + target.last_stderr = stderr + return new_event.eaten def _command_prefix(self, server, channel): return channel.get_setting("command-prefix", @@ -225,22 +215,53 @@ class Module(ModuleManager.BaseModule): prefixed_commands = event["channel"].get_setting("prefixed-commands", True) command_prefix = self._command_prefix(event["server"], event["channel"]) + command = None + args_split = None if event["message_split"][0].startswith(command_prefix): if not prefixed_commands: return command = event["message_split"][0].replace( command_prefix, "", 1).lower() - self.message(event, command) + args_split = event["message_split"][1:] elif len(event["message_split"]) > 1 and self.is_highlight( event["server"], event["message_split"][0]): command = event["message_split"][1].lower() - self.message(event, command, 2) + args_split = event["message_split"][2:] + + if command: + hook = self._find_command_hook(event["server"], command, True) + if hook: + self.command(event["server"], event["channel"], True, + event["user"], command, args_split, event["tags"], + "".join(event["statusmsg"]), hook) + else: + regex_hook = self.events.on("command.regex").get_hooks() + for hook in regex_hook: + pattern = hook.get_kwarg("pattern", None) + if not pattern and hook.get_kwarg("pattern-url", None) == "1": + pattern = utils.http.REGEX_URL + + if pattern: + match = re.match(pattern, event["message"]) + if match: + command = hook.get_kwarg("command", "") + res = self.command(event["server"], event["channel"], + True, event["user"], command, "", event["tags"], + "".join(event["statusmsg"]), hook, match=match, + message=event["message"]) + + if res: + break @utils.hook("received.message.private", priority=EventManager.PRIORITY_LOW) def private_message(self, event): if event["message_split"] and not event["action"]: command = event["message_split"][0].lower() - self.message(event, command) + hook = self._find_command_hook(event["server"], command, False) + if hook: + self.command(event["server"], event["user"], False, + event["user"], command, event["message_split"][1:], + event["tags"], "", hook) def _get_help(self, hook): return hook.get_kwarg("help", None) or hook.docstring.description diff --git a/modules/karma.py b/modules/karma.py index 2f631889..c9266ef8 100644 --- a/modules/karma.py +++ b/modules/karma.py @@ -20,68 +20,53 @@ class Module(ModuleManager.BaseModule): return utils.irc.color(str(karma), utils.consts.LIGHTGREEN) return str(karma) - @utils.hook("new.user") def new_user(self, event): event["user"].last_karma = None - @utils.hook("received.message.channel") + @utils.hook("command.regex") def channel_message(self, event): - match = re.match(REGEX_KARMA, event["message"].strip()) - if match and not event["action"]: - is_ignored_f = self.exports.get_one("is-ignored", - lambda _1, _2: False) - if is_ignored_f(event["server"], event["user"], "karma"): + """ + :command: karma + :pattern: ^(.*[^-+])[-+]*(\+{2,}|\-{2,})$ + """ + verbose = event["target"].get_setting("karma-verbose", False) + nickname_only = event["server"].get_setting("karma-nickname-only", + False) + + if not event["user"].last_karma or (time.time()-event["user" + ].last_karma) >= KARMA_DELAY_SECONDS: + target = event["match"].group(1).strip().rstrip("".join(WORD_STOP)) + if event["server"].irc_lower(target) == event["user"].name: + if verbose: + event["stdout"].write("You cannot change your own karma") return - is_silenced_f = self.exports.get_one("is-silenced", lambda _: False) - if is_silenced_f(event["channel"]): - return - - verbose = event["channel"].get_setting("karma-verbose", False) - nickname_only = event["server"].get_setting("karma-nickname-only", - False) - - if not event["user"].last_karma or (time.time()-event["user" - ].last_karma) >= KARMA_DELAY_SECONDS: - target = match.group(1).strip().rstrip("".join(WORD_STOP)) - if event["server"].irc_lower(target) == event["user"].name: - if verbose: - self.events.on("send.stderr").call( - module_name="Karma", target=event["channel"], - message="You cannot change your own karma", - server=event["server"]) + setting = "karma-%s" % target + setting_target = event["server"] + if nickname_only: + user = event["server"].get_user(target) + setting = "karma" + setting_target = user + if not event["target"].has_user(user): return - setting = "karma-%s" % target - setting_target = event["server"] - if nickname_only: - user = event["server"].get_user(target) - setting = "karma" - setting_target = user - if not event["channel"].has_user(user): - return + positive = event["match"].group(2)[0] == "+" + karma = setting_target.get_setting(setting, 0) + karma += 1 if positive else -1 - positive = match.group(2)[0] == "+" - karma = setting_target.get_setting(setting, 0) - karma += 1 if positive else -1 + if not karma == 0: + setting_target.set_setting(setting, karma) + else: + setting_target.del_setting(setting) - if not karma == 0: - setting_target.set_setting(setting, karma) - else: - setting_target.del_setting(setting) - - karma_str = self._karma_str(karma) - if verbose: - self.events.on("send.stdout").call( - module_name="Karma", target=event["channel"], - message="%s now has %s karma" % (target, karma_str), - server=event["server"]) - event["user"].last_karma = time.time() - elif verbose: - self.events.on("send.stderr").call(module_name="Karma", - target=event["channel"], server=event["server"], - message="Try again in a couple of seconds") + karma_str = self._karma_str(karma) + if verbose: + event["stdout"].write( + "%s now has %s karma" % (target, karma_str)) + event["user"].last_karma = time.time() + elif verbose: + event["stderr"].write("Try again in a couple of seconds") @utils.hook("received.command.karma") def karma(self, event): diff --git a/modules/sed.py b/modules/sed.py index f6849810..69cbaca7 100644 --- a/modules/sed.py +++ b/modules/sed.py @@ -12,19 +12,18 @@ REGEX_SED = re.compile("^s/") "validate": utils.bool_or_none}) class Module(ModuleManager.BaseModule): def _closest_setting(self, event, setting, default): - return event["channel"].get_setting(setting, + return event["target"].get_setting(setting, event["server"].get_setting(setting, default)) - @utils.hook("received.message.channel") + @utils.hook("command.regex") def channel_message(self, event): + """ + :command: sed + :pattern: ^s/ + """ sed_split = re.split(REGEX_SPLIT, event["message"], 3) if event["message"].startswith("s/") and len(sed_split) > 2: - if event["action"] or not self._closest_setting(event, "sed", - False): - return - is_ignored_f = short_url = self.exports.get_one("is-ignored", - lambda _1, _2: False) - if is_ignored_f(event["server"], event["user"], "sed"): + if not self._closest_setting(event, "sed", False): return regex_flags = 0 @@ -50,15 +49,13 @@ class Module(ModuleManager.BaseModule): pattern = re.compile(sed_split[1], regex_flags) except: traceback.print_exc() - self.events.on("send.stderr").call(target=event["channel"], - module_name="Sed", server=event["server"], - message="Invalid regex in pattern") + event["stderr"].write("Invalid regex in pattern") return replace = utils.irc.bold(sed_split[2].replace("\\/", "/")) for_user = event["user"].nickname if self._closest_setting(event, "sed-sender-only", False) else None - line = event["channel"].buffer.find(pattern, from_self=False, + line = event["target"].buffer.find(pattern, from_self=False, for_user=for_user, not_pattern=REGEX_SED) if line: new_message = re.sub(pattern, replace, line.message, count) @@ -66,6 +63,4 @@ class Module(ModuleManager.BaseModule): prefix = "* %s" % line.sender else: prefix = "<%s>" % line.sender - self.events.on("send.stdout").call(target=event[ - "channel"], module_name="Sed", server=event["server"], - message="%s %s" % (prefix, new_message)) + event["stdout"].write("%s %s" % (prefix, new_message)) diff --git a/modules/title.py b/modules/title.py index ba2d946c..79a9e8ab 100644 --- a/modules/title.py +++ b/modules/title.py @@ -44,40 +44,33 @@ class Module(ModuleManager.BaseModule): else: return None - @utils.hook("received.message.channel", + @utils.hook("command.regex", priority=EventManager.PRIORITY_MONITOR) def channel_message(self, event): - match = re.search(utils.http.REGEX_URL, event["message"]) - if match and event["channel"].get_setting("auto-title", False): - is_ignored_f = short_url = self.exports.get_one("is-ignored", - lambda _1, _2: False) - if is_ignored_f(event["server"], event["user"], "title"): - return + """ + :command: title + :pattern-url: 1 + """ + url = event["match"].group(0) + title = self._get_title(event["target"], event["match"].group(0)) - url = match.group(0) - title = self._get_title(event["channel"], match.group(0)) + if title: + message = title + if event["target"].get_setting("auto-title-first", False): + setting = "url-last-%s" % self._url_hash(url) + first_details = event["target"].get_setting(setting, None) - if title: - message = title - if event["channel"].get_setting("auto-title-first", False): - setting = "url-last-%s" % self._url_hash(url) - first_details = event["channel"].get_setting(setting, None) - - if first_details: - first_nickname, first_timestamp, _ = first_details - timestamp_parsed = utils.iso8601_parse(first_timestamp) - timestamp_human = utils.datetime_human(timestamp_parsed) - message = "%s (first posted by %s at %s)" % (title, - first_nickname, timestamp_human) - else: - event["channel"].set_setting(setting, - [event["user"].nickname, utils.iso8601_format_now(), - url]) - - - self.events.on("send.stdout").call(target=event["channel"], - message=message, module_name="Title", - server=event["server"]) + if first_details: + first_nickname, first_timestamp, _ = first_details + timestamp_parsed = utils.iso8601_parse(first_timestamp) + timestamp_human = utils.datetime_human(timestamp_parsed) + message = "%s (first posted by %s at %s)" % (title, + first_nickname, timestamp_human) + else: + event["target"].set_setting(setting, + [event["user"].nickname, utils.iso8601_format_now(), + url]) + event["stdout"].write(message) @utils.hook("received.command.t", alias_of="title") @utils.hook("received.command.title", usage="[URL]") diff --git a/modules/youtube.py b/modules/youtube.py index f29250db..8f01fd45 100644 --- a/modules/youtube.py +++ b/modules/youtube.py @@ -125,20 +125,17 @@ class Module(ModuleManager.BaseModule): else: event["stderr"].write("No search phrase provided") - @utils.hook("received.message.channel", + @utils.hook("command.regex", priority=EventManager.PRIORITY_LOW) def channel_message(self, event): - match = re.search(REGEX_YOUTUBE, event["message"]) - if match and event["channel"].get_setting("auto-youtube", False): - is_ignored_f = short_url = self.exports.get_one("is-ignored", - lambda _1, _2: False) - if is_ignored_f(event["server"], event["user"], "youtube"): - return - - youtube_id = match.group(1) + """ + :command: youtube + :-pattern: https?://(?:www.)? + (?:youtu.be/|youtube.com/watch\?[\S]*v=)([\w\-]{11}) + """ + if event["target"].get_setting("auto-youtube", False): + youtube_id = event["match"].group(1) video_details = self.video_details(youtube_id) if video_details: - self.events.on("send.stdout").call(target=event["channel"], - message=video_details, module_name="Youtube", - server=event["server"]) + event["stdout"].write(video_details) event.eat()