Skip to main content

tuwunel_service/rooms/metadata/
mod.rs

1use 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	// Look for PDUs in that room.
49	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}