1use std::collections::BTreeMap;
2
3use futures::FutureExt;
4use ruma::{
5 RoomId, RoomVersionId,
6 events::room::{
7 canonical_alias::RoomCanonicalAliasEventContent,
8 create::RoomCreateEventContent,
9 guest_access::{GuestAccess, RoomGuestAccessEventContent},
10 history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent},
11 join_rules::{JoinRule, RoomJoinRulesEventContent},
12 member::{MembershipState, RoomMemberEventContent},
13 name::RoomNameEventContent,
14 power_levels::RoomPowerLevelsEventContent,
15 preview_url::RoomPreviewUrlsEventContent,
16 topic::{RoomTopicEventContent, TopicContentBlock},
17 },
18};
19use tuwunel_core::{Result, matrix::room_version, pdu::PduBuilder};
20
21use crate::Services;
22
23pub async fn create_server_user(services: &Services) -> Result {
28 let server_user = services.globals.server_user.as_ref();
29
30 services
32 .users
33 .create(server_user, None, None)
34 .await?;
35
36 Ok(())
37}
38
39pub async fn create_admin_room(services: &Services) -> Result {
44 let room_id = RoomId::new_v1(services.globals.server_name());
45 let room_version_id = RoomVersionId::V11;
46
47 let room_version_rules = room_version::rules(&room_version_id)?;
48
49 let _short_id = services
50 .short
51 .get_or_create_shortroomid(&room_id)
52 .await;
53
54 let state_lock = services.state.mutex.lock(&room_id).await;
55
56 let server_user = services.globals.server_user.as_ref();
58 if !services.users.exists(server_user).await {
59 create_server_user(services).await?;
60 }
61
62 let create_content = if !room_version_rules
63 .authorization
64 .use_room_create_sender
65 {
66 RoomCreateEventContent::new_v1(server_user.into())
67 } else {
68 RoomCreateEventContent::new_v11()
69 };
70
71 services
73 .timeline
74 .build_and_append_pdu(
75 PduBuilder::state(String::new(), &RoomCreateEventContent {
76 federate: services.config.federate_admin_room,
77 predecessor: None,
78 room_version: room_version_id.clone(),
79 ..create_content
80 }),
81 server_user,
82 &room_id,
83 &state_lock,
84 )
85 .boxed()
86 .await?;
87
88 services
90 .timeline
91 .build_and_append_pdu(
92 PduBuilder::state(
93 String::from(server_user),
94 &RoomMemberEventContent::new(MembershipState::Join),
95 ),
96 server_user,
97 &room_id,
98 &state_lock,
99 )
100 .boxed()
101 .await?;
102
103 let users = BTreeMap::from_iter([(server_user.into(), 69420.into())]);
105
106 services
107 .timeline
108 .build_and_append_pdu(
109 PduBuilder::state(String::new(), &RoomPowerLevelsEventContent {
110 users,
111 ..Default::default()
112 }),
113 server_user,
114 &room_id,
115 &state_lock,
116 )
117 .boxed()
118 .await?;
119
120 services
122 .timeline
123 .build_and_append_pdu(
124 PduBuilder::state(String::new(), &RoomJoinRulesEventContent::new(JoinRule::Invite)),
125 server_user,
126 &room_id,
127 &state_lock,
128 )
129 .boxed()
130 .await?;
131
132 services
134 .timeline
135 .build_and_append_pdu(
136 PduBuilder::state(
137 String::new(),
138 &RoomHistoryVisibilityEventContent::new(HistoryVisibility::Shared),
139 ),
140 server_user,
141 &room_id,
142 &state_lock,
143 )
144 .boxed()
145 .await?;
146
147 services
149 .timeline
150 .build_and_append_pdu(
151 PduBuilder::state(
152 String::new(),
153 &RoomGuestAccessEventContent::new(GuestAccess::Forbidden),
154 ),
155 server_user,
156 &room_id,
157 &state_lock,
158 )
159 .boxed()
160 .await?;
161
162 let room_name = format!("{} Admin Room", services.config.server_name);
164 services
165 .timeline
166 .build_and_append_pdu(
167 PduBuilder::state(String::new(), &RoomNameEventContent::new(room_name)),
168 server_user,
169 &room_id,
170 &state_lock,
171 )
172 .boxed()
173 .await?;
174
175 services
176 .timeline
177 .build_and_append_pdu(
178 PduBuilder::state(String::new(), &RoomTopicEventContent {
179 topic_block: TopicContentBlock::default(),
180 topic: format!("Manage {} | Run commands prefixed with `!admin` | Run `!admin -h` for help | Documentation: https://matrix-construct.github.io/tuwunel", services.config.server_name),
181 }),
182 server_user,
183 &room_id,
184 &state_lock,
185 )
186 .boxed()
187 .await?;
188
189 let alias = &services.admin.admin_alias;
191
192 services
193 .timeline
194 .build_and_append_pdu(
195 PduBuilder::state(String::new(), &RoomCanonicalAliasEventContent {
196 alias: Some(alias.clone()),
197 alt_aliases: Vec::new(),
198 }),
199 server_user,
200 &room_id,
201 &state_lock,
202 )
203 .boxed()
204 .await?;
205
206 services.alias.set_alias(alias, &room_id)?;
207
208 services
210 .timeline
211 .build_and_append_pdu(
212 PduBuilder::state(String::new(), &RoomPreviewUrlsEventContent { disabled: true }),
213 server_user,
214 &room_id,
215 &state_lock,
216 )
217 .boxed()
218 .await?;
219
220 Ok(())
221}