Skip to main content

tuwunel_admin/room/moderation/
mod.rs

1mod ban_list_of_rooms;
2mod ban_room;
3mod list_banned_rooms;
4mod unban_room;
5
6use clap::Subcommand;
7use futures::{FutureExt, StreamExt};
8use ruma::{OwnedRoomOrAliasId, RoomId};
9use tuwunel_core::{Result, debug, utils::ReadyExt, warn};
10use tuwunel_service::Services;
11
12use crate::admin_command_dispatch;
13
14#[admin_command_dispatch]
15#[derive(Debug, Subcommand)]
16pub(crate) enum RoomModerationCommand {
17	/// - Bans a room from local users joining and evicts all our local users
18	///   (including server
19	/// admins)
20	///   from the room. Also blocks any invites (local and remote) for the
21	///   banned room, and disables federation entirely with it.
22	BanRoom {
23		/// The room in the format of `!roomid:example.com` or a room alias in
24		/// the format of `#roomalias:example.com`
25		room: OwnedRoomOrAliasId,
26	},
27
28	/// - Bans a list of rooms (room IDs and room aliases) from a newline
29	///   delimited codeblock similar to `user deactivate-all`. Applies the same
30	///   steps as ban-room
31	BanListOfRooms,
32
33	/// - Unbans a room to allow local users to join again
34	UnbanRoom {
35		/// The room in the format of `!roomid:example.com` or a room alias in
36		/// the format of `#roomalias:example.com`
37		room: OwnedRoomOrAliasId,
38	},
39
40	/// - List of all rooms we have banned
41	ListBannedRooms {
42		#[arg(long)]
43		/// Whether to only output room IDs without supplementary room
44		/// information
45		no_details: bool,
46	},
47}
48
49async fn do_ban_room(services: &Services, room_id: &RoomId) {
50	services.metadata.ban_room(room_id);
51
52	debug!("Banned {room_id} successfully");
53
54	debug!("Making all users leave the room {room_id} and forgetting it");
55	let mut users = services
56		.state_cache
57		.room_members(room_id)
58		.ready_filter(|user| services.globals.user_is_local(user))
59		.map(ToOwned::to_owned)
60		.boxed();
61
62	while let Some(ref user_id) = users.next().await {
63		debug!(
64			"Attempting leave for user {user_id} in room {room_id} (ignoring all errors, \
65			 evicting admins too)",
66		);
67
68		let state_lock = services.state.mutex.lock(room_id).await;
69
70		if let Err(e) = services
71			.membership
72			.leave(user_id, room_id, None, false, &state_lock)
73			.boxed()
74			.await
75		{
76			warn!("Failed to leave room: {e}");
77		}
78
79		drop(state_lock);
80
81		services.state_cache.forget(room_id, user_id);
82	}
83
84	// remove any local aliases, ignore errors
85	services
86		.alias
87		.local_aliases_for_room(room_id)
88		.map(ToOwned::to_owned)
89		.for_each(async |local_alias| {
90			if let Err(e) = services.alias.remove_alias(&local_alias).await {
91				warn!("Error removing alias {local_alias} for {room_id}: {e}");
92			}
93		})
94		.await;
95
96	// unpublish from room directory, ignore errors
97	services.directory.set_not_public(room_id);
98
99	services.metadata.disable_room(room_id);
100}