atagen

macltok

public
Star0
Watch0
9bd34e25bb861f0d2a6f19a39cbceeac12e36f93
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,
121a56ce119datagen2026-10-24 06:42 UTC6
    sync::{Arc, Mutex},
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,
17e042fbc331atagen2026-10-08 12:39 UTC31
    EncryptionCommand, EncryptionState, Error, ErrorKind, Event, EventRef, ImagePacks,
0291516d40b5atagen2026-10-22 08:31 UTC32
    InteractiveAuth, InteractiveAuthOutcome, LifecycleCommand, LinkPreview, Media, MediaCommand,
0291516d40b5atagen2026-10-22 08:31 UTC33
    MediaId, MediaLocation, MediaSize, MemberFilter, MemberSummary, MessageContent,
0291516d40b5atagen2026-10-22 08:31 UTC34
    MessageEditContent, NotificationCommand, OwnDevice, PowerLevelChanges, Profile,
0291516d40b5atagen2026-10-22 08:31 UTC35
    PushNotification, PushNotificationEvent, PushNotificationStatus, RoomCommand, RoomListFilter,
5b21b919d1e6atagen2026-09-27 11:47 UTC36
    RoomNotificationMode, RoomNotificationSettings, RoomPowerLevels, RoomSummary, SpaceChild,
5b21b919d1e6atagen2026-09-27 11:47 UTC37
    TimelineCommand, TimelineId, VerificationAction, VerificationTarget,
96fdf90aacd6atagen2026-09-08 08:24 UTC38
    encryption::encryption_state,
a250c7fa43fbatagen2026-09-26 13:35 UTC39
    engine::{MediaService, StoreLock},
96fdf90aacd6atagen2026-09-08 08:24 UTC40
    model::{PushNotificationRoom, PushNotificationSender},
0291516d40b5atagen2026-10-22 08:31 UTC41
    normalise::{image_packs, member_summary, room_summaries, space_child, url_previews_allowed},
96fdf90aacd6atagen2026-09-08 08:24 UTC42
};
96fdf90aacd6atagen2026-09-08 08:24 UTC43
96fdf90aacd6atagen2026-09-08 08:24 UTC44
#[derive(Clone)]
96fdf90aacd6atagen2026-09-08 08:24 UTC45
pub struct ClientHandle {
31ce2453c82fatagen2026-09-24 13:07 UTC46
    requests: mpsc::Sender<Request>,
96fdf90aacd6atagen2026-09-08 08:24 UTC47
    client: Client,
96fdf90aacd6atagen2026-09-08 08:24 UTC48
    connection: watch::Receiver<ConnectionState>,
96fdf90aacd6atagen2026-09-08 08:24 UTC49
    notification_client: Arc<OnceCell<NotificationClient>>,
96fdf90aacd6atagen2026-09-08 08:24 UTC50
    sync_service: Arc<SyncService>,
31ce2453c82fatagen2026-09-24 13:07 UTC51
    media: Arc<MediaService>,
31ce2453c82fatagen2026-09-24 13:07 UTC52
    spaces: Arc<OnceCell<SpaceService>>,
121a56ce119datagen2026-10-24 06:42 UTC53
    members_checked: Arc<Mutex<HashSet<OwnedRoomId>>>,
a250c7fa43fbatagen2026-09-26 13:35 UTC54
    _store_lock: Option<Arc<StoreLock>>,
96fdf90aacd6atagen2026-09-08 08:24 UTC55
}
96fdf90aacd6atagen2026-09-08 08:24 UTC56
96fdf90aacd6atagen2026-09-08 08:24 UTC57
impl fmt::Debug for ClientHandle {
96fdf90aacd6atagen2026-09-08 08:24 UTC58
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
96fdf90aacd6atagen2026-09-08 08:24 UTC59
        formatter
96fdf90aacd6atagen2026-09-08 08:24 UTC60
            .debug_struct("ClientHandle")
96fdf90aacd6atagen2026-09-08 08:24 UTC61
            .finish_non_exhaustive()
96fdf90aacd6atagen2026-09-08 08:24 UTC62
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC63
}
96fdf90aacd6atagen2026-09-08 08:24 UTC64
31ce2453c82fatagen2026-09-24 13:07 UTC65
/// a command's answer. drop it if you don't care, the command still runs
31ce2453c82fatagen2026-09-24 13:07 UTC66
#[derive(Debug)]
31ce2453c82fatagen2026-09-24 13:07 UTC67
#[must_use = "dropping it is fine but then you'll never know"]
31ce2453c82fatagen2026-09-24 13:07 UTC68
pub struct Pending(oneshot::Receiver<Result<CommandOutput, Error>>);
31ce2453c82fatagen2026-09-24 13:07 UTC69
31ce2453c82fatagen2026-09-24 13:07 UTC70
impl Future for Pending {
31ce2453c82fatagen2026-09-24 13:07 UTC71
    type Output = Result<CommandOutput, Error>;
31ce2453c82fatagen2026-09-24 13:07 UTC72
31ce2453c82fatagen2026-09-24 13:07 UTC73
    fn poll(mut self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
31ce2453c82fatagen2026-09-24 13:07 UTC74
        Pin::new(&mut self.0)
31ce2453c82fatagen2026-09-24 13:07 UTC75
            .poll(context)
31ce2453c82fatagen2026-09-24 13:07 UTC76
            .map(|result| result.unwrap_or_else(|_| Err(Error::interrupted())))
31ce2453c82fatagen2026-09-24 13:07 UTC77
    }
31ce2453c82fatagen2026-09-24 13:07 UTC78
}
31ce2453c82fatagen2026-09-24 13:07 UTC79
96fdf90aacd6atagen2026-09-08 08:24 UTC80
impl ClientHandle {
f12377bce9b9atagen2026-09-26 12:32 UTC81
    /// current credentials, it holds secrets. the engine saves it for you unless the
f12377bce9b9atagen2026-09-26 12:32 UTC82
    /// session was built `without_persistence`
62fe32759529atagen2026-09-26 08:50 UTC83
    #[cfg(any(feature = "sqlite", feature = "indexeddb"))]
96fdf90aacd6atagen2026-09-08 08:24 UTC84
    pub fn session_record(&self) -> Result<crate::SessionRecord, crate::AccountError> {
96fdf90aacd6atagen2026-09-08 08:24 UTC85
        crate::account::session_record(&self.client)
96fdf90aacd6atagen2026-09-08 08:24 UTC86
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC87
5b21b919d1e6atagen2026-09-27 11:47 UTC88
    /// the bearer token. it rotates on every refresh so ask again after a 401 rather than
5b21b919d1e6atagen2026-09-27 11:47 UTC89
    /// keeping it, and keep it in memory and away from all but the homeserver's media endpoints
5b21b919d1e6atagen2026-09-27 11:47 UTC90
    pub fn access_token(&self) -> Option<String> {
5b21b919d1e6atagen2026-09-27 11:47 UTC91
        self.client.access_token()
5b21b919d1e6atagen2026-09-27 11:47 UTC92
    }
5b21b919d1e6atagen2026-09-27 11:47 UTC93
31ce2453c82fatagen2026-09-24 13:07 UTC94
    /// queue a command, waiting for room in the queue if there isn't any
31ce2453c82fatagen2026-09-24 13:07 UTC95
    pub async fn send(&self, command: Command) -> Result<Pending, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC96
        let (request, pending) = Request::new(command);
31ce2453c82fatagen2026-09-24 13:07 UTC97
        self.requests
31ce2453c82fatagen2026-09-24 13:07 UTC98
            .send(request)
96fdf90aacd6atagen2026-09-08 08:24 UTC99
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC100
            .map_err(|_| Error::interrupted())?;
31ce2453c82fatagen2026-09-24 13:07 UTC101
        Ok(pending)
31ce2453c82fatagen2026-09-24 13:07 UTC102
    }
31ce2453c82fatagen2026-09-24 13:07 UTC103
31ce2453c82fatagen2026-09-24 13:07 UTC104
    /// queue a command or fail with `QueueFull` right now
31ce2453c82fatagen2026-09-24 13:07 UTC105
    pub fn try_send(&self, command: Command) -> Result<Pending, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC106
        let (request, pending) = Request::new(command);
31ce2453c82fatagen2026-09-24 13:07 UTC107
        self.requests
31ce2453c82fatagen2026-09-24 13:07 UTC108
            .try_send(request)
31ce2453c82fatagen2026-09-24 13:07 UTC109
            .map_err(|error| match error {
31ce2453c82fatagen2026-09-24 13:07 UTC110
                mpsc::error::TrySendError::Full(_) => {
31ce2453c82fatagen2026-09-24 13:07 UTC111
                    Error::new(ErrorKind::QueueFull, "the command queue is full")
31ce2453c82fatagen2026-09-24 13:07 UTC112
                }
31ce2453c82fatagen2026-09-24 13:07 UTC113
                mpsc::error::TrySendError::Closed(_) => Error::interrupted(),
31ce2453c82fatagen2026-09-24 13:07 UTC114
            })?;
31ce2453c82fatagen2026-09-24 13:07 UTC115
        Ok(pending)
31ce2453c82fatagen2026-09-24 13:07 UTC116
    }
31ce2453c82fatagen2026-09-24 13:07 UTC117
31ce2453c82fatagen2026-09-24 13:07 UTC118
    /// queue a command and wait for its answer. timeline sends answer once the
31ce2453c82fatagen2026-09-24 13:07 UTC119
    /// sdk send queue has them, delivery shows up later as the item's send state
31ce2453c82fatagen2026-09-24 13:07 UTC120
    pub async fn execute(&self, command: Command) -> Result<CommandOutput, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC121
        self.send(command).await?.await
31ce2453c82fatagen2026-09-24 13:07 UTC122
    }
31ce2453c82fatagen2026-09-24 13:07 UTC123
31ce2453c82fatagen2026-09-24 13:07 UTC124
    async fn run(&self, command: Command) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC125
        self.execute(command).await.map(drop)
31ce2453c82fatagen2026-09-24 13:07 UTC126
    }
31ce2453c82fatagen2026-09-24 13:07 UTC127
31ce2453c82fatagen2026-09-24 13:07 UTC128
    async fn run_for_room(&self, command: RoomCommand) -> Result<OwnedRoomId, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC129
        match self.execute(Command::Room(command)).await? {
31ce2453c82fatagen2026-09-24 13:07 UTC130
            CommandOutput::Room(room_id) => Ok(room_id),
31ce2453c82fatagen2026-09-24 13:07 UTC131
            output => Err(Error::new(
31ce2453c82fatagen2026-09-24 13:07 UTC132
                ErrorKind::Other,
31ce2453c82fatagen2026-09-24 13:07 UTC133
                format!("expected a room but got {output:?}"),
31ce2453c82fatagen2026-09-24 13:07 UTC134
            )),
31ce2453c82fatagen2026-09-24 13:07 UTC135
        }
31ce2453c82fatagen2026-09-24 13:07 UTC136
    }
31ce2453c82fatagen2026-09-24 13:07 UTC137
31ce2453c82fatagen2026-09-24 13:07 UTC138
    pub async fn shutdown(&self) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC139
        self.run(Command::Lifecycle(LifecycleCommand::Shutdown))
31ce2453c82fatagen2026-09-24 13:07 UTC140
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC141
    }
31ce2453c82fatagen2026-09-24 13:07 UTC142
31ce2453c82fatagen2026-09-24 13:07 UTC143
    pub async fn logout(&self) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC144
        self.run(Command::Account(AccountCommand::Logout)).await
96fdf90aacd6atagen2026-09-08 08:24 UTC145
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC146
96fdf90aacd6atagen2026-09-08 08:24 UTC147
    pub async fn delete_devices(
96fdf90aacd6atagen2026-09-08 08:24 UTC148
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC149
        device_ids: Vec<OwnedDeviceId>,
96fdf90aacd6atagen2026-09-08 08:24 UTC150
        auth: Option<InteractiveAuth>,
31ce2453c82fatagen2026-09-24 13:07 UTC151
    ) -> Result<InteractiveAuthOutcome, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC152
        match self
31ce2453c82fatagen2026-09-24 13:07 UTC153
            .run(Command::Account(AccountCommand::DeleteDevices {
96fdf90aacd6atagen2026-09-08 08:24 UTC154
                device_ids,
96fdf90aacd6atagen2026-09-08 08:24 UTC155
                auth,
31ce2453c82fatagen2026-09-24 13:07 UTC156
            }))
