atagen

matrix-rust-sdk

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

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

7ff8adafe9e4atagen2026-10-22 12:30 UTC1
// Copyright 2020 Damir Jelić
7ff8adafe9e4atagen2026-10-22 12:30 UTC2
// Copyright 2020 The Matrix.org Foundation C.I.C.
7ff8adafe9e4atagen2026-10-22 12:30 UTC3
// Copyright 2022 Kévin Commaille
7ff8adafe9e4atagen2026-10-22 12:30 UTC4
//
7ff8adafe9e4atagen2026-10-22 12:30 UTC5
// Licensed under the Apache License, Version 2.0 (the "License");
7ff8adafe9e4atagen2026-10-22 12:30 UTC6
// you may not use this file except in compliance with the License.
7ff8adafe9e4atagen2026-10-22 12:30 UTC7
// You may obtain a copy of the License at
7ff8adafe9e4atagen2026-10-22 12:30 UTC8
//
7ff8adafe9e4atagen2026-10-22 12:30 UTC9
//     http://www.apache.org/licenses/LICENSE-2.0
7ff8adafe9e4atagen2026-10-22 12:30 UTC10
//
7ff8adafe9e4atagen2026-10-22 12:30 UTC11
// Unless required by applicable law or agreed to in writing, software
7ff8adafe9e4atagen2026-10-22 12:30 UTC12
// distributed under the License is distributed on an "AS IS" BASIS,
7ff8adafe9e4atagen2026-10-22 12:30 UTC13
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
7ff8adafe9e4atagen2026-10-22 12:30 UTC14
// See the License for the specific language governing permissions and
7ff8adafe9e4atagen2026-10-22 12:30 UTC15
// limitations under the License.
7ff8adafe9e4atagen2026-10-22 12:30 UTC16
7ff8adafe9e4atagen2026-10-22 12:30 UTC17
use futures_core::Stream;
7ff8adafe9e4atagen2026-10-22 12:30 UTC18
use futures_util::{StreamExt, stream};
7ff8adafe9e4atagen2026-10-22 12:30 UTC19
#[cfg(feature = "experimental-element-recent-emojis")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC20
use itertools::Itertools;
7ff8adafe9e4atagen2026-10-22 12:30 UTC21
#[cfg(feature = "experimental-element-recent-emojis")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC22
use js_int::uint;
7ff8adafe9e4atagen2026-10-22 12:30 UTC23
#[cfg(feature = "experimental-element-recent-emojis")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC24
use matrix_sdk_base::recent_emojis::RecentEmojisContent;
7ff8adafe9e4atagen2026-10-22 12:30 UTC25
use matrix_sdk_base::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC26
    SendOutsideWasm, StateStoreDataKey, StateStoreDataValue, SyncOutsideWasm,
7ff8adafe9e4atagen2026-10-22 12:30 UTC27
    media::{MediaFormat, MediaRequestParameters},
7ff8adafe9e4atagen2026-10-22 12:30 UTC28
    store::StateStoreExt,
7ff8adafe9e4atagen2026-10-22 12:30 UTC29
};
7ff8adafe9e4atagen2026-10-22 12:30 UTC30
use mime::Mime;
7ff8adafe9e4atagen2026-10-22 12:30 UTC31
#[cfg(feature = "experimental-element-recent-emojis")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC32
use ruma::api::client::config::set_global_account_data::v3::Request as UpdateGlobalAccountDataRequest;
7ff8adafe9e4atagen2026-10-22 12:30 UTC33
use ruma::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC34
    ClientSecret, MxcUri, OwnedMxcUri, OwnedRoomId, OwnedUserId, RoomId, SessionId, UInt, UserId,
7ff8adafe9e4atagen2026-10-22 12:30 UTC35
    api::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC36
        Metadata,
7ff8adafe9e4atagen2026-10-22 12:30 UTC37
        client::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC38
            account::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC39
                add_3pid, change_password, deactivate, delete_3pid, get_3pids,
7ff8adafe9e4atagen2026-10-22 12:30 UTC40
                request_3pid_management_token_via_email, request_3pid_management_token_via_msisdn,
7ff8adafe9e4atagen2026-10-22 12:30 UTC41
                request_openid_token,
7ff8adafe9e4atagen2026-10-22 12:30 UTC42
            },
7ff8adafe9e4atagen2026-10-22 12:30 UTC43
            config::{get_global_account_data, set_global_account_data},
7ff8adafe9e4atagen2026-10-22 12:30 UTC44
            profile::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC45
                DisplayName, StaticProfileField, delete_profile_field, get_avatar_url, get_profile,
7ff8adafe9e4atagen2026-10-22 12:30 UTC46
                get_profile_field, set_avatar_url, set_display_name, set_profile_field,
7ff8adafe9e4atagen2026-10-22 12:30 UTC47
            },
7ff8adafe9e4atagen2026-10-22 12:30 UTC48
            uiaa::AuthData,
7ff8adafe9e4atagen2026-10-22 12:30 UTC49
        },
7ff8adafe9e4atagen2026-10-22 12:30 UTC50
        error::ErrorKind,
7ff8adafe9e4atagen2026-10-22 12:30 UTC51
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC52
    assign,
7ff8adafe9e4atagen2026-10-22 12:30 UTC53
    events::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC54
        AnyGlobalAccountDataEventContent, GlobalAccountDataEvent, GlobalAccountDataEventContent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC55
        GlobalAccountDataEventType, StaticEventContent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC56
        ignored_user_list::{IgnoredUser, IgnoredUserListEventContent},
7ff8adafe9e4atagen2026-10-22 12:30 UTC57
        media_preview_config::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC58
            InviteAvatars, MediaPreviewConfigEventContent, MediaPreviews,
7ff8adafe9e4atagen2026-10-22 12:30 UTC59
            UnstableMediaPreviewConfigEventContent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC60
        },
7ff8adafe9e4atagen2026-10-22 12:30 UTC61
        push_rules::PushRulesEventContent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC62
        room::MediaSource,
7ff8adafe9e4atagen2026-10-22 12:30 UTC63
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC64
    profile::{ProfileFieldName, ProfileFieldValue},
7ff8adafe9e4atagen2026-10-22 12:30 UTC65
    push::Ruleset,
7ff8adafe9e4atagen2026-10-22 12:30 UTC66
    serde::Raw,
7ff8adafe9e4atagen2026-10-22 12:30 UTC67
    thirdparty::Medium,
