1mod update;
9mod via;
10
11use std::{
12 collections::HashMap,
13 convert::identity,
14 sync::{Arc, RwLock},
15};
16
17use futures::{
18 Stream, StreamExt, TryStreamExt,
19 future::join5,
20 pin_mut,
21 stream::{empty, select},
22};
23use ruma::{
24 OwnedRoomId, OwnedServerName, RoomId, ServerName, UserId,
25 events::{AnyStrippedStateEvent, AnySyncStateEvent, room::member::MembershipState},
26 serde::Raw,
27};
28use serde::de::DeserializeOwned;
29use tuwunel_core::{
30 Result, debug_warn, implement,
31 matrix::{Event, Pdu, event::Owned},
32 trace,
33 utils::{
34 self, BoolExt,
35 result::NotFound,
36 stream::{BroadbandExt, IterStream, ReadyExt, TryIgnore},
37 },
38 warn,
39};
40use tuwunel_database::{Deserialized, Ignore, Interfix, Map};
41use update::EMPTY_INVITE_STATE;
42pub use update::{MembershipUpdate, StrippedRoomState};
48
49use crate::appservice::RegistrationInfo;
50
51pub struct Service {
57 appservice_in_room_cache: AppServiceInRoomCache,
58 services: Arc<crate::services::OnceServices>,
59 db: Data,
60}
61
62struct Data {
63 roomid_knockedcount: Arc<Map>,
64 roomid_invitedcount: Arc<Map>,
65 roomid_inviteviaservers: Arc<Map>,
66 roomid_joinedcount: Arc<Map>,
67 roomserverids: Arc<Map>,
68 roomuserid_invitecount: Arc<Map>,
69 roomuserid_joinedcount: Arc<Map>,
70 roomuserid_leftcount: Arc<Map>,
71 roomuserid_knockedcount: Arc<Map>,
72 roomuseroncejoinedids: Arc<Map>,
73 serverroomids: Arc<Map>,
74 userroomid_invitestate: Arc<Map>,
75 userroomid_joinedcount: Arc<Map>,
76 userroomid_leftstate: Arc<Map>,
77 userroomid_knockedstate: Arc<Map>,
78}
79
80type AppServiceInRoomCache = RwLock<HashMap<OwnedRoomId, HashMap<String, bool>>>;
81type StrippedStateEventItem = (OwnedRoomId, Vec<Raw<AnyStrippedStateEvent>>);
82type SyncStateEventItem = (OwnedRoomId, Vec<Raw<AnySyncStateEvent>>);
83
84impl crate::Service for Service {
85 fn build(args: &crate::Args<'_>) -> Result<Arc<Self>> {
86 Ok(Arc::new(Self {
87 appservice_in_room_cache: RwLock::new(HashMap::new()),
88 services: args.services.clone(),
89 db: Data {
90 roomid_knockedcount: args.db["roomid_knockedcount"].clone(),
91 roomid_invitedcount: args.db["roomid_invitedcount"].clone(),
92 roomid_inviteviaservers: args.db["roomid_inviteviaservers"].clone(),
93 roomid_joinedcount: args.db["roomid_joinedcount"].clone(),
94 roomserverids: args.db["roomserverids"].clone(),
95 roomuserid_invitecount: args.db["roomuserid_invitecount"].clone(),
96 roomuserid_joinedcount: args.db["roomuserid_joined"].clone(),
97 roomuserid_leftcount: args.db["roomuserid_leftcount"].clone(),
98 roomuserid_knockedcount: args.db["roomuserid_knockedcount"].clone(),
99 roomuseroncejoinedids: args.db["roomuseroncejoinedids"].clone(),
100 serverroomids: args.db["serverroomids"].clone(),
101 userroomid_invitestate: args.db["userroomid_invitestate"].clone(),
102 userroomid_joinedcount: args.db["userroomid_joined"].clone(),
103 userroomid_leftstate: args.db["userroomid_leftstate"].clone(),
104 userroomid_knockedstate: args.db["userroomid_knockedstate"].clone(),
105 },
106 }))
107 }
108
109 fn name(&self) -> &str { crate::service::make_name(std::module_path!()) }
110}
111
112#[implement(Service)]
120#[tracing::instrument(level = "trace", skip_all)]
121pub async fn appservice_in_room(&self, room_id: &RoomId, appservice: &RegistrationInfo) -> bool {
122 let cached = self
123 .appservice_in_room_cache
124 .read()
125 .expect("locked")
126 .get(room_id)
127 .and_then(|map| map.get(&appservice.registration.id))
128 .copied();
129
130 if let Some(cached) = cached {
131 return cached;
132 }
133
134 let in_room = self.is_joined(&appservice.sender, room_id).await
135 || self
136 .room_members(room_id)
137 .ready_any(|user_id| appservice.is_user_match(user_id))
138 .await;
139
140 self.appservice_in_room_cache
141 .write()
142 .expect("locked")
143 .entry(room_id.into())
144 .or_default()
145 .insert(appservice.registration.id.clone(), in_room);
146
147 in_room
148}
149
150#[implement(Service)]
156pub fn get_appservice_in_room_cache_usage(&self) -> (usize, usize) {
157 let cache = self
158 .appservice_in_room_cache
159 .read()
160 .expect("locked");
161
162 (cache.len(), cache.capacity())
163}
164
165#[implement(Service)]
171#[tracing::instrument(level = "debug", skip_all)]
172pub fn clear_appservice_in_room_cache(&self) {
173 self.appservice_in_room_cache
174 .write()
175 .expect("locked")
176 .clear();
177}
178
179#[implement(Service)]
186#[tracing::instrument(skip(self), level = "trace")]
187pub fn remote_room_servers<'a>(
188 &'a self,
189 room_id: &'a RoomId,
190) -> impl Stream<Item = &ServerName> + Send + 'a {
191 self.room_servers(room_id)
192 .ready_filter(|server| !self.services.globals.server_is_ours(server))
193}
194
195#[implement(Service)]
200#[tracing::instrument(skip(self), level = "debug")]
201pub fn room_servers<'a>(
202 &'a self,
203 room_id: &'a RoomId,
204) -> impl Stream<Item = &ServerName> + Send + 'a {
205 let prefix = (room_id, Interfix);
206 self.db
207 .roomserverids
208 .keys_prefix(&prefix)
209 .ignore_err()
210 .map(|(_, server): (Ignore, &ServerName)| server)
211}
212
213#[implement(Service)]
218#[tracing::instrument(skip(self), level = "trace")]
219pub async fn server_in_room<'a>(&'a self, server: &'a ServerName, room_id: &'a RoomId) -> bool {
220 let key = (server, room_id);
221 self.db.serverroomids.qry(&key).await.is_ok()
222}
223
224#[implement(Service)]
229#[tracing::instrument(skip(self), level = "debug")]
230pub fn server_rooms<'a>(
231 &'a self,
232 server: &'a ServerName,
233) -> impl Stream<Item = &RoomId> + Send + 'a {
234 let prefix = (server, Interfix);
235 self.db
236 .serverroomids
237 .keys_prefix(&prefix)
238 .ignore_err()
239 .map(|(_, room_id): (Ignore, &RoomId)| room_id)
240}
241
242#[implement(Service)]
248#[tracing::instrument(skip(self), level = "debug")]
249pub fn servers(&self) -> impl Stream<Item = &ServerName> + Send + '_ {
250 self.db
251 .serverroomids
252 .keys()
253 .ignore_err()
254 .ready_scan(
255 None,
256 |last: &mut Option<OwnedServerName>, (server, _): (&ServerName, Ignore)| {
257 let fresh = last.as_deref() != Some(server);
258
259 if fresh {
260 *last = Some(server.to_owned());
261 }
262
263 Some(fresh.then_some(server))
264 },
265 )
266 .ready_filter_map(identity)
267}
268
269#[implement(Service)]
274#[tracing::instrument(skip(self), level = "trace")]
275pub async fn server_shares_room(&self, server: &ServerName) -> bool {
276 self.server_rooms(server)
277 .ready_any(|_| true)
278 .await
279}
280
281#[implement(Service)]
286#[tracing::instrument(skip(self), level = "trace")]
287pub async fn server_sees_user(&self, server: &ServerName, user_id: &UserId) -> bool {
288 self.server_rooms(server)
289 .map(ToOwned::to_owned)
290 .broad_any(async |room_id| self.is_joined(user_id, &room_id).await)
291 .await
292}
293
294#[implement(Service)]
299#[tracing::instrument(skip(self), level = "trace")]
300pub async fn user_sees_user(&self, user_a: &UserId, user_b: &UserId) -> bool {
301 let get_shared_rooms = self.get_shared_rooms(user_a, user_b);
302
303 pin_mut!(get_shared_rooms);
304 get_shared_rooms.next().await.is_some()
305}
306
307#[implement(Service)]
314#[tracing::instrument(skip(self), level = "debug")]
315pub fn get_shared_rooms<'a>(
316 &'a self,
317 user_a: &'a UserId,
318 user_b: &'a UserId,
319) -> impl Stream<Item = &RoomId> + Send + 'a {
320 let a = self.rooms_joined(user_a);
321 let b = self.rooms_joined(user_b);
322
323 utils::set::intersection_sorted_stream2(a, b)
324}
325
326#[implement(Service)]
331#[tracing::instrument(skip(self), level = "debug")]
332pub fn room_members<'a>(
333 &'a self,
334 room_id: &'a RoomId,
335) -> impl Stream<Item = &UserId> + Send + 'a {
336 self.room_members_checked(room_id).ignore_err()
337}
338
339#[implement(Service)]
345#[tracing::instrument(skip(self), level = "debug")]
346pub fn room_members_checked<'a>(
347 &'a self,
348 room_id: &'a RoomId,
349) -> impl Stream<Item = Result<&'a UserId>> + Send + 'a {
350 let prefix = (room_id, Interfix);
351
352 self.db
353 .roomuserid_joinedcount
354 .keys_prefix(&prefix)
355 .map_ok(|(_, user_id): (Ignore, &UserId)| user_id)
356}
357
358#[implement(Service)]
364#[tracing::instrument(skip(self), level = "trace")]
365pub async fn room_joined_count(&self, room_id: &RoomId) -> Result<u64> {
366 self.db
367 .roomid_joinedcount
368 .get(room_id)
369 .await
370 .deserialized()
371}
372
373#[implement(Service)]
379#[tracing::instrument(skip(self), level = "trace")]
380pub async fn room_invited_count(&self, room_id: &RoomId) -> Result<u64> {
381 self.db
382 .roomid_invitedcount
383 .get(room_id)
384 .await
385 .deserialized()
386}
387
388#[implement(Service)]
394#[tracing::instrument(skip(self), level = "trace")]
395pub async fn room_knocked_count(&self, room_id: &RoomId) -> Result<u64> {
396 self.db
397 .roomid_knockedcount
398 .get(room_id)
399 .await
400 .deserialized()
401}
402
403#[implement(Service)]
409#[tracing::instrument(skip(self), level = "debug")]
410pub fn active_local_users_in_room<'a>(
411 &'a self,
412 room_id: &'a RoomId,
413) -> impl Stream<Item = &UserId> + Send + 'a {
414 self.local_users_in_room(room_id)
415 .filter(|user| self.services.users.is_active(user))
416}
417
418#[implement(Service)]
423#[tracing::instrument(skip(self), level = "debug")]
424pub fn local_users_in_room<'a>(
425 &'a self,
426 room_id: &'a RoomId,
427) -> impl Stream<Item = &UserId> + Send + 'a {
428 self.room_members(room_id)
429 .ready_filter(|user| self.services.globals.user_is_local(user))
430}
431
432#[implement(Service)]
437#[tracing::instrument(skip(self), level = "debug")]
438pub fn local_users_invited_to_room<'a>(
439 &'a self,
440 room_id: &'a RoomId,
441) -> impl Stream<Item = &UserId> + Send + 'a {
442 self.room_members_invited(room_id)
443 .ready_filter(|user| self.services.globals.user_is_local(user))
444}
445
446#[implement(Service)]
454#[tracing::instrument(skip(self), level = "debug")]
455pub fn room_useroncejoined<'a>(
456 &'a self,
457 room_id: &'a RoomId,
458) -> impl Stream<Item = &UserId> + Send + 'a {
459 let prefix = (room_id, Interfix);
460 self.db
461 .roomuseroncejoinedids
462 .keys_prefix(&prefix)
463 .ignore_err()
464 .map(|(_, user_id): (Ignore, &UserId)| user_id)
465}
466
467#[implement(Service)]
472#[tracing::instrument(skip(self), level = "debug")]
473pub fn room_members_invited<'a>(
474 &'a self,
475 room_id: &'a RoomId,
476) -> impl Stream<Item = &UserId> + Send + 'a {
477 let prefix = (room_id, Interfix);
478 self.db
479 .roomuserid_invitecount
480 .keys_prefix(&prefix)
481 .ignore_err()
482 .map(|(_, user_id): (Ignore, &UserId)| user_id)
483}
484
485#[implement(Service)]
490#[tracing::instrument(skip(self), level = "debug")]
491pub fn room_members_knocked<'a>(
492 &'a self,
493 room_id: &'a RoomId,
494) -> impl Stream<Item = &UserId> + Send + 'a {
495 let prefix = (room_id, Interfix);
496 self.db
497 .roomuserid_knockedcount
498 .keys_prefix(&prefix)
499 .ignore_err()
500 .map(|(_, user_id): (Ignore, &UserId)| user_id)
501}
502
503#[implement(Service)]
508#[tracing::instrument(skip(self), level = "trace")]
509pub async fn get_invite_count(&self, room_id: &RoomId, user_id: &UserId) -> Result<u64> {
510 let key = (room_id, user_id);
511 self.db
512 .roomuserid_invitecount
513 .qry(&key)
514 .await
515 .deserialized()
516}
517
518#[implement(Service)]
523#[tracing::instrument(skip(self), level = "trace")]
524pub async fn get_knock_count(&self, room_id: &RoomId, user_id: &UserId) -> Result<u64> {
525 let key = (room_id, user_id);
526 self.db
527 .roomuserid_knockedcount
528 .qry(&key)
529 .await
530 .deserialized()
531}
532
533#[implement(Service)]
538#[tracing::instrument(skip(self), level = "trace")]
539pub async fn get_left_count(&self, room_id: &RoomId, user_id: &UserId) -> Result<u64> {
540 let key = (room_id, user_id);
541 self.db
542 .roomuserid_leftcount
543 .qry(&key)
544 .await
545 .deserialized()
546}
547
548#[implement(Service)]
554#[tracing::instrument(skip(self), level = "trace")]
555pub async fn get_joined_count(&self, room_id: &RoomId, user_id: &UserId) -> Result<u64> {
556 let key = (room_id, user_id);
557
558 self.db
559 .roomuserid_joinedcount
560 .qry(&key)
561 .await
562 .and_then(|value| {
563 value
564 .is_empty()
565 .map_or_else(|| value.deserialized(), || Ok(0))
566 })
567}
568
569#[implement(Service)]
576#[inline]
577pub fn all_user_memberships<'a>(
578 &'a self,
579 user_id: &'a UserId,
580) -> impl Stream<Item = (MembershipState, &RoomId)> + Send + 'a {
581 self.user_memberships(user_id, None)
582}
583
584#[implement(Service)]
592#[tracing::instrument(skip(self), level = "debug")]
593pub fn user_memberships<'a>(
594 &'a self,
595 user_id: &'a UserId,
596 mask: Option<&[MembershipState]>,
597) -> impl Stream<Item = (MembershipState, &RoomId)> + Send + 'a {
598 let joined = mask
599 .is_none_or(|mask| mask.contains(&MembershipState::Join))
600 .then(|| {
601 self.rooms_joined(user_id)
602 .map(|room_id| (MembershipState::Join, room_id))
603 .left_stream()
604 })
605 .unwrap_or_else(|| empty().right_stream());
606
607 let invited = mask
608 .is_none_or(|mask| mask.contains(&MembershipState::Invite))
609 .then(|| {
610 self.rooms_invited(user_id)
611 .map(|room_id| (MembershipState::Invite, room_id))
612 .left_stream()
613 })
614 .unwrap_or_else(|| empty().right_stream());
615
616 let knocked = mask
617 .is_none_or(|mask| mask.contains(&MembershipState::Knock))
618 .then(|| {
619 self.rooms_knocked(user_id)
620 .map(|room_id| (MembershipState::Knock, room_id))
621 .left_stream()
622 })
623 .unwrap_or_else(|| empty().right_stream());
624
625 let left = mask
626 .is_none_or(|mask| mask.contains(&MembershipState::Leave))
627 .then(|| {
628 self.rooms_left(user_id)
629 .map(|room_id| (MembershipState::Leave, room_id))
630 .left_stream()
631 })
632 .unwrap_or_else(|| empty().right_stream());
633
634 select(select(joined, left), select(invited, knocked))
635}
636
637#[implement(Service)]
643#[tracing::instrument(skip(self), level = "debug")]
644pub fn rooms_joined<'a>(
645 &'a self,
646 user_id: &'a UserId,
647) -> impl Stream<Item = &RoomId> + Send + 'a {
648 self.db
649 .userroomid_joinedcount
650 .keys_raw_prefix(user_id)
651 .ignore_err()
652 .map(|(_, room_id): (Ignore, &RoomId)| room_id)
653}
654
655#[implement(Service)]
661#[tracing::instrument(skip(self), level = "trace")]
662pub fn rooms_joined_checked<'a>(
663 &'a self,
664 user_id: &'a UserId,
665) -> impl Stream<Item = Result<&'a RoomId>> + Send + 'a {
666 let prefix = (user_id, Interfix);
667
668 self.db
669 .userroomid_joinedcount
670 .keys_prefix(&prefix)
671 .map_ok(|(_, room_id): (Ignore, &RoomId)| room_id)
672}
673
674#[implement(Service)]
680#[tracing::instrument(skip(self), level = "debug")]
681pub fn rooms_invited<'a>(
682 &'a self,
683 user_id: &'a UserId,
684) -> impl Stream<Item = &RoomId> + Send + 'a {
685 self.db
686 .userroomid_invitestate
687 .keys_raw_prefix(user_id)
688 .ignore_err()
689 .map(|(_, room_id): (Ignore, &RoomId)| room_id)
690}
691
692#[implement(Service)]
698#[tracing::instrument(skip(self), level = "debug")]
699pub fn rooms_knocked<'a>(
700 &'a self,
701 user_id: &'a UserId,
702) -> impl Stream<Item = &RoomId> + Send + 'a {
703 self.db
704 .userroomid_knockedstate
705 .keys_raw_prefix(user_id)
706 .ignore_err()
707 .map(|(_, room_id): (Ignore, &RoomId)| room_id)
708}
709
710#[implement(Service)]
716#[tracing::instrument(skip(self), level = "debug")]
717pub fn rooms_left<'a>(&'a self, user_id: &'a UserId) -> impl Stream<Item = &RoomId> + Send + 'a {
718 self.db
719 .userroomid_leftstate
720 .keys_raw_prefix(user_id)
721 .ignore_err()
722 .map(|(_, room_id): (Ignore, &RoomId)| room_id)
723}
724
725#[implement(Service)]
730#[tracing::instrument(skip(self), level = "debug")]
731pub fn rooms_invited_state<'a>(
732 &'a self,
733 user_id: &'a UserId,
734) -> impl Stream<Item = StrippedStateEventItem> + Send + 'a {
735 type KeyVal<'a> = (Key<'a>, Raw<Vec<AnyStrippedStateEvent>>);
736 type Key<'a> = (&'a UserId, &'a RoomId);
737
738 let prefix = (user_id, Interfix);
739 self.db
740 .userroomid_invitestate
741 .stream_prefix(&prefix)
742 .ignore_err()
743 .map(|((_, room_id), state): KeyVal<'_>| (room_id.to_owned(), state))
744 .map(|(room_id, state)| Ok((room_id, state.deserialize_as_unchecked()?)))
745 .ignore_err()
746}
747
748#[implement(Service)]
753#[tracing::instrument(skip(self), level = "trace")]
754pub fn rooms_knocked_state<'a>(
755 &'a self,
756 user_id: &'a UserId,
757) -> impl Stream<Item = StrippedStateEventItem> + Send + 'a {
758 type KeyVal<'a> = (Key<'a>, Raw<Vec<AnyStrippedStateEvent>>);
759 type Key<'a> = (&'a UserId, &'a RoomId);
760
761 let prefix = (user_id, Interfix);
762 self.db
763 .userroomid_knockedstate
764 .stream_prefix(&prefix)
765 .ignore_err()
766 .map(|((_, room_id), state): KeyVal<'_>| (room_id.to_owned(), state))
767 .map(|(room_id, state)| Ok((room_id, state.deserialize_as_unchecked()?)))
768 .ignore_err()
769}
770
771#[implement(Service)]
777#[tracing::instrument(skip(self), level = "debug")]
778pub fn rooms_left_state<'a>(
779 &'a self,
780 user_id: &'a UserId,
781) -> impl Stream<Item = SyncStateEventItem> + Send + 'a {
782 type KeyVal<'a> = (Key<'a>, Raw<Vec<Raw<AnySyncStateEvent>>>);
783 type Key<'a> = (&'a UserId, &'a RoomId);
784
785 let prefix = (user_id, Interfix);
786 self.db
787 .userroomid_leftstate
788 .stream_prefix(&prefix)
789 .ignore_err()
790 .map(|((_, room_id), state): KeyVal<'_>| (room_id.to_owned(), state))
791 .map(|(room_id, state)| {
792 let state = state_events(&room_id, &state);
793
794 (room_id, state)
795 })
796}
797
798#[implement(Service)]
803#[tracing::instrument(skip(self), level = "trace")]
804pub async fn invite_state(
805 &self,
806 user_id: &UserId,
807 room_id: &RoomId,
808) -> Result<Vec<Raw<AnyStrippedStateEvent>>> {
809 let key = (user_id, room_id);
810 self.db
811 .userroomid_invitestate
812 .qry(&key)
813 .await
814 .deserialized()
815 .and_then(|val: Raw<Vec<AnyStrippedStateEvent>>| {
816 val.deserialize_as_unchecked().map_err(Into::into)
817 })
818}
819
820#[implement(Service)]
826#[tracing::instrument(skip(self), level = "trace")]
827pub async fn has_invite_state(&self, user_id: &UserId, room_id: &RoomId) -> Result<bool> {
828 let key = (user_id, room_id);
829
830 self.db
831 .userroomid_invitestate
832 .qry(&key)
833 .await
834 .optional()
835 .map(|state| state.is_some_and(|state| state.len() > EMPTY_INVITE_STATE.len()))
836}
837
838#[implement(Service)]
843#[tracing::instrument(skip(self), level = "trace")]
844pub async fn knock_state(
845 &self,
846 user_id: &UserId,
847 room_id: &RoomId,
848) -> Result<Vec<Raw<AnyStrippedStateEvent>>> {
849 let key = (user_id, room_id);
850 self.db
851 .userroomid_knockedstate
852 .qry(&key)
853 .await
854 .deserialized()
855 .and_then(|val: Raw<Vec<AnyStrippedStateEvent>>| {
856 val.deserialize_as_unchecked().map_err(Into::into)
857 })
858}
859
860#[implement(Service)]
866#[tracing::instrument(skip(self), level = "trace")]
867pub async fn left_state(
868 &self,
869 user_id: &UserId,
870 room_id: &RoomId,
871) -> Result<Vec<Raw<AnyStrippedStateEvent>>> {
872 let key = (user_id, room_id);
873 self.db
874 .userroomid_leftstate
875 .qry(&key)
876 .await
877 .deserialized()
878 .map(|state: Raw<Vec<AnyStrippedStateEvent>>| state_events(room_id, &state))
879}
880
881#[implement(Service)]
888#[tracing::instrument(skip(self), level = "trace")]
889pub async fn user_membership(
890 &self,
891 user_id: &UserId,
892 room_id: &RoomId,
893) -> Option<MembershipState> {
894 let states = join5(
895 self.is_joined(user_id, room_id),
896 self.is_left(user_id, room_id),
897 self.is_knocked(user_id, room_id),
898 self.is_invited(user_id, room_id),
899 self.once_joined(user_id, room_id),
900 )
901 .await;
902
903 match states {
904 | (true, ..) => Some(MembershipState::Join),
905 | (_, true, ..) => Some(MembershipState::Leave),
906 | (_, _, true, ..) => Some(MembershipState::Knock),
907 | (_, _, _, true, ..) => Some(MembershipState::Invite),
908 | (false, false, false, false, true) => Some(MembershipState::Ban),
909 | _ => None,
910 }
911}
912
913#[implement(Service)]
918#[tracing::instrument(skip(self), level = "debug")]
919pub async fn once_joined(&self, user_id: &UserId, room_id: &RoomId) -> bool {
920 let key = (user_id, room_id);
921 self.db.roomuseroncejoinedids.contains(&key).await
922}
923
924#[implement(Service)]
930#[tracing::instrument(skip(self, room_ids), level = "trace")]
931pub async fn is_joined_any<'a, Rooms>(&self, user_id: &UserId, room_ids: Rooms) -> bool
932where
933 Rooms: IntoIterator<Item = &'a RoomId> + Send,
934 Rooms::IntoIter: Send,
935{
936 room_ids
937 .into_iter()
938 .stream()
939 .broad_any(|room_id| self.is_joined(user_id, room_id))
940 .await
941}
942
943#[implement(Service)]
948#[tracing::instrument(skip(self), level = "trace")]
949pub async fn is_joined<'a>(&'a self, user_id: &'a UserId, room_id: &'a RoomId) -> bool {
950 let key = (user_id, room_id);
951 self.db
952 .userroomid_joinedcount
953 .contains(&key)
954 .await
955}
956
957#[implement(Service)]
962#[tracing::instrument(skip(self), level = "trace")]
963pub async fn is_knocked<'a>(&'a self, user_id: &'a UserId, room_id: &'a RoomId) -> bool {
964 let key = (user_id, room_id);
965 self.db
966 .userroomid_knockedstate
967 .contains(&key)
968 .await
969}
970
971#[implement(Service)]
976#[tracing::instrument(skip(self), level = "trace")]
977pub async fn is_invited(&self, user_id: &UserId, room_id: &RoomId) -> bool {
978 let key = (user_id, room_id);
979 self.db
980 .userroomid_invitestate
981 .contains(&key)
982 .await
983}
984
985#[implement(Service)]
990#[tracing::instrument(skip(self), level = "trace")]
991pub async fn is_left(&self, user_id: &UserId, room_id: &RoomId) -> bool {
992 let key = (user_id, room_id);
993 self.db.userroomid_leftstate.contains(&key).await
994}
995
996#[implement(Service)]
1005#[tracing::instrument(skip(self), level = "trace")]
1006pub async fn delete_room_join_counts(&self, room_id: &RoomId, force: bool) -> Result {
1007 let prefix = (room_id, Interfix);
1008 let mut txn = self.services.db.txn();
1009
1010 txn.del_raw(&self.db.roomid_knockedcount, room_id);
1011
1012 txn.del_raw(&self.db.roomid_invitedcount, room_id);
1013
1014 txn.del_raw(&self.db.roomid_inviteviaservers, room_id);
1015
1016 txn.del_raw(&self.db.roomid_joinedcount, room_id);
1017
1018 self.db
1019 .roomserverids
1020 .keys_prefix(&prefix)
1021 .ignore_err()
1022 .ready_for_each(|key: (&RoomId, &ServerName)| {
1023 trace!("Removing key: {key:?}");
1024 txn.del(&self.db.roomserverids, key);
1025
1026 let reverse_key = (key.1, key.0);
1027
1028 trace!("Removing reverse key: {reverse_key:?}");
1029 txn.del(&self.db.serverroomids, reverse_key);
1030 })
1031 .await;
1032
1033 self.db
1034 .roomuserid_invitecount
1035 .keys_prefix(&prefix)
1036 .ignore_err()
1037 .ready_for_each(|key: (&RoomId, &UserId)| {
1038 trace!("Removing key: {key:?}");
1039 txn.del(&self.db.roomuserid_invitecount, key);
1040
1041 let reverse_key = (key.1, key.0);
1042
1043 trace!("Removing reverse key: {reverse_key:?}");
1044 txn.del(&self.db.userroomid_invitestate, reverse_key);
1045 })
1046 .await;
1047
1048 self.db
1049 .roomuserid_joinedcount
1050 .keys_prefix(&prefix)
1051 .ignore_err()
1052 .ready_for_each(|key: (&RoomId, &UserId)| {
1053 trace!("Removing key: {key:?}");
1054 txn.del(&self.db.roomuserid_joinedcount, key);
1055
1056 let reverse_key = (key.1, key.0);
1057
1058 trace!("Removing reverse key: {reverse_key:?}");
1059 txn.del(&self.db.userroomid_joinedcount, reverse_key);
1060 })
1061 .await;
1062
1063 self.db
1064 .roomuserid_knockedcount
1065 .keys_prefix(&prefix)
1066 .ignore_err()
1067 .ready_for_each(|key: (&RoomId, &UserId)| {
1068 trace!("Removing key: {key:?}");
1069 txn.del(&self.db.roomuserid_knockedcount, key);
1070
1071 let reverse_key = (key.1, key.0);
1072
1073 trace!("Removing reverse key: {reverse_key:?}");
1074 txn.del(&self.db.userroomid_knockedstate, reverse_key);
1075 })
1076 .await;
1077
1078 self.db
1079 .roomuserid_leftcount
1080 .keys_prefix(&prefix)
1081 .ignore_err()
1082 .ready_filter(|(_, user_id): &(&RoomId, &UserId)| {
1083 force || !self.services.globals.user_is_local(user_id)
1084 })
1085 .ready_for_each(|key: (&RoomId, &UserId)| {
1086 trace!("Removing key: {key:?}");
1087 txn.del(&self.db.roomuserid_leftcount, key);
1088
1089 let reverse_key = (key.1, key.0);
1090
1091 trace!("Removing reverse key: {reverse_key:?}");
1092 txn.del(&self.db.userroomid_leftstate, reverse_key);
1093 })
1094 .await;
1095
1096 txn.execute();
1097
1098 Ok(())
1099}
1100
1101fn state_events<T, U>(room_id: &RoomId, state: &Raw<T>) -> Vec<U>
1107where
1108 U: DeserializeOwned + From<Owned<Pdu>>,
1109{
1110 match state.json().get().trim_start().as_bytes().first() {
1111 | Some(b'[') => state
1112 .deserialize_as_unchecked()
1113 .inspect_err(
1114 |e| debug_warn!(%room_id, error = %e, "Unusable cached membership state"),
1115 )
1116 .unwrap_or_default(),
1117
1118 | Some(b'{') => state
1120 .deserialize_as_unchecked()
1121 .map(|event: Pdu| [event.into_format()].into())
1122 .inspect_err(|e| debug_warn!(%room_id, error = %e, "Unusable cached leave event"))
1123 .unwrap_or_default(),
1124
1125 | _ => Vec::new(),
1126 }
1127}