tuwunel_service/rooms/metadata/
mod.rs1use std::sync::Arc;
2
3use futures::{FutureExt, Stream, StreamExt, pin_mut};
4use ruma::{OwnedRoomId, RoomId, events::room::join_rules::JoinRule};
5use tuwunel_core::{
6 Result, implement,
7 utils::{
8 future::BoolExt,
9 stream::{TryIgnore, WidebandExt},
10 },
11};
12use tuwunel_database::Map;
13
14pub struct Service {
15 db: Data,
16 services: Arc<crate::services::OnceServices>,
17}
18
19struct Data {
20 disabledroomids: Arc<Map>,
21 bannedroomids: Arc<Map>,
22 roomid_shortroomid: Arc<Map>,
23 pduid_pdu: Arc<Map>,
24}
25
26impl crate::Service for Service {
27 fn build(args: &crate::Args<'_>) -> Result<Arc<Self>> {
28 Ok(Arc::new(Self {
29 db: Data {
30 disabledroomids: args.db["disabledroomids"].clone(),
31 bannedroomids: args.db["bannedroomids"].clone(),
32 roomid_shortroomid: args.db["roomid_shortroomid"].clone(),
33 pduid_pdu: args.db["pduid_pdu"].clone(),
34 },
35 services: args.services.clone(),
36 }))
37 }
38
39 fn name(&self) -> &str { crate::service::make_name(std::module_path!()) }
40}
41
42#[implement(Service)]
43pub async fn exists(&self, room_id: &RoomId) -> bool {
44 let Ok(prefix) = self.services.short.get_shortroomid(room_id).await else {
45 return false;
46 };
47
48 let keys = self
50 .db
51 .pduid_pdu
52 .keys_prefix_raw(&prefix)
53 .ignore_err();
54
55 pin_mut!(keys);
56 keys.next().await.is_some()
57}
58
59#[implement(Service)]
60pub fn public_ids_prefix<'a>(
61 &'a self,
62 prefix: &'a str,
63) -> impl Stream<Item = OwnedRoomId> + Send + 'a {
64 self.ids_prefix(prefix)
65 .map(ToOwned::to_owned)
66 .wide_filter_map(async |room_id| self.is_public(&room_id).await.then_some(room_id))
67}
68
69#[implement(Service)]
70pub fn ids_prefix<'a>(&'a self, prefix: &'a str) -> impl Stream<Item = &RoomId> + Send + 'a {
71 self.db
72 .roomid_shortroomid
73 .keys_raw_prefix(prefix)
74 .ignore_err()
75}
76
77#[implement(Service)]
78pub fn iter_ids(&self) -> impl Stream<Item = &RoomId> + Send + '_ {
79 self.db.roomid_shortroomid.keys().ignore_err()
80}
81
82#[implement(Service)]
83pub async fn is_public(&self, room_id: &RoomId) -> bool {
84 let listed_public = self.services.directory.is_public_room(room_id);
85
86 let join_rule_public = self
87 .services
88 .state_accessor
89 .get_join_rules(room_id)
90 .map(|rule| matches!(rule, JoinRule::Public));
91
92 pin_mut!(listed_public, join_rule_public);
93 listed_public.or(join_rule_public).await
94}
95
96#[implement(Service)]
97#[inline]
98pub fn disable_room(&self, room_id: &RoomId) { self.db.disabledroomids.insert(room_id, []); }
99
100#[implement(Service)]
101#[inline]
102pub fn enable_room(&self, room_id: &RoomId) { self.db.disabledroomids.remove(room_id); }
103
104#[implement(Service)]
105#[inline]
106pub fn ban_room(&self, room_id: &RoomId) { self.db.bannedroomids.insert(room_id, []); }
107
108#[implement(Service)]
109#[inline]
110pub fn unban_room(&self, room_id: &RoomId) { self.db.bannedroomids.remove(room_id); }
111
112#[implement(Service)]
113pub fn list_banned_rooms(&self) -> impl Stream<Item = &RoomId> + Send + '_ {
114 self.db.bannedroomids.keys().ignore_err()
115}
116
117#[implement(Service)]
118#[inline]
119pub async fn is_disabled(&self, room_id: &RoomId) -> bool {
120 self.db.disabledroomids.get(room_id).await.is_ok()
121}
122
123#[implement(Service)]
124#[inline]
125pub async fn is_banned(&self, room_id: &RoomId) -> bool {
126 self.db.bannedroomids.get(room_id).await.is_ok()
127}