atagen

matrix-rust-sdk

public
Star0
Watch0
ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk.git

Blame: crates/matrix-sdk/src/sync.rs

7ff8adafe9e4atagen2026-10-22 12:30 UTC1
// Copyright 2023 The Matrix.org Foundation C.I.C.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2
//
7ff8adafe9e4atagen2026-10-22 12:30 UTC3
// Licensed under the Apache License, Version 2.0 (the "License");
7ff8adafe9e4atagen2026-10-22 12:30 UTC4
// you may not use this file except in compliance with the License.
7ff8adafe9e4atagen2026-10-22 12:30 UTC5
// You may obtain a copy of the License at
7ff8adafe9e4atagen2026-10-22 12:30 UTC6
//
7ff8adafe9e4atagen2026-10-22 12:30 UTC7
//     http://www.apache.org/licenses/LICENSE-2.0
7ff8adafe9e4atagen2026-10-22 12:30 UTC8
//
7ff8adafe9e4atagen2026-10-22 12:30 UTC9
// Unless required by applicable law or agreed to in writing, software
7ff8adafe9e4atagen2026-10-22 12:30 UTC10
// distributed under the License is distributed on an "AS IS" BASIS,
7ff8adafe9e4atagen2026-10-22 12:30 UTC11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
7ff8adafe9e4atagen2026-10-22 12:30 UTC12
// See the License for the specific language governing permissions and
7ff8adafe9e4atagen2026-10-22 12:30 UTC13
// limitations under the License.
7ff8adafe9e4atagen2026-10-22 12:30 UTC14
7ff8adafe9e4atagen2026-10-22 12:30 UTC15
//! The SDK's representation of the result of a `/sync` request.
7ff8adafe9e4atagen2026-10-22 12:30 UTC16
7ff8adafe9e4atagen2026-10-22 12:30 UTC17
use std::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC18
    collections::{BTreeMap, btree_map},
7ff8adafe9e4atagen2026-10-22 12:30 UTC19
    fmt,
7ff8adafe9e4atagen2026-10-22 12:30 UTC20
    time::Duration,
7ff8adafe9e4atagen2026-10-22 12:30 UTC21
};
7ff8adafe9e4atagen2026-10-22 12:30 UTC22
7ff8adafe9e4atagen2026-10-22 12:30 UTC23
pub use matrix_sdk_base::sync::*;
7ff8adafe9e4atagen2026-10-22 12:30 UTC24
use matrix_sdk_base::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC25
    debug::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC26
        DebugInvitedRoom, DebugKnockedRoom, DebugListOfProcessedToDeviceEvents,
7ff8adafe9e4atagen2026-10-22 12:30 UTC27
        DebugListOfRawEventsNoId,
7ff8adafe9e4atagen2026-10-22 12:30 UTC28
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC29
    sleep::sleep,
7ff8adafe9e4atagen2026-10-22 12:30 UTC30
    sync::SyncResponse as BaseSyncResponse,
7ff8adafe9e4atagen2026-10-22 12:30 UTC31
    timer,
7ff8adafe9e4atagen2026-10-22 12:30 UTC32
};
7ff8adafe9e4atagen2026-10-22 12:30 UTC33
use matrix_sdk_common::deserialized_responses::ProcessedToDeviceEvent;
7ff8adafe9e4atagen2026-10-22 12:30 UTC34
use ruma::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC35
    OwnedRoomId, RoomId,
7ff8adafe9e4atagen2026-10-22 12:30 UTC36
    api::client::sync::sync_events::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC37
        self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC38
        v3::{InvitedRoom, KnockedRoom},
7ff8adafe9e4atagen2026-10-22 12:30 UTC39
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC40
    events::{AnyGlobalAccountDataEvent, presence::PresenceEvent},
7ff8adafe9e4atagen2026-10-22 12:30 UTC41
    serde::Raw,
7ff8adafe9e4atagen2026-10-22 12:30 UTC42
    time::Instant,
