1use std::{
18 collections::{BTreeMap, btree_map},
19 fmt,
20 time::Duration,
21};
22
23pub use matrix_sdk_base::sync::*;
24use matrix_sdk_base::{
25 debug::{
26 DebugInvitedRoom, DebugKnockedRoom, DebugListOfProcessedToDeviceEvents,
27 DebugListOfRawEventsNoId,
28 },
29 sleep::sleep,
30 sync::SyncResponse as BaseSyncResponse,
31 timer,
32};
33use matrix_sdk_common::deserialized_responses::ProcessedToDeviceEvent;
34use ruma::{
35 OwnedRoomId, RoomId,
36 api::client::sync::sync_events::{
37 self,
38 v3::{InvitedRoom, KnockedRoom},
39 },
40 events::{AnyGlobalAccountDataEvent, presence::PresenceEvent},
41 serde::Raw,
42 time::Instant,
43};
44use tracing::{Level, error, instrument, warn};
45
46use crate::{Client, Result, Room, event_handler::HandlerKind};
47
48#[derive(Clone, Default)]
50pub struct SyncResponse {
51 pub next_batch: String,
54 pub rooms: RoomUpdates,
56 pub presence: Vec<Raw<PresenceEvent>>,
58 pub account_data: Vec<Raw<AnyGlobalAccountDataEvent>>,
60 pub to_device: Vec<ProcessedToDeviceEvent>,
62 pub notifications: BTreeMap<OwnedRoomId, Vec<Notification>>,
64}
65
66impl SyncResponse {
67 pub(crate) fn new(next_batch: String, base_response: BaseSyncResponse) -> Self {
68 let BaseSyncResponse { rooms, presence, account_data, to_device, notifications } =
69 base_response;
70
71 Self { next_batch, rooms, presence, account_data, to_device, notifications }
72 }
73}
74
75#[cfg(not(tarpaulin_include))]
76impl fmt::Debug for SyncResponse {
77 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78 f.debug_struct("SyncResponse")
79 .field("next_batch", &self.next_batch)
80 .field("rooms", &self.rooms)
81 .field("account_data", &DebugListOfRawEventsNoId(&self.account_data))
82 .field("to_device", &DebugListOfProcessedToDeviceEvents(&self.to_device))
83 .field("notifications", &self.notifications)
84 .finish_non_exhaustive()
85 }
86}
87
88#[derive(Clone)]
90pub enum RoomUpdate {
91 Left {
93 room: Room,
95 updates: LeftRoomUpdate,
97 },
98 Joined {
100 room: Room,
102 updates: JoinedRoomUpdate,
104 },
105 Invited {
107 room: Room,
109 updates: InvitedRoom,
111 },
112 Knocked {
114 room: Room,
116 updates: KnockedRoom,
118 },
119}
120
121#[cfg(not(tarpaulin_include))]
122impl fmt::Debug for RoomUpdate {
123 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124 match self {
125 Self::Left { room, updates } => {
126 f.debug_struct("Left").field("room", room).field("updates", updates).finish()
127 }
128 Self::Joined { room, updates } => {
129 f.debug_struct("Joined").field("room", room).field("updates", updates).finish()
130 }
131 Self::Invited { room, updates } => f
132 .debug_struct("Invited")
133 .field("room", room)
134 .field("updates", &DebugInvitedRoom(updates))
135 .finish(),
136 Self::Knocked { room, updates } => f
137 .debug_struct("Knocked")
138 .field("room", room)
139 .field("updates", &DebugKnockedRoom(updates))
140 .finish(),
141 }
142 }
143}
144
145impl Client {
148 pub(crate) async fn process_sync(
151 &self,
152 response: sync_events::v3::Response,
153 ) -> Result<BaseSyncResponse> {
154 subscribe_to_room_latest_events(
155 self,
156 response.rooms.join.keys().chain(response.rooms.leave.keys()),
157 )
158 .await;
159
160 let response = Box::pin(self.base_client().receive_sync_response(response)).await?;
161
162 #[cfg(feature = "e2e-encryption")]
165 self.encryption().backups().maybe_trigger_backup();
166
167 if let Some(event_cache) = self.event_cache() {
169 Box::pin(event_cache.handle_room_updates(response.rooms.clone())).await?;
170 }
171
172 self.call_sync_response_handlers(&response).await?;
174
175 Ok(response)
176 }
177
178 #[tracing::instrument(skip(self, response))]
185 pub(crate) async fn call_sync_response_handlers(
186 &self,
187 response: &BaseSyncResponse,
188 ) -> Result<()> {
189 let _timer = timer!(Level::TRACE, "_method");
190
191 let BaseSyncResponse { rooms, presence, account_data, to_device, notifications } = response;
192
193 let timer = timer!(Level::DEBUG, "Run event handlers");
194
195 self.handle_sync_events(HandlerKind::GlobalAccountData, None, account_data).await?;
196 self.handle_sync_events(HandlerKind::Presence, None, presence).await?;
197 self.handle_sync_to_device_events(to_device).await?;
198
199 if self.inner.room_updates_sender.receiver_count() > 0 {
207 let _ = self.inner.room_updates_sender.send(rooms.clone());
209 }
210
211 for (room_id, updates) in &rooms.joined {
212 let Some(room) = self.get_room(room_id) else {
213 error!(?room_id, "Can't call event handler, room not found");
214 continue;
215 };
216
217 self.send_room_update(room_id, || RoomUpdate::Joined {
218 room: room.clone(),
219 updates: updates.clone(),
220 });
221
222 let JoinedRoomUpdate {
223 unread_notifications: _,
224 timeline,
225 state,
226 account_data,
227 ephemeral,
228 ambiguity_changes: _,
229 avatar_changes: _,
230 } = updates;
231
232 let room = Some(&room);
233 self.handle_sync_events(HandlerKind::RoomAccountData, room, account_data).await?;
234 self.handle_sync_state_events(room, state).await?;
235 self.handle_sync_timeline_events(room, &timeline.events).await?;
236 self.handle_sync_events(HandlerKind::EphemeralRoomData, room, ephemeral).await?;
239 }
240
241 for (room_id, updates) in &rooms.left {
242 let Some(room) = self.get_room(room_id) else {
243 error!(?room_id, "Can't call event handler, room not found");
244 continue;
245 };
246
247 self.send_room_update(room_id, || RoomUpdate::Left {
248 room: room.clone(),
249 updates: updates.clone(),
250 });
251
252 let LeftRoomUpdate { timeline, state, account_data, ambiguity_changes: _ } = updates;
253
254 let room = Some(&room);
255 self.handle_sync_events(HandlerKind::RoomAccountData, room, account_data).await?;
256 self.handle_sync_state_events(room, state).await?;
257 self.handle_sync_timeline_events(room, &timeline.events).await?;
258 }
259
260 for (room_id, updates) in &rooms.invited {
261 let Some(room) = self.get_room(room_id) else {
262 error!(?room_id, "Can't call event handler, room not found");
263 continue;
264 };
265
266 self.send_room_update(room_id, || RoomUpdate::Invited {
267 room: room.clone(),
268 updates: updates.clone(),
269 });
270
271 let invite_state = &updates.invite_state.events;
272 self.handle_sync_events(HandlerKind::StrippedState, Some(&room), invite_state).await?;
273 }
274
275 for (room_id, updates) in &rooms.knocked {
276 let Some(room) = self.get_room(room_id) else {
277 error!(?room_id, "Can't call event handler, room not found");
278 continue;
279 };
280
281 self.send_room_update(room_id, || RoomUpdate::Knocked {
282 room: room.clone(),
283 updates: updates.clone(),
284 });
285
286 let knock_state = &updates.knock_state.events;
287 self.handle_sync_events(HandlerKind::StrippedState, Some(&room), knock_state).await?;
288 }
289
290 drop(timer);
291
292 let timer = timer!(Level::DEBUG, "Run notification handlers");
293
294 let mut futures = Vec::new();
296 for handler in &*self.notification_handlers().await {
297 for (room_id, room_notifications) in notifications {
298 let Some(room) = self.get_room(room_id) else {
299 warn!(?room_id, "Can't call notification handler, room not found");
300 continue;
301 };
302
303 futures.extend(room_notifications.iter().map(|notification| {
304 (handler)(notification.clone(), room.clone(), self.clone())
305 }));
306 }
307 }
308
309 for fut in futures {
312 fut.await;
313 }
314
315 drop(timer);
316
317 Ok(())
318 }
319
320 fn send_room_update(&self, room_id: &RoomId, make_msg: impl FnOnce() -> RoomUpdate) {
321 if let btree_map::Entry::Occupied(entry) =
322 self.inner.room_update_channels.lock().unwrap().entry(room_id.to_owned())
323 {
324 let tx = entry.get();
325 if tx.receiver_count() == 0 {
326 entry.remove();
327 } else {
328 _ = tx.send(make_msg());
329 }
330 }
331 }
332
333 async fn sleep() {
334 sleep(Duration::from_secs(1)).await;
335 }
336
337 pub(crate) async fn sync_loop_helper(
338 &self,
339 sync_settings: &mut crate::config::SyncSettings,
340 ) -> Result<SyncResponse> {
341 let response = self.sync_once(sync_settings.clone()).await;
342
343 match response {
344 Ok(r) => {
345 sync_settings.token = r.next_batch.clone().into();
346 Ok(r)
347 }
348 Err(e) => {
349 error!("Received an invalid response: {e}");
350 Err(e)
351 }
352 }
353 }
354
355 pub(crate) async fn delay_sync(last_sync_time: &mut Option<Instant>) {
356 let now = Instant::now();
357
358 if let Some(t) = last_sync_time
362 && now - *t <= Duration::from_secs(1)
363 {
364 Self::sleep().await;
365 }
366
367 *last_sync_time = Some(now);
368 }
369}
370
371#[instrument(skip_all)]
377pub(crate) async fn subscribe_to_room_latest_events<'a, R>(client: &'a Client, room_ids: R)
378where
379 R: Iterator<Item = &'a OwnedRoomId>,
380{
381 if let Some(latest_events) = client.latest_events().await {
382 for room_id in room_ids {
383 if let Err(error) = latest_events.listen_to_room(room_id).await {
384 error!(?error, ?room_id, "Failed to listen to the latest event for this room");
385 }
386 }
387 }
388}