96fdf90aacd6atagen2026-09-08 08:24 UTC157
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC158
        {
31ce2453c82fatagen2026-09-24 13:07 UTC159
            Ok(()) => Ok(InteractiveAuthOutcome::Completed),
31ce2453c82fatagen2026-09-24 13:07 UTC160
            Err(Error {
31ce2453c82fatagen2026-09-24 13:07 UTC161
                challenge: Some(challenge),
31ce2453c82fatagen2026-09-24 13:07 UTC162
                ..
31ce2453c82fatagen2026-09-24 13:07 UTC163
            }) => Ok(InteractiveAuthOutcome::Challenge(*challenge)),
31ce2453c82fatagen2026-09-24 13:07 UTC164
            Err(error) => Err(error),
31ce2453c82fatagen2026-09-24 13:07 UTC165
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC166
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC167
96fdf90aacd6atagen2026-09-08 08:24 UTC168
    pub async fn rename_device(
96fdf90aacd6atagen2026-09-08 08:24 UTC169
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC170
        device_id: OwnedDeviceId,
96fdf90aacd6atagen2026-09-08 08:24 UTC171
        display_name: impl Into<String>,
31ce2453c82fatagen2026-09-24 13:07 UTC172
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC173
        self.run(Command::Account(AccountCommand::RenameDevice {
96fdf90aacd6atagen2026-09-08 08:24 UTC174
            device_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC175
            display_name: display_name.into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC176
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC177
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC178
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC179
31ce2453c82fatagen2026-09-24 13:07 UTC180
    pub async fn set_global_account_data(
31ce2453c82fatagen2026-09-24 13:07 UTC181
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC182
        event_type: GlobalAccountDataEventType,
31ce2453c82fatagen2026-09-24 13:07 UTC183
        content: Raw<AnyGlobalAccountDataEventContent>,
31ce2453c82fatagen2026-09-24 13:07 UTC184
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC185
        self.run(Command::Account(AccountCommand::SetAccountData {
31ce2453c82fatagen2026-09-24 13:07 UTC186
            event_type,
31ce2453c82fatagen2026-09-24 13:07 UTC187
            content,
31ce2453c82fatagen2026-09-24 13:07 UTC188
        }))
31ce2453c82fatagen2026-09-24 13:07 UTC189
        .await
31ce2453c82fatagen2026-09-24 13:07 UTC190
    }
31ce2453c82fatagen2026-09-24 13:07 UTC191
31ce2453c82fatagen2026-09-24 13:07 UTC192
    pub async fn set_room_account_data(
31ce2453c82fatagen2026-09-24 13:07 UTC193
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC194
        room_id: OwnedRoomId,
31ce2453c82fatagen2026-09-24 13:07 UTC195
        event_type: RoomAccountDataEventType,
31ce2453c82fatagen2026-09-24 13:07 UTC196
        content: Raw<AnyRoomAccountDataEventContent>,
31ce2453c82fatagen2026-09-24 13:07 UTC197
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC198
        self.run(Command::Room(RoomCommand::SetAccountData {
31ce2453c82fatagen2026-09-24 13:07 UTC199
            room_id,
31ce2453c82fatagen2026-09-24 13:07 UTC200
            event_type,
31ce2453c82fatagen2026-09-24 13:07 UTC201
            content,
31ce2453c82fatagen2026-09-24 13:07 UTC202
        }))
31ce2453c82fatagen2026-09-24 13:07 UTC203
        .await
31ce2453c82fatagen2026-09-24 13:07 UTC204
    }
31ce2453c82fatagen2026-09-24 13:07 UTC205
96fdf90aacd6atagen2026-09-08 08:24 UTC206
    pub async fn set_room_notification_mode(
96fdf90aacd6atagen2026-09-08 08:24 UTC207
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC208
        room_id: OwnedRoomId,
96fdf90aacd6atagen2026-09-08 08:24 UTC209
        mode: RoomNotificationMode,
31ce2453c82fatagen2026-09-24 13:07 UTC210
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC211
        self.run(Command::Notification(NotificationCommand::SetRoomMode {
96fdf90aacd6atagen2026-09-08 08:24 UTC212
            room_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC213
            mode,
96fdf90aacd6atagen2026-09-08 08:24 UTC214
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC215
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC216
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC217
31ce2453c82fatagen2026-09-24 13:07 UTC218
    pub async fn reset_room_notification_mode(&self, room_id: OwnedRoomId) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC219
        self.run(Command::Notification(NotificationCommand::ResetRoomMode(
96fdf90aacd6atagen2026-09-08 08:24 UTC220
            room_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC221
        )))
96fdf90aacd6atagen2026-09-08 08:24 UTC222
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC223
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC224
96fdf90aacd6atagen2026-09-08 08:24 UTC225
    pub async fn set_default_notification_mode(
96fdf90aacd6atagen2026-09-08 08:24 UTC226
        &self,
96fdf90aacd6atagen2026-09-08 08:24 UTC227
        encrypted: bool,
96fdf90aacd6atagen2026-09-08 08:24 UTC228
        one_to_one: bool,
96fdf90aacd6atagen2026-09-08 08:24 UTC229
        mode: RoomNotificationMode,
31ce2453c82fatagen2026-09-24 13:07 UTC230
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC231
        self.run(Command::Notification(NotificationCommand::SetDefaultMode {
96fdf90aacd6atagen2026-09-08 08:24 UTC232
            encrypted,
96fdf90aacd6atagen2026-09-08 08:24 UTC233
            one_to_one,
96fdf90aacd6atagen2026-09-08 08:24 UTC234
            mode,
96fdf90aacd6atagen2026-09-08 08:24 UTC235
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC236
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC237
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC238
31ce2453c82fatagen2026-09-24 13:07 UTC239
    pub async fn add_notification_keyword(&self, keyword: impl Into<String>) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC240
        self.run(Command::Notification(NotificationCommand::AddKeyword(
96fdf90aacd6atagen2026-09-08 08:24 UTC241
            keyword.into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC242
        )))
96fdf90aacd6atagen2026-09-08 08:24 UTC243
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC244
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC245
96fdf90aacd6atagen2026-09-08 08:24 UTC246
    pub async fn remove_notification_keyword(
96fdf90aacd6atagen2026-09-08 08:24 UTC247
        &self,
96fdf90aacd6atagen2026-09-08 08:24 UTC248
        keyword: impl Into<String>,
31ce2453c82fatagen2026-09-24 13:07 UTC249
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC250
        self.run(Command::Notification(NotificationCommand::RemoveKeyword(
96fdf90aacd6atagen2026-09-08 08:24 UTC251
            keyword.into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC252
        )))
96fdf90aacd6atagen2026-09-08 08:24 UTC253
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC254
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC255
31ce2453c82fatagen2026-09-24 13:07 UTC256
    pub async fn open_room(&self, room_id: OwnedRoomId) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC257
        self.run(Command::Room(RoomCommand::Open(room_id))).await
96fdf90aacd6atagen2026-09-08 08:24 UTC258
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC259
31ce2453c82fatagen2026-09-24 13:07 UTC260
    pub async fn close_room(&self, room_id: OwnedRoomId) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC261
        self.run(Command::Room(RoomCommand::Close(room_id))).await
96fdf90aacd6atagen2026-09-08 08:24 UTC262
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC263
96fdf90aacd6atagen2026-09-08 08:24 UTC264
    pub async fn open_thread(
96fdf90aacd6atagen2026-09-08 08:24 UTC265
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC266
        room_id: OwnedRoomId,
31ce2453c82fatagen2026-09-24 13:07 UTC267
        root_event_id: OwnedEventId,
31ce2453c82fatagen2026-09-24 13:07 UTC268
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC269
        self.run(Command::Timeline(TimelineCommand::OpenThread {
96fdf90aacd6atagen2026-09-08 08:24 UTC270
            room_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC271
            root_event_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC272
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC273
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC274
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC275
96fdf90aacd6atagen2026-09-08 08:24 UTC276
    pub async fn close_thread(
96fdf90aacd6atagen2026-09-08 08:24 UTC277
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC278
        room_id: OwnedRoomId,
31ce2453c82fatagen2026-09-24 13:07 UTC279
        root_event_id: OwnedEventId,
31ce2453c82fatagen2026-09-24 13:07 UTC280
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC281
        self.run(Command::Timeline(TimelineCommand::CloseThread {
96fdf90aacd6atagen2026-09-08 08:24 UTC282
            room_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC283
            root_event_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC284
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC285
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC286
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC287
31ce2453c82fatagen2026-09-24 13:07 UTC288
    pub async fn resync(&self, timeline_id: TimelineId) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC289
        self.run(Command::Timeline(TimelineCommand::Resync { timeline_id }))
96fdf90aacd6atagen2026-09-08 08:24 UTC290
            .await
96fdf90aacd6atagen2026-09-08 08:24 UTC291
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC292
31ce2453c82fatagen2026-09-24 13:07 UTC293
    pub async fn paginate(&self, timeline_id: TimelineId, limit: u16) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC294
        self.run(Command::Timeline(TimelineCommand::Paginate {
31ce2453c82fatagen2026-09-24 13:07 UTC295
            timeline_id,
31ce2453c82fatagen2026-09-24 13:07 UTC296
            limit,
31ce2453c82fatagen2026-09-24 13:07 UTC297
        }))
31ce2453c82fatagen2026-09-24 13:07 UTC298
        .await
31ce2453c82fatagen2026-09-24 13:07 UTC299
    }
31ce2453c82fatagen2026-09-24 13:07 UTC300
31ce2453c82fatagen2026-09-24 13:07 UTC301
    pub async fn send_message(
96fdf90aacd6atagen2026-09-08 08:24 UTC302
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC303
        timeline_id: TimelineId,
31ce2453c82fatagen2026-09-24 13:07 UTC304
        content: MessageContent,
31ce2453c82fatagen2026-09-24 13:07 UTC305
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC306
        self.run(Command::Timeline(TimelineCommand::Send {
31ce2453c82fatagen2026-09-24 13:07 UTC307
            timeline_id,
31ce2453c82fatagen2026-09-24 13:07 UTC308
            content,
31ce2453c82fatagen2026-09-24 13:07 UTC309
        }))
31ce2453c82fatagen2026-09-24 13:07 UTC310
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC311
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC312
31ce2453c82fatagen2026-09-24 13:07 UTC313
    pub async fn edit_message(
96fdf90aacd6atagen2026-09-08 08:24 UTC314
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC315
        timeline_id: TimelineId,
960ac0721079atagen2026-09-25 00:24 UTC316
        target: impl Into<EventRef>,
31ce2453c82fatagen2026-09-24 13:07 UTC317
        content: MessageEditContent,
31ce2453c82fatagen2026-09-24 13:07 UTC318
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC319
        self.run(Command::Timeline(TimelineCommand::Edit {
31ce2453c82fatagen2026-09-24 13:07 UTC320
            timeline_id,
960ac0721079atagen2026-09-25 00:24 UTC321
            target: target.into(),
31ce2453c82fatagen2026-09-24 13:07 UTC322
            content,
31ce2453c82fatagen2026-09-24 13:07 UTC323
        }))
31ce2453c82fatagen2026-09-24 13:07 UTC324
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC325
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC326
31ce2453c82fatagen2026-09-24 13:07 UTC327
    pub async fn redact(
96fdf90aacd6atagen2026-09-08 08:24 UTC328
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC329
        timeline_id: TimelineId,
960ac0721079atagen2026-09-25 00:24 UTC330
        target: impl Into<EventRef>,
31ce2453c82fatagen2026-09-24 13:07 UTC331
        reason: Option<String>,
31ce2453c82fatagen2026-09-24 13:07 UTC332
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC333
        self.run(Command::Timeline(TimelineCommand::Redact {
31ce2453c82fatagen2026-09-24 13:07 UTC334
            timeline_id,
960ac0721079atagen2026-09-25 00:24 UTC335
            target: target.into(),
31ce2453c82fatagen2026-09-24 13:07 UTC336
            reason,
31ce2453c82fatagen2026-09-24 13:07 UTC337
        }))
31ce2453c82fatagen2026-09-24 13:07 UTC338
        .await
31ce2453c82fatagen2026-09-24 13:07 UTC339
    }
31ce2453c82fatagen2026-09-24 13:07 UTC340
31ce2453c82fatagen2026-09-24 13:07 UTC341
    pub async fn toggle_reaction(
31ce2453c82fatagen2026-09-24 13:07 UTC342
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC343
        timeline_id: TimelineId,
960ac0721079atagen2026-09-25 00:24 UTC344
        target: impl Into<EventRef>,
31ce2453c82fatagen2026-09-24 13:07 UTC345
        key: impl Into<String>,
8fa8a7cde89catagen2026-10-08 12:39 UTC346
        shortcode: Option<String>,
31ce2453c82fatagen2026-09-24 13:07 UTC347
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC348
        self.run(Command::Timeline(TimelineCommand::ToggleReaction {
31ce2453c82fatagen2026-09-24 13:07 UTC349
            timeline_id,
960ac0721079atagen2026-09-25 00:24 UTC350
            target: target.into(),
31ce2453c82fatagen2026-09-24 13:07 UTC351
            key: key.into(),
8fa8a7cde89catagen2026-10-08 12:39 UTC352
            shortcode,
31ce2453c82fatagen2026-09-24 13:07 UTC353
        }))
31ce2453c82fatagen2026-09-24 13:07 UTC354
        .await
31ce2453c82fatagen2026-09-24 13:07 UTC355
    }
31ce2453c82fatagen2026-09-24 13:07 UTC356
31ce2453c82fatagen2026-09-24 13:07 UTC357
    pub async fn mark_read(
31ce2453c82fatagen2026-09-24 13:07 UTC358
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC359
        timeline_id: TimelineId,
31ce2453c82fatagen2026-09-24 13:07 UTC360
        event_id: OwnedEventId,
31ce2453c82fatagen2026-09-24 13:07 UTC361
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC362
        self.run(Command::Timeline(TimelineCommand::MarkRead {
31ce2453c82fatagen2026-09-24 13:07 UTC363
            timeline_id,
31ce2453c82fatagen2026-09-24 13:07 UTC364
            event_id,
31ce2453c82fatagen2026-09-24 13:07 UTC365
        }))
31ce2453c82fatagen2026-09-24 13:07 UTC366
        .await
31ce2453c82fatagen2026-09-24 13:07 UTC367
    }
31ce2453c82fatagen2026-09-24 13:07 UTC368
31ce2453c82fatagen2026-09-24 13:07 UTC369
    pub async fn set_typing(&self, room_id: OwnedRoomId, typing: bool) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC370
        self.run(Command::Room(RoomCommand::SetTyping { room_id, typing }))
96fdf90aacd6atagen2026-09-08 08:24 UTC371
            .await
96fdf90aacd6atagen2026-09-08 08:24 UTC372
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC373
38c4caecdb3datagen2026-09-26 13:35 UTC374
    pub async fn prepare_to_send(&self, room_id: OwnedRoomId) -> Result<(), Error> {
38c4caecdb3datagen2026-09-26 13:35 UTC375
        self.run(Command::Room(RoomCommand::PrepareToSend(room_id)))
38c4caecdb3datagen2026-09-26 13:35 UTC376
            .await
38c4caecdb3datagen2026-09-26 13:35 UTC377
    }
38c4caecdb3datagen2026-09-26 13:35 UTC378
31ce2453c82fatagen2026-09-24 13:07 UTC379
    pub async fn join_room(
96fdf90aacd6atagen2026-09-08 08:24 UTC380
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC381
        room: OwnedRoomOrAliasId,
31ce2453c82fatagen2026-09-24 13:07 UTC382
        via: Vec<OwnedServerName>,
31ce2453c82fatagen2026-09-24 13:07 UTC383
    ) -> Result<OwnedRoomId, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC384
        self.run_for_room(RoomCommand::Join { room, via }).await
31ce2453c82fatagen2026-09-24 13:07 UTC385
    }
31ce2453c82fatagen2026-09-24 13:07 UTC386
31ce2453c82fatagen2026-09-24 13:07 UTC387
    pub async fn leave_room(&self, room_id: OwnedRoomId) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC388
        self.run(Command::Room(RoomCommand::Leave(room_id))).await
31ce2453c82fatagen2026-09-24 13:07 UTC389
    }
31ce2453c82fatagen2026-09-24 13:07 UTC390
31ce2453c82fatagen2026-09-24 13:07 UTC391
    pub async fn forget_room(&self, room_id: OwnedRoomId) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC392
        self.run(Command::Room(RoomCommand::Forget(room_id))).await
31ce2453c82fatagen2026-09-24 13:07 UTC393
    }
31ce2453c82fatagen2026-09-24 13:07 UTC394
31ce2453c82fatagen2026-09-24 13:07 UTC395
    pub async fn accept_invite(&self, room_id: OwnedRoomId) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC396
        self.run(Command::Room(RoomCommand::AcceptInvite(room_id)))
31ce2453c82fatagen2026-09-24 13:07 UTC397
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC398
    }
31ce2453c82fatagen2026-09-24 13:07 UTC399
31ce2453c82fatagen2026-09-24 13:07 UTC400
    pub async fn reject_invite(&self, room_id: OwnedRoomId) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC401
        self.run(Command::Room(RoomCommand::RejectInvite(room_id)))
96fdf90aacd6atagen2026-09-08 08:24 UTC402
            .await
96fdf90aacd6atagen2026-09-08 08:24 UTC403
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC404
96fdf90aacd6atagen2026-09-08 08:24 UTC405
    pub async fn invite_user(
96fdf90aacd6atagen2026-09-08 08:24 UTC406
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC407
        room_id: OwnedRoomId,
31ce2453c82fatagen2026-09-24 13:07 UTC408
        user_id: OwnedUserId,
31ce2453c82fatagen2026-09-24 13:07 UTC409
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC410
        self.run(Command::Room(RoomCommand::InviteUser { room_id, user_id }))
31ce2453c82fatagen2026-09-24 13:07 UTC411
            .await
96fdf90aacd6atagen2026-09-08 08:24 UTC412
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC413
96fdf90aacd6atagen2026-09-08 08:24 UTC414
    pub async fn kick_user(
96fdf90aacd6atagen2026-09-08 08:24 UTC415
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC416
        room_id: OwnedRoomId,
31ce2453c82fatagen2026-09-24 13:07 UTC417
        user_id: OwnedUserId,
96fdf90aacd6atagen2026-09-08 08:24 UTC418
        reason: Option<String>,
31ce2453c82fatagen2026-09-24 13:07 UTC419
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC420
        self.run(Command::Room(RoomCommand::KickUser {
96fdf90aacd6atagen2026-09-08 08:24 UTC421
            room_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC422
            user_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC423
            reason,
96fdf90aacd6atagen2026-09-08 08:24 UTC424
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC425
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC426
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC427
96fdf90aacd6atagen2026-09-08 08:24 UTC428
    pub async fn ban_user(
96fdf90aacd6atagen2026-09-08 08:24 UTC429
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC430
        room_id: OwnedRoomId,
31ce2453c82fatagen2026-09-24 13:07 UTC431
        user_id: OwnedUserId,
96fdf90aacd6atagen2026-09-08 08:24 UTC432
        reason: Option<String>,
31ce2453c82fatagen2026-09-24 13:07 UTC433
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC434
        self.run(Command::Room(RoomCommand::BanUser {
96fdf90aacd6atagen2026-09-08 08:24 UTC435
            room_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC436
            user_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC437
            reason,
96fdf90aacd6atagen2026-09-08 08:24 UTC438
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC439
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC440
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC441
96fdf90aacd6atagen2026-09-08 08:24 UTC442
    pub async fn unban_user(
96fdf90aacd6atagen2026-09-08 08:24 UTC443
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC444
        room_id: OwnedRoomId,
31ce2453c82fatagen2026-09-24 13:07 UTC445
        user_id: OwnedUserId,
96fdf90aacd6atagen2026-09-08 08:24 UTC446
        reason: Option<String>,
31ce2453c82fatagen2026-09-24 13:07 UTC447
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC448
        self.run(Command::Room(RoomCommand::UnbanUser {
96fdf90aacd6atagen2026-09-08 08:24 UTC449
            room_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC450
            user_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC451
            reason,
96fdf90aacd6atagen2026-09-08 08:24 UTC452
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC453
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC454
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC455
96fdf90aacd6atagen2026-09-08 08:24 UTC456
    pub async fn set_power_levels(
96fdf90aacd6atagen2026-09-08 08:24 UTC457
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC458
        room_id: OwnedRoomId,
96fdf90aacd6atagen2026-09-08 08:24 UTC459
        changes: PowerLevelChanges,
31ce2453c82fatagen2026-09-24 13:07 UTC460
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC461
        self.run(Command::Room(RoomCommand::SetPowerLevels {
96fdf90aacd6atagen2026-09-08 08:24 UTC462
            room_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC463
            changes,
96fdf90aacd6atagen2026-09-08 08:24 UTC464
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC465
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC466
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC467
96fdf90aacd6atagen2026-09-08 08:24 UTC468
    pub async fn set_user_power_level(
96fdf90aacd6atagen2026-09-08 08:24 UTC469
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC470
        room_id: OwnedRoomId,
31ce2453c82fatagen2026-09-24 13:07 UTC471
        user_id: OwnedUserId,
96fdf90aacd6atagen2026-09-08 08:24 UTC472
        level: i64,
31ce2453c82fatagen2026-09-24 13:07 UTC473
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC474
        self.run(Command::Room(RoomCommand::SetUserPowerLevel {
96fdf90aacd6atagen2026-09-08 08:24 UTC475
            room_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC476
            user_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC477
            level,
96fdf90aacd6atagen2026-09-08 08:24 UTC478
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC479
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC480
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC481
31ce2453c82fatagen2026-09-24 13:07 UTC482
    pub async fn create_room(&self, options: CreateRoomOptions) -> Result<OwnedRoomId, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC483
        self.run_for_room(RoomCommand::Create(options)).await
96fdf90aacd6atagen2026-09-08 08:24 UTC484
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC485
31ce2453c82fatagen2026-09-24 13:07 UTC486
    /// hands back the existing dm with them if there is one
31ce2453c82fatagen2026-09-24 13:07 UTC487
    pub async fn create_direct(&self, user_id: OwnedUserId) -> Result<OwnedRoomId, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC488
        self.run_for_room(RoomCommand::CreateDirect(user_id)).await
96fdf90aacd6atagen2026-09-08 08:24 UTC489
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC490
96fdf90aacd6atagen2026-09-08 08:24 UTC491
    pub async fn cancel_send(
96fdf90aacd6atagen2026-09-08 08:24 UTC492
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC493
        room_id: OwnedRoomId,
31ce2453c82fatagen2026-09-24 13:07 UTC494
        transaction_id: OwnedTransactionId,
31ce2453c82fatagen2026-09-24 13:07 UTC495
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC496
        self.run(Command::Room(RoomCommand::CancelSend {
96fdf90aacd6atagen2026-09-08 08:24 UTC497
            room_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC498
            transaction_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC499
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC500
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC501
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC502
96fdf90aacd6atagen2026-09-08 08:24 UTC503
    pub async fn retry_send(
96fdf90aacd6atagen2026-09-08 08:24 UTC504
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC505
        room_id: OwnedRoomId,
31ce2453c82fatagen2026-09-24 13:07 UTC506
        transaction_id: OwnedTransactionId,
31ce2453c82fatagen2026-09-24 13:07 UTC507
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC508
        self.run(Command::Room(RoomCommand::RetrySend {
96fdf90aacd6atagen2026-09-08 08:24 UTC509
            room_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC510
            transaction_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC511
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC512
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC513
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC514
31ce2453c82fatagen2026-09-24 13:07 UTC515
    pub async fn upload(&self, upload: AttachmentUpload) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC516
        self.run(Command::Media(MediaCommand::Upload(upload))).await
96fdf90aacd6atagen2026-09-08 08:24 UTC517
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC518
31ce2453c82fatagen2026-09-24 13:07 UTC519
    pub async fn recover(&self, recovery_key: impl Into<String>) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC520
        self.run(Command::Encryption(EncryptionCommand::Recover(
31ce2453c82fatagen2026-09-24 13:07 UTC521
            recovery_key.into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC522
        )))
96fdf90aacd6atagen2026-09-08 08:24 UTC523
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC524
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC525
31ce2453c82fatagen2026-09-24 13:07 UTC526
    pub async fn create_key_backup(&self) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC527
        self.run(Command::Encryption(EncryptionCommand::CreateKeyBackup))
31ce2453c82fatagen2026-09-24 13:07 UTC528
            .await
96fdf90aacd6atagen2026-09-08 08:24 UTC529
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC530
31ce2453c82fatagen2026-09-24 13:07 UTC531
    pub async fn enable_key_backup(&self) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC532
        self.run(Command::Encryption(EncryptionCommand::EnableKeyBackup))
31ce2453c82fatagen2026-09-24 13:07 UTC533
            .await
96fdf90aacd6atagen2026-09-08 08:24 UTC534
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC535
31ce2453c82fatagen2026-09-24 13:07 UTC536
    pub async fn disable_key_backup(&self, delete_from_server: bool) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC537
        self.run(Command::Encryption(EncryptionCommand::DisableKeyBackup {
31ce2453c82fatagen2026-09-24 13:07 UTC538
            delete_from_server,
31ce2453c82fatagen2026-09-24 13:07 UTC539
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC540
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC541
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC542
31ce2453c82fatagen2026-09-24 13:07 UTC543
    pub async fn recover_key_backup(&self, recovery_key: impl Into<String>) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC544
        self.run(Command::Encryption(EncryptionCommand::RecoverKeyBackup(
31ce2453c82fatagen2026-09-24 13:07 UTC545
            recovery_key.into(),
31ce2453c82fatagen2026-09-24 13:07 UTC546
        )))
96fdf90aacd6atagen2026-09-08 08:24 UTC547
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC548
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC549
31ce2453c82fatagen2026-09-24 13:07 UTC550
    pub async fn start_verification(&self, target: VerificationTarget) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC551
        self.run(Command::Encryption(EncryptionCommand::StartVerification(
31ce2453c82fatagen2026-09-24 13:07 UTC552
            target,
31ce2453c82fatagen2026-09-24 13:07 UTC553
        )))
96fdf90aacd6atagen2026-09-08 08:24 UTC554
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC555
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC556
31ce2453c82fatagen2026-09-24 13:07 UTC557
    pub async fn verify(&self, action: VerificationAction) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC558
        self.run(Command::Encryption(EncryptionCommand::Verify(action)))
31ce2453c82fatagen2026-09-24 13:07 UTC559
            .await
96fdf90aacd6atagen2026-09-08 08:24 UTC560
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC561
5d2974de5826atagen2026-10-08 11:29 UTC562
    /// bytes for an avatar, or for media a retained timeline showed you. concurrent asks share
5d2974de5826atagen2026-10-08 11:29 UTC563
    /// one download
31ce2453c82fatagen2026-09-24 13:07 UTC564
    pub async fn media(&self, media_id: &MediaId, size: MediaSize) -> Result<Media, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC565
        self.media.timeline_media(media_id, size).await
31ce2453c82fatagen2026-09-24 13:07 UTC566
    }
31ce2453c82fatagen2026-09-24 13:07 UTC567
5b21b919d1e6atagen2026-09-27 11:47 UTC568
    /// where `media` would fetch from, for a host streaming big files itself. the byte cap
5b21b919d1e6atagen2026-09-27 11:47 UTC569
    /// doesn't apply since nothing is buffered
5b21b919d1e6atagen2026-09-27 11:47 UTC570
    pub async fn media_source(
5b21b919d1e6atagen2026-09-27 11:47 UTC571
        &self,
5b21b919d1e6atagen2026-09-27 11:47 UTC572
        media_id: &MediaId,
5b21b919d1e6atagen2026-09-27 11:47 UTC573
        size: MediaSize,
5b21b919d1e6atagen2026-09-27 11:47 UTC574
    ) -> Result<MediaLocation, Error> {
5b21b919d1e6atagen2026-09-27 11:47 UTC575
        self.media.media_location(media_id, size).await
5b21b919d1e6atagen2026-09-27 11:47 UTC576
    }
5b21b919d1e6atagen2026-09-27 11:47 UTC577
31ce2453c82fatagen2026-09-24 13:07 UTC578
    /// the space graph builds on first use and then keeps itself current
31ce2453c82fatagen2026-09-24 13:07 UTC579
    async fn spaces(&self) -> &SpaceService {
31ce2453c82fatagen2026-09-24 13:07 UTC580
        self.spaces
31ce2453c82fatagen2026-09-24 13:07 UTC581
            .get_or_init(|| SpaceService::new(self.client.clone()))
31ce2453c82fatagen2026-09-24 13:07 UTC582
            .await
96fdf90aacd6atagen2026-09-08 08:24 UTC583
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC584
96fdf90aacd6atagen2026-09-08 08:24 UTC585
    pub async fn rooms(&self) -> Vec<RoomSummary> {
31ce2453c82fatagen2026-09-24 13:07 UTC586
        room_summaries(&self.client, self.spaces().await).await
96fdf90aacd6atagen2026-09-08 08:24 UTC587
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC588
17e042fbc331atagen2026-10-08 12:39 UTC589
    pub async fn image_packs(&self) -> Result<ImagePacks, Error> {
17e042fbc331atagen2026-10-08 12:39 UTC590
        image_packs(&self.client).await
17e042fbc331atagen2026-10-08 12:39 UTC591
    }
17e042fbc331atagen2026-10-08 12:39 UTC592
0291516d40b5atagen2026-10-22 08:31 UTC593
    /// the homeserver's preview of a page, `None` when it has none. the homeserver sees every
0291516d40b5atagen2026-10-22 08:31 UTC594
    /// url asked about, so a host decides whether encrypted rooms ask at all
0291516d40b5atagen2026-10-22 08:31 UTC595
    pub async fn url_preview(&self, url: &str) -> Result<Option<LinkPreview>, Error> {
0291516d40b5atagen2026-10-22 08:31 UTC596
        self.media.url_preview(url).await
0291516d40b5atagen2026-10-22 08:31 UTC597
    }
0291516d40b5atagen2026-10-22 08:31 UTC598
0291516d40b5atagen2026-10-22 08:31 UTC599
    /// whether the room and the user's settings let link previews show here
0291516d40b5atagen2026-10-22 08:31 UTC600
    pub async fn url_previews_allowed(&self, room_id: &RoomId) -> Result<bool, Error> {
0291516d40b5atagen2026-10-22 08:31 UTC601
        url_previews_allowed(&self.room(room_id)?).await
0291516d40b5atagen2026-10-22 08:31 UTC602
    }
0291516d40b5atagen2026-10-22 08:31 UTC603
31ce2453c82fatagen2026-09-24 13:07 UTC604
    /// browse a space the way the server sees it, rooms you haven't joined included
31ce2453c82fatagen2026-09-24 13:07 UTC605
    pub async fn space_hierarchy(&self, space_id: OwnedRoomId) -> SpaceHierarchy {
31ce2453c82fatagen2026-09-24 13:07 UTC606
        SpaceHierarchy(SpaceRoomList::new(self.client.clone(), space_id).await)
31ce2453c82fatagen2026-09-24 13:07 UTC607
    }
31ce2453c82fatagen2026-09-24 13:07 UTC608
31ce2453c82fatagen2026-09-24 13:07 UTC609
    pub async fn own_profile(&self) -> Result<Profile, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC610
        let user_id = self
31ce2453c82fatagen2026-09-24 13:07 UTC611
            .client
31ce2453c82fatagen2026-09-24 13:07 UTC612
            .user_id()
31ce2453c82fatagen2026-09-24 13:07 UTC613
            .ok_or_else(|| Error::new(ErrorKind::Unauthenticated, "the client is not logged in"))?
31ce2453c82fatagen2026-09-24 13:07 UTC614
            .to_owned();
31ce2453c82fatagen2026-09-24 13:07 UTC615
        let response = self
31ce2453c82fatagen2026-09-24 13:07 UTC616
            .client
31ce2453c82fatagen2026-09-24 13:07 UTC617
            .account()
31ce2453c82fatagen2026-09-24 13:07 UTC618
            .fetch_user_profile()
31ce2453c82fatagen2026-09-24 13:07 UTC619
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC620
            .map_err(|source| Error::from_sdk("fetching the profile failed", source))?;
31ce2453c82fatagen2026-09-24 13:07 UTC621
        profile(user_id, &response)
31ce2453c82fatagen2026-09-24 13:07 UTC622
    }
31ce2453c82fatagen2026-09-24 13:07 UTC623
31ce2453c82fatagen2026-09-24 13:07 UTC624
    pub async fn user_profile(&self, user_id: &UserId) -> Result<Profile, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC625
        let response = self
31ce2453c82fatagen2026-09-24 13:07 UTC626
            .client
31ce2453c82fatagen2026-09-24 13:07 UTC627
            .account()
31ce2453c82fatagen2026-09-24 13:07 UTC628
            .fetch_user_profile_of(user_id)
31ce2453c82fatagen2026-09-24 13:07 UTC629
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC630
            .map_err(|source| Error::from_sdk("fetching the profile failed", source))?;
31ce2453c82fatagen2026-09-24 13:07 UTC631
        profile(user_id.to_owned(), &response)
31ce2453c82fatagen2026-09-24 13:07 UTC632
    }
31ce2453c82fatagen2026-09-24 13:07 UTC633
31ce2453c82fatagen2026-09-24 13:07 UTC634
    pub async fn set_display_name(&self, name: Option<String>) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC635
        self.run(Command::Account(AccountCommand::SetDisplayName(name)))
31ce2453c82fatagen2026-09-24 13:07 UTC636
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC637
    }
31ce2453c82fatagen2026-09-24 13:07 UTC638
31ce2453c82fatagen2026-09-24 13:07 UTC639
    pub async fn set_avatar(&self, avatar: Option<OwnedMxcUri>) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC640
        self.run(Command::Account(AccountCommand::SetAvatar(avatar)))
31ce2453c82fatagen2026-09-24 13:07 UTC641
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC642
    }
31ce2453c82fatagen2026-09-24 13:07 UTC643
31ce2453c82fatagen2026-09-24 13:07 UTC644
    pub async fn upload_avatar(
31ce2453c82fatagen2026-09-24 13:07 UTC645
        &self,
31ce2453c82fatagen2026-09-24 13:07 UTC646
        mimetype: impl Into<String>,
31ce2453c82fatagen2026-09-24 13:07 UTC647
        bytes: Vec<u8>,
31ce2453c82fatagen2026-09-24 13:07 UTC648
    ) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC649
        self.run(Command::Account(AccountCommand::UploadAvatar {
31ce2453c82fatagen2026-09-24 13:07 UTC650
            mimetype: mimetype.into(),
31ce2453c82fatagen2026-09-24 13:07 UTC651
            bytes,
31ce2453c82fatagen2026-09-24 13:07 UTC652
        }))
31ce2453c82fatagen2026-09-24 13:07 UTC653
        .await
31ce2453c82fatagen2026-09-24 13:07 UTC654
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC655
9bd34e25bb86atagen2026-10-24 07:23 UTC656
    /// unencrypted, for room avatars and anything else that only needs the uri
9bd34e25bb86atagen2026-10-24 07:23 UTC657
    pub async fn upload_media(
9bd34e25bb86atagen2026-10-24 07:23 UTC658
        &self,
9bd34e25bb86atagen2026-10-24 07:23 UTC659
        mimetype: impl Into<String>,
9bd34e25bb86atagen2026-10-24 07:23 UTC660
        bytes: Vec<u8>,
9bd34e25bb86atagen2026-10-24 07:23 UTC661
    ) -> Result<OwnedMxcUri, Error> {
9bd34e25bb86atagen2026-10-24 07:23 UTC662
        let command = Command::Account(AccountCommand::UploadMedia {
9bd34e25bb86atagen2026-10-24 07:23 UTC663
            mimetype: mimetype.into(),
9bd34e25bb86atagen2026-10-24 07:23 UTC664
            bytes,
9bd34e25bb86atagen2026-10-24 07:23 UTC665
        });
9bd34e25bb86atagen2026-10-24 07:23 UTC666
        match self.execute(command).await? {
9bd34e25bb86atagen2026-10-24 07:23 UTC667
            CommandOutput::Mxc(uri) => Ok(uri),
9bd34e25bb86atagen2026-10-24 07:23 UTC668
            output => Err(Error::new(
9bd34e25bb86atagen2026-10-24 07:23 UTC669
                ErrorKind::Other,
9bd34e25bb86atagen2026-10-24 07:23 UTC670
                format!("expected an mxc but got {output:?}"),
9bd34e25bb86atagen2026-10-24 07:23 UTC671
            )),
9bd34e25bb86atagen2026-10-24 07:23 UTC672
        }
9bd34e25bb86atagen2026-10-24 07:23 UTC673
    }
9bd34e25bb86atagen2026-10-24 07:23 UTC674
9bd34e25bb86atagen2026-10-24 07:23 UTC675
    pub async fn ignore_user(&self, user_id: OwnedUserId) -> Result<(), Error> {
9bd34e25bb86atagen2026-10-24 07:23 UTC676
        self.run(Command::Account(AccountCommand::IgnoreUser(user_id)))
9bd34e25bb86atagen2026-10-24 07:23 UTC677
            .await
9bd34e25bb86atagen2026-10-24 07:23 UTC678
    }
9bd34e25bb86atagen2026-10-24 07:23 UTC679
9bd34e25bb86atagen2026-10-24 07:23 UTC680
    pub async fn unignore_user(&self, user_id: OwnedUserId) -> Result<(), Error> {
9bd34e25bb86atagen2026-10-24 07:23 UTC681
        self.run(Command::Account(AccountCommand::UnignoreUser(user_id)))
9bd34e25bb86atagen2026-10-24 07:23 UTC682
            .await
9bd34e25bb86atagen2026-10-24 07:23 UTC683
    }
9bd34e25bb86atagen2026-10-24 07:23 UTC684
9bd34e25bb86atagen2026-10-24 07:23 UTC685
    /// writes msc3230 orders so these spaces sort in exactly this sequence, touching as few as it can
9bd34e25bb86atagen2026-10-24 07:23 UTC686
    pub async fn reorder_spaces(&self, order: Vec<OwnedRoomId>) -> Result<(), Error> {
9bd34e25bb86atagen2026-10-24 07:23 UTC687
        self.run(Command::Account(AccountCommand::ReorderSpaces(order)))
9bd34e25bb86atagen2026-10-24 07:23 UTC688
            .await
9bd34e25bb86atagen2026-10-24 07:23 UTC689
    }
9bd34e25bb86atagen2026-10-24 07:23 UTC690
9bd34e25bb86atagen2026-10-24 07:23 UTC691
    /// adding m.favourite drops m.lowpriority, the way Element does
9bd34e25bb86atagen2026-10-24 07:23 UTC692
    pub async fn set_favourite(&self, room_id: OwnedRoomId, favourite: bool) -> Result<(), Error> {
9bd34e25bb86atagen2026-10-24 07:23 UTC693
        self.run(Command::Room(RoomCommand::SetFavourite {
9bd34e25bb86atagen2026-10-24 07:23 UTC694
            room_id,
9bd34e25bb86atagen2026-10-24 07:23 UTC695
            favourite,
9bd34e25bb86atagen2026-10-24 07:23 UTC696
        }))
9bd34e25bb86atagen2026-10-24 07:23 UTC697
        .await
9bd34e25bb86atagen2026-10-24 07:23 UTC698
    }
9bd34e25bb86atagen2026-10-24 07:23 UTC699
9bd34e25bb86atagen2026-10-24 07:23 UTC700
    /// None goes back to your global name
9bd34e25bb86atagen2026-10-24 07:23 UTC701
    pub async fn set_room_display_name(
9bd34e25bb86atagen2026-10-24 07:23 UTC702
        &self,
9bd34e25bb86atagen2026-10-24 07:23 UTC703
        room_id: OwnedRoomId,
9bd34e25bb86atagen2026-10-24 07:23 UTC704
        name: Option<String>,
9bd34e25bb86atagen2026-10-24 07:23 UTC705
    ) -> Result<(), Error> {
9bd34e25bb86atagen2026-10-24 07:23 UTC706
        self.run(Command::Room(RoomCommand::SetOwnDisplayName {
9bd34e25bb86atagen2026-10-24 07:23 UTC707
            room_id,
9bd34e25bb86atagen2026-10-24 07:23 UTC708
            name,
9bd34e25bb86atagen2026-10-24 07:23 UTC709
        }))
9bd34e25bb86atagen2026-10-24 07:23 UTC710
        .await
9bd34e25bb86atagen2026-10-24 07:23 UTC711
    }
9bd34e25bb86atagen2026-10-24 07:23 UTC712
9bd34e25bb86atagen2026-10-24 07:23 UTC713
    /// None goes back to your global avatar
9bd34e25bb86atagen2026-10-24 07:23 UTC714
    pub async fn set_room_avatar(
9bd34e25bb86atagen2026-10-24 07:23 UTC715
        &self,
9bd34e25bb86atagen2026-10-24 07:23 UTC716
        room_id: OwnedRoomId,
9bd34e25bb86atagen2026-10-24 07:23 UTC717
        avatar: Option<OwnedMxcUri>,
9bd34e25bb86atagen2026-10-24 07:23 UTC718
    ) -> Result<(), Error> {
9bd34e25bb86atagen2026-10-24 07:23 UTC719
        self.run(Command::Room(RoomCommand::SetOwnAvatar { room_id, avatar }))
9bd34e25bb86atagen2026-10-24 07:23 UTC720
            .await
9bd34e25bb86atagen2026-10-24 07:23 UTC721
    }
9bd34e25bb86atagen2026-10-24 07:23 UTC722
9bd34e25bb86atagen2026-10-24 07:23 UTC723
    /// muting drops them below what sending a message takes, unmuting puts a muted user back
9bd34e25bb86atagen2026-10-24 07:23 UTC724
    /// at users_default and leaves anyone else alone. `ErrorKind::Forbidden` if you can't
9bd34e25bb86atagen2026-10-24 07:23 UTC725
    pub async fn mute_user(
9bd34e25bb86atagen2026-10-24 07:23 UTC726
        &self,
9bd34e25bb86atagen2026-10-24 07:23 UTC727
        room_id: OwnedRoomId,
9bd34e25bb86atagen2026-10-24 07:23 UTC728
        user_id: OwnedUserId,
9bd34e25bb86atagen2026-10-24 07:23 UTC729
        muted: bool,
9bd34e25bb86atagen2026-10-24 07:23 UTC730
    ) -> Result<(), Error> {
9bd34e25bb86atagen2026-10-24 07:23 UTC731
        self.run(Command::Room(RoomCommand::MuteUser {
9bd34e25bb86atagen2026-10-24 07:23 UTC732
            room_id,
9bd34e25bb86atagen2026-10-24 07:23 UTC733
            user_id,
9bd34e25bb86atagen2026-10-24 07:23 UTC734
            muted,
9bd34e25bb86atagen2026-10-24 07:23 UTC735
        }))
9bd34e25bb86atagen2026-10-24 07:23 UTC736
        .await
9bd34e25bb86atagen2026-10-24 07:23 UTC737
    }
9bd34e25bb86atagen2026-10-24 07:23 UTC738
31ce2453c82fatagen2026-09-24 13:07 UTC739
    pub async fn rooms_filtered(&self, filter: &RoomListFilter) -> Vec<RoomSummary> {
31ce2453c82fatagen2026-09-24 13:07 UTC740
        let matcher = filter.matcher();
96fdf90aacd6atagen2026-09-08 08:24 UTC741
        self.rooms()
96fdf90aacd6atagen2026-09-08 08:24 UTC742
            .await
96fdf90aacd6atagen2026-09-08 08:24 UTC743
            .into_iter()
31ce2453c82fatagen2026-09-24 13:07 UTC744
            .filter(|room| matcher.matches(room))
96fdf90aacd6atagen2026-09-08 08:24 UTC745
            .collect()
96fdf90aacd6atagen2026-09-08 08:24 UTC746
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC747
31ce2453c82fatagen2026-09-24 13:07 UTC748
    pub async fn devices(&self) -> Result<Vec<OwnDevice>, Error> {
96fdf90aacd6atagen2026-09-08 08:24 UTC749
        let current_device_id = self.client.device_id();
96fdf90aacd6atagen2026-09-08 08:24 UTC750
        Ok(self
96fdf90aacd6atagen2026-09-08 08:24 UTC751
            .client
96fdf90aacd6atagen2026-09-08 08:24 UTC752
            .devices()
96fdf90aacd6atagen2026-09-08 08:24 UTC753
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC754
            .map_err(|source| Error::from_http("listing devices failed", source))?
96fdf90aacd6atagen2026-09-08 08:24 UTC755
            .devices
96fdf90aacd6atagen2026-09-08 08:24 UTC756
            .into_iter()
96fdf90aacd6atagen2026-09-08 08:24 UTC757
            .map(|device| OwnDevice {
96fdf90aacd6atagen2026-09-08 08:24 UTC758
                current: current_device_id == Some(device.device_id.as_ref()),
96fdf90aacd6atagen2026-09-08 08:24 UTC759
                device_id: device.device_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC760
                display_name: device.display_name,
96fdf90aacd6atagen2026-09-08 08:24 UTC761
                last_seen_ip: device.last_seen_ip,
96fdf90aacd6atagen2026-09-08 08:24 UTC762
                last_seen_timestamp_ms: device.last_seen_ts.map(|timestamp| timestamp.get().into()),
96fdf90aacd6atagen2026-09-08 08:24 UTC763
            })
96fdf90aacd6atagen2026-09-08 08:24 UTC764
            .collect())
96fdf90aacd6atagen2026-09-08 08:24 UTC765
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC766
31ce2453c82fatagen2026-09-24 13:07 UTC767
    fn room(&self, room_id: &RoomId) -> Result<matrix_sdk::Room, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC768
        self.client
31ce2453c82fatagen2026-09-24 13:07 UTC769
            .get_room(room_id)
31ce2453c82fatagen2026-09-24 13:07 UTC770
            .ok_or_else(Error::room_unknown)
31ce2453c82fatagen2026-09-24 13:07 UTC771
    }
31ce2453c82fatagen2026-09-24 13:07 UTC772
96fdf90aacd6atagen2026-09-08 08:24 UTC773
    pub async fn room_notification_settings(
96fdf90aacd6atagen2026-09-08 08:24 UTC774
        &self,
96fdf90aacd6atagen2026-09-08 08:24 UTC775
        room_id: &RoomId,
31ce2453c82fatagen2026-09-24 13:07 UTC776
    ) -> Result<Option<RoomNotificationSettings>, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC777
        let room = self.room(room_id)?;
96fdf90aacd6atagen2026-09-08 08:24 UTC778
        let custom = room.user_defined_notification_mode().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC779
        let mode = match custom {
96fdf90aacd6atagen2026-09-08 08:24 UTC780
            Some(mode) => mode,
96fdf90aacd6atagen2026-09-08 08:24 UTC781
            None => match room.notification_mode().await {
96fdf90aacd6atagen2026-09-08 08:24 UTC782
                Some(mode) => mode,
96fdf90aacd6atagen2026-09-08 08:24 UTC783
                None => return Ok(None),
96fdf90aacd6atagen2026-09-08 08:24 UTC784
            },
96fdf90aacd6atagen2026-09-08 08:24 UTC785
        };
96fdf90aacd6atagen2026-09-08 08:24 UTC786
        Ok(Some(RoomNotificationSettings {
96fdf90aacd6atagen2026-09-08 08:24 UTC787
            mode: crate::normalise::notification_mode(mode),
96fdf90aacd6atagen2026-09-08 08:24 UTC788
            custom: custom.is_some(),
96fdf90aacd6atagen2026-09-08 08:24 UTC789
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC790
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC791
96fdf90aacd6atagen2026-09-08 08:24 UTC792
    pub async fn notification_keywords(&self) -> Vec<String> {
96fdf90aacd6atagen2026-09-08 08:24 UTC793
        self.client
96fdf90aacd6atagen2026-09-08 08:24 UTC794
            .notification_settings()
96fdf90aacd6atagen2026-09-08 08:24 UTC795
            .await
96fdf90aacd6atagen2026-09-08 08:24 UTC796
            .enabled_keywords()
96fdf90aacd6atagen2026-09-08 08:24 UTC797
            .await
96fdf90aacd6atagen2026-09-08 08:24 UTC798
            .into_iter()
96fdf90aacd6atagen2026-09-08 08:24 UTC799
            .collect()
96fdf90aacd6atagen2026-09-08 08:24 UTC800
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC801
96fdf90aacd6atagen2026-09-08 08:24 UTC802
    pub async fn resolve_push_notification(
96fdf90aacd6atagen2026-09-08 08:24 UTC803
        &self,
96fdf90aacd6atagen2026-09-08 08:24 UTC804
        room_id: &RoomId,
96fdf90aacd6atagen2026-09-08 08:24 UTC805
        event_id: &EventId,
31ce2453c82fatagen2026-09-24 13:07 UTC806
    ) -> Result<PushNotificationStatus, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC807
        let notification_failed = |source| {
31ce2453c82fatagen2026-09-24 13:07 UTC808
            Error::with_source(
31ce2453c82fatagen2026-09-24 13:07 UTC809
                ErrorKind::Other,
31ce2453c82fatagen2026-09-24 13:07 UTC810
                "resolving the push notification failed",
31ce2453c82fatagen2026-09-24 13:07 UTC811
                source,
31ce2453c82fatagen2026-09-24 13:07 UTC812
            )
31ce2453c82fatagen2026-09-24 13:07 UTC813
        };
96fdf90aacd6atagen2026-09-08 08:24 UTC814
        let client = self
96fdf90aacd6atagen2026-09-08 08:24 UTC815
            .notification_client
96fdf90aacd6atagen2026-09-08 08:24 UTC816
            .get_or_try_init(|| async {
96fdf90aacd6atagen2026-09-08 08:24 UTC817
                NotificationClient::new(
96fdf90aacd6atagen2026-09-08 08:24 UTC818
                    self.client.clone(),
96fdf90aacd6atagen2026-09-08 08:24 UTC819
                    NotificationProcessSetup::SingleProcess {
96fdf90aacd6atagen2026-09-08 08:24 UTC820
                        sync_service: self.sync_service.clone(),
96fdf90aacd6atagen2026-09-08 08:24 UTC821
                    },
96fdf90aacd6atagen2026-09-08 08:24 UTC822
                )
96fdf90aacd6atagen2026-09-08 08:24 UTC823
                .await
96fdf90aacd6atagen2026-09-08 08:24 UTC824
            })
96fdf90aacd6atagen2026-09-08 08:24 UTC825
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC826
            .map_err(notification_failed)?;
96fdf90aacd6atagen2026-09-08 08:24 UTC827
        let status = client
96fdf90aacd6atagen2026-09-08 08:24 UTC828
            .get_notification(room_id, event_id)
96fdf90aacd6atagen2026-09-08 08:24 UTC829
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC830
            .map_err(notification_failed)?;
96fdf90aacd6atagen2026-09-08 08:24 UTC831
        Ok(normalise_push_notification(room_id, event_id, status))
96fdf90aacd6atagen2026-09-08 08:24 UTC832
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC833
96fdf90aacd6atagen2026-09-08 08:24 UTC834
    pub async fn members(
96fdf90aacd6atagen2026-09-08 08:24 UTC835
        &self,
96fdf90aacd6atagen2026-09-08 08:24 UTC836
        room_id: &RoomId,
96fdf90aacd6atagen2026-09-08 08:24 UTC837
        filter: MemberFilter,
31ce2453c82fatagen2026-09-24 13:07 UTC838
    ) -> Result<Vec<MemberSummary>, Error> {
121a56ce119datagen2026-10-24 06:42 UTC839
        let room = self.room(room_id)?;
121a56ce119datagen2026-10-24 06:42 UTC840
        // stores have been seen with members_synced persisted over a partial
121a56ce119datagen2026-10-24 06:42 UTC841
        // roster, and the sdk never asks /members again once it's set
9bd34e25bb86atagen2026-10-24 07:23 UTC842
        if self
9bd34e25bb86atagen2026-10-24 07:23 UTC843
            .members_checked
9bd34e25bb86atagen2026-10-24 07:23 UTC844
            .lock()
9bd34e25bb86atagen2026-10-24 07:23 UTC845
            .unwrap()
9bd34e25bb86atagen2026-10-24 07:23 UTC846
            .insert(room_id.to_owned())
9bd34e25bb86atagen2026-10-24 07:23 UTC847
        {
121a56ce119datagen2026-10-24 06:42 UTC848
            room.mark_members_missing();
121a56ce119datagen2026-10-24 06:42 UTC849
        }
121a56ce119datagen2026-10-24 06:42 UTC850
        list_members(&room, filter).await
121a56ce119datagen2026-10-24 06:42 UTC851
    }
121a56ce119datagen2026-10-24 06:42 UTC852
121a56ce119datagen2026-10-24 06:42 UTC853
    /// drop the cached roster for a room and fetch /members again
121a56ce119datagen2026-10-24 06:42 UTC854
    pub async fn refresh_members(
121a56ce119datagen2026-10-24 06:42 UTC855
        &self,
121a56ce119datagen2026-10-24 06:42 UTC856
        room_id: &RoomId,
121a56ce119datagen2026-10-24 06:42 UTC857
        filter: MemberFilter,
121a56ce119datagen2026-10-24 06:42 UTC858
    ) -> Result<Vec<MemberSummary>, Error> {
121a56ce119datagen2026-10-24 06:42 UTC859
        let room = self.room(room_id)?;
121a56ce119datagen2026-10-24 06:42 UTC860
        room.mark_members_missing();
9bd34e25bb86atagen2026-10-24 07:23 UTC861
        self.members_checked
9bd34e25bb86atagen2026-10-24 07:23 UTC862
            .lock()
9bd34e25bb86atagen2026-10-24 07:23 UTC863
            .unwrap()
9bd34e25bb86atagen2026-10-24 07:23 UTC864
            .insert(room_id.to_owned());
121a56ce119datagen2026-10-24 06:42 UTC865
        list_members(&room, filter).await
96fdf90aacd6atagen2026-09-08 08:24 UTC866
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC867
31ce2453c82fatagen2026-09-24 13:07 UTC868
    pub async fn room_power_levels(&self, room_id: &RoomId) -> Result<RoomPowerLevels, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC869
        let levels = self
31ce2453c82fatagen2026-09-24 13:07 UTC870
            .room(room_id)?
96fdf90aacd6atagen2026-09-08 08:24 UTC871
            .power_levels()
96fdf90aacd6atagen2026-09-08 08:24 UTC872
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC873
            .map_err(|source| Error::from_sdk("reading power levels failed", source.into()))?;
96fdf90aacd6atagen2026-09-08 08:24 UTC874
        Ok(RoomPowerLevels {
96fdf90aacd6atagen2026-09-08 08:24 UTC875
            ban: levels.ban.into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC876
            invite: levels.invite.into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC877
            kick: levels.kick.into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC878
            redact: levels.redact.into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC879
            events_default: levels.events_default.into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC880
            state_default: levels.state_default.into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC881
            users_default: levels.users_default.into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC882
            room_notification: levels.notifications.room.into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC883
            users: levels
96fdf90aacd6atagen2026-09-08 08:24 UTC884
                .users
96fdf90aacd6atagen2026-09-08 08:24 UTC885
                .into_iter()
96fdf90aacd6atagen2026-09-08 08:24 UTC886
                .map(|(user_id, level)| (user_id, level.into()))
96fdf90aacd6atagen2026-09-08 08:24 UTC887
                .collect(),
96fdf90aacd6atagen2026-09-08 08:24 UTC888
            events: levels
96fdf90aacd6atagen2026-09-08 08:24 UTC889
                .events
96fdf90aacd6atagen2026-09-08 08:24 UTC890
                .into_iter()
96fdf90aacd6atagen2026-09-08 08:24 UTC891
                .map(|(event_type, level)| (event_type.to_string(), level.into()))
96fdf90aacd6atagen2026-09-08 08:24 UTC892
                .collect(),
5a52012c9b1datagen2026-09-24 23:45 UTC893
            creators: levels
5a52012c9b1datagen2026-09-24 23:45 UTC894
                .rules
5a52012c9b1datagen2026-09-24 23:45 UTC895
                .privileged_creators
5a52012c9b1datagen2026-09-24 23:45 UTC896
                .into_iter()
5a52012c9b1datagen2026-09-24 23:45 UTC897
                .flatten()
5a52012c9b1datagen2026-09-24 23:45 UTC898
                .collect(),
96fdf90aacd6atagen2026-09-08 08:24 UTC899
        })
96fdf90aacd6atagen2026-09-08 08:24 UTC900
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC901
96fdf90aacd6atagen2026-09-08 08:24 UTC902
    pub async fn pending_send_ids(
96fdf90aacd6atagen2026-09-08 08:24 UTC903
        &self,
96fdf90aacd6atagen2026-09-08 08:24 UTC904
        room_id: &RoomId,
31ce2453c82fatagen2026-09-24 13:07 UTC905
    ) -> Result<Vec<OwnedTransactionId>, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC906
        let (echoes, _) = self
31ce2453c82fatagen2026-09-24 13:07 UTC907
            .room(room_id)?
96fdf90aacd6atagen2026-09-08 08:24 UTC908
            .send_queue()
96fdf90aacd6atagen2026-09-08 08:24 UTC909
            .subscribe()
96fdf90aacd6atagen2026-09-08 08:24 UTC910
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC911
            .map_err(|source| {
31ce2453c82fatagen2026-09-24 13:07 UTC912
                Error::with_source(ErrorKind::Store, "reading the send queue failed", source)
31ce2453c82fatagen2026-09-24 13:07 UTC913
            })?;
96fdf90aacd6atagen2026-09-08 08:24 UTC914
        Ok(echoes
96fdf90aacd6atagen2026-09-08 08:24 UTC915
            .into_iter()
96fdf90aacd6atagen2026-09-08 08:24 UTC916
            .filter(|echo| {
96fdf90aacd6atagen2026-09-08 08:24 UTC917
                matches!(
96fdf90aacd6atagen2026-09-08 08:24 UTC918
                    echo.content,
96fdf90aacd6atagen2026-09-08 08:24 UTC919
                    matrix_sdk::send_queue::LocalEchoContent::Event { .. }
96fdf90aacd6atagen2026-09-08 08:24 UTC920
                )
96fdf90aacd6atagen2026-09-08 08:24 UTC921
            })
96fdf90aacd6atagen2026-09-08 08:24 UTC922
            .map(|echo| echo.transaction_id)
96fdf90aacd6atagen2026-09-08 08:24 UTC923
            .collect())
96fdf90aacd6atagen2026-09-08 08:24 UTC924
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC925
96fdf90aacd6atagen2026-09-08 08:24 UTC926
    pub async fn global_account_data(
96fdf90aacd6atagen2026-09-08 08:24 UTC927
        &self,
96fdf90aacd6atagen2026-09-08 08:24 UTC928
        event_type: GlobalAccountDataEventType,
31ce2453c82fatagen2026-09-24 13:07 UTC929
    ) -> Result<Option<Raw<AnyGlobalAccountDataEventContent>>, Error> {
96fdf90aacd6atagen2026-09-08 08:24 UTC930
        self.client
96fdf90aacd6atagen2026-09-08 08:24 UTC931
            .account()
96fdf90aacd6atagen2026-09-08 08:24 UTC932
            .account_data_raw(event_type)
96fdf90aacd6atagen2026-09-08 08:24 UTC933
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC934
            .map_err(|source| Error::from_sdk("reading account data failed", source))
96fdf90aacd6atagen2026-09-08 08:24 UTC935
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC936
96fdf90aacd6atagen2026-09-08 08:24 UTC937
    pub async fn room_account_data(
96fdf90aacd6atagen2026-09-08 08:24 UTC938
        &self,
96fdf90aacd6atagen2026-09-08 08:24 UTC939
        room_id: &RoomId,
96fdf90aacd6atagen2026-09-08 08:24 UTC940
        event_type: RoomAccountDataEventType,
31ce2453c82fatagen2026-09-24 13:07 UTC941
    ) -> Result<Option<Raw<AnyRoomAccountDataEvent>>, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC942
        self.room(room_id)?
31ce2453c82fatagen2026-09-24 13:07 UTC943
            .account_data(event_type)
96fdf90aacd6atagen2026-09-08 08:24 UTC944
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC945
            .map_err(|source| Error::from_sdk("reading room account data failed", source))
96fdf90aacd6atagen2026-09-08 08:24 UTC946
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC947
96fdf90aacd6atagen2026-09-08 08:24 UTC948
    #[must_use]
96fdf90aacd6atagen2026-09-08 08:24 UTC949
    pub fn connection_state(&self) -> watch::Receiver<ConnectionState> {
96fdf90aacd6atagen2026-09-08 08:24 UTC950
        self.connection.clone()
96fdf90aacd6atagen2026-09-08 08:24 UTC951
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC952
96fdf90aacd6atagen2026-09-08 08:24 UTC953
    #[must_use]
96fdf90aacd6atagen2026-09-08 08:24 UTC954
    pub fn encryption_state(&self) -> Option<EncryptionState> {
96fdf90aacd6atagen2026-09-08 08:24 UTC955
        let device_id = self.client.device_id()?.to_owned();
96fdf90aacd6atagen2026-09-08 08:24 UTC956
        let encryption = self.client.encryption();
96fdf90aacd6atagen2026-09-08 08:24 UTC957
        Some(encryption_state(
96fdf90aacd6atagen2026-09-08 08:24 UTC958
            device_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC959
            encryption.recovery().state(),
96fdf90aacd6atagen2026-09-08 08:24 UTC960
            encryption.verification_state().get(),
96fdf90aacd6atagen2026-09-08 08:24 UTC961
            encryption.backups().state(),
96fdf90aacd6atagen2026-09-08 08:24 UTC962
        ))
96fdf90aacd6atagen2026-09-08 08:24 UTC963
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC964
}
96fdf90aacd6atagen2026-09-08 08:24 UTC965
31ce2453c82fatagen2026-09-24 13:07 UTC966
/// 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 UTC967
pub struct SpaceHierarchy(SpaceRoomList);
31ce2453c82fatagen2026-09-24 13:07 UTC968
31ce2453c82fatagen2026-09-24 13:07 UTC969
impl fmt::Debug for SpaceHierarchy {
31ce2453c82fatagen2026-09-24 13:07 UTC970
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
31ce2453c82fatagen2026-09-24 13:07 UTC971
        formatter
31ce2453c82fatagen2026-09-24 13:07 UTC972
            .debug_struct("SpaceHierarchy")
31ce2453c82fatagen2026-09-24 13:07 UTC973
            .finish_non_exhaustive()
31ce2453c82fatagen2026-09-24 13:07 UTC974
    }
31ce2453c82fatagen2026-09-24 13:07 UTC975
}
31ce2453c82fatagen2026-09-24 13:07 UTC976
31ce2453c82fatagen2026-09-24 13:07 UTC977
impl SpaceHierarchy {
31ce2453c82fatagen2026-09-24 13:07 UTC978
    /// fetches the next page. a no-op once the end is reached or while a page is in flight
31ce2453c82fatagen2026-09-24 13:07 UTC979
    pub async fn paginate(&self) -> Result<(), Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC980
        self.0.paginate().await.map_err(|source| {
31ce2453c82fatagen2026-09-24 13:07 UTC981
            Error::with_source(
31ce2453c82fatagen2026-09-24 13:07 UTC982
                ErrorKind::Server,
31ce2453c82fatagen2026-09-24 13:07 UTC983
                "fetching the space hierarchy failed",
31ce2453c82fatagen2026-09-24 13:07 UTC984
                source,
31ce2453c82fatagen2026-09-24 13:07 UTC985
            )
31ce2453c82fatagen2026-09-24 13:07 UTC986
        })
31ce2453c82fatagen2026-09-24 13:07 UTC987
    }
31ce2453c82fatagen2026-09-24 13:07 UTC988
31ce2453c82fatagen2026-09-24 13:07 UTC989
    /// everything fetched so far, the space itself excluded
31ce2453c82fatagen2026-09-24 13:07 UTC990
    pub async fn rooms(&self) -> Vec<SpaceChild> {
31ce2453c82fatagen2026-09-24 13:07 UTC991
        self.0.rooms().await.into_iter().map(space_child).collect()
31ce2453c82fatagen2026-09-24 13:07 UTC992
    }
31ce2453c82fatagen2026-09-24 13:07 UTC993
31ce2453c82fatagen2026-09-24 13:07 UTC994
    #[must_use]
31ce2453c82fatagen2026-09-24 13:07 UTC995
    pub fn end_reached(&self) -> bool {
31ce2453c82fatagen2026-09-24 13:07 UTC996
        matches!(
31ce2453c82fatagen2026-09-24 13:07 UTC997
            self.0.pagination_state(),
31ce2453c82fatagen2026-09-24 13:07 UTC998
            SpaceRoomListPaginationState::Idle { end_reached: true }
31ce2453c82fatagen2026-09-24 13:07 UTC999
        )
31ce2453c82fatagen2026-09-24 13:07 UTC1000
    }
31ce2453c82fatagen2026-09-24 13:07 UTC1001
}
31ce2453c82fatagen2026-09-24 13:07 UTC1002
31ce2453c82fatagen2026-09-24 13:07 UTC1003
fn profile(
31ce2453c82fatagen2026-09-24 13:07 UTC1004
    user_id: OwnedUserId,
31ce2453c82fatagen2026-09-24 13:07 UTC1005
    response: &ruma::api::client::profile::get_profile::v3::Response,
31ce2453c82fatagen2026-09-24 13:07 UTC1006
) -> Result<Profile, Error> {
31ce2453c82fatagen2026-09-24 13:07 UTC1007
    use ruma::api::client::profile::{AvatarUrl, DisplayName};
31ce2453c82fatagen2026-09-24 13:07 UTC1008
    let malformed = |source| {
31ce2453c82fatagen2026-09-24 13:07 UTC1009
        Error::with_source(
31ce2453c82fatagen2026-09-24 13:07 UTC1010
            ErrorKind::Server,
31ce2453c82fatagen2026-09-24 13:07 UTC1011
            "the server sent a malformed profile",
31ce2453c82fatagen2026-09-24 13:07 UTC1012
            source,
31ce2453c82fatagen2026-09-24 13:07 UTC1013
        )
31ce2453c82fatagen2026-09-24 13:07 UTC1014
    };
31ce2453c82fatagen2026-09-24 13:07 UTC1015
    Ok(Profile {
31ce2453c82fatagen2026-09-24 13:07 UTC1016
        user_id,
31ce2453c82fatagen2026-09-24 13:07 UTC1017
        display_name: response.get_static::<DisplayName>().map_err(malformed)?,
5d2974de5826atagen2026-10-08 11:29 UTC1018
        avatar: response
5d2974de5826atagen2026-10-08 11:29 UTC1019
            .get_static::<AvatarUrl>()
5d2974de5826atagen2026-10-08 11:29 UTC1020
            .map_err(malformed)?
5d2974de5826atagen2026-10-08 11:29 UTC1021
            .as_deref()
5d2974de5826atagen2026-10-08 11:29 UTC1022
            .map(MediaId::plain),
31ce2453c82fatagen2026-09-24 13:07 UTC1023
    })
31ce2453c82fatagen2026-09-24 13:07 UTC1024
}
31ce2453c82fatagen2026-09-24 13:07 UTC1025
31ce2453c82fatagen2026-09-24 13:07 UTC1026
#[derive(Debug)]
31ce2453c82fatagen2026-09-24 13:07 UTC1027
pub(crate) struct Request {
96fdf90aacd6atagen2026-09-08 08:24 UTC1028
    pub(crate) command: Command,
31ce2453c82fatagen2026-09-24 13:07 UTC1029
    pub(crate) reply: Responder,
31ce2453c82fatagen2026-09-24 13:07 UTC1030
}
31ce2453c82fatagen2026-09-24 13:07 UTC1031
31ce2453c82fatagen2026-09-24 13:07 UTC1032
impl Request {
31ce2453c82fatagen2026-09-24 13:07 UTC1033
    fn new(command: Command) -> (Self, Pending) {
31ce2453c82fatagen2026-09-24 13:07 UTC1034
        let (reply, pending) = oneshot::channel();
31ce2453c82fatagen2026-09-24 13:07 UTC1035
        (
31ce2453c82fatagen2026-09-24 13:07 UTC1036
            Self {
31ce2453c82fatagen2026-09-24 13:07 UTC1037
                command,
31ce2453c82fatagen2026-09-24 13:07 UTC1038
                reply: Responder(reply),
31ce2453c82fatagen2026-09-24 13:07 UTC1039
            },
31ce2453c82fatagen2026-09-24 13:07 UTC1040
            Pending(pending),
31ce2453c82fatagen2026-09-24 13:07 UTC1041
        )
31ce2453c82fatagen2026-09-24 13:07 UTC1042
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1043
}
96fdf90aacd6atagen2026-09-08 08:24 UTC1044
31ce2453c82fatagen2026-09-24 13:07 UTC1045
/// whoever holds this owes the caller an answer. dropping it answers `Interrupted`
96fdf90aacd6atagen2026-09-08 08:24 UTC1046
#[derive(Debug)]
31ce2453c82fatagen2026-09-24 13:07 UTC1047
pub(crate) struct Responder(oneshot::Sender<Result<CommandOutput, Error>>);
31ce2453c82fatagen2026-09-24 13:07 UTC1048
31ce2453c82fatagen2026-09-24 13:07 UTC1049
impl Responder {
31ce2453c82fatagen2026-09-24 13:07 UTC1050
    pub(crate) fn send(self, result: Result<CommandOutput, Error>) {
31ce2453c82fatagen2026-09-24 13:07 UTC1051
        if let Err(error) = &result {
31ce2453c82fatagen2026-09-24 13:07 UTC1052
            tracing::debug!(%error, kind = ?error.kind, "command failed");
31ce2453c82fatagen2026-09-24 13:07 UTC1053
        }
31ce2453c82fatagen2026-09-24 13:07 UTC1054
        let _ = self.0.send(result);
31ce2453c82fatagen2026-09-24 13:07 UTC1055
    }
31ce2453c82fatagen2026-09-24 13:07 UTC1056
31ce2453c82fatagen2026-09-24 13:07 UTC1057
    pub(crate) fn done(self, result: Result<(), Error>) {
31ce2453c82fatagen2026-09-24 13:07 UTC1058
        self.send(result.map(|()| CommandOutput::Done));
31ce2453c82fatagen2026-09-24 13:07 UTC1059
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1060
}
96fdf90aacd6atagen2026-09-08 08:24 UTC1061
96fdf90aacd6atagen2026-09-08 08:24 UTC1062
#[derive(Debug)]
96fdf90aacd6atagen2026-09-08 08:24 UTC1063
pub struct EventStream {
96fdf90aacd6atagen2026-09-08 08:24 UTC1064
    events: mpsc::Receiver<Event>,
96fdf90aacd6atagen2026-09-08 08:24 UTC1065
}
96fdf90aacd6atagen2026-09-08 08:24 UTC1066
96fdf90aacd6atagen2026-09-08 08:24 UTC1067
impl EventStream {
96fdf90aacd6atagen2026-09-08 08:24 UTC1068
    pub async fn recv(&mut self) -> Option<Event> {
96fdf90aacd6atagen2026-09-08 08:24 UTC1069
        self.events.recv().await
96fdf90aacd6atagen2026-09-08 08:24 UTC1070
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1071
96fdf90aacd6atagen2026-09-08 08:24 UTC1072
    pub fn try_recv(&mut self) -> Result<Event, mpsc::error::TryRecvError> {
96fdf90aacd6atagen2026-09-08 08:24 UTC1073
        self.events.try_recv()
96fdf90aacd6atagen2026-09-08 08:24 UTC1074
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1075
}
96fdf90aacd6atagen2026-09-08 08:24 UTC1076
96fdf90aacd6atagen2026-09-08 08:24 UTC1077
impl futures_util::Stream for EventStream {
96fdf90aacd6atagen2026-09-08 08:24 UTC1078
    type Item = Event;
96fdf90aacd6atagen2026-09-08 08:24 UTC1079
96fdf90aacd6atagen2026-09-08 08:24 UTC1080
    fn poll_next(mut self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Option<Self::Item>> {
96fdf90aacd6atagen2026-09-08 08:24 UTC1081
        self.events.poll_recv(context)
96fdf90aacd6atagen2026-09-08 08:24 UTC1082
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1083
}
96fdf90aacd6atagen2026-09-08 08:24 UTC1084
96fdf90aacd6atagen2026-09-08 08:24 UTC1085
#[derive(Debug)]
96fdf90aacd6atagen2026-09-08 08:24 UTC1086
pub(crate) struct DriverPort {
31ce2453c82fatagen2026-09-24 13:07 UTC1087
    requests: mpsc::Receiver<Request>,
96fdf90aacd6atagen2026-09-08 08:24 UTC1088
    events: mpsc::Sender<Event>,
96fdf90aacd6atagen2026-09-08 08:24 UTC1089
    connection: watch::Sender<ConnectionState>,
96fdf90aacd6atagen2026-09-08 08:24 UTC1090
}
96fdf90aacd6atagen2026-09-08 08:24 UTC1091
96fdf90aacd6atagen2026-09-08 08:24 UTC1092
#[derive(Clone, Debug)]
96fdf90aacd6atagen2026-09-08 08:24 UTC1093
pub(crate) struct EventSender(mpsc::Sender<Event>);
96fdf90aacd6atagen2026-09-08 08:24 UTC1094
96fdf90aacd6atagen2026-09-08 08:24 UTC1095
impl EventSender {
96fdf90aacd6atagen2026-09-08 08:24 UTC1096
    pub(crate) async fn send(&self, event: Event) -> Result<(), SendEventError> {
31ce2453c82fatagen2026-09-24 13:07 UTC1097
        self.0.send(event).await.map_err(|_| SendEventError)
96fdf90aacd6atagen2026-09-08 08:24 UTC1098
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1099
411bc1c78b65atagen2026-10-22 07:39 UTC1100
    pub(crate) async fn reserve(&self) -> Result<mpsc::Permit<'_, Event>, SendEventError> {
411bc1c78b65atagen2026-10-22 07:39 UTC1101
        self.0.reserve().await.map_err(|_| SendEventError)
411bc1c78b65atagen2026-10-22 07:39 UTC1102
    }
411bc1c78b65atagen2026-10-22 07:39 UTC1103
31ce2453c82fatagen2026-09-24 13:07 UTC1104
    /// resolves once every `EventStream` is gone
96fdf90aacd6atagen2026-09-08 08:24 UTC1105
    pub(crate) async fn closed(&self) {
96fdf90aacd6atagen2026-09-08 08:24 UTC1106
        self.0.closed().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC1107
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1108
}
96fdf90aacd6atagen2026-09-08 08:24 UTC1109
96fdf90aacd6atagen2026-09-08 08:24 UTC1110
impl DriverPort {
96fdf90aacd6atagen2026-09-08 08:24 UTC1111
    pub(crate) fn event_sender(&self) -> EventSender {
96fdf90aacd6atagen2026-09-08 08:24 UTC1112
        EventSender(self.events.clone())
96fdf90aacd6atagen2026-09-08 08:24 UTC1113
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1114
96fdf90aacd6atagen2026-09-08 08:24 UTC1115
    pub(crate) fn connection_sender(&self) -> watch::Sender<ConnectionState> {
96fdf90aacd6atagen2026-09-08 08:24 UTC1116
        self.connection.clone()
96fdf90aacd6atagen2026-09-08 08:24 UTC1117
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1118
31ce2453c82fatagen2026-09-24 13:07 UTC1119
    pub(crate) async fn recv(&mut self) -> Option<Request> {
31ce2453c82fatagen2026-09-24 13:07 UTC1120
        self.requests.recv().await
96fdf90aacd6atagen2026-09-08 08:24 UTC1121
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1122
5a52012c9b1datagen2026-09-24 23:45 UTC1123
    pub(crate) fn try_recv(&mut self) -> Option<Request> {
5a52012c9b1datagen2026-09-24 23:45 UTC1124
        self.requests.try_recv().ok()
5a52012c9b1datagen2026-09-24 23:45 UTC1125
    }
5a52012c9b1datagen2026-09-24 23:45 UTC1126
31ce2453c82fatagen2026-09-24 13:07 UTC1127
    pub(crate) fn close(&mut self) {
31ce2453c82fatagen2026-09-24 13:07 UTC1128
        self.requests.close();
96fdf90aacd6atagen2026-09-08 08:24 UTC1129
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1130
}
96fdf90aacd6atagen2026-09-08 08:24 UTC1131
31ce2453c82fatagen2026-09-24 13:07 UTC1132
#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
31ce2453c82fatagen2026-09-24 13:07 UTC1133
#[error("the event receiver has closed")]
31ce2453c82fatagen2026-09-24 13:07 UTC1134
pub(crate) struct SendEventError;
96fdf90aacd6atagen2026-09-08 08:24 UTC1135
96fdf90aacd6atagen2026-09-08 08:24 UTC1136
#[must_use]
96fdf90aacd6atagen2026-09-08 08:24 UTC1137
pub(crate) fn transport_pair(
96fdf90aacd6atagen2026-09-08 08:24 UTC1138
    client: Client,
96fdf90aacd6atagen2026-09-08 08:24 UTC1139
    sync_service: Arc<SyncService>,
31ce2453c82fatagen2026-09-24 13:07 UTC1140
    media: Arc<MediaService>,
a250c7fa43fbatagen2026-09-26 13:35 UTC1141
    store_lock: Option<Arc<StoreLock>>,
96fdf90aacd6atagen2026-09-08 08:24 UTC1142
    command_capacity: usize,
96fdf90aacd6atagen2026-09-08 08:24 UTC1143
    event_capacity: usize,
96fdf90aacd6atagen2026-09-08 08:24 UTC1144
) -> (ClientHandle, DriverPort, EventStream) {
31ce2453c82fatagen2026-09-24 13:07 UTC1145
    let (request_tx, request_rx) = mpsc::channel(command_capacity);
96fdf90aacd6atagen2026-09-08 08:24 UTC1146
    let (event_tx, event_rx) = mpsc::channel(event_capacity);
96fdf90aacd6atagen2026-09-08 08:24 UTC1147
    let (connection_tx, connection_rx) = watch::channel(ConnectionState::Idle);
96fdf90aacd6atagen2026-09-08 08:24 UTC1148
    (
96fdf90aacd6atagen2026-09-08 08:24 UTC1149
        ClientHandle {
31ce2453c82fatagen2026-09-24 13:07 UTC1150
            requests: request_tx,
96fdf90aacd6atagen2026-09-08 08:24 UTC1151
            client,
96fdf90aacd6atagen2026-09-08 08:24 UTC1152
            connection: connection_rx,
96fdf90aacd6atagen2026-09-08 08:24 UTC1153
            notification_client: Arc::new(OnceCell::new()),
96fdf90aacd6atagen2026-09-08 08:24 UTC1154
            sync_service,
31ce2453c82fatagen2026-09-24 13:07 UTC1155
            media,
31ce2453c82fatagen2026-09-24 13:07 UTC1156
            spaces: Arc::new(OnceCell::new()),
121a56ce119datagen2026-10-24 06:42 UTC1157
            members_checked: Arc::default(),
a250c7fa43fbatagen2026-09-26 13:35 UTC1158
            _store_lock: store_lock,
96fdf90aacd6atagen2026-09-08 08:24 UTC1159
        },
96fdf90aacd6atagen2026-09-08 08:24 UTC1160
        DriverPort {
31ce2453c82fatagen2026-09-24 13:07 UTC1161
            requests: request_rx,
96fdf90aacd6atagen2026-09-08 08:24 UTC1162
            events: event_tx,
96fdf90aacd6atagen2026-09-08 08:24 UTC1163
            connection: connection_tx,
96fdf90aacd6atagen2026-09-08 08:24 UTC1164
        },
96fdf90aacd6atagen2026-09-08 08:24 UTC1165
        EventStream { events: event_rx },
96fdf90aacd6atagen2026-09-08 08:24 UTC1166
    )
96fdf90aacd6atagen2026-09-08 08:24 UTC1167
}
96fdf90aacd6atagen2026-09-08 08:24 UTC1168
121a56ce119datagen2026-10-24 06:42 UTC1169
async fn list_members(
121a56ce119datagen2026-10-24 06:42 UTC1170
    room: &matrix_sdk::Room,
121a56ce119datagen2026-10-24 06:42 UTC1171
    filter: MemberFilter,
121a56ce119datagen2026-10-24 06:42 UTC1172
) -> Result<Vec<MemberSummary>, Error> {
121a56ce119datagen2026-10-24 06:42 UTC1173
    let memberships = match filter {
121a56ce119datagen2026-10-24 06:42 UTC1174
        MemberFilter::Active => RoomMemberships::ACTIVE,
121a56ce119datagen2026-10-24 06:42 UTC1175
        MemberFilter::Joined => RoomMemberships::JOIN,
121a56ce119datagen2026-10-24 06:42 UTC1176
        MemberFilter::Invited => RoomMemberships::INVITE,
121a56ce119datagen2026-10-24 06:42 UTC1177
        MemberFilter::Banned => RoomMemberships::BAN,
121a56ce119datagen2026-10-24 06:42 UTC1178
        MemberFilter::Left => RoomMemberships::LEAVE,
121a56ce119datagen2026-10-24 06:42 UTC1179
        // An empty mask is the SDK's unfiltered wildcard: it matches every
121a56ce119datagen2026-10-24 06:42 UTC1180
        // membership, including custom states that `all()` would exclude.
121a56ce119datagen2026-10-24 06:42 UTC1181
        MemberFilter::All => RoomMemberships::empty(),
121a56ce119datagen2026-10-24 06:42 UTC1182
    };
121a56ce119datagen2026-10-24 06:42 UTC1183
    Ok(room
121a56ce119datagen2026-10-24 06:42 UTC1184
        .members(memberships)
121a56ce119datagen2026-10-24 06:42 UTC1185
        .await
121a56ce119datagen2026-10-24 06:42 UTC1186
        .map_err(|source| Error::from_sdk("listing members failed", source))?
121a56ce119datagen2026-10-24 06:42 UTC1187
        .into_iter()
121a56ce119datagen2026-10-24 06:42 UTC1188
        .map(member_summary)
121a56ce119datagen2026-10-24 06:42 UTC1189
        .collect())
121a56ce119datagen2026-10-24 06:42 UTC1190
}
121a56ce119datagen2026-10-24 06:42 UTC1191
96fdf90aacd6atagen2026-09-08 08:24 UTC1192
fn normalise_push_notification(
96fdf90aacd6atagen2026-09-08 08:24 UTC1193
    room_id: &RoomId,
96fdf90aacd6atagen2026-09-08 08:24 UTC1194
    event_id: &EventId,
96fdf90aacd6atagen2026-09-08 08:24 UTC1195
    status: NotificationStatus,
96fdf90aacd6atagen2026-09-08 08:24 UTC1196
) -> PushNotificationStatus {
96fdf90aacd6atagen2026-09-08 08:24 UTC1197
    match status {
96fdf90aacd6atagen2026-09-08 08:24 UTC1198
        NotificationStatus::Event(item) => {
96fdf90aacd6atagen2026-09-08 08:24 UTC1199
            let event = match &item.raw_event {
96fdf90aacd6atagen2026-09-08 08:24 UTC1200
                RawNotificationEvent::Timeline(event) => {
96fdf90aacd6atagen2026-09-08 08:24 UTC1201
                    PushNotificationEvent::Timeline(event.clone())
96fdf90aacd6atagen2026-09-08 08:24 UTC1202
                }
96fdf90aacd6atagen2026-09-08 08:24 UTC1203
                RawNotificationEvent::Invite(event) => PushNotificationEvent::Invite(event.clone()),
96fdf90aacd6atagen2026-09-08 08:24 UTC1204
            };
96fdf90aacd6atagen2026-09-08 08:24 UTC1205
            PushNotificationStatus::Event(Box::new(PushNotification {
96fdf90aacd6atagen2026-09-08 08:24 UTC1206
                event_id: event_id.to_owned(),
96fdf90aacd6atagen2026-09-08 08:24 UTC1207
                event,
96fdf90aacd6atagen2026-09-08 08:24 UTC1208
                sender: PushNotificationSender {
96fdf90aacd6atagen2026-09-08 08:24 UTC1209
                    user_id: item.event.sender().to_owned(),
96fdf90aacd6atagen2026-09-08 08:24 UTC1210
                    display_name: item.sender_display_name,
5d2974de5826atagen2026-10-08 11:29 UTC1211
                    avatar_url: item.sender_avatar_url.as_deref().map(MediaId::plain),
96fdf90aacd6atagen2026-09-08 08:24 UTC1212
                    name_ambiguous: item.is_sender_name_ambiguous,
96fdf90aacd6atagen2026-09-08 08:24 UTC1213
                },
96fdf90aacd6atagen2026-09-08 08:24 UTC1214
                room: PushNotificationRoom {
96fdf90aacd6atagen2026-09-08 08:24 UTC1215
                    room_id: room_id.to_owned(),
96fdf90aacd6atagen2026-09-08 08:24 UTC1216
                    name: item.room_computed_display_name,
5d2974de5826atagen2026-10-08 11:29 UTC1217
                    avatar_url: item.room_avatar_url.as_deref().map(MediaId::plain),
96fdf90aacd6atagen2026-09-08 08:24 UTC1218
                    canonical_alias: item
96fdf90aacd6atagen2026-09-08 08:24 UTC1219
                        .room_canonical_alias
96fdf90aacd6atagen2026-09-08 08:24 UTC1220
                        .as_deref()
96fdf90aacd6atagen2026-09-08 08:24 UTC1221
                        .and_then(|alias| alias.parse().ok()),
96fdf90aacd6atagen2026-09-08 08:24 UTC1222
                    topic: item.room_topic,
96fdf90aacd6atagen2026-09-08 08:24 UTC1223
                    encrypted: item.is_room_encrypted,
96fdf90aacd6atagen2026-09-08 08:24 UTC1224
                    direct: item.room_is_dm,
96fdf90aacd6atagen2026-09-08 08:24 UTC1225
                    joined_members_count: item.joined_members_count,
96fdf90aacd6atagen2026-09-08 08:24 UTC1226
                    service_members: item
96fdf90aacd6atagen2026-09-08 08:24 UTC1227
                        .service_members
96fdf90aacd6atagen2026-09-08 08:24 UTC1228
                        .into_iter()
96fdf90aacd6atagen2026-09-08 08:24 UTC1229
                        .filter_map(|user_id| user_id.parse().ok())
96fdf90aacd6atagen2026-09-08 08:24 UTC1230
                        .collect(),
96fdf90aacd6atagen2026-09-08 08:24 UTC1231
                    active_service_members_count: item.active_service_members_count,
96fdf90aacd6atagen2026-09-08 08:24 UTC1232
                    space: item.is_space,
96fdf90aacd6atagen2026-09-08 08:24 UTC1233
                },
96fdf90aacd6atagen2026-09-08 08:24 UTC1234
                noisy: item.is_noisy,
96fdf90aacd6atagen2026-09-08 08:24 UTC1235
                has_mention: item.has_mention,
96fdf90aacd6atagen2026-09-08 08:24 UTC1236
                thread_id: item.thread_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC1237
            }))
96fdf90aacd6atagen2026-09-08 08:24 UTC1238
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC1239
        NotificationStatus::EventNotFound => PushNotificationStatus::NotFound,
96fdf90aacd6atagen2026-09-08 08:24 UTC1240
        NotificationStatus::EventFilteredOut => PushNotificationStatus::FilteredOut,
96fdf90aacd6atagen2026-09-08 08:24 UTC1241
        NotificationStatus::EventRedacted => PushNotificationStatus::Redacted,
96fdf90aacd6atagen2026-09-08 08:24 UTC1242
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC1243
}