1use std::{
16 collections::{BTreeMap, BTreeSet, HashMap},
17 mem,
18 sync::Arc,
19};
20
21use bitflags::bitflags;
22use ruma::{
23 events::{
24 ignored_user_list::IgnoredUserListEventContent,
25 presence::PresenceEvent,
26 room::{
27 member::{MembershipState, RoomMemberEventContent},
28 power_levels::{PowerLevelAction, RoomPowerLevels, RoomPowerLevelsEventContent},
29 },
30 MessageLikeEventType, StateEventType,
31 },
32 MxcUri, OwnedUserId, UserId,
33};
34use tracing::debug;
35
36use super::Room;
37use crate::{
38 deserialized_responses::{DisplayName, MemberEvent, SyncOrStrippedState},
39 store::{ambiguity_map::is_display_name_ambiguous, Result as StoreResult, StateStoreExt},
40 MinimalRoomMemberEvent,
41};
42
43impl Room {
44 pub fn are_members_synced(&self) -> bool {
51 self.inner.read().members_synced
52 }
53
54 #[cfg(feature = "testing")]
59 pub fn mark_members_synced(&self) {
60 self.inner.update(|info| {
61 info.members_synced = true;
62 });
63 }
64
65 pub fn mark_members_missing(&self) {
67 self.inner.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.inner.read().active_members_count()
123 }
124
125 pub fn invited_members_count(&self) -> u64 {
127 self.inner.read().invited_members_count()
128 }
129
130 pub fn joined_members_count(&self) -> u64 {
132 self.inner.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 room_creator = self.inner.read().creator().map(ToOwned::to_owned);
170
171 let power_levels = self
172 .store
173 .get_state_event_static(self.room_id())
174 .await?
175 .and_then(|e| e.deserialize().ok());
176
177 let users_display_names =
178 self.store.get_users_with_display_names(self.room_id(), display_names).await?;
179
180 let ignored_users = self
181 .store
182 .get_account_data_event_static::<IgnoredUserListEventContent>()
183 .await?
184 .map(|c| c.deserialize())
185 .transpose()?
186 .map(|e| e.content.ignored_users.into_keys().collect());
187
188 Ok(MemberRoomInfo {
189 power_levels: power_levels.into(),
190 max_power_level,
191 room_creator,
192 users_display_names,
193 ignored_users,
194 })
195 }
196}
197
198#[derive(Clone, Debug)]
200pub struct RoomMember {
201 pub(crate) event: Arc<MemberEvent>,
202 pub(crate) profile: Arc<Option<MinimalRoomMemberEvent>>,
206 #[allow(dead_code)]
207 pub(crate) presence: Arc<Option<PresenceEvent>>,
208 pub(crate) power_levels: Arc<Option<SyncOrStrippedState<RoomPowerLevelsEventContent>>>,
209 pub(crate) max_power_level: i64,
210 pub(crate) is_room_creator: bool,
211 pub(crate) display_name_ambiguous: bool,
212 pub(crate) is_ignored: bool,
213}
214
215impl RoomMember {
216 pub(crate) fn from_parts(
217 event: MemberEvent,
218 profile: Option<MinimalRoomMemberEvent>,
219 presence: Option<PresenceEvent>,
220 room_info: &MemberRoomInfo<'_>,
221 ) -> Self {
222 let MemberRoomInfo {
223 power_levels,
224 max_power_level,
225 room_creator,
226 users_display_names,
227 ignored_users,
228 } = room_info;
229
230 let is_room_creator = room_creator.as_deref() == Some(event.user_id());
231 let display_name = event.display_name();
232 let display_name_ambiguous = users_display_names
233 .get(&display_name)
234 .is_some_and(|s| is_display_name_ambiguous(&display_name, s));
235 let is_ignored = ignored_users.as_ref().is_some_and(|s| s.contains(event.user_id()));
236
237 Self {
238 event: event.into(),
239 profile: profile.into(),
240 presence: presence.into(),
241 power_levels: power_levels.clone(),
242 max_power_level: *max_power_level,
243 is_room_creator,
244 display_name_ambiguous,
245 is_ignored,
246 }
247 }
248
249 pub fn user_id(&self) -> &UserId {
251 self.event.user_id()
252 }
253
254 pub fn event(&self) -> &Arc<MemberEvent> {
256 &self.event
257 }
258
259 pub fn display_name(&self) -> Option<&str> {
261 if let Some(p) = self.profile.as_ref() {
262 p.as_original().and_then(|e| e.content.displayname.as_deref())
263 } else {
264 self.event.original_content()?.displayname.as_deref()
265 }
266 }
267
268 pub fn name(&self) -> &str {
273 if let Some(d) = self.display_name() {
274 d
275 } else {
276 self.user_id().localpart()
277 }
278 }
279
280 pub fn avatar_url(&self) -> Option<&MxcUri> {
282 if let Some(p) = self.profile.as_ref() {
283 p.as_original().and_then(|e| e.content.avatar_url.as_deref())
284 } else {
285 self.event.original_content()?.avatar_url.as_deref()
286 }
287 }
288
289 pub fn normalized_power_level(&self) -> i64 {
295 if self.max_power_level > 0 {
296 (self.power_level() * 100) / self.max_power_level
297 } else {
298 self.power_level()
299 }
300 }
301
302 pub fn power_level(&self) -> i64 {
304 (*self.power_levels)
305 .as_ref()
306 .map(|e| e.power_levels().for_user(self.user_id()).into())
307 .unwrap_or_else(|| if self.is_room_creator { 100 } else { 0 })
308 }
309
310 pub fn can_ban(&self) -> bool {
314 self.can_do_impl(|pls| pls.user_can_ban(self.user_id()))
315 }
316
317 pub fn can_invite(&self) -> bool {
321 self.can_do_impl(|pls| pls.user_can_invite(self.user_id()))
322 }
323
324 pub fn can_kick(&self) -> bool {
328 self.can_do_impl(|pls| pls.user_can_kick(self.user_id()))
329 }
330
331 pub fn can_redact_own(&self) -> bool {
335 self.can_do_impl(|pls| pls.user_can_redact_own_event(self.user_id()))
336 }
337
338 pub fn can_redact_other(&self) -> bool {
343 self.can_do_impl(|pls| pls.user_can_redact_event_of_other(self.user_id()))
344 }
345
346 pub fn can_send_message(&self, msg_type: MessageLikeEventType) -> bool {
350 self.can_do_impl(|pls| pls.user_can_send_message(self.user_id(), msg_type))
351 }
352
353 pub fn can_send_state(&self, state_type: StateEventType) -> bool {
357 self.can_do_impl(|pls| pls.user_can_send_state(self.user_id(), state_type))
358 }
359
360 pub fn can_pin_or_unpin_event(&self) -> bool {
362 self.can_send_state(StateEventType::RoomPinnedEvents)
363 }
364
365 pub fn can_trigger_room_notification(&self) -> bool {
371 self.can_do_impl(|pls| pls.user_can_trigger_room_notification(self.user_id()))
372 }
373
374 pub fn can_do(&self, action: PowerLevelAction) -> bool {
377 self.can_do_impl(|pls| pls.user_can_do(self.user_id(), action))
378 }
379
380 fn can_do_impl(&self, f: impl FnOnce(RoomPowerLevels) -> bool) -> bool {
381 match &*self.power_levels {
382 Some(event) => f(event.power_levels()),
383 None => self.is_room_creator,
384 }
385 }
386
387 pub fn name_ambiguous(&self) -> bool {
392 self.display_name_ambiguous
393 }
394
395 pub fn membership(&self) -> &MembershipState {
397 self.event.membership()
398 }
399
400 pub fn is_ignored(&self) -> bool {
402 self.is_ignored
403 }
404}
405
406pub(crate) struct MemberRoomInfo<'a> {
408 pub(crate) power_levels: Arc<Option<SyncOrStrippedState<RoomPowerLevelsEventContent>>>,
409 pub(crate) max_power_level: i64,
410 pub(crate) room_creator: Option<OwnedUserId>,
411 pub(crate) users_display_names: HashMap<&'a DisplayName, BTreeSet<OwnedUserId>>,
412 pub(crate) ignored_users: Option<BTreeSet<OwnedUserId>>,
413}
414
415#[derive(Debug, Clone)]
417pub enum RoomMembersUpdate {
418 FullReload,
420 Partial(BTreeSet<OwnedUserId>),
422}
423
424bitflags! {
425 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
430 pub struct RoomMemberships: u16 {
431 const JOIN = 0b00000001;
433 const INVITE = 0b00000010;
435 const KNOCK = 0b00000100;
437 const LEAVE = 0b00001000;
439 const BAN = 0b00010000;
441
442 const ACTIVE = Self::JOIN.bits() | Self::INVITE.bits();
444 }
445}
446
447impl RoomMemberships {
448 pub fn matches(&self, membership: &MembershipState) -> bool {
450 if self.is_empty() {
451 return true;
452 }
453
454 let membership = match membership {
455 MembershipState::Ban => Self::BAN,
456 MembershipState::Invite => Self::INVITE,
457 MembershipState::Join => Self::JOIN,
458 MembershipState::Knock => Self::KNOCK,
459 MembershipState::Leave => Self::LEAVE,
460 _ => return false,
461 };
462
463 self.contains(membership)
464 }
465
466 pub fn as_vec(&self) -> Vec<MembershipState> {
468 let mut memberships = Vec::new();
469
470 if self.contains(Self::JOIN) {
471 memberships.push(MembershipState::Join);
472 }
473 if self.contains(Self::INVITE) {
474 memberships.push(MembershipState::Invite);
475 }
476 if self.contains(Self::KNOCK) {
477 memberships.push(MembershipState::Knock);
478 }
479 if self.contains(Self::LEAVE) {
480 memberships.push(MembershipState::Leave);
481 }
482 if self.contains(Self::BAN) {
483 memberships.push(MembershipState::Ban);
484 }
485
486 memberships
487 }
488}