7ff8adafe9e4atagen2026-10-22 12:30 UTC43
};
7ff8adafe9e4atagen2026-10-22 12:30 UTC44
use tracing::{debug, error, instrument, warn};
7ff8adafe9e4atagen2026-10-22 12:30 UTC45
7ff8adafe9e4atagen2026-10-22 12:30 UTC46
use crate::{Client, Result, Room, event_handler::HandlerKind};
7ff8adafe9e4atagen2026-10-22 12:30 UTC47
7ff8adafe9e4atagen2026-10-22 12:30 UTC48
/// The processed response of a `/sync` request.
7ff8adafe9e4atagen2026-10-22 12:30 UTC49
#[derive(Clone, Default)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC50
pub struct SyncResponse {
7ff8adafe9e4atagen2026-10-22 12:30 UTC51
    /// The batch token to supply in the `since` param of the next `/sync`
7ff8adafe9e4atagen2026-10-22 12:30 UTC52
    /// request.
7ff8adafe9e4atagen2026-10-22 12:30 UTC53
    pub next_batch: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC54
    /// Updates to rooms.
7ff8adafe9e4atagen2026-10-22 12:30 UTC55
    pub rooms: RoomUpdates,
7ff8adafe9e4atagen2026-10-22 12:30 UTC56
    /// Updates to the presence status of other users.
7ff8adafe9e4atagen2026-10-22 12:30 UTC57
    pub presence: Vec<Raw<PresenceEvent>>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC58
    /// The global private data created by this user.
7ff8adafe9e4atagen2026-10-22 12:30 UTC59
    pub account_data: Vec<Raw<AnyGlobalAccountDataEvent>>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC60
    /// Messages sent directly between devices.
7ff8adafe9e4atagen2026-10-22 12:30 UTC61
    pub to_device: Vec<ProcessedToDeviceEvent>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC62
    /// New notifications per room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC63
    pub notifications: BTreeMap<OwnedRoomId, Vec<Notification>>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC64
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC65
7ff8adafe9e4atagen2026-10-22 12:30 UTC66
impl SyncResponse {
7ff8adafe9e4atagen2026-10-22 12:30 UTC67
    pub(crate) fn new(next_batch: String, base_response: BaseSyncResponse) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC68
        let BaseSyncResponse { rooms, presence, account_data, to_device, notifications } =
7ff8adafe9e4atagen2026-10-22 12:30 UTC69
            base_response;
7ff8adafe9e4atagen2026-10-22 12:30 UTC70
7ff8adafe9e4atagen2026-10-22 12:30 UTC71
        Self { next_batch, rooms, presence, account_data, to_device, notifications }
7ff8adafe9e4atagen2026-10-22 12:30 UTC72
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC73
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC74
7ff8adafe9e4atagen2026-10-22 12:30 UTC75
#[cfg(not(tarpaulin_include))]
7ff8adafe9e4atagen2026-10-22 12:30 UTC76
impl fmt::Debug for SyncResponse {
7ff8adafe9e4atagen2026-10-22 12:30 UTC77
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
7ff8adafe9e4atagen2026-10-22 12:30 UTC78
        f.debug_struct("SyncResponse")
7ff8adafe9e4atagen2026-10-22 12:30 UTC79
            .field("next_batch", &self.next_batch)
7ff8adafe9e4atagen2026-10-22 12:30 UTC80
            .field("rooms", &self.rooms)
7ff8adafe9e4atagen2026-10-22 12:30 UTC81
            .field("account_data", &DebugListOfRawEventsNoId(&self.account_data))
7ff8adafe9e4atagen2026-10-22 12:30 UTC82
            .field("to_device", &DebugListOfProcessedToDeviceEvents(&self.to_device))
7ff8adafe9e4atagen2026-10-22 12:30 UTC83
            .field("notifications", &self.notifications)
7ff8adafe9e4atagen2026-10-22 12:30 UTC84
            .finish_non_exhaustive()
7ff8adafe9e4atagen2026-10-22 12:30 UTC85
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC86
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC87
7ff8adafe9e4atagen2026-10-22 12:30 UTC88
/// A batch of updates to a room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC89
#[derive(Clone)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC90
pub enum RoomUpdate {
7ff8adafe9e4atagen2026-10-22 12:30 UTC91
    /// Updates to a room the user is no longer in.
7ff8adafe9e4atagen2026-10-22 12:30 UTC92
    Left {
7ff8adafe9e4atagen2026-10-22 12:30 UTC93
        /// Room object with general information on the room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC94
        room: Room,
7ff8adafe9e4atagen2026-10-22 12:30 UTC95
        /// Updates to the room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC96
        updates: LeftRoomUpdate,
7ff8adafe9e4atagen2026-10-22 12:30 UTC97
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC98
    /// Updates to a room the user is currently in.
7ff8adafe9e4atagen2026-10-22 12:30 UTC99
    Joined {
7ff8adafe9e4atagen2026-10-22 12:30 UTC100
        /// Room object with general information on the room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC101
        room: Room,
7ff8adafe9e4atagen2026-10-22 12:30 UTC102
        /// Updates to the room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC103
        updates: JoinedRoomUpdate,
7ff8adafe9e4atagen2026-10-22 12:30 UTC104
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC105
    /// Updates to a room the user is invited to.
7ff8adafe9e4atagen2026-10-22 12:30 UTC106
    Invited {
7ff8adafe9e4atagen2026-10-22 12:30 UTC107
        /// Room object with general information on the room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC108
        room: Room,
7ff8adafe9e4atagen2026-10-22 12:30 UTC109
        /// Updates to the room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC110
        updates: InvitedRoom,
7ff8adafe9e4atagen2026-10-22 12:30 UTC111
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC112
    /// Updates to a room the user knocked on.
7ff8adafe9e4atagen2026-10-22 12:30 UTC113
    Knocked {
7ff8adafe9e4atagen2026-10-22 12:30 UTC114
        /// Room object with general information on the room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC115
        room: Room,
7ff8adafe9e4atagen2026-10-22 12:30 UTC116
        /// Updates to the room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC117
        updates: KnockedRoom,
7ff8adafe9e4atagen2026-10-22 12:30 UTC118
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC119
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC120
7ff8adafe9e4atagen2026-10-22 12:30 UTC121
#[cfg(not(tarpaulin_include))]
7ff8adafe9e4atagen2026-10-22 12:30 UTC122
impl fmt::Debug for RoomUpdate {
7ff8adafe9e4atagen2026-10-22 12:30 UTC123
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
7ff8adafe9e4atagen2026-10-22 12:30 UTC124
        match self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC125
            Self::Left { room, updates } => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC126
                f.debug_struct("Left").field("room", room).field("updates", updates).finish()
7ff8adafe9e4atagen2026-10-22 12:30 UTC127
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC128
            Self::Joined { room, updates } => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC129
                f.debug_struct("Joined").field("room", room).field("updates", updates).finish()
7ff8adafe9e4atagen2026-10-22 12:30 UTC130
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC131
            Self::Invited { room, updates } => f
7ff8adafe9e4atagen2026-10-22 12:30 UTC132
                .debug_struct("Invited")
7ff8adafe9e4atagen2026-10-22 12:30 UTC133
                .field("room", room)
7ff8adafe9e4atagen2026-10-22 12:30 UTC134
                .field("updates", &DebugInvitedRoom(updates))
7ff8adafe9e4atagen2026-10-22 12:30 UTC135
                .finish(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC136
            Self::Knocked { room, updates } => f
7ff8adafe9e4atagen2026-10-22 12:30 UTC137
                .debug_struct("Knocked")
7ff8adafe9e4atagen2026-10-22 12:30 UTC138
                .field("room", room)
7ff8adafe9e4atagen2026-10-22 12:30 UTC139
                .field("updates", &DebugKnockedRoom(updates))
7ff8adafe9e4atagen2026-10-22 12:30 UTC140
                .finish(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC141
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC142
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC143
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC144
7ff8adafe9e4atagen2026-10-22 12:30 UTC145
/// Internal functionality related to getting events from the server
7ff8adafe9e4atagen2026-10-22 12:30 UTC146
/// (`sync_events` endpoint)
7ff8adafe9e4atagen2026-10-22 12:30 UTC147
impl Client {
7ff8adafe9e4atagen2026-10-22 12:30 UTC148
    /// Receive a sync response, compute extra information out of it and store
7ff8adafe9e4atagen2026-10-22 12:30 UTC149
    /// the interesting bits in the database, then call all the handlers.
7ff8adafe9e4atagen2026-10-22 12:30 UTC150
    pub(crate) async fn process_sync(
7ff8adafe9e4atagen2026-10-22 12:30 UTC151
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC152
        response: sync_events::v3::Response,
7ff8adafe9e4atagen2026-10-22 12:30 UTC153
    ) -> Result<BaseSyncResponse> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC154
        subscribe_to_room_latest_events(
7ff8adafe9e4atagen2026-10-22 12:30 UTC155
            self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC156
            response.rooms.join.keys().chain(response.rooms.leave.keys()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC157
        )
7ff8adafe9e4atagen2026-10-22 12:30 UTC158
        .await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC159
7ff8adafe9e4atagen2026-10-22 12:30 UTC160
        let response = Box::pin(self.base_client().receive_sync_response(response)).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC161
7ff8adafe9e4atagen2026-10-22 12:30 UTC162
        // Some new keys might have been received, so trigger a backup if needed.
7ff8adafe9e4atagen2026-10-22 12:30 UTC163
        #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC164
        self.encryption().backups().maybe_trigger_backup();
7ff8adafe9e4atagen2026-10-22 12:30 UTC165
7ff8adafe9e4atagen2026-10-22 12:30 UTC166
        self.call_sync_response_handlers(&response).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC167
7ff8adafe9e4atagen2026-10-22 12:30 UTC168
        Ok(response)
7ff8adafe9e4atagen2026-10-22 12:30 UTC169
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC170
7ff8adafe9e4atagen2026-10-22 12:30 UTC171
    /// Calls event handlers and notification handlers after a sync response has
7ff8adafe9e4atagen2026-10-22 12:30 UTC172
    /// been processed.
7ff8adafe9e4atagen2026-10-22 12:30 UTC173
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC174
    /// At this point, the sync response's data has been taken into account and
7ff8adafe9e4atagen2026-10-22 12:30 UTC175
    /// persisted in the store, if needs be. This function is only calling
7ff8adafe9e4atagen2026-10-22 12:30 UTC176
    /// the event, room update and notification handlers.
7ff8adafe9e4atagen2026-10-22 12:30 UTC177
    #[tracing::instrument(skip(self, response))]
7ff8adafe9e4atagen2026-10-22 12:30 UTC178
    pub(crate) async fn call_sync_response_handlers(
7ff8adafe9e4atagen2026-10-22 12:30 UTC179
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC180
        response: &BaseSyncResponse,
7ff8adafe9e4atagen2026-10-22 12:30 UTC181
    ) -> Result<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC182
        let _timer = timer!(tracing::Level::TRACE, "_method");
7ff8adafe9e4atagen2026-10-22 12:30 UTC183
7ff8adafe9e4atagen2026-10-22 12:30 UTC184
        let BaseSyncResponse { rooms, presence, account_data, to_device, notifications } = response;
7ff8adafe9e4atagen2026-10-22 12:30 UTC185
7ff8adafe9e4atagen2026-10-22 12:30 UTC186
        let now = Instant::now();
7ff8adafe9e4atagen2026-10-22 12:30 UTC187
        self.handle_sync_events(HandlerKind::GlobalAccountData, None, account_data).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC188
        self.handle_sync_events(HandlerKind::Presence, None, presence).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC189
        self.handle_sync_to_device_events(to_device).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC190
f05c136873e9atagen2026-10-27 04:38 UTC191
        self.send_room_updates(rooms.clone());
7ff8adafe9e4atagen2026-10-22 12:30 UTC192
7ff8adafe9e4atagen2026-10-22 12:30 UTC193
        for (room_id, room_info) in &rooms.joined {
7ff8adafe9e4atagen2026-10-22 12:30 UTC194
            let Some(room) = self.get_room(room_id) else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC195
                error!(?room_id, "Can't call event handler, room not found");
7ff8adafe9e4atagen2026-10-22 12:30 UTC196
                continue;
7ff8adafe9e4atagen2026-10-22 12:30 UTC197
            };
7ff8adafe9e4atagen2026-10-22 12:30 UTC198
7ff8adafe9e4atagen2026-10-22 12:30 UTC199
            self.send_room_update(room_id, || RoomUpdate::Joined {
7ff8adafe9e4atagen2026-10-22 12:30 UTC200
                room: room.clone(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC201
                updates: room_info.clone(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC202
            });
7ff8adafe9e4atagen2026-10-22 12:30 UTC203
7ff8adafe9e4atagen2026-10-22 12:30 UTC204
            let JoinedRoomUpdate {
7ff8adafe9e4atagen2026-10-22 12:30 UTC205
                unread_notifications: _,
7ff8adafe9e4atagen2026-10-22 12:30 UTC206
                timeline,
7ff8adafe9e4atagen2026-10-22 12:30 UTC207
                state,
7ff8adafe9e4atagen2026-10-22 12:30 UTC208
                account_data,
7ff8adafe9e4atagen2026-10-22 12:30 UTC209
                ephemeral,
7ff8adafe9e4atagen2026-10-22 12:30 UTC210
                ambiguity_changes: _,
7ff8adafe9e4atagen2026-10-22 12:30 UTC211
                avatar_changes: _,
7ff8adafe9e4atagen2026-10-22 12:30 UTC212
            } = room_info;
7ff8adafe9e4atagen2026-10-22 12:30 UTC213
7ff8adafe9e4atagen2026-10-22 12:30 UTC214
            let room = Some(&room);
7ff8adafe9e4atagen2026-10-22 12:30 UTC215
            self.handle_sync_events(HandlerKind::RoomAccountData, room, account_data).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC216
            self.handle_sync_state_events(room, state).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC217
            self.handle_sync_timeline_events(room, &timeline.events).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC218
            // Handle ephemeral events after timeline, read receipts in here
7ff8adafe9e4atagen2026-10-22 12:30 UTC219
            // could refer to timeline events from the same response.
7ff8adafe9e4atagen2026-10-22 12:30 UTC220
            self.handle_sync_events(HandlerKind::EphemeralRoomData, room, ephemeral).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC221
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC222
7ff8adafe9e4atagen2026-10-22 12:30 UTC223
        for (room_id, room_info) in &rooms.left {
7ff8adafe9e4atagen2026-10-22 12:30 UTC224
            let Some(room) = self.get_room(room_id) else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC225
                error!(?room_id, "Can't call event handler, room not found");
7ff8adafe9e4atagen2026-10-22 12:30 UTC226
                continue;
7ff8adafe9e4atagen2026-10-22 12:30 UTC227
            };
7ff8adafe9e4atagen2026-10-22 12:30 UTC228
7ff8adafe9e4atagen2026-10-22 12:30 UTC229
            self.send_room_update(room_id, || RoomUpdate::Left {
7ff8adafe9e4atagen2026-10-22 12:30 UTC230
                room: room.clone(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC231
                updates: room_info.clone(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC232
            });
7ff8adafe9e4atagen2026-10-22 12:30 UTC233
7ff8adafe9e4atagen2026-10-22 12:30 UTC234
            let LeftRoomUpdate { timeline, state, account_data, ambiguity_changes: _ } = room_info;
7ff8adafe9e4atagen2026-10-22 12:30 UTC235
7ff8adafe9e4atagen2026-10-22 12:30 UTC236
            let room = Some(&room);
7ff8adafe9e4atagen2026-10-22 12:30 UTC237
            self.handle_sync_events(HandlerKind::RoomAccountData, room, account_data).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC238
            self.handle_sync_state_events(room, state).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC239
            self.handle_sync_timeline_events(room, &timeline.events).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC240
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC241
7ff8adafe9e4atagen2026-10-22 12:30 UTC242
        for (room_id, room_info) in &rooms.invited {
7ff8adafe9e4atagen2026-10-22 12:30 UTC243
            let Some(room) = self.get_room(room_id) else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC244
                error!(?room_id, "Can't call event handler, room not found");
7ff8adafe9e4atagen2026-10-22 12:30 UTC245
                continue;
7ff8adafe9e4atagen2026-10-22 12:30 UTC246
            };
7ff8adafe9e4atagen2026-10-22 12:30 UTC247
7ff8adafe9e4atagen2026-10-22 12:30 UTC248
            self.send_room_update(room_id, || RoomUpdate::Invited {
7ff8adafe9e4atagen2026-10-22 12:30 UTC249
                room: room.clone(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC250
                updates: room_info.clone(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC251
            });
7ff8adafe9e4atagen2026-10-22 12:30 UTC252
7ff8adafe9e4atagen2026-10-22 12:30 UTC253
            let invite_state = &room_info.invite_state.events;
7ff8adafe9e4atagen2026-10-22 12:30 UTC254
            self.handle_sync_events(HandlerKind::StrippedState, Some(&room), invite_state).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC255
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC256
7ff8adafe9e4atagen2026-10-22 12:30 UTC257
        for (room_id, room_info) in &rooms.knocked {
7ff8adafe9e4atagen2026-10-22 12:30 UTC258
            let Some(room) = self.get_room(room_id) else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC259
                error!(?room_id, "Can't call event handler, room not found");
7ff8adafe9e4atagen2026-10-22 12:30 UTC260
                continue;
7ff8adafe9e4atagen2026-10-22 12:30 UTC261
            };
7ff8adafe9e4atagen2026-10-22 12:30 UTC262
7ff8adafe9e4atagen2026-10-22 12:30 UTC263
            self.send_room_update(room_id, || RoomUpdate::Knocked {
7ff8adafe9e4atagen2026-10-22 12:30 UTC264
                room: room.clone(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC265
                updates: room_info.clone(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC266
            });
7ff8adafe9e4atagen2026-10-22 12:30 UTC267
7ff8adafe9e4atagen2026-10-22 12:30 UTC268
            let knock_state = &room_info.knock_state.events;
7ff8adafe9e4atagen2026-10-22 12:30 UTC269
            self.handle_sync_events(HandlerKind::StrippedState, Some(&room), knock_state).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC270
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC271
7ff8adafe9e4atagen2026-10-22 12:30 UTC272
        debug!("Ran event handlers in {:?}", now.elapsed());
7ff8adafe9e4atagen2026-10-22 12:30 UTC273
7ff8adafe9e4atagen2026-10-22 12:30 UTC274
        let now = Instant::now();
7ff8adafe9e4atagen2026-10-22 12:30 UTC275
7ff8adafe9e4atagen2026-10-22 12:30 UTC276
        // Construct notification event handler futures
7ff8adafe9e4atagen2026-10-22 12:30 UTC277
        let mut futures = Vec::new();
7ff8adafe9e4atagen2026-10-22 12:30 UTC278
        for handler in &*self.notification_handlers().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC279
            for (room_id, room_notifications) in notifications {
7ff8adafe9e4atagen2026-10-22 12:30 UTC280
                let Some(room) = self.get_room(room_id) else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC281
                    warn!(?room_id, "Can't call notification handler, room not found");
7ff8adafe9e4atagen2026-10-22 12:30 UTC282
                    continue;
7ff8adafe9e4atagen2026-10-22 12:30 UTC283
                };
7ff8adafe9e4atagen2026-10-22 12:30 UTC284
7ff8adafe9e4atagen2026-10-22 12:30 UTC285
                futures.extend(room_notifications.iter().map(|notification| {
7ff8adafe9e4atagen2026-10-22 12:30 UTC286
                    (handler)(notification.clone(), room.clone(), self.clone())
7ff8adafe9e4atagen2026-10-22 12:30 UTC287
                }));
7ff8adafe9e4atagen2026-10-22 12:30 UTC288
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC289
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC290
7ff8adafe9e4atagen2026-10-22 12:30 UTC291
        // Run the notification handler futures with the
7ff8adafe9e4atagen2026-10-22 12:30 UTC292
        // `self.notification_handlers` lock no longer being held, in order.
7ff8adafe9e4atagen2026-10-22 12:30 UTC293
        for fut in futures {
7ff8adafe9e4atagen2026-10-22 12:30 UTC294
            fut.await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC295
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC296
7ff8adafe9e4atagen2026-10-22 12:30 UTC297
        debug!("Ran notification handlers in {:?}", now.elapsed());
7ff8adafe9e4atagen2026-10-22 12:30 UTC298
7ff8adafe9e4atagen2026-10-22 12:30 UTC299
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC300
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC301
7ff8adafe9e4atagen2026-10-22 12:30 UTC302
    fn send_room_update(&self, room_id: &RoomId, make_msg: impl FnOnce() -> RoomUpdate) {
7ff8adafe9e4atagen2026-10-22 12:30 UTC303
        if let btree_map::Entry::Occupied(entry) =
7ff8adafe9e4atagen2026-10-22 12:30 UTC304
            self.inner.room_update_channels.lock().unwrap().entry(room_id.to_owned())
7ff8adafe9e4atagen2026-10-22 12:30 UTC305
        {
7ff8adafe9e4atagen2026-10-22 12:30 UTC306
            let tx = entry.get();
7ff8adafe9e4atagen2026-10-22 12:30 UTC307
            if tx.receiver_count() == 0 {
7ff8adafe9e4atagen2026-10-22 12:30 UTC308
                entry.remove();
7ff8adafe9e4atagen2026-10-22 12:30 UTC309
            } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC310
                _ = tx.send(make_msg());
7ff8adafe9e4atagen2026-10-22 12:30 UTC311
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC312
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC313
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC314
7ff8adafe9e4atagen2026-10-22 12:30 UTC315
    async fn sleep() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC316
        sleep(Duration::from_secs(1)).await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC317
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC318
7ff8adafe9e4atagen2026-10-22 12:30 UTC319
    pub(crate) async fn sync_loop_helper(
7ff8adafe9e4atagen2026-10-22 12:30 UTC320
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC321
        sync_settings: &mut crate::config::SyncSettings,
7ff8adafe9e4atagen2026-10-22 12:30 UTC322
    ) -> Result<SyncResponse> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC323
        let response = self.sync_once(sync_settings.clone()).await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC324
7ff8adafe9e4atagen2026-10-22 12:30 UTC325
        match response {
7ff8adafe9e4atagen2026-10-22 12:30 UTC326
            Ok(r) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC327
                sync_settings.token = r.next_batch.clone().into();
7ff8adafe9e4atagen2026-10-22 12:30 UTC328
                Ok(r)
7ff8adafe9e4atagen2026-10-22 12:30 UTC329
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC330
            Err(e) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC331
                error!("Received an invalid response: {e}");
7ff8adafe9e4atagen2026-10-22 12:30 UTC332
                Err(e)
7ff8adafe9e4atagen2026-10-22 12:30 UTC333
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC334
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC335
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC336
7ff8adafe9e4atagen2026-10-22 12:30 UTC337
    pub(crate) async fn delay_sync(last_sync_time: &mut Option<Instant>) {
7ff8adafe9e4atagen2026-10-22 12:30 UTC338
        let now = Instant::now();
7ff8adafe9e4atagen2026-10-22 12:30 UTC339
7ff8adafe9e4atagen2026-10-22 12:30 UTC340
        // If the last sync happened less than a second ago, sleep for a
7ff8adafe9e4atagen2026-10-22 12:30 UTC341
        // while to not hammer out requests if the server doesn't respect
7ff8adafe9e4atagen2026-10-22 12:30 UTC342
        // the sync timeout.
7ff8adafe9e4atagen2026-10-22 12:30 UTC343
        if let Some(t) = last_sync_time
7ff8adafe9e4atagen2026-10-22 12:30 UTC344
            && now - *t <= Duration::from_secs(1)
7ff8adafe9e4atagen2026-10-22 12:30 UTC345
        {
7ff8adafe9e4atagen2026-10-22 12:30 UTC346
            Self::sleep().await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC347
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC348
7ff8adafe9e4atagen2026-10-22 12:30 UTC349
        *last_sync_time = Some(now);
7ff8adafe9e4atagen2026-10-22 12:30 UTC350
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC351
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC352
7ff8adafe9e4atagen2026-10-22 12:30 UTC353
/// Call `LatestEvents::listen_to_room` for rooms in `response`.
7ff8adafe9e4atagen2026-10-22 12:30 UTC354
///
7ff8adafe9e4atagen2026-10-22 12:30 UTC355
/// That way, the latest event is computed and updated for all rooms receiving
7ff8adafe9e4atagen2026-10-22 12:30 UTC356
/// an update from the sync.
7ff8adafe9e4atagen2026-10-22 12:30 UTC357
#[instrument(skip_all)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC358
pub(crate) async fn subscribe_to_room_latest_events<'a, R>(client: &'a Client, room_ids: R)
7ff8adafe9e4atagen2026-10-22 12:30 UTC359
where
7ff8adafe9e4atagen2026-10-22 12:30 UTC360
    R: Iterator<Item = &'a OwnedRoomId>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC361
{
7ff8adafe9e4atagen2026-10-22 12:30 UTC362
    if !client.event_cache().has_subscribed() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC363
        return;
7ff8adafe9e4atagen2026-10-22 12:30 UTC364
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC365
7ff8adafe9e4atagen2026-10-22 12:30 UTC366
    let latest_events = client.latest_events().await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC367
7ff8adafe9e4atagen2026-10-22 12:30 UTC368
    for room_id in room_ids {
7ff8adafe9e4atagen2026-10-22 12:30 UTC369
        if let Err(error) = latest_events.listen_to_room(room_id).await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC370
            error!(?error, ?room_id, "Failed to listen to the latest event for this room");
7ff8adafe9e4atagen2026-10-22 12:30 UTC371
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC372
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC373
}