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