atagen

macltok

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