From 404bdd1db57b84f5002ba2969736f681d29fcdeb Mon Sep 17 00:00:00 2001
From: strawberry <strawberry@puppygock.gay>
Date: Mon, 15 Apr 2024 20:32:52 -0400
Subject: [PATCH] allow ban-list-of-rooms to take room aliases

Signed-off-by: strawberry <strawberry@puppygock.gay>
---
 src/service/admin/room_moderation.rs | 118 +++++++++++++++++++++++----
 1 file changed, 100 insertions(+), 18 deletions(-)

diff --git a/src/service/admin/room_moderation.rs b/src/service/admin/room_moderation.rs
index e450a20c5..413186ff1 100644
--- a/src/service/admin/room_moderation.rs
+++ b/src/service/admin/room_moderation.rs
@@ -1,7 +1,9 @@
 use std::fmt::Write as _;
 
 use clap::Subcommand;
-use ruma::{events::room::message::RoomMessageEventContent, OwnedUserId, RoomAliasId, RoomId, RoomOrAliasId};
+use ruma::{
+	events::room::message::RoomMessageEventContent, OwnedRoomId, OwnedUserId, RoomAliasId, RoomId, RoomOrAliasId,
+};
 use tracing::{debug, error, info};
 
 use crate::{
@@ -37,8 +39,8 @@ pub(crate) enum RoomModerationCommand {
 		room: Box<RoomOrAliasId>,
 	},
 
-	/// - Bans a list of rooms from a newline delimited codeblock similar to
-	///   `user deactivate-all`
+	/// - Bans a list of rooms (room IDs and room aliases) from a newline
+	///   delimited codeblock similar to `user deactivate-all`
 	BanListOfRooms {
 		#[arg(short, long)]
 		/// Evicts admins out of the room and ignores any potential errors when
@@ -236,41 +238,121 @@ pub(crate) async fn process(command: RoomModerationCommand, body: Vec<&str>) ->
 			if body.len() > 2 && body[0].trim().starts_with("```") && body.last().unwrap().trim() == "```" {
 				let rooms_s = body.clone().drain(1..body.len() - 1).collect::<Vec<_>>();
 
+				let admin_room_alias: Box<RoomAliasId> = format!("#admins:{}", services().globals.server_name())
+					.try_into()
+					.expect("#admins:server_name is a valid alias name");
+
 				let mut room_ban_count = 0;
-				let mut room_ids: Vec<&RoomId> = Vec::new();
+				let mut room_ids: Vec<OwnedRoomId> = Vec::new();
 
-				for &room_id in &rooms_s {
-					match <&RoomId>::try_from(room_id) {
-						Ok(owned_room_id) => {
-							// silently ignore deleting admin room
+				for &room in &rooms_s {
+					match <&RoomOrAliasId>::try_from(room) {
+						Ok(room_alias_or_id) => {
 							if let Some(admin_room_id) = Service::get_admin_room()? {
-								if owned_room_id.eq(&admin_room_id) {
+								if room.to_owned().eq(&admin_room_id) || room.to_owned().eq(&admin_room_alias) {
 									info!("User specified admin room in bulk ban list, ignoring");
 									continue;
 								}
 							}
 
-							room_ids.push(owned_room_id);
+							if room_alias_or_id.is_room_id() {
+								let room_id = match RoomId::parse(room_alias_or_id) {
+									Ok(room_id) => room_id,
+									Err(e) => {
+										if force {
+											// ignore rooms we failed to parse if we're force banning
+											error!(
+												"Error parsing room \"{room}\" during bulk room banning, ignoring \
+												 error and logging here: {e}"
+											);
+											continue;
+										}
+
+										return Ok(RoomMessageEventContent::text_plain(format!(
+											"{room} is not a valid room ID or room alias, please fix the list and try \
+											 again: {e}"
+										)));
+									},
+								};
+
+								room_ids.push(room_id);
+							}
+
+							if room_alias_or_id.is_room_alias_id() {
+								match RoomAliasId::parse(room_alias_or_id) {
+									Ok(room_alias) => {
+										let room_id = if let Some(room_id) =
+											services().rooms.alias.resolve_local_alias(&room_alias)?
+										{
+											room_id
+										} else {
+											debug!(
+												"We don't have this room alias to a room ID locally, attempting to \
+												 fetch room ID over federation"
+											);
+
+											match get_alias_helper(room_alias).await {
+												Ok(response) => {
+													debug!(
+														"Got federation response fetching room ID for room {room}: \
+														 {:?}",
+														response
+													);
+													response.room_id
+												},
+												Err(e) => {
+													if force {
+														format!(
+															"Failed to resolve room alias {room} to a room ID: {e}"
+														);
+													}
+
+													return Ok(RoomMessageEventContent::text_plain(format!(
+														"Failed to resolve room alias {room} to a room ID: {e}"
+													)));
+												},
+											}
+										};
+
+										room_ids.push(room_id);
+									},
+									Err(e) => {
+										if force {
+											// ignore rooms we failed to parse if we're force deleting
+											error!(
+												"Error parsing room \"{room}\" during bulk room banning, ignoring \
+												 error and logging here: {e}"
+											);
+											continue;
+										}
+
+										return Ok(RoomMessageEventContent::text_plain(format!(
+											"{room} is not a valid room ID or room alias, please fix the list and try \
+											 again: {e}"
+										)));
+									},
+								}
+							}
 						},
 						Err(e) => {
 							if force {
 								// ignore rooms we failed to parse if we're force deleting
 								error!(
-									"Error parsing room ID {room_id} during bulk room banning, ignoring error and \
+									"Error parsing room \"{room}\" during bulk room banning, ignoring error and \
 									 logging here: {e}"
 								);
 								continue;
 							}
 
 							return Ok(RoomMessageEventContent::text_plain(format!(
-								"{room_id} is not a valid room ID, please fix the list and try again: {e}"
+								"{room} is not a valid room ID or room alias, please fix the list and try again: {e}"
 							)));
 						},
 					}
 				}
 
 				for room_id in room_ids {
-					if services().rooms.metadata.ban_room(room_id, true).is_ok() {
+					if services().rooms.metadata.ban_room(&room_id, true).is_ok() {
 						debug!("Banned {room_id} successfully");
 						room_ban_count += 1;
 					}
@@ -280,7 +362,7 @@ pub(crate) async fn process(command: RoomModerationCommand, body: Vec<&str>) ->
 						for local_user in services()
 							.rooms
 							.state_cache
-							.room_members(room_id)
+							.room_members(&room_id)
 							.filter_map(|user| {
 								user.ok().filter(|local_user| {
 									local_user.server_name() == services().globals.server_name()
@@ -306,13 +388,13 @@ pub(crate) async fn process(command: RoomModerationCommand, body: Vec<&str>) ->
 								 admins too)",
 								&local_user, room_id
 							);
-							_ = leave_room(&local_user, room_id, None).await;
+							_ = leave_room(&local_user, &room_id, None).await;
 						}
 					} else {
 						for local_user in services()
 							.rooms
 							.state_cache
-							.room_members(room_id)
+							.room_members(&room_id)
 							.filter_map(|user| {
 								user.ok().filter(|local_user| {
 									local_user.server_name() == services().globals.server_name()
@@ -329,7 +411,7 @@ pub(crate) async fn process(command: RoomModerationCommand, body: Vec<&str>) ->
 							.collect::<Vec<OwnedUserId>>()
 						{
 							debug!("Attempting leave for user {} in room {}", &local_user, &room_id);
-							if let Err(e) = leave_room(&local_user, room_id, None).await {
+							if let Err(e) = leave_room(&local_user, &room_id, None).await {
 								error!(
 									"Error attempting to make local user {} leave room {} during bulk room banning: {}",
 									&local_user, &room_id, e
@@ -345,7 +427,7 @@ pub(crate) async fn process(command: RoomModerationCommand, body: Vec<&str>) ->
 					}
 
 					if disable_federation {
-						services().rooms.metadata.disable_room(room_id, true)?;
+						services().rooms.metadata.disable_room(&room_id, true)?;
 					}
 				}
 
-- 
GitLab