From a1b7d0b1ab2ad63a26616b54a7fc64b035f4334b Mon Sep 17 00:00:00 2001 From: Ville Ranki Date: Mon, 9 Dec 2019 20:24:51 +0200 Subject: [PATCH] Added echo module --- README.md | 9 ++++++ bot.py | 77 +++++++++++++++++++++++++++++++++++++++++++++---- modules/echo.py | 16 ++++++++++ 3 files changed, 96 insertions(+), 6 deletions(-) create mode 100644 modules/echo.py diff --git a/README.md b/README.md index 0de54d1..c52632e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,14 @@ # Hemppa - generic Matrix bot +## Module list + +Write !modulename in Matrix room where the bot is to use modules. + +### Echo + +Simple example module that just echoes what user said. Can be used as an +example for writing own modules. + ## First * Create a Matrix user diff --git a/bot.py b/bot.py index 210e5d6..4037f56 100755 --- a/bot.py +++ b/bot.py @@ -1,14 +1,20 @@ import asyncio import os import json +import glob +import traceback +import importlib +import sys +import re from nio import (AsyncClient, RoomMessageText, RoomMessageUnknown, JoinError, InviteEvent) class Bot: client = None join_on_invite = False + modules = dict() - async def send_html(self, body, client, room): + async def send_text(self, room, body): msg = { "body": body, "msgtype": "m.text" @@ -16,14 +22,29 @@ class Bot: await self.client.room_send(self.get_room_id(room), 'm.room.message', msg) def get_room_id(self, room): - for roomid in client.rooms: + for roomid in self.client.rooms: if self.client.rooms[roomid].named_room_name() == room.named_room_name(): return roomid print('Cannot find id for room', room.named_room_name(), ' - is the bot on it?') return None async def message_cb(self, room, event): - pass + # Figure out the command + body = event.body + if len(body) == 0: + return + command = body.split().pop(0) + + # Strip away non-alphanumeric characters, including leading ! for security + command = re.sub(r'\W+', '', command) + + moduleobject = self.modules.get(command) + + if "matrix_message" in dir(moduleobject): + try: + await moduleobject.matrix_message(bot, room, event) + except: + traceback.print_exc(file=sys.stderr) async def unknown_cb(self, room, event): if event.msgtype != 'm.location': @@ -40,15 +61,56 @@ class Bot: else: break + def load_module(self, modulename): + try: + module = importlib.import_module('modules.' + modulename) + cls = getattr(module, 'MatrixModule') + return cls() + except ModuleNotFoundError: + print('Module ', modulename, ' failed to load!') + traceback.print_exc(file=sys.stderr) + return None + + def get_modules(self): + modulefiles = glob.glob('./modules/*.py') + + for modulefile in modulefiles: + modulename = os.path.splitext(os.path.basename(modulefile))[0] + moduleobject = self.load_module(modulename) + if moduleobject: + self.modules[modulename] = moduleobject + def init(self): self.client = AsyncClient(os.environ['MATRIX_SERVER'], os.environ['MATRIX_USER']) self.client.access_token = os.getenv('MATRIX_ACCESS_TOKEN') self.join_on_invite = os.getenv('JOIN_ON_INVITE') + self.get_modules() + + print(f'Starting {len(self.modules)} modules..') + + for modulename, moduleobject in self.modules.items(): + print(modulename + '..') + if "matrix_start" in dir(moduleobject): + try: + moduleobject.matrix_start(bot) + except: + traceback.print_exc(file=sys.stderr) + + def stop(self): + print(f'Stopping {len(self.modules)} modules..') + for modulename, moduleobject in self.modules.items(): + print(modulename + '..') + if "matrix_stop" in dir(moduleobject): + try: + moduleobject.matrix_stop(bot) + except: + traceback.print_exc(file=sys.stderr) + async def run(self): if not self.client.access_token: await self.client.login(os.environ['MATRIX_PASSWORD']) - print("Logged in with password, access token:", client.access_token) + print("Logged in with password, access token:", self.client.access_token) await self.client.sync() @@ -66,5 +128,8 @@ class Bot: bot = Bot() bot.init() - -asyncio.get_event_loop().run_until_complete(bot.run()) +try: + asyncio.get_event_loop().run_until_complete(bot.run()) +except KeyboardInterrupt: + pass +bot.stop() diff --git a/modules/echo.py b/modules/echo.py new file mode 100644 index 0000000..a697b3e --- /dev/null +++ b/modules/echo.py @@ -0,0 +1,16 @@ +class MatrixModule: + def matrix_start(self, bot): + print("Echo start!") + + def matrix_stop(self, bot): + print("Echo stop!") + + async def matrix_message(self, bot, room, event): + args = event.body.split() + args.pop(0) + + # Echo what they said back + await bot.send_text(room, ' '.join(args)) + + def help(self): + return('Echoes back what user has said')