atagen

macltok

public
Star0
Watch0
2c13c8db61741baf208b9c98b78c4a9e97d1fe61
3
ssh://brewery@pls.lobotomise.me:33/atagen/macltok.git

Blame: src/transport.rs

96fdf90aacd6atagen2026-09-08 08:24 UTC1
use std::{
121a56ce119datagen2026-10-24 06:42 UTC2
    collections::HashSet,
96fdf90aacd6atagen2026-09-08 08:24 UTC3
    fmt,
31ce2453c82fatagen2026-09-24 13:07 UTC4
    future::Future,
96fdf90aacd6atagen2026-09-08 08:24 UTC5
    pin::Pin,
80381e06a573atagen2026-10-25 03:02 UTC6
    sync::{Arc, Mutex, Weak},
96fdf90aacd6atagen2026-09-08 08:24 UTC7
    task::{Context, Poll},
96fdf90aacd6atagen2026-09-08 08:24 UTC8
};
96fdf90aacd6atagen2026-09-08 08:24 UTC9
96fdf90aacd6atagen2026-09-08 08:24 UTC10
use matrix_sdk::{Client, RoomMemberships};
96fdf90aacd6atagen2026-09-08 08:24 UTC11
use matrix_sdk_ui::{
96fdf90aacd6atagen2026-09-08 08:24 UTC12
    notification_client::{
96fdf90aacd6atagen2026-09-08 08:24 UTC13
        NotificationClient, NotificationProcessSetup, NotificationStatus, RawNotificationEvent,
96fdf90aacd6atagen2026-09-08 08:24 UTC14
    },
31ce2453c82fatagen2026-09-24 13:07 UTC15
    spaces::{SpaceRoomList, SpaceService, room_list::SpaceRoomListPaginationState},
96fdf90aacd6atagen2026-09-08 08:24 UTC16
    sync_service::SyncService,
96fdf90aacd6atagen2026-09-08 08:24 UTC17
};
96fdf90aacd6atagen2026-09-08 08:24 UTC18
use ruma::{
5d2974de5826atagen2026-10-08 11:29 UTC19
    EventId, OwnedDeviceId, OwnedEventId, OwnedMxcUri, OwnedRoomId, OwnedRoomOrAliasId,
31ce2453c82fatagen2026-09-24 13:07 UTC20
    OwnedServerName, OwnedTransactionId, OwnedUserId, RoomId, UserId,
96fdf90aacd6atagen2026-09-08 08:24 UTC21
    events::{
31ce2453c82fatagen2026-09-24 13:07 UTC22
        AnyGlobalAccountDataEventContent, AnyRoomAccountDataEvent, AnyRoomAccountDataEventContent,
31ce2453c82fatagen2026-09-24 13:07 UTC23
        GlobalAccountDataEventType, RoomAccountDataEventType,
96fdf90aacd6atagen2026-09-08 08:24 UTC24
    },
96fdf90aacd6atagen2026-09-08 08:24 UTC25
    serde::Raw,
96fdf90aacd6atagen2026-09-08 08:24 UTC26
};
96fdf90aacd6atagen2026-09-08 08:24 UTC27
use tokio::sync::{OnceCell, mpsc, oneshot, watch};
96fdf90aacd6atagen2026-09-08 08:24 UTC28
96fdf90aacd6atagen2026-09-08 08:24 UTC29
use crate::{
31ce2453c82fatagen2026-09-24 13:07 UTC30
    AccountCommand, AttachmentUpload, Command, CommandOutput, ConnectionState, CreateRoomOptions,
89bc9f8d246datagen2026-10-24 13:40 UTC31
    EncryptionCommand, EncryptionState, Error, ErrorKind, Event, EventContext, EventContextCommand,
80381e06a573atagen2026-10-25 03:02 UTC32
    EventContextPage, EventRef, HistoryRepairBudget, HistoryRepairCommand, HistoryRepairPolicy,
80381e06a573atagen2026-10-25 03:02 UTC33
    HistoryRepairReport, ImagePackSource, ImagePacks, InteractiveAuth, InteractiveAuthOutcome,
80381e06a573atagen2026-10-25 03:02 UTC34
    LifecycleCommand, LinkPreview, Media, MediaCommand, MediaId, MediaLocation, MediaSize,
80381e06a573atagen2026-10-25 03:02 UTC35
    MemberFilter, MemberSummary, MentionKey, MentionsBoundary, MentionsCommand, MentionsPage,
80381e06a573atagen2026-10-25 03:02 UTC36
    MessageContent, MessageEditContent, NotificationCommand, OwnDevice, PackCommand, PackImageInfo,
80381e06a573atagen2026-10-25 03:02 UTC37
    PackUsage, Patch, PowerLevelChanges, Profile, PushNotification, PushNotificationEvent,
80381e06a573atagen2026-10-25 03:02 UTC38
    PushNotificationStatus, RoomCommand, RoomListFilter, RoomNotificationMode,
80381e06a573atagen2026-10-25 03:02 UTC39
    RoomNotificationSettings, RoomPowerLevels, RoomSummary, SavedMentionContext, SpaceChild,
80381e06a573atagen2026-10-25 03:02 UTC40
    TimelineCommand, TimelineId, VerificationAction, VerificationTarget,
96fdf90aacd6atagen2026-09-08 08:24 UTC41
    encryption::encryption_state,
a250c7fa43fbatagen2026-09-26 13:35 UTC42
    engine::{MediaService, StoreLock},
96fdf90aacd6atagen2026-09-08 08:24 UTC43
    model::{PushNotificationRoom, PushNotificationSender},
0291516d40b5atagen2026-10-22 08:31 UTC44
    normalise::{image_packs, member_summary, room_summaries, space_child, url_previews_allowed},
96fdf90aacd6atagen2026-09-08 08:24 UTC45
};
96fdf90aacd6atagen2026-09-08 08:24 UTC46
96fdf90aacd6atagen2026-09-08 08:24 UTC47
#[derive(Clone)]
96fdf90aacd6atagen2026-09-08 08:24 UTC48
pub struct ClientHandle {
31ce2453c82fatagen2026-09-24 13:07 UTC49
    requests: mpsc::Sender<Request>,
96fdf90aacd6atagen2026-09-08 08:24 UTC50
    client: Client,
96fdf90aacd6atagen2026-09-08 08:24 UTC51
    connection: watch::Receiver<ConnectionState>,
96fdf90aacd6atagen2026-09-08 08:24 UTC52
    notification_client: Arc<OnceCell<NotificationClient>>,
96fdf90aacd6atagen2026-09-08 08:24 UTC53
    sync_service: Arc<SyncService>,
31ce2453c82fatagen2026-09-24 13:07 UTC54
    media: Arc<MediaService>,
31ce2453c82fatagen2026-09-24 13:07 UTC55
    spaces: Arc<OnceCell<SpaceService>>,
121a56ce119datagen2026-10-24 06:42 UTC56
    members_checked: Arc<Mutex<HashSet<OwnedRoomId>>>,
80381e06a573atagen2026-10-25 03:02 UTC57
    publication: Arc<PublicationRegistry>,
a250c7fa43fbatagen2026-09-26 13:35 UTC58
    _store_lock: Option<Arc<StoreLock>>,
96fdf90aacd6atagen2026-09-08 08:24 UTC59
}
96fdf90aacd6atagen2026-09-08 08:24 UTC60
96fdf90aacd6atagen2026-09-08 08:24 UTC61
impl fmt::Debug for ClientHandle {
96fdf90aacd6atagen2026-09-08 08:24 UTC62
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
96fdf90aacd6atagen2026-09-08 08:24 UTC63
        formatter
96fdf90aacd6atagen2026-09-08 08:24 UTC64
            .debug_struct("ClientHandle")
96fdf90aacd6atagen2026-09-08 08:24 UTC65
            .finish_non_exhaustive()
96fdf90aacd6atagen2026-09-08 08:24 UTC66
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC67
}
96fdf90aacd6atagen2026-09-08 08:24 UTC68
31ce2453c82fatagen2026-09-24 13:07 UTC69
/// a command's answer. drop it if you don't care, the command still runs
31ce2453c82fatagen2026-09-24 13:07 UTC70
#[derive(Debug)]
31ce2453c82fatagen2026-09-24 13:07 UTC71
#[must_use = "dropping it is fine but then you'll never know"]
31ce2453c82fatagen2026-09-24 13:07 UTC72
pub struct Pending(oneshot::Receiver<Result<CommandOutput, Error>>);
31ce2453c82fatagen2026-09-24 13:07 UTC73
31ce2453c82fatagen2026-09-24 13:07 UTC74
impl Future for Pending {
31ce2453c82fatagen2026-09-24 13:07 UTC75
    type Output = Result<CommandOutput, Error>;
31ce2453c82fatagen2026-09-24 13:07 UTC76
31ce2453c82fatagen2026-09-24 13:07 UTC77
    fn poll(mut self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
31ce2453c82fatagen2026-09-24 13:07 UTC78
        Pin::new(&mut self.0)
31ce2453c82fatagen2026-09-24 13:07 UTC79
            .poll(context)
31ce2453c82fatagen2026-09-24 13:07 UTC80
            .map(|result| result.unwrap_or_else(|_| Err(Error::interrupted())))
31ce2453c82fatagen2026-09-24 13:07 UTC81
    }
31ce2453c82fatagen2026-09-24 13:07 UTC82
}
31ce2453c82fatagen2026-09-24 13:07 UTC83
96fdf90aacd6atagen2026-09-08 08:24 UTC84
impl ClientHandle {
80381e06a573atagen2026-10-25 03:02 UTC85
    pub(crate) fn register_publication_mirror(&self) -> PublicationRegistration {
80381e06a573atagen2026-10-25 03:02 UTC86
        self.publication.register_mirror()
80381e06a573atagen2026-10-25 03:02 UTC87
    }
80381e06a573atagen2026-10-25 03:02 UTC88
f12377bce9b9atagen2026-09-26 12:32 UTC89
    /// current credentials, it holds secrets. the engine saves it for you unless the
f12377bce9b9atagen2026-09-26 12:32 UTC90
    /// session was built `without_persistence`
62fe32759529atagen2026-09-26 08:50 UTC91
    #[cfg(any(feature = "sqlite", feature = "indexeddb"))]
96fdf90aacd6atagen2026-09-08 08:24 UTC92
    pub fn session_record(&self) -> Result<crate::SessionRecord, crate::AccountError> {
96fdf90aacd6atagen2026-09-08 08:24 UTC93
        crate::account::session_record(&self.client)
96fdf90aacd6atagen2026-09-08 08:24 UTC94
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC95
5b21b919d1e6atagen2026-09-27 11:47 UTC96
    /// the bearer token. it rotates on every refresh so ask again after a 401 rather than
5b21b919d1e6atagen2026-09-27 11:47 UTC97
    /// keeping it, and keep it in memory and away from all but the homeserver's media endpoints
5b21b919d1e6atagen2026-09-27 11:47 UTC98
    pub fn access_token(&self) -> Option<String> {
5b21b919d1e6atagen2026-09-27 11:47 UTC99
        self.client.access_token()
5b21b919d1e6atagen2026-09-27 11:47 UTC100
    }
5b21b919d1e6atagen2026-09-27 11:47 UTC101
31ce2453c82fatagen2026-09-24 13:07 UTC102
    /// queue a command, waiting for room in the queue if there isn't any
31ce2453c82fatagen2026-09-24 13:07 UTC103
    pub async fn send(&self, command: Command) -> Result<Pending, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC104
        let (request, pending) = Request::new(command);
31ce2453c82fatagen2026-09-24 13:07 UTC105
        self.requests
31ce2453c82fatagen2026-09-24 13:07 UTC106
            .send(request)
96fdf90aacd6atagen2026-09-08 08:24 UTC107
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC108
            .map_err(|_| Error::interrupted())?;
31ce2453c82fatagen2026-09-24 13:07 UTC109
        Ok(pending)
31ce2453c82fatagen2026-09-24 13:07 UTC110
    }
31ce2453c82fatagen2026-09-24 13:07 UTC111
31ce2453c82fatagen2026-09-24 13:07 UTC112
    /// queue a command or fail with `QueueFull` right now
31ce2453c82fatagen2026-09-24 13:07 UTC113
    pub fn try_send(&self, command: Command) -> Result<Pending, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC114
        let (request, pending) = Request::new(command);
31ce2453c82fatagen2026-09-24 13:07 UTC115
        self.requests
31ce2453c82fatagen2026-09-24 13:07 UTC116
            .try_send(request)
31ce2453c82fatagen2026-09-24 13:07 UTC117
            .map_err(|error| match error {
31ce2453c82fatagen2026-09-24 13:07 UTC118
                mpsc::error::TrySendError::Full(_) => {
31ce2453c82fatagen2026-09-24 13:07 UTC119
                    Error::new(ErrorKind::QueueFull, "the command queue is full")
31ce2453c82fatagen2026-09-24 13:07 UTC120
                }
31ce2453c82fatagen2026-09-24 13:07 UTC121
                mpsc::error::TrySendError::Closed(_) => Error::interrupted(),
31ce2453c82fatagen2026-09-24 13:07 UTC122
            })?;
31ce2453c82fatagen2026-09-24 13:07 UTC123
        Ok(pending)
31ce2453c82fatagen2026-09-24 13:07 UTC124
    }
31ce2453c82fatagen2026-09-24 13:07 UTC125
31ce2453c82fatagen2026-09-24 13:07 UTC126
    /// queue a command and wait for its answer. timeline sends answer once the
31ce2453c82fatagen2026-09-24 13:07 UTC127
    /// sdk send queue has them, delivery shows up later as the item's send state
31ce2453c82fatagen2026-09-24 13:07 UTC128
    pub async fn execute(&self, command: Command) -> Result<CommandOutput, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC129
        self.send(command).await?.await
31ce2453c82fatagen2026-09-24 13:07 UTC130
    }
31ce2453c82fatagen2026-09-24 13:07 UTC131
31ce2453c82fatagen2026-09-24 13:07 UTC132
    async fn run(&self, command: Command) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC133
        self.execute(command).await.map(drop)
31ce2453c82fatagen2026-09-24 13:07 UTC134
    }
31ce2453c82fatagen2026-09-24 13:07 UTC135
31ce2453c82fatagen2026-09-24 13:07 UTC136
    async fn run_for_room(&self, command: RoomCommand) -> Result<OwnedRoomId, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC137
        match self.execute(Command::Room(command)).await? {
31ce2453c82fatagen2026-09-24 13:07 UTC138
            CommandOutput::Room(room_id) => Ok(room_id),
31ce2453c82fatagen2026-09-24 13:07 UTC139
            output => Err(Error::new(
31ce2453c82fatagen2026-09-24 13:07 UTC140
                ErrorKind::Other,
31ce2453c82fatagen2026-09-24 13:07 UTC141
                format!("expected a room but got {output:?}"),
31ce2453c82fatagen2026-09-24 13:07 UTC142
            )),
31ce2453c82fatagen2026-09-24 13:07 UTC143
        }
31ce2453c82fatagen2026-09-24 13:07 UTC144
    }
31ce2453c82fatagen2026-09-24 13:07 UTC145
31ce2453c82fatagen2026-09-24 13:07 UTC146
    pub async fn shutdown(&self) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC147
        self.run(Command::Lifecycle(LifecycleCommand::Shutdown))
31ce2453c82fatagen2026-09-24 13:07 UTC148
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC149
    }
31ce2453c82fatagen2026-09-24 13:07 UTC150
31ce2453c82fatagen2026-09-24 13:07 UTC151
    pub async fn logout(&self) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC152
        self.run(Command::Account(AccountCommand::Logout)).await
96fdf90aacd6atagen2026-09-08 08:24 UTC153
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC154
96fdf90aacd6atagen2026-09-08 08:24 UTC155
    pub async fn delete_devices(
96fdf90aacd6atagen2026-09-08 08:24 UTC156
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC157
        device_ids: Vec<OwnedDeviceId>,
96fdf90aacd6atagen2026-09-08 08:24 UTC158
        auth: Option<InteractiveAuth>,
31ce2453c82fatagen2026-09-24 13:07 UTC159
    ) -> Result<InteractiveAuthOutcome, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC160
        match self
31ce2453c82fatagen2026-09-24 13:07 UTC161
            .run(Command::Account(AccountCommand::DeleteDevices {
96fdf90aacd6atagen2026-09-08 08:24 UTC162
                device_ids,
96fdf90aacd6atagen2026-09-08 08:24 UTC163
                auth,
31ce2453c82fatagen2026-09-24 13:07 UTC164
            }))
