diff --git a/.gitignore b/.gitignore index d9fcaee..c0b7f35 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ config.ini __pycache__/ dist/ pantalaimon.conf -build/ \ No newline at end of file +build/ +venv/ \ No newline at end of file diff --git a/README.md b/README.md index ac62b82..57b35a0 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,17 @@ -# Matrix-RSSBot +# Matrix-ReportBot [![Support Private.coffee!](https://shields.private.coffee/badge/private.coffee-support%20us!-pink?logo=coffeescript)](https://private.coffee) -[![PyPI](https://shields.private.coffee/pypi/v/matrix-rssbot)](https://pypi.org/project/matrix-rssbot/) -[![PyPI - Python Version](https://shields.private.coffee/pypi/pyversions/matrix-rssbot)](https://pypi.org/project/matrix-rssbot/) -[![PyPI - License](https://shields.private.coffee/pypi/l/matrix-rssbot)](https://pypi.org/project/matrix-rssbot/) -[![Latest Git Commit](https://shields.private.coffee/gitea/last-commit/privatecoffee/matrix-rssbot?gitea_url=https://git.private.coffee)](https://git.private.coffee/privatecoffee/matrix-rssbot) +[![PyPI](https://shields.private.coffee/pypi/v/matrix-reportbot)](https://pypi.org/project/matrix-reportbot/) +[![PyPI - Python Version](https://shields.private.coffee/pypi/pyversions/matrix-reportbot)](https://pypi.org/project/matrix-reportbot/) +[![PyPI - License](https://shields.private.coffee/pypi/l/matrix-reportbot)](https://pypi.org/project/matrix-reportbot/) +[![Latest Git Commit](https://shields.private.coffee/gitea/last-commit/privatecoffee/matrix-reportbot?gitea_url=https://git.private.coffee)](https://git.private.coffee/privatecoffee/matrix-reportbot) -This is a simple, no-database RSS/Atom feed bot for Matrix. It is designed to be easy to use and lightweight. +This is a simple bot that can be used to display incoming moderation reports in a Matrix room. ## Installation ```bash -pip install matrix-rssbot +pip install matrix-reportbot ``` Create a configuration file in `config.ini` based on the [config.dist.ini](config.dist.ini) provided in the repository. @@ -22,42 +22,19 @@ At the very least, you need to provide the following configuration: [Matrix] Homeserver = http://your-homeserver.example.com AccessToken = syt_YourAccessTokenHere +RoomId = !yourRoomId:your-homeserver.example.com ``` +Note: The AccessToken must be for a admin user, because the bot needs to be able to read the moderation events. + We recommend using pantalaimon as a proxy, because the bot itself does not support end-to-end encryption. You can start the bot by running: ```bash -rssbot +reportbot ``` -## Usage - -The bot will automatically join all rooms it is invited to. - -You have to ensure that the bot has the necessary permissions to send state events and messages in the room. Regular users cannot send state events, so you have to either raise the bot user's power level (`Moderator` level should do) or lower the power level required to send state events. - -You can now add a feed to the bot by sending a message to the bot in the room you want the feed to be posted in. The message should be in the following format: - -``` -!rssbot addfeed https://example.com/feed.xml -``` - -To list all feeds in a room, you can use the following command: - -``` -!rssbot listfeeds -``` - -Finally, to remove a feed, you can use the following command: - -``` -!rssbot removefeed https://example.com/feed.xml -``` - -Alternatively, you can use the number of the feed in the list, which you can get by using the `listfeeds` command instead of the URL. - ## License This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. diff --git a/config.dist.ini b/config.dist.ini index 6617fa5..b4e75ce 100644 --- a/config.dist.ini +++ b/config.dist.ini @@ -5,7 +5,7 @@ ############################################################################### -[RSSBot] +[ReportBot] # Some way for the user to contact you. # Ideally, either your personal user ID or a support room @@ -24,20 +24,9 @@ Operator = Contact details not set # # Debug = 1 -# The default room name used by the !newroom command -# Defaults to RSSBot if not set -# -# DefaultRoomName = RSSBot - # Display name for the bot # -# DisplayName = RSSBot - -# A list of allowed users -# If not defined, everyone is allowed to use the bot -# Use the "*:homeserver.matrix" syntax to allow everyone on a given homeserver -# -# AllowedUsers = ["*:matrix.local"] +# DisplayName = ReportBot # Minimum level of log messages that should be printed # Available log levels in ascending order: trace, debug, info, warning, error, critical @@ -64,6 +53,10 @@ Homeserver = https://matrix.local # AccessToken = syt_yoursynapsetoken +# The room ID in which reports should be posted +# +RoomID = !yourroomid:matrix.local + # The Matrix user ID of the bot (@local:domain.tld) # Only specify this if the bot fails to figure it out by itself # diff --git a/pyproject.toml b/pyproject.toml index 4201adf..f4f0639 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,20 +6,20 @@ build-backend = "hatchling.build" allow-direct-references = true [project] -name = "matrix-rssbot" -version = "0.1.1" +name = "matrix-reportbot" +version = "0.0.1" authors = [ { name="Private.coffee Team", email="support@private.coffee" }, ] -description = "Simple RSS feed bridge for Matrix" +description = "Simple content report subscription bot for Matrix." readme = "README.md" license = { file="LICENSE" } requires-python = ">=3.10" packages = [ - "src/matrix_rssbot" + "src/matrix_reportbot" ] classifiers = [ @@ -33,15 +33,14 @@ dependencies = [ "pantalaimon", "setuptools", "markdown2", - "feedparser", ] [project.urls] -"Homepage" = "https://git.private.coffee/PrivateCoffee/matrix-rssbot" -"Bug Tracker" = "https://git.private.coffee/PrivateCoffee/matrix-rssbot/issues" +"Homepage" = "https://git.private.coffee/PrivateCoffee/matrix-reportbot" +"Bug Tracker" = "https://git.private.coffee/PrivateCoffee/matrix-reportbot/issues" [project.scripts] -rssbot = "matrix_rssbot.__main__:main" +reportbot = "matrix_reportbot.__main__:main" [tool.hatch.build.targets.wheel] -packages = ["src/matrix_rssbot"] \ No newline at end of file +packages = ["src/matrix_reportbot"] \ No newline at end of file diff --git a/reportbot-pantalaimon.service b/reportbot-pantalaimon.service new file mode 100644 index 0000000..9848d78 --- /dev/null +++ b/reportbot-pantalaimon.service @@ -0,0 +1,15 @@ +[Unit] +Description=Pantalaimon for ReportBot +Requires=network.target + +[Service] +Type=simple +User=reportbot +Group=reportbot +WorkingDirectory=/opt/reportbot +ExecStart=/opt/reportbot/venv/bin/python3 -um pantalaimon.main -c pantalaimon.conf +Restart=always +RestartSec=10 + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/reportbot.service b/reportbot.service new file mode 100644 index 0000000..656b196 --- /dev/null +++ b/reportbot.service @@ -0,0 +1,15 @@ +[Unit] +Description=ReportBot +Requires=network.target + +[Service] +Type=simple +User=reportbot +Group=reportbot +WorkingDirectory=/opt/reportbot +ExecStart=/opt/reportbot/.venv/bin/python3 -um reportbot +Restart=always +RestartSec=10 + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/rssbot-pantalaimon.service b/rssbot-pantalaimon.service deleted file mode 100644 index 1110216..0000000 --- a/rssbot-pantalaimon.service +++ /dev/null @@ -1,15 +0,0 @@ -[Unit] -Description=Pantalaimon for RSSbot -Requires=network.target - -[Service] -Type=simple -User=rssbot -Group=rssbot -WorkingDirectory=/opt/rssbot -ExecStart=/opt/rssbot/venv/bin/python3 -um pantalaimon.main -c pantalaimon.conf -Restart=always -RestartSec=10 - -[Install] -WantedBy=multi-user.target \ No newline at end of file diff --git a/rssbot.service b/rssbot.service deleted file mode 100644 index 2394450..0000000 --- a/rssbot.service +++ /dev/null @@ -1,15 +0,0 @@ -[Unit] -Description=RSSbot -Requires=network.target - -[Service] -Type=simple -User=rssbot -Group=rssbot -WorkingDirectory=/opt/rssbot -ExecStart=/opt/rssbot/.venv/bin/python3 -um rssbot -Restart=always -RestartSec=10 - -[Install] -WantedBy=multi-user.target \ No newline at end of file diff --git a/src/matrix_rssbot/__init__.py b/src/matrix_reportbot/__init__.py similarity index 100% rename from src/matrix_rssbot/__init__.py rename to src/matrix_reportbot/__init__.py diff --git a/src/matrix_rssbot/__main__.py b/src/matrix_reportbot/__main__.py similarity index 92% rename from src/matrix_rssbot/__main__.py rename to src/matrix_reportbot/__main__.py index deb3995..ed52928 100644 --- a/src/matrix_rssbot/__main__.py +++ b/src/matrix_reportbot/__main__.py @@ -1,4 +1,4 @@ -from .classes.bot import RSSBot +from .classes.bot import ReportBot from argparse import ArgumentParser from configparser import ConfigParser @@ -12,7 +12,7 @@ def sigterm_handler(_signo, _stack_frame): def get_version(): try: - package_version = pkg_resources.get_distribution("matrix_rssbot").version + package_version = pkg_resources.get_distribution("matrix_reportbot").version except pkg_resources.DistributionNotFound: return None return package_version @@ -40,7 +40,7 @@ def main(): config.read(args.config) # Create bot - bot = RSSBot.from_config(config) + bot = ReportBot.from_config(config) # Listen for SIGTERM signal.signal(signal.SIGTERM, sigterm_handler) diff --git a/src/matrix_rssbot/classes/__init__.py b/src/matrix_reportbot/classes/__init__.py similarity index 100% rename from src/matrix_rssbot/classes/__init__.py rename to src/matrix_reportbot/classes/__init__.py diff --git a/src/matrix_rssbot/classes/bot.py b/src/matrix_reportbot/classes/bot.py similarity index 58% rename from src/matrix_rssbot/classes/bot.py rename to src/matrix_reportbot/classes/bot.py index 6a7c962..8e80d06 100644 --- a/src/matrix_rssbot/classes/bot.py +++ b/src/matrix_reportbot/classes/bot.py @@ -1,5 +1,6 @@ import asyncio import functools +import aiohttp from nio import ( AsyncClient, @@ -50,14 +51,11 @@ import sys import traceback import markdown2 -import feedparser from .logging import Logger -from .callbacks import RESPONSE_CALLBACKS, EVENT_CALLBACKS -from .commands import COMMANDS -class RSSBot: +class ReportBot: # Default values matrix_client: Optional[AsyncClient] = None sync_token: Optional[str] = None @@ -75,7 +73,7 @@ class RSSBot: @property def loop_duration(self) -> int: - return self.config["RSSBot"].getint("LoopDuration", 300) + return self.config["ReportBot"].getint("LoopDuration", 300) @property def allowed_users(self) -> List[str]: @@ -85,18 +83,27 @@ class RSSBot: List[str]: List of user IDs. Defaults to [], which means all users are allowed. """ try: - return json.loads(self.config["RSSBot"]["AllowedUsers"]) + return json.loads(self.config["ReportBot"]["AllowedUsers"]) except: return [] + @property + def room_id(self) -> str: + """Room ID to send reports to. + + Returns: + str: The room ID to send reports to. + """ + return self.config["Matrix"]["RoomID"] + @property def display_name(self) -> str: """Display name of the bot user. Returns: - str: The display name of the bot user. Defaults to "RSSBot". + str: The display name of the bot user. Defaults to "ReportBot". """ - return self.config["RSSBot"].get("DisplayName", "RSSBot") + return self.config["ReportBot"].get("DisplayName", "ReportBot") @property def default_room_name(self) -> str: @@ -105,7 +112,7 @@ class RSSBot: Returns: str: The default name of rooms created by the bot. Defaults to the display name of the bot. """ - return self.config["RSSBot"].get("DefaultRoomName", self.display_name) + return self.config["ReportBot"].get("DefaultRoomName", self.display_name) @property def debug(self) -> bool: @@ -114,32 +121,30 @@ class RSSBot: Returns: bool: Whether to enable debug logging. Defaults to False. """ - return self.config["RSSBot"].getboolean("Debug", False) + return self.config["ReportBot"].getboolean("Debug", False) # User agent to use for HTTP requests - USER_AGENT = ( - "matrix-rssbot/dev (+https://git.private.coffee/PrivateCoffee/matrix-rssbot)" - ) + USER_AGENT = "matrix-reportbot/dev (+https://git.private.coffee/PrivateCoffee/matrix-reportbot)" @classmethod def from_config(cls, config: ConfigParser): - """Create a new RSSBot instance from a config file. + """Create a new ReportBot instance from a config file. Args: config (ConfigParser): ConfigParser instance with the bot's config. Returns: - RSSBot: The new RSSBot instance. + ReportBot: The new ReportBot instance. """ - # Create a new RSSBot instance + # Create a new ReportBot instance bot = cls() bot.config = config # Override default values - if "RSSBot" in config: - if "LogLevel" in config["RSSBot"]: - bot.logger = Logger(config["RSSBot"]["LogLevel"]) + if "ReportBot" in config: + if "LogLevel" in config["ReportBot"]: + bot.logger = Logger(config["ReportBot"]["LogLevel"]) # Set up the Matrix client @@ -151,7 +156,7 @@ class RSSBot: bot.matrix_client.user_id = config["Matrix"].get("UserID") bot.matrix_client.device_id = config["Matrix"].get("DeviceID") - # Return the new RSSBot instance + # Return the new ReportBot instance return bot async def _get_user_id(self) -> str: @@ -200,135 +205,6 @@ class RSSBot: return device_id - async def process_command(self, room: MatrixRoom, event: RoomMessageText): - """Process a command. Called from the event_callback() method. - Delegates to the appropriate command handler. - - Args: - room (MatrixRoom): The room the command was sent in. - event (RoomMessageText): The event containing the command. - """ - - self.logger.log( - f"Received command {event.body} from {event.sender} in room {room.room_id}", - "debug", - ) - - if event.body.startswith("* "): - event.body = event.body[2:] - - command = event.body.split()[1] if event.body.split()[1:] else None - - await COMMANDS.get(command, COMMANDS[None])(room, event, self) - - async def _event_callback(self, room: MatrixRoom, event: Event): - self.logger.log("Received event: " + str(event.event_id), "debug") - try: - for eventtype, callback in EVENT_CALLBACKS.items(): - if isinstance(event, eventtype): - await callback(room, event, self) - except Exception as e: - self.logger.log( - f"Error in event callback for {event.__class__}: {e}", "error" - ) - - if self.debug: - await self.send_message( - room, f"Error: {e}\n\n```\n{traceback.format_exc()}\n```", True - ) - - def user_is_allowed(self, user_id: str) -> bool: - """Check if a user is allowed to use the bot. - - Args: - user_id (str): The user ID to check. - - Returns: - bool: Whether the user is allowed to use the bot. - """ - - return ( - ( - user_id in self.allowed_users - or f"*:{user_id.split(':')[1]}" in self.allowed_users - or f"@*:{user_id.split(':')[1]}" in self.allowed_users - ) - if self.allowed_users - else True - ) - - async def event_callback(self, room: MatrixRoom, event: Event): - """Callback for events. - - Args: - room (MatrixRoom): The room the event was sent in. - event (Event): The event. - """ - - if event.sender == self.matrix_client.user_id: - return - - if not self.user_is_allowed(event.sender): - if len(room.users) == 2: - await self.matrix_client.room_send( - room.room_id, - "m.room.message", - { - "msgtype": "m.notice", - "body": f"You are not allowed to use this bot. Please contact {self.operator} for more information.", - }, - ) - return - - task = asyncio.create_task(self._event_callback(room, event)) - - async def _response_callback(self, response: Response): - for response_type, callback in RESPONSE_CALLBACKS.items(): - if isinstance(response, response_type): - await callback(response, self) - - async def response_callback(self, response: Response): - task = asyncio.create_task(self._response_callback(response)) - - async def accept_pending_invites(self): - """Accept all pending invites.""" - - assert self.matrix_client, "Matrix client not set up" - - invites = self.matrix_client.invited_rooms - - for invite in [k for k in invites.keys()]: - if invite in self.room_ignore_list: - self.logger.log( - f"Ignoring invite to room {invite} (room is in ignore list)", - "debug", - ) - continue - - self.logger.log(f"Accepting invite to room {invite}") - - response = await self.matrix_client.join(invite) - - if isinstance(response, JoinError): - self.logger.log( - f"Error joining room {invite}: {response.message}. Not trying again.", - "error", - ) - - leave_response = await self.matrix_client.room_leave(invite) - - if isinstance(leave_response, RoomLeaveError): - self.logger.log( - f"Error leaving room {invite}: {leave_response.message}", - "error", - ) - self.room_ignore_list.append(invite) - - else: - await self.send_message( - invite, "Thank you for inviting me to your room!" - ) - async def upload_file( self, file: bytes, @@ -461,7 +337,7 @@ class RSSBot: msgtype = msgtype if msgtype else "m.notice" if notice else "m.text" - if not msgtype.startswith("rssbot."): + if not msgtype.startswith("reportbot."): msgcontent = { "msgtype": msgtype, "body": message, @@ -528,82 +404,127 @@ class RSSBot: if state_key is None or event["state_key"] == state_key: return event - async def process_room(self, room): - self.logger.log(f"Processing room {room}", "debug") - - state = await self.get_state_event(room, "rssbot.feeds") - - if not state: - feeds = [] - else: - feeds = state["content"]["feeds"] - - for feed in feeds: - self.logger.log(f"Processing {feed} in {room}", "debug") - - feed_state = await self.get_state_event(room, "rssbot.feed_state", feed) - - if feed_state: - self.logger.log( - f"Identified feed timestamp as {feed_state['content']['timestamp']}", - "debug", - ) - timestamp = int(feed_state["content"]["timestamp"]) - else: - timestamp = 0 - - try: - feed_content = feedparser.parse(feed) - new_timestamp = timestamp - for entry in feed_content.entries: - try: - entry_time_info = entry.published_parsed - except: - entry_time_info = entry.updated_parsed - - entry_timestamp = int(datetime(*entry_time_info[:6]).timestamp()) - - self.logger.log(f"Entry timestamp identified as {entry_timestamp}") - - if entry_timestamp > timestamp: - entry_message = f"__{feed_content.feed.title}: {entry.title}__\n\n{entry.description}\n\n{entry.link}" - await self.send_message(room, entry_message) - new_timestamp = max(entry_timestamp, new_timestamp) - - await self.send_state_event( - room, "rssbot.feed_state", {"timestamp": new_timestamp}, feed - ) - except Exception as e: - self.logger.log(f"Error processing feed at {feed}: {e}") - await self.send_message( - room, - f"Could not access or parse RSS feed at {feed}. Please ensure that you got the URL right, and that it is actually an RSS feed.", - True, - ) - - async def process_rooms(self): - while True: - self.logger.log("Starting to process rooms", "debug") - - start_timestamp = datetime.now() - - for room in self.matrix_client.rooms.values(): + async def get_new_reports(self, last_report_id): + # Call the Synapse admin API to get event reports since the last known one + endpoint = f"/_synapse/admin/v1/event_reports?from={last_report_id}" + async with aiohttp.ClientSession() as session: + async with session.get( + f"{self.matrix_client.homeserver}{endpoint}", + headers={"Authorization": f"Bearer {self.matrix_client.access_token}"}, + ) as response: try: - await self.process_room(room) + response_json = await response.json() + return ( + response_json.get("event_reports", []) if response_json else [] + ) + except json.JSONDecodeError: + self.logger.log("Failed to decode JSON response", "error") + return [] + + async def get_report_details(self, report_id): + # Call the Synapse admin API to get full details on a report + endpoint = f"/_synapse/admin/v1/event_reports/{report_id}" + async with aiohttp.ClientSession() as session: + async with session.get( + f"{self.matrix_client.homeserver}{endpoint}", + headers={"Authorization": f"Bearer {self.matrix_client.access_token}"}, + ) as response: + try: + response_json = await response.json() + return response_json + except json.JSONDecodeError: + self.logger.log("Failed to decode JSON response", "error") + return {} + + async def post_report_message(self, report_details): + # Extract relevant information from the report details + report_id = report_details.get("id") + event_id = report_details.get("event_id") + user_id = report_details.get("user_id") + room_id = report_details.get("room_id") + reason = report_details.get("reason") + content = report_details.get("event_json", {}) + sender = content.get("sender") + event_type = content.get("type") + body = content.get("content", {}).get("body", "No message content") + + # Format the message + message = ( + f"🚨 New Event Report (ID: {report_id}) 🚨\n" + f"Event ID: {event_id}\n" + f"Reported by: {user_id}\n" + f"Room ID: {room_id}\n" + f"Reason: {reason}\n" + f"Sender: {sender}\n" + f"Event Type: {event_type}\n" + f"Message Content: {body}" + ) + + # Send the formatted message to the pre-configured room + await self.matrix_client.room_send( + room_id=self.room_id, + message_type="m.room.message", + content={ + "msgtype": "m.text", + "body": message, + "format": "org.matrix.custom.html", + "formatted_body": f"
{message}
",
+ },
+ )
+
+ async def process_reports(self):
+ # Task to process reports
+ while True:
+ try:
+ self.logger.log("Starting to process reports", "debug")
+ report_state = await self.get_state_event(
+ self.room_id, "reportbot.report_state"
+ )
+
+ try:
+ known_report = int(report_state["content"]["report"])
+ except:
+ known_report = 0
+
+ self.logger.log(f"Processing reports since: {known_report}", "debug")
+
+ try:
+ reports = await self.get_new_reports(known_report)
+
+ for report in reports:
+ report_id = report["id"]
+
+ self.logger.log(f"Processing report: {report_id}", "debug")
+
+ known_report = max(known_report, report_id)
+ report_details = await self.get_report_details(report_id)
+ await self.post_report_message(report_details)
+
+ await self.send_state_event(
+ self.room_id, "reportbot.report_state", {"report": known_report}
+ )
except Exception as e:
- self.logger.log(
- f"Something went wrong processing room {room.room_id}: {e}",
- "error",
+ self.logger.log(f"Error processing reports: {e}")
+ await self.send_message(
+ self.room_id,
+ f"Something went wrong processing reports: {e}.",
+ True,
)
- end_timestamp = datetime.now()
-
- self.logger.log("Done processing rooms", "debug")
-
- if (
- time_taken := (end_timestamp - start_timestamp).seconds
- ) < self.loop_duration:
- await asyncio.sleep(self.loop_duration - time_taken)
+ self.logger.log("Done processing reports", "debug")
+ await asyncio.sleep(self.loop_duration)
+ except asyncio.CancelledError:
+ break
+ except Exception as e:
+ self.logger.log(f"Error processing reports: {e}")
+ try:
+ await self.send_message(
+ self.room_id,
+ f"Something went wrong processing reports: {e}.",
+ True,
+ )
+ except Exception as e:
+ self.logger.log(f"Error sending error message: {e}")
async def run(self):
"""Start the bot."""
@@ -629,18 +550,6 @@ class RSSBot:
self.logger.log("Running initial sync...", "debug")
sync = await self.matrix_client.sync(timeout=30000, full_state=True)
- if isinstance(sync, SyncResponse):
- await self.response_callback(sync)
- else:
- self.logger.log(f"Initial sync failed, aborting: {sync}", "critical")
- exit(1)
-
- # Set up callbacks
-
- self.logger.log("Setting up callbacks...", "debug")
-
- self.matrix_client.add_event_callback(self.event_callback, Event)
- self.matrix_client.add_response_callback(self.response_callback, Response)
# Set custom name
@@ -651,9 +560,9 @@ class RSSBot:
# Start syncing events
self.logger.log("Starting sync loop...", "warning")
sync_task = self.matrix_client.sync_forever(timeout=30000, full_state=True)
- feed_task = self.process_rooms()
+ reports_task = self.process_reports()
- tasks = asyncio.gather(sync_task, feed_task)
+ tasks = asyncio.gather(sync_task, reports_task)
try:
await tasks
diff --git a/src/matrix_rssbot/classes/logging.py b/src/matrix_reportbot/classes/logging.py
similarity index 100%
rename from src/matrix_rssbot/classes/logging.py
rename to src/matrix_reportbot/classes/logging.py
diff --git a/src/matrix_rssbot/classes/callbacks/__init__.py b/src/matrix_rssbot/classes/callbacks/__init__.py
deleted file mode 100644
index 3d1f602..0000000
--- a/src/matrix_rssbot/classes/callbacks/__init__.py
+++ /dev/null
@@ -1,26 +0,0 @@
-from nio import (
- RoomMessageText,
- InviteEvent,
- Event,
- SyncResponse,
- JoinResponse,
- RoomMemberEvent,
- Response,
- MegolmEvent,
- KeysQueryResponse
-)
-
-from .sync import sync_callback
-from .invite import room_invite_callback
-from .message import message_callback
-from .roommember import roommember_callback
-
-RESPONSE_CALLBACKS = {
- SyncResponse: sync_callback,
-}
-
-EVENT_CALLBACKS = {
- InviteEvent: room_invite_callback,
- RoomMessageText: message_callback,
- RoomMemberEvent: roommember_callback,
-}
\ No newline at end of file
diff --git a/src/matrix_rssbot/classes/callbacks/invite.py b/src/matrix_rssbot/classes/callbacks/invite.py
deleted file mode 100644
index 67995bc..0000000
--- a/src/matrix_rssbot/classes/callbacks/invite.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from nio import InviteEvent, MatrixRoom
-
-async def room_invite_callback(room: MatrixRoom, event: InviteEvent, bot):
- if room.room_id in bot.matrix_client.rooms:
- logging(f"Already in room {room.room_id} - ignoring invite")
- return
-
- bot.logger.log(f"Received invite to room {room.room_id} - joining...")
-
- response = await bot.matrix_client.join(room.room_id)
\ No newline at end of file
diff --git a/src/matrix_rssbot/classes/callbacks/message.py b/src/matrix_rssbot/classes/callbacks/message.py
deleted file mode 100644
index 256a587..0000000
--- a/src/matrix_rssbot/classes/callbacks/message.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from nio import MatrixRoom, RoomMessageText
-
-from datetime import datetime
-
-async def message_callback(room: MatrixRoom | str, event: RoomMessageText, bot):
- bot.logger.log(f"Received message from {event.sender} in room {room.room_id}")
-
- sent = datetime.fromtimestamp(event.server_timestamp / 1000)
- received = datetime.now()
- latency = received - sent
-
- if event.sender == bot.matrix_client.user_id:
- bot.logger.log("Message is from bot itself - ignoring", "debug")
-
- elif event.body.startswith("!rssbot") or event.body.startswith("* !rssbot"):
- await bot.process_command(room, event)
-
- elif event.body.startswith("!"):
- bot.logger.log(f"Received {event.body} - might be a command, but not for this bot - ignoring", "debug")
-
- else:
- bot.logger.log(f"Received regular message - ignoring", "debug")
-
- processed = datetime.now()
- processing_time = processed - received
-
- bot.logger.log(f"Message processing took {processing_time.total_seconds()} seconds (latency: {latency.total_seconds()} seconds)", "debug")
diff --git a/src/matrix_rssbot/classes/callbacks/roommember.py b/src/matrix_rssbot/classes/callbacks/roommember.py
deleted file mode 100644
index 3abf7de..0000000
--- a/src/matrix_rssbot/classes/callbacks/roommember.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from nio import RoomMemberEvent, MatrixRoom, KeysUploadError
-
-async def roommember_callback(room: MatrixRoom, event: RoomMemberEvent, bot):
- if event.membership == "leave":
- bot.logger.log(f"User {event.state_key} left room {room.room_id} - am I alone now?", "debug")
-
- if len(room.users) == 1:
- bot.logger.log("Yes, I was abandoned - leaving...", "debug")
- await bot.matrix_client.room_leave(room.room_id)
- return
diff --git a/src/matrix_rssbot/classes/callbacks/sync.py b/src/matrix_rssbot/classes/callbacks/sync.py
deleted file mode 100644
index 61df74d..0000000
--- a/src/matrix_rssbot/classes/callbacks/sync.py
+++ /dev/null
@@ -1,7 +0,0 @@
-async def sync_callback(response, bot):
- bot.logger.log(
- f"Sync response received (next batch: {response.next_batch})", "debug")
-
- bot.sync_response = response
-
- await bot.accept_pending_invites()
\ No newline at end of file
diff --git a/src/matrix_rssbot/classes/commands/__init__.py b/src/matrix_rssbot/classes/commands/__init__.py
deleted file mode 100644
index 3298263..0000000
--- a/src/matrix_rssbot/classes/commands/__init__.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from importlib import import_module
-
-from .unknown import command_unknown
-
-COMMANDS = {}
-
-for command in [
- "help",
- "newroom",
- "botinfo",
- "privacy",
- "addfeed",
- "listfeeds",
- "processfeeds",
- "removefeed",
-]:
- function = getattr(import_module(
- "." + command, "matrix_rssbot.classes.commands"), "command_" + command)
- COMMANDS[command] = function
-
-COMMANDS[None] = command_unknown
diff --git a/src/matrix_rssbot/classes/commands/addfeed.py b/src/matrix_rssbot/classes/commands/addfeed.py
deleted file mode 100644
index 16ab691..0000000
--- a/src/matrix_rssbot/classes/commands/addfeed.py
+++ /dev/null
@@ -1,67 +0,0 @@
-from nio.events.room_events import RoomMessageText
-from nio import RoomPutStateError
-from nio.rooms import MatrixRoom
-
-from datetime import datetime
-from urllib.request import urlopen
-
-import feedparser
-
-
-async def command_addfeed(room: MatrixRoom, event: RoomMessageText, bot):
- url = event.body.split()[2]
-
- bot.logger.log(f"Adding new feed to room {room.room_id}")
-
- state = await bot.get_state_event(room, "rssbot.feeds")
-
- if not state:
- feeds = []
- else:
- feeds = state["content"]["feeds"]
-
- feeds.append(url)
-
- feeds = list(set(feeds))
-
- try:
- feedparser.parse(url)
- except:
- await bot.send_state_event(
- f"Could not access or parse feed at {url}. Please ensure that you got the URL right, and that it is actually an RSS/Atom feed.",
- True,
- )
-
- try:
- response1 = await bot.send_state_event(
- room,
- "rssbot.feed_state",
- {"timestamp": int(datetime.now().timestamp())},
- url,
- )
-
- if isinstance(response1, RoomPutStateError):
- if response1.status_code == "M_FORBIDDEN":
- await bot.send_message(
- room,
- "Unable to put status events into this room. Please ensure I have the required permissions, then try again.",
- )
-
- await bot.send_message(
- room, "Unable to write feed state to the room. Please try again.", True
- )
- return
-
- response2 = await bot.send_state_event(room, "rssbot.feeds", {"feeds": feeds})
-
- if isinstance(response2, RoomPutStateError):
- await bot.send_message(
- room, "Unable to write feed list to the room. Please try again.", True
- )
- return
-
- await bot.send_message(room, f"Added {url} to this room's feeds.", True)
- except:
- await bot.send_message(
- room, "Sorry, something went wrong. Please try again.", true
- )
diff --git a/src/matrix_rssbot/classes/commands/botinfo.py b/src/matrix_rssbot/classes/commands/botinfo.py
deleted file mode 100644
index 5e725db..0000000
--- a/src/matrix_rssbot/classes/commands/botinfo.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from nio.events.room_events import RoomMessageText
-from nio.rooms import MatrixRoom
-
-
-async def command_botinfo(room: MatrixRoom, event: RoomMessageText, bot):
- logging("Showing bot info...")
-
- body = f"""
-Room info:
-
-Bot user ID: {bot.matrix_client.user_id}
-Current room ID: {room.room_id}
-"""
-
- await bot.send_message(room, body, True)
diff --git a/src/matrix_rssbot/classes/commands/help.py b/src/matrix_rssbot/classes/commands/help.py
deleted file mode 100644
index 5477d69..0000000
--- a/src/matrix_rssbot/classes/commands/help.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from nio.events.room_events import RoomMessageText
-from nio.rooms import MatrixRoom
-
-
-async def command_help(room: MatrixRoom, event: RoomMessageText, bot):
- body = """Available commands:
-
-- !rssbot help - Show this message
-- !rssbot botinfo - Show information about the bot
-- !rssbot privacy - Show privacy information
-- !rssbot addfeed \