tuwunel_admin/user/
reject_invites.rs1use futures::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 .await
37 {
38 | Ok(()) => RejectInvitesAcc { rejected: 1, ..Default::default() },
39 | Err(e) => {
40 debug_warn!(%user_id, %room_id, "Failed to reject invite: {e}");
41 RejectInvitesAcc { failed: 1, ..Default::default() }
42 },
43 }
44 };
45
46 let RejectInvitesAcc { rejected, failed } = self
47 .services
48 .state_cache
49 .rooms_invited(&user_id)
50 .map(ToOwned::to_owned)
51 .broad_then(reject)
52 .ready_fold(RejectInvitesAcc::default(), RejectInvitesAcc::merge)
53 .await;
54
55 if rejected == 0 && failed == 0 {
56 return Err!("{user_id} has no pending invites.");
57 }
58
59 write!(self, "Rejected {rejected} invite(s) for {user_id}. {failed} failed.").await
60}