1use axum::{Json, extract::State, http::StatusCode};
2
3use ruma::events::room::{
4 history_visibility::{HistoryVisibility, RoomHistoryVisibilityEvent},
5 member::{MembershipState, RoomMemberEvent},
6};
7use ruma::events::space::child::SpaceChildEvent;
8use std::time::Duration;
9
10use ruma::events::macros::EventContent;
11
12use serde::{Deserialize, Serialize};
13use serde_json::{Value, json};
14use std::sync::Arc;
15
16use crate::AppState;
17
18use crate::cache::CacheKey;
19
20#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
21#[ruma_event(type = "commune.public.room", kind = State, state_key_type = String)]
22pub struct CommunePublicRoomEventContent {
23 pub public: bool,
24}
25
26pub async fn transactions(
27 State(state): State<Arc<AppState>>,
28 Json(payload): Json<Value>,
29) -> Result<Json<Value>, (StatusCode, String)> {
30 let events = match payload.get("events") {
31 Some(Value::Array(events)) => events,
32 Some(_) | None => {
33 tracing::info!("Events is not an array");
34 return Ok(Json(json!({})));
35 }
36 };
37
38 for event in events {
39 if cfg!(debug_assertions) {
40 tracing::info!("Event: {:#?}", event);
41 }
42
43 if state.config.appservice.rules.auto_join {
45 if let Ok(event) = serde_json::from_value::<RoomHistoryVisibilityEvent>(event.clone()) {
46 if event.history_visibility() == &HistoryVisibility::WorldReadable {
47 tracing::info!("History Visibility: World Readable");
48
49 tokio::spawn(async move {
50 tokio::time::sleep(Duration::from_secs(5)).await;
53
54 let room_id = event.room_id().to_owned();
55 tracing::info!("Joining room: {}", room_id);
56 if let Err(e) = state.appservice.join_room(room_id.clone()).await {
57 tracing::warn!("Failed to join room: {}. Error: {}", room_id, e);
58 } else {
59 tracing::info!("Successfully joined room: {}", room_id);
60 }
61 });
62
63 return Ok(Json(json!({})));
64 }
65 }
66
67 if let Ok(event) = serde_json::from_value::<SpaceChildEvent>(event.clone()) {
68 tracing::info!("Auto joining space child room");
69
70 tokio::spawn(async move {
71 let room_id = event.room_id().to_owned();
72 tracing::info!("Joining room: {}", room_id);
73 if let Err(e) = state.appservice.join_room(room_id.clone()).await {
74 tracing::warn!("Failed to join room: {}. Error: {}", room_id, e);
75 } else {
76 tracing::info!("Successfully joined room: {}", room_id);
77 }
78 });
79
80 return Ok(Json(json!({})));
81 }
82 };
83
84 let public = event["content"]["public"].as_bool();
85 if let Ok(event) = serde_json::from_value::<CommunePublicRoomEvent>(event.clone()) {
86 tracing::info!("Commune Public room event.");
87 let room_id = event.room_id().to_owned();
88 match public {
89 Some(true) => {
90 tracing::info!("Joining room: {}", room_id);
91 let joined = state.appservice.join_room(room_id.clone()).await;
92 if let Ok(joined) = joined {
94 let cache_key = ("appservice:joined", room_id.as_str()).cache_key();
95 if (state.cache.cache_data(&cache_key, &joined, 300).await).is_ok() {
96 tracing::info!("Cached joined status for room: {}", room_id);
97 } else {
98 tracing::warn!("Failed to cache joined status for room: {}", room_id);
99 }
100 }
101 }
102 Some(false) => {
103 tracing::info!("Leaving room: {}", room_id);
104 if let Err(e) = state.appservice.leave_room(room_id.clone()).await {
105 tracing::warn!("Failed to leave room: {}. Error: {}", room_id, e);
106 } else {
107 tracing::info!("Successfully left room: {}", room_id);
108 }
109 let cache_key = ("appservice:joined", room_id.as_str()).cache_key();
110 if let Err(e) = state.cache.delete_cached_data(&cache_key).await {
111 tracing::warn!(
112 "Failed to delete room from cache: {}. Error: {}",
113 room_id,
114 e
115 );
116 } else {
117 tracing::info!("Successfully removed room from cache: {}", room_id);
118 }
119 }
120 None => {}
121 }
122 };
123
124 let member_event =
125 if let Ok(event) = serde_json::from_value::<RoomMemberEvent>(event.clone()) {
126 event
127 } else {
128 continue;
129 };
130
131 println!("Member Event: {member_event:#?}");
132
133 let room_id = member_event.room_id().to_owned();
134 let membership = member_event.membership().to_owned();
135 let server_name = member_event.room_id().server_name();
136
137 match server_name {
138 Some(server_name) => {
139 let allowed = state
140 .config
141 .appservice
142 .rules
143 .federation_domain_whitelist
144 .iter()
145 .any(|domain| server_name.as_str().ends_with(domain));
146
147 if server_name.as_str() != state.config.matrix.server_name && allowed {
148 if state.config.appservice.rules.invite_by_local_user {
151 tracing::info!(
152 "Ignoring event for room on different server: {}",
153 server_name
154 );
155 continue;
156 }
157 }
158 }
159 None => {
160 tracing::info!("Ignoring event for room with no server name");
161 continue;
162 }
163 }
164
165 let invited_user = member_event.state_key().to_owned();
167 if invited_user != state.appservice.user_id() {
168 tracing::info!("Ignoring event for user: {}", invited_user);
169 continue;
170 }
171
172 match membership {
173 MembershipState::Invite => {
174 tracing::info!("Joining room: {}", room_id);
175 if let Err(e) = state.appservice.join_room(room_id.clone()).await {
176 tracing::warn!("Failed to join room: {}. Error: {}", room_id, e);
177 } else {
178 tracing::info!("Successfully joined room: {}", room_id);
179 }
180 if state
181 .appservice
182 .add_to_joined_rooms(room_id.clone())
183 .is_err()
184 {
185 tracing::warn!("Failed to add room to joined rooms list: {}", room_id);
186 }
187 }
188 MembershipState::Leave => {
189 if let Err(e) = state.appservice.leave_room(room_id.clone()).await {
190 tracing::warn!("Failed to leave room: {}. Error: {}", room_id, e);
191 } else {
192 tracing::info!("Successfully left room: {}", room_id);
193 }
194 if let Err(e) = state.appservice.remove_from_joined_rooms(&room_id) {
195 tracing::warn!(
196 "Failed to remove room from joined rooms list: {} {}",
197 room_id,
198 e
199 );
200 }
201 }
202 MembershipState::Ban => {
203 tracing::info!("Banned from room: {}", room_id);
204 if let Err(e) = state.appservice.remove_from_joined_rooms(&room_id) {
205 tracing::warn!(
206 "Failed to remove room from joined rooms list: {} {}",
207 room_id,
208 e
209 );
210 }
211 }
212 _ => {}
213 }
214 }
215
216 Ok(Json(json!({})))
217}