tuwunel_admin/user/
reject_invites.rs1use 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}