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	// kicking doesn't make sense if there is no membership
25	let Ok(event) = self
26		.services
27		.state_accessor
28		.get_member(room_id, user_id)
29		.await
30	else {
31		return Ok(());
32	};
33
34	// this is required to prevent ban -> leave transitions
35	if !matches!(
36		event.membership,
37		MembershipState::Invite | MembershipState::Knock | MembershipState::Join,
38	) {
39		return Err!(Request(Forbidden(
40			"Cannot kick a user who is not apart of the room (current membership: {})",
41			event.membership
42		)));
43	}
44
45	self.services
46		.timeline
47		.build_and_append_pdu(
48			PduBuilder::state(user_id.to_string(), &RoomMemberEventContent {
49				membership: MembershipState::Leave,
50				reason: reason.cloned(),
51				is_direct: None,
52				join_authorized_via_users_server: None,
53				third_party_invite: None,
54				..event
55			}),
56			sender_user,
57			room_id,
58			state_lock,
59		)
60		.await?;
61
62	Ok(())
63}