tuwunel_admin/room/
info.rs1use futures::{FutureExt, StreamExt, TryFutureExt, join};
2use ruma::{
3 OwnedRoomAliasId, OwnedRoomOrAliasId, RoomAliasId,
4 events::{
5 StateEventType,
6 room::{canonical_alias::RoomCanonicalAliasEventContent, power_levels::UserPowerLevel},
7 },
8};
9use tuwunel_core::{Err, Result, utils::TryFutureExtExt};
10
11use crate::admin_command;
12
13#[admin_command]
14pub(super) async fn room_info(&self, room: OwnedRoomOrAliasId) -> Result {
15 let room_id = self.services.alias.maybe_resolve(&room).await?;
16
17 if !self.services.metadata.exists(&room_id).await {
18 return Err!("Room {room_id} is not known to this server.");
19 }
20
21 let create_event = self
22 .services
23 .state_accessor
24 .get_create(&room_id)
25 .boxed();
26
27 let name = self
28 .services
29 .state_accessor
30 .get_name(&room_id)
31 .unwrap_or_else(|_| "(none)".to_owned())
32 .boxed();
33
34 let topic = self
35 .services
36 .state_accessor
37 .get_room_topic(&room_id)
38 .unwrap_or_else(|_| "(none)".to_owned())
39 .boxed();
40
41 let canonical_alias = self
42 .services
43 .state_accessor
44 .room_state_get_content(&room_id, &StateEventType::RoomCanonicalAlias, "")
45 .map_ok(|content: RoomCanonicalAliasEventContent| (content.alias, content.alt_aliases))
46 .unwrap_or_default()
47 .boxed();
48
49 let aliases = self
50 .services
51 .alias
52 .local_aliases_for_room(&room_id)
53 .map(Into::into)
54 .collect::<Vec<OwnedRoomAliasId>>()
55 .boxed();
56
57 let power_levels = self
58 .services
59 .state_accessor
60 .get_power_levels(&room_id)
61 .boxed();
62
63 let (create_event, name, topic, (canonical_alias, alt_aliases), mut aliases, power_levels) =
64 join!(create_event, name, topic, canonical_alias, aliases, power_levels);
65
66 let create_event = create_event?;
67 let power_levels = power_levels?;
68
69 let room_version = create_event.room_version()?;
70
71 aliases.sort();
72
73 let unlisted: Vec<_> = aliases
75 .iter()
76 .filter(|&alias| canonical_alias.as_ref() != Some(alias) && !alt_aliases.contains(alias))
77 .collect();
78
79 let state_default = power_levels.state_default;
80
81 let mut admins: Vec<_> = power_levels
82 .users
83 .keys()
84 .chain(
85 power_levels
86 .rules
87 .privileged_creators
88 .iter()
89 .flatten(),
90 )
91 .map(|user_id| (user_id, power_levels.for_user(user_id)))
92 .filter(|&(_, pl)| pl >= state_default)
93 .collect();
94
95 admins.sort_by(|(user_a, pl_a), (user_b, pl_b)| {
96 pl_b.cmp(pl_a).then_with(|| user_a.cmp(user_b))
97 });
98
99 writeln!(self, "```\nRoom information for {room_id}\n").await?;
100
101 writeln!(self, "Room version: {room_version}\n").await?;
102
103 writeln!(self, "Name: {name}").await?;
104 writeln!(self, "Topic: \n{topic}\n").await?;
105
106 writeln!(self, "Aliases:").await?;
107 let canonical_alias = canonical_alias
108 .as_deref()
109 .map(RoomAliasId::as_str)
110 .unwrap_or("none");
111 writeln!(self, " Canonical: {canonical_alias}").await?;
112
113 writeln!(self, " Alternative:").await?;
114 if alt_aliases.is_empty() {
115 writeln!(self, " none").await?;
116 } else {
117 for alias in &alt_aliases {
118 writeln!(self, " - {alias}").await?;
119 }
120 }
121
122 writeln!(self, " Local unlisted:").await?;
123 if unlisted.is_empty() {
124 writeln!(self, " none").await?;
125 } else {
126 for alias in unlisted {
127 writeln!(self, " - {alias}").await?;
128 }
129 }
130
131 writeln!(self, "\nAdmins (power level >= {state_default}):").await?;
132 for (user_id, pl) in &admins {
133 let pl = match pl {
134 | UserPowerLevel::Int(pl) => pl.to_string(),
135 | UserPowerLevel::Infinite => "creator".to_owned(),
136 };
137
138 writeln!(self, " - {user_id} ({pl})").await?;
139 }
140
141 writeln!(self, "```").await?;
142
143 Ok(())
144}