Skip to main content

tuwunel_admin/user/
reject_invites.rs

1use futures::{FutureExt, StreamExt};
2use ruma::OwnedRoomId;
3use tuwunel_core::{
4	Err, Result, debug_warn,
5	utils::{ReadyExt, stream::BroadbandExt},
6};
7
8use crate::{admin_command, utils::parse_local_user_id};
9
10#[derive(Default)]
11struct RejectInvitesAcc {
12	rejected: usize,
13	failed: usize,
14}
15
16impl RejectInvitesAcc {
17	fn merge(mut self, Self { rejected, failed }: Self) -> Self {
18		self.rejected = self.rejected.saturating_add(rejected);
19		self.failed = self.failed.saturating_add(failed);
20		self
21	}
22}
23
24#[admin_command]
25pub(super) async fn reject_invites(&self, user_id: String, reason: Option<String>) -> Result {
26	let user_id = parse_local_user_id(self.services, &user_id)?;
27	let reason = reason.as_deref();
28
29	let reject = async |room_id: OwnedRoomId| {
30		let state_lock = self.services.state.mutex.lock(&room_id).await;
31
32		match self
33			.services
34			.membership
35			.leave(&user_id, &room_id, reason.map(str::to_owned), false, &state_lock)
36			.boxed()
37			.await
38		{
39			| Ok(()) => RejectInvitesAcc { rejected: 1, ..Default::default() },
40			| Err(e) => {
41				debug_warn!(%user_id, %room_id, "Failed to reject invite: {e}");
42				RejectInvitesAcc { failed: 1, ..Default::default() }
43			},
44		}
45	};
46
47	let RejectInvitesAcc { rejected, failed } = self
48		.services
49		.state_cache
50		.rooms_invited(&user_id)
51		.map(ToOwned::to_owned)
52		.broad_then(reject)
53		.ready_fold(RejectInvitesAcc::default(), RejectInvitesAcc::merge)
54		.await;
55
56	if rejected == 0 && failed == 0 {
57		return Err!("{user_id} has no pending invites.");
58	}
59
60	write!(self, "Rejected {rejected} invite(s) for {user_id}. {failed} failed.").await
61}