From 3748312c416aa842d87fd72d67a4d8e2bdd3ff09 Mon Sep 17 00:00:00 2001 From: dece Date: Mon, 2 Nov 2020 17:41:47 +0100 Subject: [PATCH] plugin: handle aliases for commands --- README.md | 2 +- edmond/plugin.py | 65 ++++++++++++++++++++++++++++++++--------- edmond/plugins/mood.py | 2 ++ edmond/plugins/notes.py | 8 ++--- edmond/plugins/sleep.py | 6 ++-- 5 files changed, 59 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 1a206a8..e445fe1 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Missing features - [x] Wikipedia: find definition, get random page - [ ] Wolframalpha - [ ] Youtube: parsing for title, requests for channel or video -- [ ] Command aliases +- [x] Command aliases - [ ] Question aliases - [x] Sleep - [ ] Various macros: diff --git a/edmond/plugin.py b/edmond/plugin.py index 055771d..e9c0e9a 100644 --- a/edmond/plugin.py +++ b/edmond/plugin.py @@ -129,33 +129,59 @@ class Plugin: return False # Is it a valid command? - command = self.parse_command(message, no_content=no_content) - if not command: + parsed_command = self.__parse_command(message, no_content=no_content) + if not parsed_command: return False # Is it a command I can handle? - commands = self.config.get("commands", []) - if ( - any(command.ident == c for c in commands) or - (no_content and any(command.ident.startswith(c) for c in commands)) - ): - self.command = command - self.bot.log_d(f"Processing command from plugin {self.name}.") - return True + available_commands = self.config.get("commands", []) + aliases = self.config.get("aliases", {}) + for ident in available_commands: + # Match commands differently according to no_content. If no_content + # is True, check the parsed command (pc) raw data as a string that + # may contain the available identifier (ai) at its beginning. + # If no_content is False (default), simply compare the parsed + # identifier with available identifiers. + if no_content: + match = lambda pc, ai: ( + pc.raw == ai or pc.raw.startswith(ai + " ") + ) + else: + match = lambda pc, ai: pc.ident == ai + # First case: the command identifier has been used. + if match(parsed_command, ident): + parsed_command.ident = ident + parsed_command.match = ident + self.__save_command(parsed_command) + return True + # Second case: an alias of the identifier has been used. + ident_aliases = aliases.get(ident, []) + for alias in ident_aliases: + if match(parsed_command, alias): + parsed_command.ident = ident + parsed_command.match = alias + self.__save_command(parsed_command) + return True return False - def parse_command(self, message, no_content=False): - """Return a command ID if this message is a command.""" + def __parse_command(self, message, no_content=False): + """Return a command ID if this message is a command. + + The command raw field is always set. The ident and content fields are + not set when no_content is True. The match field is never set by this + method. + """ words = message.split() command_suffix = self.config["command_suffix"] if words[0].lower() in self.bot.names and words[-1] == command_suffix: + raw = " ".join(words[1:-1]) if no_content: - ident = " ".join(words[1:-1]) + ident = "" content = "" else: ident = words[1] content = " ".join(words[2:-1]) - return Command(ident, content) + return Command(ident, content, raw) def __respects_handling_conditions(self, exclude_conditions=None): """Check if question conditions are valid.""" @@ -172,6 +198,11 @@ class Plugin: return False return True + def __save_command(self, command): + """Save command in instance for further processing by the plugin.""" + self.command = command + self.bot.log_d(f"Processing command from plugin {self.name}: {command}") + def signal_failure(self, target): """Signal a plugin failure to target.""" self.bot.say(target, self.bot.config["error_message"]) @@ -185,5 +216,11 @@ class Question: @dataclass class Command: + # Identifier as set in config. Set even when an alias has been used. ident: str + # Content of the command when it has been parsed, empty str otherwise. content: str + # Raw command content (minus name and suffix), always set. + raw: str + # Identifier matched, possibly an alias. Set only when matched. + match: str = "" diff --git a/edmond/plugins/mood.py b/edmond/plugins/mood.py index 587d2f0..01c56bd 100644 --- a/edmond/plugins/mood.py +++ b/edmond/plugins/mood.py @@ -44,9 +44,11 @@ class MoodPlugin(Plugin): self.bot.say(event.target, random.choice(greetings)) def on_pubmsg(self, event): + # Only one command: calm down. if self.should_handle_command(event.arguments[0], no_content=True): self.calm_down(event.target) return True + # Only one question: what's your mood? if self.should_answer_question(event.arguments[0]): self.say_mood(event.target) return True diff --git a/edmond/plugins/notes.py b/edmond/plugins/notes.py index c4f31de..878b0fe 100644 --- a/edmond/plugins/notes.py +++ b/edmond/plugins/notes.py @@ -31,9 +31,8 @@ class NotesPlugin(Plugin): return False # "note down" command. - command0 = self.config["commands"][0] - if self.command.ident.startswith(command0): - content = self.command.ident[len(command0):].strip() + if self.command.ident == self.config["commands"][0]: + content = self.command.raw[len(self.command.match):].strip() match = self.content_re.match(content) if not match: return False @@ -59,8 +58,7 @@ class NotesPlugin(Plugin): return True # "deliver notes for me" command. - command1 = self.config["commands"][1] - if self.command.ident == command1: + if self.command.ident == self.config["commands"][1]: self.deliver_notes(event.target, event.source.nick) return True diff --git a/edmond/plugins/sleep.py b/edmond/plugins/sleep.py index 65cb93e..5871f6e 100644 --- a/edmond/plugins/sleep.py +++ b/edmond/plugins/sleep.py @@ -42,14 +42,12 @@ class SleepPlugin(Plugin): return False # "sleep" command. - command0 = self.config["commands"][0] - if self.command.ident.startswith(command0): + if self.command.ident == self.config["commands"][0]: self.fall_asleep() return True # "wake up" command. - command1 = self.config["commands"][1] - if self.command.ident.startswith(command1): + if self.command.ident == self.config["commands"][1]: self.wake_up() return True