1use std::{
16 collections::{BTreeMap, BTreeSet, HashMap},
17 mem,
18 sync::Arc,
19};
20
21use bitflags::bitflags;
22use ruma::{
23 Int, MxcUri, OwnedUserId, UserId,
24 events::{
25 MessageLikeEventType, StateEventType,
26 ignored_user_list::IgnoredUserListEventContent,
27 presence::PresenceEvent,
28 room::{
29 member::{MembershipState, RoomMemberEventContent},
30 power_levels::{PowerLevelAction, RoomPowerLevels, UserPowerLevel},
31 },
32 },
33};
34use tracing::debug;
35
36use super::Room;
37use crate::{
38 MinimalRoomMemberEvent,
39 deserialized_responses::{DisplayName, MemberEvent},
40 store::{Result as StoreResult, StateStoreExt, ambiguity_map::is_display_name_ambiguous},
41};
42
43impl Room {
44 pub fn are_members_synced(&self) -> bool {
51 self.info.read().members_synced
52 }
53
54 #[cfg(feature = "testing")]
59 pub fn mark_members_synced(&self) {
60 self.info.update(|info| {
61 info.members_synced = true;
62 });
63 }
64
65 pub fn mark_members_missing(&self) {
67 self.info.update_if(|info| {
68 mem::replace(&mut info.members_synced, false)
70 })
71 }
72
73 pub async fn members(&self, memberships: RoomMemberships) -> StoreResult<Vec<RoomMember>> {
76 let user_ids = self.store.get_user_ids(self.room_id(), memberships).await?;
77
78 if user_ids.is_empty() {
79 return Ok(Vec::new());
80 }
81
82 let member_events = self
83 .store
84 .get_state_events_for_keys_static::<RoomMemberEventContent, _, _>(
85 self.room_id(),
86 &user_ids,
87 )
88 .await?
89 .into_iter()
90 .map(|raw_event| raw_event.deserialize())
91 .collect::<Result<Vec<_>, _>>()?;
92
93 let mut profiles = self.store.get_profiles(self.room_id(), &user_ids).await?;
94
95 let mut presences = self
96 .store
97 .get_presence_events(&user_ids)
98 .await?
99 .into_iter()
100 .filter_map(|e| {
101 e.deserialize().ok().map(|presence| (presence.sender.clone(), presence))
102 })
103 .collect::<BTreeMap<_, _>>();
104
105 let display_names = member_events.iter().map(|e| e.display_name()).collect::<Vec<_>>();
106 let room_info = self.member_room_info(&display_names).await?;
107
108 let mut members = Vec::new();
109
110 for event in member_events {
111 let profile = profiles.remove(event.user_id());
112 let presence = presences.remove(event.user_id());
113 members.push(RoomMember::from_parts(event, profile, presence, &room_info))
114 }
115
116 Ok(members)
117 }
118
119 pub fn active_members_count(&self) -> u64 {
122 self.info.read().active_members_count()
123 }
124
125 pub fn invited_members_count(&self) -> u64 {
127 self.info.read().invited_members_count()
128 }
129
130 pub fn joined_members_count(&self) -> u64 {
132 self.info.read().joined_members_count()
133 }
134
135 pub async fn get_member(&self, user_id: &UserId) -> StoreResult<Option<RoomMember>> {
143 let Some(raw_event) = self.store.get_member_event(self.room_id(), user_id).await? else {
144 debug!(%user_id, "Member event not found in state store");
145 return Ok(None);
146 };
147
148 let event = raw_event.deserialize()?;
149
150 let presence =
151 self.store.get_presence_event(user_id).await?.and_then(|e| e.deserialize().ok());
152
153 let profile = self.store.get_profile(self.room_id(), user_id).await?;
154
155 let display_names = [event.display_name()];
156 let room_info = self.member_room_info(&display_names).await?;
157
158 Ok(Some(RoomMember::from_parts(event, profile, presence, &room_info)))
159 }
160
161 async fn member_room_info<'a>(
165 &self,
166 display_names: &'a [DisplayName],
167 ) -> StoreResult<MemberRoomInfo<'a>> {
168 let max_power_level = self.max_power_level();
169 let power_levels = self.power_levels_or_default().await;
170
171 let users_display_names =
172 self.store.get_users_with_display_names(self.room_id(), display_names).await?;
173
174 let ignored_users = self
175 .store
176 .get_account_data_event_static::<IgnoredUserListEventContent>()
177 .await?
178 .map(|c| c.deserialize())
179 .transpose()?
180 .map(|e| e.content.ignored_users.into_keys().collect());
181
182 Ok(MemberRoomInfo {
183 power_levels: power_levels.into(),
184 max_power_level,
185 users_display_names,
186 ignored_users,
187 })
188 }
189}
190
191#[derive(Clone, Debug)]
193pub struct RoomMember {
194 pub(crate) event: Arc<MemberEvent>,
195 pub(crate) profile: Arc<Option<MinimalRoomMemberEvent>>,
199 #[allow(dead_code)]
200 pub(crate) presence: Arc<Option<PresenceEvent>>,
201 pub(crate) power_levels: Arc<RoomPowerLevels>,
202 pub(crate) max_power_level: i64,
203 pub(crate) display_name_ambiguous: bool,
204 pub(crate) is_ignored: bool,
205}
206
207impl RoomMember {
208 pub(crate) fn from_parts(
209 event: MemberEvent,
210 profile: Option<MinimalRoomMemberEvent>,
211 presence: Option<PresenceEvent>,
212 room_info: &MemberRoomInfo<'_>,
213 ) -> Self {
214 let MemberRoomInfo { power_levels, max_power_level, users_display_names, ignored_users } =
215 room_info;
216
217 let display_name = event.display_name();
218 let display_name_ambiguous = users_display_names
219 .get(&display_name)
220 .is_some_and(|s| is_display_name_ambiguous(&display_name, s));
221 let is_ignored = ignored_users.as_ref().is_some_and(|s| s.contains(event.user_id()));
222
223 Self {
224 event: event.into(),
225 profile: profile.into(),
226 presence: presence.into(),
227 power_levels: power_levels.clone(),
228 max_power_level: *max_power_level,
229 display_name_ambiguous,
230 is_ignored,
231 }
232 }
233
234 pub fn user_id(&self) -> &UserId {
236 self.event.user_id()
237 }
238
239 pub fn event(&self) -> &Arc<MemberEvent> {
241 &self.event
242 }
243
244 pub fn display_name(&self) -> Option<&str> {
246 if let Some(p) = self.profile.as_ref() {
247 p.as_original().and_then(|e| e.content.displayname.as_deref())
248 } else {
249 self.event.original_content()?.displayname.as_deref()
250 }
251 }
252
253 pub fn name(&self) -> &str {
258 if let Some(d) = self.display_name() { d } else { self.user_id().localpart() }
259 }
260
261 pub fn avatar_url(&self) -> Option<&MxcUri> {
263 if let Some(p) = self.profile.as_ref() {
264 p.as_original().and_then(|e| e.content.avatar_url.as_deref())
265 } else {
266 self.event.original_content()?.avatar_url.as_deref()
267 }
268 }
269
270 pub fn normalized_power_level(&self) -> UserPowerLevel {
276 let UserPowerLevel::Int(power_level) = self.power_level() else {
277 return UserPowerLevel::Infinite;
278 };
279
280 let normalized_power_level = if self.max_power_level > 0 {
281 normalize_power_level(power_level, self.max_power_level)
282 } else {
283 power_level
284 };
285
286 UserPowerLevel::Int(normalized_power_level)
287 }
288
289 pub fn power_level(&self) -> UserPowerLevel {
291 self.power_levels.for_user(self.user_id())
292 }
293
294 pub fn can_ban(&self) -> bool {
298 self.can_do_impl(|pls| pls.user_can_ban(self.user_id()))
299 }
300
301 pub fn can_invite(&self) -> bool {
305 self.can_do_impl(|pls| pls.user_can_invite(self.user_id()))
306 }
307
308 pub fn can_kick(&self) -> bool {
312 self.can_do_impl(|pls| pls.user_can_kick(self.user_id()))
313 }
314
315 pub fn can_redact_own(&self) -> bool {
319 self.can_do_impl(|pls| pls.user_can_redact_own_event(self.user_id()))
320 }
321
322 pub fn can_redact_other(&self) -> bool {
327 self.can_do_impl(|pls| pls.user_can_redact_event_of_other(self.user_id()))
328 }
329
330 pub fn can_send_message(&self, msg_type: MessageLikeEventType) -> bool {
334 self.can_do_impl(|pls| pls.user_can_send_message(self.user_id(), msg_type))
335 }
336
337 pub fn can_send_state(&self, state_type: StateEventType) -> bool {
341 self.can_do_impl(|pls| pls.user_can_send_state(self.user_id(), state_type))
342 }
343
344 pub fn can_pin_or_unpin_event(&self) -> bool {
346 self.can_send_state(StateEventType::RoomPinnedEvents)
347 }
348
349 pub fn can_trigger_room_notification(&self) -> bool {
355 self.can_do_impl(|pls| pls.user_can_trigger_room_notification(self.user_id()))
356 }
357
358 pub fn can_do(&self, action: PowerLevelAction) -> bool {
361 self.can_do_impl(|pls| pls.user_can_do(self.user_id(), action))
362 }
363
364 fn can_do_impl(&self, f: impl FnOnce(&RoomPowerLevels) -> bool) -> bool {
365 f(&self.power_levels)
366 }
367
368 pub fn name_ambiguous(&self) -> bool {
373 self.display_name_ambiguous
374 }
375
376 pub fn membership(&self) -> &MembershipState {
378 self.event.membership()
379 }
380
381 pub fn is_ignored(&self) -> bool {
383 self.is_ignored
384 }
385}
386
387pub(crate) struct MemberRoomInfo<'a> {
389 pub(crate) power_levels: Arc<RoomPowerLevels>,
390 pub(crate) max_power_level: i64,
391 pub(crate) users_display_names: HashMap<&'a DisplayName, BTreeSet<OwnedUserId>>,
392 pub(crate) ignored_users: Option<BTreeSet<OwnedUserId>>,
393}
394
395#[derive(Debug, Clone)]
397pub enum RoomMembersUpdate {
398 FullReload,
400 Partial(BTreeSet<OwnedUserId>),
402}
403
404bitflags! {
405 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
410 pub struct RoomMemberships: u16 {
411 const JOIN = 0b00000001;
413 const INVITE = 0b00000010;
415 const KNOCK = 0b00000100;
417 const LEAVE = 0b00001000;
419 const BAN = 0b00010000;
421
422 const ACTIVE = Self::JOIN.bits() | Self::INVITE.bits();
424 }
425}
426
427impl RoomMemberships {
428 pub fn matches(&self, membership: &MembershipState) -> bool {
430 if self.is_empty() {
431 return true;
432 }
433
434 let membership = match membership {
435 MembershipState::Ban => Self::BAN,
436 MembershipState::Invite => Self::INVITE,
437 MembershipState::Join => Self::JOIN,
438 MembershipState::Knock => Self::KNOCK,
439 MembershipState::Leave => Self::LEAVE,
440 _ => return false,
441 };
442
443 self.contains(membership)
444 }
445
446 pub fn as_vec(&self) -> Vec<MembershipState> {
448 let mut memberships = Vec::new();
449
450 if self.contains(Self::JOIN) {
451 memberships.push(MembershipState::Join);
452 }
453 if self.contains(Self::INVITE) {
454 memberships.push(MembershipState::Invite);
455 }
456 if self.contains(Self::KNOCK) {
457 memberships.push(MembershipState::Knock);
458 }
459 if self.contains(Self::LEAVE) {
460 memberships.push(MembershipState::Leave);
461 }
462 if self.contains(Self::BAN) {
463 memberships.push(MembershipState::Ban);
464 }
465
466 memberships
467 }
468}
469
470pub fn normalize_power_level(power_level: Int, max_power_level: i64) -> Int {
472 let mut power_level = i64::from(power_level);
473 power_level = (power_level * 100) / max_power_level;
474
475 Int::try_from(power_level.clamp(0, 100))
476 .expect("We clamped the normalized power level so they must fit into the Int")
477}
478
479#[cfg(test)]
480mod tests {
481 use proptest::prelude::*;
482
483 use super::*;
484
485 prop_compose! {
486 fn arb_int()(id in any::<i64>()) -> Int {
487 id.try_into().unwrap_or_default()
488 }
489 }
490
491 proptest! {
492 #![proptest_config(ProptestConfig::with_cases(10_000))]
493 #[test]
494 fn test_power_level_normalization_with_min_max_level(power_level in arb_int()) {
495 let normalized = normalize_power_level(power_level, 1);
496 let normalized = i64::from(normalized);
497
498 assert!(normalized >= 0);
499 assert!(normalized <= 100);
500 }
501 }
502
503 proptest! {
504 #![proptest_config(ProptestConfig::with_cases(10_000))]
505 #[test]
506 fn test_power_level_normalization(power_level in arb_int(), max_level in 1i64..) {
507 let normalized = normalize_power_level(power_level, max_level);
508 let normalized = i64::from(normalized);
509
510 assert!(normalized >= 0);
511 assert!(normalized <= 100);
512 }
513 }
514
515 #[test]
516 fn test_power_level_normalization_limits() {
517 let level = Int::MIN;
518 let normalized = normalize_power_level(level, 1);
519 let normalized = i64::from(normalized);
520 assert!(normalized >= 0);
521 assert!(normalized <= 100);
522
523 let level = Int::MAX;
524 let normalized = normalize_power_level(level, 1);
525 let normalized = i64::from(normalized);
526 assert!(normalized >= 0);
527 assert!(normalized <= 100);
528 }
529}