use ruma::{ RoomId, UserId, events::room::member::{MembershipState, RoomMemberEventContent}, }; use tuwunel_core::{Err, Result, implement, pdu::PduBuilder}; use super::Service; use crate::rooms::timeline::RoomMutexGuard; #[implement(Service)] #[tracing::instrument( level = "debug", skip_all, fields(%sender_user, %room_id, %user_id) )] pub async fn kick( &self, room_id: &RoomId, user_id: &UserId, reason: Option<&String>, sender_user: &UserId, state_lock: &RoomMutexGuard, ) -> Result { // kicking doesn't make sense if there is no membership let Ok(event) = self .services .state_accessor .get_member(room_id, user_id) .await else { return Ok(()); }; // this is required to prevent ban -> leave transitions if !matches!( event.membership, MembershipState::Invite | MembershipState::Knock | MembershipState::Join, ) { return Err!(Request(Forbidden( "Cannot kick a user who is not apart of the room (current membership: {})", event.membership ))); } self.services .timeline .build_and_append_pdu( PduBuilder::state(user_id.to_string(), &RoomMemberEventContent { membership: MembershipState::Leave, reason: reason.cloned(), is_direct: None, join_authorized_via_users_server: None, third_party_invite: None, ..event }), sender_user, room_id, state_lock, ) .await?; Ok(()) }