diff --git a/data/settings.json b/data/settings.json index bcbd20a..9ee2d23 100644 --- a/data/settings.json +++ b/data/settings.json @@ -13,7 +13,8 @@ "admin_command_list": [ "shrug", "emoji_size", - "emoji_list" + "emoji_list", + "mutual_rooms" ], "command_list": [ "ping", @@ -32,7 +33,8 @@ "shrug": "{prefix}shrug {text}: Append ¯\\_(ツ)_/¯ to the text", "emoji_size": "{prefix}emoji_size {size}: Set the default emoji size", "emoji_list": "{prefix}emoji_list: Get a list of usable custom emojis", - "aw": "{prefix}aw (Search term): Search the Arch Wiki" + "aw": "{prefix}aw (Search term): Search the Arch Wiki", + "mutual_rooms": "{prefix}mutual_rooms {user}: Get list of rooms shared with user" }, "source_url": "https://g.deadca.de/deadcade/Matrix-Selfbot" } diff --git a/main.py b/main.py index 406a465..2549a07 100644 --- a/main.py +++ b/main.py @@ -12,7 +12,7 @@ import requests # XKCD command import difflib # XKCD Title lookup import subprocess # GIF Emojis, ping command (admin) import random # roll command, http cat without args -import datetime # Ratelimiting +import datetime # Ratelimiting, room member cache invalidation # External dependencies import nio # Matrix library, matirx-nio[e2e] @@ -59,6 +59,7 @@ credentials = load_data(settings["credentials_path"]) # Set up global variables client = None ratelimits = {} +room_member_cache = {} # Common function definitions # Filter a title for local lookup with XKCD @@ -128,7 +129,7 @@ def resize_image(filename, size): # Mentions a user def mention(user): - return f"[{user.split(':')[0][1:]}](https://matrix.to/#/{user})" + return f"{user.split(':')[0][1:]}" # Send file to homeserver async def send_file(filename): @@ -324,6 +325,55 @@ async def aw(args, room, event): else: return await send_text(room.room_id, "Please provide a search term.") +async def mutual_rooms(args, room, event): + global client, room_member_cache + # Custom arg parser, because we need to use formatted_body for mentions from modern clients + if len(args) > 0: + args = event.formatted_body.split(" ")[1:] if event.formatted_body else event.body.split(" ")[1:] + user = "" + for arg in args: + if arg.startswith("href"): + # Parse user from a matrix.to link + try: + user = "@" + arg.replace("%40", "@").replace("%3A", ":").split("@")[1].split('"')[0] + except Exception: + pass + break + elif arg.startswith("@"): + # User is direct text + user = arg + break + if not user: + return await send_text(room.room_id, "Error parsing user MXID") + + # Create a list of mutual rooms + # Determine if the cache is usable + usecache = "date" in room_member_cache and room_member_cache["date"] > int(datetime.datetime.utcnow().timestamp()) + mutual = [] + # Get list of rooms the bot user is in + joined_rooms = await client.joined_rooms() + # If cache is invalid + if not usecache: + # Reset the cache timestamp + room_member_cache["date"] = int(datetime.datetime.utcnow().timestamp()) + 1800 + # Check every room + for lroom in joined_rooms.rooms: + members = [] + # If there's a valid cache entry + if usecache and lroom in room_member_cache: + # Use cache + members = room_member_cache[lroom] + else: + # Grab room member list from HS and store it in cache + members = await client.joined_members(lroom) + members = members.members + room_member_cache[lroom] = members + # Check if user is in room + if user in [x.user_id for x in members]: + mutual.append(lroom) + # Send room list + return await send_text(room.room_id, "List of mutual rooms with " + mention(user) + ":\n" + "\n".join([f"{x}" for x in mutual]) + "\n") + async def unknown_callback(room: nio.MatrixRoom, event: nio.UnknownEvent) -> None: if event.type == "m.reaction": # Get the ID of the event this was a reaction to