96fdf90aacd6atagen2026-09-08 08:24 UTC165
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC166
        {
31ce2453c82fatagen2026-09-24 13:07 UTC167
            Ok(()) => Ok(InteractiveAuthOutcome::Completed),
31ce2453c82fatagen2026-09-24 13:07 UTC168
            Err(Error {
31ce2453c82fatagen2026-09-24 13:07 UTC169
                challenge: Some(challenge),
31ce2453c82fatagen2026-09-24 13:07 UTC170
                ..
31ce2453c82fatagen2026-09-24 13:07 UTC171
            }) => Ok(InteractiveAuthOutcome::Challenge(*challenge)),
31ce2453c82fatagen2026-09-24 13:07 UTC172
            Err(error) => Err(error),
31ce2453c82fatagen2026-09-24 13:07 UTC173
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC174
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC175
96fdf90aacd6atagen2026-09-08 08:24 UTC176
    pub async fn rename_device(
96fdf90aacd6atagen2026-09-08 08:24 UTC177
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC178
        device_id: OwnedDeviceId,
96fdf90aacd6atagen2026-09-08 08:24 UTC179
        display_name: impl Into<String>,
31ce2453c82fatagen2026-09-24 13:07 UTC180
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC181
        self.run(Command::Account(AccountCommand::RenameDevice {
96fdf90aacd6atagen2026-09-08 08:24 UTC182
            device_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC183
            display_name: display_name.into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC184
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC185
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC186
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC187
31ce2453c82fatagen2026-09-24 13:07 UTC188
    pub async fn set_global_account_data(
31ce2453c82fatagen2026-09-24 13:07 UTC189
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC190
        event_type: GlobalAccountDataEventType,
31ce2453c82fatagen2026-09-24 13:07 UTC191
        content: Raw<AnyGlobalAccountDataEventContent>,
31ce2453c82fatagen2026-09-24 13:07 UTC192
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC193
        self.run(Command::Account(AccountCommand::SetAccountData {
31ce2453c82fatagen2026-09-24 13:07 UTC194
            event_type,
31ce2453c82fatagen2026-09-24 13:07 UTC195
            content,
31ce2453c82fatagen2026-09-24 13:07 UTC196
        }))
31ce2453c82fatagen2026-09-24 13:07 UTC197
        .await
31ce2453c82fatagen2026-09-24 13:07 UTC198
    }
31ce2453c82fatagen2026-09-24 13:07 UTC199
31ce2453c82fatagen2026-09-24 13:07 UTC200
    pub async fn set_room_account_data(
31ce2453c82fatagen2026-09-24 13:07 UTC201
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC202
        room_id: OwnedRoomId,
31ce2453c82fatagen2026-09-24 13:07 UTC203
        event_type: RoomAccountDataEventType,
31ce2453c82fatagen2026-09-24 13:07 UTC204
        content: Raw<AnyRoomAccountDataEventContent>,
31ce2453c82fatagen2026-09-24 13:07 UTC205
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC206
        self.run(Command::Room(RoomCommand::SetAccountData {
31ce2453c82fatagen2026-09-24 13:07 UTC207
            room_id,
31ce2453c82fatagen2026-09-24 13:07 UTC208
            event_type,
31ce2453c82fatagen2026-09-24 13:07 UTC209
            content,
31ce2453c82fatagen2026-09-24 13:07 UTC210
        }))
31ce2453c82fatagen2026-09-24 13:07 UTC211
        .await
31ce2453c82fatagen2026-09-24 13:07 UTC212
    }
31ce2453c82fatagen2026-09-24 13:07 UTC213
96fdf90aacd6atagen2026-09-08 08:24 UTC214
    pub async fn set_room_notification_mode(
96fdf90aacd6atagen2026-09-08 08:24 UTC215
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC216
        room_id: OwnedRoomId,
96fdf90aacd6atagen2026-09-08 08:24 UTC217
        mode: RoomNotificationMode,
31ce2453c82fatagen2026-09-24 13:07 UTC218
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC219
        self.run(Command::Notification(NotificationCommand::SetRoomMode {
96fdf90aacd6atagen2026-09-08 08:24 UTC220
            room_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC221
            mode,
96fdf90aacd6atagen2026-09-08 08:24 UTC222
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC223
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC224
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC225
31ce2453c82fatagen2026-09-24 13:07 UTC226
    pub async fn reset_room_notification_mode(&self, room_id: OwnedRoomId) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC227
        self.run(Command::Notification(NotificationCommand::ResetRoomMode(
96fdf90aacd6atagen2026-09-08 08:24 UTC228
            room_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC229
        )))
96fdf90aacd6atagen2026-09-08 08:24 UTC230
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC231
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC232
96fdf90aacd6atagen2026-09-08 08:24 UTC233
    pub async fn set_default_notification_mode(
96fdf90aacd6atagen2026-09-08 08:24 UTC234
        &self,
96fdf90aacd6atagen2026-09-08 08:24 UTC235
        encrypted: bool,
96fdf90aacd6atagen2026-09-08 08:24 UTC236
        one_to_one: bool,
96fdf90aacd6atagen2026-09-08 08:24 UTC237
        mode: RoomNotificationMode,
31ce2453c82fatagen2026-09-24 13:07 UTC238
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC239
        self.run(Command::Notification(NotificationCommand::SetDefaultMode {
96fdf90aacd6atagen2026-09-08 08:24 UTC240
            encrypted,
96fdf90aacd6atagen2026-09-08 08:24 UTC241
            one_to_one,
96fdf90aacd6atagen2026-09-08 08:24 UTC242
            mode,
96fdf90aacd6atagen2026-09-08 08:24 UTC243
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC244
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC245
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC246
31ce2453c82fatagen2026-09-24 13:07 UTC247
    pub async fn add_notification_keyword(&self, keyword: impl Into<String>) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC248
        self.run(Command::Notification(NotificationCommand::AddKeyword(
96fdf90aacd6atagen2026-09-08 08:24 UTC249
            keyword.into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC250
        )))
96fdf90aacd6atagen2026-09-08 08:24 UTC251
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC252
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC253
96fdf90aacd6atagen2026-09-08 08:24 UTC254
    pub async fn remove_notification_keyword(
96fdf90aacd6atagen2026-09-08 08:24 UTC255
        &self,
96fdf90aacd6atagen2026-09-08 08:24 UTC256
        keyword: impl Into<String>,
31ce2453c82fatagen2026-09-24 13:07 UTC257
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC258
        self.run(Command::Notification(NotificationCommand::RemoveKeyword(
96fdf90aacd6atagen2026-09-08 08:24 UTC259
            keyword.into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC260
        )))
96fdf90aacd6atagen2026-09-08 08:24 UTC261
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC262
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC263
31ce2453c82fatagen2026-09-24 13:07 UTC264
    pub async fn open_room(&self, room_id: OwnedRoomId) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC265
        self.run(Command::Room(RoomCommand::Open(room_id))).await
96fdf90aacd6atagen2026-09-08 08:24 UTC266
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC267
31ce2453c82fatagen2026-09-24 13:07 UTC268
    pub async fn close_room(&self, room_id: OwnedRoomId) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC269
        self.run(Command::Room(RoomCommand::Close(room_id))).await
96fdf90aacd6atagen2026-09-08 08:24 UTC270
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC271
96fdf90aacd6atagen2026-09-08 08:24 UTC272
    pub async fn open_thread(
96fdf90aacd6atagen2026-09-08 08:24 UTC273
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC274
        room_id: OwnedRoomId,
31ce2453c82fatagen2026-09-24 13:07 UTC275
        root_event_id: OwnedEventId,
31ce2453c82fatagen2026-09-24 13:07 UTC276
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC277
        self.run(Command::Timeline(TimelineCommand::OpenThread {
96fdf90aacd6atagen2026-09-08 08:24 UTC278
            room_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC279
            root_event_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC280
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC281
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC282
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC283
96fdf90aacd6atagen2026-09-08 08:24 UTC284
    pub async fn close_thread(
96fdf90aacd6atagen2026-09-08 08:24 UTC285
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC286
        room_id: OwnedRoomId,
31ce2453c82fatagen2026-09-24 13:07 UTC287
        root_event_id: OwnedEventId,
31ce2453c82fatagen2026-09-24 13:07 UTC288
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC289
        self.run(Command::Timeline(TimelineCommand::CloseThread {
96fdf90aacd6atagen2026-09-08 08:24 UTC290
            room_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC291
            root_event_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC292
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC293
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC294
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC295
fa5b4e09943aatagen2026-10-24 09:33 UTC296
    /// the room's pins as `TimelineId::Pinned`, kept current as they change. the room has to be open
fa5b4e09943aatagen2026-10-24 09:33 UTC297
    pub async fn open_pinned(&self, room_id: OwnedRoomId) -> Result<(), Error> {
fa5b4e09943aatagen2026-10-24 09:33 UTC298
        self.run(Command::Timeline(TimelineCommand::OpenPinned(room_id)))
fa5b4e09943aatagen2026-10-24 09:33 UTC299
            .await
fa5b4e09943aatagen2026-10-24 09:33 UTC300
    }
fa5b4e09943aatagen2026-10-24 09:33 UTC301
fa5b4e09943aatagen2026-10-24 09:33 UTC302
    pub async fn close_pinned(&self, room_id: OwnedRoomId) -> Result<(), Error> {
fa5b4e09943aatagen2026-10-24 09:33 UTC303
        self.run(Command::Timeline(TimelineCommand::ClosePinned(room_id)))
fa5b4e09943aatagen2026-10-24 09:33 UTC304
            .await
fa5b4e09943aatagen2026-10-24 09:33 UTC305
    }
fa5b4e09943aatagen2026-10-24 09:33 UTC306
fa5b4e09943aatagen2026-10-24 09:33 UTC307
    /// reads the server's list so a pin made elsewhere just before isn't dropped
fa5b4e09943aatagen2026-10-24 09:33 UTC308
    pub async fn set_pinned(
fa5b4e09943aatagen2026-10-24 09:33 UTC309
        &self,
fa5b4e09943aatagen2026-10-24 09:33 UTC310
        room_id: OwnedRoomId,
fa5b4e09943aatagen2026-10-24 09:33 UTC311
        event_id: OwnedEventId,
fa5b4e09943aatagen2026-10-24 09:33 UTC312
        pinned: bool,
fa5b4e09943aatagen2026-10-24 09:33 UTC313
    ) -> Result<(), Error> {
fa5b4e09943aatagen2026-10-24 09:33 UTC314
        self.run(Command::Room(RoomCommand::SetPinned {
fa5b4e09943aatagen2026-10-24 09:33 UTC315
            room_id,
fa5b4e09943aatagen2026-10-24 09:33 UTC316
            event_id,
fa5b4e09943aatagen2026-10-24 09:33 UTC317
            pinned,
fa5b4e09943aatagen2026-10-24 09:33 UTC318
        }))
fa5b4e09943aatagen2026-10-24 09:33 UTC319
        .await
fa5b4e09943aatagen2026-10-24 09:33 UTC320
    }
fa5b4e09943aatagen2026-10-24 09:33 UTC321
31ce2453c82fatagen2026-09-24 13:07 UTC322
    pub async fn resync(&self, timeline_id: TimelineId) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC323
        self.run(Command::Timeline(TimelineCommand::Resync { timeline_id }))
96fdf90aacd6atagen2026-09-08 08:24 UTC324
            .await
96fdf90aacd6atagen2026-09-08 08:24 UTC325
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC326
31ce2453c82fatagen2026-09-24 13:07 UTC327
    pub async fn paginate(&self, timeline_id: TimelineId, limit: u16) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC328
        self.run(Command::Timeline(TimelineCommand::Paginate {
31ce2453c82fatagen2026-09-24 13:07 UTC329
            timeline_id,
31ce2453c82fatagen2026-09-24 13:07 UTC330
            limit,
31ce2453c82fatagen2026-09-24 13:07 UTC331
        }))
31ce2453c82fatagen2026-09-24 13:07 UTC332
        .await
31ce2453c82fatagen2026-09-24 13:07 UTC333
    }
31ce2453c82fatagen2026-09-24 13:07 UTC334
80381e06a573atagen2026-10-25 03:02 UTC335
    pub async fn history_repair(
80381e06a573atagen2026-10-25 03:02 UTC336
        &self,
80381e06a573atagen2026-10-25 03:02 UTC337
        room_id: OwnedRoomId,
80381e06a573atagen2026-10-25 03:02 UTC338
        policy: Option<HistoryRepairPolicy>,
80381e06a573atagen2026-10-25 03:02 UTC339
        budget: HistoryRepairBudget,
80381e06a573atagen2026-10-25 03:02 UTC340
    ) -> Result<HistoryRepairReport, Error> {
80381e06a573atagen2026-10-25 03:02 UTC341
        match self
80381e06a573atagen2026-10-25 03:02 UTC342
            .execute(Command::HistoryRepair(HistoryRepairCommand::Start {
80381e06a573atagen2026-10-25 03:02 UTC343
                room_id,
80381e06a573atagen2026-10-25 03:02 UTC344
                policy,
80381e06a573atagen2026-10-25 03:02 UTC345
                budget,
80381e06a573atagen2026-10-25 03:02 UTC346
            }))
80381e06a573atagen2026-10-25 03:02 UTC347
            .await?
80381e06a573atagen2026-10-25 03:02 UTC348
        {
80381e06a573atagen2026-10-25 03:02 UTC349
            CommandOutput::HistoryRepair(report) => Ok(report),
80381e06a573atagen2026-10-25 03:02 UTC350
            output => Err(unexpected("history repair", &output)),
80381e06a573atagen2026-10-25 03:02 UTC351
        }
80381e06a573atagen2026-10-25 03:02 UTC352
    }
80381e06a573atagen2026-10-25 03:02 UTC353
80381e06a573atagen2026-10-25 03:02 UTC354
    pub async fn continue_history_repair(
80381e06a573atagen2026-10-25 03:02 UTC355
        &self,
80381e06a573atagen2026-10-25 03:02 UTC356
        room_id: OwnedRoomId,
80381e06a573atagen2026-10-25 03:02 UTC357
        budget: HistoryRepairBudget,
80381e06a573atagen2026-10-25 03:02 UTC358
    ) -> Result<HistoryRepairReport, Error> {
80381e06a573atagen2026-10-25 03:02 UTC359
        match self
80381e06a573atagen2026-10-25 03:02 UTC360
            .execute(Command::HistoryRepair(HistoryRepairCommand::Continue {
80381e06a573atagen2026-10-25 03:02 UTC361
                room_id,
80381e06a573atagen2026-10-25 03:02 UTC362
                budget,
80381e06a573atagen2026-10-25 03:02 UTC363
            }))
80381e06a573atagen2026-10-25 03:02 UTC364
            .await?
80381e06a573atagen2026-10-25 03:02 UTC365
        {
80381e06a573atagen2026-10-25 03:02 UTC366
            CommandOutput::HistoryRepair(report) => Ok(report),
80381e06a573atagen2026-10-25 03:02 UTC367
            output => Err(unexpected("history repair", &output)),
80381e06a573atagen2026-10-25 03:02 UTC368
        }
80381e06a573atagen2026-10-25 03:02 UTC369
    }
80381e06a573atagen2026-10-25 03:02 UTC370
80381e06a573atagen2026-10-25 03:02 UTC371
    pub async fn cancel_history_repair(&self, room_id: OwnedRoomId) -> Result<(), Error> {
80381e06a573atagen2026-10-25 03:02 UTC372
        self.run(Command::HistoryRepair(HistoryRepairCommand::Cancel {
80381e06a573atagen2026-10-25 03:02 UTC373
            room_id,
80381e06a573atagen2026-10-25 03:02 UTC374
        }))
80381e06a573atagen2026-10-25 03:02 UTC375
        .await
80381e06a573atagen2026-10-25 03:02 UTC376
    }
80381e06a573atagen2026-10-25 03:02 UTC377
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC378
    pub async fn fill_history_gap(
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC379
        &self,
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC380
        room_id: OwnedRoomId,
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC381
        gap_id: ruma::OwnedTransactionId,
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC382
    ) -> Result<(), Error> {
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC383
        self.run(Command::HistoryRepair(HistoryRepairCommand::FillGap {
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC384
            room_id,
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC385
            gap_id,
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC386
        }))
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC387
        .await
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC388
    }
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC389
80381e06a573atagen2026-10-25 03:02 UTC390
    pub async fn start_recent_history(
80381e06a573atagen2026-10-25 03:02 UTC391
        &self,
80381e06a573atagen2026-10-25 03:02 UTC392
        room_id: OwnedRoomId,
80381e06a573atagen2026-10-25 03:02 UTC393
    ) -> Result<HistoryRepairReport, Error> {
80381e06a573atagen2026-10-25 03:02 UTC394
        match self
80381e06a573atagen2026-10-25 03:02 UTC395
            .execute(Command::HistoryRepair(HistoryRepairCommand::StartRecent {
80381e06a573atagen2026-10-25 03:02 UTC396
                room_id,
80381e06a573atagen2026-10-25 03:02 UTC397
            }))
80381e06a573atagen2026-10-25 03:02 UTC398
            .await?
80381e06a573atagen2026-10-25 03:02 UTC399
        {
80381e06a573atagen2026-10-25 03:02 UTC400
            CommandOutput::HistoryRepair(report) => Ok(report),
80381e06a573atagen2026-10-25 03:02 UTC401
            output => Err(unexpected("history repair", &output)),
80381e06a573atagen2026-10-25 03:02 UTC402
        }
80381e06a573atagen2026-10-25 03:02 UTC403
    }
80381e06a573atagen2026-10-25 03:02 UTC404
80381e06a573atagen2026-10-25 03:02 UTC405
    pub async fn history_repair_policy(&self) -> Result<HistoryRepairPolicy, Error> {
80381e06a573atagen2026-10-25 03:02 UTC406
        match self
80381e06a573atagen2026-10-25 03:02 UTC407
            .execute(Command::HistoryRepair(HistoryRepairCommand::GetPolicy))
80381e06a573atagen2026-10-25 03:02 UTC408
            .await?
80381e06a573atagen2026-10-25 03:02 UTC409
        {
80381e06a573atagen2026-10-25 03:02 UTC410
            CommandOutput::HistoryRepairPolicy(policy) => Ok(policy),
80381e06a573atagen2026-10-25 03:02 UTC411
            output => Err(unexpected("history repair policy", &output)),
80381e06a573atagen2026-10-25 03:02 UTC412
        }
80381e06a573atagen2026-10-25 03:02 UTC413
    }
80381e06a573atagen2026-10-25 03:02 UTC414
80381e06a573atagen2026-10-25 03:02 UTC415
    pub async fn set_history_repair_policy(
80381e06a573atagen2026-10-25 03:02 UTC416
        &self,
80381e06a573atagen2026-10-25 03:02 UTC417
        policy: HistoryRepairPolicy,
80381e06a573atagen2026-10-25 03:02 UTC418
    ) -> Result<(), Error> {
80381e06a573atagen2026-10-25 03:02 UTC419
        self.run(Command::HistoryRepair(HistoryRepairCommand::SetPolicy(
80381e06a573atagen2026-10-25 03:02 UTC420
            policy,
80381e06a573atagen2026-10-25 03:02 UTC421
        )))
80381e06a573atagen2026-10-25 03:02 UTC422
        .await
80381e06a573atagen2026-10-25 03:02 UTC423
    }
80381e06a573atagen2026-10-25 03:02 UTC424
89bc9f8d246datagen2026-10-24 13:40 UTC425
    pub async fn event_context(
89bc9f8d246datagen2026-10-24 13:40 UTC426
        &self,
89bc9f8d246datagen2026-10-24 13:40 UTC427
        room_id: &RoomId,
89bc9f8d246datagen2026-10-24 13:40 UTC428
        event_id: &EventId,
89bc9f8d246datagen2026-10-24 13:40 UTC429
        before_limit: u32,
89bc9f8d246datagen2026-10-24 13:40 UTC430
        after_limit: u32,
89bc9f8d246datagen2026-10-24 13:40 UTC431
    ) -> Result<EventContext, Error> {
89bc9f8d246datagen2026-10-24 13:40 UTC432
        match self
89bc9f8d246datagen2026-10-24 13:40 UTC433
            .execute(Command::EventContext(EventContextCommand::Fetch {
89bc9f8d246datagen2026-10-24 13:40 UTC434
                room_id: room_id.to_owned(),
89bc9f8d246datagen2026-10-24 13:40 UTC435
                event_id: event_id.to_owned(),
89bc9f8d246datagen2026-10-24 13:40 UTC436
                before_limit: before_limit.min(50) as u16,
89bc9f8d246datagen2026-10-24 13:40 UTC437
                after_limit: after_limit.min(50) as u16,
89bc9f8d246datagen2026-10-24 13:40 UTC438
            }))
89bc9f8d246datagen2026-10-24 13:40 UTC439
            .await?
89bc9f8d246datagen2026-10-24 13:40 UTC440
        {
89bc9f8d246datagen2026-10-24 13:40 UTC441
            CommandOutput::EventContext(context) => Ok(context),
89bc9f8d246datagen2026-10-24 13:40 UTC442
            output => Err(unexpected("event context", &output)),
89bc9f8d246datagen2026-10-24 13:40 UTC443
        }
89bc9f8d246datagen2026-10-24 13:40 UTC444
    }
89bc9f8d246datagen2026-10-24 13:40 UTC445
89bc9f8d246datagen2026-10-24 13:40 UTC446
    pub async fn event_context_before(
89bc9f8d246datagen2026-10-24 13:40 UTC447
        &self,
89bc9f8d246datagen2026-10-24 13:40 UTC448
        room_id: &RoomId,
89bc9f8d246datagen2026-10-24 13:40 UTC449
        token: &str,
89bc9f8d246datagen2026-10-24 13:40 UTC450
        limit: u32,
89bc9f8d246datagen2026-10-24 13:40 UTC451
    ) -> Result<EventContextPage, Error> {
89bc9f8d246datagen2026-10-24 13:40 UTC452
        self.event_context_page(EventContextCommand::Before {
89bc9f8d246datagen2026-10-24 13:40 UTC453
            room_id: room_id.to_owned(),
89bc9f8d246datagen2026-10-24 13:40 UTC454
            limit: limit.min(50) as u16,
89bc9f8d246datagen2026-10-24 13:40 UTC455
            token: token.to_owned(),
89bc9f8d246datagen2026-10-24 13:40 UTC456
        })
89bc9f8d246datagen2026-10-24 13:40 UTC457
        .await
89bc9f8d246datagen2026-10-24 13:40 UTC458
    }
89bc9f8d246datagen2026-10-24 13:40 UTC459
89bc9f8d246datagen2026-10-24 13:40 UTC460
    pub async fn event_context_after(
89bc9f8d246datagen2026-10-24 13:40 UTC461
        &self,
89bc9f8d246datagen2026-10-24 13:40 UTC462
        room_id: &RoomId,
89bc9f8d246datagen2026-10-24 13:40 UTC463
        token: &str,
89bc9f8d246datagen2026-10-24 13:40 UTC464
        limit: u32,
89bc9f8d246datagen2026-10-24 13:40 UTC465
    ) -> Result<EventContextPage, Error> {
89bc9f8d246datagen2026-10-24 13:40 UTC466
        self.event_context_page(EventContextCommand::After {
89bc9f8d246datagen2026-10-24 13:40 UTC467
            room_id: room_id.to_owned(),
89bc9f8d246datagen2026-10-24 13:40 UTC468
            limit: limit.min(50) as u16,
89bc9f8d246datagen2026-10-24 13:40 UTC469
            token: token.to_owned(),
89bc9f8d246datagen2026-10-24 13:40 UTC470
        })
89bc9f8d246datagen2026-10-24 13:40 UTC471
        .await
89bc9f8d246datagen2026-10-24 13:40 UTC472
    }
89bc9f8d246datagen2026-10-24 13:40 UTC473
89bc9f8d246datagen2026-10-24 13:40 UTC474
    async fn event_context_page(
89bc9f8d246datagen2026-10-24 13:40 UTC475
        &self,
89bc9f8d246datagen2026-10-24 13:40 UTC476
        command: EventContextCommand,
89bc9f8d246datagen2026-10-24 13:40 UTC477
    ) -> Result<EventContextPage, Error> {
89bc9f8d246datagen2026-10-24 13:40 UTC478
        match self.execute(Command::EventContext(command)).await? {
89bc9f8d246datagen2026-10-24 13:40 UTC479
            CommandOutput::EventContextPage(page) => Ok(page),
89bc9f8d246datagen2026-10-24 13:40 UTC480
            output => Err(unexpected("event context page", &output)),
89bc9f8d246datagen2026-10-24 13:40 UTC481
        }
89bc9f8d246datagen2026-10-24 13:40 UTC482
    }
89bc9f8d246datagen2026-10-24 13:40 UTC483
cbb715e11963atagen2026-10-24 23:24 UTC484
    pub async fn mentions_page(
cbb715e11963atagen2026-10-24 23:24 UTC485
        &self,
cbb715e11963atagen2026-10-24 23:24 UTC486
        cursor: Option<String>,
cbb715e11963atagen2026-10-24 23:24 UTC487
        limit: u32,
cbb715e11963atagen2026-10-24 23:24 UTC488
    ) -> Result<MentionsPage, Error> {
cbb715e11963atagen2026-10-24 23:24 UTC489
        match self
cbb715e11963atagen2026-10-24 23:24 UTC490
            .execute(Command::Mentions(MentionsCommand::Page {
cbb715e11963atagen2026-10-24 23:24 UTC491
                cursor,
cbb715e11963atagen2026-10-24 23:24 UTC492
                limit: limit.min(50) as u16,
cbb715e11963atagen2026-10-24 23:24 UTC493
            }))
cbb715e11963atagen2026-10-24 23:24 UTC494
            .await?
cbb715e11963atagen2026-10-24 23:24 UTC495
        {
cbb715e11963atagen2026-10-24 23:24 UTC496
            CommandOutput::MentionsPage(page) => Ok(page),
cbb715e11963atagen2026-10-24 23:24 UTC497
            output => Err(unexpected("mentions page", &output)),
cbb715e11963atagen2026-10-24 23:24 UTC498
        }
cbb715e11963atagen2026-10-24 23:24 UTC499
    }
cbb715e11963atagen2026-10-24 23:24 UTC500
cbb715e11963atagen2026-10-24 23:24 UTC501
    pub async fn mention_context(
cbb715e11963atagen2026-10-24 23:24 UTC502
        &self,
cbb715e11963atagen2026-10-24 23:24 UTC503
        room_id: &RoomId,
cbb715e11963atagen2026-10-24 23:24 UTC504
        event_id: &EventId,
cbb715e11963atagen2026-10-24 23:24 UTC505
    ) -> Result<SavedMentionContext, Error> {
cbb715e11963atagen2026-10-24 23:24 UTC506
        self.mention_context_command(MentionsCommand::Context(MentionKey {
cbb715e11963atagen2026-10-24 23:24 UTC507
            room_id: room_id.to_owned(),
cbb715e11963atagen2026-10-24 23:24 UTC508
            event_id: event_id.to_owned(),
cbb715e11963atagen2026-10-24 23:24 UTC509
        }))
cbb715e11963atagen2026-10-24 23:24 UTC510
        .await
cbb715e11963atagen2026-10-24 23:24 UTC511
    }
cbb715e11963atagen2026-10-24 23:24 UTC512
cbb715e11963atagen2026-10-24 23:24 UTC513
    pub async fn retry_mention_context(
cbb715e11963atagen2026-10-24 23:24 UTC514
        &self,
cbb715e11963atagen2026-10-24 23:24 UTC515
        room_id: &RoomId,
cbb715e11963atagen2026-10-24 23:24 UTC516
        event_id: &EventId,
cbb715e11963atagen2026-10-24 23:24 UTC517
    ) -> Result<SavedMentionContext, Error> {
cbb715e11963atagen2026-10-24 23:24 UTC518
        self.mention_context_command(MentionsCommand::RetryContext(MentionKey {
cbb715e11963atagen2026-10-24 23:24 UTC519
            room_id: room_id.to_owned(),
cbb715e11963atagen2026-10-24 23:24 UTC520
            event_id: event_id.to_owned(),
cbb715e11963atagen2026-10-24 23:24 UTC521
        }))
cbb715e11963atagen2026-10-24 23:24 UTC522
        .await
cbb715e11963atagen2026-10-24 23:24 UTC523
    }
cbb715e11963atagen2026-10-24 23:24 UTC524
cbb715e11963atagen2026-10-24 23:24 UTC525
    async fn mention_context_command(
cbb715e11963atagen2026-10-24 23:24 UTC526
        &self,
cbb715e11963atagen2026-10-24 23:24 UTC527
        command: MentionsCommand,
cbb715e11963atagen2026-10-24 23:24 UTC528
    ) -> Result<SavedMentionContext, Error> {
cbb715e11963atagen2026-10-24 23:24 UTC529
        match self.execute(Command::Mentions(command)).await? {
cbb715e11963atagen2026-10-24 23:24 UTC530
            CommandOutput::MentionContext(context) => Ok(context),
cbb715e11963atagen2026-10-24 23:24 UTC531
            output => Err(unexpected("mention context", &output)),
cbb715e11963atagen2026-10-24 23:24 UTC532
        }
cbb715e11963atagen2026-10-24 23:24 UTC533
    }
cbb715e11963atagen2026-10-24 23:24 UTC534
cbb715e11963atagen2026-10-24 23:24 UTC535
    pub async fn set_mentions_read(&self, keys: Vec<MentionKey>, read: bool) -> Result<(), Error> {
cbb715e11963atagen2026-10-24 23:24 UTC536
        self.run(Command::Mentions(MentionsCommand::SetRead { keys, read }))
cbb715e11963atagen2026-10-24 23:24 UTC537
            .await
cbb715e11963atagen2026-10-24 23:24 UTC538
    }
cbb715e11963atagen2026-10-24 23:24 UTC539
cbb715e11963atagen2026-10-24 23:24 UTC540
    pub async fn mark_mentions_read_through(
cbb715e11963atagen2026-10-24 23:24 UTC541
        &self,
cbb715e11963atagen2026-10-24 23:24 UTC542
        boundary: MentionsBoundary,
cbb715e11963atagen2026-10-24 23:24 UTC543
    ) -> Result<(), Error> {
cbb715e11963atagen2026-10-24 23:24 UTC544
        self.run(Command::Mentions(MentionsCommand::ReadThrough(boundary)))
cbb715e11963atagen2026-10-24 23:24 UTC545
            .await
cbb715e11963atagen2026-10-24 23:24 UTC546
    }
cbb715e11963atagen2026-10-24 23:24 UTC547
cbb715e11963atagen2026-10-24 23:24 UTC548
    pub async fn recover_mentions(&self) -> Result<(), Error> {
cbb715e11963atagen2026-10-24 23:24 UTC549
        self.run(Command::Mentions(MentionsCommand::Recover)).await
cbb715e11963atagen2026-10-24 23:24 UTC550
    }
cbb715e11963atagen2026-10-24 23:24 UTC551
31ce2453c82fatagen2026-09-24 13:07 UTC552
    pub async fn send_message(
96fdf90aacd6atagen2026-09-08 08:24 UTC553
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC554
        timeline_id: TimelineId,
31ce2453c82fatagen2026-09-24 13:07 UTC555
        content: MessageContent,
31ce2453c82fatagen2026-09-24 13:07 UTC556
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC557
        self.run(Command::Timeline(TimelineCommand::Send {
31ce2453c82fatagen2026-09-24 13:07 UTC558
            timeline_id,
31ce2453c82fatagen2026-09-24 13:07 UTC559
            content,
31ce2453c82fatagen2026-09-24 13:07 UTC560
        }))
31ce2453c82fatagen2026-09-24 13:07 UTC561
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC562
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC563
31ce2453c82fatagen2026-09-24 13:07 UTC564
    pub async fn edit_message(
96fdf90aacd6atagen2026-09-08 08:24 UTC565
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC566
        timeline_id: TimelineId,
960ac0721079atagen2026-09-25 00:24 UTC567
        target: impl Into<EventRef>,
31ce2453c82fatagen2026-09-24 13:07 UTC568
        content: MessageEditContent,
31ce2453c82fatagen2026-09-24 13:07 UTC569
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC570
        self.run(Command::Timeline(TimelineCommand::Edit {
31ce2453c82fatagen2026-09-24 13:07 UTC571
            timeline_id,
960ac0721079atagen2026-09-25 00:24 UTC572
            target: target.into(),
31ce2453c82fatagen2026-09-24 13:07 UTC573
            content,
31ce2453c82fatagen2026-09-24 13:07 UTC574
        }))
31ce2453c82fatagen2026-09-24 13:07 UTC575
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC576
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC577
31ce2453c82fatagen2026-09-24 13:07 UTC578
    pub async fn redact(
96fdf90aacd6atagen2026-09-08 08:24 UTC579
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC580
        timeline_id: TimelineId,
960ac0721079atagen2026-09-25 00:24 UTC581
        target: impl Into<EventRef>,
31ce2453c82fatagen2026-09-24 13:07 UTC582
        reason: Option<String>,
31ce2453c82fatagen2026-09-24 13:07 UTC583
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC584
        self.run(Command::Timeline(TimelineCommand::Redact {
31ce2453c82fatagen2026-09-24 13:07 UTC585
            timeline_id,
960ac0721079atagen2026-09-25 00:24 UTC586
            target: target.into(),
31ce2453c82fatagen2026-09-24 13:07 UTC587
            reason,
31ce2453c82fatagen2026-09-24 13:07 UTC588
        }))
31ce2453c82fatagen2026-09-24 13:07 UTC589
        .await
31ce2453c82fatagen2026-09-24 13:07 UTC590
    }
31ce2453c82fatagen2026-09-24 13:07 UTC591
31ce2453c82fatagen2026-09-24 13:07 UTC592
    pub async fn toggle_reaction(
31ce2453c82fatagen2026-09-24 13:07 UTC593
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC594
        timeline_id: TimelineId,
960ac0721079atagen2026-09-25 00:24 UTC595
        target: impl Into<EventRef>,
31ce2453c82fatagen2026-09-24 13:07 UTC596
        key: impl Into<String>,
8fa8a7cde89catagen2026-10-08 12:39 UTC597
        shortcode: Option<String>,
31ce2453c82fatagen2026-09-24 13:07 UTC598
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC599
        self.run(Command::Timeline(TimelineCommand::ToggleReaction {
31ce2453c82fatagen2026-09-24 13:07 UTC600
            timeline_id,
960ac0721079atagen2026-09-25 00:24 UTC601
            target: target.into(),
31ce2453c82fatagen2026-09-24 13:07 UTC602
            key: key.into(),
8fa8a7cde89catagen2026-10-08 12:39 UTC603
            shortcode,
31ce2453c82fatagen2026-09-24 13:07 UTC604
        }))
31ce2453c82fatagen2026-09-24 13:07 UTC605
        .await
31ce2453c82fatagen2026-09-24 13:07 UTC606
    }
31ce2453c82fatagen2026-09-24 13:07 UTC607
31ce2453c82fatagen2026-09-24 13:07 UTC608
    pub async fn mark_read(
31ce2453c82fatagen2026-09-24 13:07 UTC609
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC610
        timeline_id: TimelineId,
31ce2453c82fatagen2026-09-24 13:07 UTC611
        event_id: OwnedEventId,
31ce2453c82fatagen2026-09-24 13:07 UTC612
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC613
        self.run(Command::Timeline(TimelineCommand::MarkRead {
31ce2453c82fatagen2026-09-24 13:07 UTC614
            timeline_id,
31ce2453c82fatagen2026-09-24 13:07 UTC615
            event_id,
31ce2453c82fatagen2026-09-24 13:07 UTC616
        }))
31ce2453c82fatagen2026-09-24 13:07 UTC617
        .await
31ce2453c82fatagen2026-09-24 13:07 UTC618
    }
31ce2453c82fatagen2026-09-24 13:07 UTC619
31ce2453c82fatagen2026-09-24 13:07 UTC620
    pub async fn set_typing(&self, room_id: OwnedRoomId, typing: bool) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC621
        self.run(Command::Room(RoomCommand::SetTyping { room_id, typing }))
96fdf90aacd6atagen2026-09-08 08:24 UTC622
            .await
96fdf90aacd6atagen2026-09-08 08:24 UTC623
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC624
38c4caecdb3datagen2026-09-26 13:35 UTC625
    pub async fn prepare_to_send(&self, room_id: OwnedRoomId) -> Result<(), Error> {
38c4caecdb3datagen2026-09-26 13:35 UTC626
        self.run(Command::Room(RoomCommand::PrepareToSend(room_id)))
38c4caecdb3datagen2026-09-26 13:35 UTC627
            .await
38c4caecdb3datagen2026-09-26 13:35 UTC628
    }
38c4caecdb3datagen2026-09-26 13:35 UTC629
31ce2453c82fatagen2026-09-24 13:07 UTC630
    pub async fn join_room(
96fdf90aacd6atagen2026-09-08 08:24 UTC631
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC632
        room: OwnedRoomOrAliasId,
31ce2453c82fatagen2026-09-24 13:07 UTC633
        via: Vec<OwnedServerName>,
31ce2453c82fatagen2026-09-24 13:07 UTC634
    ) -> Result<OwnedRoomId, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC635
        self.run_for_room(RoomCommand::Join { room, via }).await
31ce2453c82fatagen2026-09-24 13:07 UTC636
    }
31ce2453c82fatagen2026-09-24 13:07 UTC637
31ce2453c82fatagen2026-09-24 13:07 UTC638
    pub async fn leave_room(&self, room_id: OwnedRoomId) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC639
        self.run(Command::Room(RoomCommand::Leave(room_id))).await
31ce2453c82fatagen2026-09-24 13:07 UTC640
    }
31ce2453c82fatagen2026-09-24 13:07 UTC641
31ce2453c82fatagen2026-09-24 13:07 UTC642
    pub async fn forget_room(&self, room_id: OwnedRoomId) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC643
        self.run(Command::Room(RoomCommand::Forget(room_id))).await
31ce2453c82fatagen2026-09-24 13:07 UTC644
    }
31ce2453c82fatagen2026-09-24 13:07 UTC645
31ce2453c82fatagen2026-09-24 13:07 UTC646
    pub async fn accept_invite(&self, room_id: OwnedRoomId) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC647
        self.run(Command::Room(RoomCommand::AcceptInvite(room_id)))
31ce2453c82fatagen2026-09-24 13:07 UTC648
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC649
    }
31ce2453c82fatagen2026-09-24 13:07 UTC650
31ce2453c82fatagen2026-09-24 13:07 UTC651
    pub async fn reject_invite(&self, room_id: OwnedRoomId) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC652
        self.run(Command::Room(RoomCommand::RejectInvite(room_id)))
96fdf90aacd6atagen2026-09-08 08:24 UTC653
            .await
96fdf90aacd6atagen2026-09-08 08:24 UTC654
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC655
96fdf90aacd6atagen2026-09-08 08:24 UTC656
    pub async fn invite_user(
96fdf90aacd6atagen2026-09-08 08:24 UTC657
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC658
        room_id: OwnedRoomId,
31ce2453c82fatagen2026-09-24 13:07 UTC659
        user_id: OwnedUserId,
31ce2453c82fatagen2026-09-24 13:07 UTC660
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC661
        self.run(Command::Room(RoomCommand::InviteUser { room_id, user_id }))
31ce2453c82fatagen2026-09-24 13:07 UTC662
            .await
96fdf90aacd6atagen2026-09-08 08:24 UTC663
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC664
96fdf90aacd6atagen2026-09-08 08:24 UTC665
    pub async fn kick_user(
96fdf90aacd6atagen2026-09-08 08:24 UTC666
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC667
        room_id: OwnedRoomId,
31ce2453c82fatagen2026-09-24 13:07 UTC668
        user_id: OwnedUserId,
96fdf90aacd6atagen2026-09-08 08:24 UTC669
        reason: Option<String>,
31ce2453c82fatagen2026-09-24 13:07 UTC670
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC671
        self.run(Command::Room(RoomCommand::KickUser {
96fdf90aacd6atagen2026-09-08 08:24 UTC672
            room_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC673
            user_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC674
            reason,
96fdf90aacd6atagen2026-09-08 08:24 UTC675
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC676
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC677
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC678
96fdf90aacd6atagen2026-09-08 08:24 UTC679
    pub async fn ban_user(
96fdf90aacd6atagen2026-09-08 08:24 UTC680
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC681
        room_id: OwnedRoomId,
31ce2453c82fatagen2026-09-24 13:07 UTC682
        user_id: OwnedUserId,
96fdf90aacd6atagen2026-09-08 08:24 UTC683
        reason: Option<String>,
31ce2453c82fatagen2026-09-24 13:07 UTC684
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC685
        self.run(Command::Room(RoomCommand::BanUser {
96fdf90aacd6atagen2026-09-08 08:24 UTC686
            room_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC687
            user_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC688
            reason,
96fdf90aacd6atagen2026-09-08 08:24 UTC689
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC690
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC691
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC692
96fdf90aacd6atagen2026-09-08 08:24 UTC693
    pub async fn unban_user(
96fdf90aacd6atagen2026-09-08 08:24 UTC694
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC695
        room_id: OwnedRoomId,
31ce2453c82fatagen2026-09-24 13:07 UTC696
        user_id: OwnedUserId,
96fdf90aacd6atagen2026-09-08 08:24 UTC697
        reason: Option<String>,
31ce2453c82fatagen2026-09-24 13:07 UTC698
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC699
        self.run(Command::Room(RoomCommand::UnbanUser {
96fdf90aacd6atagen2026-09-08 08:24 UTC700
            room_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC701
            user_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC702
            reason,
96fdf90aacd6atagen2026-09-08 08:24 UTC703
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC704
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC705
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC706
96fdf90aacd6atagen2026-09-08 08:24 UTC707
    pub async fn set_power_levels(
96fdf90aacd6atagen2026-09-08 08:24 UTC708
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC709
        room_id: OwnedRoomId,
96fdf90aacd6atagen2026-09-08 08:24 UTC710
        changes: PowerLevelChanges,
31ce2453c82fatagen2026-09-24 13:07 UTC711
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC712
        self.run(Command::Room(RoomCommand::SetPowerLevels {
96fdf90aacd6atagen2026-09-08 08:24 UTC713
            room_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC714
            changes,
96fdf90aacd6atagen2026-09-08 08:24 UTC715
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC716
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC717
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC718
96fdf90aacd6atagen2026-09-08 08:24 UTC719
    pub async fn set_user_power_level(
96fdf90aacd6atagen2026-09-08 08:24 UTC720
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC721
        room_id: OwnedRoomId,
31ce2453c82fatagen2026-09-24 13:07 UTC722
        user_id: OwnedUserId,
96fdf90aacd6atagen2026-09-08 08:24 UTC723
        level: i64,
31ce2453c82fatagen2026-09-24 13:07 UTC724
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC725
        self.run(Command::Room(RoomCommand::SetUserPowerLevel {
96fdf90aacd6atagen2026-09-08 08:24 UTC726
            room_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC727
            user_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC728
            level,
96fdf90aacd6atagen2026-09-08 08:24 UTC729
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC730
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC731
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC732
31ce2453c82fatagen2026-09-24 13:07 UTC733
    pub async fn create_room(&self, options: CreateRoomOptions) -> Result<OwnedRoomId, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC734
        self.run_for_room(RoomCommand::Create(options)).await
96fdf90aacd6atagen2026-09-08 08:24 UTC735
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC736
31ce2453c82fatagen2026-09-24 13:07 UTC737
    /// hands back the existing dm with them if there is one
31ce2453c82fatagen2026-09-24 13:07 UTC738
    pub async fn create_direct(&self, user_id: OwnedUserId) -> Result<OwnedRoomId, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC739
        self.run_for_room(RoomCommand::CreateDirect(user_id)).await
96fdf90aacd6atagen2026-09-08 08:24 UTC740
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC741
96fdf90aacd6atagen2026-09-08 08:24 UTC742
    pub async fn cancel_send(
96fdf90aacd6atagen2026-09-08 08:24 UTC743
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC744
        room_id: OwnedRoomId,
31ce2453c82fatagen2026-09-24 13:07 UTC745
        transaction_id: OwnedTransactionId,
31ce2453c82fatagen2026-09-24 13:07 UTC746
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC747
        self.run(Command::Room(RoomCommand::CancelSend {
96fdf90aacd6atagen2026-09-08 08:24 UTC748
            room_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC749
            transaction_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC750
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC751
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC752
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC753
96fdf90aacd6atagen2026-09-08 08:24 UTC754
    pub async fn retry_send(
96fdf90aacd6atagen2026-09-08 08:24 UTC755
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC756
        room_id: OwnedRoomId,
31ce2453c82fatagen2026-09-24 13:07 UTC757
        transaction_id: OwnedTransactionId,
31ce2453c82fatagen2026-09-24 13:07 UTC758
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC759
        self.run(Command::Room(RoomCommand::RetrySend {
96fdf90aacd6atagen2026-09-08 08:24 UTC760
            room_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC761
            transaction_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC762
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC763
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC764
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC765
31ce2453c82fatagen2026-09-24 13:07 UTC766
    pub async fn upload(&self, upload: AttachmentUpload) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC767
        self.run(Command::Media(MediaCommand::Upload(upload))).await
96fdf90aacd6atagen2026-09-08 08:24 UTC768
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC769
31ce2453c82fatagen2026-09-24 13:07 UTC770
    pub async fn recover(&self, recovery_key: impl Into<String>) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC771
        self.run(Command::Encryption(EncryptionCommand::Recover(
31ce2453c82fatagen2026-09-24 13:07 UTC772
            recovery_key.into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC773
        )))
96fdf90aacd6atagen2026-09-08 08:24 UTC774
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC775
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC776
31ce2453c82fatagen2026-09-24 13:07 UTC777
    pub async fn create_key_backup(&self) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC778
        self.run(Command::Encryption(EncryptionCommand::CreateKeyBackup))
31ce2453c82fatagen2026-09-24 13:07 UTC779
            .await
96fdf90aacd6atagen2026-09-08 08:24 UTC780
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC781
31ce2453c82fatagen2026-09-24 13:07 UTC782
    pub async fn enable_key_backup(&self) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC783
        self.run(Command::Encryption(EncryptionCommand::EnableKeyBackup))
31ce2453c82fatagen2026-09-24 13:07 UTC784
            .await
96fdf90aacd6atagen2026-09-08 08:24 UTC785
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC786
31ce2453c82fatagen2026-09-24 13:07 UTC787
    pub async fn disable_key_backup(&self, delete_from_server: bool) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC788
        self.run(Command::Encryption(EncryptionCommand::DisableKeyBackup {
31ce2453c82fatagen2026-09-24 13:07 UTC789
            delete_from_server,
31ce2453c82fatagen2026-09-24 13:07 UTC790
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC791
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC792
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC793
31ce2453c82fatagen2026-09-24 13:07 UTC794
    pub async fn recover_key_backup(&self, recovery_key: impl Into<String>) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC795
        self.run(Command::Encryption(EncryptionCommand::RecoverKeyBackup(
31ce2453c82fatagen2026-09-24 13:07 UTC796
            recovery_key.into(),
31ce2453c82fatagen2026-09-24 13:07 UTC797
        )))
96fdf90aacd6atagen2026-09-08 08:24 UTC798
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC799
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC800
31ce2453c82fatagen2026-09-24 13:07 UTC801
    pub async fn start_verification(&self, target: VerificationTarget) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC802
        self.run(Command::Encryption(EncryptionCommand::StartVerification(
31ce2453c82fatagen2026-09-24 13:07 UTC803
            target,
31ce2453c82fatagen2026-09-24 13:07 UTC804
        )))
96fdf90aacd6atagen2026-09-08 08:24 UTC805
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC806
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC807
31ce2453c82fatagen2026-09-24 13:07 UTC808
    pub async fn verify(&self, action: VerificationAction) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC809
        self.run(Command::Encryption(EncryptionCommand::Verify(action)))
31ce2453c82fatagen2026-09-24 13:07 UTC810
            .await
96fdf90aacd6atagen2026-09-08 08:24 UTC811
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC812
5d2974de5826atagen2026-10-08 11:29 UTC813
    /// bytes for an avatar, or for media a retained timeline showed you. concurrent asks share
5d2974de5826atagen2026-10-08 11:29 UTC814
    /// one download
31ce2453c82fatagen2026-09-24 13:07 UTC815
    pub async fn media(&self, media_id: &MediaId, size: MediaSize) -> Result<Media, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC816
        self.media.timeline_media(media_id, size).await
31ce2453c82fatagen2026-09-24 13:07 UTC817
    }
31ce2453c82fatagen2026-09-24 13:07 UTC818
5b21b919d1e6atagen2026-09-27 11:47 UTC819
    /// where `media` would fetch from, for a host streaming big files itself. the byte cap
5b21b919d1e6atagen2026-09-27 11:47 UTC820
    /// doesn't apply since nothing is buffered
5b21b919d1e6atagen2026-09-27 11:47 UTC821
    pub async fn media_source(
5b21b919d1e6atagen2026-09-27 11:47 UTC822
        &self,
5b21b919d1e6atagen2026-09-27 11:47 UTC823
        media_id: &MediaId,
5b21b919d1e6atagen2026-09-27 11:47 UTC824
        size: MediaSize,
5b21b919d1e6atagen2026-09-27 11:47 UTC825
    ) -> Result<MediaLocation, Error> {
5b21b919d1e6atagen2026-09-27 11:47 UTC826
        self.media.media_location(media_id, size).await
5b21b919d1e6atagen2026-09-27 11:47 UTC827
    }
5b21b919d1e6atagen2026-09-27 11:47 UTC828
31ce2453c82fatagen2026-09-24 13:07 UTC829
    /// the space graph builds on first use and then keeps itself current
31ce2453c82fatagen2026-09-24 13:07 UTC830
    async fn spaces(&self) -> &SpaceService {
31ce2453c82fatagen2026-09-24 13:07 UTC831
        self.spaces
31ce2453c82fatagen2026-09-24 13:07 UTC832
            .get_or_init(|| SpaceService::new(self.client.clone()))
31ce2453c82fatagen2026-09-24 13:07 UTC833
            .await
96fdf90aacd6atagen2026-09-08 08:24 UTC834
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC835
96fdf90aacd6atagen2026-09-08 08:24 UTC836
    pub async fn rooms(&self) -> Vec<RoomSummary> {
31ce2453c82fatagen2026-09-24 13:07 UTC837
        room_summaries(&self.client, self.spaces().await).await
96fdf90aacd6atagen2026-09-08 08:24 UTC838
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC839
17e042fbc331atagen2026-10-08 12:39 UTC840
    pub async fn image_packs(&self) -> Result<ImagePacks, Error> {
17e042fbc331atagen2026-10-08 12:39 UTC841
        image_packs(&self.client).await
17e042fbc331atagen2026-10-08 12:39 UTC842
    }
17e042fbc331atagen2026-10-08 12:39 UTC843
0291516d40b5atagen2026-10-22 08:31 UTC844
    /// the homeserver's preview of a page, `None` when it has none. the homeserver sees every
0291516d40b5atagen2026-10-22 08:31 UTC845
    /// url asked about, so a host decides whether encrypted rooms ask at all
0291516d40b5atagen2026-10-22 08:31 UTC846
    pub async fn url_preview(&self, url: &str) -> Result<Option<LinkPreview>, Error> {
0291516d40b5atagen2026-10-22 08:31 UTC847
        self.media.url_preview(url).await
0291516d40b5atagen2026-10-22 08:31 UTC848
    }
0291516d40b5atagen2026-10-22 08:31 UTC849
0291516d40b5atagen2026-10-22 08:31 UTC850
    /// whether the room and the user's settings let link previews show here
0291516d40b5atagen2026-10-22 08:31 UTC851
    pub async fn url_previews_allowed(&self, room_id: &RoomId) -> Result<bool, Error> {
0291516d40b5atagen2026-10-22 08:31 UTC852
        url_previews_allowed(&self.room(room_id)?).await
0291516d40b5atagen2026-10-22 08:31 UTC853
    }
0291516d40b5atagen2026-10-22 08:31 UTC854
31ce2453c82fatagen2026-09-24 13:07 UTC855
    /// browse a space the way the server sees it, rooms you haven't joined included
31ce2453c82fatagen2026-09-24 13:07 UTC856
    pub async fn space_hierarchy(&self, space_id: OwnedRoomId) -> SpaceHierarchy {
31ce2453c82fatagen2026-09-24 13:07 UTC857
        SpaceHierarchy(SpaceRoomList::new(self.client.clone(), space_id).await)
31ce2453c82fatagen2026-09-24 13:07 UTC858
    }
31ce2453c82fatagen2026-09-24 13:07 UTC859
31ce2453c82fatagen2026-09-24 13:07 UTC860
    pub async fn own_profile(&self) -> Result<Profile, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC861
        let user_id = self
31ce2453c82fatagen2026-09-24 13:07 UTC862
            .client
31ce2453c82fatagen2026-09-24 13:07 UTC863
            .user_id()
31ce2453c82fatagen2026-09-24 13:07 UTC864
            .ok_or_else(|| Error::new(ErrorKind::Unauthenticated, "the client is not logged in"))?
31ce2453c82fatagen2026-09-24 13:07 UTC865
            .to_owned();
31ce2453c82fatagen2026-09-24 13:07 UTC866
        let response = self
31ce2453c82fatagen2026-09-24 13:07 UTC867
            .client
31ce2453c82fatagen2026-09-24 13:07 UTC868
            .account()
31ce2453c82fatagen2026-09-24 13:07 UTC869
            .fetch_user_profile()
31ce2453c82fatagen2026-09-24 13:07 UTC870
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC871
            .map_err(|source| Error::from_sdk("fetching the profile failed", source))?;
31ce2453c82fatagen2026-09-24 13:07 UTC872
        profile(user_id, &response)
31ce2453c82fatagen2026-09-24 13:07 UTC873
    }
31ce2453c82fatagen2026-09-24 13:07 UTC874
31ce2453c82fatagen2026-09-24 13:07 UTC875
    pub async fn user_profile(&self, user_id: &UserId) -> Result<Profile, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC876
        let response = self
31ce2453c82fatagen2026-09-24 13:07 UTC877
            .client
31ce2453c82fatagen2026-09-24 13:07 UTC878
            .account()
31ce2453c82fatagen2026-09-24 13:07 UTC879
            .fetch_user_profile_of(user_id)
31ce2453c82fatagen2026-09-24 13:07 UTC880
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC881
            .map_err(|source| Error::from_sdk("fetching the profile failed", source))?;
31ce2453c82fatagen2026-09-24 13:07 UTC882
        profile(user_id.to_owned(), &response)
31ce2453c82fatagen2026-09-24 13:07 UTC883
    }
31ce2453c82fatagen2026-09-24 13:07 UTC884
31ce2453c82fatagen2026-09-24 13:07 UTC885
    pub async fn set_display_name(&self, name: Option<String>) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC886
        self.run(Command::Account(AccountCommand::SetDisplayName(name)))
31ce2453c82fatagen2026-09-24 13:07 UTC887
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC888
    }
31ce2453c82fatagen2026-09-24 13:07 UTC889
31ce2453c82fatagen2026-09-24 13:07 UTC890
    pub async fn set_avatar(&self, avatar: Option<OwnedMxcUri>) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC891
        self.run(Command::Account(AccountCommand::SetAvatar(avatar)))
31ce2453c82fatagen2026-09-24 13:07 UTC892
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC893
    }
31ce2453c82fatagen2026-09-24 13:07 UTC894
31ce2453c82fatagen2026-09-24 13:07 UTC895
    pub async fn upload_avatar(
31ce2453c82fatagen2026-09-24 13:07 UTC896
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC897
        mimetype: impl Into<String>,
31ce2453c82fatagen2026-09-24 13:07 UTC898
        bytes: Vec<u8>,
31ce2453c82fatagen2026-09-24 13:07 UTC899
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC900
        self.run(Command::Account(AccountCommand::UploadAvatar {
31ce2453c82fatagen2026-09-24 13:07 UTC901
            mimetype: mimetype.into(),
31ce2453c82fatagen2026-09-24 13:07 UTC902
            bytes,
31ce2453c82fatagen2026-09-24 13:07 UTC903
        }))
31ce2453c82fatagen2026-09-24 13:07 UTC904
        .await
31ce2453c82fatagen2026-09-24 13:07 UTC905
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC906
9bd34e25bb86atagen2026-10-24 07:23 UTC907
    /// unencrypted, for room avatars and anything else that only needs the uri
9bd34e25bb86atagen2026-10-24 07:23 UTC908
    pub async fn upload_media(
9bd34e25bb86atagen2026-10-24 07:23 UTC909
        &self,
9bd34e25bb86atagen2026-10-24 07:23 UTC910
        mimetype: impl Into<String>,
9bd34e25bb86atagen2026-10-24 07:23 UTC911
        bytes: Vec<u8>,
9bd34e25bb86atagen2026-10-24 07:23 UTC912
    ) -> Result<OwnedMxcUri, Error> {
9bd34e25bb86atagen2026-10-24 07:23 UTC913
        let command = Command::Account(AccountCommand::UploadMedia {
9bd34e25bb86atagen2026-10-24 07:23 UTC914
            mimetype: mimetype.into(),
9bd34e25bb86atagen2026-10-24 07:23 UTC915
            bytes,
9bd34e25bb86atagen2026-10-24 07:23 UTC916
        });
9bd34e25bb86atagen2026-10-24 07:23 UTC917
        match self.execute(command).await? {
9bd34e25bb86atagen2026-10-24 07:23 UTC918
            CommandOutput::Mxc(uri) => Ok(uri),
9bd34e25bb86atagen2026-10-24 07:23 UTC919
            output => Err(Error::new(
9bd34e25bb86atagen2026-10-24 07:23 UTC920
                ErrorKind::Other,
9bd34e25bb86atagen2026-10-24 07:23 UTC921
                format!("expected an mxc but got {output:?}"),
9bd34e25bb86atagen2026-10-24 07:23 UTC922
            )),
9bd34e25bb86atagen2026-10-24 07:23 UTC923
        }
9bd34e25bb86atagen2026-10-24 07:23 UTC924
    }
9bd34e25bb86atagen2026-10-24 07:23 UTC925
9bd34e25bb86atagen2026-10-24 07:23 UTC926
    pub async fn ignore_user(&self, user_id: OwnedUserId) -> Result<(), Error> {
9bd34e25bb86atagen2026-10-24 07:23 UTC927
        self.run(Command::Account(AccountCommand::IgnoreUser(user_id)))
9bd34e25bb86atagen2026-10-24 07:23 UTC928
            .await
9bd34e25bb86atagen2026-10-24 07:23 UTC929
    }
9bd34e25bb86atagen2026-10-24 07:23 UTC930
9bd34e25bb86atagen2026-10-24 07:23 UTC931
    pub async fn unignore_user(&self, user_id: OwnedUserId) -> Result<(), Error> {
9bd34e25bb86atagen2026-10-24 07:23 UTC932
        self.run(Command::Account(AccountCommand::UnignoreUser(user_id)))
9bd34e25bb86atagen2026-10-24 07:23 UTC933
            .await
9bd34e25bb86atagen2026-10-24 07:23 UTC934
    }
9bd34e25bb86atagen2026-10-24 07:23 UTC935
9bd34e25bb86atagen2026-10-24 07:23 UTC936
    /// writes msc3230 orders so these spaces sort in exactly this sequence, touching as few as it can
9bd34e25bb86atagen2026-10-24 07:23 UTC937
    pub async fn reorder_spaces(&self, order: Vec<OwnedRoomId>) -> Result<(), Error> {
9bd34e25bb86atagen2026-10-24 07:23 UTC938
        self.run(Command::Account(AccountCommand::ReorderSpaces(order)))
9bd34e25bb86atagen2026-10-24 07:23 UTC939
            .await
9bd34e25bb86atagen2026-10-24 07:23 UTC940
    }
9bd34e25bb86atagen2026-10-24 07:23 UTC941
9bd34e25bb86atagen2026-10-24 07:23 UTC942
    /// adding m.favourite drops m.lowpriority, the way Element does
9bd34e25bb86atagen2026-10-24 07:23 UTC943
    pub async fn set_favourite(&self, room_id: OwnedRoomId, favourite: bool) -> Result<(), Error> {
9bd34e25bb86atagen2026-10-24 07:23 UTC944
        self.run(Command::Room(RoomCommand::SetFavourite {
9bd34e25bb86atagen2026-10-24 07:23 UTC945
            room_id,
9bd34e25bb86atagen2026-10-24 07:23 UTC946
            favourite,
9bd34e25bb86atagen2026-10-24 07:23 UTC947
        }))
9bd34e25bb86atagen2026-10-24 07:23 UTC948
        .await
9bd34e25bb86atagen2026-10-24 07:23 UTC949
    }
9bd34e25bb86atagen2026-10-24 07:23 UTC950
9bd34e25bb86atagen2026-10-24 07:23 UTC951
    /// None goes back to your global name
9bd34e25bb86atagen2026-10-24 07:23 UTC952
    pub async fn set_room_display_name(
9bd34e25bb86atagen2026-10-24 07:23 UTC953
        &self,
9bd34e25bb86atagen2026-10-24 07:23 UTC954
        room_id: OwnedRoomId,
9bd34e25bb86atagen2026-10-24 07:23 UTC955
        name: Option<String>,
9bd34e25bb86atagen2026-10-24 07:23 UTC956
    ) -> Result<(), Error> {
9bd34e25bb86atagen2026-10-24 07:23 UTC957
        self.run(Command::Room(RoomCommand::SetOwnDisplayName {
9bd34e25bb86atagen2026-10-24 07:23 UTC958
            room_id,
9bd34e25bb86atagen2026-10-24 07:23 UTC959
            name,
9bd34e25bb86atagen2026-10-24 07:23 UTC960
        }))
9bd34e25bb86atagen2026-10-24 07:23 UTC961
        .await
9bd34e25bb86atagen2026-10-24 07:23 UTC962
    }
9bd34e25bb86atagen2026-10-24 07:23 UTC963
9bd34e25bb86atagen2026-10-24 07:23 UTC964
    /// None goes back to your global avatar
9bd34e25bb86atagen2026-10-24 07:23 UTC965
    pub async fn set_room_avatar(
9bd34e25bb86atagen2026-10-24 07:23 UTC966
        &self,
9bd34e25bb86atagen2026-10-24 07:23 UTC967
        room_id: OwnedRoomId,
9bd34e25bb86atagen2026-10-24 07:23 UTC968
        avatar: Option<OwnedMxcUri>,
9bd34e25bb86atagen2026-10-24 07:23 UTC969
    ) -> Result<(), Error> {
9bd34e25bb86atagen2026-10-24 07:23 UTC970
        self.run(Command::Room(RoomCommand::SetOwnAvatar { room_id, avatar }))
9bd34e25bb86atagen2026-10-24 07:23 UTC971
            .await
9bd34e25bb86atagen2026-10-24 07:23 UTC972
    }
9bd34e25bb86atagen2026-10-24 07:23 UTC973
9bd34e25bb86atagen2026-10-24 07:23 UTC974
    /// muting drops them below what sending a message takes, unmuting puts a muted user back
9bd34e25bb86atagen2026-10-24 07:23 UTC975
    /// at users_default and leaves anyone else alone. `ErrorKind::Forbidden` if you can't
9bd34e25bb86atagen2026-10-24 07:23 UTC976
    pub async fn mute_user(
9bd34e25bb86atagen2026-10-24 07:23 UTC977
        &self,
9bd34e25bb86atagen2026-10-24 07:23 UTC978
        room_id: OwnedRoomId,
9bd34e25bb86atagen2026-10-24 07:23 UTC979
        user_id: OwnedUserId,
9bd34e25bb86atagen2026-10-24 07:23 UTC980
        muted: bool,
9bd34e25bb86atagen2026-10-24 07:23 UTC981
    ) -> Result<(), Error> {
9bd34e25bb86atagen2026-10-24 07:23 UTC982
        self.run(Command::Room(RoomCommand::MuteUser {
9bd34e25bb86atagen2026-10-24 07:23 UTC983
            room_id,
9bd34e25bb86atagen2026-10-24 07:23 UTC984
            user_id,
9bd34e25bb86atagen2026-10-24 07:23 UTC985
            muted,
9bd34e25bb86atagen2026-10-24 07:23 UTC986
        }))
9bd34e25bb86atagen2026-10-24 07:23 UTC987
        .await
9bd34e25bb86atagen2026-10-24 07:23 UTC988
    }
9bd34e25bb86atagen2026-10-24 07:23 UTC989
fa51490b6ae9atagen2026-10-24 08:41 UTC990
    /// body defaults to the shortcode
fa51490b6ae9atagen2026-10-24 08:41 UTC991
    pub async fn send_sticker(
fa51490b6ae9atagen2026-10-24 08:41 UTC992
        &self,
fa51490b6ae9atagen2026-10-24 08:41 UTC993
        room_id: OwnedRoomId,
fa51490b6ae9atagen2026-10-24 08:41 UTC994
        pack_id: String,
fa51490b6ae9atagen2026-10-24 08:41 UTC995
        shortcode: String,
fa51490b6ae9atagen2026-10-24 08:41 UTC996
    ) -> Result<(), Error> {
fa51490b6ae9atagen2026-10-24 08:41 UTC997
        self.run(Command::Pack(PackCommand::SendSticker {
fa51490b6ae9atagen2026-10-24 08:41 UTC998
            room_id,
fa51490b6ae9atagen2026-10-24 08:41 UTC999
            pack_id,
fa51490b6ae9atagen2026-10-24 08:41 UTC1000
            shortcode,
fa51490b6ae9atagen2026-10-24 08:41 UTC1001
        }))
fa51490b6ae9atagen2026-10-24 08:41 UTC1002
        .await
fa51490b6ae9atagen2026-10-24 08:41 UTC1003
    }
fa51490b6ae9atagen2026-10-24 08:41 UTC1004
fa51490b6ae9atagen2026-10-24 08:41 UTC1005
    /// fails if the state key already holds a pack
fa51490b6ae9atagen2026-10-24 08:41 UTC1006
    pub async fn create_room_pack(
fa51490b6ae9atagen2026-10-24 08:41 UTC1007
        &self,
fa51490b6ae9atagen2026-10-24 08:41 UTC1008
        room_id: OwnedRoomId,
fa51490b6ae9atagen2026-10-24 08:41 UTC1009
        state_key: String,
fa51490b6ae9atagen2026-10-24 08:41 UTC1010
        display_name: String,
fa51490b6ae9atagen2026-10-24 08:41 UTC1011
    ) -> Result<(), Error> {
fa51490b6ae9atagen2026-10-24 08:41 UTC1012
        self.run(Command::Pack(PackCommand::CreateRoomPack {
fa51490b6ae9atagen2026-10-24 08:41 UTC1013
            room_id,
fa51490b6ae9atagen2026-10-24 08:41 UTC1014
            state_key,
fa51490b6ae9atagen2026-10-24 08:41 UTC1015
            display_name,
fa51490b6ae9atagen2026-10-24 08:41 UTC1016
        }))
fa51490b6ae9atagen2026-10-24 08:41 UTC1017
        .await
fa51490b6ae9atagen2026-10-24 08:41 UTC1018
    }
fa51490b6ae9atagen2026-10-24 08:41 UTC1019
fa51490b6ae9atagen2026-10-24 08:41 UTC1020
    pub async fn delete_pack(&self, pack: ImagePackSource) -> Result<(), Error> {
fa51490b6ae9atagen2026-10-24 08:41 UTC1021
        self.run(Command::Pack(PackCommand::Delete(pack))).await
fa51490b6ae9atagen2026-10-24 08:41 UTC1022
    }
fa51490b6ae9atagen2026-10-24 08:41 UTC1023
fa51490b6ae9atagen2026-10-24 08:41 UTC1024
    pub async fn set_pack_meta(
fa51490b6ae9atagen2026-10-24 08:41 UTC1025
        &self,
fa51490b6ae9atagen2026-10-24 08:41 UTC1026
        pack: ImagePackSource,
fa51490b6ae9atagen2026-10-24 08:41 UTC1027
        display_name: Patch<String>,
fa51490b6ae9atagen2026-10-24 08:41 UTC1028
        avatar: Patch<OwnedMxcUri>,
fa51490b6ae9atagen2026-10-24 08:41 UTC1029
        usage: Patch<Vec<PackUsage>>,
fa51490b6ae9atagen2026-10-24 08:41 UTC1030
    ) -> Result<(), Error> {
fa51490b6ae9atagen2026-10-24 08:41 UTC1031
        self.run(Command::Pack(PackCommand::SetMeta {
fa51490b6ae9atagen2026-10-24 08:41 UTC1032
            pack,
fa51490b6ae9atagen2026-10-24 08:41 UTC1033
            display_name,
fa51490b6ae9atagen2026-10-24 08:41 UTC1034
            avatar,
fa51490b6ae9atagen2026-10-24 08:41 UTC1035
            usage,
fa51490b6ae9atagen2026-10-24 08:41 UTC1036
        }))
fa51490b6ae9atagen2026-10-24 08:41 UTC1037
        .await
fa51490b6ae9atagen2026-10-24 08:41 UTC1038
    }
fa51490b6ae9atagen2026-10-24 08:41 UTC1039
fa51490b6ae9atagen2026-10-24 08:41 UTC1040
    /// adds or replaces by shortcode. a replaced image keeps its info unless the url changed
fa51490b6ae9atagen2026-10-24 08:41 UTC1041
    pub async fn put_pack_image(
fa51490b6ae9atagen2026-10-24 08:41 UTC1042
        &self,
fa51490b6ae9atagen2026-10-24 08:41 UTC1043
        pack: ImagePackSource,
fa51490b6ae9atagen2026-10-24 08:41 UTC1044
        shortcode: String,
fa51490b6ae9atagen2026-10-24 08:41 UTC1045
        mxc: OwnedMxcUri,
fa51490b6ae9atagen2026-10-24 08:41 UTC1046
        body: Option<String>,
fa51490b6ae9atagen2026-10-24 08:41 UTC1047
        usage: Option<Vec<PackUsage>>,
fa51490b6ae9atagen2026-10-24 08:41 UTC1048
        info: Option<PackImageInfo>,
fa51490b6ae9atagen2026-10-24 08:41 UTC1049
    ) -> Result<(), Error> {
fa51490b6ae9atagen2026-10-24 08:41 UTC1050
        self.run(Command::Pack(PackCommand::PutImage {
fa51490b6ae9atagen2026-10-24 08:41 UTC1051
            pack,
fa51490b6ae9atagen2026-10-24 08:41 UTC1052
            shortcode,
fa51490b6ae9atagen2026-10-24 08:41 UTC1053
            mxc,
fa51490b6ae9atagen2026-10-24 08:41 UTC1054
            body,
fa51490b6ae9atagen2026-10-24 08:41 UTC1055
            usage,
fa51490b6ae9atagen2026-10-24 08:41 UTC1056
            info,
fa51490b6ae9atagen2026-10-24 08:41 UTC1057
        }))
fa51490b6ae9atagen2026-10-24 08:41 UTC1058
        .await
fa51490b6ae9atagen2026-10-24 08:41 UTC1059
    }
fa51490b6ae9atagen2026-10-24 08:41 UTC1060
fa51490b6ae9atagen2026-10-24 08:41 UTC1061
    pub async fn rename_pack_image(
fa51490b6ae9atagen2026-10-24 08:41 UTC1062
        &self,
fa51490b6ae9atagen2026-10-24 08:41 UTC1063
        pack: ImagePackSource,
fa51490b6ae9atagen2026-10-24 08:41 UTC1064
        from: String,
fa51490b6ae9atagen2026-10-24 08:41 UTC1065
        to: String,
fa51490b6ae9atagen2026-10-24 08:41 UTC1066
    ) -> Result<(), Error> {
fa51490b6ae9atagen2026-10-24 08:41 UTC1067
        self.run(Command::Pack(PackCommand::RenameImage { pack, from, to }))
fa51490b6ae9atagen2026-10-24 08:41 UTC1068
            .await
fa51490b6ae9atagen2026-10-24 08:41 UTC1069
    }
fa51490b6ae9atagen2026-10-24 08:41 UTC1070
fa51490b6ae9atagen2026-10-24 08:41 UTC1071
    pub async fn remove_pack_image(
fa51490b6ae9atagen2026-10-24 08:41 UTC1072
        &self,
fa51490b6ae9atagen2026-10-24 08:41 UTC1073
        pack: ImagePackSource,
fa51490b6ae9atagen2026-10-24 08:41 UTC1074
        shortcode: String,
fa51490b6ae9atagen2026-10-24 08:41 UTC1075
    ) -> Result<(), Error> {
fa51490b6ae9atagen2026-10-24 08:41 UTC1076
        self.run(Command::Pack(PackCommand::RemoveImage { pack, shortcode }))
fa51490b6ae9atagen2026-10-24 08:41 UTC1077
            .await
fa51490b6ae9atagen2026-10-24 08:41 UTC1078
    }
fa51490b6ae9atagen2026-10-24 08:41 UTC1079
fa51490b6ae9atagen2026-10-24 08:41 UTC1080
    pub async fn set_pack_enabled_everywhere(
fa51490b6ae9atagen2026-10-24 08:41 UTC1081
        &self,
fa51490b6ae9atagen2026-10-24 08:41 UTC1082
        room_id: OwnedRoomId,
fa51490b6ae9atagen2026-10-24 08:41 UTC1083
        state_key: String,
fa51490b6ae9atagen2026-10-24 08:41 UTC1084
        enabled: bool,
fa51490b6ae9atagen2026-10-24 08:41 UTC1085
    ) -> Result<(), Error> {
fa51490b6ae9atagen2026-10-24 08:41 UTC1086
        self.run(Command::Pack(PackCommand::SetEnabledEverywhere {
fa51490b6ae9atagen2026-10-24 08:41 UTC1087
            room_id,
fa51490b6ae9atagen2026-10-24 08:41 UTC1088
            state_key,
fa51490b6ae9atagen2026-10-24 08:41 UTC1089
            enabled,
fa51490b6ae9atagen2026-10-24 08:41 UTC1090
        }))
fa51490b6ae9atagen2026-10-24 08:41 UTC1091
        .await
fa51490b6ae9atagen2026-10-24 08:41 UTC1092
    }
fa51490b6ae9atagen2026-10-24 08:41 UTC1093
fa51490b6ae9atagen2026-10-24 08:41 UTC1094
    /// the same uri when it already lives on your homeserver
fa51490b6ae9atagen2026-10-24 08:41 UTC1095
    pub async fn rehost_media(&self, mxc: OwnedMxcUri) -> Result<OwnedMxcUri, Error> {
fa51490b6ae9atagen2026-10-24 08:41 UTC1096
        match self
fa51490b6ae9atagen2026-10-24 08:41 UTC1097
            .execute(Command::Pack(PackCommand::Rehost(mxc)))
fa51490b6ae9atagen2026-10-24 08:41 UTC1098
            .await?
fa51490b6ae9atagen2026-10-24 08:41 UTC1099
        {
fa51490b6ae9atagen2026-10-24 08:41 UTC1100
            CommandOutput::Mxc(uri) => Ok(uri),
fa51490b6ae9atagen2026-10-24 08:41 UTC1101
            output => Err(unexpected("an mxc", &output)),
fa51490b6ae9atagen2026-10-24 08:41 UTC1102
        }
fa51490b6ae9atagen2026-10-24 08:41 UTC1103
    }
fa51490b6ae9atagen2026-10-24 08:41 UTC1104
fa51490b6ae9atagen2026-10-24 08:41 UTC1105
    /// hands back the shortcode it ended up under
fa51490b6ae9atagen2026-10-24 08:41 UTC1106
    pub async fn clone_pack_image(
fa51490b6ae9atagen2026-10-24 08:41 UTC1107
        &self,
fa51490b6ae9atagen2026-10-24 08:41 UTC1108
        from_pack: String,
fa51490b6ae9atagen2026-10-24 08:41 UTC1109
        shortcode: String,
fa51490b6ae9atagen2026-10-24 08:41 UTC1110
        to: ImagePackSource,
fa51490b6ae9atagen2026-10-24 08:41 UTC1111
    ) -> Result<String, Error> {
2656c1d26d6batagen2026-10-25 05:44 UTC1112
        self.clone_pack_image_with_usage(from_pack, shortcode, to, None)
2656c1d26d6batagen2026-10-25 05:44 UTC1113
            .await
2656c1d26d6batagen2026-10-25 05:44 UTC1114
    }
2656c1d26d6batagen2026-10-25 05:44 UTC1115
2656c1d26d6batagen2026-10-25 05:44 UTC1116
    pub async fn clone_pack_image_with_usage(
2656c1d26d6batagen2026-10-25 05:44 UTC1117
        &self,
2656c1d26d6batagen2026-10-25 05:44 UTC1118
        from_pack: String,
2656c1d26d6batagen2026-10-25 05:44 UTC1119
        shortcode: String,
2656c1d26d6batagen2026-10-25 05:44 UTC1120
        to: ImagePackSource,
2656c1d26d6batagen2026-10-25 05:44 UTC1121
        usage: Option<Vec<PackUsage>>,
2656c1d26d6batagen2026-10-25 05:44 UTC1122
    ) -> Result<String, Error> {
fa51490b6ae9atagen2026-10-24 08:41 UTC1123
        let (mut shortcodes, _) = self
fa51490b6ae9atagen2026-10-24 08:41 UTC1124
            .run_clone(PackCommand::CloneImage {
fa51490b6ae9atagen2026-10-24 08:41 UTC1125
                from_pack,
fa51490b6ae9atagen2026-10-24 08:41 UTC1126
                shortcode,
fa51490b6ae9atagen2026-10-24 08:41 UTC1127
                to,
2656c1d26d6batagen2026-10-25 05:44 UTC1128
                usage,
fa51490b6ae9atagen2026-10-24 08:41 UTC1129
            })
fa51490b6ae9atagen2026-10-24 08:41 UTC1130
            .await?;
fa51490b6ae9atagen2026-10-24 08:41 UTC1131
        shortcodes
fa51490b6ae9atagen2026-10-24 08:41 UTC1132
            .pop()
fa51490b6ae9atagen2026-10-24 08:41 UTC1133
            .ok_or_else(|| unexpected("a shortcode", &CommandOutput::Done))
fa51490b6ae9atagen2026-10-24 08:41 UTC1134
    }
fa51490b6ae9atagen2026-10-24 08:41 UTC1135
fa51490b6ae9atagen2026-10-24 08:41 UTC1136
    /// hands back the shortcode it ended up under
fa51490b6ae9atagen2026-10-24 08:41 UTC1137
    pub async fn clone_sticker(
fa51490b6ae9atagen2026-10-24 08:41 UTC1138
        &self,
fa51490b6ae9atagen2026-10-24 08:41 UTC1139
        room_id: OwnedRoomId,
fa51490b6ae9atagen2026-10-24 08:41 UTC1140
        event_id: OwnedEventId,
fa51490b6ae9atagen2026-10-24 08:41 UTC1141
        to: ImagePackSource,
fa51490b6ae9atagen2026-10-24 08:41 UTC1142
        shortcode: String,
fa51490b6ae9atagen2026-10-24 08:41 UTC1143
    ) -> Result<String, Error> {
2656c1d26d6batagen2026-10-25 05:44 UTC1144
        self.clone_sticker_with_usage(room_id, event_id, to, shortcode, None)
2656c1d26d6batagen2026-10-25 05:44 UTC1145
            .await
2656c1d26d6batagen2026-10-25 05:44 UTC1146
    }
2656c1d26d6batagen2026-10-25 05:44 UTC1147
2656c1d26d6batagen2026-10-25 05:44 UTC1148
    pub async fn clone_sticker_with_usage(
2656c1d26d6batagen2026-10-25 05:44 UTC1149
        &self,
2656c1d26d6batagen2026-10-25 05:44 UTC1150
        room_id: OwnedRoomId,
2656c1d26d6batagen2026-10-25 05:44 UTC1151
        event_id: OwnedEventId,
2656c1d26d6batagen2026-10-25 05:44 UTC1152
        to: ImagePackSource,
2656c1d26d6batagen2026-10-25 05:44 UTC1153
        shortcode: String,
2656c1d26d6batagen2026-10-25 05:44 UTC1154
        usage: Option<Vec<PackUsage>>,
2656c1d26d6batagen2026-10-25 05:44 UTC1155
    ) -> Result<String, Error> {
fa51490b6ae9atagen2026-10-24 08:41 UTC1156
        let (mut shortcodes, _) = self
fa51490b6ae9atagen2026-10-24 08:41 UTC1157
            .run_clone(PackCommand::CloneSticker {
fa51490b6ae9atagen2026-10-24 08:41 UTC1158
                room_id,
fa51490b6ae9atagen2026-10-24 08:41 UTC1159
                event_id,
fa51490b6ae9atagen2026-10-24 08:41 UTC1160
                to,
fa51490b6ae9atagen2026-10-24 08:41 UTC1161
                shortcode,
2656c1d26d6batagen2026-10-25 05:44 UTC1162
                usage,
fa51490b6ae9atagen2026-10-24 08:41 UTC1163
            })
fa51490b6ae9atagen2026-10-24 08:41 UTC1164
            .await?;
fa51490b6ae9atagen2026-10-24 08:41 UTC1165
        shortcodes
fa51490b6ae9atagen2026-10-24 08:41 UTC1166
            .pop()
fa51490b6ae9atagen2026-10-24 08:41 UTC1167
            .ok_or_else(|| unexpected("a shortcode", &CommandOutput::Done))
fa51490b6ae9atagen2026-10-24 08:41 UTC1168
    }
fa51490b6ae9atagen2026-10-24 08:41 UTC1169
fa51490b6ae9atagen2026-10-24 08:41 UTC1170
    /// hands back the shortcodes that couldn't be fetched, everything else is written
fa51490b6ae9atagen2026-10-24 08:41 UTC1171
    pub async fn clone_pack(
fa51490b6ae9atagen2026-10-24 08:41 UTC1172
        &self,
fa51490b6ae9atagen2026-10-24 08:41 UTC1173
        from_pack: String,
fa51490b6ae9atagen2026-10-24 08:41 UTC1174
        to: ImagePackSource,
fa51490b6ae9atagen2026-10-24 08:41 UTC1175
        display_name: Option<String>,
fa51490b6ae9atagen2026-10-24 08:41 UTC1176
    ) -> Result<Vec<String>, Error> {
2656c1d26d6batagen2026-10-25 05:44 UTC1177
        self.clone_pack_with_usage(from_pack, to, display_name, None)
2656c1d26d6batagen2026-10-25 05:44 UTC1178
            .await
2656c1d26d6batagen2026-10-25 05:44 UTC1179
    }
2656c1d26d6batagen2026-10-25 05:44 UTC1180
2656c1d26d6batagen2026-10-25 05:44 UTC1181
    pub async fn clone_pack_with_usage(
2656c1d26d6batagen2026-10-25 05:44 UTC1182
        &self,
2656c1d26d6batagen2026-10-25 05:44 UTC1183
        from_pack: String,
2656c1d26d6batagen2026-10-25 05:44 UTC1184
        to: ImagePackSource,
2656c1d26d6batagen2026-10-25 05:44 UTC1185
        display_name: Option<String>,
2656c1d26d6batagen2026-10-25 05:44 UTC1186
        usage: Option<Vec<PackUsage>>,
2656c1d26d6batagen2026-10-25 05:44 UTC1187
    ) -> Result<Vec<String>, Error> {
fa51490b6ae9atagen2026-10-24 08:41 UTC1188
        let (_, skipped) = self
fa51490b6ae9atagen2026-10-24 08:41 UTC1189
            .run_clone(PackCommand::ClonePack {
fa51490b6ae9atagen2026-10-24 08:41 UTC1190
                from_pack,
fa51490b6ae9atagen2026-10-24 08:41 UTC1191
                to,
fa51490b6ae9atagen2026-10-24 08:41 UTC1192
                display_name,
2656c1d26d6batagen2026-10-25 05:44 UTC1193
                usage,
fa51490b6ae9atagen2026-10-24 08:41 UTC1194
            })
fa51490b6ae9atagen2026-10-24 08:41 UTC1195
            .await?;
fa51490b6ae9atagen2026-10-24 08:41 UTC1196
        Ok(skipped)
fa51490b6ae9atagen2026-10-24 08:41 UTC1197
    }
fa51490b6ae9atagen2026-10-24 08:41 UTC1198
fa51490b6ae9atagen2026-10-24 08:41 UTC1199
    async fn run_clone(&self, command: PackCommand) -> Result<(Vec<String>, Vec<String>), Error> {
fa51490b6ae9atagen2026-10-24 08:41 UTC1200
        match self.execute(Command::Pack(command)).await? {
fa51490b6ae9atagen2026-10-24 08:41 UTC1201
            CommandOutput::Cloned {
fa51490b6ae9atagen2026-10-24 08:41 UTC1202
                shortcodes,
fa51490b6ae9atagen2026-10-24 08:41 UTC1203
                skipped,
fa51490b6ae9atagen2026-10-24 08:41 UTC1204
            } => Ok((shortcodes, skipped)),
fa51490b6ae9atagen2026-10-24 08:41 UTC1205
            output => Err(unexpected("a clone", &output)),
fa51490b6ae9atagen2026-10-24 08:41 UTC1206
        }
fa51490b6ae9atagen2026-10-24 08:41 UTC1207
    }
fa51490b6ae9atagen2026-10-24 08:41 UTC1208
31ce2453c82fatagen2026-09-24 13:07 UTC1209
    pub async fn rooms_filtered(&self, filter: &RoomListFilter) -> Vec<RoomSummary> {
31ce2453c82fatagen2026-09-24 13:07 UTC1210
        let matcher = filter.matcher();
96fdf90aacd6atagen2026-09-08 08:24 UTC1211
        self.rooms()
96fdf90aacd6atagen2026-09-08 08:24 UTC1212
            .await
96fdf90aacd6atagen2026-09-08 08:24 UTC1213
            .into_iter()
31ce2453c82fatagen2026-09-24 13:07 UTC1214
            .filter(|room| matcher.matches(room))
96fdf90aacd6atagen2026-09-08 08:24 UTC1215
            .collect()
96fdf90aacd6atagen2026-09-08 08:24 UTC1216
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1217
31ce2453c82fatagen2026-09-24 13:07 UTC1218
    pub async fn devices(&self) -> Result<Vec<OwnDevice>, Error> {
96fdf90aacd6atagen2026-09-08 08:24 UTC1219
        let current_device_id = self.client.device_id();
96fdf90aacd6atagen2026-09-08 08:24 UTC1220
        Ok(self
96fdf90aacd6atagen2026-09-08 08:24 UTC1221
            .client
96fdf90aacd6atagen2026-09-08 08:24 UTC1222
            .devices()
96fdf90aacd6atagen2026-09-08 08:24 UTC1223
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC1224
            .map_err(|source| Error::from_http("listing devices failed", source))?
96fdf90aacd6atagen2026-09-08 08:24 UTC1225
            .devices
96fdf90aacd6atagen2026-09-08 08:24 UTC1226
            .into_iter()
96fdf90aacd6atagen2026-09-08 08:24 UTC1227
            .map(|device| OwnDevice {
96fdf90aacd6atagen2026-09-08 08:24 UTC1228
                current: current_device_id == Some(device.device_id.as_ref()),
96fdf90aacd6atagen2026-09-08 08:24 UTC1229
                device_id: device.device_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC1230
                display_name: device.display_name,
96fdf90aacd6atagen2026-09-08 08:24 UTC1231
                last_seen_ip: device.last_seen_ip,
96fdf90aacd6atagen2026-09-08 08:24 UTC1232
                last_seen_timestamp_ms: device.last_seen_ts.map(|timestamp| timestamp.get().into()),
96fdf90aacd6atagen2026-09-08 08:24 UTC1233
            })
96fdf90aacd6atagen2026-09-08 08:24 UTC1234
            .collect())
96fdf90aacd6atagen2026-09-08 08:24 UTC1235
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1236
31ce2453c82fatagen2026-09-24 13:07 UTC1237
    fn room(&self, room_id: &RoomId) -> Result<matrix_sdk::Room, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC1238
        self.client
31ce2453c82fatagen2026-09-24 13:07 UTC1239
            .get_room(room_id)
31ce2453c82fatagen2026-09-24 13:07 UTC1240
            .ok_or_else(Error::room_unknown)
31ce2453c82fatagen2026-09-24 13:07 UTC1241
    }
31ce2453c82fatagen2026-09-24 13:07 UTC1242
96fdf90aacd6atagen2026-09-08 08:24 UTC1243
    pub async fn room_notification_settings(
96fdf90aacd6atagen2026-09-08 08:24 UTC1244
        &self,
96fdf90aacd6atagen2026-09-08 08:24 UTC1245
        room_id: &RoomId,
31ce2453c82fatagen2026-09-24 13:07 UTC1246
    ) -> Result<Option<RoomNotificationSettings>, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC1247
        let room = self.room(room_id)?;
96fdf90aacd6atagen2026-09-08 08:24 UTC1248
        let custom = room.user_defined_notification_mode().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC1249
        let mode = match custom {
96fdf90aacd6atagen2026-09-08 08:24 UTC1250
            Some(mode) => mode,
96fdf90aacd6atagen2026-09-08 08:24 UTC1251
            None => match room.notification_mode().await {
96fdf90aacd6atagen2026-09-08 08:24 UTC1252
                Some(mode) => mode,
96fdf90aacd6atagen2026-09-08 08:24 UTC1253
                None => return Ok(None),
96fdf90aacd6atagen2026-09-08 08:24 UTC1254
            },
96fdf90aacd6atagen2026-09-08 08:24 UTC1255
        };
96fdf90aacd6atagen2026-09-08 08:24 UTC1256
        Ok(Some(RoomNotificationSettings {
96fdf90aacd6atagen2026-09-08 08:24 UTC1257
            mode: crate::normalise::notification_mode(mode),
96fdf90aacd6atagen2026-09-08 08:24 UTC1258
            custom: custom.is_some(),
96fdf90aacd6atagen2026-09-08 08:24 UTC1259
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC1260
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1261
96fdf90aacd6atagen2026-09-08 08:24 UTC1262
    pub async fn notification_keywords(&self) -> Vec<String> {
96fdf90aacd6atagen2026-09-08 08:24 UTC1263
        self.client
96fdf90aacd6atagen2026-09-08 08:24 UTC1264
            .notification_settings()
96fdf90aacd6atagen2026-09-08 08:24 UTC1265
            .await
96fdf90aacd6atagen2026-09-08 08:24 UTC1266
            .enabled_keywords()
96fdf90aacd6atagen2026-09-08 08:24 UTC1267
            .await
96fdf90aacd6atagen2026-09-08 08:24 UTC1268
            .into_iter()
96fdf90aacd6atagen2026-09-08 08:24 UTC1269
            .collect()
96fdf90aacd6atagen2026-09-08 08:24 UTC1270
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1271
96fdf90aacd6atagen2026-09-08 08:24 UTC1272
    pub async fn resolve_push_notification(
96fdf90aacd6atagen2026-09-08 08:24 UTC1273
        &self,
96fdf90aacd6atagen2026-09-08 08:24 UTC1274
        room_id: &RoomId,
96fdf90aacd6atagen2026-09-08 08:24 UTC1275
        event_id: &EventId,
31ce2453c82fatagen2026-09-24 13:07 UTC1276
    ) -> Result<PushNotificationStatus, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC1277
        let notification_failed = |source| {
31ce2453c82fatagen2026-09-24 13:07 UTC1278
            Error::with_source(
31ce2453c82fatagen2026-09-24 13:07 UTC1279
                ErrorKind::Other,
31ce2453c82fatagen2026-09-24 13:07 UTC1280
                "resolving the push notification failed",
31ce2453c82fatagen2026-09-24 13:07 UTC1281
                source,
31ce2453c82fatagen2026-09-24 13:07 UTC1282
            )
31ce2453c82fatagen2026-09-24 13:07 UTC1283
        };
96fdf90aacd6atagen2026-09-08 08:24 UTC1284
        let client = self
96fdf90aacd6atagen2026-09-08 08:24 UTC1285
            .notification_client
96fdf90aacd6atagen2026-09-08 08:24 UTC1286
            .get_or_try_init(|| async {
96fdf90aacd6atagen2026-09-08 08:24 UTC1287
                NotificationClient::new(
96fdf90aacd6atagen2026-09-08 08:24 UTC1288
                    self.client.clone(),
96fdf90aacd6atagen2026-09-08 08:24 UTC1289
                    NotificationProcessSetup::SingleProcess {
96fdf90aacd6atagen2026-09-08 08:24 UTC1290
                        sync_service: self.sync_service.clone(),
96fdf90aacd6atagen2026-09-08 08:24 UTC1291
                    },
96fdf90aacd6atagen2026-09-08 08:24 UTC1292
                )
96fdf90aacd6atagen2026-09-08 08:24 UTC1293
                .await
96fdf90aacd6atagen2026-09-08 08:24 UTC1294
            })
96fdf90aacd6atagen2026-09-08 08:24 UTC1295
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC1296
            .map_err(notification_failed)?;
96fdf90aacd6atagen2026-09-08 08:24 UTC1297
        let status = client
96fdf90aacd6atagen2026-09-08 08:24 UTC1298
            .get_notification(room_id, event_id)
96fdf90aacd6atagen2026-09-08 08:24 UTC1299
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC1300
            .map_err(notification_failed)?;
96fdf90aacd6atagen2026-09-08 08:24 UTC1301
        Ok(normalise_push_notification(room_id, event_id, status))
96fdf90aacd6atagen2026-09-08 08:24 UTC1302
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1303
96fdf90aacd6atagen2026-09-08 08:24 UTC1304
    pub async fn members(
96fdf90aacd6atagen2026-09-08 08:24 UTC1305
        &self,
96fdf90aacd6atagen2026-09-08 08:24 UTC1306
        room_id: &RoomId,
96fdf90aacd6atagen2026-09-08 08:24 UTC1307
        filter: MemberFilter,
31ce2453c82fatagen2026-09-24 13:07 UTC1308
    ) -> Result<Vec<MemberSummary>, Error> {
121a56ce119datagen2026-10-24 06:42 UTC1309
        let room = self.room(room_id)?;
121a56ce119datagen2026-10-24 06:42 UTC1310
        // stores have been seen with members_synced persisted over a partial
121a56ce119datagen2026-10-24 06:42 UTC1311
        // roster, and the sdk never asks /members again once it's set
9bd34e25bb86atagen2026-10-24 07:23 UTC1312
        if self
9bd34e25bb86atagen2026-10-24 07:23 UTC1313
            .members_checked
9bd34e25bb86atagen2026-10-24 07:23 UTC1314
            .lock()
9bd34e25bb86atagen2026-10-24 07:23 UTC1315
            .unwrap()
9bd34e25bb86atagen2026-10-24 07:23 UTC1316
            .insert(room_id.to_owned())
9bd34e25bb86atagen2026-10-24 07:23 UTC1317
        {
121a56ce119datagen2026-10-24 06:42 UTC1318
            room.mark_members_missing();
121a56ce119datagen2026-10-24 06:42 UTC1319
        }
121a56ce119datagen2026-10-24 06:42 UTC1320
        list_members(&room, filter).await
121a56ce119datagen2026-10-24 06:42 UTC1321
    }
121a56ce119datagen2026-10-24 06:42 UTC1322
121a56ce119datagen2026-10-24 06:42 UTC1323
    /// drop the cached roster for a room and fetch /members again
121a56ce119datagen2026-10-24 06:42 UTC1324
    pub async fn refresh_members(
121a56ce119datagen2026-10-24 06:42 UTC1325
        &self,
121a56ce119datagen2026-10-24 06:42 UTC1326
        room_id: &RoomId,
121a56ce119datagen2026-10-24 06:42 UTC1327
        filter: MemberFilter,
121a56ce119datagen2026-10-24 06:42 UTC1328
    ) -> Result<Vec<MemberSummary>, Error> {
121a56ce119datagen2026-10-24 06:42 UTC1329
        let room = self.room(room_id)?;
121a56ce119datagen2026-10-24 06:42 UTC1330
        room.mark_members_missing();
9bd34e25bb86atagen2026-10-24 07:23 UTC1331
        self.members_checked
9bd34e25bb86atagen2026-10-24 07:23 UTC1332
            .lock()
9bd34e25bb86atagen2026-10-24 07:23 UTC1333
            .unwrap()
9bd34e25bb86atagen2026-10-24 07:23 UTC1334
            .insert(room_id.to_owned());
121a56ce119datagen2026-10-24 06:42 UTC1335
        list_members(&room, filter).await
96fdf90aacd6atagen2026-09-08 08:24 UTC1336
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1337
31ce2453c82fatagen2026-09-24 13:07 UTC1338
    pub async fn room_power_levels(&self, room_id: &RoomId) -> Result<RoomPowerLevels, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC1339
        let levels = self
31ce2453c82fatagen2026-09-24 13:07 UTC1340
            .room(room_id)?
96fdf90aacd6atagen2026-09-08 08:24 UTC1341
            .power_levels()
96fdf90aacd6atagen2026-09-08 08:24 UTC1342
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC1343
            .map_err(|source| Error::from_sdk("reading power levels failed", source.into()))?;
96fdf90aacd6atagen2026-09-08 08:24 UTC1344
        Ok(RoomPowerLevels {
96fdf90aacd6atagen2026-09-08 08:24 UTC1345
            ban: levels.ban.into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC1346
            invite: levels.invite.into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC1347
            kick: levels.kick.into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC1348
            redact: levels.redact.into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC1349
            events_default: levels.events_default.into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC1350
            state_default: levels.state_default.into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC1351
            users_default: levels.users_default.into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC1352
            room_notification: levels.notifications.room.into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC1353
            users: levels
96fdf90aacd6atagen2026-09-08 08:24 UTC1354
                .users
96fdf90aacd6atagen2026-09-08 08:24 UTC1355
                .into_iter()
96fdf90aacd6atagen2026-09-08 08:24 UTC1356
                .map(|(user_id, level)| (user_id, level.into()))
96fdf90aacd6atagen2026-09-08 08:24 UTC1357
                .collect(),
96fdf90aacd6atagen2026-09-08 08:24 UTC1358
            events: levels
96fdf90aacd6atagen2026-09-08 08:24 UTC1359
                .events
96fdf90aacd6atagen2026-09-08 08:24 UTC1360
                .into_iter()
96fdf90aacd6atagen2026-09-08 08:24 UTC1361
                .map(|(event_type, level)| (event_type.to_string(), level.into()))
96fdf90aacd6atagen2026-09-08 08:24 UTC1362
                .collect(),
5a52012c9b1datagen2026-09-24 23:45 UTC1363
            creators: levels
5a52012c9b1datagen2026-09-24 23:45 UTC1364
                .rules
5a52012c9b1datagen2026-09-24 23:45 UTC1365
                .privileged_creators
5a52012c9b1datagen2026-09-24 23:45 UTC1366
                .into_iter()
5a52012c9b1datagen2026-09-24 23:45 UTC1367
                .flatten()
5a52012c9b1datagen2026-09-24 23:45 UTC1368
                .collect(),
96fdf90aacd6atagen2026-09-08 08:24 UTC1369
        })
96fdf90aacd6atagen2026-09-08 08:24 UTC1370
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1371
96fdf90aacd6atagen2026-09-08 08:24 UTC1372
    pub async fn pending_send_ids(
96fdf90aacd6atagen2026-09-08 08:24 UTC1373
        &self,
96fdf90aacd6atagen2026-09-08 08:24 UTC1374
        room_id: &RoomId,
31ce2453c82fatagen2026-09-24 13:07 UTC1375
    ) -> Result<Vec<OwnedTransactionId>, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC1376
        let (echoes, _) = self
31ce2453c82fatagen2026-09-24 13:07 UTC1377
            .room(room_id)?
96fdf90aacd6atagen2026-09-08 08:24 UTC1378
            .send_queue()
96fdf90aacd6atagen2026-09-08 08:24 UTC1379
            .subscribe()
96fdf90aacd6atagen2026-09-08 08:24 UTC1380
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC1381
            .map_err(|source| {
31ce2453c82fatagen2026-09-24 13:07 UTC1382
                Error::with_source(ErrorKind::Store, "reading the send queue failed", source)
31ce2453c82fatagen2026-09-24 13:07 UTC1383
            })?;
96fdf90aacd6atagen2026-09-08 08:24 UTC1384
        Ok(echoes
96fdf90aacd6atagen2026-09-08 08:24 UTC1385
            .into_iter()
96fdf90aacd6atagen2026-09-08 08:24 UTC1386
            .filter(|echo| {
96fdf90aacd6atagen2026-09-08 08:24 UTC1387
                matches!(
96fdf90aacd6atagen2026-09-08 08:24 UTC1388
                    echo.content,
96fdf90aacd6atagen2026-09-08 08:24 UTC1389
                    matrix_sdk::send_queue::LocalEchoContent::Event { .. }
96fdf90aacd6atagen2026-09-08 08:24 UTC1390
                )
96fdf90aacd6atagen2026-09-08 08:24 UTC1391
            })
96fdf90aacd6atagen2026-09-08 08:24 UTC1392
            .map(|echo| echo.transaction_id)
96fdf90aacd6atagen2026-09-08 08:24 UTC1393
            .collect())
96fdf90aacd6atagen2026-09-08 08:24 UTC1394
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1395
96fdf90aacd6atagen2026-09-08 08:24 UTC1396
    pub async fn global_account_data(
96fdf90aacd6atagen2026-09-08 08:24 UTC1397
        &self,
96fdf90aacd6atagen2026-09-08 08:24 UTC1398
        event_type: GlobalAccountDataEventType,
31ce2453c82fatagen2026-09-24 13:07 UTC1399
    ) -> Result<Option<Raw<AnyGlobalAccountDataEventContent>>, Error> {
96fdf90aacd6atagen2026-09-08 08:24 UTC1400
        self.client
96fdf90aacd6atagen2026-09-08 08:24 UTC1401
            .account()
96fdf90aacd6atagen2026-09-08 08:24 UTC1402
            .account_data_raw(event_type)
96fdf90aacd6atagen2026-09-08 08:24 UTC1403
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC1404
            .map_err(|source| Error::from_sdk("reading account data failed", source))
96fdf90aacd6atagen2026-09-08 08:24 UTC1405
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1406
96fdf90aacd6atagen2026-09-08 08:24 UTC1407
    pub async fn room_account_data(
96fdf90aacd6atagen2026-09-08 08:24 UTC1408
        &self,
96fdf90aacd6atagen2026-09-08 08:24 UTC1409
        room_id: &RoomId,
96fdf90aacd6atagen2026-09-08 08:24 UTC1410
        event_type: RoomAccountDataEventType,
31ce2453c82fatagen2026-09-24 13:07 UTC1411
    ) -> Result<Option<Raw<AnyRoomAccountDataEvent>>, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC1412
        self.room(room_id)?
31ce2453c82fatagen2026-09-24 13:07 UTC1413
            .account_data(event_type)
96fdf90aacd6atagen2026-09-08 08:24 UTC1414
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC1415
            .map_err(|source| Error::from_sdk("reading room account data failed", source))
96fdf90aacd6atagen2026-09-08 08:24 UTC1416
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1417
96fdf90aacd6atagen2026-09-08 08:24 UTC1418
    #[must_use]
96fdf90aacd6atagen2026-09-08 08:24 UTC1419
    pub fn connection_state(&self) -> watch::Receiver<ConnectionState> {
96fdf90aacd6atagen2026-09-08 08:24 UTC1420
        self.connection.clone()
96fdf90aacd6atagen2026-09-08 08:24 UTC1421
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1422
96fdf90aacd6atagen2026-09-08 08:24 UTC1423
    #[must_use]
96fdf90aacd6atagen2026-09-08 08:24 UTC1424
    pub fn encryption_state(&self) -> Option<EncryptionState> {
96fdf90aacd6atagen2026-09-08 08:24 UTC1425
        let device_id = self.client.device_id()?.to_owned();
96fdf90aacd6atagen2026-09-08 08:24 UTC1426
        let encryption = self.client.encryption();
96fdf90aacd6atagen2026-09-08 08:24 UTC1427
        Some(encryption_state(
96fdf90aacd6atagen2026-09-08 08:24 UTC1428
            device_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC1429
            encryption.recovery().state(),
96fdf90aacd6atagen2026-09-08 08:24 UTC1430
            encryption.verification_state().get(),
96fdf90aacd6atagen2026-09-08 08:24 UTC1431
            encryption.backups().state(),
96fdf90aacd6atagen2026-09-08 08:24 UTC1432
        ))
96fdf90aacd6atagen2026-09-08 08:24 UTC1433
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1434
}
96fdf90aacd6atagen2026-09-08 08:24 UTC1435
31ce2453c82fatagen2026-09-24 13:07 UTC1436
/// one space's hierarchy, a page at a time. hold on to it while you browse, it remembers where it got to
31ce2453c82fatagen2026-09-24 13:07 UTC1437
pub struct SpaceHierarchy(SpaceRoomList);
31ce2453c82fatagen2026-09-24 13:07 UTC1438
31ce2453c82fatagen2026-09-24 13:07 UTC1439
impl fmt::Debug for SpaceHierarchy {
31ce2453c82fatagen2026-09-24 13:07 UTC1440
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
31ce2453c82fatagen2026-09-24 13:07 UTC1441
        formatter
31ce2453c82fatagen2026-09-24 13:07 UTC1442
            .debug_struct("SpaceHierarchy")
31ce2453c82fatagen2026-09-24 13:07 UTC1443
            .finish_non_exhaustive()
31ce2453c82fatagen2026-09-24 13:07 UTC1444
    }
31ce2453c82fatagen2026-09-24 13:07 UTC1445
}
31ce2453c82fatagen2026-09-24 13:07 UTC1446
31ce2453c82fatagen2026-09-24 13:07 UTC1447
impl SpaceHierarchy {
31ce2453c82fatagen2026-09-24 13:07 UTC1448
    /// fetches the next page. a no-op once the end is reached or while a page is in flight
31ce2453c82fatagen2026-09-24 13:07 UTC1449
    pub async fn paginate(&self) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC1450
        self.0.paginate().await.map_err(|source| {
31ce2453c82fatagen2026-09-24 13:07 UTC1451
            Error::with_source(
31ce2453c82fatagen2026-09-24 13:07 UTC1452
                ErrorKind::Server,
31ce2453c82fatagen2026-09-24 13:07 UTC1453
                "fetching the space hierarchy failed",
31ce2453c82fatagen2026-09-24 13:07 UTC1454
                source,
31ce2453c82fatagen2026-09-24 13:07 UTC1455
            )
31ce2453c82fatagen2026-09-24 13:07 UTC1456
        })
31ce2453c82fatagen2026-09-24 13:07 UTC1457
    }
31ce2453c82fatagen2026-09-24 13:07 UTC1458
31ce2453c82fatagen2026-09-24 13:07 UTC1459
    /// everything fetched so far, the space itself excluded
31ce2453c82fatagen2026-09-24 13:07 UTC1460
    pub async fn rooms(&self) -> Vec<SpaceChild> {
31ce2453c82fatagen2026-09-24 13:07 UTC1461
        self.0.rooms().await.into_iter().map(space_child).collect()
31ce2453c82fatagen2026-09-24 13:07 UTC1462
    }
31ce2453c82fatagen2026-09-24 13:07 UTC1463
31ce2453c82fatagen2026-09-24 13:07 UTC1464
    #[must_use]
31ce2453c82fatagen2026-09-24 13:07 UTC1465
    pub fn end_reached(&self) -> bool {
31ce2453c82fatagen2026-09-24 13:07 UTC1466
        matches!(
31ce2453c82fatagen2026-09-24 13:07 UTC1467
            self.0.pagination_state(),
31ce2453c82fatagen2026-09-24 13:07 UTC1468
            SpaceRoomListPaginationState::Idle { end_reached: true }
31ce2453c82fatagen2026-09-24 13:07 UTC1469
        )
31ce2453c82fatagen2026-09-24 13:07 UTC1470
    }
31ce2453c82fatagen2026-09-24 13:07 UTC1471
}
31ce2453c82fatagen2026-09-24 13:07 UTC1472
31ce2453c82fatagen2026-09-24 13:07 UTC1473
fn profile(
31ce2453c82fatagen2026-09-24 13:07 UTC1474
    user_id: OwnedUserId,
31ce2453c82fatagen2026-09-24 13:07 UTC1475
    response: &ruma::api::client::profile::get_profile::v3::Response,
31ce2453c82fatagen2026-09-24 13:07 UTC1476
) -> Result<Profile, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC1477
    use ruma::api::client::profile::{AvatarUrl, DisplayName};
31ce2453c82fatagen2026-09-24 13:07 UTC1478
    let malformed = |source| {
31ce2453c82fatagen2026-09-24 13:07 UTC1479
        Error::with_source(
31ce2453c82fatagen2026-09-24 13:07 UTC1480
            ErrorKind::Server,
31ce2453c82fatagen2026-09-24 13:07 UTC1481
            "the server sent a malformed profile",
31ce2453c82fatagen2026-09-24 13:07 UTC1482
            source,
31ce2453c82fatagen2026-09-24 13:07 UTC1483
        )
31ce2453c82fatagen2026-09-24 13:07 UTC1484
    };
31ce2453c82fatagen2026-09-24 13:07 UTC1485
    Ok(Profile {
31ce2453c82fatagen2026-09-24 13:07 UTC1486
        user_id,
31ce2453c82fatagen2026-09-24 13:07 UTC1487
        display_name: response.get_static::<DisplayName>().map_err(malformed)?,
5d2974de5826atagen2026-10-08 11:29 UTC1488
        avatar: response
5d2974de5826atagen2026-10-08 11:29 UTC1489
            .get_static::<AvatarUrl>()
5d2974de5826atagen2026-10-08 11:29 UTC1490
            .map_err(malformed)?
5d2974de5826atagen2026-10-08 11:29 UTC1491
            .as_deref()
5d2974de5826atagen2026-10-08 11:29 UTC1492
            .map(MediaId::plain),
31ce2453c82fatagen2026-09-24 13:07 UTC1493
    })
31ce2453c82fatagen2026-09-24 13:07 UTC1494
}
31ce2453c82fatagen2026-09-24 13:07 UTC1495
31ce2453c82fatagen2026-09-24 13:07 UTC1496
#[derive(Debug)]
31ce2453c82fatagen2026-09-24 13:07 UTC1497
pub(crate) struct Request {
96fdf90aacd6atagen2026-09-08 08:24 UTC1498
    pub(crate) command: Command,
31ce2453c82fatagen2026-09-24 13:07 UTC1499
    pub(crate) reply: Responder,
31ce2453c82fatagen2026-09-24 13:07 UTC1500
}
31ce2453c82fatagen2026-09-24 13:07 UTC1501
31ce2453c82fatagen2026-09-24 13:07 UTC1502
impl Request {
31ce2453c82fatagen2026-09-24 13:07 UTC1503
    fn new(command: Command) -> (Self, Pending) {
31ce2453c82fatagen2026-09-24 13:07 UTC1504
        let (reply, pending) = oneshot::channel();
31ce2453c82fatagen2026-09-24 13:07 UTC1505
        (
31ce2453c82fatagen2026-09-24 13:07 UTC1506
            Self {
31ce2453c82fatagen2026-09-24 13:07 UTC1507
                command,
31ce2453c82fatagen2026-09-24 13:07 UTC1508
                reply: Responder(reply),
31ce2453c82fatagen2026-09-24 13:07 UTC1509
            },
31ce2453c82fatagen2026-09-24 13:07 UTC1510
            Pending(pending),
31ce2453c82fatagen2026-09-24 13:07 UTC1511
        )
31ce2453c82fatagen2026-09-24 13:07 UTC1512
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1513
}
96fdf90aacd6atagen2026-09-08 08:24 UTC1514
31ce2453c82fatagen2026-09-24 13:07 UTC1515
/// whoever holds this owes the caller an answer. dropping it answers `Interrupted`
96fdf90aacd6atagen2026-09-08 08:24 UTC1516
#[derive(Debug)]
31ce2453c82fatagen2026-09-24 13:07 UTC1517
pub(crate) struct Responder(oneshot::Sender<Result<CommandOutput, Error>>);
31ce2453c82fatagen2026-09-24 13:07 UTC1518
31ce2453c82fatagen2026-09-24 13:07 UTC1519
impl Responder {
fa51490b6ae9atagen2026-10-24 08:41 UTC1520
    pub(crate) fn channel() -> (Self, Pending) {
fa51490b6ae9atagen2026-10-24 08:41 UTC1521
        let (reply, pending) = oneshot::channel();
fa51490b6ae9atagen2026-10-24 08:41 UTC1522
        (Self(reply), Pending(pending))
fa51490b6ae9atagen2026-10-24 08:41 UTC1523
    }
fa51490b6ae9atagen2026-10-24 08:41 UTC1524
31ce2453c82fatagen2026-09-24 13:07 UTC1525
    pub(crate) fn send(self, result: Result<CommandOutput, Error>) {
31ce2453c82fatagen2026-09-24 13:07 UTC1526
        if let Err(error) = &result {
31ce2453c82fatagen2026-09-24 13:07 UTC1527
            tracing::debug!(%error, kind = ?error.kind, "command failed");
31ce2453c82fatagen2026-09-24 13:07 UTC1528
        }
31ce2453c82fatagen2026-09-24 13:07 UTC1529
        let _ = self.0.send(result);
31ce2453c82fatagen2026-09-24 13:07 UTC1530
    }
31ce2453c82fatagen2026-09-24 13:07 UTC1531
31ce2453c82fatagen2026-09-24 13:07 UTC1532
    pub(crate) fn done(self, result: Result<(), Error>) {
31ce2453c82fatagen2026-09-24 13:07 UTC1533
        self.send(result.map(|()| CommandOutput::Done));
31ce2453c82fatagen2026-09-24 13:07 UTC1534
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1535
}
96fdf90aacd6atagen2026-09-08 08:24 UTC1536
96fdf90aacd6atagen2026-09-08 08:24 UTC1537
#[derive(Debug)]
96fdf90aacd6atagen2026-09-08 08:24 UTC1538
pub struct EventStream {
96fdf90aacd6atagen2026-09-08 08:24 UTC1539
    events: mpsc::Receiver<Event>,
96fdf90aacd6atagen2026-09-08 08:24 UTC1540
}
96fdf90aacd6atagen2026-09-08 08:24 UTC1541
96fdf90aacd6atagen2026-09-08 08:24 UTC1542
impl EventStream {
96fdf90aacd6atagen2026-09-08 08:24 UTC1543
    pub async fn recv(&mut self) -> Option<Event> {
96fdf90aacd6atagen2026-09-08 08:24 UTC1544
        self.events.recv().await
96fdf90aacd6atagen2026-09-08 08:24 UTC1545
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1546
96fdf90aacd6atagen2026-09-08 08:24 UTC1547
    pub fn try_recv(&mut self) -> Result<Event, mpsc::error::TryRecvError> {
96fdf90aacd6atagen2026-09-08 08:24 UTC1548
        self.events.try_recv()
96fdf90aacd6atagen2026-09-08 08:24 UTC1549
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1550
}
96fdf90aacd6atagen2026-09-08 08:24 UTC1551
96fdf90aacd6atagen2026-09-08 08:24 UTC1552
impl futures_util::Stream for EventStream {
96fdf90aacd6atagen2026-09-08 08:24 UTC1553
    type Item = Event;
96fdf90aacd6atagen2026-09-08 08:24 UTC1554
96fdf90aacd6atagen2026-09-08 08:24 UTC1555
    fn poll_next(mut self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Option<Self::Item>> {
96fdf90aacd6atagen2026-09-08 08:24 UTC1556
        self.events.poll_recv(context)
96fdf90aacd6atagen2026-09-08 08:24 UTC1557
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1558
}
96fdf90aacd6atagen2026-09-08 08:24 UTC1559
96fdf90aacd6atagen2026-09-08 08:24 UTC1560
#[derive(Debug)]
96fdf90aacd6atagen2026-09-08 08:24 UTC1561
pub(crate) struct DriverPort {
31ce2453c82fatagen2026-09-24 13:07 UTC1562
    requests: mpsc::Receiver<Request>,
96fdf90aacd6atagen2026-09-08 08:24 UTC1563
    events: mpsc::Sender<Event>,
96fdf90aacd6atagen2026-09-08 08:24 UTC1564
    connection: watch::Sender<ConnectionState>,
80381e06a573atagen2026-10-25 03:02 UTC1565
    publication: Arc<PublicationRegistry>,
80381e06a573atagen2026-10-25 03:02 UTC1566
}
80381e06a573atagen2026-10-25 03:02 UTC1567
80381e06a573atagen2026-10-25 03:02 UTC1568
#[doc(hidden)]
80381e06a573atagen2026-10-25 03:02 UTC1569
#[derive(Clone, Debug)]
80381e06a573atagen2026-10-25 03:02 UTC1570
pub struct PublicationAck(Arc<PublicationAckInner>);
80381e06a573atagen2026-10-25 03:02 UTC1571
80381e06a573atagen2026-10-25 03:02 UTC1572
#[derive(Debug)]
80381e06a573atagen2026-10-25 03:02 UTC1573
struct PublicationAckInner {
80381e06a573atagen2026-10-25 03:02 UTC1574
    sender: Mutex<Option<oneshot::Sender<bool>>>,
80381e06a573atagen2026-10-25 03:02 UTC1575
}
80381e06a573atagen2026-10-25 03:02 UTC1576
80381e06a573atagen2026-10-25 03:02 UTC1577
impl PartialEq for PublicationAck {
80381e06a573atagen2026-10-25 03:02 UTC1578
    fn eq(&self, other: &Self) -> bool {
80381e06a573atagen2026-10-25 03:02 UTC1579
        Arc::ptr_eq(&self.0, &other.0)
80381e06a573atagen2026-10-25 03:02 UTC1580
    }
80381e06a573atagen2026-10-25 03:02 UTC1581
}
80381e06a573atagen2026-10-25 03:02 UTC1582
80381e06a573atagen2026-10-25 03:02 UTC1583
impl Eq for PublicationAck {}
80381e06a573atagen2026-10-25 03:02 UTC1584
80381e06a573atagen2026-10-25 03:02 UTC1585
impl PublicationAck {
80381e06a573atagen2026-10-25 03:02 UTC1586
    pub(crate) fn complete(&self, published: bool) {
80381e06a573atagen2026-10-25 03:02 UTC1587
        let sender = self
80381e06a573atagen2026-10-25 03:02 UTC1588
            .0
80381e06a573atagen2026-10-25 03:02 UTC1589
            .sender
80381e06a573atagen2026-10-25 03:02 UTC1590
            .lock()
80381e06a573atagen2026-10-25 03:02 UTC1591
            .unwrap_or_else(|error| error.into_inner())
80381e06a573atagen2026-10-25 03:02 UTC1592
            .take();
80381e06a573atagen2026-10-25 03:02 UTC1593
        if let Some(sender) = sender {
80381e06a573atagen2026-10-25 03:02 UTC1594
            let _ = sender.send(published);
80381e06a573atagen2026-10-25 03:02 UTC1595
        }
80381e06a573atagen2026-10-25 03:02 UTC1596
    }
80381e06a573atagen2026-10-25 03:02 UTC1597
}
80381e06a573atagen2026-10-25 03:02 UTC1598
80381e06a573atagen2026-10-25 03:02 UTC1599
#[derive(Debug, Default)]
80381e06a573atagen2026-10-25 03:02 UTC1600
struct PublicationRegistry {
80381e06a573atagen2026-10-25 03:02 UTC1601
    state: Mutex<PublicationRegistryState>,
80381e06a573atagen2026-10-25 03:02 UTC1602
}
80381e06a573atagen2026-10-25 03:02 UTC1603
80381e06a573atagen2026-10-25 03:02 UTC1604
#[derive(Debug, Default)]
80381e06a573atagen2026-10-25 03:02 UTC1605
struct PublicationRegistryState {
80381e06a573atagen2026-10-25 03:02 UTC1606
    mirrors: usize,
80381e06a573atagen2026-10-25 03:02 UTC1607
    pending: Vec<Weak<PublicationAckInner>>,
80381e06a573atagen2026-10-25 03:02 UTC1608
}
80381e06a573atagen2026-10-25 03:02 UTC1609
80381e06a573atagen2026-10-25 03:02 UTC1610
impl PublicationRegistry {
80381e06a573atagen2026-10-25 03:02 UTC1611
    fn register_mirror(self: &Arc<Self>) -> PublicationRegistration {
80381e06a573atagen2026-10-25 03:02 UTC1612
        self.state
80381e06a573atagen2026-10-25 03:02 UTC1613
            .lock()
80381e06a573atagen2026-10-25 03:02 UTC1614
            .unwrap_or_else(|error| error.into_inner())
80381e06a573atagen2026-10-25 03:02 UTC1615
            .mirrors += 1;
80381e06a573atagen2026-10-25 03:02 UTC1616
        PublicationRegistration {
80381e06a573atagen2026-10-25 03:02 UTC1617
            registry: self.clone(),
80381e06a573atagen2026-10-25 03:02 UTC1618
        }
80381e06a573atagen2026-10-25 03:02 UTC1619
    }
80381e06a573atagen2026-10-25 03:02 UTC1620
80381e06a573atagen2026-10-25 03:02 UTC1621
    fn acknowledgement(&self) -> Option<(PublicationAck, oneshot::Receiver<bool>)> {
80381e06a573atagen2026-10-25 03:02 UTC1622
        let mut state = self.state.lock().unwrap_or_else(|error| error.into_inner());
80381e06a573atagen2026-10-25 03:02 UTC1623
        if state.mirrors == 0 {
80381e06a573atagen2026-10-25 03:02 UTC1624
            return None;
80381e06a573atagen2026-10-25 03:02 UTC1625
        }
80381e06a573atagen2026-10-25 03:02 UTC1626
        state.pending.retain(|pending| pending.strong_count() > 0);
80381e06a573atagen2026-10-25 03:02 UTC1627
        let (sender, receiver) = oneshot::channel();
80381e06a573atagen2026-10-25 03:02 UTC1628
        let acknowledgement = PublicationAck(Arc::new(PublicationAckInner {
80381e06a573atagen2026-10-25 03:02 UTC1629
            sender: Mutex::new(Some(sender)),
80381e06a573atagen2026-10-25 03:02 UTC1630
        }));
80381e06a573atagen2026-10-25 03:02 UTC1631
        state.pending.push(Arc::downgrade(&acknowledgement.0));
80381e06a573atagen2026-10-25 03:02 UTC1632
        Some((acknowledgement, receiver))
80381e06a573atagen2026-10-25 03:02 UTC1633
    }
80381e06a573atagen2026-10-25 03:02 UTC1634
}
80381e06a573atagen2026-10-25 03:02 UTC1635
80381e06a573atagen2026-10-25 03:02 UTC1636
pub(crate) struct PublicationRegistration {
80381e06a573atagen2026-10-25 03:02 UTC1637
    registry: Arc<PublicationRegistry>,
80381e06a573atagen2026-10-25 03:02 UTC1638
}
80381e06a573atagen2026-10-25 03:02 UTC1639
80381e06a573atagen2026-10-25 03:02 UTC1640
impl Drop for PublicationRegistration {
80381e06a573atagen2026-10-25 03:02 UTC1641
    fn drop(&mut self) {
80381e06a573atagen2026-10-25 03:02 UTC1642
        let pending = {
80381e06a573atagen2026-10-25 03:02 UTC1643
            let mut state = self
80381e06a573atagen2026-10-25 03:02 UTC1644
                .registry
80381e06a573atagen2026-10-25 03:02 UTC1645
                .state
80381e06a573atagen2026-10-25 03:02 UTC1646
                .lock()
80381e06a573atagen2026-10-25 03:02 UTC1647
                .unwrap_or_else(|error| error.into_inner());
80381e06a573atagen2026-10-25 03:02 UTC1648
            state.mirrors -= 1;
80381e06a573atagen2026-10-25 03:02 UTC1649
            if state.mirrors == 0 {
80381e06a573atagen2026-10-25 03:02 UTC1650
                std::mem::take(&mut state.pending)
80381e06a573atagen2026-10-25 03:02 UTC1651
            } else {
80381e06a573atagen2026-10-25 03:02 UTC1652
                Vec::new()
80381e06a573atagen2026-10-25 03:02 UTC1653
            }
80381e06a573atagen2026-10-25 03:02 UTC1654
        };
80381e06a573atagen2026-10-25 03:02 UTC1655
        for acknowledgement in pending.into_iter().filter_map(|ack| ack.upgrade()) {
80381e06a573atagen2026-10-25 03:02 UTC1656
            PublicationAck(acknowledgement).complete(false);
80381e06a573atagen2026-10-25 03:02 UTC1657
        }
80381e06a573atagen2026-10-25 03:02 UTC1658
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1659
}
96fdf90aacd6atagen2026-09-08 08:24 UTC1660
96fdf90aacd6atagen2026-09-08 08:24 UTC1661
#[derive(Clone, Debug)]
80381e06a573atagen2026-10-25 03:02 UTC1662
pub(crate) struct EventSender {
80381e06a573atagen2026-10-25 03:02 UTC1663
    events: mpsc::Sender<Event>,
80381e06a573atagen2026-10-25 03:02 UTC1664
    publication: Arc<PublicationRegistry>,
80381e06a573atagen2026-10-25 03:02 UTC1665
}
96fdf90aacd6atagen2026-09-08 08:24 UTC1666
96fdf90aacd6atagen2026-09-08 08:24 UTC1667
impl EventSender {
96fdf90aacd6atagen2026-09-08 08:24 UTC1668
    pub(crate) async fn send(&self, event: Event) -> Result<(), SendEventError> {
80381e06a573atagen2026-10-25 03:02 UTC1669
        self.events.send(event).await.map_err(|_| SendEventError)
80381e06a573atagen2026-10-25 03:02 UTC1670
    }
80381e06a573atagen2026-10-25 03:02 UTC1671
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC1672
    pub(crate) async fn publish_barrier(&self) -> Result<(), SendEventError> {
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC1673
        let Some((acknowledgement, published)) = self.publication.acknowledgement() else {
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC1674
            return Ok(());
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC1675
        };
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC1676
        if let Err(error) = self
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC1677
            .send(Event::PublicationBarrier {
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC1678
                acknowledgement: Some(acknowledgement.clone()),
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC1679
            })
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC1680
            .await
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC1681
        {
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC1682
            acknowledgement.complete(false);
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC1683
            return Err(error);
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC1684
        }
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC1685
        drop(acknowledgement);
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC1686
        match published.await {
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC1687
            Ok(true) => Ok(()),
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC1688
            Ok(false) | Err(_) => Err(SendEventError),
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC1689
        }
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC1690
    }
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC1691
80381e06a573atagen2026-10-25 03:02 UTC1692
    pub(crate) async fn publish_repair(
80381e06a573atagen2026-10-25 03:02 UTC1693
        &self,
80381e06a573atagen2026-10-25 03:02 UTC1694
        report: crate::HistoryRepairReport,
80381e06a573atagen2026-10-25 03:02 UTC1695
    ) -> Result<(), SendEventError> {
80381e06a573atagen2026-10-25 03:02 UTC1696
        let Some((acknowledgement, published)) = self.publication.acknowledgement() else {
80381e06a573atagen2026-10-25 03:02 UTC1697
            return self.send(Event::HistoryRepairChanged(report)).await;
80381e06a573atagen2026-10-25 03:02 UTC1698
        };
80381e06a573atagen2026-10-25 03:02 UTC1699
        if let Err(error) = self
80381e06a573atagen2026-10-25 03:02 UTC1700
            .send(Event::HistoryRepairPublished {
80381e06a573atagen2026-10-25 03:02 UTC1701
                report,
80381e06a573atagen2026-10-25 03:02 UTC1702
                acknowledgement: Some(acknowledgement.clone()),
80381e06a573atagen2026-10-25 03:02 UTC1703
            })
80381e06a573atagen2026-10-25 03:02 UTC1704
            .await
80381e06a573atagen2026-10-25 03:02 UTC1705
        {
80381e06a573atagen2026-10-25 03:02 UTC1706
            acknowledgement.complete(false);
80381e06a573atagen2026-10-25 03:02 UTC1707
            return Err(error);
80381e06a573atagen2026-10-25 03:02 UTC1708
        }
80381e06a573atagen2026-10-25 03:02 UTC1709
        drop(acknowledgement);
80381e06a573atagen2026-10-25 03:02 UTC1710
        match published.await {
80381e06a573atagen2026-10-25 03:02 UTC1711
            Ok(true) => Ok(()),
80381e06a573atagen2026-10-25 03:02 UTC1712
            Ok(false) | Err(_) => Err(SendEventError),
80381e06a573atagen2026-10-25 03:02 UTC1713
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC1714
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1715
411bc1c78b65atagen2026-10-22 07:39 UTC1716
    pub(crate) async fn reserve(&self) -> Result<mpsc::Permit<'_, Event>, SendEventError> {
80381e06a573atagen2026-10-25 03:02 UTC1717
        self.events.reserve().await.map_err(|_| SendEventError)
411bc1c78b65atagen2026-10-22 07:39 UTC1718
    }
411bc1c78b65atagen2026-10-22 07:39 UTC1719
31ce2453c82fatagen2026-09-24 13:07 UTC1720
    /// resolves once every `EventStream` is gone
96fdf90aacd6atagen2026-09-08 08:24 UTC1721
    pub(crate) async fn closed(&self) {
80381e06a573atagen2026-10-25 03:02 UTC1722
        self.events.closed().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC1723
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1724
}
96fdf90aacd6atagen2026-09-08 08:24 UTC1725
96fdf90aacd6atagen2026-09-08 08:24 UTC1726
impl DriverPort {
96fdf90aacd6atagen2026-09-08 08:24 UTC1727
    pub(crate) fn event_sender(&self) -> EventSender {
80381e06a573atagen2026-10-25 03:02 UTC1728
        EventSender {
80381e06a573atagen2026-10-25 03:02 UTC1729
            events: self.events.clone(),
80381e06a573atagen2026-10-25 03:02 UTC1730
            publication: self.publication.clone(),
80381e06a573atagen2026-10-25 03:02 UTC1731
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC1732
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1733
96fdf90aacd6atagen2026-09-08 08:24 UTC1734
    pub(crate) fn connection_sender(&self) -> watch::Sender<ConnectionState> {
96fdf90aacd6atagen2026-09-08 08:24 UTC1735
        self.connection.clone()
96fdf90aacd6atagen2026-09-08 08:24 UTC1736
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1737
31ce2453c82fatagen2026-09-24 13:07 UTC1738
    pub(crate) async fn recv(&mut self) -> Option<Request> {
31ce2453c82fatagen2026-09-24 13:07 UTC1739
        self.requests.recv().await
96fdf90aacd6atagen2026-09-08 08:24 UTC1740
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1741
5a52012c9b1datagen2026-09-24 23:45 UTC1742
    pub(crate) fn try_recv(&mut self) -> Option<Request> {
5a52012c9b1datagen2026-09-24 23:45 UTC1743
        self.requests.try_recv().ok()
5a52012c9b1datagen2026-09-24 23:45 UTC1744
    }
5a52012c9b1datagen2026-09-24 23:45 UTC1745
31ce2453c82fatagen2026-09-24 13:07 UTC1746
    pub(crate) fn close(&mut self) {
31ce2453c82fatagen2026-09-24 13:07 UTC1747
        self.requests.close();
96fdf90aacd6atagen2026-09-08 08:24 UTC1748
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1749
}
96fdf90aacd6atagen2026-09-08 08:24 UTC1750
31ce2453c82fatagen2026-09-24 13:07 UTC1751
#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
31ce2453c82fatagen2026-09-24 13:07 UTC1752
#[error("the event receiver has closed")]
31ce2453c82fatagen2026-09-24 13:07 UTC1753
pub(crate) struct SendEventError;
96fdf90aacd6atagen2026-09-08 08:24 UTC1754
96fdf90aacd6atagen2026-09-08 08:24 UTC1755
#[must_use]
96fdf90aacd6atagen2026-09-08 08:24 UTC1756
pub(crate) fn transport_pair(
96fdf90aacd6atagen2026-09-08 08:24 UTC1757
    client: Client,
96fdf90aacd6atagen2026-09-08 08:24 UTC1758
    sync_service: Arc<SyncService>,
31ce2453c82fatagen2026-09-24 13:07 UTC1759
    media: Arc<MediaService>,
a250c7fa43fbatagen2026-09-26 13:35 UTC1760
    store_lock: Option<Arc<StoreLock>>,
96fdf90aacd6atagen2026-09-08 08:24 UTC1761
    command_capacity: usize,
96fdf90aacd6atagen2026-09-08 08:24 UTC1762
    event_capacity: usize,
96fdf90aacd6atagen2026-09-08 08:24 UTC1763
) -> (ClientHandle, DriverPort, EventStream) {
31ce2453c82fatagen2026-09-24 13:07 UTC1764
    let (request_tx, request_rx) = mpsc::channel(command_capacity);
96fdf90aacd6atagen2026-09-08 08:24 UTC1765
    let (event_tx, event_rx) = mpsc::channel(event_capacity);
96fdf90aacd6atagen2026-09-08 08:24 UTC1766
    let (connection_tx, connection_rx) = watch::channel(ConnectionState::Idle);
80381e06a573atagen2026-10-25 03:02 UTC1767
    let publication = Arc::new(PublicationRegistry::default());
96fdf90aacd6atagen2026-09-08 08:24 UTC1768
    (
96fdf90aacd6atagen2026-09-08 08:24 UTC1769
        ClientHandle {
31ce2453c82fatagen2026-09-24 13:07 UTC1770
            requests: request_tx,
96fdf90aacd6atagen2026-09-08 08:24 UTC1771
            client,
96fdf90aacd6atagen2026-09-08 08:24 UTC1772
            connection: connection_rx,
96fdf90aacd6atagen2026-09-08 08:24 UTC1773
            notification_client: Arc::new(OnceCell::new()),
96fdf90aacd6atagen2026-09-08 08:24 UTC1774
            sync_service,
31ce2453c82fatagen2026-09-24 13:07 UTC1775
            media,
31ce2453c82fatagen2026-09-24 13:07 UTC1776
            spaces: Arc::new(OnceCell::new()),
121a56ce119datagen2026-10-24 06:42 UTC1777
            members_checked: Arc::default(),
80381e06a573atagen2026-10-25 03:02 UTC1778
            publication: publication.clone(),
a250c7fa43fbatagen2026-09-26 13:35 UTC1779
            _store_lock: store_lock,
96fdf90aacd6atagen2026-09-08 08:24 UTC1780
        },
96fdf90aacd6atagen2026-09-08 08:24 UTC1781
        DriverPort {
31ce2453c82fatagen2026-09-24 13:07 UTC1782
            requests: request_rx,
96fdf90aacd6atagen2026-09-08 08:24 UTC1783
            events: event_tx,
96fdf90aacd6atagen2026-09-08 08:24 UTC1784
            connection: connection_tx,
80381e06a573atagen2026-10-25 03:02 UTC1785
            publication,
96fdf90aacd6atagen2026-09-08 08:24 UTC1786
        },
96fdf90aacd6atagen2026-09-08 08:24 UTC1787
        EventStream { events: event_rx },
96fdf90aacd6atagen2026-09-08 08:24 UTC1788
    )
96fdf90aacd6atagen2026-09-08 08:24 UTC1789
}
96fdf90aacd6atagen2026-09-08 08:24 UTC1790
121a56ce119datagen2026-10-24 06:42 UTC1791
async fn list_members(
121a56ce119datagen2026-10-24 06:42 UTC1792
    room: &matrix_sdk::Room,
121a56ce119datagen2026-10-24 06:42 UTC1793
    filter: MemberFilter,
121a56ce119datagen2026-10-24 06:42 UTC1794
) -> Result<Vec<MemberSummary>, Error> {
121a56ce119datagen2026-10-24 06:42 UTC1795
    let memberships = match filter {
121a56ce119datagen2026-10-24 06:42 UTC1796
        MemberFilter::Active => RoomMemberships::ACTIVE,
121a56ce119datagen2026-10-24 06:42 UTC1797
        MemberFilter::Joined => RoomMemberships::JOIN,
121a56ce119datagen2026-10-24 06:42 UTC1798
        MemberFilter::Invited => RoomMemberships::INVITE,
121a56ce119datagen2026-10-24 06:42 UTC1799
        MemberFilter::Banned => RoomMemberships::BAN,
121a56ce119datagen2026-10-24 06:42 UTC1800
        MemberFilter::Left => RoomMemberships::LEAVE,
121a56ce119datagen2026-10-24 06:42 UTC1801
        // An empty mask is the SDK's unfiltered wildcard: it matches every
121a56ce119datagen2026-10-24 06:42 UTC1802
        // membership, including custom states that `all()` would exclude.
121a56ce119datagen2026-10-24 06:42 UTC1803
        MemberFilter::All => RoomMemberships::empty(),
121a56ce119datagen2026-10-24 06:42 UTC1804
    };
121a56ce119datagen2026-10-24 06:42 UTC1805
    Ok(room
121a56ce119datagen2026-10-24 06:42 UTC1806
        .members(memberships)
121a56ce119datagen2026-10-24 06:42 UTC1807
        .await
121a56ce119datagen2026-10-24 06:42 UTC1808
        .map_err(|source| Error::from_sdk("listing members failed", source))?
121a56ce119datagen2026-10-24 06:42 UTC1809
        .into_iter()
121a56ce119datagen2026-10-24 06:42 UTC1810
        .map(member_summary)
121a56ce119datagen2026-10-24 06:42 UTC1811
        .collect())
121a56ce119datagen2026-10-24 06:42 UTC1812
}
121a56ce119datagen2026-10-24 06:42 UTC1813
96fdf90aacd6atagen2026-09-08 08:24 UTC1814
fn normalise_push_notification(
96fdf90aacd6atagen2026-09-08 08:24 UTC1815
    room_id: &RoomId,
96fdf90aacd6atagen2026-09-08 08:24 UTC1816
    event_id: &EventId,
96fdf90aacd6atagen2026-09-08 08:24 UTC1817
    status: NotificationStatus,
96fdf90aacd6atagen2026-09-08 08:24 UTC1818
) -> PushNotificationStatus {
96fdf90aacd6atagen2026-09-08 08:24 UTC1819
    match status {
96fdf90aacd6atagen2026-09-08 08:24 UTC1820
        NotificationStatus::Event(item) => {
96fdf90aacd6atagen2026-09-08 08:24 UTC1821
            let event = match &item.raw_event {
96fdf90aacd6atagen2026-09-08 08:24 UTC1822
                RawNotificationEvent::Timeline(event) => {
96fdf90aacd6atagen2026-09-08 08:24 UTC1823
                    PushNotificationEvent::Timeline(event.clone())
96fdf90aacd6atagen2026-09-08 08:24 UTC1824
                }
96fdf90aacd6atagen2026-09-08 08:24 UTC1825
                RawNotificationEvent::Invite(event) => PushNotificationEvent::Invite(event.clone()),
96fdf90aacd6atagen2026-09-08 08:24 UTC1826
            };
96fdf90aacd6atagen2026-09-08 08:24 UTC1827
            PushNotificationStatus::Event(Box::new(PushNotification {
96fdf90aacd6atagen2026-09-08 08:24 UTC1828
                event_id: event_id.to_owned(),
96fdf90aacd6atagen2026-09-08 08:24 UTC1829
                event,
96fdf90aacd6atagen2026-09-08 08:24 UTC1830
                sender: PushNotificationSender {
96fdf90aacd6atagen2026-09-08 08:24 UTC1831
                    user_id: item.event.sender().to_owned(),
96fdf90aacd6atagen2026-09-08 08:24 UTC1832
                    display_name: item.sender_display_name,
5d2974de5826atagen2026-10-08 11:29 UTC1833
                    avatar_url: item.sender_avatar_url.as_deref().map(MediaId::plain),
96fdf90aacd6atagen2026-09-08 08:24 UTC1834
                    name_ambiguous: item.is_sender_name_ambiguous,
96fdf90aacd6atagen2026-09-08 08:24 UTC1835
                },
96fdf90aacd6atagen2026-09-08 08:24 UTC1836
                room: PushNotificationRoom {
96fdf90aacd6atagen2026-09-08 08:24 UTC1837
                    room_id: room_id.to_owned(),
96fdf90aacd6atagen2026-09-08 08:24 UTC1838
                    name: item.room_computed_display_name,
5d2974de5826atagen2026-10-08 11:29 UTC1839
                    avatar_url: item.room_avatar_url.as_deref().map(MediaId::plain),
96fdf90aacd6atagen2026-09-08 08:24 UTC1840
                    canonical_alias: item
96fdf90aacd6atagen2026-09-08 08:24 UTC1841
                        .room_canonical_alias
96fdf90aacd6atagen2026-09-08 08:24 UTC1842
                        .as_deref()
96fdf90aacd6atagen2026-09-08 08:24 UTC1843
                        .and_then(|alias| alias.parse().ok()),
96fdf90aacd6atagen2026-09-08 08:24 UTC1844
                    topic: item.room_topic,
96fdf90aacd6atagen2026-09-08 08:24 UTC1845
                    encrypted: item.is_room_encrypted,
96fdf90aacd6atagen2026-09-08 08:24 UTC1846
                    direct: item.room_is_dm,
96fdf90aacd6atagen2026-09-08 08:24 UTC1847
                    joined_members_count: item.joined_members_count,
96fdf90aacd6atagen2026-09-08 08:24 UTC1848
                    service_members: item
96fdf90aacd6atagen2026-09-08 08:24 UTC1849
                        .service_members
96fdf90aacd6atagen2026-09-08 08:24 UTC1850
                        .into_iter()
96fdf90aacd6atagen2026-09-08 08:24 UTC1851
                        .filter_map(|user_id| user_id.parse().ok())
96fdf90aacd6atagen2026-09-08 08:24 UTC1852
                        .collect(),
96fdf90aacd6atagen2026-09-08 08:24 UTC1853
                    active_service_members_count: item.active_service_members_count,
96fdf90aacd6atagen2026-09-08 08:24 UTC1854
                    space: item.is_space,
96fdf90aacd6atagen2026-09-08 08:24 UTC1855
                },
96fdf90aacd6atagen2026-09-08 08:24 UTC1856
                noisy: item.is_noisy,
96fdf90aacd6atagen2026-09-08 08:24 UTC1857
                has_mention: item.has_mention,
96fdf90aacd6atagen2026-09-08 08:24 UTC1858
                thread_id: item.thread_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC1859
            }))
96fdf90aacd6atagen2026-09-08 08:24 UTC1860
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC1861
        NotificationStatus::EventNotFound => PushNotificationStatus::NotFound,
96fdf90aacd6atagen2026-09-08 08:24 UTC1862
        NotificationStatus::EventFilteredOut => PushNotificationStatus::FilteredOut,
96fdf90aacd6atagen2026-09-08 08:24 UTC1863
        NotificationStatus::EventRedacted => PushNotificationStatus::Redacted,
96fdf90aacd6atagen2026-09-08 08:24 UTC1864
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1865
}
fa51490b6ae9atagen2026-10-24 08:41 UTC1866
fa51490b6ae9atagen2026-10-24 08:41 UTC1867
fn unexpected(wanted: &str, output: &CommandOutput) -> Error {
fa51490b6ae9atagen2026-10-24 08:41 UTC1868
    Error::new(
fa51490b6ae9atagen2026-10-24 08:41 UTC1869
        ErrorKind::Other,
fa51490b6ae9atagen2026-10-24 08:41 UTC1870
        format!("expected {wanted} but got {output:?}"),
fa51490b6ae9atagen2026-10-24 08:41 UTC1871
    )
fa51490b6ae9atagen2026-10-24 08:41 UTC1872
}
80381e06a573atagen2026-10-25 03:02 UTC1873
80381e06a573atagen2026-10-25 03:02 UTC1874
#[cfg(test)]
80381e06a573atagen2026-10-25 03:02 UTC1875
mod tests {
80381e06a573atagen2026-10-25 03:02 UTC1876
    use super::*;
80381e06a573atagen2026-10-25 03:02 UTC1877
    use crate::{
80381e06a573atagen2026-10-25 03:02 UTC1878
        HistoryContinuity, HistoryRepairPhase, HistoryRepairPolicy, HistoryRepairStrategy,
80381e06a573atagen2026-10-25 03:02 UTC1879
    };
80381e06a573atagen2026-10-25 03:02 UTC1880
    use ruma::room_id;
80381e06a573atagen2026-10-25 03:02 UTC1881
80381e06a573atagen2026-10-25 03:02 UTC1882
    fn report() -> HistoryRepairReport {
80381e06a573atagen2026-10-25 03:02 UTC1883
        HistoryRepairReport {
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC1884
            gap_id: None,
80381e06a573atagen2026-10-25 03:02 UTC1885
            room_id: room_id!("!room:example.org").to_owned(),
80381e06a573atagen2026-10-25 03:02 UTC1886
            requested_policy: HistoryRepairPolicy::Bridge,
80381e06a573atagen2026-10-25 03:02 UTC1887
            strategy: HistoryRepairStrategy::Bridge,
80381e06a573atagen2026-10-25 03:02 UTC1888
            coverage_duration_ms: None,
80381e06a573atagen2026-10-25 03:02 UTC1889
            threshold_evaluated: false,
80381e06a573atagen2026-10-25 03:02 UTC1890
            pages: 0,
80381e06a573atagen2026-10-25 03:02 UTC1891
            events: 0,
80381e06a573atagen2026-10-25 03:02 UTC1892
            phase: HistoryRepairPhase::Ready,
80381e06a573atagen2026-10-25 03:02 UTC1893
            continuity: HistoryContinuity::Verified,
80381e06a573atagen2026-10-25 03:02 UTC1894
            recent_available: true,
80381e06a573atagen2026-10-25 03:02 UTC1895
            stop: None,
80381e06a573atagen2026-10-25 03:02 UTC1896
            failure: None,
80381e06a573atagen2026-10-25 03:02 UTC1897
        }
80381e06a573atagen2026-10-25 03:02 UTC1898
    }
80381e06a573atagen2026-10-25 03:02 UTC1899
80381e06a573atagen2026-10-25 03:02 UTC1900
    #[tokio::test]
80381e06a573atagen2026-10-25 03:02 UTC1901
    async fn publish_repair_waits_for_mirror_publication() {
80381e06a573atagen2026-10-25 03:02 UTC1902
        let publication = Arc::new(PublicationRegistry::default());
80381e06a573atagen2026-10-25 03:02 UTC1903
        let _registration = publication.register_mirror();
80381e06a573atagen2026-10-25 03:02 UTC1904
        let (events, mut received) = mpsc::channel(1);
80381e06a573atagen2026-10-25 03:02 UTC1905
        let sender = EventSender {
80381e06a573atagen2026-10-25 03:02 UTC1906
            events,
80381e06a573atagen2026-10-25 03:02 UTC1907
            publication,
80381e06a573atagen2026-10-25 03:02 UTC1908
        };
80381e06a573atagen2026-10-25 03:02 UTC1909
        let expected = report();
80381e06a573atagen2026-10-25 03:02 UTC1910
        let expected_for_task = expected.clone();
80381e06a573atagen2026-10-25 03:02 UTC1911
        let publisher = tokio::spawn(async move { sender.publish_repair(expected_for_task).await });
80381e06a573atagen2026-10-25 03:02 UTC1912
80381e06a573atagen2026-10-25 03:02 UTC1913
        let Some(Event::HistoryRepairPublished {
80381e06a573atagen2026-10-25 03:02 UTC1914
            report,
80381e06a573atagen2026-10-25 03:02 UTC1915
            acknowledgement: Some(acknowledgement),
80381e06a573atagen2026-10-25 03:02 UTC1916
        }) = received.recv().await
80381e06a573atagen2026-10-25 03:02 UTC1917
        else {
80381e06a573atagen2026-10-25 03:02 UTC1918
            panic!("repair event was not published");
80381e06a573atagen2026-10-25 03:02 UTC1919
        };
80381e06a573atagen2026-10-25 03:02 UTC1920
        assert_eq!(report, expected);
80381e06a573atagen2026-10-25 03:02 UTC1921
        tokio::task::yield_now().await;
80381e06a573atagen2026-10-25 03:02 UTC1922
        assert!(!publisher.is_finished());
80381e06a573atagen2026-10-25 03:02 UTC1923
80381e06a573atagen2026-10-25 03:02 UTC1924
        acknowledgement.complete(true);
80381e06a573atagen2026-10-25 03:02 UTC1925
        assert!(matches!(publisher.await.unwrap(), Ok(())));
80381e06a573atagen2026-10-25 03:02 UTC1926
    }
80381e06a573atagen2026-10-25 03:02 UTC1927
80381e06a573atagen2026-10-25 03:02 UTC1928
    #[tokio::test]
80381e06a573atagen2026-10-25 03:02 UTC1929
    async fn publish_repair_fails_when_last_mirror_drops() {
80381e06a573atagen2026-10-25 03:02 UTC1930
        let publication = Arc::new(PublicationRegistry::default());
80381e06a573atagen2026-10-25 03:02 UTC1931
        let registration = publication.register_mirror();
80381e06a573atagen2026-10-25 03:02 UTC1932
        let (events, mut received) = mpsc::channel(1);
80381e06a573atagen2026-10-25 03:02 UTC1933
        let sender = EventSender {
80381e06a573atagen2026-10-25 03:02 UTC1934
            events,
80381e06a573atagen2026-10-25 03:02 UTC1935
            publication,
80381e06a573atagen2026-10-25 03:02 UTC1936
        };
80381e06a573atagen2026-10-25 03:02 UTC1937
        let publisher = tokio::spawn(async move { sender.publish_repair(report()).await });
80381e06a573atagen2026-10-25 03:02 UTC1938
80381e06a573atagen2026-10-25 03:02 UTC1939
        assert!(matches!(
80381e06a573atagen2026-10-25 03:02 UTC1940
            received.recv().await,
80381e06a573atagen2026-10-25 03:02 UTC1941
            Some(Event::HistoryRepairPublished {
80381e06a573atagen2026-10-25 03:02 UTC1942
                acknowledgement: Some(_),
80381e06a573atagen2026-10-25 03:02 UTC1943
                ..
80381e06a573atagen2026-10-25 03:02 UTC1944
            })
80381e06a573atagen2026-10-25 03:02 UTC1945
        ));
80381e06a573atagen2026-10-25 03:02 UTC1946
        drop(registration);
80381e06a573atagen2026-10-25 03:02 UTC1947
80381e06a573atagen2026-10-25 03:02 UTC1948
        assert!(matches!(publisher.await.unwrap(), Err(SendEventError)));
80381e06a573atagen2026-10-25 03:02 UTC1949
    }
80381e06a573atagen2026-10-25 03:02 UTC1950
}