Git Product home page Git Product logo

mastobot's Introduction

mastobot's People

Contributors

e-dreyer avatar

Stargazers

 avatar  avatar

mastobot's Issues

API helper functions

Problem

The MastoBot package is intended to help simplify the process of developing a Mastodon bot. For this reason, certain commonly used requests and calculations should be included in the main package. These functions should stay true to the generic implementation of the MastoBot class and should only serve as helper functions.

Furthermore, the API level provided by Mastodon.py is abstracted and hidden away as a private variable. In Python, private variables can however still be externally accessed, but this is not good practice. Currently, users of MastoBot are limited by this and are unable to implement the following functions.

Functions

isByFollower

As a user of the bot, it would be useful to have access to a simple function to determine whether a user is a follower of the bot account. Determinining whether a user is a follower, requires a call to the Mastodon.py function account_relationships. The output of this function should then further be processed to determine whether a user is a follower.

Proposed implementation

def isByFollower(self, status_id: int) -> bool:
        api_mention = self.getStatus(status_id)
        relationships = self._api.account_relationships(api_mention.get("account"))
        return relationships[0].get("followed_by", False)

isParentStatus

One common design decision when creating group bots, MastoBot-3D being one, is to have responses and behviours being dependent on whether a post is a parent status. A parent status is one which is not a reply to any other status or notification and can be seen as a root status. A child status is one which has a parent.

Proposed implementation

def isParentStatus(self, status_id: int) -> bool:
        api_status = self.getStatus(status_id)
        if api_status.get("in_reply_to_id"):
            return False
        else:
            return True

Regex system

Problem

MastoBot-3D currently implements a reporting system, allowing users to mention the bot as @3dprinting $report <some report message>.

This allows for a custom response to reply to the report with a templating system similar to the proposed one in #7 and also sends a message to the moderators.

This functionality can be extended to allow for more advanced commands to be sent to the bot and is generic enough to be part of MastoBot

Solution

A possinle solution would be to allow users to supply MastoBot with a list of regex expressions and a list of callbacks. Thus allowing the user to implement actions similar to how abstract class methods are currently used to implement custom behavior.

Improved exception handling required

Problem

Currently MastoBot implements a wrapper and decorator function which allows for the handling of exceptions in most functions making API calls through Mastodon.py. This however is not the best solution. Exceptions are still able to slip through and there is actually no handling of exceptions and they are rather logged.

It should be investigated how a queue system for API calls can be created and operations then postponed until a connection error or rate limiting possibly sort itself out.

Related issues

Current solution

def handleMastodonExceptions(func) -> Callable:
    def wrapper(self, *args, **kwargs):
        try:
            result = func(self, *args, **kwargs)
            return result
        except MastodonServerError as e:
            logging.critical(f"MastodonServerError: {e}")
        except MastodonIllegalArgumentError as e:
            logging.critical(f"MastodonIllegalArgumentError: {e}")
        except MastodonFileNotFoundError as e:
            logging.critical(f"MastodonFileNotFoundError: {e}")
        except MastodonNetworkError as e:
            logging.critical(f"MastodonNetworkError: {e}")
        except MastodonAPIError as e:
            logging.critical(f"MastodonAPIError: {e}")
        except MastodonMalformedEventError as e:
            logging.critical(f"MastodonMalformedEventError: {e}")
        except MastodonRatelimitError as e:
            logging.critical(f"MastodonRatelimitError: {e}")
        except MastodonVersionError as e:
            logging.critical(f"MastodonVersionError: {e}")
        except Exception as e:
            logging.critical(f"Error in function {func.__name__}")
            logging.critical(e)
            raise e
    return wrapper

Allow for profile values to be set

Problem

Part of the simplification of creating a bot is defining the profile of the bot. Users should be able to create templates as mentioned in #7 to define the profile, bio and fields of the profile of the bot.

Templating system

Problem

MastoBot is intended to simplify the process of creating bots for Mastodon. This involves allowing the user to perform actions on notifications and filtering them, but often, users would like to respond to a notification with a post.

This can easily be done in code, but Jinja2 templates are by far the most sustainable and future-proof method to do this.

It would allow for better customization and better implementations, and templates could also be easily shared between users but still allow for a personalized touch.

Such a templating system is generic enough to be part of MastoBot

Misallignment in logging for processMention

Problem

The alignment for processMention is wrong in the logging.

2023-07-26 09:33:53 2023-07-26 07:33:53,461   - [INFO] - mastoBot.refresh_rate                   - ⌛     Refresh rate set to: 10                       
2023-07-26 09:33:53 2023-07-26 07:33:53,461   - [INFO] - mastoBot.__init__                       - ✅     Config and credentials initialized            
2023-07-26 09:33:54 2023-07-26 07:33:54,346   - [INFO] - mastoBot.__init__                       - ✅     Mastodon.py initialized                       
2023-07-26 09:33:54 2023-07-26 07:33:54,348   - [INFO] - mastoBot.run                            - ⛏️     Starting main loop                           
2023-07-26 09:34:48 2023-07-26 07:34:48,158   - [INFO] - mastoBot.reblogStatus                   - 🗣️     Status reblogged                             
2023-07-26 09:34:49 2023-07-26 07:34:49,570   - [INFO] - mastoBot.favoriteStatus                 - ⭐     Status favorited                              
2023-07-26 09:34:49 2023-07-26 07:34:49,573   - [INFO] - main.processMention                 - 📬         Mention processed: 2179567                    
2023-07-26 09:34:49 2023-07-26 07:34:49,872   - [INFO] - mastoBot.dismissNotification            - 📭     Notification 2179567 dismissed

Favourited statusses are being printed out in the log

Currently there is a bug where entire status objects are logged to the console when a new favorite is received.

2023-07-25 19:30:38,369 - [INFO] - mastoBot.favoriteStatus       - ⭐         Status favorited: {'id': 110776380880090671, 'created_at': datetime.datetime(2023, 7, 25, 19, 30, 27, tzinfo=tzlocal()), 'in_reply_to_id': 110776325607636451, 'in_reply_to_account_id': 109538684057237209, 'sensitive': False, 'spoiler_text': '', 'visibility': 'public', 'language': 'en', 'uri': 'https://mstdn.social/users/botolo86/statuses/110776380783827192', 'url': 'https://mstdn.social/@botolo86/110776380783827192', 'replies_count': 0, 'reblogs_count': 0, 'favourites_count': 0, 'edited_at': None, 'favourited': False, 'reblogged': False, 'muted': False, 'bookmarked': False, 'content': '<p><span class="h-card"><a href="https://techhub.social/@Stark9837" class="u-url mention" rel="nofollow noopener noreferrer" target="_blank">@<span>Stark9837</span></a></span> <span class="h-card"><a href="https://tech.lgbt/@jsj" class="u-url mention" rel="nofollow noopener noreferrer" target="_blank">@<span>jsj</span></a></span> <span class="h-card"><a href="https://techhub.social/@3dprinting" class="u-url mention" rel="nofollow noopener noreferrer" target="_blank">@<span>3dprinting</span></a></span> It looks like the V6 color engine of AnkerMake will have zero purge.</p>', 'filtered': [], 'reblog': None, 'account': {'id': 109469707711538381, 'username': 'botolo86', 'acct': '[email protected]', 'display_name': 'Botolo', 'locked': False, 'bot': False, 'discoverable': False, 'group': False, 'created_at': datetime.datetime(2022, 12, 7, 0, 0, tzinfo=tzlocal()), 'note': '', 'url': 'https://mstdn.social/@botolo86', 'avatar': 'https://files.techhub.social/cache/accounts/avatars/109/469/707/711/538/381/original/18821310774daf64.jpg', 'avatar_static': 'https://files.techhub.social/cache/accounts/avatars/109/469/707/711/538/381/original/18821310774daf64.jpg', 'header': 'https://techhub.social/headers/original/missing.png', 'header_static': 'https://techhub.social/headers/original/missing.png', 'followers_count': 99, 'following_count': 72, 'statuses_count': 418, 'last_status_at': datetime.datetime(2023, 7, 25, 0, 0), 'emojis': [], 'fields': []}, 'media_attachments': [], 'mentions': [{'id': 109538684057237209, 'username': 'Stark9837', 'url': 'https://techhub.social/@Stark9837', 'acct': 'Stark9837'}, {'id': 110513374291535967, 'username': 'jsj', 'url': 'https://tech.lgbt/@jsj', 'acct': '[email protected]'}, {'id': 109897487128650085, 'username': '3dprinting', 'url': 'https://techhub.social/@3dprinting', 'acct': '3dprinting'}], 'tags': [], 'emojis': [], 'card': None, 'poll': None}

Updating posts is not implemented

Problem

Currently, the notification type update isn't implemented. This notification was received for a user updating a type:poll post, but will occur for any edited status

2023-07-26 07:14:14 2023-07-26 05:14:14,942 - [INFO] - main.processMention       - 📬     Mention processed: 2179152
2023-07-26 07:14:15 2023-07-26 05:14:15,257 - [INFO] - mastoBot.dismissNotification  - 📭         Notification 2179152 dismissed
2023-07-26 07:14:15 2023-07-26 05:14:15,258 - [WARNING] - mastoBot._process_notifications - ❗    Invalid notification type: update
2023-07-26 07:14:25 2023-07-26 05:14:25,618 - [WARNING] - mastoBot._process_notifications - ❗    Invalid notification type: update
2023-07-26 07:14:36 2023-07-26 05:14:36,000 - [WARNING] - mastoBot._process_notifications - ❗    Invalid notification type: update
2023-07-26 07:14:46 2023-07-26 05:14:46,367 - [WARNING] - mastoBot._process_notifications - ❗    Invalid notification type: update
2023-07-26 07:14:56 2023-07-26 05:14:56,717 - [WARNING] - mastoBot._process_notifications - ❗    Invalid notification type: update
2023-07-26 07:15:07 2023-07-26 05:15:07,116 - [WARNING] - mastoBot._process_notifications - ❗    Invalid notification type: update
2023-07-26 07:15:17 2023-07-26 05:15:17,491 - [WARNING] - mastoBot._process_notifications - ❗    Invalid notification type: update

