style: Refactor admin crate, drop admin_command macro

`admin_command` depended on `implement`, which is now gone.
This commit is contained in:
timedout committed 2026-06-27 18:37:26 +01:00
1 parent 478560daae
commit 6fdeadc356
22 files changed
+4068 -4106

No files matched your search

+72 -72
View File
@@ -2,83 +2,83 @@ use conduwuit::{Err, Result};
use futures::StreamExt;
use ruma::OwnedRoomId;
use crate::{PAGE_SIZE, admin_command, get_room_info};
use crate::{PAGE_SIZE, get_room_info};
#[allow(clippy::fn_params_excessive_bools)]
#[admin_command]
pub(super) async fn list_rooms(
&self,
page: Option<usize>,
exclude_disabled: bool,
exclude_banned: bool,
include_empty: 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
.rooms
.metadata
.iter_ids()
.filter_map(|room_id| async move {
(!exclude_disabled || !self.services.rooms.metadata.is_disabled(&room_id).await)
.then_some(room_id)
})
.filter_map(|room_id| async move {
(!exclude_banned || !self.services.rooms.metadata.is_banned(&room_id).await)
.then_some(room_id)
})
.then(async |room_id| get_room_info(self.services, &room_id).await)
.then(|(room_id, total_members, name)| async move {
let local_members: Vec<_> = self
.services
.rooms
.state_cache
.active_local_users_in_room(&room_id)
.collect()
.await;
let local_members = local_members.len();
(room_id, total_members, local_members, name)
})
.filter_map(|(room_id, total_members, local_members, name)| async move {
(include_empty || local_members > 0).then_some((room_id, total_members, name))
})
.collect::<Vec<_>>()
.await;
impl crate::Context<'_> {
#[allow(clippy::fn_params_excessive_bools)]
pub(super) async fn list_rooms(
&self,
page: Option<usize>,
exclude_disabled: bool,
exclude_banned: bool,
include_empty: 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
.rooms
.metadata
.iter_ids()
.filter_map(|room_id| async move {
(!exclude_disabled || !self.services.rooms.metadata.is_disabled(&room_id).await)
.then_some(room_id)
})
.filter_map(|room_id| async move {
(!exclude_banned || !self.services.rooms.metadata.is_banned(&room_id).await)
.then_some(room_id)
})
.then(async |room_id| get_room_info(self.services, &room_id).await)
.then(|(room_id, total_members, name)| async move {
let local_members: Vec<_> = self
.services
.rooms
.state_cache
.active_local_users_in_room(&room_id)
.collect()
.await;
let local_members = local_members.len();
(room_id, total_members, local_members, name)
})
.filter_map(|(room_id, total_members, local_members, name)| async move {
(include_empty || local_members > 0).then_some((room_id, total_members, name))
})
.collect::<Vec<_>>()
.await;
rooms.sort_by_key(|r| r.1);
rooms.reverse();
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::<Vec<_>>();
let rooms = rooms
.into_iter()
.skip(page.saturating_sub(1).saturating_mul(PAGE_SIZE))
.take(PAGE_SIZE)
.collect::<Vec<_>>();
if rooms.is_empty() {
return Err!("No more rooms.");
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::<Vec<_>>()
.join("\n");
self.write_str(&format!("Rooms ({}):\n```\n{body}\n```", rooms.len()))
.await
}
let body = rooms
.iter()
.map(|(id, members, name)| {
if no_details {
format!("{id}")
} else {
format!("{id}\tMembers: {members}\tName: {name}")
}
})
.collect::<Vec<_>>()
.join("\n");
pub(super) async fn exists(&self, room_id: OwnedRoomId) -> Result {
let result = self.services.rooms.metadata.exists(&room_id).await;
self.write_str(&format!("Rooms ({}):\n```\n{body}\n```", rooms.len()))
.await
}
#[admin_command]
pub(super) async fn exists(&self, room_id: OwnedRoomId) -> Result {
let result = self.services.rooms.metadata.exists(&room_id).await;
self.write_str(&format!("{result}")).await
self.write_str(&format!("{result}")).await
}
}
+54 -54
View File
@@ -3,7 +3,7 @@ use conduwuit::{Err, Result, utils::ReadyExt};
use futures::StreamExt;
use ruma::OwnedRoomId;
use crate::{admin_command, admin_command_dispatch};
use crate::admin_command_dispatch;
#[admin_command_dispatch]
#[derive(Debug, Subcommand)]
@@ -26,62 +26,62 @@ pub enum RoomInfoCommand {
},
}
#[admin_command]
async fn list_joined_members(&self, room_id: OwnedRoomId, local_only: bool) -> Result {
let room_name = self
.services
.rooms
.state_accessor
.get_name(&room_id)
.await
.unwrap_or_else(|_| room_id.to_string());
impl crate::Context<'_> {
async fn list_joined_members(&self, room_id: OwnedRoomId, local_only: bool) -> Result {
let room_name = self
.services
.rooms
.state_accessor
.get_name(&room_id)
.await
.unwrap_or_else(|_| room_id.to_string());
let member_info: Vec<_> = self
.services
.rooms
.state_cache
.room_members(&room_id)
.ready_filter(|user_id| {
local_only
.then(|| self.services.globals.user_is_local(user_id))
.unwrap_or(true)
})
.filter_map(|user_id| async move {
Some((
self.services
.users
.displayname(&user_id)
.await
.unwrap_or_else(|_| user_id.to_string()),
user_id,
))
})
.collect()
.await;
let member_info: Vec<_> = self
.services
.rooms
.state_cache
.room_members(&room_id)
.ready_filter(|user_id| {
local_only
.then(|| self.services.globals.user_is_local(user_id))
.unwrap_or(true)
})
.filter_map(|user_id| async move {
Some((
self.services
.users
.displayname(&user_id)
.await
.unwrap_or_else(|_| user_id.to_string()),
user_id,
))
})
.collect()
.await;
let num = member_info.len();
let body = member_info
.into_iter()
.map(|(displayname, mxid)| format!("{mxid} | {displayname}"))
.collect::<Vec<_>>()
.join("\n");
let num = member_info.len();
let body = member_info
.into_iter()
.map(|(displayname, mxid)| format!("{mxid} | {displayname}"))
.collect::<Vec<_>>()
.join("\n");
self.write_str(&format!("{num} Members in Room \"{room_name}\":\n```\n{body}\n```"))
.await
}
self.write_str(&format!("{num} Members in Room \"{room_name}\":\n```\n{body}\n```"))
.await
}
#[admin_command]
async fn view_room_topic(&self, room_id: OwnedRoomId) -> Result {
let Ok(room_topic) = self
.services
.rooms
.state_accessor
.get_room_topic(&room_id)
.await
else {
return Err!("Room does not have a room topic set.");
};
async fn view_room_topic(&self, room_id: OwnedRoomId) -> Result {
let Ok(room_topic) = self
.services
.rooms
.state_accessor
.get_room_topic(&room_id)
.await
else {
return Err!("Room does not have a room topic set.");
};
self.write_str(&format!("Room topic:\n```\n{room_topic}\n```"))
.await
self.write_str(&format!("Room topic:\n```\n{room_topic}\n```"))
.await
}
}
+352 -351
View File
@@ -8,7 +8,7 @@ use conduwuit::{
use futures::{FutureExt, StreamExt};
use ruma::{OwnedRoomId, OwnedRoomOrAliasId, RoomAliasId, RoomId, RoomOrAliasId};
use crate::{admin_command, admin_command_dispatch, get_room_info};
use crate::{admin_command_dispatch, get_room_info};
#[admin_command_dispatch]
#[derive(Debug, Subcommand)]
@@ -45,238 +45,72 @@ pub enum RoomModerationCommand {
},
}
#[admin_command]
async fn ban_room(&self, room: OwnedRoomOrAliasId) -> Result {
debug!("Got room alias or ID: {}", room);
impl crate::Context<'_> {
async fn ban_room(&self, room: OwnedRoomOrAliasId) -> Result {
debug!("Got room alias or ID: {}", room);
let admin_room_alias = &self.services.globals.admin_alias;
let admin_room_alias = &self.services.globals.admin_alias;
if let Ok(admin_room_id) = self.services.admin.get_admin_room().await {
if room.to_string().eq(&admin_room_id) || room.to_string().eq(admin_room_alias) {
return Err!("Not allowed to ban the admin room.");
if let Ok(admin_room_id) = self.services.admin.get_admin_room().await {
if room.to_string().eq(&admin_room_id) || room.to_string().eq(admin_room_alias) {
return Err!("Not allowed to ban the admin room.");
}
}
}
let room_id = if room.is_room_id() {
let room_id = match RoomId::parse(&room) {
| Ok(room_id) => room_id,
| Err(e) => {
return Err!(
"Failed to parse room ID {room}. Please note that this requires a full room \
ID (`!awIh6gGInaS5wLQJwa:example.com`) or a room alias \
(`#roomalias:example.com`): {e}"
);
},
let room_id = if room.is_room_id() {
let room_id = match RoomId::parse(&room) {
| Ok(room_id) => room_id,
| Err(e) => {
return Err!(
"Failed to parse room ID {room}. Please note that this requires a full \
room ID (`!awIh6gGInaS5wLQJwa:example.com`) or a room alias \
(`#roomalias:example.com`): {e}"
);
},
};
debug!("Room specified is a room ID, banning room ID");
room_id.clone()
} else if room.is_room_alias_id() {
let room_alias = match RoomAliasId::parse(&room) {
| Ok(room_alias) => room_alias,
| Err(e) => {
return Err!(
"Failed to parse room ID {room}. Please note that this requires a full \
room ID (`!awIh6gGInaS5wLQJwa:example.com`) or a room alias \
(`#roomalias:example.com`): {e}"
);
},
};
debug!(
"Room specified is not a room ID, attempting to resolve room alias to a room ID \
locally, if not using get_alias_helper to fetch room ID remotely"
);
match self.services.rooms.alias.resolve_alias(&room_alias).await {
| Ok((room_id, servers)) => {
debug!(
%room_id,
?servers,
"Got federation response fetching room ID for room {room}"
);
room_id
},
| Err(e) => {
return Err!("Failed to resolve room alias {room} to a room ID: {e}");
},
}
} else {
return Err!(
"Room specified is not a room ID or room alias. Please note that this requires \
a full room ID (`!awIh6gGInaS5wLQJwa:example.com`) or a room alias \
(`#roomalias:example.com`)",
);
};
debug!("Room specified is a room ID, banning room ID");
room_id.clone()
} else if room.is_room_alias_id() {
let room_alias = match RoomAliasId::parse(&room) {
| Ok(room_alias) => room_alias,
| Err(e) => {
return Err!(
"Failed to parse room ID {room}. Please note that this requires a full room \
ID (`!awIh6gGInaS5wLQJwa:example.com`) or a room alias \
(`#roomalias:example.com`): {e}"
);
},
};
debug!(
"Room specified is not a room ID, attempting to resolve room alias to a room ID \
locally, if not using get_alias_helper to fetch room ID remotely"
);
match self.services.rooms.alias.resolve_alias(&room_alias).await {
| Ok((room_id, servers)) => {
debug!(
%room_id,
?servers,
"Got federation response fetching room ID for room {room}"
);
room_id
},
| Err(e) => {
return Err!("Failed to resolve room alias {room} to a room ID: {e}");
},
}
} else {
return Err!(
"Room specified is not a room ID or room alias. Please note that this requires a \
full room ID (`!awIh6gGInaS5wLQJwa:example.com`) or a room alias \
(`#roomalias:example.com`)",
);
};
info!("Making all users leave the room {room_id} and forgetting it");
let mut users = self
.services
.rooms
.state_cache
.room_members(&room_id)
.ready_filter(|user| self.services.globals.user_is_local(user))
.boxed();
while let Some(ref user_id) = users.next().await {
info!(
"Attempting leave for user {user_id} in room {room_id} (ignoring all errors, \
evicting admins too)",
);
if let Err(e) = leave_room(self.services, user_id, &room_id, None)
.boxed()
.await
{
warn!("Failed to leave room: {e}");
}
self.services.rooms.state_cache.forget(&room_id, user_id);
}
self.services
.rooms
.alias
.local_aliases_for_room(&room_id)
.for_each(|local_alias| async move {
self.services
.rooms
.alias
.remove_alias(&local_alias, &self.services.globals.server_user)
.await
.ok();
})
.await;
self.services.rooms.directory.set_not_public(&room_id); // remove from the room directory
self.services.rooms.metadata.ban_room(&room_id, true); // prevent further joins
self.services.rooms.metadata.disable_room(&room_id, true); // disable federation
self.write_str(
"Room banned, removed all our local users, and disabled incoming federation with room.",
)
.await
}
#[admin_command]
async fn ban_list_of_rooms(&self) -> Result {
if self.body.len() < 2
|| !self.body[0].trim().starts_with("```")
|| self.body.last().unwrap_or(&"").trim() != "```"
{
return Err!("Expected code block in command body. Add --help for details.",);
}
let rooms_s = self
.body
.to_vec()
.drain(1..self.body.len().saturating_sub(1))
.collect::<Vec<_>>();
let admin_room_alias = &self.services.globals.admin_alias;
let mut room_ban_count: usize = 0;
let mut room_ids: Vec<OwnedRoomId> = Vec::new();
for &room in &rooms_s {
match <&RoomOrAliasId>::try_from(room) {
| Ok(room_alias_or_id) => {
if let Ok(admin_room_id) = self.services.admin.get_admin_room().await {
if room.to_owned().eq(&admin_room_id) || room.to_owned().eq(admin_room_alias)
{
warn!("User specified admin room in bulk ban list, ignoring");
continue;
}
}
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) => {
// ignore rooms we failed to parse
warn!(
"Error parsing room \"{room}\" during bulk room banning, \
ignoring error and logging here: {e}"
);
continue;
},
};
room_ids.push(room_id.clone());
}
if room_alias_or_id.is_room_alias_id() {
match RoomAliasId::parse(room_alias_or_id) {
| Ok(room_alias) => {
let room_id = match self
.services
.rooms
.alias
.resolve_local_alias(&room_alias)
.await
{
| Ok(room_id) => room_id,
| _ => {
debug!(
"We don't have this room alias to a room ID locally, \
attempting to fetch room ID over federation"
);
match self
.services
.rooms
.alias
.resolve_alias(&room_alias)
.await
{
| Ok((room_id, servers)) => {
debug!(
%room_id,
?servers,
"Got federation response fetching room ID for \
{room}",
);
room_id
},
| Err(e) => {
warn!(
"Failed to resolve room alias {room} to a room \
ID: {e}"
);
continue;
},
}
},
};
room_ids.push(room_id);
},
| Err(e) => {
warn!(
"Error parsing room \"{room}\" during bulk room banning, \
ignoring error and logging here: {e}"
);
continue;
},
}
}
},
| Err(e) => {
warn!(
"Error parsing room \"{room}\" during bulk room banning, ignoring error and \
logging here: {e}"
);
continue;
},
}
}
for room_id in room_ids {
debug!("Banned {room_id} successfully");
room_ban_count = room_ban_count.saturating_add(1);
debug!("Making all users leave the room {room_id} and forgetting it");
info!("Making all users leave the room {room_id} and forgetting it");
let mut users = self
.services
.rooms
@@ -286,7 +120,7 @@ async fn ban_list_of_rooms(&self) -> Result {
.boxed();
while let Some(ref user_id) = users.next().await {
debug!(
info!(
"Attempting leave for user {user_id} in room {room_id} (ignoring all errors, \
evicting admins too)",
);
@@ -301,7 +135,6 @@ async fn ban_list_of_rooms(&self) -> Result {
self.services.rooms.state_cache.forget(&room_id, user_id);
}
// remove any local aliases, ignore errors
self.services
.rooms
.alias
@@ -316,139 +149,307 @@ async fn ban_list_of_rooms(&self) -> Result {
})
.await;
self.services.rooms.metadata.ban_room(&room_id, true);
// unpublish from room directory, ignore errors
self.services.rooms.directory.set_not_public(&room_id);
self.services.rooms.metadata.disable_room(&room_id, true);
self.services.rooms.directory.set_not_public(&room_id); // remove from the room directory
self.services.rooms.metadata.ban_room(&room_id, true); // prevent further joins
self.services.rooms.metadata.disable_room(&room_id, true); // disable federation
self.write_str(
"Room banned, removed all our local users, and disabled incoming federation with \
room.",
)
.await
}
self.write_str(&format!(
"Finished bulk room ban, banned {room_ban_count} total rooms, evicted all users, and \
disabled incoming federation with the room."
))
.await
}
async fn ban_list_of_rooms(&self) -> Result {
if self.body.len() < 2
|| !self.body[0].trim().starts_with("```")
|| self.body.last().unwrap_or(&"").trim() != "```"
{
return Err!("Expected code block in command body. Add --help for details.",);
}
#[admin_command]
async fn unban_room(&self, room: OwnedRoomOrAliasId) -> Result {
let room_id = if room.is_room_id() {
let room_id = match RoomId::parse(&room) {
| Ok(room_id) => room_id,
| Err(e) => {
return Err!(
"Failed to parse room ID {room}. Please note that this requires a full room \
ID (`!awIh6gGInaS5wLQJwa:example.com`) or a room alias \
(`#roomalias:example.com`): {e}"
let rooms_s = self
.body
.to_vec()
.drain(1..self.body.len().saturating_sub(1))
.collect::<Vec<_>>();
let admin_room_alias = &self.services.globals.admin_alias;
let mut room_ban_count: usize = 0;
let mut room_ids: Vec<OwnedRoomId> = Vec::new();
for &room in &rooms_s {
match <&RoomOrAliasId>::try_from(room) {
| Ok(room_alias_or_id) => {
if let Ok(admin_room_id) = self.services.admin.get_admin_room().await {
if room.to_owned().eq(&admin_room_id)
|| room.to_owned().eq(admin_room_alias)
{
warn!("User specified admin room in bulk ban list, ignoring");
continue;
}
}
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) => {
// ignore rooms we failed to parse
warn!(
"Error parsing room \"{room}\" during bulk room banning, \
ignoring error and logging here: {e}"
);
continue;
},
};
room_ids.push(room_id.clone());
}
if room_alias_or_id.is_room_alias_id() {
match RoomAliasId::parse(room_alias_or_id) {
| Ok(room_alias) => {
let room_id = match self
.services
.rooms
.alias
.resolve_local_alias(&room_alias)
.await
{
| Ok(room_id) => room_id,
| _ => {
debug!(
"We don't have this room alias to a room ID \
locally, attempting to fetch room ID over \
federation"
);
match self
.services
.rooms
.alias
.resolve_alias(&room_alias)
.await
{
| Ok((room_id, servers)) => {
debug!(
%room_id,
?servers,
"Got federation response fetching room ID for \
{room}",
);
room_id
},
| Err(e) => {
warn!(
"Failed to resolve room alias {room} to a \
room ID: {e}"
);
continue;
},
}
},
};
room_ids.push(room_id);
},
| Err(e) => {
warn!(
"Error parsing room \"{room}\" during bulk room banning, \
ignoring error and logging here: {e}"
);
continue;
},
}
}
},
| Err(e) => {
warn!(
"Error parsing room \"{room}\" during bulk room banning, ignoring error \
and logging here: {e}"
);
continue;
},
}
}
for room_id in room_ids {
debug!("Banned {room_id} successfully");
room_ban_count = room_ban_count.saturating_add(1);
debug!("Making all users leave the room {room_id} and forgetting it");
let mut users = self
.services
.rooms
.state_cache
.room_members(&room_id)
.ready_filter(|user| self.services.globals.user_is_local(user))
.boxed();
while let Some(ref user_id) = users.next().await {
debug!(
"Attempting leave for user {user_id} in room {room_id} (ignoring all \
errors, evicting admins too)",
);
},
if let Err(e) = leave_room(self.services, user_id, &room_id, None)
.boxed()
.await
{
warn!("Failed to leave room: {e}");
}
self.services.rooms.state_cache.forget(&room_id, user_id);
}
// remove any local aliases, ignore errors
self.services
.rooms
.alias
.local_aliases_for_room(&room_id)
.for_each(|local_alias| async move {
self.services
.rooms
.alias
.remove_alias(&local_alias, &self.services.globals.server_user)
.await
.ok();
})
.await;
self.services.rooms.metadata.ban_room(&room_id, true);
// unpublish from room directory, ignore errors
self.services.rooms.directory.set_not_public(&room_id);
self.services.rooms.metadata.disable_room(&room_id, true);
}
self.write_str(&format!(
"Finished bulk room ban, banned {room_ban_count} total rooms, evicted all users, \
and disabled incoming federation with the room."
))
.await
}
async fn unban_room(&self, room: OwnedRoomOrAliasId) -> Result {
let room_id = if room.is_room_id() {
let room_id = match RoomId::parse(&room) {
| Ok(room_id) => room_id,
| Err(e) => {
return Err!(
"Failed to parse room ID {room}. Please note that this requires a full \
room ID (`!awIh6gGInaS5wLQJwa:example.com`) or a room alias \
(`#roomalias:example.com`): {e}"
);
},
};
debug!("Room specified is a room ID, unbanning room ID");
self.services.rooms.metadata.ban_room(&room_id, false);
room_id.clone()
} else if room.is_room_alias_id() {
let room_alias = match RoomAliasId::parse(&room) {
| Ok(room_alias) => room_alias,
| Err(e) => {
return Err!(
"Failed to parse room ID {room}. Please note that this requires a full \
room ID (`!awIh6gGInaS5wLQJwa:example.com`) or a room alias \
(`#roomalias:example.com`): {e}"
);
},
};
debug!(
"Room specified is not a room ID, attempting to resolve room alias to a room ID \
locally, if not using get_alias_helper to fetch room ID remotely"
);
let room_id = match self
.services
.rooms
.alias
.resolve_local_alias(&room_alias)
.await
{
| Ok(room_id) => room_id,
| _ => {
debug!(
"We don't have this room alias to a room ID locally, attempting to \
fetch room ID over federation"
);
match self.services.rooms.alias.resolve_alias(&room_alias).await {
| Ok((room_id, servers)) => {
debug!(
%room_id,
?servers,
"Got federation response fetching room ID for room {room}"
);
room_id
},
| Err(e) => {
return Err!("Failed to resolve room alias {room} to a room ID: {e}");
},
}
},
};
self.services.rooms.metadata.ban_room(&room_id, false);
room_id
} else {
return Err!(
"Room specified is not a room ID or room alias. Please note that this requires \
a full room ID (`!awIh6gGInaS5wLQJwa:example.com`) or a room alias \
(`#roomalias:example.com`)",
);
};
debug!("Room specified is a room ID, unbanning room ID");
self.services.rooms.metadata.ban_room(&room_id, false);
self.services.rooms.metadata.disable_room(&room_id, false);
self.write_str("Room unbanned and federation re-enabled.")
.await
}
room_id.clone()
} else if room.is_room_alias_id() {
let room_alias = match RoomAliasId::parse(&room) {
| Ok(room_alias) => room_alias,
| Err(e) => {
return Err!(
"Failed to parse room ID {room}. Please note that this requires a full room \
ID (`!awIh6gGInaS5wLQJwa:example.com`) or a room alias \
(`#roomalias:example.com`): {e}"
);
},
};
debug!(
"Room specified is not a room ID, attempting to resolve room alias to a room ID \
locally, if not using get_alias_helper to fetch room ID remotely"
);
let room_id = match self
async fn list_banned_rooms(&self, no_details: bool) -> Result {
let room_ids: Vec<OwnedRoomId> = self
.services
.rooms
.alias
.resolve_local_alias(&room_alias)
.await
{
| Ok(room_id) => room_id,
| _ => {
debug!(
"We don't have this room alias to a room ID locally, attempting to fetch \
room ID over federation"
);
.metadata
.list_banned_rooms()
.map(Into::into)
.collect()
.await;
match self.services.rooms.alias.resolve_alias(&room_alias).await {
| Ok((room_id, servers)) => {
debug!(
%room_id,
?servers,
"Got federation response fetching room ID for room {room}"
);
room_id
},
| Err(e) => {
return Err!("Failed to resolve room alias {room} to a room ID: {e}");
},
if room_ids.is_empty() {
return Err!("No rooms are banned.");
}
let mut rooms = room_ids
.iter()
.stream()
.then(|room_id| get_room_info(self.services, room_id))
.collect::<Vec<_>>()
.await;
rooms.sort_by_key(|r| r.1);
rooms.reverse();
let num = rooms.len();
let body = rooms
.iter()
.map(|(id, members, name)| {
if no_details {
format!("{id}")
} else {
format!("{id}\tMembers: {members}\tName: {name}")
}
},
};
})
.collect::<Vec<_>>()
.join("\n");
self.services.rooms.metadata.ban_room(&room_id, false);
room_id
} else {
return Err!(
"Room specified is not a room ID or room alias. Please note that this requires a \
full room ID (`!awIh6gGInaS5wLQJwa:example.com`) or a room alias \
(`#roomalias:example.com`)",
);
};
self.services.rooms.metadata.disable_room(&room_id, false);
self.write_str("Room unbanned and federation re-enabled.")
.await
}
#[admin_command]
async fn list_banned_rooms(&self, no_details: bool) -> Result {
let room_ids: Vec<OwnedRoomId> = self
.services
.rooms
.metadata
.list_banned_rooms()
.map(Into::into)
.collect()
.await;
if room_ids.is_empty() {
return Err!("No rooms are banned.");
self.write_str(&format!("Rooms Banned ({num}):\n```\n{body}\n```"))
.await
}
let mut rooms = room_ids
.iter()
.stream()
.then(|room_id| get_room_info(self.services, room_id))
.collect::<Vec<_>>()
.await;
rooms.sort_by_key(|r| r.1);
rooms.reverse();
let num = rooms.len();
let body = rooms
.iter()
.map(|(id, members, name)| {
if no_details {
format!("{id}")
} else {
format!("{id}\tMembers: {members}\tName: {name}")
}
})
.collect::<Vec<_>>()
.join("\n");
self.write_str(&format!("Rooms Banned ({num}):\n```\n{body}\n```"))
.await
}