Skip to main content

tuwunel_admin/user/
force_promote.rs

1use futures::StreamExt;
2use ruma::{
3	Int, OwnedRoomOrAliasId,
4	events::room::power_levels::{RoomPowerLevelsEventContent, UserPowerLevel},
5};
6use tuwunel_core::{Err, Result, info, matrix::pdu::PduBuilder, utils::ReadyExt};
7
8use crate::{
9	admin_command,
10	utils::{check_existing_local_user, parse_user_id},
11};
12
13#[admin_command]
14pub(super) async fn force_promote(
15	&self,
16	target_id: String,
17	room_id: OwnedRoomOrAliasId,
18) -> Result {
19	let target_id = parse_user_id(self.services, &target_id)?;
20
21	if self.services.globals.user_is_local(&target_id) {
22		check_existing_local_user(self.services, &target_id).await?;
23	}
24
25	let room_id = self
26		.services
27		.alias
28		.maybe_resolve(&room_id)
29		.await?;
30
31	let state_lock = self.services.state.mutex.lock(&room_id).await;
32
33	let room_power_levels = self
34		.services
35		.state_accessor
36		.get_power_levels(&room_id)
37		.await?;
38
39	let privileged_member = self
40		.services
41		.state_cache
42		.room_members(&room_id)
43		.ready_filter(|member_id| {
44			self.services.globals.user_is_local(member_id)
45				&& room_power_levels.user_can_change_user_power_level(member_id, &target_id)
46		})
47		.map(ToOwned::to_owned)
48		.ready_fold_default(|selected_user, member_id| match selected_user {
49			| None => Some(member_id),
50			| Some(selected_user) => Some(
51				if room_power_levels.for_user(&selected_user)
52					> room_power_levels.for_user(&member_id)
53				{
54					selected_user
55				} else {
56					member_id
57				},
58			),
59		})
60		.await;
61
62	let Some(privileged_member) = privileged_member else {
63		return Err!("No privileged user exists in room, cannot promote.");
64	};
65
66	info!("Selected privileged member {privileged_member}");
67
68	let power_level: Int = match room_power_levels.for_user(&privileged_member) {
69		| UserPowerLevel::Infinite => Int::MAX,
70		| UserPowerLevel::Int(x) => x,
71	};
72
73	let mut power_levels_content: RoomPowerLevelsEventContent = room_power_levels.try_into()?;
74
75	power_levels_content
76		.users
77		.insert(target_id.clone(), power_level);
78
79	let event_id = self
80		.services
81		.timeline
82		.build_and_append_pdu(
83			PduBuilder::state(String::new(), &power_levels_content),
84			&privileged_member,
85			&room_id,
86			&state_lock,
87		)
88		.await?;
89
90	drop(state_lock);
91
92	write!(
93		self,
94		"User {privileged_member} promoted {target_id} to {power_level} power level in \
95		 {room_id} - {event_id}"
96	)
97	.await?;
98
99	Ok(())
100}