tuwunel_service/rooms/metadata/
mod.rs1use std::sync::Arc;
7
8use futures::{FutureExt, Stream, StreamExt, pin_mut};
9use ruma::{OwnedRoomId, OwnedUserId, RoomId, UserId, events::room::join_rules::JoinRule};
10use tuwunel_core::{
11 Result, implement,
12 utils::{
13 future::BoolExt,
14 stream::{TryIgnore, WidebandExt},
15 },
16};
17use tuwunel_database::Map;
18
19pub struct Service {
24 db: Data,
25 services: Arc<crate::services::OnceServices>,
26}
27
28struct Data {
29 disabledroomids: Arc<Map>,
30 bannedroomids: Arc<Map>,
31 roomid_shortroomid: Arc<Map>,
32 pduid_pdu: Arc<Map>,
33}
34
35impl crate::Service for Service {
36 fn build(args: &crate::Args<'_>) -> Result<Arc<Self>> {
37 Ok(Arc::new(Self {
38 db: Data {
39 disabledroomids: args.db["disabledroomids"].clone(),
40 bannedroomids: args.db["bannedroomids"].clone(),
41 roomid_shortroomid: args.db["roomid_shortroomid"].clone(),
42 pduid_pdu: args.db["pduid_pdu"].clone(),
43 },
44 services: args.services.clone(),
45 }))
46 }
47
48 fn name(&self) -> &str { crate::service::make_name(std::module_path!()) }
49}
50
51#[implement(Service)]
56pub async fn exists(&self, room_id: &RoomId) -> bool {
57 let Ok(prefix) = self.services.short.get_shortroomid(room_id).await else {
58 return false;
59 };
60
61 let keys = self
63 .db
64 .pduid_pdu
65 .keys_prefix_raw(&prefix)
66 .ignore_err();
67
68 pin_mut!(keys);
69 keys.next().await.is_some()
70}
71
72#[implement(Service)]
77pub fn public_ids_prefix<'a>(
78 &'a self,
79 prefix: &'a str,
80) -> impl Stream<Item = OwnedRoomId> + Send + 'a {
81 self.ids_prefix(prefix)
82 .map(ToOwned::to_owned)
83 .wide_filter_map(async |room_id| self.is_public(&room_id).await.then_some(room_id))
84}
85
86#[implement(Service)]
91pub fn ids_prefix<'a>(&'a self, prefix: &'a str) -> impl Stream<Item = &RoomId> + Send + 'a {
92 self.db
93 .roomid_shortroomid
94 .keys_raw_prefix(prefix)
95 .ignore_err()
96}
97
98#[implement(Service)]
103pub fn iter_ids(&self) -> impl Stream<Item = &RoomId> + Send + '_ {
104 self.db.roomid_shortroomid.keys().ignore_err()
105}
106
107#[implement(Service)]
112pub async fn is_public(&self, room_id: &RoomId) -> bool {
113 let listed_public = self.services.directory.is_public_room(room_id);
114
115 let join_rule_public = self
116 .services
117 .state_accessor
118 .get_join_rules(room_id)
119 .map(|rule| matches!(rule, JoinRule::Public));
120
121 pin_mut!(listed_public, join_rule_public);
122 listed_public.or(join_rule_public).await
123}
124
125#[implement(Service)]
130#[inline]
131pub fn disable_room(&self, room_id: &RoomId) { self.db.disabledroomids.insert(room_id, []); }
132
133#[implement(Service)]
137#[inline]
138pub fn enable_room(&self, room_id: &RoomId) { self.db.disabledroomids.remove(room_id); }
139
140#[implement(Service)]
145#[inline]
146pub fn ban_room(&self, room_id: &RoomId) { self.db.bannedroomids.insert(room_id, []); }
147
148#[implement(Service)]
153#[inline]
154pub fn unban_room(&self, room_id: &RoomId) { self.db.bannedroomids.remove(room_id); }
155
156#[implement(Service)]
161#[inline]
162pub fn block_room(&self, room_id: &RoomId, blocker: &UserId) {
163 self.db
164 .bannedroomids
165 .insert(room_id, blocker.as_bytes());
166}
167
168#[implement(Service)]
173pub async fn banned_room_blocker(&self, room_id: &RoomId) -> Option<OwnedUserId> {
174 self.db
175 .bannedroomids
176 .get(room_id)
177 .await
178 .ok()
179 .and_then(|blocker| {
180 str::from_utf8(&blocker)
181 .ok()
182 .and_then(|mxid| UserId::parse(mxid).ok())
183 })
184}
185
186#[implement(Service)]
191pub fn list_banned_rooms(&self) -> impl Stream<Item = &RoomId> + Send + '_ {
192 self.db.bannedroomids.keys().ignore_err()
193}
194
195#[implement(Service)]
199#[inline]
200pub async fn is_disabled(&self, room_id: &RoomId) -> bool {
201 self.db.disabledroomids.get(room_id).await.is_ok()
202}
203
204#[implement(Service)]
209#[inline]
210pub async fn is_banned(&self, room_id: &RoomId) -> bool {
211 self.db.bannedroomids.get(room_id).await.is_ok()
212}