feat: Move & merge some membership-related commands into rooms membership

This commit is contained in:
timedout committed 2026-11-01 02:00:53 +00:00
1 parent 1666993187
commit 693f80eeb2
5 files changed
+129 -138

No files matched your search

+117
View File
@@ -0,0 +1,117 @@
use std::hash::RandomState;
use clap::Subcommand;
use conduwuit::{Err, Result, info};
use conduwuit_macros::admin_command_dispatch;
use ruma::{OwnedRoomOrAliasId, OwnedServerName, OwnedUserId};
#[admin_command_dispatch]
#[derive(Debug, Subcommand)]
pub enum RoomMembershipCommands {
/// Make a local user join a room.
#[clap(name = "join")]
JoinRoom {
/// The user to make join the room. Must be a local user.
user_id: OwnedUserId,
/// The room (ID or alias) to join.
room: OwnedRoomOrAliasId,
/// The server to join through, if a remote room.
///
/// Residents returned after resolving an alias override this option.
#[arg(long)]
via: Option<OwnedServerName>,
},
/// Make a local user leave a room or reject an invite.
#[clap(name = "leave", alias = "reject-invite")]
LeaveRoom {
/// The user to make leave the room. Must be a local user.
user_id: OwnedUserId,
/// The room (ID or alias) to leave.
room: OwnedRoomOrAliasId,
/// The server to leave through, if a room we are not a member of (e.g.
/// an invite).
///
/// Residents returned after resolving an alias override this option.
#[arg(long)]
via: Option<OwnedServerName>,
},
}
impl crate::Context<'_> {
pub(super) async fn join_room(
&self,
user_id: OwnedUserId,
room: OwnedRoomOrAliasId,
via: Option<OwnedServerName>,
) -> Result {
self.bail_restricted()?;
if !self.services.globals.user_is_local(&user_id) {
return Err!("User {user_id:?} does not belong to our server.");
}
let (room_id, residents) = self
.services
.rooms
.alias
.resolve_with_servers(&room, via.map(|v| vec![v]))
.await?;
self.services
.rooms
.membership
.join_room(&user_id, &room_id, None, &residents)
.await?;
self.write_str(&format!("Successfully joined {room_id} as {user_id}."))
.await
}
pub(super) async fn leave_room(
&self,
user_id: OwnedUserId,
room: OwnedRoomOrAliasId,
via: Option<OwnedServerName>,
) -> Result {
self.bail_restricted()?;
if !self.services.globals.user_is_local(&user_id) {
return Err!("User {user_id:?} does not belong to our server.");
}
let no_explicit_via = via.is_none();
let (room_id, residents) = self
.services
.rooms
.alias
.resolve_with_servers(&room, via.map(|v| vec![v]))
.await?;
if no_explicit_via
|| self
.services
.rooms
.state_cache
.server_in_room(self.services.globals.server_name(), &room_id)
.await
{
if !self
.services
.rooms
.state_cache
.is_joined(&user_id, &room_id)
.await
{
return Err!("User {user_id:?} is not a member of room {room_id:?}.");
}
info!("Leaving room locally...");
conduwuit_api::client::leave_room(self.services, &user_id, &room_id, None).await?;
} else {
info!("Leaving room remotely...");
conduwuit_api::client::remote_leave_room::<RandomState>(
self.services,
&user_id,
&room_id,
None,
residents.into_iter().collect(),
)
.await?;
}
self.write_str(&format!("Successfully made {user_id} leave {room_id}."))
.await
}
}
+8 -2
View File
@@ -3,6 +3,7 @@ mod commands;
mod directory;
mod graph;
mod info;
mod membership;
mod moderation;
mod state;
@@ -12,9 +13,10 @@ use ruma::OwnedRoomId;
use self::{
alias::RoomAliasCommand, directory::RoomDirectoryCommand, graph::GraphCommand,
info::RoomInfoCommand, moderation::RoomModerationCommand,
info::RoomInfoCommand, membership::RoomMembershipCommands, moderation::RoomModerationCommand,
state::RoomStateCommand,
};
use crate::{admin_command_dispatch, room::state::RoomStateCommand};
use crate::admin_command_dispatch;
#[admin_command_dispatch]
#[derive(Debug, Subcommand)]
@@ -58,6 +60,10 @@ pub enum RoomCommand {
#[command(subcommand)]
Directory(RoomDirectoryCommand),
/// Manage room memberships (join, leave, invite, etc.) for local users.
#[command(subcommand)]
Membership(RoomMembershipCommands),
/// Interface with room graphs directly (advanced).
#[command(subcommand)]
Graph(GraphCommand),
+3 -109
View File
@@ -1,8 +1,6 @@
use std::collections::{BTreeMap, HashSet};
use std::collections::BTreeMap;
use api::client::{
full_user_deactivate, leave_room, recreate_push_rules_and_return, remote_leave_room,
};
use api::client::{full_user_deactivate, leave_room, recreate_push_rules_and_return};
use conduwuit::{
Err, Result, debug_warn, info,
matrix::{Event, pdu::PartialPdu},
@@ -12,8 +10,7 @@ use conduwuit::{
use futures::{FutureExt, StreamExt};
use lettre::Address;
use ruma::{
OwnedEventId, OwnedRoomId, OwnedRoomOrAliasId, OwnedServerName, OwnedUserId, ServerName,
UserId, assign,
OwnedEventId, OwnedRoomId, OwnedRoomOrAliasId, OwnedUserId, UserId, assign,
events::{
RoomAccountDataEventType,
room::{power_levels::RoomPowerLevelsEventContent, redaction::RoomRedactionEventContent},
@@ -667,69 +664,6 @@ impl crate::Context<'_> {
.await
}
pub(super) async fn force_join_room(
&self,
user_id: String,
room_id: OwnedRoomOrAliasId,
via: Option<String>,
) -> Result {
let user_id = parse_local_user_id(self.services, &user_id)?;
let (room_id, mut servers) = self
.services
.rooms
.alias
.resolve_with_servers(&room_id, None)
.await?;
if let Some(via) = via.map(ServerName::parse).transpose()? {
servers.retain(|n| *n != via);
servers.insert(0, via);
}
assert!(
self.services.globals.user_is_local(&user_id),
"Parsed user_id must be a local user"
);
self.services
.rooms
.membership
.join_room(&user_id, &room_id, None, &servers)
.await?;
self.write_str(&format!("{user_id} has been joined to {room_id}."))
.await
}
pub(super) async fn force_leave_room(
&self,
user_id: String,
room_id: OwnedRoomOrAliasId,
) -> Result {
let user_id = parse_local_user_id(self.services, &user_id)?;
let room_id = self.services.rooms.alias.resolve(&room_id).await?;
assert!(
self.services.globals.user_is_local(&user_id),
"Parsed user_id must be a local user"
);
if !self
.services
.rooms
.state_cache
.is_joined(&user_id, &room_id)
.await
{
return Err!("{user_id} is not joined in the room");
}
leave_room(self.services, &user_id, &room_id, None)
.boxed()
.await?;
self.write_str(&format!("{user_id} has left {room_id}."))
.await
}
pub(super) async fn force_demote(
&self,
user_id: String,
@@ -943,46 +877,6 @@ impl crate::Context<'_> {
.await
}
pub(super) async fn force_leave_remote_room(
&self,
user_id: String,
room_id: OwnedRoomOrAliasId,
via: Option<String>,
) -> Result {
let user_id = parse_local_user_id(self.services, &user_id)?;
let (room_id, vias_raw) = self
.services
.rooms
.alias
.resolve_with_servers(
&room_id,
if let Some(v) = via.clone() {
Some(vec![ServerName::parse(v)?])
} else {
None
},
)
.await?;
assert!(
self.services.globals.user_is_local(&user_id),
"Parsed user_id must be a local user"
);
let mut vias: HashSet<OwnedServerName> = HashSet::new();
if let Some(via) = via {
vias.insert(ServerName::parse(via)?);
}
for server in vias_raw {
vias.insert(server);
}
remote_leave_room(self.services, &user_id, &room_id, None, vias)
.boxed()
.await?;
self.write_str(&format!("{user_id} successfully left {room_id} via remote server."))
.await
}
pub(super) async fn lock(&self, user_id: String) -> Result {
let user_id = parse_active_local_user_id(self.services, &user_id).await?;
-26
View File
@@ -193,32 +193,6 @@ pub enum UserCommand {
user_id: String,
},
/// Manually join a local user to a room.
ForceJoinRoom {
/// The user to join
user_id: String,
/// The room to join
room_id: OwnedRoomOrAliasId,
/// The server name to join via.
///
/// This server will always be tried first, however if more are
/// available, they may be tried after.
via: Option<String>,
},
/// Manually leave a local user from a room.
ForceLeaveRoom {
user_id: String,
room_id: OwnedRoomOrAliasId,
},
/// Manually leave a remote room for a local user.
ForceLeaveRemoteRoom {
user_id: String,
room_id: OwnedRoomOrAliasId,
via: Option<String>,
},
/// Forces the specified user to drop their power levels to the room
/// default, if their permissions allow and the auth check permits
ForceDemote {
+1 -1
View File
@@ -214,7 +214,7 @@ pub async fn leave_room(
Ok(())
}
pub async fn remote_leave_room<S: ::std::hash::BuildHasher>(
pub async fn remote_leave_room<S: std::hash::BuildHasher>(
services: &Services,
user_id: &UserId,
room_id: &RoomId,