From 4c5b6ff3654cfe25e18e3a20f76395b61cbc4d27 Mon Sep 17 00:00:00 2001 From: Richard von Seck Date: Sat, 5 May 2018 13:22:35 +0200 Subject: [PATCH 1/3] matrix_bot_api: Added dynamic handler adding/removing + arguments Dynamic handler management: The bot API is now able to dynamically remove registered handlers _without_ restarting. This allows e.g. the registration/deletion of new bot features/handlers through a matrix bot command. Additional Handler Argument: All handlers now have an extra (3rd) argument besides (room, event). This allows to pass information to the handler while registering. A possible use case is a handler, which reacts in the same way on different keywords, but needs to know about the context it was registered in. --- matrix_bot_api/matrix_bot_api.py | 32 ++++++++++++++++++++++++++++-- matrix_bot_api/mcommand_handler.py | 6 ++++++ matrix_bot_api/mregex_handler.py | 6 ++++++ 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/matrix_bot_api/matrix_bot_api.py b/matrix_bot_api/matrix_bot_api.py index f4f89d2..3c4805b 100644 --- a/matrix_bot_api/matrix_bot_api.py +++ b/matrix_bot_api/matrix_bot_api.py @@ -31,6 +31,11 @@ def __init__(self, username, password, server, rooms=None): # Store empty list of handlers self.handlers = [] + # Store dict with additional arguments to handlers + # This allows to provide additional arguments to a specific handler + # callback on registration. + self.additional_arguments = {} + # If rooms is None, we should listen for invites and automatically accept them if rooms is None: self.client.add_invite_listener(self.handle_invite) @@ -45,8 +50,30 @@ def __init__(self, username, password, server, rooms=None): for room in self.rooms: room.add_listener(self.handle_message) - def add_handler(self, handler): + def add_handler(self, handler, arg=''): self.handlers.append(handler) + self.additional_arguments[handler] = arg + + def remove_handler(self, handler): + try: + self.handlers.remove(handler) + except ValueError as e: + return + + try: + self.additional_arguments.pop(handler) + except KeyError as e: + return + + def get_handler(self, trigger): + res = [] + + for h in self.handlers: + if h.triggers_on(trigger): + res.append(h) + + return res + def handle_message(self, room, event): # Make sure we didn't send this message @@ -57,7 +84,8 @@ def handle_message(self, room, event): for handler in self.handlers: if handler.test_callback(room, event): # This handler needs to be called - handler.handle_callback(room, event) + arg = self.additional_arguments[handler] + handler.handle_callback(room, event, arg) def handle_invite(self, room_id, state): print("Got invite to room: " + str(room_id)) diff --git a/matrix_bot_api/mcommand_handler.py b/matrix_bot_api/mcommand_handler.py index bc56789..062069b 100644 --- a/matrix_bot_api/mcommand_handler.py +++ b/matrix_bot_api/mcommand_handler.py @@ -23,3 +23,9 @@ def test_command(self, room, event): if re.match(self.cmd_char + self.command, event['content']['body']): return True return False + + def triggers_on(self, trigger): + if trigger == self.command: + return True + else: + return False diff --git a/matrix_bot_api/mregex_handler.py b/matrix_bot_api/mregex_handler.py index 32aa684..bee4eba 100644 --- a/matrix_bot_api/mregex_handler.py +++ b/matrix_bot_api/mregex_handler.py @@ -23,3 +23,9 @@ def test_regex(self, room, event): return True return False + + def triggers_on(self, trigger): + if trigger == self.regex_str: + return True + else: + return False From ea038360371141dfd806da0d1427c8b246c75ac9 Mon Sep 17 00:00:00 2001 From: Richard von Seck Date: Sat, 5 May 2018 13:41:07 +0200 Subject: [PATCH 2/3] example_bot: Added example handlers using the new data argument --- example_bot.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/example_bot.py b/example_bot.py index 02ec264..efb4c00 100644 --- a/example_bot.py +++ b/example_bot.py @@ -19,12 +19,12 @@ SERVER = "" # Matrix server URL -def hi_callback(room, event): +def hi_callback(room, event, data): # Somebody said hi, let's say Hi back room.send_text("Hi, " + event['sender']) -def echo_callback(room, event): +def echo_callback(room, event, data): args = event['content']['body'].split() args.pop(0) @@ -32,7 +32,7 @@ def echo_callback(room, event): room.send_text(' '.join(args)) -def dieroll_callback(room, event): +def dieroll_callback(room, event, data): # someone wants a random number args = event['content']['body'].split() @@ -55,6 +55,11 @@ def dieroll_callback(room, event): result = random.randrange(1,die_max+1) room.send_text(str(result)) +def generic_callback(room, event, data): + # Somebody wanted to be greeted, let's answer him with the registered + # greeting, passed in the data argument + room.send_text(data + event['sender']) + def main(): # Create an instance of the MatrixBotAPI @@ -72,6 +77,13 @@ def main(): dieroll_handler = MCommandHandler("d", dieroll_callback) bot.add_handler(dieroll_handler) + # Add two handlers for the greet/cheers command, using the same generic + # greeting callback and the data argument + cheers_handler = MCommandHandler("cheers", generic_callback) + greet_handler = MCommandHandler("greet", generic_callback) + bot.add_handler(cheers_handler, "Cheers, ") + bot.add_handler(greet_handler, "Greetings, ") + # Start polling bot.start_polling() From e7578b93b6c7838e770ab376e60819027f7838ff Mon Sep 17 00:00:00 2001 From: Richard von Seck Date: Fri, 29 Jun 2018 12:20:49 +0200 Subject: [PATCH 3/3] matrix_bot_api: Backward compatible handler callbacks --- example_bot.py | 12 ++++++------ matrix_bot_api/matrix_bot_api.py | 21 ++++++++++++++++----- matrix_bot_api/mcommand_handler.py | 1 + matrix_bot_api/mregex_handler.py | 1 + 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/example_bot.py b/example_bot.py index efb4c00..9c0f237 100644 --- a/example_bot.py +++ b/example_bot.py @@ -19,12 +19,12 @@ SERVER = "" # Matrix server URL -def hi_callback(room, event, data): +def hi_callback(room, event): # Somebody said hi, let's say Hi back room.send_text("Hi, " + event['sender']) -def echo_callback(room, event, data): +def echo_callback(room, event): args = event['content']['body'].split() args.pop(0) @@ -32,7 +32,7 @@ def echo_callback(room, event, data): room.send_text(' '.join(args)) -def dieroll_callback(room, event, data): +def dieroll_callback(room, event): # someone wants a random number args = event['content']['body'].split() @@ -55,7 +55,7 @@ def dieroll_callback(room, event, data): result = random.randrange(1,die_max+1) room.send_text(str(result)) -def generic_callback(room, event, data): +def generic_greeting_callback(room, event, data): # Somebody wanted to be greeted, let's answer him with the registered # greeting, passed in the data argument room.send_text(data + event['sender']) @@ -79,8 +79,8 @@ def main(): # Add two handlers for the greet/cheers command, using the same generic # greeting callback and the data argument - cheers_handler = MCommandHandler("cheers", generic_callback) - greet_handler = MCommandHandler("greet", generic_callback) + cheers_handler = MCommandHandler("cheers", generic_greeting_callback) + greet_handler = MCommandHandler("greet", generic_greeting_callback) bot.add_handler(cheers_handler, "Cheers, ") bot.add_handler(greet_handler, "Greetings, ") diff --git a/matrix_bot_api/matrix_bot_api.py b/matrix_bot_api/matrix_bot_api.py index 3c4805b..c5c0e3d 100644 --- a/matrix_bot_api/matrix_bot_api.py +++ b/matrix_bot_api/matrix_bot_api.py @@ -50,9 +50,13 @@ def __init__(self, username, password, server, rooms=None): for room in self.rooms: room.add_listener(self.handle_message) - def add_handler(self, handler, arg=''): + # Add a new handler to the bot. If arg is given, it is provided as a third + # argument on every invocation of handler. + def add_handler(self, handler, arg=None): self.handlers.append(handler) - self.additional_arguments[handler] = arg + + if arg: + self.additional_arguments[handler] = arg def remove_handler(self, handler): try: @@ -62,7 +66,7 @@ def remove_handler(self, handler): try: self.additional_arguments.pop(handler) - except KeyError as e: + except KeyError: return def get_handler(self, trigger): @@ -84,8 +88,15 @@ def handle_message(self, room, event): for handler in self.handlers: if handler.test_callback(room, event): # This handler needs to be called - arg = self.additional_arguments[handler] - handler.handle_callback(room, event, arg) + try: + # If an additional argument is registered for the handler, + # call it with this argument + arg = self.additional_arguments[handler] + handler.handle_callback(room, event, arg) + except KeyError: + # Otherwise leave it out + handler.handle_callback(room, event) + def handle_invite(self, room_id, state): print("Got invite to room: " + str(room_id)) diff --git a/matrix_bot_api/mcommand_handler.py b/matrix_bot_api/mcommand_handler.py index 062069b..c0ec667 100644 --- a/matrix_bot_api/mcommand_handler.py +++ b/matrix_bot_api/mcommand_handler.py @@ -24,6 +24,7 @@ def test_command(self, room, event): return True return False + # Generic command testing function for all MHandler objects def triggers_on(self, trigger): if trigger == self.command: return True diff --git a/matrix_bot_api/mregex_handler.py b/matrix_bot_api/mregex_handler.py index bee4eba..e628a2c 100644 --- a/matrix_bot_api/mregex_handler.py +++ b/matrix_bot_api/mregex_handler.py @@ -24,6 +24,7 @@ def test_regex(self, room, event): return False + # Generic command testing function for all MHandler objects def triggers_on(self, trigger): if trigger == self.regex_str: return True