From f2749a8d320442b590b654b5c3714b2e8b2b2a99 Mon Sep 17 00:00:00 2001 From: Xavier Arnaus Date: Wed, 6 Mar 2024 08:10:32 +0100 Subject: [PATCH] Avoid answering with ERROR_INVALID_ACTION if it's organic mention (#8) * Fix empty parameters when needed. Improve username removal. Add new error when no command * format * changelog * more format * Reflect the missing parameters error in the docs --------- Co-authored-by: Xavi --- CHANGELOG.md | 1 + docs/commands.md | 40 ++++++++++++++ mastofeed/lib/mentions_listener.py | 83 +++++++++++++++++++++++++----- 3 files changed, 112 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 683dc00..40cd791 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Bump `pyxavi` to `v0.8.0` ([#3](https://github.com/XaviArnaus/masto-feed/pull/3)) - Initial documentation ([#4](https://github.com/XaviArnaus/masto-feed/pull/4)) - Make Log rotation as default ([#5](https://github.com/XaviArnaus/masto-feed/pull/5)) +- Avoid answering with ERROR_INVALID_ACTION if it's organic mention ([#8](https://github.com/XaviArnaus/masto-feed/pull/8)) ### Fixed diff --git a/docs/commands.md b/docs/commands.md index a36101f..62244a3 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -92,6 +92,16 @@ will return something like @xavi I could not get a valid RSS feed from the given URL. Perhaps it is in a /blog subdirectory? ``` +🔴 When the URL is missing: +``` +@feeder test +``` + +will return something like +``` +@xavi Seems like you forgot parameters +``` + ### 💬 *add* command This command adds a given *Site URL* into the records, so the content will be gathered and processed. @@ -179,6 +189,16 @@ will return something like @xavi The alias is already taken ``` +🔴 When the parameters are missing: +``` +@feeder add +``` + +will return something like +``` +@xavi Seems like you forgot parameters +``` + ### 💬 *update* command This command updates an existing record's parameters. The only parameter that can't be changed is the `alias`, at it works as ID. All given values will overwrite the previous ones. @@ -250,6 +270,16 @@ will return something like @xavi I can't find that Alias in my records ``` +🔴 When the parameters are missing: +``` +@feeder update +``` + +will return something like +``` +@xavi Seems like you forgot parameters +``` + ### 💬 *remove* command This command deletes an existing record. @@ -280,4 +310,14 @@ will return something like will return something like ``` @xavi I can't find that Alias in my records +``` + +🔴 When the alias is missing: +``` +@feeder remove +``` + +will return something like +``` +@xavi Seems like you forgot parameters ``` \ No newline at end of file diff --git a/mastofeed/lib/mentions_listener.py b/mastofeed/lib/mentions_listener.py index 952728c..d3e441e 100644 --- a/mastofeed/lib/mentions_listener.py +++ b/mastofeed/lib/mentions_listener.py @@ -51,6 +51,8 @@ class MentionParser: ERROR_ALIAS_ALREADY_EXISTS = "The alias is already taken" ERROR_NOT_FOUND_ALIAS = "I can't find that Alias in my records" ERROR_NOT_ALLOWED = "You're not allowed to Create, Update or Remove records." + ERROR_NO_COMMAND = "hi! 👋🏼" + ERROR_MISSING_PARAMS = "Seems like you forgot parameters" INFO_ADDED = "Added" INFO_UPDATED = "Updated" INFO_REMOVED = "Removed" @@ -103,12 +105,14 @@ def parse(self) -> bool: # Before anything, remove the HTML stuff content = bs4(self.mention.content, features="html.parser").get_text() - # First, remove ourself from the mention - content = content.replace(self.me, "") + # Removing the self username from the mention, so we have a clean string to parse + username_position, content = self.remove_self_username_from_content(content=content) - # Wait, let's try the same without the domain in the user - small_me = self.small_user(self.me) - content = content.replace(small_me, "") + # If the username was NOT in the beginning, we assume that this is an organic mention, + # meaning that it does not contain a command. Let's be polite and say Hi! + if username_position > 0: + self.error = self.ERROR_NO_COMMAND + return False # ... and trim spaces content = content.strip() @@ -129,6 +133,7 @@ def parse(self) -> bool: # If we don't have a proper action, mark it and stop here if self.action is None: + self._logger.debug("No action, then complaining") self.error = self.ERROR_INVALID_ACTION return False @@ -146,8 +151,10 @@ def execute(self) -> bool: # Let's prepare the answer with the error # We add the HELLO in case of unknown action only if self.error == self.ERROR_INVALID_ACTION: + self._logger.debug("Error: Invalid action") text = f"{self.error}\n\n{self.INFO_HELLO}" else: + self._logger.debug(f"Error: General: {self.error}") text = self.error self.answer = StatusPost.from_dict( { @@ -160,7 +167,7 @@ def execute(self) -> bool: # So, let's answer according to the action elif self.action == MentionAction.HELLO: - + self._logger.debug("Action HELLO") self.answer = StatusPost.from_dict( { "status": self._format_answer(self.INFO_HELLO), @@ -171,7 +178,9 @@ def execute(self) -> bool: return True elif self.action == MentionAction.ADD: + self._logger.debug("Action ADD") if not self.user_can_write(): + self._logger.debug("not allowed to write write") self.answer = StatusPost.from_dict( { "status": self._format_answer(self.ERROR_NOT_ALLOWED), @@ -200,7 +209,9 @@ def execute(self) -> bool: return True elif self.action == MentionAction.UPDATE: + self._logger.debug("Action UPDATE") if not self.user_can_write(): + self._logger.debug("not allowed to write write") self.answer = StatusPost.from_dict( { "status": self._format_answer(self.ERROR_NOT_ALLOWED), @@ -229,7 +240,9 @@ def execute(self) -> bool: return True elif self.action == MentionAction.REMOVE: + self._logger.debug("Action REMOVE") if not self.user_can_write(): + self._logger.debug("not allowed to write write") self.answer = StatusPost.from_dict( { "status": self._format_answer(self.ERROR_NOT_ALLOWED), @@ -251,6 +264,7 @@ def execute(self) -> bool: return True elif self.action == MentionAction.LIST: + self._logger.debug("Action LIST") aliases = self._feeds_storage.get_all() if len(aliases) > 0: registers = [ @@ -271,6 +285,7 @@ def execute(self) -> bool: return True elif self.action == MentionAction.TEST: + self._logger.debug("Action TEST") if self.complements['site_url'] == self.complements['feed_url']: text = f"The site URL {self.complements['site_url']} appears to be " +\ "a valid feed itself" @@ -296,20 +311,20 @@ def _format_answer(self, text: str) -> str: return f"@{self.mention.username} {text}" - def parse_complements(self, words: list, quoted_text: str = None) -> None: + def parse_complements(self, words: list, quoted_text: str = None) -> bool: self._logger.debug("Parsing complements") - # If no complements, no parsing - if len(words) == 0: - return True - # So let's check the given complements as per every action needs. - elif self.action == MentionAction.HELLO: + if self.action == MentionAction.HELLO: # It does not need complements. return True elif self.action == MentionAction.ADD: + # Do we have actually words to parse? + if len(words) == 0: + self.error = self.ERROR_MISSING_PARAMS + return False # First word needs to be a valid URL first_word = words.pop(0) if not Url.is_valid(first_word): @@ -353,6 +368,10 @@ def parse_complements(self, words: list, quoted_text: str = None) -> None: return True elif self.action == MentionAction.UPDATE: + # Do we have actually words to parse? + if len(words) == 0: + self.error = self.ERROR_MISSING_PARAMS + return False # First word needs to be an alias first_word = words.pop(0) if not self._feeds_storage.key_exists(first_word): @@ -385,6 +404,10 @@ def parse_complements(self, words: list, quoted_text: str = None) -> None: return True elif self.action == MentionAction.REMOVE: + # Do we have actually words to parse? + if len(words) == 0: + self.error = self.ERROR_MISSING_PARAMS + return False # First word needs to be an alias first_word = words.pop(0) if not self._feeds_storage.key_exists(first_word): @@ -399,6 +422,10 @@ def parse_complements(self, words: list, quoted_text: str = None) -> None: return True elif self.action == MentionAction.TEST: + # Do we have actually words to parse? + if len(words) == 0: + self.error = self.ERROR_MISSING_PARAMS + return False # First word needs to be a valid URL first_word = words.pop(0) if not Url.is_valid(first_word): @@ -439,6 +466,38 @@ def get_text_inside_quotes(self, content) -> str: return m.group(1) + def remove_self_username_from_content(self, content: str): + """ + Removes the self username from the content. + """ + + # This is a mention. There has to be ALWAYS the self username anywhere in the content. + # The username is expressed as short @feeder or long @feeder@social.arnaus.net. + # Let's start with the long one, as it's the one we have without calculations. + + username_position = content.find(self.me) + + # We get a non -1 integer, meaning that the username is there + if username_position >= 0: + # Remove the username from the content + content = content.replace(self.me, "") + # Now return the position it was found + return username_position, content + + # Still here? Let's try now with the small version of the username + small_me = self.small_user(self.me) + username_position = content.find(small_me) + + # We get a non -1 integer, meaning that the username is there + if username_position >= 0: + # Remove the username from the content + content = content.replace(small_me, "") + # Now return the position it was found + return username_position, content + + # Cannot be that you're already here! Then return something unexpected + return -1, content + class MentionAction: