From 9e139a0f3ee67fd363c7ffc6e62c7a6aa47c2204 Mon Sep 17 00:00:00 2001 From: Dennis Date: Sun, 20 Jan 2019 00:41:02 +0100 Subject: [PATCH] Add custom Matrix Python SDK. Solves #5 and #2 This commit releases a custom Matrix Python SDK. This means that the most important classes---MatrixClient, Room, and MatrixHttpApi---are inherited by Custom classes. This enables us to change behaviour or add new features in a well structured and predictable way. The first issue that is solved with this new custom SDK is the problem that was introduced in #2. --- matrix_bot_api/custom_matrix_client/api.py | 10 ++ matrix_bot_api/custom_matrix_client/client.py | 76 +++++++++++++ .../messages/messages.dutch.hjson | 0 matrix_bot_api/custom_matrix_client/room.py | 6 + matrix_bot_api/matrix_bot_api.py | 106 ++++++------------ plugins/admidio_events/admidio_events.py | 2 +- 6 files changed, 126 insertions(+), 74 deletions(-) create mode 100644 matrix_bot_api/custom_matrix_client/api.py create mode 100644 matrix_bot_api/custom_matrix_client/client.py rename matrix_bot_api/{ => custom_matrix_client}/messages/messages.dutch.hjson (100%) create mode 100644 matrix_bot_api/custom_matrix_client/room.py diff --git a/matrix_bot_api/custom_matrix_client/api.py b/matrix_bot_api/custom_matrix_client/api.py new file mode 100644 index 0000000..16caec0 --- /dev/null +++ b/matrix_bot_api/custom_matrix_client/api.py @@ -0,0 +1,10 @@ +from matrix_client.api import MatrixHttpApi + +class CustomMatrixHttpApi(MatrixHttpApi): + def __init__( + self, base_url, token=None, identity=None, + default_429_wait_ms=5000, + use_authorization_header=True + ): + + super().__init__(base_url, token, identity, default_429_wait_ms) diff --git a/matrix_bot_api/custom_matrix_client/client.py b/matrix_bot_api/custom_matrix_client/client.py new file mode 100644 index 0000000..542f1fa --- /dev/null +++ b/matrix_bot_api/custom_matrix_client/client.py @@ -0,0 +1,76 @@ +import os +import hjson + +from matrix_client.client import MatrixClient + +from .api import CustomMatrixHttpApi +from .room import CustomRoom + +MESSAGES_DIR = os.path.join(os.path.dirname(__file__), 'messages') +MESSAGES_LOCATION = MESSAGES_DIR + '/messages.dutch.hjson' + +class CustomMatrixClient(MatrixClient): + def __init__(self, base_url, token=None): + super().__init__(base_url, token) + + self.api = CustomMatrixHttpApi(base_url, token) + + # Load messages + with open(MESSAGES_LOCATION) as hjson_data: + self.messages = hjson.load(hjson_data) + + + def _mkroom(self, room_id): + room = CustomRoom(self, room_id) + + self.rooms[room_id] = room + return self.rooms[room_id] + + def create_direct_room(self, invitees=None): + content = { + "visibility": "private", + "is_direct": True, + "invite": [invitees] + } + + room_dict = self.api._send("POST", "/createRoom", content) + + return self._mkroom(room_dict["room_id"]) + + + def send_message_private_public(self, room, event, message): + """This method takes a room, event, and message and makes sure + that the message is sent in a private room and not in a public + room. If no private room exists, it will create a private room + with the sender of the event. + """ + + orig_room = room + found_room = False + + for room_id, room in self.rooms.items(): + joined_members = room.get_joined_members() + + # Check for rooms with only two members + if len(joined_members) == 2: + # Check if sender is in that room + for member in joined_members: + if event['sender'] == member.user_id: + found_room = True + + if found_room: + # If the flag is set, we do not need to check further rooms + break + + # Send help message to an existing room or to a new room + if found_room: + room.send_html(message) + else: + room = self.create_direct_room(event['sender']); + room.send_html(message) + + if room != orig_room: + display_name_sender = self.api.get_display_name(event['sender']) + orig_room.send_text(self.messages['private_message'].format( + display_name_sender)) + diff --git a/matrix_bot_api/messages/messages.dutch.hjson b/matrix_bot_api/custom_matrix_client/messages/messages.dutch.hjson similarity index 100% rename from matrix_bot_api/messages/messages.dutch.hjson rename to matrix_bot_api/custom_matrix_client/messages/messages.dutch.hjson diff --git a/matrix_bot_api/custom_matrix_client/room.py b/matrix_bot_api/custom_matrix_client/room.py new file mode 100644 index 0000000..5244752 --- /dev/null +++ b/matrix_bot_api/custom_matrix_client/room.py @@ -0,0 +1,6 @@ +from matrix_client.room import Room + + +class CustomRoom(Room): + def __init__(self, client, room_id): + super().__init__(client, room_id) diff --git a/matrix_bot_api/matrix_bot_api.py b/matrix_bot_api/matrix_bot_api.py index c3fa0c7..66af0fc 100644 --- a/matrix_bot_api/matrix_bot_api.py +++ b/matrix_bot_api/matrix_bot_api.py @@ -9,17 +9,16 @@ import datetime as dt from pip._internal import main as pipmain -from matrix_client.client import MatrixClient -from matrix_client.api import MatrixRequestError, MatrixHttpApi -from matrix_client.user import User - +from matrix_client.api import MatrixRequestError from matrix_bot_api.mregex_handler import MRegexHandler +from .custom_matrix_client.api import CustomMatrixHttpApi +from .custom_matrix_client.client import CustomMatrixClient + MESSAGES_DIR = os.path.join(os.path.dirname(__file__), 'messages') DATA_LOCATION = os.path.join(os.path.dirname(__file__), '../data/bot.db') HELP_LOCATION = MESSAGES_DIR + '/help' -MESSAGES_LOCATION = MESSAGES_DIR + '/messages.dutch.hjson' def eprint(*args, **kwargs): """Print error messages to stderr""" @@ -32,7 +31,8 @@ class MatrixBotAPI: self.username = self.config['bot_credentials']['username'] # Authenticate with given credentials - self.client = MatrixClient(self.config['bot_credentials']['server']) + self.client = CustomMatrixClient( + self.config['bot_credentials']['server']) try: self.token = self.client.login_with_password( self.config['bot_credentials']['username'], @@ -41,13 +41,11 @@ class MatrixBotAPI: print(e) if e.code == 403: print("Bad username/password") + sys.exit() except Exception as e: print("Invalid server URL") traceback.print_exc() - - self.api = MatrixHttpApi( - self.config['bot_credentials']['server'], - token=self.token) + sys.exit() # Store allowed rooms self.rooms = rooms @@ -63,7 +61,7 @@ class MatrixBotAPI: # Add all rooms we're currently in to self.rooms and add their # callbacks - for room_id, room in self.client.get_rooms().items(): + for room_id, room in self.client.rooms.items(): room.add_listener(self.handle_message) self.rooms.append(room_id) else: @@ -86,10 +84,6 @@ class MatrixBotAPI: self.config['triggers']['help'], self.help) self.add_handler(self.help_handler) - # Load messages - with open(MESSAGES_LOCATION) as hjson_data: - self.messages = hjson.load(hjson_data) - def add_plugins(self): """Acquire list of plugins from configuration, load them, @@ -103,30 +97,31 @@ class MatrixBotAPI: # ./plugins directory modules = [] - #try: + try: # Loop through the available plugins, install their requirements, # load them as module, run their setup, append them to a list, # and add their handler variables - for i, plugin in enumerate(self.config['plugins']): - # Install requirements - self.install_requirements(plugin) + for i, plugin in enumerate(self.config['plugins']): + # Install requirements + self.install_requirements(plugin) - # Dynamically load the module - modules.append( - importlib.import_module( - "plugins.{0}.{0}".format(plugin), package = None)) + # Dynamically load the module + modules.append( + importlib.import_module( + "plugins.{0}.{0}".format(plugin), package = None)) - # Run the module's setup function and save that it got installed - self.setup_plugin(modules[i]) + # Run the module's setup function and save that it got installed + self.setup_plugin(modules[i]) - # Create new instance of plugin and append to plugin_objects array - self.plugin_objects.append(modules[i].Plugin(self)) + # Create new instance of plugin and append to plugin_objects array + self.plugin_objects.append(modules[i].Plugin(self)) - # Add handler of newly created instance to bot - self.add_handler(self.plugin_objects[i].handler) - #except: - # eprint("Importing one or more of the plugins did not go well!") - # sys.exit() + # Add handler of newly created instance to bot + self.add_handler(self.plugin_objects[i].handler) + except: + eprint("Importing one or more of the plugins did not go well!") + traceback.print_exc() + sys.exit() def check_installation_plugin(self, module_name): """Function returns 0 if a plugin with that name is not @@ -175,10 +170,10 @@ class MatrixBotAPI: print(f"Running installation of {module_name}.") # Run module's install method - #try: - module.setup() - #except: - # print(f"{module_name} did not specify setup(). Skipping...") + try: + module.setup() + except: + print(f"{module_name} did not specify setup(). Skipping...") # Save in database that we installed this plugin datetime_added = dt.datetime.now().strftime("%Y-%m-%d %H:%M:%S") @@ -238,42 +233,6 @@ class MatrixBotAPI: # Do nothing, data directory already exists pass - def send_message_private_public(self, room, event, message): - """This method takes a room, event, and message and makes sure - that the message is sent in a private room and not in a public - room. If no private room exists, it will create a private room - with the sender of the event. - """ - - orig_room = room - found_room = False - - for room_id, room in self.client.get_rooms().items(): - joined_members = room.get_joined_members() - - # Check for rooms with only two members - if len(joined_members) == 2: - # Check if sender is in that room - for member in joined_members: - if event['sender'] == member.user_id: - found_room = True - - if found_room: - # If the flag is set, we do not need to check further rooms - break - - # Send help message to an existing room or to a new room - if found_room: - room.send_html(message) - else: - room = self.client.create_room(invitees=[event['sender']]); - room.send_html(message) - - if room != orig_room: - display_name_sender = self.api.get_display_name(event['sender']) - orig_room.send_text(self.messages['private_message'].format( - display_name_sender)) - def help(self, room, event): """Prints a general help message and then grabs all help messages from the different plugins @@ -288,7 +247,7 @@ class MatrixBotAPI: # The plugin probably returned 0, ignore it pass - self.send_message_private_public(room, event, help_text) + self.client.send_message_private_public(room, event, help_text) def add_handler(self, handler): try: @@ -327,4 +286,5 @@ class MatrixBotAPI: def start_polling(self): # Starts polling for messages self.client.start_listener_thread() + return self.client.sync_thread diff --git a/plugins/admidio_events/admidio_events.py b/plugins/admidio_events/admidio_events.py index 79af503..ed45185 100644 --- a/plugins/admidio_events/admidio_events.py +++ b/plugins/admidio_events/admidio_events.py @@ -146,7 +146,7 @@ class Plugin: if row[1] in mapping[0]: # We got a winner! - for i, room in self.bot.client.get_rooms().items(): + for i, room in self.bot.client.rooms.items(): if room.display_name in mapping[1]: room.send_html( self.messages["new_event"].format(