From 61f813c18727c9f5e8538b254a92146b355991df Mon Sep 17 00:00:00 2001 From: strawberry Date: Sat, 27 Apr 2024 01:06:43 -0400 Subject: [PATCH] admin command to get rooms a remote user is in, remove unnecessary dedupe+sort imagine this SQL query but in conduwuit: select * from users_in_public_rooms where user_id like '%user_id%'; Signed-off-by: strawberry --- .../admin/federation/federation_commands.rs | 66 ++++++++++++++++++- src/service/admin/federation/mod.rs | 14 +++- src/service/admin/user/user_commands.rs | 3 - 3 files changed, 76 insertions(+), 7 deletions(-) diff --git a/src/service/admin/federation/federation_commands.rs b/src/service/admin/federation/federation_commands.rs index 03a3edc7..b69fe73c 100644 --- a/src/service/admin/federation/federation_commands.rs +++ b/src/service/admin/federation/federation_commands.rs @@ -1,8 +1,13 @@ use std::fmt::Write as _; -use ruma::{events::room::message::RoomMessageEventContent, RoomId, ServerName}; +use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId, RoomId, ServerName, UserId}; -use crate::{services, utils::HtmlEscape, Result}; +use crate::{ + service::admin::{escape_html, get_room_info}, + services, + utils::HtmlEscape, + Result, +}; pub(crate) async fn disable_room(_body: Vec<&str>, room_id: Box) -> Result { services().rooms.metadata.disable_room(&room_id, true)?; @@ -70,3 +75,60 @@ pub(crate) async fn fetch_support_well_known( ), )) } + +pub(crate) async fn remote_user_in_rooms(_body: Vec<&str>, user_id: Box) -> Result { + if user_id.server_name() == services().globals.config.server_name { + return Ok(RoomMessageEventContent::text_plain( + "User belongs to our server, please use `list-joined-rooms` user admin command instead.", + )); + } + + if !services().users.exists(&user_id)? { + return Ok(RoomMessageEventContent::text_plain( + "Remote user does not exist in our database.", + )); + } + + let mut rooms: Vec<(OwnedRoomId, u64, String)> = services() + .rooms + .state_cache + .rooms_joined(&user_id) + .filter_map(Result::ok) + .map(|room_id| get_room_info(&room_id)) + .collect(); + + if rooms.is_empty() { + return Ok(RoomMessageEventContent::text_plain("User is not in any rooms.")); + } + + rooms.sort_by_key(|r| r.1); + rooms.reverse(); + + let output_plain = format!( + "Rooms {user_id} shares with us:\n{}", + rooms + .iter() + .map(|(id, members, name)| format!("{id}\tMembers: {members}\tName: {name}")) + .collect::>() + .join("\n") + ); + let output_html = format!( + "\n\t\t\n{}
Rooms {user_id} shares with \ + us
idmembersname
", + rooms + .iter() + .fold(String::new(), |mut output, (id, members, name)| { + writeln!( + output, + "{}\t{}\t{}", + escape_html(id.as_ref()), + members, + escape_html(name) + ) + .unwrap(); + output + }) + ); + + Ok(RoomMessageEventContent::text_html(output_plain, output_html)) +} diff --git a/src/service/admin/federation/mod.rs b/src/service/admin/federation/mod.rs index 1f8280b7..b3da33f2 100644 --- a/src/service/admin/federation/mod.rs +++ b/src/service/admin/federation/mod.rs @@ -1,7 +1,9 @@ use clap::Subcommand; -use ruma::{events::room::message::RoomMessageEventContent, RoomId, ServerName}; +use ruma::{events::room::message::RoomMessageEventContent, RoomId, ServerName, UserId}; -use self::federation_commands::{disable_room, enable_room, fetch_support_well_known, incoming_federeation}; +use self::federation_commands::{ + disable_room, enable_room, fetch_support_well_known, incoming_federeation, remote_user_in_rooms, +}; use crate::Result; pub(crate) mod federation_commands; @@ -34,6 +36,11 @@ pub(crate) enum FederationCommand { FetchSupportWellKnown { server_name: Box, }, + + /// - Lists all the rooms we share/track with the specified *remote* user + RemoteUserInRooms { + user_id: Box, + }, } pub(crate) async fn process(command: FederationCommand, body: Vec<&str>) -> Result { @@ -48,5 +55,8 @@ pub(crate) async fn process(command: FederationCommand, body: Vec<&str>) -> Resu FederationCommand::FetchSupportWellKnown { server_name, } => fetch_support_well_known(body, server_name).await?, + FederationCommand::RemoteUserInRooms { + user_id, + } => remote_user_in_rooms(body, user_id).await?, }) } diff --git a/src/service/admin/user/user_commands.rs b/src/service/admin/user/user_commands.rs index 4cb27636..b1a6436d 100644 --- a/src/service/admin/user/user_commands.rs +++ b/src/service/admin/user/user_commands.rs @@ -1,6 +1,5 @@ use std::{fmt::Write as _, sync::Arc}; -use itertools::Itertools; use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId, UserId}; use tracing::{error, info, warn}; @@ -318,8 +317,6 @@ pub(crate) async fn list_joined_rooms(_body: Vec<&str>, user_id: String) -> Resu .rooms_joined(&user_id) .filter_map(Result::ok) .map(|room_id| get_room_info(&room_id)) - .sorted_unstable() - .dedup() .collect(); if rooms.is_empty() {