Skip to main content

tuwunel_service/membership/
kick.rs

1use ruma::{
2	RoomId, UserId,
3	events::room::member::{MembershipState, RoomMemberEventContent},
4};
5use tuwunel_core::{Err, Result, implement, pdu::PduBuilder};
6
7use super::Service;
8use crate::rooms::timeline::RoomMutexGuard;
9
10#[implement(Service)]
11#[tracing::instrument(
12    level = "debug",
13    skip_all,
14    fields(%sender_user, %room_id, %user_id)
15)]
16pub async fn kick(
17	&self,
18	room_id: &RoomId,
19	user_id: &UserId,
20	reason: Option<&String>,
21	sender_user: &UserId,
22	state_lock: &RoomMutexGuard,
23) -> Result {
24	let Ok(event) = self
25		.services
26		.state_accessor
27		.get_member(room_id, user_id)
28		.await
29	else {
30		return Err!(Request(Forbidden("Cannot kick a user who is not in the room.")));
31	};
32
33	// this is required to prevent ban -> leave transitions
34	if !matches!(
35		event.membership,
36		MembershipState::Invite | MembershipState::Knock | MembershipState::Join,
37	) {
38		return Err!(Request(Forbidden(
39			"Cannot kick a user who is not apart of the room (current membership: {})",
40			event.membership
41		)));
42	}
43
44	self.services
45		.timeline
46		.build_and_append_pdu(
47			PduBuilder::state(user_id.to_string(), &RoomMemberEventContent {
48				membership: MembershipState::Leave,
49				reason: reason.cloned(),
50				is_direct: None,
51				join_authorized_via_users_server: None,
52				third_party_invite: None,
53				..event
54			}),
55			sender_user,
56			room_id,
57			state_lock,
58		)
59		.await?;
60
61	Ok(())
62}