7ff8adafe9e4atagen2026-10-22 12:30 UTC68
};
7ff8adafe9e4atagen2026-10-22 12:30 UTC69
use serde::Deserialize;
7ff8adafe9e4atagen2026-10-22 12:30 UTC70
use tracing::error;
7ff8adafe9e4atagen2026-10-22 12:30 UTC71
7ff8adafe9e4atagen2026-10-22 12:30 UTC72
use crate::{Client, Error, Result, config::RequestConfig};
7ff8adafe9e4atagen2026-10-22 12:30 UTC73
7ff8adafe9e4atagen2026-10-22 12:30 UTC74
/// The maximum number of recent emojis that should be stored and loaded.
7ff8adafe9e4atagen2026-10-22 12:30 UTC75
#[cfg(feature = "experimental-element-recent-emojis")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC76
const MAX_RECENT_EMOJI_COUNT: usize = 100;
7ff8adafe9e4atagen2026-10-22 12:30 UTC77
7ff8adafe9e4atagen2026-10-22 12:30 UTC78
/// A high-level API to manage the client owner's account.
7ff8adafe9e4atagen2026-10-22 12:30 UTC79
///
7ff8adafe9e4atagen2026-10-22 12:30 UTC80
/// All the methods on this struct send a request to the homeserver.
7ff8adafe9e4atagen2026-10-22 12:30 UTC81
#[derive(Debug, Clone)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC82
pub struct Account {
7ff8adafe9e4atagen2026-10-22 12:30 UTC83
    /// The underlying HTTP client.
7ff8adafe9e4atagen2026-10-22 12:30 UTC84
    client: Client,
7ff8adafe9e4atagen2026-10-22 12:30 UTC85
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC86
7ff8adafe9e4atagen2026-10-22 12:30 UTC87
impl Account {
7ff8adafe9e4atagen2026-10-22 12:30 UTC88
    /// The maximum number of visited room identifiers to keep in the state
7ff8adafe9e4atagen2026-10-22 12:30 UTC89
    /// store.
7ff8adafe9e4atagen2026-10-22 12:30 UTC90
    const VISITED_ROOMS_LIMIT: usize = 20;
7ff8adafe9e4atagen2026-10-22 12:30 UTC91
7ff8adafe9e4atagen2026-10-22 12:30 UTC92
    pub(crate) fn new(client: Client) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC93
        Self { client }
7ff8adafe9e4atagen2026-10-22 12:30 UTC94
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC95
7ff8adafe9e4atagen2026-10-22 12:30 UTC96
    /// Get the display name of the account.
7ff8adafe9e4atagen2026-10-22 12:30 UTC97
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC98
    /// # Examples
7ff8adafe9e4atagen2026-10-22 12:30 UTC99
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC100
    /// ```no_run
7ff8adafe9e4atagen2026-10-22 12:30 UTC101
    /// # use matrix_sdk::Client;
7ff8adafe9e4atagen2026-10-22 12:30 UTC102
    /// # use url::Url;
7ff8adafe9e4atagen2026-10-22 12:30 UTC103
    /// # async {
7ff8adafe9e4atagen2026-10-22 12:30 UTC104
    /// # let homeserver = Url::parse("http://example.com")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC105
    /// let user = "example";
7ff8adafe9e4atagen2026-10-22 12:30 UTC106
    /// let client = Client::new(homeserver).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC107
    /// client.matrix_auth().login_username(user, "password").send().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC108
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC109
    /// if let Some(name) = client.account().get_display_name().await? {
7ff8adafe9e4atagen2026-10-22 12:30 UTC110
    ///     println!("Logged in as user '{user}' with display name '{name}'");
7ff8adafe9e4atagen2026-10-22 12:30 UTC111
    /// }
7ff8adafe9e4atagen2026-10-22 12:30 UTC112
    /// # anyhow::Ok(()) };
7ff8adafe9e4atagen2026-10-22 12:30 UTC113
    /// ```
7ff8adafe9e4atagen2026-10-22 12:30 UTC114
    pub async fn get_display_name(&self) -> Result<Option<String>> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC115
        let user_id = self.client.user_id().ok_or(Error::AuthenticationRequired)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC116
        self.fetch_profile_field_of_static::<DisplayName>(user_id.to_owned()).await
7ff8adafe9e4atagen2026-10-22 12:30 UTC117
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC118
7ff8adafe9e4atagen2026-10-22 12:30 UTC119
    /// Set the display name of the account.
7ff8adafe9e4atagen2026-10-22 12:30 UTC120
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC121
    /// # Examples
7ff8adafe9e4atagen2026-10-22 12:30 UTC122
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC123
    /// ```no_run
7ff8adafe9e4atagen2026-10-22 12:30 UTC124
    /// # use matrix_sdk::Client;
7ff8adafe9e4atagen2026-10-22 12:30 UTC125
    /// # use url::Url;
7ff8adafe9e4atagen2026-10-22 12:30 UTC126
    /// # async {
7ff8adafe9e4atagen2026-10-22 12:30 UTC127
    /// # let homeserver = Url::parse("http://example.com")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC128
    /// let user = "example";
7ff8adafe9e4atagen2026-10-22 12:30 UTC129
    /// let client = Client::new(homeserver).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC130
    /// client.matrix_auth().login_username(user, "password").send().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC131
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC132
    /// client.account().set_display_name(Some("Alice")).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC133
    /// # anyhow::Ok(()) };
7ff8adafe9e4atagen2026-10-22 12:30 UTC134
    /// ```
7ff8adafe9e4atagen2026-10-22 12:30 UTC135
    pub async fn set_display_name(&self, name: Option<&str>) -> Result<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC136
        let user_id = self.client.user_id().ok_or(Error::AuthenticationRequired)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC137
7ff8adafe9e4atagen2026-10-22 12:30 UTC138
        // Prefer the endpoint to delete profile fields, if it is supported.
7ff8adafe9e4atagen2026-10-22 12:30 UTC139
        if name.is_none() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC140
            let versions = self.client.supported_versions().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC141
7ff8adafe9e4atagen2026-10-22 12:30 UTC142
            if delete_profile_field::v3::Request::PATH_BUILDER.is_supported(&versions) {
7ff8adafe9e4atagen2026-10-22 12:30 UTC143
                return self.delete_profile_field(ProfileFieldName::DisplayName).await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC144
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC145
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC146
7ff8adafe9e4atagen2026-10-22 12:30 UTC147
        // If name is `Some(_)`, this endpoint is the same as `set_profile_field`, but
7ff8adafe9e4atagen2026-10-22 12:30 UTC148
        // we still need to use it in case it is `None` and the server doesn't support
7ff8adafe9e4atagen2026-10-22 12:30 UTC149
        // the delete endpoint yet.
7ff8adafe9e4atagen2026-10-22 12:30 UTC150
        #[allow(deprecated)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC151
        let request =
7ff8adafe9e4atagen2026-10-22 12:30 UTC152
            set_display_name::v3::Request::new(user_id.to_owned(), name.map(ToOwned::to_owned));
7ff8adafe9e4atagen2026-10-22 12:30 UTC153
        self.client.send(request).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC154
7ff8adafe9e4atagen2026-10-22 12:30 UTC155
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC156
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC157
7ff8adafe9e4atagen2026-10-22 12:30 UTC158
    /// Request an OpenID token for the current account.
7ff8adafe9e4atagen2026-10-22 12:30 UTC159
    pub async fn request_openid_token(&self) -> Result<request_openid_token::v3::Response> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC160
        let user_id = self.client.user_id().ok_or(Error::AuthenticationRequired)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC161
7ff8adafe9e4atagen2026-10-22 12:30 UTC162
        self.client
7ff8adafe9e4atagen2026-10-22 12:30 UTC163
            .send(request_openid_token::v3::Request::new(user_id.to_owned()))
7ff8adafe9e4atagen2026-10-22 12:30 UTC164
            .await
7ff8adafe9e4atagen2026-10-22 12:30 UTC165
            .map_err(|error| Error::Http(Box::new(error)))
7ff8adafe9e4atagen2026-10-22 12:30 UTC166
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC167
7ff8adafe9e4atagen2026-10-22 12:30 UTC168
    /// Get the MXC URI of the account's avatar, if set.
7ff8adafe9e4atagen2026-10-22 12:30 UTC169
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC170
    /// This always sends a request to the server to retrieve this information.
7ff8adafe9e4atagen2026-10-22 12:30 UTC171
    /// If successful, this fills the cache, and makes it so that
7ff8adafe9e4atagen2026-10-22 12:30 UTC172
    /// [`Self::get_cached_avatar_url`] will always return something.
7ff8adafe9e4atagen2026-10-22 12:30 UTC173
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC174
    /// # Examples
7ff8adafe9e4atagen2026-10-22 12:30 UTC175
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC176
    /// ```no_run
7ff8adafe9e4atagen2026-10-22 12:30 UTC177
    /// # use matrix_sdk::Client;
7ff8adafe9e4atagen2026-10-22 12:30 UTC178
    /// # use url::Url;
7ff8adafe9e4atagen2026-10-22 12:30 UTC179
    /// # async {
7ff8adafe9e4atagen2026-10-22 12:30 UTC180
    /// # let homeserver = Url::parse("http://example.com")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC181
    /// # let user = "example";
7ff8adafe9e4atagen2026-10-22 12:30 UTC182
    /// let client = Client::new(homeserver).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC183
    /// client.matrix_auth().login_username(user, "password").send().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC184
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC185
    /// if let Some(url) = client.account().get_avatar_url().await? {
7ff8adafe9e4atagen2026-10-22 12:30 UTC186
    ///     println!("Your avatar's mxc url is {url}");
7ff8adafe9e4atagen2026-10-22 12:30 UTC187
    /// }
7ff8adafe9e4atagen2026-10-22 12:30 UTC188
    /// # anyhow::Ok(()) };
7ff8adafe9e4atagen2026-10-22 12:30 UTC189
    /// ```
7ff8adafe9e4atagen2026-10-22 12:30 UTC190
    pub async fn get_avatar_url(&self) -> Result<Option<OwnedMxcUri>> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC191
        let user_id = self.client.user_id().ok_or(Error::AuthenticationRequired)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC192
7ff8adafe9e4atagen2026-10-22 12:30 UTC193
        #[allow(deprecated)] // get_profile_field fails when the response is {"avatar_url":null} 🤷‍♂️
7ff8adafe9e4atagen2026-10-22 12:30 UTC194
        let request = get_avatar_url::v3::Request::new(user_id.to_owned());
7ff8adafe9e4atagen2026-10-22 12:30 UTC195
        let avatar_url = self
7ff8adafe9e4atagen2026-10-22 12:30 UTC196
            .client
7ff8adafe9e4atagen2026-10-22 12:30 UTC197
            .send(request)
7ff8adafe9e4atagen2026-10-22 12:30 UTC198
            .with_request_config(RequestConfig::short_retry().force_auth())
7ff8adafe9e4atagen2026-10-22 12:30 UTC199
            .await?
7ff8adafe9e4atagen2026-10-22 12:30 UTC200
            .avatar_url;
7ff8adafe9e4atagen2026-10-22 12:30 UTC201
7ff8adafe9e4atagen2026-10-22 12:30 UTC202
        if let Some(url) = avatar_url.clone() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC203
            // If an avatar is found cache it.
7ff8adafe9e4atagen2026-10-22 12:30 UTC204
            let _ = self
7ff8adafe9e4atagen2026-10-22 12:30 UTC205
                .client
7ff8adafe9e4atagen2026-10-22 12:30 UTC206
                .state_store()
7ff8adafe9e4atagen2026-10-22 12:30 UTC207
                .set_kv_data(
7ff8adafe9e4atagen2026-10-22 12:30 UTC208
                    StateStoreDataKey::UserAvatarUrl(user_id),
7ff8adafe9e4atagen2026-10-22 12:30 UTC209
                    StateStoreDataValue::UserAvatarUrl(url),
7ff8adafe9e4atagen2026-10-22 12:30 UTC210
                )
7ff8adafe9e4atagen2026-10-22 12:30 UTC211
                .await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC212
        } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC213
            // If there is no avatar the user has removed it and we uncache it.
7ff8adafe9e4atagen2026-10-22 12:30 UTC214
            let _ = self
7ff8adafe9e4atagen2026-10-22 12:30 UTC215
                .client
7ff8adafe9e4atagen2026-10-22 12:30 UTC216
                .state_store()
7ff8adafe9e4atagen2026-10-22 12:30 UTC217
                .remove_kv_data(StateStoreDataKey::UserAvatarUrl(user_id))
7ff8adafe9e4atagen2026-10-22 12:30 UTC218
                .await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC219
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC220
        Ok(avatar_url)
7ff8adafe9e4atagen2026-10-22 12:30 UTC221
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC222
7ff8adafe9e4atagen2026-10-22 12:30 UTC223
    /// Get the URL of the account's avatar, if is stored in cache.
7ff8adafe9e4atagen2026-10-22 12:30 UTC224
    pub async fn get_cached_avatar_url(&self) -> Result<Option<OwnedMxcUri>> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC225
        let user_id = self.client.user_id().ok_or(Error::AuthenticationRequired)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC226
        let data = self
7ff8adafe9e4atagen2026-10-22 12:30 UTC227
            .client
7ff8adafe9e4atagen2026-10-22 12:30 UTC228
            .state_store()
7ff8adafe9e4atagen2026-10-22 12:30 UTC229
            .get_kv_data(StateStoreDataKey::UserAvatarUrl(user_id))
7ff8adafe9e4atagen2026-10-22 12:30 UTC230
            .await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC231
        Ok(data.map(|v| v.into_user_avatar_url().expect("Session data is not a user avatar url")))
7ff8adafe9e4atagen2026-10-22 12:30 UTC232
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC233
7ff8adafe9e4atagen2026-10-22 12:30 UTC234
    /// Set the MXC URI of the account's avatar.
7ff8adafe9e4atagen2026-10-22 12:30 UTC235
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC236
    /// The avatar is unset if `url` is `None`.
7ff8adafe9e4atagen2026-10-22 12:30 UTC237
    pub async fn set_avatar_url(&self, url: Option<&MxcUri>) -> Result<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC238
        let user_id = self.client.user_id().ok_or(Error::AuthenticationRequired)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC239
7ff8adafe9e4atagen2026-10-22 12:30 UTC240
        // Prefer the endpoint to delete profile fields, if it is supported.
7ff8adafe9e4atagen2026-10-22 12:30 UTC241
        if url.is_none() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC242
            let versions = self.client.supported_versions().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC243
7ff8adafe9e4atagen2026-10-22 12:30 UTC244
            if delete_profile_field::v3::Request::PATH_BUILDER.is_supported(&versions) {
7ff8adafe9e4atagen2026-10-22 12:30 UTC245
                return self.delete_profile_field(ProfileFieldName::AvatarUrl).await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC246
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC247
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC248
7ff8adafe9e4atagen2026-10-22 12:30 UTC249
        // If url is `Some(_)`, this endpoint is the same as `set_profile_field`, but
7ff8adafe9e4atagen2026-10-22 12:30 UTC250
        // we still need to use it in case it is `None` and the server doesn't support
7ff8adafe9e4atagen2026-10-22 12:30 UTC251
        // the delete endpoint yet.
7ff8adafe9e4atagen2026-10-22 12:30 UTC252
        #[allow(deprecated)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC253
        let request =
7ff8adafe9e4atagen2026-10-22 12:30 UTC254
            set_avatar_url::v3::Request::new(user_id.to_owned(), url.map(ToOwned::to_owned));
7ff8adafe9e4atagen2026-10-22 12:30 UTC255
        self.client.send(request).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC256
7ff8adafe9e4atagen2026-10-22 12:30 UTC257
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC258
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC259
7ff8adafe9e4atagen2026-10-22 12:30 UTC260
    /// Get the account's avatar, if set.
7ff8adafe9e4atagen2026-10-22 12:30 UTC261
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC262
    /// Returns the avatar.
7ff8adafe9e4atagen2026-10-22 12:30 UTC263
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC264
    /// If a thumbnail is requested no guarantee on the size of the image is
7ff8adafe9e4atagen2026-10-22 12:30 UTC265
    /// given.
7ff8adafe9e4atagen2026-10-22 12:30 UTC266
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC267
    /// # Arguments
7ff8adafe9e4atagen2026-10-22 12:30 UTC268
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC269
    /// * `format` - The desired format of the avatar.
7ff8adafe9e4atagen2026-10-22 12:30 UTC270
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC271
    /// # Examples
7ff8adafe9e4atagen2026-10-22 12:30 UTC272
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC273
    /// ```no_run
7ff8adafe9e4atagen2026-10-22 12:30 UTC274
    /// # use matrix_sdk::Client;
7ff8adafe9e4atagen2026-10-22 12:30 UTC275
    /// # use matrix_sdk::ruma::room_id;
7ff8adafe9e4atagen2026-10-22 12:30 UTC276
    /// # use matrix_sdk::media::MediaFormat;
7ff8adafe9e4atagen2026-10-22 12:30 UTC277
    /// # use url::Url;
7ff8adafe9e4atagen2026-10-22 12:30 UTC278
    /// # async {
7ff8adafe9e4atagen2026-10-22 12:30 UTC279
    /// # let homeserver = Url::parse("http://example.com")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC280
    /// # let user = "example";
7ff8adafe9e4atagen2026-10-22 12:30 UTC281
    /// let client = Client::new(homeserver).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC282
    /// client.matrix_auth().login_username(user, "password").send().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC283
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC284
    /// if let Some(avatar) = client.account().get_avatar(MediaFormat::File).await?
7ff8adafe9e4atagen2026-10-22 12:30 UTC285
    /// {
7ff8adafe9e4atagen2026-10-22 12:30 UTC286
    ///     std::fs::write("avatar.png", avatar);
7ff8adafe9e4atagen2026-10-22 12:30 UTC287
    /// }
7ff8adafe9e4atagen2026-10-22 12:30 UTC288
    /// # anyhow::Ok(()) };
7ff8adafe9e4atagen2026-10-22 12:30 UTC289
    /// ```
7ff8adafe9e4atagen2026-10-22 12:30 UTC290
    pub async fn get_avatar(&self, format: MediaFormat) -> Result<Option<Vec<u8>>> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC291
        if let Some(url) = self.get_avatar_url().await? {
7ff8adafe9e4atagen2026-10-22 12:30 UTC292
            let request = MediaRequestParameters { source: MediaSource::Plain(url), format };
7ff8adafe9e4atagen2026-10-22 12:30 UTC293
            Ok(Some(self.client.media().get_media_content(&request, true).await?))
7ff8adafe9e4atagen2026-10-22 12:30 UTC294
        } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC295
            Ok(None)
7ff8adafe9e4atagen2026-10-22 12:30 UTC296
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC297
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC298
7ff8adafe9e4atagen2026-10-22 12:30 UTC299
    /// Upload and set the account's avatar.
7ff8adafe9e4atagen2026-10-22 12:30 UTC300
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC301
    /// This will upload the data produced by the reader to the homeserver's
7ff8adafe9e4atagen2026-10-22 12:30 UTC302
    /// content repository, and set the user's avatar to the MXC URI for the
7ff8adafe9e4atagen2026-10-22 12:30 UTC303
    /// uploaded file.
7ff8adafe9e4atagen2026-10-22 12:30 UTC304
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC305
    /// This is a convenience method for calling [`Media::upload()`],
7ff8adafe9e4atagen2026-10-22 12:30 UTC306
    /// followed by [`Account::set_avatar_url()`].
7ff8adafe9e4atagen2026-10-22 12:30 UTC307
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC308
    /// Returns the MXC URI of the uploaded avatar.
7ff8adafe9e4atagen2026-10-22 12:30 UTC309
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC310
    /// # Examples
7ff8adafe9e4atagen2026-10-22 12:30 UTC311
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC312
    /// ```no_run
7ff8adafe9e4atagen2026-10-22 12:30 UTC313
    /// # use std::fs;
7ff8adafe9e4atagen2026-10-22 12:30 UTC314
    /// # use matrix_sdk::Client;
7ff8adafe9e4atagen2026-10-22 12:30 UTC315
    /// # use url::Url;
7ff8adafe9e4atagen2026-10-22 12:30 UTC316
    /// # async {
7ff8adafe9e4atagen2026-10-22 12:30 UTC317
    /// # let homeserver = Url::parse("http://localhost:8080")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC318
    /// # let client = Client::new(homeserver).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC319
    /// let image = fs::read("/home/example/selfie.jpg")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC320
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC321
    /// client.account().upload_avatar(&mime::IMAGE_JPEG, image).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC322
    /// # anyhow::Ok(()) };
7ff8adafe9e4atagen2026-10-22 12:30 UTC323
    /// ```
7ff8adafe9e4atagen2026-10-22 12:30 UTC324
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC325
    /// [`Media::upload()`]: crate::Media::upload
7ff8adafe9e4atagen2026-10-22 12:30 UTC326
    pub async fn upload_avatar(&self, content_type: &Mime, data: Vec<u8>) -> Result<OwnedMxcUri> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC327
        let upload_response = self.client.media().upload(content_type, data, None).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC328
        self.set_avatar_url(Some(&upload_response.content_uri)).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC329
        Ok(upload_response.content_uri)
7ff8adafe9e4atagen2026-10-22 12:30 UTC330
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC331
7ff8adafe9e4atagen2026-10-22 12:30 UTC332
    /// Get the profile of this account.
7ff8adafe9e4atagen2026-10-22 12:30 UTC333
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC334
    /// Allows to get all the profile data in a single call.
7ff8adafe9e4atagen2026-10-22 12:30 UTC335
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC336
    /// # Examples
7ff8adafe9e4atagen2026-10-22 12:30 UTC337
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC338
    /// ```no_run
7ff8adafe9e4atagen2026-10-22 12:30 UTC339
    /// # use matrix_sdk::Client;
7ff8adafe9e4atagen2026-10-22 12:30 UTC340
    /// use ruma::api::client::profile::{AvatarUrl, DisplayName};
7ff8adafe9e4atagen2026-10-22 12:30 UTC341
    /// # use url::Url;
7ff8adafe9e4atagen2026-10-22 12:30 UTC342
    /// # async {
7ff8adafe9e4atagen2026-10-22 12:30 UTC343
    /// # let homeserver = Url::parse("http://localhost:8080")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC344
    /// # let client = Client::new(homeserver).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC345
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC346
    /// let profile = client.account().fetch_user_profile().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC347
    /// let display_name = profile.get_static::<DisplayName>()?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC348
    /// let avatar_url = profile.get_static::<AvatarUrl>()?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC349
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC350
    /// println!("You are '{display_name:?}' with avatar '{avatar_url:?}'");
7ff8adafe9e4atagen2026-10-22 12:30 UTC351
    /// # anyhow::Ok(()) };
7ff8adafe9e4atagen2026-10-22 12:30 UTC352
    /// ```
7ff8adafe9e4atagen2026-10-22 12:30 UTC353
    pub async fn fetch_user_profile(&self) -> Result<get_profile::v3::Response> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC354
        let user_id = self.client.user_id().ok_or(Error::AuthenticationRequired)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC355
        self.fetch_user_profile_of(user_id).await
7ff8adafe9e4atagen2026-10-22 12:30 UTC356
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC357
7ff8adafe9e4atagen2026-10-22 12:30 UTC358
    /// Get the profile for a given user id
7ff8adafe9e4atagen2026-10-22 12:30 UTC359
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC360
    /// # Arguments
7ff8adafe9e4atagen2026-10-22 12:30 UTC361
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC362
    /// * `user_id` the matrix id this function downloads the profile for
7ff8adafe9e4atagen2026-10-22 12:30 UTC363
    pub async fn fetch_user_profile_of(
7ff8adafe9e4atagen2026-10-22 12:30 UTC364
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC365
        user_id: &UserId,
7ff8adafe9e4atagen2026-10-22 12:30 UTC366
    ) -> Result<get_profile::v3::Response> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC367
        let request = get_profile::v3::Request::new(user_id.to_owned());
7ff8adafe9e4atagen2026-10-22 12:30 UTC368
        Ok(self
7ff8adafe9e4atagen2026-10-22 12:30 UTC369
            .client
7ff8adafe9e4atagen2026-10-22 12:30 UTC370
            .send(request)
7ff8adafe9e4atagen2026-10-22 12:30 UTC371
            .with_request_config(RequestConfig::short_retry().force_auth())
7ff8adafe9e4atagen2026-10-22 12:30 UTC372
            .await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC373
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC374
7ff8adafe9e4atagen2026-10-22 12:30 UTC375
    /// Get the given field from the given user's profile.
7ff8adafe9e4atagen2026-10-22 12:30 UTC376
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC377
    /// # Arguments
7ff8adafe9e4atagen2026-10-22 12:30 UTC378
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC379
    /// * `user_id` - The ID of the user to get the profile field of.
7ff8adafe9e4atagen2026-10-22 12:30 UTC380
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC381
    /// * `field` - The name of the profile field to get.
7ff8adafe9e4atagen2026-10-22 12:30 UTC382
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC383
    /// # Returns
7ff8adafe9e4atagen2026-10-22 12:30 UTC384
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC385
    /// Returns an error if the request fails or if deserialization of the
7ff8adafe9e4atagen2026-10-22 12:30 UTC386
    /// response fails.
7ff8adafe9e4atagen2026-10-22 12:30 UTC387
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC388
    /// If the field is not set, the server should respond with an error with an
7ff8adafe9e4atagen2026-10-22 12:30 UTC389
    /// [`ErrorCode::NotFound`], but it might also respond with an empty
7ff8adafe9e4atagen2026-10-22 12:30 UTC390
    /// response, which would result in `Ok(None)`. Note that this error code
7ff8adafe9e4atagen2026-10-22 12:30 UTC391
    /// might also mean that the given user ID doesn't exist.
7ff8adafe9e4atagen2026-10-22 12:30 UTC392
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC393
    /// [`ErrorCode::NotFound`]: ruma::api::error::ErrorCode::NotFound
7ff8adafe9e4atagen2026-10-22 12:30 UTC394
    pub async fn fetch_profile_field_of(
7ff8adafe9e4atagen2026-10-22 12:30 UTC395
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC396
        user_id: OwnedUserId,
7ff8adafe9e4atagen2026-10-22 12:30 UTC397
        field: ProfileFieldName,
7ff8adafe9e4atagen2026-10-22 12:30 UTC398
    ) -> Result<Option<ProfileFieldValue>> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC399
        let request = get_profile_field::v3::Request::new(user_id, field);
7ff8adafe9e4atagen2026-10-22 12:30 UTC400
        let response = self
7ff8adafe9e4atagen2026-10-22 12:30 UTC401
            .client
7ff8adafe9e4atagen2026-10-22 12:30 UTC402
            .send(request)
7ff8adafe9e4atagen2026-10-22 12:30 UTC403
            .with_request_config(RequestConfig::short_retry().force_auth())
7ff8adafe9e4atagen2026-10-22 12:30 UTC404
            .await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC405
7ff8adafe9e4atagen2026-10-22 12:30 UTC406
        Ok(response.value)
7ff8adafe9e4atagen2026-10-22 12:30 UTC407
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC408
7ff8adafe9e4atagen2026-10-22 12:30 UTC409
    /// Get the given statically-known field from the given user's profile.
7ff8adafe9e4atagen2026-10-22 12:30 UTC410
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC411
    /// # Arguments
7ff8adafe9e4atagen2026-10-22 12:30 UTC412
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC413
    /// * `user_id` - The ID of the user to get the profile field of.
7ff8adafe9e4atagen2026-10-22 12:30 UTC414
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC415
    /// # Returns
7ff8adafe9e4atagen2026-10-22 12:30 UTC416
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC417
    /// Returns an error if the request fails or if deserialization of the
7ff8adafe9e4atagen2026-10-22 12:30 UTC418
    /// response fails.
7ff8adafe9e4atagen2026-10-22 12:30 UTC419
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC420
    /// If the field is not set, the server should respond with an error with an
7ff8adafe9e4atagen2026-10-22 12:30 UTC421
    /// [`ErrorCode::NotFound`], but it might also respond with an empty
7ff8adafe9e4atagen2026-10-22 12:30 UTC422
    /// response, which would result in `Ok(None)`. Note that this error code
7ff8adafe9e4atagen2026-10-22 12:30 UTC423
    /// might also mean that the given user ID doesn't exist.
7ff8adafe9e4atagen2026-10-22 12:30 UTC424
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC425
    /// [`ErrorCode::NotFound`]: ruma::api::error::ErrorCode::NotFound
7ff8adafe9e4atagen2026-10-22 12:30 UTC426
    pub async fn fetch_profile_field_of_static<F>(
7ff8adafe9e4atagen2026-10-22 12:30 UTC427
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC428
        user_id: OwnedUserId,
7ff8adafe9e4atagen2026-10-22 12:30 UTC429
    ) -> Result<Option<F::Value>>
7ff8adafe9e4atagen2026-10-22 12:30 UTC430
    where
7ff8adafe9e4atagen2026-10-22 12:30 UTC431
        F: StaticProfileField
7ff8adafe9e4atagen2026-10-22 12:30 UTC432
            + std::fmt::Debug
7ff8adafe9e4atagen2026-10-22 12:30 UTC433
            + Clone
7ff8adafe9e4atagen2026-10-22 12:30 UTC434
            + SendOutsideWasm
7ff8adafe9e4atagen2026-10-22 12:30 UTC435
            + SyncOutsideWasm
7ff8adafe9e4atagen2026-10-22 12:30 UTC436
            + 'static,
7ff8adafe9e4atagen2026-10-22 12:30 UTC437
        F::Value: SendOutsideWasm + SyncOutsideWasm,
7ff8adafe9e4atagen2026-10-22 12:30 UTC438
    {
7ff8adafe9e4atagen2026-10-22 12:30 UTC439
        let request = get_profile_field::v3::Request::new_static::<F>(user_id);
7ff8adafe9e4atagen2026-10-22 12:30 UTC440
        let response = self
7ff8adafe9e4atagen2026-10-22 12:30 UTC441
            .client
7ff8adafe9e4atagen2026-10-22 12:30 UTC442
            .send(request)
7ff8adafe9e4atagen2026-10-22 12:30 UTC443
            .with_request_config(RequestConfig::short_retry().force_auth())
7ff8adafe9e4atagen2026-10-22 12:30 UTC444
            .await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC445
7ff8adafe9e4atagen2026-10-22 12:30 UTC446
        Ok(response.value)
7ff8adafe9e4atagen2026-10-22 12:30 UTC447
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC448
7ff8adafe9e4atagen2026-10-22 12:30 UTC449
    /// Set the given field of our own user's profile.
7ff8adafe9e4atagen2026-10-22 12:30 UTC450
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC451
    /// [`Client::homeserver_capabilities()`] should be called first to check it
7ff8adafe9e4atagen2026-10-22 12:30 UTC452
    /// the field can be set on the homeserver.
7ff8adafe9e4atagen2026-10-22 12:30 UTC453
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC454
    /// # Arguments
7ff8adafe9e4atagen2026-10-22 12:30 UTC455
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC456
    /// * `value` - The value of the profile field to set.
7ff8adafe9e4atagen2026-10-22 12:30 UTC457
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC458
    /// # Returns
7ff8adafe9e4atagen2026-10-22 12:30 UTC459
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC460
    /// Returns an error if the request fails.
7ff8adafe9e4atagen2026-10-22 12:30 UTC461
    pub async fn set_profile_field(&self, value: ProfileFieldValue) -> Result<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC462
        let user_id = self.client.user_id().ok_or(Error::AuthenticationRequired)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC463
        let request = set_profile_field::v3::Request::new(user_id.to_owned(), value);
7ff8adafe9e4atagen2026-10-22 12:30 UTC464
        self.client.send(request).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC465
7ff8adafe9e4atagen2026-10-22 12:30 UTC466
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC467
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC468
7ff8adafe9e4atagen2026-10-22 12:30 UTC469
    /// Delete the given field of our own user's profile.
7ff8adafe9e4atagen2026-10-22 12:30 UTC470
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC471
    /// [`Client::homeserver_capabilities()`] should be called first to check it
7ff8adafe9e4atagen2026-10-22 12:30 UTC472
    /// the field can be modified on the homeserver.
7ff8adafe9e4atagen2026-10-22 12:30 UTC473
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC474
    /// # Arguments
7ff8adafe9e4atagen2026-10-22 12:30 UTC475
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC476
    /// * `field` - The profile field to delete.
7ff8adafe9e4atagen2026-10-22 12:30 UTC477
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC478
    /// # Returns
7ff8adafe9e4atagen2026-10-22 12:30 UTC479
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC480
    /// Returns an error if the server doesn't support extended profile fields
7ff8adafe9e4atagen2026-10-22 12:30 UTC481
    /// of if the request fails in some other way.
7ff8adafe9e4atagen2026-10-22 12:30 UTC482
    pub async fn delete_profile_field(&self, field: ProfileFieldName) -> Result<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC483
        let user_id = self.client.user_id().ok_or(Error::AuthenticationRequired)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC484
        let request = delete_profile_field::v3::Request::new(user_id.to_owned(), field);
7ff8adafe9e4atagen2026-10-22 12:30 UTC485
        self.client.send(request).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC486
7ff8adafe9e4atagen2026-10-22 12:30 UTC487
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC488
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC489
7ff8adafe9e4atagen2026-10-22 12:30 UTC490
    /// Change the password of the account.
7ff8adafe9e4atagen2026-10-22 12:30 UTC491
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC492
    /// # Arguments
7ff8adafe9e4atagen2026-10-22 12:30 UTC493
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC494
    /// * `new_password` - The new password to set.
7ff8adafe9e4atagen2026-10-22 12:30 UTC495
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC496
    /// * `auth_data` - This request uses the [User-Interactive Authentication
7ff8adafe9e4atagen2026-10-22 12:30 UTC497
    ///   API][uiaa]. The first request needs to set this to `None` and will
7ff8adafe9e4atagen2026-10-22 12:30 UTC498
    ///   always fail with an [`UiaaResponse`]. The response will contain
7ff8adafe9e4atagen2026-10-22 12:30 UTC499
    ///   information for the interactive auth and the same request needs to be
7ff8adafe9e4atagen2026-10-22 12:30 UTC500
    ///   made but this time with some `auth_data` provided.
7ff8adafe9e4atagen2026-10-22 12:30 UTC501
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC502
    /// # Returns
7ff8adafe9e4atagen2026-10-22 12:30 UTC503
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC504
    /// This method might return an [`ErrorKind::WeakPassword`] error if the new
7ff8adafe9e4atagen2026-10-22 12:30 UTC505
    /// password is considered insecure by the homeserver, with details about
7ff8adafe9e4atagen2026-10-22 12:30 UTC506
    /// the strength requirements in the error's message.
7ff8adafe9e4atagen2026-10-22 12:30 UTC507
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC508
    /// # Examples
7ff8adafe9e4atagen2026-10-22 12:30 UTC509
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC510
    /// ```no_run
7ff8adafe9e4atagen2026-10-22 12:30 UTC511
    /// # use matrix_sdk::Client;
7ff8adafe9e4atagen2026-10-22 12:30 UTC512
    /// # use matrix_sdk::ruma::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC513
    /// #     api::client::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC514
    /// #         account::change_password::v3::{Request as ChangePasswordRequest},
7ff8adafe9e4atagen2026-10-22 12:30 UTC515
    /// #         uiaa::{AuthData, Dummy},
7ff8adafe9e4atagen2026-10-22 12:30 UTC516
    /// #     },
7ff8adafe9e4atagen2026-10-22 12:30 UTC517
    /// #     assign,
7ff8adafe9e4atagen2026-10-22 12:30 UTC518
    /// # };
7ff8adafe9e4atagen2026-10-22 12:30 UTC519
    /// # use url::Url;
7ff8adafe9e4atagen2026-10-22 12:30 UTC520
    /// # async {
7ff8adafe9e4atagen2026-10-22 12:30 UTC521
    /// # let homeserver = Url::parse("http://localhost:8080")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC522
    /// # let client = Client::new(homeserver).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC523
    /// client.account().change_password(
7ff8adafe9e4atagen2026-10-22 12:30 UTC524
    ///     "myverysecretpassword",
7ff8adafe9e4atagen2026-10-22 12:30 UTC525
    ///     Some(AuthData::Dummy(Dummy::new())),
7ff8adafe9e4atagen2026-10-22 12:30 UTC526
    /// ).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC527
    /// # anyhow::Ok(()) };
7ff8adafe9e4atagen2026-10-22 12:30 UTC528
    /// ```
7ff8adafe9e4atagen2026-10-22 12:30 UTC529
    /// [uiaa]: https://spec.matrix.org/v1.2/client-server-api/#user-interactive-authentication-api
7ff8adafe9e4atagen2026-10-22 12:30 UTC530
    /// [`UiaaResponse`]: ruma::api::client::uiaa::UiaaResponse
7ff8adafe9e4atagen2026-10-22 12:30 UTC531
    /// [`ErrorKind::WeakPassword`]: ruma::api::error::ErrorKind::WeakPassword
7ff8adafe9e4atagen2026-10-22 12:30 UTC532
    pub async fn change_password(
7ff8adafe9e4atagen2026-10-22 12:30 UTC533
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC534
        new_password: &str,
7ff8adafe9e4atagen2026-10-22 12:30 UTC535
        auth_data: Option<AuthData>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC536
    ) -> Result<change_password::v3::Response> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC537
        let request = assign!(change_password::v3::Request::new(new_password.to_owned()), {
7ff8adafe9e4atagen2026-10-22 12:30 UTC538
            auth: auth_data,
7ff8adafe9e4atagen2026-10-22 12:30 UTC539
        });
7ff8adafe9e4atagen2026-10-22 12:30 UTC540
        Ok(self.client.send(request).await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC541
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC542
7ff8adafe9e4atagen2026-10-22 12:30 UTC543
    /// Deactivate this account definitively.
7ff8adafe9e4atagen2026-10-22 12:30 UTC544
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC545
    /// # Arguments
7ff8adafe9e4atagen2026-10-22 12:30 UTC546
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC547
    /// * `id_server` - The identity server from which to unbind the user’s
7ff8adafe9e4atagen2026-10-22 12:30 UTC548
    ///   [Third Party Identifiers][3pid].
7ff8adafe9e4atagen2026-10-22 12:30 UTC549
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC550
    /// * `auth_data` - This request uses the [User-Interactive Authentication
7ff8adafe9e4atagen2026-10-22 12:30 UTC551
    ///   API][uiaa]. The first request needs to set this to `None` and will
7ff8adafe9e4atagen2026-10-22 12:30 UTC552
    ///   always fail with an [`UiaaResponse`]. The response will contain
7ff8adafe9e4atagen2026-10-22 12:30 UTC553
    ///   information for the interactive auth and the same request needs to be
7ff8adafe9e4atagen2026-10-22 12:30 UTC554
    ///   made but this time with some `auth_data` provided.
7ff8adafe9e4atagen2026-10-22 12:30 UTC555
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC556
    /// * `erase` - Whether the user would like their content to be erased as
7ff8adafe9e4atagen2026-10-22 12:30 UTC557
    ///   much as possible from the server.
7ff8adafe9e4atagen2026-10-22 12:30 UTC558
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC559
    /// # Examples
7ff8adafe9e4atagen2026-10-22 12:30 UTC560
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC561
    /// ```no_run
7ff8adafe9e4atagen2026-10-22 12:30 UTC562
    /// # use matrix_sdk::Client;
7ff8adafe9e4atagen2026-10-22 12:30 UTC563
    /// # use matrix_sdk::ruma::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC564
    /// #     api::client::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC565
    /// #         account::change_password::v3::{Request as ChangePasswordRequest},
7ff8adafe9e4atagen2026-10-22 12:30 UTC566
    /// #         uiaa::{AuthData, Dummy},
7ff8adafe9e4atagen2026-10-22 12:30 UTC567
    /// #     },
7ff8adafe9e4atagen2026-10-22 12:30 UTC568
    /// #     assign,
7ff8adafe9e4atagen2026-10-22 12:30 UTC569
    /// # };
7ff8adafe9e4atagen2026-10-22 12:30 UTC570
    /// # use url::Url;
7ff8adafe9e4atagen2026-10-22 12:30 UTC571
    /// # async {
7ff8adafe9e4atagen2026-10-22 12:30 UTC572
    /// # let homeserver = Url::parse("http://localhost:8080")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC573
    /// # let client = Client::new(homeserver).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC574
    /// # let account = client.account();
7ff8adafe9e4atagen2026-10-22 12:30 UTC575
    /// let response = account.deactivate(None, None, false).await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC576
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC577
    /// // Proceed with UIAA.
7ff8adafe9e4atagen2026-10-22 12:30 UTC578
    /// # anyhow::Ok(()) };
7ff8adafe9e4atagen2026-10-22 12:30 UTC579
    /// ```
7ff8adafe9e4atagen2026-10-22 12:30 UTC580
    /// [3pid]: https://spec.matrix.org/v1.2/appendices/#3pid-types
7ff8adafe9e4atagen2026-10-22 12:30 UTC581
    /// [uiaa]: https://spec.matrix.org/v1.2/client-server-api/#user-interactive-authentication-api
7ff8adafe9e4atagen2026-10-22 12:30 UTC582
    /// [`UiaaResponse`]: ruma::api::client::uiaa::UiaaResponse
7ff8adafe9e4atagen2026-10-22 12:30 UTC583
    pub async fn deactivate(
7ff8adafe9e4atagen2026-10-22 12:30 UTC584
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC585
        id_server: Option<&str>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC586
        auth_data: Option<AuthData>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC587
        erase_data: bool,
7ff8adafe9e4atagen2026-10-22 12:30 UTC588
    ) -> Result<deactivate::v3::Response> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC589
        let request = assign!(deactivate::v3::Request::new(), {
7ff8adafe9e4atagen2026-10-22 12:30 UTC590
            id_server: id_server.map(ToOwned::to_owned),
7ff8adafe9e4atagen2026-10-22 12:30 UTC591
            auth: auth_data,
7ff8adafe9e4atagen2026-10-22 12:30 UTC592
            erase: erase_data,
7ff8adafe9e4atagen2026-10-22 12:30 UTC593
        });
7ff8adafe9e4atagen2026-10-22 12:30 UTC594
        Ok(self.client.send(request).await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC595
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC596
7ff8adafe9e4atagen2026-10-22 12:30 UTC597
    /// Get the registered [Third Party Identifiers][3pid] on the homeserver of
7ff8adafe9e4atagen2026-10-22 12:30 UTC598
    /// the account.
7ff8adafe9e4atagen2026-10-22 12:30 UTC599
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC600
    /// These 3PIDs may be used by the homeserver to authenticate the user
7ff8adafe9e4atagen2026-10-22 12:30 UTC601
    /// during sensitive operations.
7ff8adafe9e4atagen2026-10-22 12:30 UTC602
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC603
    /// # Examples
7ff8adafe9e4atagen2026-10-22 12:30 UTC604
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC605
    /// ```no_run
7ff8adafe9e4atagen2026-10-22 12:30 UTC606
    /// # use matrix_sdk::Client;
7ff8adafe9e4atagen2026-10-22 12:30 UTC607
    /// # use url::Url;
7ff8adafe9e4atagen2026-10-22 12:30 UTC608
    /// # async {
7ff8adafe9e4atagen2026-10-22 12:30 UTC609
    /// # let homeserver = Url::parse("http://localhost:8080")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC610
    /// # let client = Client::new(homeserver).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC611
    /// let threepids = client.account().get_3pids().await?.threepids;
7ff8adafe9e4atagen2026-10-22 12:30 UTC612
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC613
    /// for threepid in threepids {
7ff8adafe9e4atagen2026-10-22 12:30 UTC614
    ///     println!(
7ff8adafe9e4atagen2026-10-22 12:30 UTC615
    ///         "Found 3PID '{}' of type '{}'",
7ff8adafe9e4atagen2026-10-22 12:30 UTC616
    ///         threepid.address, threepid.medium
7ff8adafe9e4atagen2026-10-22 12:30 UTC617
    ///     );
7ff8adafe9e4atagen2026-10-22 12:30 UTC618
    /// }
7ff8adafe9e4atagen2026-10-22 12:30 UTC619
    /// # anyhow::Ok(()) };
7ff8adafe9e4atagen2026-10-22 12:30 UTC620
    /// ```
7ff8adafe9e4atagen2026-10-22 12:30 UTC621
    /// [3pid]: https://spec.matrix.org/v1.2/appendices/#3pid-types
7ff8adafe9e4atagen2026-10-22 12:30 UTC622
    pub async fn get_3pids(&self) -> Result<get_3pids::v3::Response> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC623
        let request = get_3pids::v3::Request::new();
7ff8adafe9e4atagen2026-10-22 12:30 UTC624
        Ok(self.client.send(request).await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC625
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC626
7ff8adafe9e4atagen2026-10-22 12:30 UTC627
    /// Request a token to validate an email address as a [Third Party
7ff8adafe9e4atagen2026-10-22 12:30 UTC628
    /// Identifier][3pid].
7ff8adafe9e4atagen2026-10-22 12:30 UTC629
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC630
    /// This is the first step in registering an email address as 3PID. Next,
7ff8adafe9e4atagen2026-10-22 12:30 UTC631
    /// call [`Account::add_3pid()`] with the same `client_secret` and the
7ff8adafe9e4atagen2026-10-22 12:30 UTC632
    /// returned `sid`.
7ff8adafe9e4atagen2026-10-22 12:30 UTC633
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC634
    /// # Arguments
7ff8adafe9e4atagen2026-10-22 12:30 UTC635
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC636
    /// * `client_secret` - A client-generated secret string used to protect
7ff8adafe9e4atagen2026-10-22 12:30 UTC637
    ///   this session.
7ff8adafe9e4atagen2026-10-22 12:30 UTC638
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC639
    /// * `email` - The email address to validate.
7ff8adafe9e4atagen2026-10-22 12:30 UTC640
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC641
    /// * `send_attempt` - The attempt number. This number needs to be
7ff8adafe9e4atagen2026-10-22 12:30 UTC642
    ///   incremented if you want to request another token for the same
7ff8adafe9e4atagen2026-10-22 12:30 UTC643
    ///   validation.
7ff8adafe9e4atagen2026-10-22 12:30 UTC644
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC645
    /// # Returns
7ff8adafe9e4atagen2026-10-22 12:30 UTC646
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC647
    /// * `sid` - The session ID to be used in following requests for this 3PID.
7ff8adafe9e4atagen2026-10-22 12:30 UTC648
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC649
    /// * `submit_url` - If present, the user will submit the token to the
7ff8adafe9e4atagen2026-10-22 12:30 UTC650
    ///   client, that must send it to this URL. If not, the client will not be
7ff8adafe9e4atagen2026-10-22 12:30 UTC651
    ///   involved in the token submission.
7ff8adafe9e4atagen2026-10-22 12:30 UTC652
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC653
    /// This method might return an [`ErrorKind::ThreepidInUse`] error if the
7ff8adafe9e4atagen2026-10-22 12:30 UTC654
    /// email address is already registered for this account or another, or an
7ff8adafe9e4atagen2026-10-22 12:30 UTC655
    /// [`ErrorKind::ThreepidDenied`] error if it is denied.
7ff8adafe9e4atagen2026-10-22 12:30 UTC656
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC657
    /// # Examples
7ff8adafe9e4atagen2026-10-22 12:30 UTC658
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC659
    /// ```no_run
7ff8adafe9e4atagen2026-10-22 12:30 UTC660
    /// # use matrix_sdk::Client;
7ff8adafe9e4atagen2026-10-22 12:30 UTC661
    /// # use matrix_sdk::ruma::{ClientSecret, uint};
7ff8adafe9e4atagen2026-10-22 12:30 UTC662
    /// # use url::Url;
7ff8adafe9e4atagen2026-10-22 12:30 UTC663
    /// # async {
7ff8adafe9e4atagen2026-10-22 12:30 UTC664
    /// # let homeserver = Url::parse("http://localhost:8080")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC665
    /// # let client = Client::new(homeserver).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC666
    /// # let account = client.account();
7ff8adafe9e4atagen2026-10-22 12:30 UTC667
    /// # let secret = ClientSecret::parse("secret")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC668
    /// let token_response = account
7ff8adafe9e4atagen2026-10-22 12:30 UTC669
    ///     .request_3pid_email_token(&secret, "john@matrix.org", uint!(0))
7ff8adafe9e4atagen2026-10-22 12:30 UTC670
    ///     .await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC671
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC672
    /// // Wait for the user to confirm that the token was submitted or prompt
7ff8adafe9e4atagen2026-10-22 12:30 UTC673
    /// // the user for the token and send it to submit_url.
7ff8adafe9e4atagen2026-10-22 12:30 UTC674
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC675
    /// let uiaa_response =
7ff8adafe9e4atagen2026-10-22 12:30 UTC676
    ///     account.add_3pid(&secret, &token_response.sid, None).await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC677
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC678
    /// // Proceed with UIAA.
7ff8adafe9e4atagen2026-10-22 12:30 UTC679
    /// # anyhow::Ok(()) };
7ff8adafe9e4atagen2026-10-22 12:30 UTC680
    /// ```
7ff8adafe9e4atagen2026-10-22 12:30 UTC681
    /// [3pid]: https://spec.matrix.org/v1.2/appendices/#3pid-types
7ff8adafe9e4atagen2026-10-22 12:30 UTC682
    /// [`ErrorKind::ThreepidInUse`]: ruma::api::error::ErrorKind::ThreepidInUse
7ff8adafe9e4atagen2026-10-22 12:30 UTC683
    /// [`ErrorKind::ThreepidDenied`]: ruma::api::error::ErrorKind::ThreepidDenied
7ff8adafe9e4atagen2026-10-22 12:30 UTC684
    pub async fn request_3pid_email_token(
7ff8adafe9e4atagen2026-10-22 12:30 UTC685
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC686
        client_secret: &ClientSecret,
7ff8adafe9e4atagen2026-10-22 12:30 UTC687
        email: &str,
7ff8adafe9e4atagen2026-10-22 12:30 UTC688
        send_attempt: UInt,
7ff8adafe9e4atagen2026-10-22 12:30 UTC689
    ) -> Result<request_3pid_management_token_via_email::v3::Response> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC690
        let request = request_3pid_management_token_via_email::v3::Request::new(
7ff8adafe9e4atagen2026-10-22 12:30 UTC691
            client_secret.to_owned(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC692
            email.to_owned(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC693
            send_attempt,
7ff8adafe9e4atagen2026-10-22 12:30 UTC694
        );
7ff8adafe9e4atagen2026-10-22 12:30 UTC695
        Ok(self.client.send(request).await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC696
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC697
7ff8adafe9e4atagen2026-10-22 12:30 UTC698
    /// Request a token to validate a phone number as a [Third Party
7ff8adafe9e4atagen2026-10-22 12:30 UTC699
    /// Identifier][3pid].
7ff8adafe9e4atagen2026-10-22 12:30 UTC700
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC701
    /// This is the first step in registering a phone number as 3PID. Next,
7ff8adafe9e4atagen2026-10-22 12:30 UTC702
    /// call [`Account::add_3pid()`] with the same `client_secret` and the
7ff8adafe9e4atagen2026-10-22 12:30 UTC703
    /// returned `sid`.
7ff8adafe9e4atagen2026-10-22 12:30 UTC704
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC705
    /// # Arguments
7ff8adafe9e4atagen2026-10-22 12:30 UTC706
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC707
    /// * `client_secret` - A client-generated secret string used to protect
7ff8adafe9e4atagen2026-10-22 12:30 UTC708
    ///   this session.
7ff8adafe9e4atagen2026-10-22 12:30 UTC709
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC710
    /// * `country` - The two-letter uppercase ISO-3166-1 alpha-2 country code
7ff8adafe9e4atagen2026-10-22 12:30 UTC711
    ///   that the number in phone_number should be parsed as if it were dialled
7ff8adafe9e4atagen2026-10-22 12:30 UTC712
    ///   from.
7ff8adafe9e4atagen2026-10-22 12:30 UTC713
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC714
    /// * `phone_number` - The phone number to validate.
7ff8adafe9e4atagen2026-10-22 12:30 UTC715
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC716
    /// * `send_attempt` - The attempt number. This number needs to be
7ff8adafe9e4atagen2026-10-22 12:30 UTC717
    ///   incremented if you want to request another token for the same
7ff8adafe9e4atagen2026-10-22 12:30 UTC718
    ///   validation.
7ff8adafe9e4atagen2026-10-22 12:30 UTC719
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC720
    /// # Returns
7ff8adafe9e4atagen2026-10-22 12:30 UTC721
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC722
    /// * `sid` - The session ID to be used in following requests for this 3PID.
7ff8adafe9e4atagen2026-10-22 12:30 UTC723
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC724
    /// * `submit_url` - If present, the user will submit the token to the
7ff8adafe9e4atagen2026-10-22 12:30 UTC725
    ///   client, that must send it to this URL. If not, the client will not be
7ff8adafe9e4atagen2026-10-22 12:30 UTC726
    ///   involved in the token submission.
7ff8adafe9e4atagen2026-10-22 12:30 UTC727
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC728
    /// This method might return an [`ErrorKind::ThreepidInUse`] error if the
7ff8adafe9e4atagen2026-10-22 12:30 UTC729
    /// phone number is already registered for this account or another, or an
7ff8adafe9e4atagen2026-10-22 12:30 UTC730
    /// [`ErrorKind::ThreepidDenied`] error if it is denied.
7ff8adafe9e4atagen2026-10-22 12:30 UTC731
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC732
    /// # Examples
7ff8adafe9e4atagen2026-10-22 12:30 UTC733
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC734
    /// ```no_run
7ff8adafe9e4atagen2026-10-22 12:30 UTC735
    /// # use matrix_sdk::Client;
7ff8adafe9e4atagen2026-10-22 12:30 UTC736
    /// # use matrix_sdk::ruma::{ClientSecret, uint};
7ff8adafe9e4atagen2026-10-22 12:30 UTC737
    /// # use url::Url;
7ff8adafe9e4atagen2026-10-22 12:30 UTC738
    /// # async {
7ff8adafe9e4atagen2026-10-22 12:30 UTC739
    /// # let homeserver = Url::parse("http://localhost:8080")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC740
    /// # let client = Client::new(homeserver).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC741
    /// # let account = client.account();
7ff8adafe9e4atagen2026-10-22 12:30 UTC742
    /// # let secret = ClientSecret::parse("secret")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC743
    /// let token_response = account
7ff8adafe9e4atagen2026-10-22 12:30 UTC744
    ///     .request_3pid_msisdn_token(&secret, "FR", "0123456789", uint!(0))
7ff8adafe9e4atagen2026-10-22 12:30 UTC745
    ///     .await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC746
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC747
    /// // Wait for the user to confirm that the token was submitted or prompt
7ff8adafe9e4atagen2026-10-22 12:30 UTC748
    /// // the user for the token and send it to submit_url.
7ff8adafe9e4atagen2026-10-22 12:30 UTC749
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC750
    /// let uiaa_response =
7ff8adafe9e4atagen2026-10-22 12:30 UTC751
    ///     account.add_3pid(&secret, &token_response.sid, None).await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC752
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC753
    /// // Proceed with UIAA.
7ff8adafe9e4atagen2026-10-22 12:30 UTC754
    /// # anyhow::Ok(()) };
7ff8adafe9e4atagen2026-10-22 12:30 UTC755
    /// ```
7ff8adafe9e4atagen2026-10-22 12:30 UTC756
    /// [3pid]: https://spec.matrix.org/v1.2/appendices/#3pid-types
7ff8adafe9e4atagen2026-10-22 12:30 UTC757
    /// [`ErrorKind::ThreepidInUse`]: ruma::api::error::ErrorKind::ThreepidInUse
7ff8adafe9e4atagen2026-10-22 12:30 UTC758
    /// [`ErrorKind::ThreepidDenied`]: ruma::api::error::ErrorKind::ThreepidDenied
7ff8adafe9e4atagen2026-10-22 12:30 UTC759
    pub async fn request_3pid_msisdn_token(
7ff8adafe9e4atagen2026-10-22 12:30 UTC760
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC761
        client_secret: &ClientSecret,
7ff8adafe9e4atagen2026-10-22 12:30 UTC762
        country: &str,
7ff8adafe9e4atagen2026-10-22 12:30 UTC763
        phone_number: &str,
7ff8adafe9e4atagen2026-10-22 12:30 UTC764
        send_attempt: UInt,
7ff8adafe9e4atagen2026-10-22 12:30 UTC765
    ) -> Result<request_3pid_management_token_via_msisdn::v3::Response> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC766
        let request = request_3pid_management_token_via_msisdn::v3::Request::new(
7ff8adafe9e4atagen2026-10-22 12:30 UTC767
            client_secret.to_owned(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC768
            country.to_owned(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC769
            phone_number.to_owned(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC770
            send_attempt,
7ff8adafe9e4atagen2026-10-22 12:30 UTC771
        );
7ff8adafe9e4atagen2026-10-22 12:30 UTC772
        Ok(self.client.send(request).await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC773
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC774
7ff8adafe9e4atagen2026-10-22 12:30 UTC775
    /// Add a [Third Party Identifier][3pid] on the homeserver for this
7ff8adafe9e4atagen2026-10-22 12:30 UTC776
    /// account.
7ff8adafe9e4atagen2026-10-22 12:30 UTC777
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC778
    /// This 3PID may be used by the homeserver to authenticate the user
7ff8adafe9e4atagen2026-10-22 12:30 UTC779
    /// during sensitive operations.
7ff8adafe9e4atagen2026-10-22 12:30 UTC780
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC781
    /// This method should be called after
7ff8adafe9e4atagen2026-10-22 12:30 UTC782
    /// [`Account::request_3pid_email_token()`] or
7ff8adafe9e4atagen2026-10-22 12:30 UTC783
    /// [`Account::request_3pid_msisdn_token()`] to complete the 3PID
7ff8adafe9e4atagen2026-10-22 12:30 UTC784
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC785
    /// # Arguments
7ff8adafe9e4atagen2026-10-22 12:30 UTC786
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC787
    /// * `client_secret` - The same client secret used in
7ff8adafe9e4atagen2026-10-22 12:30 UTC788
    ///   [`Account::request_3pid_email_token()`] or
7ff8adafe9e4atagen2026-10-22 12:30 UTC789
    ///   [`Account::request_3pid_msisdn_token()`].
7ff8adafe9e4atagen2026-10-22 12:30 UTC790
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC791
    /// * `sid` - The session ID returned in
7ff8adafe9e4atagen2026-10-22 12:30 UTC792
    ///   [`Account::request_3pid_email_token()`] or
7ff8adafe9e4atagen2026-10-22 12:30 UTC793
    ///   [`Account::request_3pid_msisdn_token()`].
7ff8adafe9e4atagen2026-10-22 12:30 UTC794
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC795
    /// * `auth_data` - This request uses the [User-Interactive Authentication
7ff8adafe9e4atagen2026-10-22 12:30 UTC796
    ///   API][uiaa]. The first request needs to set this to `None` and will
7ff8adafe9e4atagen2026-10-22 12:30 UTC797
    ///   always fail with an [`UiaaResponse`]. The response will contain
7ff8adafe9e4atagen2026-10-22 12:30 UTC798
    ///   information for the interactive auth and the same request needs to be
7ff8adafe9e4atagen2026-10-22 12:30 UTC799
    ///   made but this time with some `auth_data` provided.
7ff8adafe9e4atagen2026-10-22 12:30 UTC800
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC801
    /// [3pid]: https://spec.matrix.org/v1.2/appendices/#3pid-types
7ff8adafe9e4atagen2026-10-22 12:30 UTC802
    /// [uiaa]: https://spec.matrix.org/v1.2/client-server-api/#user-interactive-authentication-api
7ff8adafe9e4atagen2026-10-22 12:30 UTC803
    /// [`UiaaResponse`]: ruma::api::client::uiaa::UiaaResponse
7ff8adafe9e4atagen2026-10-22 12:30 UTC804
    pub async fn add_3pid(
7ff8adafe9e4atagen2026-10-22 12:30 UTC805
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC806
        client_secret: &ClientSecret,
7ff8adafe9e4atagen2026-10-22 12:30 UTC807
        sid: &SessionId,
7ff8adafe9e4atagen2026-10-22 12:30 UTC808
        auth_data: Option<AuthData>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC809
    ) -> Result<add_3pid::v3::Response> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC810
        #[rustfmt::skip] // rustfmt wants to merge the next two lines
7ff8adafe9e4atagen2026-10-22 12:30 UTC811
        let request =
7ff8adafe9e4atagen2026-10-22 12:30 UTC812
            assign!(add_3pid::v3::Request::new(client_secret.to_owned(), sid.to_owned()), {
7ff8adafe9e4atagen2026-10-22 12:30 UTC813
                auth: auth_data
7ff8adafe9e4atagen2026-10-22 12:30 UTC814
            });
7ff8adafe9e4atagen2026-10-22 12:30 UTC815
        Ok(self.client.send(request).await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC816
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC817
7ff8adafe9e4atagen2026-10-22 12:30 UTC818
    /// Delete a [Third Party Identifier][3pid] from the homeserver for this
7ff8adafe9e4atagen2026-10-22 12:30 UTC819
    /// account.
7ff8adafe9e4atagen2026-10-22 12:30 UTC820
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC821
    /// # Arguments
7ff8adafe9e4atagen2026-10-22 12:30 UTC822
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC823
    /// * `address` - The 3PID being removed.
7ff8adafe9e4atagen2026-10-22 12:30 UTC824
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC825
    /// * `medium` - The type of the 3PID.
7ff8adafe9e4atagen2026-10-22 12:30 UTC826
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC827
    /// * `id_server` - The identity server to unbind from. If not provided, the
7ff8adafe9e4atagen2026-10-22 12:30 UTC828
    ///   homeserver should unbind the 3PID from the identity server it was
7ff8adafe9e4atagen2026-10-22 12:30 UTC829
    ///   bound to previously.
7ff8adafe9e4atagen2026-10-22 12:30 UTC830
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC831
    /// # Returns
7ff8adafe9e4atagen2026-10-22 12:30 UTC832
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC833
    /// * [`ThirdPartyIdRemovalStatus::Success`] if the 3PID was also unbound
7ff8adafe9e4atagen2026-10-22 12:30 UTC834
    ///   from the identity server.
7ff8adafe9e4atagen2026-10-22 12:30 UTC835
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC836
    /// * [`ThirdPartyIdRemovalStatus::NoSupport`] if the 3PID was not unbound
7ff8adafe9e4atagen2026-10-22 12:30 UTC837
    ///   from the identity server. This can also mean that the 3PID was not
7ff8adafe9e4atagen2026-10-22 12:30 UTC838
    ///   bound to an identity server in the first place.
7ff8adafe9e4atagen2026-10-22 12:30 UTC839
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC840
    /// # Examples
7ff8adafe9e4atagen2026-10-22 12:30 UTC841
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC842
    /// ```no_run
7ff8adafe9e4atagen2026-10-22 12:30 UTC843
    /// # use matrix_sdk::Client;
7ff8adafe9e4atagen2026-10-22 12:30 UTC844
    /// # use matrix_sdk::ruma::thirdparty::Medium;
7ff8adafe9e4atagen2026-10-22 12:30 UTC845
    /// # use matrix_sdk::ruma::api::client::account::ThirdPartyIdRemovalStatus;
7ff8adafe9e4atagen2026-10-22 12:30 UTC846
    /// # use url::Url;
7ff8adafe9e4atagen2026-10-22 12:30 UTC847
    /// # async {
7ff8adafe9e4atagen2026-10-22 12:30 UTC848
    /// # let homeserver = Url::parse("http://localhost:8080")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC849
    /// # let client = Client::new(homeserver).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC850
    /// # let account = client.account();
7ff8adafe9e4atagen2026-10-22 12:30 UTC851
    /// match account
7ff8adafe9e4atagen2026-10-22 12:30 UTC852
    ///     .delete_3pid("paul@matrix.org", Medium::Email, None)
7ff8adafe9e4atagen2026-10-22 12:30 UTC853
    ///     .await?
7ff8adafe9e4atagen2026-10-22 12:30 UTC854
    ///     .id_server_unbind_result
7ff8adafe9e4atagen2026-10-22 12:30 UTC855
    /// {
7ff8adafe9e4atagen2026-10-22 12:30 UTC856
    ///     ThirdPartyIdRemovalStatus::Success => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC857
    ///         println!("3PID unbound from the Identity Server");
7ff8adafe9e4atagen2026-10-22 12:30 UTC858
    ///     }
7ff8adafe9e4atagen2026-10-22 12:30 UTC859
    ///     _ => println!("Could not unbind 3PID from the Identity Server"),
7ff8adafe9e4atagen2026-10-22 12:30 UTC860
    /// }
7ff8adafe9e4atagen2026-10-22 12:30 UTC861
    /// # anyhow::Ok(()) };
7ff8adafe9e4atagen2026-10-22 12:30 UTC862
    /// ```
7ff8adafe9e4atagen2026-10-22 12:30 UTC863
    /// [3pid]: https://spec.matrix.org/v1.2/appendices/#3pid-types
7ff8adafe9e4atagen2026-10-22 12:30 UTC864
    /// [`ThirdPartyIdRemovalStatus::Success`]: ruma::api::client::account::ThirdPartyIdRemovalStatus::Success
7ff8adafe9e4atagen2026-10-22 12:30 UTC865
    /// [`ThirdPartyIdRemovalStatus::NoSupport`]: ruma::api::client::account::ThirdPartyIdRemovalStatus::NoSupport
7ff8adafe9e4atagen2026-10-22 12:30 UTC866
    pub async fn delete_3pid(
7ff8adafe9e4atagen2026-10-22 12:30 UTC867
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC868
        address: &str,
7ff8adafe9e4atagen2026-10-22 12:30 UTC869
        medium: Medium,
7ff8adafe9e4atagen2026-10-22 12:30 UTC870
        id_server: Option<&str>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC871
    ) -> Result<delete_3pid::v3::Response> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC872
        let request = assign!(delete_3pid::v3::Request::new(medium, address.to_owned()), {
7ff8adafe9e4atagen2026-10-22 12:30 UTC873
            id_server: id_server.map(ToOwned::to_owned),
7ff8adafe9e4atagen2026-10-22 12:30 UTC874
        });
7ff8adafe9e4atagen2026-10-22 12:30 UTC875
        Ok(self.client.send(request).await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC876
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC877
7ff8adafe9e4atagen2026-10-22 12:30 UTC878
    /// Get the content of an account data event of statically-known type, from
7ff8adafe9e4atagen2026-10-22 12:30 UTC879
    /// storage.
7ff8adafe9e4atagen2026-10-22 12:30 UTC880
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC881
    /// # Examples
7ff8adafe9e4atagen2026-10-22 12:30 UTC882
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC883
    /// ```no_run
7ff8adafe9e4atagen2026-10-22 12:30 UTC884
    /// # use matrix_sdk::Client;
7ff8adafe9e4atagen2026-10-22 12:30 UTC885
    /// # async {
7ff8adafe9e4atagen2026-10-22 12:30 UTC886
    /// # let client = Client::new("http://localhost:8080".parse()?).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC887
    /// # let account = client.account();
7ff8adafe9e4atagen2026-10-22 12:30 UTC888
    /// use matrix_sdk::ruma::events::ignored_user_list::IgnoredUserListEventContent;
7ff8adafe9e4atagen2026-10-22 12:30 UTC889
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC890
    /// let maybe_content = account.account_data::<IgnoredUserListEventContent>().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC891
    /// if let Some(raw_content) = maybe_content {
7ff8adafe9e4atagen2026-10-22 12:30 UTC892
    ///     let content = raw_content.deserialize()?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC893
    ///     println!("Ignored users:");
7ff8adafe9e4atagen2026-10-22 12:30 UTC894
    ///     for user_id in content.ignored_users.keys() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC895
    ///         println!("- {user_id}");
7ff8adafe9e4atagen2026-10-22 12:30 UTC896
    ///     }
7ff8adafe9e4atagen2026-10-22 12:30 UTC897
    /// }
7ff8adafe9e4atagen2026-10-22 12:30 UTC898
    /// # anyhow::Ok(()) };
7ff8adafe9e4atagen2026-10-22 12:30 UTC899
    /// ```
7ff8adafe9e4atagen2026-10-22 12:30 UTC900
    pub async fn account_data<C>(&self) -> Result<Option<Raw<C>>>
7ff8adafe9e4atagen2026-10-22 12:30 UTC901
    where
7ff8adafe9e4atagen2026-10-22 12:30 UTC902
        C: GlobalAccountDataEventContent + StaticEventContent<IsPrefix = ruma::events::False>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC903
    {
7ff8adafe9e4atagen2026-10-22 12:30 UTC904
        get_raw_content(self.client.state_store().get_account_data_event_static::<C>().await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC905
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC906
7ff8adafe9e4atagen2026-10-22 12:30 UTC907
    /// Get the content of an account data event of a given type, from storage.
7ff8adafe9e4atagen2026-10-22 12:30 UTC908
    pub async fn account_data_raw(
7ff8adafe9e4atagen2026-10-22 12:30 UTC909
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC910
        event_type: GlobalAccountDataEventType,
7ff8adafe9e4atagen2026-10-22 12:30 UTC911
    ) -> Result<Option<Raw<AnyGlobalAccountDataEventContent>>> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC912
        get_raw_content(self.client.state_store().get_account_data_event(event_type).await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC913
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC914
7ff8adafe9e4atagen2026-10-22 12:30 UTC915
    /// Fetch a global account data event from the server.
7ff8adafe9e4atagen2026-10-22 12:30 UTC916
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC917
    /// The content from the response will not be persisted in the store.
7ff8adafe9e4atagen2026-10-22 12:30 UTC918
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC919
    /// Examples
7ff8adafe9e4atagen2026-10-22 12:30 UTC920
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC921
    /// ```no_run
7ff8adafe9e4atagen2026-10-22 12:30 UTC922
    /// # use matrix_sdk::Client;
7ff8adafe9e4atagen2026-10-22 12:30 UTC923
    /// # async {
7ff8adafe9e4atagen2026-10-22 12:30 UTC924
    /// # let client = Client::new("http://localhost:8080".parse()?).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC925
    /// # let account = client.account();
7ff8adafe9e4atagen2026-10-22 12:30 UTC926
    /// use matrix_sdk::ruma::events::{ignored_user_list::IgnoredUserListEventContent, GlobalAccountDataEventType};
7ff8adafe9e4atagen2026-10-22 12:30 UTC927
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC928
    /// if let Some(raw_content) = account.fetch_account_data(GlobalAccountDataEventType::IgnoredUserList).await? {
7ff8adafe9e4atagen2026-10-22 12:30 UTC929
    ///     let content = raw_content.deserialize_as_unchecked::<IgnoredUserListEventContent>()?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC930
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC931
    ///     println!("Ignored users:");
7ff8adafe9e4atagen2026-10-22 12:30 UTC932
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC933
    ///     for user_id in content.ignored_users.keys() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC934
    ///         println!("- {user_id}");
7ff8adafe9e4atagen2026-10-22 12:30 UTC935
    ///     }
7ff8adafe9e4atagen2026-10-22 12:30 UTC936
    /// }
7ff8adafe9e4atagen2026-10-22 12:30 UTC937
    /// # anyhow::Ok(()) };
7ff8adafe9e4atagen2026-10-22 12:30 UTC938
    pub async fn fetch_account_data(
7ff8adafe9e4atagen2026-10-22 12:30 UTC939
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC940
        event_type: GlobalAccountDataEventType,
7ff8adafe9e4atagen2026-10-22 12:30 UTC941
    ) -> Result<Option<Raw<AnyGlobalAccountDataEventContent>>> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC942
        let own_user = self.client.user_id().ok_or(Error::AuthenticationRequired)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC943
7ff8adafe9e4atagen2026-10-22 12:30 UTC944
        let request = get_global_account_data::v3::Request::new(own_user.to_owned(), event_type);
7ff8adafe9e4atagen2026-10-22 12:30 UTC945
7ff8adafe9e4atagen2026-10-22 12:30 UTC946
        match self.client.send(request).await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC947
            Ok(r) => Ok(Some(r.account_data)),
7ff8adafe9e4atagen2026-10-22 12:30 UTC948
            Err(e) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC949
                if let Some(kind) = e.client_api_error_kind() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC950
                    if kind == &ErrorKind::NotFound { Ok(None) } else { Err(e.into()) }
7ff8adafe9e4atagen2026-10-22 12:30 UTC951
                } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC952
                    Err(e.into())
7ff8adafe9e4atagen2026-10-22 12:30 UTC953
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC954
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC955
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC956
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC957
7ff8adafe9e4atagen2026-10-22 12:30 UTC958
    /// Fetch an account data event of statically-known type from the server.
7ff8adafe9e4atagen2026-10-22 12:30 UTC959
    pub async fn fetch_account_data_static<C>(&self) -> Result<Option<Raw<C>>>
7ff8adafe9e4atagen2026-10-22 12:30 UTC960
    where
7ff8adafe9e4atagen2026-10-22 12:30 UTC961
        C: GlobalAccountDataEventContent + StaticEventContent<IsPrefix = ruma::events::False>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC962
    {
7ff8adafe9e4atagen2026-10-22 12:30 UTC963
        Ok(self.fetch_account_data(C::TYPE.into()).await?.map(Raw::cast_unchecked))
7ff8adafe9e4atagen2026-10-22 12:30 UTC964
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC965
7ff8adafe9e4atagen2026-10-22 12:30 UTC966
    /// Set the given account data event.
7ff8adafe9e4atagen2026-10-22 12:30 UTC967
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC968
    /// # Examples
7ff8adafe9e4atagen2026-10-22 12:30 UTC969
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC970
    /// ```no_run
7ff8adafe9e4atagen2026-10-22 12:30 UTC971
    /// # use matrix_sdk::Client;
7ff8adafe9e4atagen2026-10-22 12:30 UTC972
    /// # async {
7ff8adafe9e4atagen2026-10-22 12:30 UTC973
    /// # let client = Client::new("http://localhost:8080".parse()?).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC974
    /// # let account = client.account();
7ff8adafe9e4atagen2026-10-22 12:30 UTC975
    /// use matrix_sdk::ruma::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC976
    ///     events::ignored_user_list::{IgnoredUser, IgnoredUserListEventContent},
7ff8adafe9e4atagen2026-10-22 12:30 UTC977
    ///     user_id,
7ff8adafe9e4atagen2026-10-22 12:30 UTC978
    /// };
7ff8adafe9e4atagen2026-10-22 12:30 UTC979
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC980
    /// let mut content = account
7ff8adafe9e4atagen2026-10-22 12:30 UTC981
    ///     .account_data::<IgnoredUserListEventContent>()
7ff8adafe9e4atagen2026-10-22 12:30 UTC982
    ///     .await?
7ff8adafe9e4atagen2026-10-22 12:30 UTC983
    ///     .map(|c| c.deserialize())
7ff8adafe9e4atagen2026-10-22 12:30 UTC984
    ///     .transpose()?
7ff8adafe9e4atagen2026-10-22 12:30 UTC985
    ///     .unwrap_or_default();
7ff8adafe9e4atagen2026-10-22 12:30 UTC986
    /// content
7ff8adafe9e4atagen2026-10-22 12:30 UTC987
    ///     .ignored_users
7ff8adafe9e4atagen2026-10-22 12:30 UTC988
    ///     .insert(user_id!("@foo:bar.com").to_owned(), IgnoredUser::new());
7ff8adafe9e4atagen2026-10-22 12:30 UTC989
    /// account.set_account_data(content).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC990
    /// # anyhow::Ok(()) };
7ff8adafe9e4atagen2026-10-22 12:30 UTC991
    /// ```
7ff8adafe9e4atagen2026-10-22 12:30 UTC992
    pub async fn set_account_data<T>(
7ff8adafe9e4atagen2026-10-22 12:30 UTC993
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC994
        content: T,
7ff8adafe9e4atagen2026-10-22 12:30 UTC995
    ) -> Result<set_global_account_data::v3::Response>
7ff8adafe9e4atagen2026-10-22 12:30 UTC996
    where
7ff8adafe9e4atagen2026-10-22 12:30 UTC997
        T: GlobalAccountDataEventContent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC998
    {
7ff8adafe9e4atagen2026-10-22 12:30 UTC999
        let own_user = self.client.user_id().ok_or(Error::AuthenticationRequired)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1000
7ff8adafe9e4atagen2026-10-22 12:30 UTC1001
        let request = set_global_account_data::v3::Request::new(own_user.to_owned(), &content)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1002
7ff8adafe9e4atagen2026-10-22 12:30 UTC1003
        Ok(self.client.send(request).await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1004
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1005
7ff8adafe9e4atagen2026-10-22 12:30 UTC1006
    /// Set the given raw account data event.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1007
    pub async fn set_account_data_raw(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1008
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1009
        event_type: GlobalAccountDataEventType,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1010
        content: Raw<AnyGlobalAccountDataEventContent>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1011
    ) -> Result<set_global_account_data::v3::Response> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1012
        let own_user = self.client.user_id().ok_or(Error::AuthenticationRequired)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1013
7ff8adafe9e4atagen2026-10-22 12:30 UTC1014
        let request =
7ff8adafe9e4atagen2026-10-22 12:30 UTC1015
            set_global_account_data::v3::Request::new_raw(own_user.to_owned(), event_type, content);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1016
7ff8adafe9e4atagen2026-10-22 12:30 UTC1017
        Ok(self.client.send(request).await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1018
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1019
7ff8adafe9e4atagen2026-10-22 12:30 UTC1020
    /// Marks the room identified by `room_id` as a "direct chat" with each
7ff8adafe9e4atagen2026-10-22 12:30 UTC1021
    /// user in `user_ids`.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1022
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1023
    /// # Arguments
7ff8adafe9e4atagen2026-10-22 12:30 UTC1024
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1025
    /// * `room_id` - The room ID of the direct message room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1026
    /// * `user_ids` - The user IDs to be associated with this direct message
7ff8adafe9e4atagen2026-10-22 12:30 UTC1027
    ///   room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1028
    pub async fn mark_as_dm(&self, room_id: &RoomId, user_ids: &[OwnedUserId]) -> Result<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1029
        use ruma::events::direct::DirectEventContent;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1030
7ff8adafe9e4atagen2026-10-22 12:30 UTC1031
        // This function does a read/update/store of an account data event stored on the
7ff8adafe9e4atagen2026-10-22 12:30 UTC1032
        // homeserver. We first fetch the existing account data event, the event
7ff8adafe9e4atagen2026-10-22 12:30 UTC1033
        // contains a map which gets updated by this method, finally we upload the
7ff8adafe9e4atagen2026-10-22 12:30 UTC1034
        // modified event.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1035
        //
7ff8adafe9e4atagen2026-10-22 12:30 UTC1036
        // To prevent multiple calls to this method trying to update the map of DMs same
7ff8adafe9e4atagen2026-10-22 12:30 UTC1037
        // time, and thus trampling on each other we introduce a lock which acts
7ff8adafe9e4atagen2026-10-22 12:30 UTC1038
        // as a semaphore.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1039
        let _guard = self.client.locks().mark_as_dm_lock.lock().await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1040
7ff8adafe9e4atagen2026-10-22 12:30 UTC1041
        // Now we need to mark the room as a DM for ourselves, we fetch the
7ff8adafe9e4atagen2026-10-22 12:30 UTC1042
        // existing `m.direct` event and append the room to the list of DMs we
7ff8adafe9e4atagen2026-10-22 12:30 UTC1043
        // have with this user.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1044
7ff8adafe9e4atagen2026-10-22 12:30 UTC1045
        // We are fetching the content from the server because we currently can't rely
7ff8adafe9e4atagen2026-10-22 12:30 UTC1046
        // on `/sync` giving us the correct data in a timely manner.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1047
        let raw_content = self.fetch_account_data_static::<DirectEventContent>().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1048
7ff8adafe9e4atagen2026-10-22 12:30 UTC1049
        let mut content = if let Some(raw_content) = raw_content {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1050
            // Log the error and pass it upwards if we fail to deserialize the m.direct
7ff8adafe9e4atagen2026-10-22 12:30 UTC1051
            // event.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1052
            raw_content.deserialize().map_err(|err| {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1053
                error!("unable to deserialize m.direct event content; aborting request to mark {room_id} as dm: {err}");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1054
                err
7ff8adafe9e4atagen2026-10-22 12:30 UTC1055
            })?
7ff8adafe9e4atagen2026-10-22 12:30 UTC1056
        } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1057
            // If there was no m.direct event server-side, create a default one.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1058
            Default::default()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1059
        };
7ff8adafe9e4atagen2026-10-22 12:30 UTC1060
7ff8adafe9e4atagen2026-10-22 12:30 UTC1061
        for user_id in user_ids {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1062
            content.entry(user_id.into()).or_default().push(room_id.to_owned());
7ff8adafe9e4atagen2026-10-22 12:30 UTC1063
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1064
7ff8adafe9e4atagen2026-10-22 12:30 UTC1065
        // TODO: We should probably save the fact that we need to send this out
7ff8adafe9e4atagen2026-10-22 12:30 UTC1066
        // because otherwise we might end up in a state where we have a DM that
7ff8adafe9e4atagen2026-10-22 12:30 UTC1067
        // isn't marked as one.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1068
        self.set_account_data(content).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1069
7ff8adafe9e4atagen2026-10-22 12:30 UTC1070
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1071
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1072
7ff8adafe9e4atagen2026-10-22 12:30 UTC1073
    /// Adds the given user ID to the account's ignore list.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1074
    pub async fn ignore_user(&self, user_id: &UserId) -> Result<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1075
        let own_user_id = self.client.user_id().ok_or(Error::AuthenticationRequired)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1076
        if user_id == own_user_id {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1077
            return Err(Error::CantIgnoreLoggedInUser);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1078
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1079
7ff8adafe9e4atagen2026-10-22 12:30 UTC1080
        let mut ignored_user_list = self.get_ignored_user_list_event_content().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1081
        ignored_user_list.ignored_users.insert(user_id.to_owned(), IgnoredUser::new());
7ff8adafe9e4atagen2026-10-22 12:30 UTC1082
7ff8adafe9e4atagen2026-10-22 12:30 UTC1083
        self.set_account_data(ignored_user_list).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1084
7ff8adafe9e4atagen2026-10-22 12:30 UTC1085
        // In theory, we should also clear some caches here, because they may include
7ff8adafe9e4atagen2026-10-22 12:30 UTC1086
        // events sent by the ignored user. In practice, we expect callers to
7ff8adafe9e4atagen2026-10-22 12:30 UTC1087
        // take care of this, or subsystems to listen to user list changes and
7ff8adafe9e4atagen2026-10-22 12:30 UTC1088
        // clear caches accordingly.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1089
7ff8adafe9e4atagen2026-10-22 12:30 UTC1090
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1091
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1092
7ff8adafe9e4atagen2026-10-22 12:30 UTC1093
    /// Removes the given user ID from the account's ignore list.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1094
    pub async fn unignore_user(&self, user_id: &UserId) -> Result<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1095
        let mut ignored_user_list = self.get_ignored_user_list_event_content().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1096
7ff8adafe9e4atagen2026-10-22 12:30 UTC1097
        // Only update account data if the user was ignored in the first place.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1098
        if ignored_user_list.ignored_users.remove(user_id).is_some() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1099
            self.set_account_data(ignored_user_list).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1100
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1101
7ff8adafe9e4atagen2026-10-22 12:30 UTC1102
        // See comment in `ignore_user`.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1103
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1104
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1105
7ff8adafe9e4atagen2026-10-22 12:30 UTC1106
    async fn get_ignored_user_list_event_content(&self) -> Result<IgnoredUserListEventContent> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1107
        let ignored_user_list = self
7ff8adafe9e4atagen2026-10-22 12:30 UTC1108
            .account_data::<IgnoredUserListEventContent>()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1109
            .await?
7ff8adafe9e4atagen2026-10-22 12:30 UTC1110
            .map(|c| c.deserialize())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1111
            .transpose()?
7ff8adafe9e4atagen2026-10-22 12:30 UTC1112
            .unwrap_or_default();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1113
        Ok(ignored_user_list)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1114
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1115
7ff8adafe9e4atagen2026-10-22 12:30 UTC1116
    /// Get the current push rules from storage.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1117
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1118
    /// If no push rules event was found, or it fails to deserialize, a ruleset
7ff8adafe9e4atagen2026-10-22 12:30 UTC1119
    /// with the server-default push rules is returned.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1120
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1121
    /// Panics if called when the client is not logged in.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1122
    pub async fn push_rules(&self) -> Result<Ruleset> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1123
        Ok(self
7ff8adafe9e4atagen2026-10-22 12:30 UTC1124
            .account_data::<PushRulesEventContent>()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1125
            .await?
7ff8adafe9e4atagen2026-10-22 12:30 UTC1126
            .and_then(|r| match r.deserialize() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1127
                Ok(r) => Some(r.global),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1128
                Err(e) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1129
                    error!("Push rules event failed to deserialize: {e}");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1130
                    None
7ff8adafe9e4atagen2026-10-22 12:30 UTC1131
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1132
            })
7ff8adafe9e4atagen2026-10-22 12:30 UTC1133
            .unwrap_or_else(|| {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1134
                Ruleset::server_default(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1135
                    self.client.user_id().expect("The client should be logged in"),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1136
                )
7ff8adafe9e4atagen2026-10-22 12:30 UTC1137
            }))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1138
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1139
7ff8adafe9e4atagen2026-10-22 12:30 UTC1140
    /// Retrieves the user's recently visited room list
7ff8adafe9e4atagen2026-10-22 12:30 UTC1141
    pub async fn get_recently_visited_rooms(&self) -> Result<Vec<OwnedRoomId>> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1142
        let user_id = self.client.user_id().ok_or(Error::AuthenticationRequired)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1143
        let data = self
7ff8adafe9e4atagen2026-10-22 12:30 UTC1144
            .client
7ff8adafe9e4atagen2026-10-22 12:30 UTC1145
            .state_store()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1146
            .get_kv_data(StateStoreDataKey::RecentlyVisitedRooms(user_id))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1147
            .await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1148
7ff8adafe9e4atagen2026-10-22 12:30 UTC1149
        Ok(data
7ff8adafe9e4atagen2026-10-22 12:30 UTC1150
            .map(|v| {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1151
                v.into_recently_visited_rooms()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1152
                    .expect("Session data is not a list of recently visited rooms")
7ff8adafe9e4atagen2026-10-22 12:30 UTC1153
            })
7ff8adafe9e4atagen2026-10-22 12:30 UTC1154
            .unwrap_or_default())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1155
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1156
7ff8adafe9e4atagen2026-10-22 12:30 UTC1157
    /// Moves/inserts the given room to the front of the recently visited list
7ff8adafe9e4atagen2026-10-22 12:30 UTC1158
    pub async fn track_recently_visited_room(&self, room_id: OwnedRoomId) -> Result<(), Error> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1159
        let user_id = self.client.user_id().ok_or(Error::AuthenticationRequired)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1160
7ff8adafe9e4atagen2026-10-22 12:30 UTC1161
        // Get the previously stored recently visited rooms
7ff8adafe9e4atagen2026-10-22 12:30 UTC1162
        let mut recently_visited_rooms = self.get_recently_visited_rooms().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1163
7ff8adafe9e4atagen2026-10-22 12:30 UTC1164
        // Remove all other occurrences of the new room_id
7ff8adafe9e4atagen2026-10-22 12:30 UTC1165
        recently_visited_rooms.retain(|r| r != &room_id);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1166
7ff8adafe9e4atagen2026-10-22 12:30 UTC1167
        // And insert it as the most recent
7ff8adafe9e4atagen2026-10-22 12:30 UTC1168
        recently_visited_rooms.insert(0, room_id);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1169
7ff8adafe9e4atagen2026-10-22 12:30 UTC1170
        // Cap the whole list to the VISITED_ROOMS_LIMIT
7ff8adafe9e4atagen2026-10-22 12:30 UTC1171
        recently_visited_rooms.truncate(Self::VISITED_ROOMS_LIMIT);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1172
7ff8adafe9e4atagen2026-10-22 12:30 UTC1173
        let data = StateStoreDataValue::RecentlyVisitedRooms(recently_visited_rooms);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1174
        self.client
7ff8adafe9e4atagen2026-10-22 12:30 UTC1175
            .state_store()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1176
            .set_kv_data(StateStoreDataKey::RecentlyVisitedRooms(user_id), data)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1177
            .await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1178
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1179
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1180
7ff8adafe9e4atagen2026-10-22 12:30 UTC1181
    /// Observes the media preview configuration.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1182
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1183
    /// This value is linked to the [MSC 4278](https://github.com/matrix-org/matrix-spec-proposals/pull/4278) which is still in an unstable state.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1184
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1185
    /// This will return the initial value of the configuration and a stream
7ff8adafe9e4atagen2026-10-22 12:30 UTC1186
    /// that will yield new values as they are received.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1187
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1188
    /// The initial value is the one that was stored in the account data
7ff8adafe9e4atagen2026-10-22 12:30 UTC1189
    /// when the client was started.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1190
    /// and the following code is using a temporary solution until we know which
7ff8adafe9e4atagen2026-10-22 12:30 UTC1191
    /// Matrix version will support the stable type.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1192
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1193
    /// # Examples
7ff8adafe9e4atagen2026-10-22 12:30 UTC1194
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1195
    /// ```no_run
7ff8adafe9e4atagen2026-10-22 12:30 UTC1196
    /// # use futures_util::{pin_mut, StreamExt};
7ff8adafe9e4atagen2026-10-22 12:30 UTC1197
    /// # use matrix_sdk::Client;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1198
    /// # use matrix_sdk::ruma::events::media_preview_config::MediaPreviews;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1199
    /// # use url::Url;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1200
    /// # async {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1201
    /// # let homeserver = Url::parse("http://localhost:8080")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1202
    /// # let client = Client::new(homeserver).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1203
    /// let account = client.account();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1204
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1205
    /// let (initial_config, config_stream) =
7ff8adafe9e4atagen2026-10-22 12:30 UTC1206
    ///     account.observe_media_preview_config().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1207
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1208
    /// println!("Initial media preview config: {:?}", initial_config);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1209
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1210
    /// pin_mut!(config_stream);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1211
    /// while let Some(new_config) = config_stream.next().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1212
    ///     println!("Updated media preview config: {:?}", new_config);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1213
    /// }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1214
    /// # anyhow::Ok(()) };
7ff8adafe9e4atagen2026-10-22 12:30 UTC1215
    /// ```
7ff8adafe9e4atagen2026-10-22 12:30 UTC1216
    pub async fn observe_media_preview_config(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1217
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1218
    ) -> Result<
7ff8adafe9e4atagen2026-10-22 12:30 UTC1219
        (
7ff8adafe9e4atagen2026-10-22 12:30 UTC1220
            Option<MediaPreviewConfigEventContent>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1221
            impl Stream<Item = MediaPreviewConfigEventContent> + use<>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1222
        ),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1223
        Error,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1224
    > {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1225
        // We need to create two observers, one for the stable event and one for the
7ff8adafe9e4atagen2026-10-22 12:30 UTC1226
        // unstable and combine them into a single stream.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1227
        let first_observer = self
7ff8adafe9e4atagen2026-10-22 12:30 UTC1228
            .client
7ff8adafe9e4atagen2026-10-22 12:30 UTC1229
            .observe_events::<GlobalAccountDataEvent<MediaPreviewConfigEventContent>, ()>();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1230
7ff8adafe9e4atagen2026-10-22 12:30 UTC1231
        let stream = first_observer.subscribe().map(|event| event.0.content);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1232
7ff8adafe9e4atagen2026-10-22 12:30 UTC1233
        let second_observer = self
7ff8adafe9e4atagen2026-10-22 12:30 UTC1234
            .client
7ff8adafe9e4atagen2026-10-22 12:30 UTC1235
            .observe_events::<GlobalAccountDataEvent<UnstableMediaPreviewConfigEventContent>, ()>();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1236
7ff8adafe9e4atagen2026-10-22 12:30 UTC1237
        let second_stream = second_observer.subscribe().map(|event| event.0.content.0);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1238
7ff8adafe9e4atagen2026-10-22 12:30 UTC1239
        let mut combined_stream = stream::select(stream, second_stream);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1240
7ff8adafe9e4atagen2026-10-22 12:30 UTC1241
        let result_stream = async_stream::stream! {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1242
            // The observers need to be alive for the individual streams to be alive, so let's now
7ff8adafe9e4atagen2026-10-22 12:30 UTC1243
            // create a stream that takes ownership of them.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1244
            let _first_observer = first_observer;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1245
            let _second_observer = second_observer;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1246
7ff8adafe9e4atagen2026-10-22 12:30 UTC1247
            while let Some(item) = combined_stream.next().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1248
                yield item
7ff8adafe9e4atagen2026-10-22 12:30 UTC1249
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1250
        };
7ff8adafe9e4atagen2026-10-22 12:30 UTC1251
7ff8adafe9e4atagen2026-10-22 12:30 UTC1252
        // We need to get the initial value of the media preview config event
7ff8adafe9e4atagen2026-10-22 12:30 UTC1253
        // we do this after creating the observers to make sure that we don't
7ff8adafe9e4atagen2026-10-22 12:30 UTC1254
        // create a race condition
7ff8adafe9e4atagen2026-10-22 12:30 UTC1255
        let initial_value = self.get_media_preview_config_event_content().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1256
7ff8adafe9e4atagen2026-10-22 12:30 UTC1257
        Ok((initial_value, result_stream))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1258
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1259
7ff8adafe9e4atagen2026-10-22 12:30 UTC1260
    /// Fetch the media preview configuration event content from the server.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1261
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1262
    /// Will check first for the stable event and then for the unstable one.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1263
    pub async fn fetch_media_preview_config_event_content(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1264
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1265
    ) -> Result<Option<MediaPreviewConfigEventContent>> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1266
        // First we check if there is a value in the stable event
7ff8adafe9e4atagen2026-10-22 12:30 UTC1267
        let media_preview_config =
7ff8adafe9e4atagen2026-10-22 12:30 UTC1268
            self.fetch_account_data_static::<MediaPreviewConfigEventContent>().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1269
7ff8adafe9e4atagen2026-10-22 12:30 UTC1270
        let media_preview_config = if let Some(media_preview_config) = media_preview_config {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1271
            Some(media_preview_config)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1272
        } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1273
            // If there is no value in the stable event, we check the unstable
7ff8adafe9e4atagen2026-10-22 12:30 UTC1274
            self.fetch_account_data_static::<UnstableMediaPreviewConfigEventContent>()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1275
                .await?
7ff8adafe9e4atagen2026-10-22 12:30 UTC1276
                .map(Raw::cast)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1277
        };
7ff8adafe9e4atagen2026-10-22 12:30 UTC1278
7ff8adafe9e4atagen2026-10-22 12:30 UTC1279
        // We deserialize the content of the event, if is not found we return the
7ff8adafe9e4atagen2026-10-22 12:30 UTC1280
        // default
7ff8adafe9e4atagen2026-10-22 12:30 UTC1281
        let media_preview_config = media_preview_config.and_then(|value| value.deserialize().ok());
7ff8adafe9e4atagen2026-10-22 12:30 UTC1282
7ff8adafe9e4atagen2026-10-22 12:30 UTC1283
        Ok(media_preview_config)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1284
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1285
7ff8adafe9e4atagen2026-10-22 12:30 UTC1286
    /// Get the media preview configuration event content stored in the cache.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1287
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1288
    /// Will check first for the stable event and then for the unstable one.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1289
    pub async fn get_media_preview_config_event_content(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1290
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1291
    ) -> Result<Option<MediaPreviewConfigEventContent>> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1292
        let media_preview_config = self
7ff8adafe9e4atagen2026-10-22 12:30 UTC1293
            .account_data::<MediaPreviewConfigEventContent>()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1294
            .await?
7ff8adafe9e4atagen2026-10-22 12:30 UTC1295
            .and_then(|r| r.deserialize().ok());
7ff8adafe9e4atagen2026-10-22 12:30 UTC1296
7ff8adafe9e4atagen2026-10-22 12:30 UTC1297
        if let Some(media_preview_config) = media_preview_config {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1298
            Ok(Some(media_preview_config))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1299
        } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1300
            Ok(self
7ff8adafe9e4atagen2026-10-22 12:30 UTC1301
                .account_data::<UnstableMediaPreviewConfigEventContent>()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1302
                .await?
7ff8adafe9e4atagen2026-10-22 12:30 UTC1303
                .and_then(|r| r.deserialize().ok())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1304
                .map(Into::into))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1305
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1306
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1307
7ff8adafe9e4atagen2026-10-22 12:30 UTC1308
    /// Set the media previews display policy in the timeline.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1309
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1310
    /// This will always use the unstable event until we know which Matrix
7ff8adafe9e4atagen2026-10-22 12:30 UTC1311
    /// version will support it.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1312
    pub async fn set_media_previews_display_policy(&self, policy: MediaPreviews) -> Result<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1313
        let mut media_preview_config =
7ff8adafe9e4atagen2026-10-22 12:30 UTC1314
            self.fetch_media_preview_config_event_content().await?.unwrap_or_default();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1315
        media_preview_config.media_previews = Some(policy);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1316
7ff8adafe9e4atagen2026-10-22 12:30 UTC1317
        // Updating the unstable account data
7ff8adafe9e4atagen2026-10-22 12:30 UTC1318
        let unstable_media_preview_config =
7ff8adafe9e4atagen2026-10-22 12:30 UTC1319
            UnstableMediaPreviewConfigEventContent::from(media_preview_config);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1320
        self.set_account_data(unstable_media_preview_config).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1321
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1322
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1323
7ff8adafe9e4atagen2026-10-22 12:30 UTC1324
    /// Set the display policy for avatars in invite requests.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1325
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1326
    /// This will always use the unstable event until we know which matrix
7ff8adafe9e4atagen2026-10-22 12:30 UTC1327
    /// version will support it.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1328
    pub async fn set_invite_avatars_display_policy(&self, policy: InviteAvatars) -> Result<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1329
        let mut media_preview_config =
7ff8adafe9e4atagen2026-10-22 12:30 UTC1330
            self.fetch_media_preview_config_event_content().await?.unwrap_or_default();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1331
        media_preview_config.invite_avatars = Some(policy);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1332
7ff8adafe9e4atagen2026-10-22 12:30 UTC1333
        // Updating the unstable account data
7ff8adafe9e4atagen2026-10-22 12:30 UTC1334
        let unstable_media_preview_config =
7ff8adafe9e4atagen2026-10-22 12:30 UTC1335
            UnstableMediaPreviewConfigEventContent::from(media_preview_config);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1336
        self.set_account_data(unstable_media_preview_config).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1337
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1338
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1339
7ff8adafe9e4atagen2026-10-22 12:30 UTC1340
    /// Adds a recently used emoji to the list and uploads the updated
7ff8adafe9e4atagen2026-10-22 12:30 UTC1341
    /// `io.element.recent_emoji` content to the global account data.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1342
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1343
    /// Before updating the data, it'll fetch it from the homeserver, to make
7ff8adafe9e4atagen2026-10-22 12:30 UTC1344
    /// sure the updated values are always used. However, note this could still
7ff8adafe9e4atagen2026-10-22 12:30 UTC1345
    /// result in a race condition if it's used concurrently.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1346
    #[cfg(feature = "experimental-element-recent-emojis")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1347
    pub async fn add_recent_emoji(&self, emoji: &str) -> Result<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1348
        let Some(user_id) = self.client.user_id() else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1349
            return Err(Error::AuthenticationRequired);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1350
        };
7ff8adafe9e4atagen2026-10-22 12:30 UTC1351
        let mut recent_emojis = self.get_recent_emojis(true).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1352
7ff8adafe9e4atagen2026-10-22 12:30 UTC1353
        let index = recent_emojis.iter().position(|(unicode, _)| unicode == emoji);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1354
7ff8adafe9e4atagen2026-10-22 12:30 UTC1355
        // Truncate to the max allowed size, which will remove any emojis that
7ff8adafe9e4atagen2026-10-22 12:30 UTC1356
        // haven't been used in a very long time. This will also ease the pressure on
7ff8adafe9e4atagen2026-10-22 12:30 UTC1357
        // `remove` and `insert` shifting lots of elements in the list
7ff8adafe9e4atagen2026-10-22 12:30 UTC1358
        recent_emojis.truncate(MAX_RECENT_EMOJI_COUNT);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1359
7ff8adafe9e4atagen2026-10-22 12:30 UTC1360
        // Remove the emoji from the list if it was present and get it's `count` value
7ff8adafe9e4atagen2026-10-22 12:30 UTC1361
        let count = if let Some(index) = index { recent_emojis.remove(index).1 } else { uint!(0) };
7ff8adafe9e4atagen2026-10-22 12:30 UTC1362
7ff8adafe9e4atagen2026-10-22 12:30 UTC1363
        // Insert the emoji with the updated count at the start of the list, so it's
7ff8adafe9e4atagen2026-10-22 12:30 UTC1364
        // considered the most recently used emoji
7ff8adafe9e4atagen2026-10-22 12:30 UTC1365
        recent_emojis.insert(0, (emoji.to_owned(), count + uint!(1)));
7ff8adafe9e4atagen2026-10-22 12:30 UTC1366
7ff8adafe9e4atagen2026-10-22 12:30 UTC1367
        // If the item was a new one, the list will now be `MAX_RECENT_EMOJI_COUNT` + 1,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1368
        // so truncate it again (this is a no-op if it already has the right size)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1369
        recent_emojis.truncate(MAX_RECENT_EMOJI_COUNT);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1370
7ff8adafe9e4atagen2026-10-22 12:30 UTC1371
        let request = UpdateGlobalAccountDataRequest::new(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1372
            user_id.to_owned(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1373
            &RecentEmojisContent::new(recent_emojis),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1374
        )?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1375
        let _ = self.client.send(request).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1376
7ff8adafe9e4atagen2026-10-22 12:30 UTC1377
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1378
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1379
7ff8adafe9e4atagen2026-10-22 12:30 UTC1380
    /// Gets the list of recently used emojis from the `io.element.recent_emoji`
7ff8adafe9e4atagen2026-10-22 12:30 UTC1381
    /// global account data.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1382
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1383
    /// If the `refresh` param is `true`, the data will be fetched from the
7ff8adafe9e4atagen2026-10-22 12:30 UTC1384
    /// homeserver instead of the local storage.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1385
    #[cfg(feature = "experimental-element-recent-emojis")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1386
    pub async fn get_recent_emojis(&self, refresh: bool) -> Result<Vec<(String, UInt)>> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1387
        let content = if refresh {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1388
            let Some(user_id) = self.client.user_id() else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1389
                return Err(Error::AuthenticationRequired);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1390
            };
7ff8adafe9e4atagen2026-10-22 12:30 UTC1391
            let event_type = RecentEmojisContent::default().event_type();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1392
            let response = self
7ff8adafe9e4atagen2026-10-22 12:30 UTC1393
                .client
7ff8adafe9e4atagen2026-10-22 12:30 UTC1394
                .send(get_global_account_data::v3::Request::new(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1395
                    user_id.to_owned(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1396
                    event_type.clone(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1397
                ))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1398
                .await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1399
            let content = response.account_data.cast_unchecked().deserialize()?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1400
            Some(content)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1401
        } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1402
            self.client
7ff8adafe9e4atagen2026-10-22 12:30 UTC1403
                .state_store()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1404
                .get_account_data_event_static::<RecentEmojisContent>()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1405
                .await?
7ff8adafe9e4atagen2026-10-22 12:30 UTC1406
                .map(|raw| raw.deserialize().map(|event| event.content))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1407
                .transpose()?
7ff8adafe9e4atagen2026-10-22 12:30 UTC1408
        };
7ff8adafe9e4atagen2026-10-22 12:30 UTC1409
7ff8adafe9e4atagen2026-10-22 12:30 UTC1410
        if let Some(content) = content {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1411
            // Sort by count, descending. For items with the same count, since they were
7ff8adafe9e4atagen2026-10-22 12:30 UTC1412
            // previously ordered by recency in the list, more recent emojis will be
7ff8adafe9e4atagen2026-10-22 12:30 UTC1413
            // returned first.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1414
            let sorted_emojis = content
7ff8adafe9e4atagen2026-10-22 12:30 UTC1415
                .recent_emoji
7ff8adafe9e4atagen2026-10-22 12:30 UTC1416
                .into_iter()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1417
                // Items with higher counts should be first
7ff8adafe9e4atagen2026-10-22 12:30 UTC1418
                .sorted_by(|(_, count_a), (_, count_b)| count_b.cmp(count_a))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1419
                // Make sure we take only up to MAX_RECENT_EMOJI_COUNT
7ff8adafe9e4atagen2026-10-22 12:30 UTC1420
                .take(MAX_RECENT_EMOJI_COUNT)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1421
                .collect();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1422
            Ok(sorted_emojis)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1423
        } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1424
            Ok(Vec::new())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1425
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1426
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1427
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC1428
7ff8adafe9e4atagen2026-10-22 12:30 UTC1429
fn get_raw_content<Ev, C>(raw: Option<Raw<Ev>>) -> Result<Option<Raw<C>>> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1430
    #[derive(Deserialize)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1431
    #[serde(bound = "C: Sized")] // Replace default Deserialize bound
7ff8adafe9e4atagen2026-10-22 12:30 UTC1432
    struct GetRawContent<C> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1433
        content: Raw<C>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1434
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1435
7ff8adafe9e4atagen2026-10-22 12:30 UTC1436
    Ok(raw
7ff8adafe9e4atagen2026-10-22 12:30 UTC1437
        .map(|event| event.deserialize_as_unchecked::<GetRawContent<C>>())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1438
        .transpose()?
7ff8adafe9e4atagen2026-10-22 12:30 UTC1439
        .map(|get_raw| get_raw.content))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1440
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC1441
7ff8adafe9e4atagen2026-10-22 12:30 UTC1442
#[cfg(test)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1443
mod tests {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1444
    use assert_matches::assert_matches;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1445
    use matrix_sdk_test::async_test;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1446
7ff8adafe9e4atagen2026-10-22 12:30 UTC1447
    use crate::{Error, test_utils::client::MockClientBuilder};
7ff8adafe9e4atagen2026-10-22 12:30 UTC1448
7ff8adafe9e4atagen2026-10-22 12:30 UTC1449
    #[async_test]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1450
    async fn test_dont_ignore_oneself() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1451
        let client = MockClientBuilder::new(None).build().await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1452
7ff8adafe9e4atagen2026-10-22 12:30 UTC1453
        // It's forbidden to ignore the logged-in user.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1454
        assert_matches!(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1455
            client.account().ignore_user(client.user_id().unwrap()).await,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1456
            Err(Error::CantIgnoreLoggedInUser)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1457
        );
7ff8adafe9e4atagen2026-10-22 12:30 UTC1458
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1459
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC1460
7ff8adafe9e4atagen2026-10-22 12:30 UTC1461
#[cfg(test)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1462
#[cfg(feature = "experimental-element-recent-emojis")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1463
mod test_recent_emojis {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1464
    use js_int::{UInt, uint};
7ff8adafe9e4atagen2026-10-22 12:30 UTC1465
    use matrix_sdk_base::recent_emojis::RecentEmojisContent;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1466
    use matrix_sdk_test::{async_test, event_factory::EventFactory};
7ff8adafe9e4atagen2026-10-22 12:30 UTC1467
7ff8adafe9e4atagen2026-10-22 12:30 UTC1468
    use crate::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC1469
        account::MAX_RECENT_EMOJI_COUNT, config::SyncSettings, test_utils::mocks::MatrixMockServer,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1470
    };
7ff8adafe9e4atagen2026-10-22 12:30 UTC1471
7ff8adafe9e4atagen2026-10-22 12:30 UTC1472
    #[async_test]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1473
    async fn test_recent_emojis() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1474
        let server = MatrixMockServer::new().await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1475
        let client = server.client_builder().build().await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1476
        let user_id = client.user_id().expect("session_id");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1477
7ff8adafe9e4atagen2026-10-22 12:30 UTC1478
        server
7ff8adafe9e4atagen2026-10-22 12:30 UTC1479
            .mock_add_recent_emojis()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1480
            .ok(user_id)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1481
            .named("Update recent emojis global account data")
7ff8adafe9e4atagen2026-10-22 12:30 UTC1482
            .mock_once()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1483
            .mount()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1484
            .await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1485
7ff8adafe9e4atagen2026-10-22 12:30 UTC1486
        let recent_emojis = client.account().get_recent_emojis(false).await.expect("recent emojis");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1487
        assert!(recent_emojis.is_empty());
7ff8adafe9e4atagen2026-10-22 12:30 UTC1488
7ff8adafe9e4atagen2026-10-22 12:30 UTC1489
        let emoji_list = vec![
7ff8adafe9e4atagen2026-10-22 12:30 UTC1490
            (":/".to_owned(), uint!(1)),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1491
            (":)".to_owned(), uint!(12)),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1492
            (":D".to_owned(), uint!(12)),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1493
        ];
7ff8adafe9e4atagen2026-10-22 12:30 UTC1494
7ff8adafe9e4atagen2026-10-22 12:30 UTC1495
        server
7ff8adafe9e4atagen2026-10-22 12:30 UTC1496
            .mock_get_recent_emojis()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1497
            .ok(user_id, emoji_list.clone())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1498
            .named("Fetch recent emojis")
7ff8adafe9e4atagen2026-10-22 12:30 UTC1499
            .mock_once()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1500
            .mount()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1501
            .await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1502
7ff8adafe9e4atagen2026-10-22 12:30 UTC1503
        client.account().add_recent_emoji(":)").await.expect("adding emoji");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1504
7ff8adafe9e4atagen2026-10-22 12:30 UTC1505
        server
7ff8adafe9e4atagen2026-10-22 12:30 UTC1506
            .mock_sync()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1507
            .ok(|builder| {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1508
                let content = RecentEmojisContent::new(emoji_list);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1509
                let event_builder = EventFactory::new().global_account_data(content);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1510
                builder.add_global_account_data(event_builder);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1511
            })
7ff8adafe9e4atagen2026-10-22 12:30 UTC1512
            .named("Sync")
7ff8adafe9e4atagen2026-10-22 12:30 UTC1513
            .mount()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1514
            .await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1515
7ff8adafe9e4atagen2026-10-22 12:30 UTC1516
        client.sync_once(SyncSettings::default()).await.expect("sync failed");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1517
7ff8adafe9e4atagen2026-10-22 12:30 UTC1518
        let recent_emojis = client.account().get_recent_emojis(false).await.expect("recent emojis");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1519
7ff8adafe9e4atagen2026-10-22 12:30 UTC1520
        // Assert size
7ff8adafe9e4atagen2026-10-22 12:30 UTC1521
        assert_eq!(recent_emojis.len(), 3);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1522
7ff8adafe9e4atagen2026-10-22 12:30 UTC1523
        // Assert ordering: first by times used, then by recency
7ff8adafe9e4atagen2026-10-22 12:30 UTC1524
        assert_eq!(recent_emojis[0].0, ":)");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1525
        assert_eq!(recent_emojis[1].0, ":D");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1526
        assert_eq!(recent_emojis[2].0, ":/");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1527
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1528
7ff8adafe9e4atagen2026-10-22 12:30 UTC1529
    #[async_test]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1530
    async fn test_max_recent_emoji_count() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1531
        let server = MatrixMockServer::new().await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1532
        let client = server.client_builder().build().await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1533
        let user_id = client.user_id().expect("session_id");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1534
7ff8adafe9e4atagen2026-10-22 12:30 UTC1535
        // This list is > the MAX_RECENT_EMOJI_COUNT
7ff8adafe9e4atagen2026-10-22 12:30 UTC1536
        let long_emoji_list = (0..MAX_RECENT_EMOJI_COUNT * 2)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1537
            .map(|i| (i.to_string(), uint!(1)))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1538
            .collect::<Vec<(String, UInt)>>();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1539
7ff8adafe9e4atagen2026-10-22 12:30 UTC1540
        // Initially we locally don't have any emojis
7ff8adafe9e4atagen2026-10-22 12:30 UTC1541
        let recent_emojis = client.account().get_recent_emojis(false).await.expect("recent emojis");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1542
        assert!(recent_emojis.is_empty());
7ff8adafe9e4atagen2026-10-22 12:30 UTC1543
7ff8adafe9e4atagen2026-10-22 12:30 UTC1544
        server
7ff8adafe9e4atagen2026-10-22 12:30 UTC1545
            .mock_get_recent_emojis()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1546
            .ok(user_id, long_emoji_list.clone())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1547
            .named("Fetch recent emojis")
7ff8adafe9e4atagen2026-10-22 12:30 UTC1548
            .expect(3)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1549
            .mount()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1550
            .await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1551
7ff8adafe9e4atagen2026-10-22 12:30 UTC1552
        // Now with a list of emojis longer than the max count, we fetch the emoji list
7ff8adafe9e4atagen2026-10-22 12:30 UTC1553
        let recent_emojis = client.account().get_recent_emojis(true).await.expect("recent emojis");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1554
7ff8adafe9e4atagen2026-10-22 12:30 UTC1555
        // It should only return until the max count
7ff8adafe9e4atagen2026-10-22 12:30 UTC1556
        assert_eq!(recent_emojis.len(), MAX_RECENT_EMOJI_COUNT);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1557
        assert_eq!(recent_emojis, long_emoji_list[..MAX_RECENT_EMOJI_COUNT]);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1558
7ff8adafe9e4atagen2026-10-22 12:30 UTC1559
        // Simulate the logic we expect when adding a new emoji:
7ff8adafe9e4atagen2026-10-22 12:30 UTC1560
        // 1. Remove the existing emoji if present
7ff8adafe9e4atagen2026-10-22 12:30 UTC1561
        // 2. Increase its count value and insert it at the front.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1562
        // 3. Truncate at MAX_RECENT_EMOJI_COUNT
7ff8adafe9e4atagen2026-10-22 12:30 UTC1563
        let expected_updated_emoji_list = {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1564
            let mut list = long_emoji_list.clone();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1565
            let item = list.remove(50);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1566
            list.insert(0, (item.0, item.1 + uint!(1)));
7ff8adafe9e4atagen2026-10-22 12:30 UTC1567
            list.truncate(MAX_RECENT_EMOJI_COUNT);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1568
            list
7ff8adafe9e4atagen2026-10-22 12:30 UTC1569
        };
7ff8adafe9e4atagen2026-10-22 12:30 UTC1570
7ff8adafe9e4atagen2026-10-22 12:30 UTC1571
        // Now if we add a new emoji that was not in the list, the last one in the list
7ff8adafe9e4atagen2026-10-22 12:30 UTC1572
        // should be gone
7ff8adafe9e4atagen2026-10-22 12:30 UTC1573
        server
7ff8adafe9e4atagen2026-10-22 12:30 UTC1574
            .mock_add_recent_emojis()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1575
            .match_emojis_in_request_body(expected_updated_emoji_list)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1576
            .ok(user_id)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1577
            .named("Update recent emojis global account data with existing emoji")
7ff8adafe9e4atagen2026-10-22 12:30 UTC1578
            .mock_once()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1579
            .mount()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1580
            .await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1581
7ff8adafe9e4atagen2026-10-22 12:30 UTC1582
        client.account().add_recent_emoji("50").await.expect("adding emoji");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1583
7ff8adafe9e4atagen2026-10-22 12:30 UTC1584
        // Do the same, but now with a new emoji that wasn't previously in the list
7ff8adafe9e4atagen2026-10-22 12:30 UTC1585
        let expected_updated_emoji_list = {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1586
            let mut list = long_emoji_list.clone();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1587
            let item = (":D".to_owned(), uint!(1));
7ff8adafe9e4atagen2026-10-22 12:30 UTC1588
            list.insert(0, item);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1589
            list.truncate(MAX_RECENT_EMOJI_COUNT);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1590
            list
7ff8adafe9e4atagen2026-10-22 12:30 UTC1591
        };
7ff8adafe9e4atagen2026-10-22 12:30 UTC1592
7ff8adafe9e4atagen2026-10-22 12:30 UTC1593
        // We should still have `MAX_RECENT_EMOJI_COUNT` items
7ff8adafe9e4atagen2026-10-22 12:30 UTC1594
        server
7ff8adafe9e4atagen2026-10-22 12:30 UTC1595
            .mock_add_recent_emojis()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1596
            .match_emojis_in_request_body(expected_updated_emoji_list)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1597
            .ok(user_id)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1598
            .named("Update recent emojis global account data with new emoji")
7ff8adafe9e4atagen2026-10-22 12:30 UTC1599
            .mock_once()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1600
            .mount()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1601
            .await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1602
7ff8adafe9e4atagen2026-10-22 12:30 UTC1603
        client.account().add_recent_emoji(":D").await.expect("adding emoji");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1604
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1605
}