diff --git a/Dockerfile b/Dockerfile index 91aee39..f39a2b8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ WORKDIR /bot COPY Pipfile . RUN pip install pipenv && \ - pipenv install --pre && \ + pipenv install --pre && \ pipenv install --deploy --system && \ rm -r /root/.cache/* && \ rm -r /root/.local/* diff --git a/README.md b/README.md index 633ac07..270576f 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,9 @@ Bot management commands. * !bot reload - reload all bot modules (Must be done as bot owner) * !bot stats - show statistics on matrix users seen by bot * !bot leave - ask bot to leave this room (Must be done as admin in room) +* !bot modules - list all modules including enabled status +* !bot enable [module] - enable module (Must be done as admin in room) +* !bot disable [module] - disable module (Must be done as admin in room) ### Help diff --git a/bot.py b/bot.py index 099c228..418e08b 100755 --- a/bot.py +++ b/bot.py @@ -75,6 +75,12 @@ class Bot: } await self.client.room_send(room.room_id, 'm.room.message', msg) + def remove_callback(self, callback): + for cb_object in bot.client.event_callbacks: + if cb_object.func == callback: + self.logger.info("remove callback") + bot.client.event_callbacks.remove(cb_object) + def get_room_by_id(self, room_id): return self.client.rooms[room_id] @@ -148,7 +154,8 @@ class Bot: except CommandRequiresOwner: await self.send_text(room, f'Sorry, only bot owner can run that command.') except Exception: - await self.send_text(room, f'Module {command} experienced difficulty: {sys.exc_info()[0]} - see log for details') + await self.send_text(room, + f'Module {command} experienced difficulty: {sys.exc_info()[0]} - see log for details') traceback.print_exc(file=sys.stderr) else: self.logger.error(f"Unknown command: {command}") diff --git a/modules/common/pollingservice.py b/modules/common/pollingservice.py index 4d45406..c8da02f 100644 --- a/modules/common/pollingservice.py +++ b/modules/common/pollingservice.py @@ -105,9 +105,12 @@ class PollingService(BotModule): await bot.send_text(room, f'Removed {self.service_name} account from this room') def get_settings(self): - return {'account_rooms': self.account_rooms} + data = super().get_settings() + data['account_rooms'] = self.account_rooms + return data def set_settings(self, data): + super().set_settings(data) if data.get('account_rooms'): self.account_rooms = data['account_rooms'] diff --git a/modules/loc.py b/modules/loc.py index 56b5f7c..b1046b9 100644 --- a/modules/loc.py +++ b/modules/loc.py @@ -1,5 +1,6 @@ from geopy.geocoders import Nominatim -from nio import RoomMessageUnknown +from nio import RoomMessageUnknown, AsyncClient +from modules.common.module import BotModule from modules.common.module import BotModule @@ -12,6 +13,10 @@ class MatrixModule(BotModule): self.bot = bot bot.client.add_event_callback(self.unknown_cb, RoomMessageUnknown) + def matrix_stop(self, bot): + super().matrix_stop(bot) + bot.remove_callback(self.unknown_cb) + async def unknown_cb(self, room, event): if event.msgtype != 'm.location': return diff --git a/modules/off/googlecal.py b/modules/off/googlecal.py index 7d835a5..37b7817 100644 --- a/modules/off/googlecal.py +++ b/modules/off/googlecal.py @@ -20,14 +20,17 @@ from modules.common.module import BotModule class MatrixModule(BotModule): - def matrix_start(self, bot): - super().matrix_start(bot) - self.bot = bot - self.SCOPES = ['https://www.googleapis.com/auth/calendar.readonly'] + def __init__(self, name): + super().__init__(name) self.credentials_file = "credentials.json" + self.SCOPES = ['https://www.googleapis.com/auth/calendar.readonly'] + self.bot = None self.service = None self.calendar_rooms = dict() # Contains room_id -> [calid, calid] .. + def matrix_start(self, bot): + super().matrix_start(bot) + self.bot = bot creds = None if not os.path.exists(self.credentials_file) or os.path.getsize(self.credentials_file) == 0: @@ -43,8 +46,7 @@ class MatrixModule(BotModule): if creds and creds.expired and creds.refresh_token: creds.refresh(Request()) else: - flow = InstalledAppFlow.from_client_secrets_file( - self.credentials_file, self.SCOPES) + flow = InstalledAppFlow.from_client_secrets_file(self.credentials_file, self.SCOPES) # urn:ietf:wg:oauth:2.0:oob creds = flow.run_local_server(port=0) # Save the credentials for the next run diff --git a/modules/off/teamup.py b/modules/off/teamup.py index 4189cf9..6027307 100644 --- a/modules/off/teamup.py +++ b/modules/off/teamup.py @@ -3,6 +3,7 @@ from datetime import datetime from pyteamup import Calendar + # # TeamUp calendar notifications # diff --git a/modules/off/url.py b/modules/off/url.py index c12f223..24c5ec4 100644 --- a/modules/off/url.py +++ b/modules/off/url.py @@ -4,7 +4,7 @@ from functools import lru_cache import httpx from bs4 import BeautifulSoup -from nio import RoomMessageText +from nio import RoomMessageText, AsyncClient from modules.common.module import BotModule @@ -16,15 +16,18 @@ class MatrixModule(BotModule): Everytime a url is seen in a message we do http request to it and try to get a title tag contents to spit out to the room. """ - bot = None - status = dict() # room_id -> what to do with urls + def __init__(self, name): + super().__init__(name) - STATUSES = { - "OFF": "Not spamming this channel", - "TITLE": "Spamming this channel with titles", - "DESCRIPTION": "Spamming this channel with descriptions", - "BOTH": "Spamming this channel with both title and description", - } + self.bot = None + self.status = dict() # room_id -> what to do with urls + + self.STATUSES = { + "OFF": "Not spamming this channel", + "TITLE": "Spamming this channel with titles", + "DESCRIPTION": "Spamming this channel with descriptions", + "BOTH": "Spamming this channel with both title and description", + } def matrix_start(self, bot): """ @@ -34,6 +37,10 @@ class MatrixModule(BotModule): self.bot = bot bot.client.add_event_callback(self.text_cb, RoomMessageText) + def matrix_stop(self, bot): + super().matrix_stop(bot) + bot.remove_callback(self.text_cb) + async def text_cb(self, room, event): """ Handle client callbacks for all room text events @@ -103,7 +110,7 @@ class MatrixModule(BotModule): # try parse and get the title try: soup = BeautifulSoup(r.text, "html.parser") - title = soup.title.string + title = soup.title.string.strip() descr_tag = soup.find("meta", attrs={"name": "description"}) if descr_tag: description = descr_tag.get("content", None)