use futures::{FutureExt, StreamExt}; use ruma::OwnedRoomId; use tuwunel_core::{Err, Result, utils::FutureBoolExt}; use crate::{PAGE_SIZE, admin_command, get_room_info}; #[admin_command] pub(super) async fn room_list( &self, page: Option, exclude_disabled: bool, exclude_banned: bool, no_details: bool, ) -> Result { // TODO: i know there's a way to do this with clap, but i can't seem to find it let page = page.unwrap_or(1); let mut rooms = self .services .metadata .iter_ids() .filter_map(async |room_id| { (!exclude_disabled || !self.services.metadata.is_disabled(room_id).await) .then_some(room_id) }) .filter_map(async |room_id| { (!exclude_banned || !self.services.metadata.is_banned(room_id).await) .then_some(room_id) }) .then(|room_id| get_room_info(self.services, room_id)) .collect::>() .await; rooms.sort_by_key(|r| r.1); rooms.reverse(); let rooms = rooms .into_iter() .skip(page.saturating_sub(1).saturating_mul(PAGE_SIZE)) .take(PAGE_SIZE) .collect::>(); if rooms.is_empty() { return Err!("No more rooms."); } let body = rooms .iter() .map(|(id, members, name)| { if no_details { format!("{id}") } else { format!("{id}\tMembers: {members}\tName: {name}") } }) .collect::>() .join("\n"); self.write_str(&format!("Rooms ({}):\n```\n{body}\n```", rooms.len())) .await } #[admin_command] pub(super) async fn room_exists(&self, room_id: OwnedRoomId) -> Result { let result = self.services.metadata.exists(&room_id).await; self.write_str(&format!("{result}")).await } #[admin_command] pub(super) async fn room_delete(&self, room_id: OwnedRoomId, force: bool) -> Result { if self.services.admin.is_admin_room(&room_id).await { return Err!("Cannot delete admin room"); } let state_lock = self.services.state.mutex.lock(&room_id).await; self.services .delete .delete_room(&room_id, force, state_lock) .await?; self.write_str("Successfully deleted the room from our database.") .await?; Ok(()) } #[admin_command] pub(super) async fn room_prune_empty(&self, force: bool) -> Result { let rooms = self .services .metadata .iter_ids() .filter(|room_id| { let has_no_local_users = self .services .state_cache .local_users_in_room(room_id) .boxed() .into_future() .map(|(next, ..)| next.is_none()) .boxed(); let has_no_local_invites = self .services .state_cache .local_users_invited_to_room(room_id) .boxed() .into_future() .map(|(next, ..)| next.is_none()) .boxed(); has_no_local_users.and(has_no_local_invites) }) .map(ToOwned::to_owned) .collect::>() .await; for room_id in &rooms { let state_lock = self.services.state.mutex.lock(room_id).await; self.services .delete .delete_room(room_id, force, state_lock) .await?; } let rooms_len = rooms.len(); self.write_str(&format!("Successfully deleted {rooms_len} rooms from our database.")) .await?; Ok(()) }