tuwunel_api_client/
notice.rs1use futures::{FutureExt, TryFutureExt, future::ready};
2use ruma::{
3 RoomId, UserId,
4 events::{StateEventType, tag::TagName},
5};
6use tuwunel_core::{Result, utils::BoolExt};
7use tuwunel_matrix::Event;
8use tuwunel_service::Services;
9
10#[tracing::instrument(level = "trace", skip_all)]
15pub(crate) async fn is_notice_room(
16 services: &Services,
17 target: &UserId,
18 room_id: &RoomId,
19) -> Result<bool> {
20 let server_user = services.globals.server_user.as_ref();
21 let tag = notice_tag(&services.config.admin.room.tag);
22
23 if services
24 .admin
25 .get_admin_room()
26 .map(|admin| admin.is_ok_and(|admin| admin == room_id))
27 .await
28 {
29 return Ok(false);
30 }
31
32 room_is_notice(services, server_user, target, &tag, room_id).await
33}
34
35#[tracing::instrument(level = "trace", skip_all)]
39pub async fn room_is_notice(
40 services: &Services,
41 server_user: &UserId,
42 target: &UserId,
43 tag: &TagName,
44 room_id: &RoomId,
45) -> Result<bool> {
46 if !services
47 .state_cache
48 .is_joined(server_user, room_id)
49 .await
50 {
51 return Ok(false);
52 }
53
54 let tagged = services
55 .account_data
56 .get_room_tags(target, room_id)
57 .map_ok(|tags| tags.contains_key(tag))
58 .or_else(|error| ready(error.is_not_found().then_ok_or(false, error)))
59 .await?;
60
61 if !tagged {
62 return Ok(false);
63 }
64
65 services
66 .state_accessor
67 .room_state_get(room_id, &StateEventType::RoomCreate, "")
68 .map_ok(|create| is_notice_creator(create.sender(), server_user))
69 .await
70}
71
72pub fn notice_tag(tag: &str) -> TagName {
76 Some(tag)
77 .filter(|tag| !tag.is_empty())
78 .map_or(TagName::ServerNotice, Into::into)
79}
80
81fn is_notice_creator(create_sender: &UserId, server_user: &UserId) -> bool {
85 create_sender == server_user
86}
87
88#[cfg(test)]
89mod tests {
90 use ruma::{events::tag::TagName, user_id};
91
92 use super::{is_notice_creator, notice_tag};
93
94 #[test]
95 fn only_the_server_user_creates_a_notice_room() {
96 let server = user_id!("@server:example.com");
97 let other = user_id!("@other:example.com");
98
99 assert!(is_notice_creator(server, server));
100 assert!(!is_notice_creator(other, server));
101 }
102
103 #[test]
104 fn empty_config_tag_falls_back_to_server_notice() {
105 assert_eq!(notice_tag(""), TagName::ServerNotice);
106 assert_eq!(notice_tag("m.server_notice"), TagName::ServerNotice);
107 assert_eq!(notice_tag("u.custom"), TagName::from("u.custom"));
108 }
109}