The culprit

It simply isn't implemented due to the fact that Mastodon.py documentation didn't include this type.

    @handleMastodonExceptions
    def _process_notifications(self, notifications: List[Dict[Any, Any]]) -> None:
        for notification in notifications:
            if notification.get("type") == "mention":
                self.processMention(notification)
            elif notification.get("type") == "reblog":
                self.processReblog(notification)
            elif notification.get("type") == "favourite":
                self.processFavourite(notification)
            elif notification.get("type") == "follow":
                self.processFollow(notification)
            elif notification.get("type") == "poll":
                self.processPoll(notification)
            elif notification.get("type") == "follow_request":
                self.processFollowRequest(notification)
            else:
                logging.warning(f"❗ \t Invalid notification type: {notification.get('type')}")

Solution

This can simply be fixed by adding a new abstract class method processUpdate.

    @handleMastodonExceptions
    def _process_notifications(self, notifications: List[Dict[Any, Any]]) -> None:
        for notification in notifications:
            if notification.get("type") == "mention":
                self.processMention(notification)
            elif notification.get("type") == "reblog":
                self.processReblog(notification)
            elif notification.get("type") == "favourite":
                self.processFavourite(notification)
            elif notification.get("type") == "follow":
                self.processFollow(notification)
            elif notification.get("type") == "poll":
                self.processPoll(notification)
            elif notification.get("type") == "follow_request":
                self.processFollowRequest(notification)
            elif notification.get("type") == "update": # This should be added
                self.processUpdate(notification)
            else:
                logging.warning(f"❗ \t Invalid notification type: {notification.get('type')}")

Unhandled MastodonNetworkError in MastoBot

Problem

There is an unhandled MastodonNetworkError exception in the decorator function used for logging exceptions. This exception however is not exported by Mastodon.py and further investigation should be done to see how it can be caught and handled.

Reproducing

Currently, I am unsure how to reproduce this bug and I am not sure what caused it. It happened while MastoBot-3D was under light load with only a few pending mentions and 1 follow notification

Error

2023-07-25 21:16:11 2023-07-25 19:16:11,061 - [CRITICAL] - mastoBot.wrapper              - MastodonNetworkError: Could not complete request: ('Connection aborted.', ConnectionResetError(104, 'Connection reset by peer'))
2023-07-25 21:16:11 2023-07-25 19:16:11,064 - [CRITICAL] - mastoBot.wrapper              - Error in function _process_notifications
2023-07-25 21:16:11 Traceback (most recent call last):
2023-07-25 21:16:11   File "/app/main.py", line 170, in <module>
2023-07-25 21:16:11     bot.run()
2023-07-25 21:16:11   File "/usr/local/lib/python3.9/site-packages/mastoBot/mastoBot.py", line 118, in run
2023-07-25 21:16:11     self._process_notifications(notifications)
2023-07-25 21:16:11   File "/usr/local/lib/python3.9/site-packages/mastoBot/mastoBot.py", line 43, in wrapper
2023-07-25 21:16:11     raise e
2023-07-25 21:16:11   File "/usr/local/lib/python3.9/site-packages/mastoBot/mastoBot.py", line 23, in wrapper
2023-07-25 21:16:11     result = func(self, *args, **kwargs)
2023-07-25 21:16:11   File "/usr/local/lib/python3.9/site-packages/mastoBot/mastoBot.py", line 129, in _process_notifications
2023-07-25 21:16:11     for notification in notifications:
2023-07-25 21:16:11 TypeError: 'NoneType' object is not iterable

Improved notification handling

Problem

The Mastodon API had a great feature, which implements a push notification service. This not only allows apps to easily implement push notifications but also for ways of tracking new notifications in extended APIs.

It is this core functionality that is used in MastoBot to allow for the detection of new mentions, favorites, follows, etc.

This, however, comes at a cost. If this feature is used to track notifications. These notifications should either be dismissed on the API level or they should be written to some local database to allow for the tracking and persistence of the bot's state.

In early versions of MastoBot, sqlite, mysql, firebase and redis was used to do this, but had since been removed in order to keep the overall design generic and Docker setup minimalistic.

This, however, leads to a problem with mentions of the bot. Mentions can be used to simulate a group, similar to MastoBot-3D. In doing so, notifications of mentions are dismissed in order to perform certain behaviors.

If users, however, mention the bot, the creators and owners of said bot will be unable to track notifications in most apps and in the Mastodon web UI.

A method needs to be implemented to either forward certain mentions which only mention the bot, thus it is directed at the bot and its team or other criteria, to the team or allow the teacking of such notifications.

As the usage of MastoBot-3D has evolved from a simple boosting bot to an actual community and posts are being made by the MastoBot-3D team, users are directing questions to the team and these posts are being missed. The only current solution is to implement this functionality in MastoBot-3D or have users mention the admins directly.

This is a core functionality and is generic enough that it should be part of MastoBot

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.