1use std::{
16 collections::{BTreeMap, BTreeSet, HashMap},
17 mem,
18 sync::Arc,
19};
20
21use bitflags::bitflags;
22use futures_util::future;
23use ruma::{
24 Int, MxcUri, OwnedUserId, UserId,
25 events::{
26 MessageLikeEventType, StateEventType,
27 ignored_user_list::IgnoredUserListEventContent,
28 presence::PresenceEvent,
29 room::{
30 member::{MembershipState, RoomMemberEventContent},
31 power_levels::{PowerLevelAction, RoomPowerLevels, UserPowerLevel},
32 },
33 },
34};
35use tracing::debug;
36
37use super::Room;
38use crate::{
39 MinimalRoomMemberEvent, StateStore, StoreError,
40 deserialized_responses::{DisplayName, MemberEvent},
41 store::{Result as StoreResult, StateStoreExt, ambiguity_map::is_display_name_ambiguous},
42};
43
44impl Room {
45 pub fn are_members_synced(&self) -> bool {
52 self.info.read().members_synced
53 }
54
55 #[cfg(feature = "testing")]
60 pub fn mark_members_synced(&self) {
61 self.info.update(|info| {
62 info.members_synced = true;
63 });
64 }
65
66 pub fn mark_members_missing(&self) {
68 self.info.update_if(|info| {
69 mem::replace(&mut info.members_synced, false)
71 })
72 }
73
74 pub async fn members(&self, memberships: RoomMemberships) -> StoreResult<Vec<RoomMember>> {
77 let user_ids = self.store.get_user_ids(self.room_id(), memberships).await?;
78
79 if user_ids.is_empty() {
80 return Ok(Vec::new());
81 }
82
83 let member_events = self
84 .store
85 .get_state_events_for_keys_static::<RoomMemberEventContent, _, _>(
86 self.room_id(),
87 &user_ids,
88 )
89 .await?
90 .into_iter()
91 .map(|raw_event| raw_event.deserialize())
92 .collect::<Result<Vec<_>, _>>()?;
93
94 let mut profiles = self.store.get_profiles(self.room_id(), &user_ids).await?;
95
96 let mut presences = self
97 .store
98 .get_presence_events(&user_ids)
99 .await?
100 .into_iter()
101 .filter_map(|e| {
102 e.deserialize().ok().map(|presence| (presence.sender.clone(), presence))
103 })
104 .collect::<BTreeMap<_, _>>();
105
106 let display_names = member_events.iter().map(|e| e.display_name()).collect::<Vec<_>>();
107 let room_info = self.member_room_info(&display_names).await?;
108
109 let mut members = Vec::new();
110
111 for event in member_events {
112 let profile = profiles.remove(event.user_id());
113 let presence = presences.remove(event.user_id());
114 members.push(RoomMember::from_parts(event, profile, presence, &room_info))
115 }
116
117 Ok(members)
118 }
119
120 pub fn active_members_count(&self) -> u64 {
123 self.info.read().active_members_count()
124 }
125
126 pub fn invited_members_count(&self) -> u64 {
128 self.info.read().invited_members_count()
129 }
130
131 pub fn joined_members_count(&self) -> u64 {
133 self.info.read().joined_members_count()
134 }
135
136 pub async fn get_member(&self, user_id: &UserId) -> StoreResult<Option<RoomMember>> {
144 let event = async {
145 let Some(raw_event) = self.store.get_member_event(self.room_id(), user_id).await?
146 else {
147 debug!(%user_id, "Member event not found in state store");
148 return Ok(None);
149 };
150
151 Ok(Some(raw_event.deserialize()?))
152 };
153 let presence = async {
154 let raw_event = self.store.get_presence_event(user_id).await?;
155 Ok::<Option<PresenceEvent>, StoreError>(raw_event.and_then(|e| e.deserialize().ok()))
156 };
157
158 let profile = async { self.store.get_profile(self.room_id(), user_id).await };
159
160 let (Some(event), presence, profile) = future::try_join3(event, presence, profile).await?
161 else {
162 return Ok(None);
163 };
164
165 let display_names = [event.display_name()];
166 let room_info = self.member_room_info(&display_names).await?;
167
168 Ok(Some(RoomMember::from_parts(event, profile, presence, &room_info)))
169 }
170
171 async fn member_room_info<'a>(
175 &self,
176 display_names: &'a [DisplayName],
177 ) -> StoreResult<MemberRoomInfo<'a>> {
178 let max_power_level = self.max_power_level();
179 let power_levels = async { Ok(self.power_levels_or_default().await) };
180
181 let users_display_names =
182 self.store.get_users_with_display_names(self.room_id(), display_names);
183
184 let ignored_users = async {
185 Ok(self
186 .store
187 .get_account_data_event_static::<IgnoredUserListEventContent>()
188 .await?
189 .map(|c| c.deserialize())
190 .transpose()?
191 .map(|e| e.content.ignored_users.into_keys().collect()))
192 };
193
194 let (power_levels, users_display_names, ignored_users) =
195 future::try_join3(power_levels, users_display_names, ignored_users).await?;
196
197 Ok(MemberRoomInfo {
198 power_levels: power_levels.into(),
199 max_power_level,
200 users_display_names,
201 ignored_users,
202 service_members: self.service_members(),
203 })
204 }
205}
206
207#[derive(Clone, Debug)]
209pub struct RoomMember {
210 pub(crate) event: Arc<MemberEvent>,
211 pub(crate) profile: Arc<Option<MinimalRoomMemberEvent>>,
215 #[allow(dead_code)]
216 pub(crate) presence: Arc<Option<PresenceEvent>>,
217 pub(crate) power_levels: Arc<RoomPowerLevels>,
218 pub(crate) max_power_level: i64,
219 pub(crate) display_name_ambiguous: bool,
220 pub(crate) is_ignored: bool,
221 pub(crate) is_service_member: bool,
222}
223
224impl RoomMember {
225 pub(crate) fn from_parts(
226 event: MemberEvent,
227 profile: Option<MinimalRoomMemberEvent>,
228 presence: Option<PresenceEvent>,
229 room_info: &MemberRoomInfo<'_>,
230 ) -> Self {
231 let MemberRoomInfo {
232 power_levels,
233 max_power_level,
234 users_display_names,
235 ignored_users,
236 service_members,
237 } = room_info;
238
239 let user_id = event.user_id().to_owned();
240 let display_name = event.display_name();
241 let display_name_ambiguous = users_display_names
242 .get(&display_name)
243 .is_some_and(|s| is_display_name_ambiguous(&display_name, s));
244 let is_ignored = ignored_users.as_ref().is_some_and(|s| s.contains(event.user_id()));
245 let is_service_member = service_members.as_ref().is_some_and(|s| s.contains(&user_id));
246
247 Self {
248 event: event.into(),
249 profile: profile.into(),
250 presence: presence.into(),
251 power_levels: power_levels.clone(),
252 max_power_level: *max_power_level,
253 display_name_ambiguous,
254 is_ignored,
255 is_service_member,
256 }
257 }
258
259 pub fn user_id(&self) -> &UserId {
261 self.event.user_id()
262 }
263
264 pub fn event(&self) -> &Arc<MemberEvent> {
266 &self.event
267 }
268
269 pub fn display_name(&self) -> Option<&str> {
271 if let Some(p) = self.profile.as_ref() {
272 p.content.displayname.as_deref()
273 } else {
274 self.event.displayname_value()
275 }
276 }
277
278 pub fn name(&self) -> &str {
283 if let Some(d) = self.display_name() { d } else { self.user_id().localpart() }
284 }
285
286 pub fn avatar_url(&self) -> Option<&MxcUri> {
288 if let Some(p) = self.profile.as_ref() {
289 p.content.avatar_url.as_deref()
290 } else {
291 self.event.avatar_url()
292 }
293 }
294
295 pub fn normalized_power_level(&self) -> UserPowerLevel {
301 let UserPowerLevel::Int(power_level) = self.power_level() else {
302 return UserPowerLevel::Infinite;
303 };
304
305 let normalized_power_level = if self.max_power_level > 0 {
306 normalize_power_level(power_level, self.max_power_level)
307 } else {
308 power_level
309 };
310
311 UserPowerLevel::Int(normalized_power_level)
312 }
313
314 pub fn power_level(&self) -> UserPowerLevel {
316 self.power_levels.for_user(self.user_id())
317 }
318
319 pub fn can_ban(&self) -> bool {
323 self.can_do_impl(|pls| pls.user_can_ban(self.user_id()))
324 }
325
326 pub fn can_invite(&self) -> bool {
330 self.can_do_impl(|pls| pls.user_can_invite(self.user_id()))
331 }
332
333 pub fn can_kick(&self) -> bool {
337 self.can_do_impl(|pls| pls.user_can_kick(self.user_id()))
338 }
339
340 pub fn can_redact_own(&self) -> bool {
344 self.can_do_impl(|pls| pls.user_can_redact_own_event(self.user_id()))
345 }
346
347 pub fn can_redact_other(&self) -> bool {
352 self.can_do_impl(|pls| pls.user_can_redact_event_of_other(self.user_id()))
353 }
354
355 pub fn can_send_message(&self, msg_type: MessageLikeEventType) -> bool {
359 self.can_do_impl(|pls| pls.user_can_send_message(self.user_id(), msg_type))
360 }
361
362 pub fn can_send_state(&self, state_type: StateEventType) -> bool {
366 self.can_do_impl(|pls| pls.user_can_send_state(self.user_id(), state_type))
367 }
368
369 pub fn can_pin_or_unpin_event(&self) -> bool {
371 self.can_send_state(StateEventType::RoomPinnedEvents)
372 }
373
374 pub fn can_trigger_room_notification(&self) -> bool {
380 self.can_do_impl(|pls| pls.user_can_trigger_room_notification(self.user_id()))
381 }
382
383 pub fn can_do(&self, action: PowerLevelAction) -> bool {
386 self.can_do_impl(|pls| pls.user_can_do(self.user_id(), action))
387 }
388
389 fn can_do_impl(&self, f: impl FnOnce(&RoomPowerLevels) -> bool) -> bool {
390 f(&self.power_levels)
391 }
392
393 pub fn name_ambiguous(&self) -> bool {
398 self.display_name_ambiguous
399 }
400
401 pub fn membership(&self) -> &MembershipState {
403 self.event.membership()
404 }
405
406 pub fn is_ignored(&self) -> bool {
408 self.is_ignored
409 }
410
411 pub fn is_service_member(&self) -> bool {
413 self.is_service_member
414 }
415}
416
417pub(crate) struct MemberRoomInfo<'a> {
419 pub(crate) power_levels: Arc<RoomPowerLevels>,
420 pub(crate) max_power_level: i64,
421 pub(crate) users_display_names: HashMap<&'a DisplayName, BTreeSet<OwnedUserId>>,
422 pub(crate) ignored_users: Option<BTreeSet<OwnedUserId>>,
423 pub(crate) service_members: Option<BTreeSet<OwnedUserId>>,
424}
425
426#[derive(Debug, Clone)]
428pub enum RoomMembersUpdate {
429 FullReload,
431 Partial(BTreeSet<OwnedUserId>),
433}
434
435bitflags! {
436 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
441 pub struct RoomMemberships: u16 {
442 const JOIN = 0b00000001;
444 const INVITE = 0b00000010;
446 const KNOCK = 0b00000100;
448 const LEAVE = 0b00001000;
450 const BAN = 0b00010000;
452
453 const ACTIVE = Self::JOIN.bits() | Self::INVITE.bits();
455 }
456}
457
458impl RoomMemberships {
459 pub fn matches(&self, membership: &MembershipState) -> bool {
461 if self.is_empty() {
462 return true;
463 }
464
465 let membership = match membership {
466 MembershipState::Ban => Self::BAN,
467 MembershipState::Invite => Self::INVITE,
468 MembershipState::Join => Self::JOIN,
469 MembershipState::Knock => Self::KNOCK,
470 MembershipState::Leave => Self::LEAVE,
471 _ => return false,
472 };
473
474 self.contains(membership)
475 }
476
477 pub fn as_vec(&self) -> Vec<MembershipState> {
479 let mut memberships = Vec::new();
480
481 if self.contains(Self::JOIN) {
482 memberships.push(MembershipState::Join);
483 }
484 if self.contains(Self::INVITE) {
485 memberships.push(MembershipState::Invite);
486 }
487 if self.contains(Self::KNOCK) {
488 memberships.push(MembershipState::Knock);
489 }
490 if self.contains(Self::LEAVE) {
491 memberships.push(MembershipState::Leave);
492 }
493 if self.contains(Self::BAN) {
494 memberships.push(MembershipState::Ban);
495 }
496
497 memberships
498 }
499}
500
501pub fn normalize_power_level(power_level: Int, max_power_level: i64) -> Int {
503 let mut power_level = i64::from(power_level);
504 power_level = (power_level * 100) / max_power_level;
505
506 Int::try_from(power_level.clamp(0, 100))
507 .expect("We clamped the normalized power level so they must fit into the Int")
508}
509
510#[cfg(test)]
511mod tests {
512 use proptest::prelude::*;
513
514 use super::*;
515
516 prop_compose! {
517 fn arb_int()(id in any::<i64>()) -> Int {
518 id.try_into().unwrap_or_default()
519 }
520 }
521
522 proptest! {
523 #![proptest_config(ProptestConfig::with_cases(10_000))]
524 #[test]
525 fn test_power_level_normalization_with_min_max_level(power_level in arb_int()) {
526 let normalized = normalize_power_level(power_level, 1);
527 let normalized = i64::from(normalized);
528
529 assert!(normalized >= 0);
530 assert!(normalized <= 100);
531 }
532 }
533
534 proptest! {
535 #![proptest_config(ProptestConfig::with_cases(10_000))]
536 #[test]
537 fn test_power_level_normalization(power_level in arb_int(), max_level in 1i64..) {
538 let normalized = normalize_power_level(power_level, max_level);
539 let normalized = i64::from(normalized);
540
541 assert!(normalized >= 0);
542 assert!(normalized <= 100);
543 }
544 }
545
546 #[test]
547 fn test_power_level_normalization_limits() {
548 let level = Int::MIN;
549 let normalized = normalize_power_level(level, 1);
550 let normalized = i64::from(normalized);
551 assert!(normalized >= 0);
552 assert!(normalized <= 100);
553
554 let level = Int::MAX;
555 let normalized = normalize_power_level(level, 1);
556 let normalized = i64::from(normalized);
557 assert!(normalized >= 0);
558 assert!(normalized <= 100);
559 }
560}