7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1 | // Copyright 2023 The Matrix.org Foundation C.I.C. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 2 | // |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 4 | // you may not use this file except in compliance with the License. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 5 | // You may obtain a copy of the License at |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 6 | // |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 8 | // |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 9 | // Unless required by applicable law or agreed to in writing, software |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 12 | // See the License for the specific language governing permissions and |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 13 | // limitations under the License. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 14 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 15 | //! The SDK's representation of the result of a `/sync` request. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 16 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 17 | use std::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 18 | collections::{BTreeMap, btree_map}, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 19 | fmt, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 20 | time::Duration, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 21 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 22 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 23 | pub use matrix_sdk_base::sync::*; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 24 | use matrix_sdk_base::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 25 | debug::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 26 | DebugInvitedRoom, DebugKnockedRoom, DebugListOfProcessedToDeviceEvents, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 27 | DebugListOfRawEventsNoId, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 28 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 29 | sleep::sleep, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 30 | sync::SyncResponse as BaseSyncResponse, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 31 | timer, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 32 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 33 | use matrix_sdk_common::deserialized_responses::ProcessedToDeviceEvent; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 34 | use ruma::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 35 | OwnedRoomId, RoomId, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 36 | api::client::sync::sync_events::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 37 | self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 38 | v3::{InvitedRoom, KnockedRoom}, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 39 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 40 | events::{AnyGlobalAccountDataEvent, presence::PresenceEvent}, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 41 | serde::Raw, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 42 | time::Instant, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 43 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 44 | use tracing::{debug, error, instrument, warn}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 45 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 46 | use crate::{Client, Result, Room, event_handler::HandlerKind}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 47 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 48 | /// The processed response of a `/sync` request. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 49 | #[derive(Clone, Default)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 50 | pub struct SyncResponse { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 51 | /// The batch token to supply in the `since` param of the next `/sync` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 52 | /// request. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 53 | pub next_batch: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 54 | /// Updates to rooms. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 55 | pub rooms: RoomUpdates, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 56 | /// Updates to the presence status of other users. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 57 | pub presence: Vec<Raw<PresenceEvent>>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 58 | /// The global private data created by this user. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 59 | pub account_data: Vec<Raw<AnyGlobalAccountDataEvent>>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 60 | /// Messages sent directly between devices. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 61 | pub to_device: Vec<ProcessedToDeviceEvent>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 62 | /// New notifications per room. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 63 | pub notifications: BTreeMap<OwnedRoomId, Vec<Notification>>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 64 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 65 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 66 | impl SyncResponse { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 67 | pub(crate) fn new(next_batch: String, base_response: BaseSyncResponse) -> Self { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 68 | let BaseSyncResponse { rooms, presence, account_data, to_device, notifications } = |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 69 | base_response; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 70 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 71 | Self { next_batch, rooms, presence, account_data, to_device, notifications } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 72 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 73 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 74 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 75 | #[cfg(not(tarpaulin_include))] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 76 | impl fmt::Debug for SyncResponse { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 77 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 78 | f.debug_struct("SyncResponse") |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 79 | .field("next_batch", &self.next_batch) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 80 | .field("rooms", &self.rooms) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 81 | .field("account_data", &DebugListOfRawEventsNoId(&self.account_data)) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 82 | .field("to_device", &DebugListOfProcessedToDeviceEvents(&self.to_device)) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 83 | .field("notifications", &self.notifications) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 84 | .finish_non_exhaustive() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 85 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 86 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 87 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 88 | /// A batch of updates to a room. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 89 | #[derive(Clone)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 90 | pub enum RoomUpdate { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 91 | /// Updates to a room the user is no longer in. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 92 | Left { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 93 | /// Room object with general information on the room. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 94 | room: Room, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 95 | /// Updates to the room. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 96 | updates: LeftRoomUpdate, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 97 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 98 | /// Updates to a room the user is currently in. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 99 | Joined { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 100 | /// Room object with general information on the room. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 101 | room: Room, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 102 | /// Updates to the room. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 103 | updates: JoinedRoomUpdate, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 104 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 105 | /// Updates to a room the user is invited to. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 106 | Invited { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 107 | /// Room object with general information on the room. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 108 | room: Room, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 109 | /// Updates to the room. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 110 | updates: InvitedRoom, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 111 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 112 | /// Updates to a room the user knocked on. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 113 | Knocked { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 114 | /// Room object with general information on the room. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 115 | room: Room, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 116 | /// Updates to the room. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 117 | updates: KnockedRoom, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 118 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 119 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 120 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 121 | #[cfg(not(tarpaulin_include))] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 122 | impl fmt::Debug for RoomUpdate { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 123 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 124 | match self { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 125 | Self::Left { room, updates } => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 126 | f.debug_struct("Left").field("room", room).field("updates", updates).finish() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 127 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 128 | Self::Joined { room, updates } => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 129 | f.debug_struct("Joined").field("room", room).field("updates", updates).finish() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 130 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 131 | Self::Invited { room, updates } => f |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 132 | .debug_struct("Invited") |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 133 | .field("room", room) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 134 | .field("updates", &DebugInvitedRoom(updates)) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 135 | .finish(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 136 | Self::Knocked { room, updates } => f |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 137 | .debug_struct("Knocked") |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 138 | .field("room", room) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 139 | .field("updates", &DebugKnockedRoom(updates)) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 140 | .finish(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 141 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 142 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 143 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 144 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 145 | /// Internal functionality related to getting events from the server |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 146 | /// (`sync_events` endpoint) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 147 | impl Client { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 148 | /// Receive a sync response, compute extra information out of it and store |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 149 | /// the interesting bits in the database, then call all the handlers. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 150 | pub(crate) async fn process_sync( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 151 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 152 | response: sync_events::v3::Response, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 153 | ) -> Result<BaseSyncResponse> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 154 | subscribe_to_room_latest_events( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 155 | self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 156 | response.rooms.join.keys().chain(response.rooms.leave.keys()), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 157 | ) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 158 | .await; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 159 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 160 | let response = Box::pin(self.base_client().receive_sync_response(response)).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 161 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 162 | // Some new keys might have been received, so trigger a backup if needed. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 163 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 164 | self.encryption().backups().maybe_trigger_backup(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 165 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 166 | self.call_sync_response_handlers(&response).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 167 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 168 | Ok(response) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 169 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 170 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 171 | /// Calls event handlers and notification handlers after a sync response has |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 172 | /// been processed. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 173 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 174 | /// At this point, the sync response's data has been taken into account and |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 175 | /// persisted in the store, if needs be. This function is only calling |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 176 | /// the event, room update and notification handlers. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 177 | #[tracing::instrument(skip(self, response))] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 178 | pub(crate) async fn call_sync_response_handlers( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 179 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 180 | response: &BaseSyncResponse, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 181 | ) -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 182 | let _timer = timer!(tracing::Level::TRACE, "_method"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 183 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 184 | let BaseSyncResponse { rooms, presence, account_data, to_device, notifications } = response; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 185 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 186 | let now = Instant::now(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 187 | self.handle_sync_events(HandlerKind::GlobalAccountData, None, account_data).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 188 | self.handle_sync_events(HandlerKind::Presence, None, presence).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 189 | self.handle_sync_to_device_events(to_device).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 190 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 191 | // Ignore errors when there are no receivers. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 192 | let _ = self.inner.room_updates_sender.send(rooms.clone()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 193 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 194 | for (room_id, room_info) in &rooms.joined { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 195 | let Some(room) = self.get_room(room_id) else { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 196 | error!(?room_id, "Can't call event handler, room not found"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 197 | continue; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 198 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 199 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 200 | self.send_room_update(room_id, || RoomUpdate::Joined { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 201 | room: room.clone(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 202 | updates: room_info.clone(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 203 | }); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 204 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 205 | let JoinedRoomUpdate { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 206 | unread_notifications: _, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 207 | timeline, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 208 | state, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 209 | account_data, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 210 | ephemeral, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 211 | ambiguity_changes: _, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 212 | avatar_changes: _, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 213 | } = room_info; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 214 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 215 | let room = Some(&room); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 216 | self.handle_sync_events(HandlerKind::RoomAccountData, room, account_data).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 217 | self.handle_sync_state_events(room, state).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 218 | self.handle_sync_timeline_events(room, &timeline.events).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 219 | // Handle ephemeral events after timeline, read receipts in here |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 220 | // could refer to timeline events from the same response. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 221 | self.handle_sync_events(HandlerKind::EphemeralRoomData, room, ephemeral).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 222 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 223 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 224 | for (room_id, room_info) in &rooms.left { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 225 | let Some(room) = self.get_room(room_id) else { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 226 | error!(?room_id, "Can't call event handler, room not found"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 227 | continue; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 228 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 229 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 230 | self.send_room_update(room_id, || RoomUpdate::Left { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 231 | room: room.clone(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 232 | updates: room_info.clone(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 233 | }); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 234 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 235 | let LeftRoomUpdate { timeline, state, account_data, ambiguity_changes: _ } = room_info; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 236 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 237 | let room = Some(&room); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 238 | self.handle_sync_events(HandlerKind::RoomAccountData, room, account_data).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 239 | self.handle_sync_state_events(room, state).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 240 | self.handle_sync_timeline_events(room, &timeline.events).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 241 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 242 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 243 | for (room_id, room_info) in &rooms.invited { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 244 | let Some(room) = self.get_room(room_id) else { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 245 | error!(?room_id, "Can't call event handler, room not found"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 246 | continue; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 247 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 248 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 249 | self.send_room_update(room_id, || RoomUpdate::Invited { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 250 | room: room.clone(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 251 | updates: room_info.clone(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 252 | }); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 253 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 254 | let invite_state = &room_info.invite_state.events; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 255 | self.handle_sync_events(HandlerKind::StrippedState, Some(&room), invite_state).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 256 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 257 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 258 | for (room_id, room_info) in &rooms.knocked { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 259 | let Some(room) = self.get_room(room_id) else { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 260 | error!(?room_id, "Can't call event handler, room not found"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 261 | continue; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 262 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 263 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 264 | self.send_room_update(room_id, || RoomUpdate::Knocked { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 265 | room: room.clone(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 266 | updates: room_info.clone(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 267 | }); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 268 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 269 | let knock_state = &room_info.knock_state.events; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 270 | self.handle_sync_events(HandlerKind::StrippedState, Some(&room), knock_state).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 271 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 272 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 273 | debug!("Ran event handlers in {:?}", now.elapsed()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 274 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 275 | let now = Instant::now(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 276 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 277 | // Construct notification event handler futures |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 278 | let mut futures = Vec::new(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 279 | for handler in &*self.notification_handlers().await { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 280 | for (room_id, room_notifications) in notifications { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 281 | let Some(room) = self.get_room(room_id) else { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 282 | warn!(?room_id, "Can't call notification handler, room not found"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 283 | continue; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 284 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 285 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 286 | futures.extend(room_notifications.iter().map(|notification| { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 287 | (handler)(notification.clone(), room.clone(), self.clone()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 288 | })); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 289 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 290 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 291 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 292 | // Run the notification handler futures with the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 293 | // `self.notification_handlers` lock no longer being held, in order. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 294 | for fut in futures { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 295 | fut.await; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 296 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 297 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 298 | debug!("Ran notification handlers in {:?}", now.elapsed()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 299 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 300 | Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 301 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 302 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 303 | fn send_room_update(&self, room_id: &RoomId, make_msg: impl FnOnce() -> RoomUpdate) { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 304 | if let btree_map::Entry::Occupied(entry) = |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 305 | self.inner.room_update_channels.lock().unwrap().entry(room_id.to_owned()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 306 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 307 | let tx = entry.get(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 308 | if tx.receiver_count() == 0 { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 309 | entry.remove(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 310 | } else { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 311 | _ = tx.send(make_msg()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 312 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 313 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 314 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 315 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 316 | async fn sleep() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 317 | sleep(Duration::from_secs(1)).await; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 318 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 319 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 320 | pub(crate) async fn sync_loop_helper( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 321 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 322 | sync_settings: &mut crate::config::SyncSettings, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 323 | ) -> Result<SyncResponse> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 324 | let response = self.sync_once(sync_settings.clone()).await; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 325 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 326 | match response { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 327 | Ok(r) => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 328 | sync_settings.token = r.next_batch.clone().into(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 329 | Ok(r) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 330 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 331 | Err(e) => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 332 | error!("Received an invalid response: {e}"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 333 | Err(e) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 334 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 335 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 336 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 337 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 338 | pub(crate) async fn delay_sync(last_sync_time: &mut Option<Instant>) { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 339 | let now = Instant::now(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 340 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 341 | // If the last sync happened less than a second ago, sleep for a |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 342 | // while to not hammer out requests if the server doesn't respect |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 343 | // the sync timeout. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 344 | if let Some(t) = last_sync_time |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 345 | && now - *t <= Duration::from_secs(1) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 346 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 347 | Self::sleep().await; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 348 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 349 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 350 | *last_sync_time = Some(now); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 351 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 352 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 353 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 354 | /// Call `LatestEvents::listen_to_room` for rooms in `response`. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 355 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 356 | /// That way, the latest event is computed and updated for all rooms receiving |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 357 | /// an update from the sync. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 358 | #[instrument(skip_all)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 359 | pub(crate) async fn subscribe_to_room_latest_events<'a, R>(client: &'a Client, room_ids: R) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 360 | where |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 361 | R: Iterator<Item = &'a OwnedRoomId>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 362 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 363 | if !client.event_cache().has_subscribed() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 364 | return; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 365 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 366 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 367 | let latest_events = client.latest_events().await; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 368 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 369 | for room_id in room_ids { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 370 | if let Err(error) = latest_events.listen_to_room(room_id).await { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 371 | error!(?error, ?room_id, "Failed to listen to the latest event for this room"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 372 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 373 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 374 | } |