pub struct Client { /* private fields */ }
Expand description

An async/await enabled Matrix client.

All of the state is held in an Arc so the Client can be cloned freely.

Implementations

Create a new Client that will use the given homeserver.

Arguments
  • homeserver_url - The homeserver that the client should connect to.

Create a new ClientBuilder.

Change the homeserver URL used by this client.

Arguments
  • homeserver_url - The new URL to use.

Get the capabilities of the homeserver.

This method should be used to check what features are supported by the homeserver.

Example
let client = Client::new(homeserver).await?;

let capabilities = client.get_capabilities().await?;

if capabilities.change_password.enabled {
    // Change password
}

Is the client logged in.

The Homeserver of the client.

Get the user id of the current owner of the client.

Get the device id that identifies the current session.

Get the whole session info of this client.

Will be None if the client has not been logged in.

Can be used with Client::restore_login to restore a previously logged in session.

Get a reference to the store.

Get the account of the current owner of the client.

Available on crate feature e2e-encryption only.

Get the encryption manager of the client.

Register a handler for a specific event type.

The handler is a function or closure with one or more arguments. The first argument is the event itself. All additional arguments are “context” arguments: They have to implement EventHandlerContext. This trait is named that way because most of the types implementing it give additional context about an event: The room it was in, its raw form and other similar things. As an exception to this, Client also implements the EventHandlerContext trait so you don’t have to clone your client into the event handler manually.

Some context arguments are not universally applicable. A context argument that isn’t available for the given event type will result in the event handler being skipped and an error being logged. The following context argument types are only available for a subset of event types:

  • Room is only available for room-specific events, i.e. not for events like global account data events or presence events
Examples
use matrix_sdk::{
    deserialized_responses::EncryptionInfo,
    room::Room,
    ruma::{
        events::{
            macros::EventContent,
            push_rules::PushRulesEvent,
            room::{message::SyncRoomMessageEvent, topic::SyncRoomTopicEvent},
        },
        Int, MilliSecondsSinceUnixEpoch,
    },
    Client,
};
use serde::{Deserialize, Serialize};

client
    .register_event_handler(
        |ev: SyncRoomMessageEvent, room: Room, client: Client| async move {
            // Common usage: Room event plus room and client.
        },
    )
    .await
    .register_event_handler(
        |ev: SyncRoomMessageEvent, room: Room, encryption_info: Option<EncryptionInfo>| {
            async move {
                // An `Option<EncryptionInfo>` parameter lets you distinguish between
                // unencrypted events and events that were decrypted by the SDK.
            }
        },
    )
    .await
    .register_event_handler(|ev: SyncRoomTopicEvent| async move {
        // You can omit any or all arguments after the first.
    })
    .await;

// Custom events work exactly the same way, you just need to declare
// the content struct and use the EventContent derive macro on it.
#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
#[ruma_event(type = "org.shiny_new_2fa.token", kind = MessageLike)]
struct TokenEventContent {
    token: String,
    #[serde(rename = "exp")]
    expires_at: MilliSecondsSinceUnixEpoch,
}

client.register_event_handler(|ev: SyncTokenEvent, room: Room| async move {
    todo!("Display the token");
}).await;

// Adding your custom data to the handler can be done as well
let data = "MyCustomIdentifier".to_owned();

client.register_event_handler({
    let data = data.clone();
    move |ev: SyncRoomMessageEvent | {
        let data = data.clone();
        async move {
            println!("Calling the handler with identifier {}", data);
        }
    }
}).await;

Add an arbitrary value for use as event handler context.

The value can be obtained in an event handler by adding an argument of the type Ctx<T>.

If a value of the same type has been added before, it will be overwritten.

Example
use matrix_sdk::{
    event_handler::Ctx,
    room::Room,
    ruma::events::room::message::SyncRoomMessageEvent,
};

// Handle used to send messages to the UI part of the app
let my_gui_handle: SomeType = obtain_gui_handle();

client
    .register_event_handler_context(my_gui_handle.clone())
    .register_event_handler(
        |ev: SyncRoomMessageEvent, room: Room, gui_handle: Ctx<SomeType>| async move {
            // gui_handle.send(DisplayMessage { message: ev });
        },
    )
    .await;

Register a handler for a notification.

Similar to Client::register_event_handler, but only allows functions or closures with exactly the three arguments [Notification], room::Room, Client for now.

Get all the rooms the client knows about.

This will return the list of joined, invited, and left rooms.

Returns the joined rooms this client knows about.

Returns the invited rooms this client knows about.

Returns the left rooms this client knows about.

Get a room with the given room id.

Arguments

room_id - The unique id of the room that should be fetched.

Get a joined room with the given room id.

Arguments

room_id - The unique id of the room that should be fetched.

Get an invited room with the given room id.

Arguments

room_id - The unique id of the room that should be fetched.

Get a left room with the given room id.

Arguments

room_id - The unique id of the room that should be fetched.

Gets the homeserver’s supported login types.

This should be the first step when trying to login so you can call the appropriate method for the next step.

Get the URL to use to login via Single Sign-On.

Returns a URL that should be opened in a web browser to let the user login.

After a successful login, the loginToken received at the redirect URL should be used to login with login_with_token.

Arguments
  • redirect_url - The URL that will receive a loginToken after a successful SSO login.

  • idp_id - The optional ID of the identity provider to login with.

Login to the server.

This can be used for the first login as well as for subsequent logins, note that if the device id isn’t provided a new device will be created.

If this isn’t the first login a device id should be provided to restore the correct stores.

Alternatively the restore_login method can be used to restore a logged in client without the password.

Arguments
  • user - The user that should be logged in to the homeserver.

  • password - The password of the user.

  • device_id - A unique id that will be associated with this session. If not given the homeserver will create one. Can be an existing device_id from a previous login call. Note that this should be done only if the client also holds the encryption keys for this device.

Example
use matrix_sdk::Client;

let client = Client::new(homeserver).await?;
let user = "example";

let response = client
    .login(user, "wordpass", None, Some("My bot")).await?;

println!(
    "Logged in as {}, got device_id {} and access_token {}",
    user, response.device_id, response.access_token
);
Available on crate feature sso-login and non-WebAssembly only.

Login to the server via Single Sign-On.

This takes care of the whole SSO flow:

  • Spawn a local http server
  • Provide a callback to open the SSO login URL in a web browser
  • Wait for the local http server to get the loginToken
  • Call login_with_token

If cancellation is needed the method should be wrapped in a cancellable task. Note that users with root access to the system have the ability to snoop in on the data/token that is passed to the local HTTP server that will be spawned.

If you need more control over the SSO login process, you should use get_sso_login_url and login_with_token directly.

This should only be used for the first login.

The restore_login method should be used to restore a logged in client after the first login.

A device id should be provided to restore the correct stores, if the device id isn’t provided a new device will be created.

Arguments
  • use_sso_login_url - A callback that will receive the SSO Login URL. It should usually be used to open the SSO URL in a browser and must return Ok(()) if the URL was successfully opened. If it returns Err, the error will be forwarded.

  • server_url - The local URL the server is going to try to bind to, e.g. http://localhost:3030. If None, the server will try to open a random port on 127.0.0.1.

  • server_response - The text that will be shown on the webpage at the end of the login process. This can be an HTML page. If None, a default text will be displayed.

  • device_id - A unique id that will be associated with this session. If not given the homeserver will create one. Can be an existing device_id from a previous login call. Note that this should be provided only if the client also holds the encryption keys for this device.

  • initial_device_display_name - A public display name that will be associated with the device_id. Only necessary the first time you login with this device_id. It can be changed later.

  • idp_id - The optional ID of the identity provider to login with.

Example
let client = Client::new(homeserver).await.unwrap();

let response = client
    .login_with_sso(
        |sso_url| async move {
            // Open sso_url
            Ok(())
        },
        None,
        None,
        None,
        Some("My app"),
        None,
    )
    .await
    .unwrap();

println!("Logged in as {}, got device_id {} and access_token {}",
         response.user_id, response.device_id, response.access_token);

Login to the server with a token.

This token is usually received in the SSO flow after following the URL provided by get_sso_login_url, note that this is not the access token of a session.

This should only be used for the first login.

The restore_login method should be used to restore a logged in client after the first login.

A device id should be provided to restore the correct stores, if the device id isn’t provided a new device will be created.

Arguments
  • token - A login token.

  • device_id - A unique id that will be associated with this session. If not given the homeserver will create one. Can be an existing device_id from a previous login call. Note that this should be provided only if the client also holds the encryption keys for this device.

  • initial_device_display_name - A public display name that will be associated with the device_id. Only necessary the first time you login with this device_id. It can be changed later.

Example
let client = Client::new(homeserver).await.unwrap();
let sso_url = client.get_sso_login_url(redirect_url, None);

// Let the user authenticate at the SSO URL
// Receive the loginToken param at redirect_url

let response = client
    .login_with_token(login_token, None, Some("My app")).await
    .unwrap();

println!("Logged in as {}, got device_id {} and access_token {}",
         response.user_id, response.device_id, response.access_token);

Restore a previously logged in session.

This can be used to restore the client to a logged in state, loading all the stored state and encryption keys.

Alternatively, if the whole session isn’t stored the login method can be used with a device id.

Arguments
  • session - A session that the user already has from a previous login call.
Examples
use matrix_sdk::{Client, Session, ruma::{device_id, user_id}};

let homeserver = Url::parse("http://example.com")?;
let client = Client::new(homeserver).await?;

let session = Session {
    access_token: "My-Token".to_owned(),
    user_id: user_id!("@example:localhost").to_owned(),
    device_id: device_id!("MYDEVICEID").to_owned(),
};

client.restore_login(session).await?;

The Session object can also be created from the response the Client::login() method returns:

use matrix_sdk::{Client, Session};

let homeserver = Url::parse("http://example.com")?;
let client = Client::new(homeserver).await?;

let session: Session = client
    .login("example", "my-password", None, None)
    .await?
    .into();

// Persist the `Session` so it can later be used to restore the login.
client.restore_login(session).await?;

Register a user to the server.

Arguments
  • registration - The easiest way to create this request is using the [register::v3::Request] itself.
Examples

let request = assign!(RegistrationRequest::new(), {
    username: Some("user"),
    password: Some("password"),
    auth: Some(uiaa::AuthData::FallbackAcknowledgement(
        uiaa::FallbackAcknowledgement::new("foobar"),
    )),
});
let client = Client::new(homeserver).await.unwrap();
client.register(request).await;

Get or upload a sync filter.

This method will either get a filter ID from the store or upload the filter definition to the homeserver and return the new filter ID.

Arguments
  • filter_name - The unique name of the filter, this name will be used locally to store and identify the filter ID returned by the server.

  • definition - The filter definition that should be uploaded to the server if no filter ID can be found in the store.

Examples
let mut filter = FilterDefinition::default();

// Let's enable member lazy loading.
filter.room.state.lazy_load_options =
    LazyLoadOptions::Enabled { include_redundant_members: false };

let filter_id = client
    .get_or_upload_filter("sync", filter)
    .await
    .unwrap();

let sync_settings = SyncSettings::new()
    .filter(Filter::FilterId(&filter_id));

let response = client.sync_once(sync_settings).await.unwrap();

Join a room by RoomId.

Returns a join_room_by_id::Response consisting of the joined rooms RoomId.

Arguments
  • room_id - The RoomId of the room to be joined.

Join a room by RoomId.

Returns a join_room_by_id_or_alias::Response consisting of the joined rooms RoomId.

Arguments
  • alias - The RoomId or RoomAliasId of the room to be joined. An alias looks like #name:example.com.

Search the homeserver’s directory of public rooms.

Sends a request to “_matrix/client/r0/publicRooms”, returns a get_public_rooms::Response.

Arguments
  • limit - The number of PublicRoomsChunks in each response.

  • since - Pagination token from a previous request.

  • server - The name of the server, if None the requested server is used.

Examples
use matrix_sdk::Client;

let mut client = Client::new(homeserver).await.unwrap();

client.public_rooms(limit, since, server).await;

Create a room using the RoomBuilder and send the request.

Sends a request to /_matrix/client/r0/createRoom, returns a create_room::Response, this is an empty response.

Arguments
  • room - The easiest way to create this request is using the create_room::Request itself.
Examples
use matrix_sdk::Client;

let request = CreateRoomRequest::new();
let client = Client::new(homeserver).await.unwrap();
assert!(client.create_room(request).await.is_ok());

Search the homeserver’s directory for public rooms with a filter.

Arguments
  • room_search - The easiest way to create this request is using the get_public_rooms_filtered::Request itself.
Examples
use matrix_sdk::{
    ruma::{
        api::client::directory::get_public_rooms_filtered::v3::Request,
        directory::Filter,
        assign,
    }
};

let generic_search_term = Some("rust");
let filter = assign!(Filter::new(), { generic_search_term });
let request = assign!(Request::new(), { filter });

let response = client.public_rooms_filtered(request).await?;

for room in response.chunk {
    println!("Found room {:?}", room);
}

Upload some media to the server.

Arguments
  • content_type - The type of the media, this will be used as the content-type header.

  • reader - A Reader that will be used to fetch the raw bytes of the media.

Examples
let path = PathBuf::from("/home/example/my-cat.jpg");
let mut image = File::open(path)?;

let response = client
    .upload(&mime::IMAGE_JPEG, &mut image)
    .await?;

println!("Cat URI: {}", response.content_uri);

Send an arbitrary request to the server, without updating client state.

Warning: Because this method does not update the client state, it is important to make sure that you account for this yourself, and use wrapper methods where available. This method should only be used if a wrapper method for the endpoint you’d like to use is not available.

Arguments
  • request - A filled out and valid request for the endpoint to be hit

  • timeout - An optional request timeout setting, this overrides the default request setting if one was set.

Example
use matrix_sdk::ruma::{api::client::profile, user_id};

// First construct the request you want to make
// See https://docs.rs/ruma-client-api/latest/ruma_client_api/index.html
// for all available Endpoints
let user_id = user_id!("@example:localhost");
let request = profile::get_profile::v3::Request::new(&user_id);

// Start the request using Client::send()
let response = client.send(request, None).await?;

// Check the corresponding Response struct to find out what types are
// returned

Get information of all our own devices.

Examples
let response = client.devices().await?;

for device in response.devices {
    println!(
        "Device: {} {}",
        device.device_id,
        device.display_name.as_deref().unwrap_or("")
    );
}

Delete the given devices from the server.

Arguments
  • devices - The list of devices that should be deleted from the server.

  • auth_data - This request requires user interactive auth, the first request needs to set this to None and will always fail with an UiaaResponse. The response will contain information for the interactive auth and the same request needs to be made but this time with some auth_data provided.

let devices = &[device_id!("DEVICEID").to_owned()];

if let Err(e) = client.delete_devices(devices, None).await {
    if let Some(info) = e.uiaa_response() {
        let auth_data = uiaa::AuthData::Password(assign!(
            uiaa::Password::new(
                uiaa::UserIdentifier::UserIdOrLocalpart("example"),
                "wordpass",
            ), {
                session: info.session.as_deref(),
            }
        ));

        client
            .delete_devices(devices, Some(auth_data))
            .await?;
    }
}

Synchronize the client’s state with the latest state on the server.

Syncing Events

Messages or any other type of event need to be periodically fetched from the server, this is achieved by sending a /sync request to the server.

The first sync is sent out without a token. The response of the first sync will contain a next_batch field which should then be used in the subsequent sync calls as the token. This ensures that we don’t receive the same events multiple times.

Long Polling

A sync should in the usual case always be in flight. The SyncSettings have a timeout option, which controls how long the server will wait for new events before it will respond. The server will respond immediately if some new events arrive before the timeout has expired. If no changes arrive and the timeout expires an empty sync response will be sent to the client.

This method of sending a request that may not receive a response immediately is called long polling.

Filtering Events

The number or type of messages and events that the client should receive from the server can be altered using a Filter.

Filters can be non-trivial and, since they will be sent with every sync request, they may take up a bunch of unnecessary bandwidth.

Luckily filters can be uploaded to the server and reused using an unique identifier, this can be achieved using the get_or_upload_filter() method.

Arguments
  • sync_settings - Settings for the sync call, this allows us to set various options to configure the sync:
    • filter - To configure which events we receive and which get filtered by the server
    • timeout - To configure our long polling setup.
    • token - To tell the server which events we already received and where we wish to continue syncing.
    • full_state - To tell the server that we wish to receive all state events, regardless of our configured token.
Examples
use matrix_sdk::{
    Client, config::SyncSettings,
    ruma::events::room::message::OriginalSyncRoomMessageEvent,
};

let client = Client::new(homeserver).await?;
client.login(&username, &password, None, None).await?;

// Sync once so we receive the client state and old messages.
client.sync_once(SyncSettings::default()).await?;

// Register our handler so we start responding once we receive a new
// event.
client.register_event_handler(|ev: OriginalSyncRoomMessageEvent| async move {
    println!("Received event {}: {:?}", ev.sender, ev.content);
}).await;

// Now keep on syncing forever. `sync()` will use the stored sync token
// from our `sync_once()` call automatically.
client.sync(SyncSettings::default()).await;

Repeatedly synchronize the client state with the server.

This method will never return, if cancellation is needed the method should be wrapped in a cancelable task or the Client::sync_with_callback method can be used.

This method will internally call Client::sync_once in a loop.

This method can be used with the Client::register_event_handler method to react to individual events. If you instead wish to handle events in a bulk manner the Client::sync_with_callback and Client::sync_stream methods can be used instead. Those two methods repeatedly return the whole sync response.

Arguments
  • sync_settings - Settings for the sync call. Note that those settings will be only used for the first sync call. See the argument docs for Client::sync_once for more info.
Examples
use matrix_sdk::{
    Client, config::SyncSettings,
    ruma::events::room::message::OriginalSyncRoomMessageEvent,
};

let client = Client::new(homeserver).await?;
client.login(&username, &password, None, None).await?;

// Register our handler so we start responding once we receive a new
// event.
client.register_event_handler(|ev: OriginalSyncRoomMessageEvent| async move {
    println!("Received event {}: {:?}", ev.sender, ev.content);
}).await;

// Now keep on syncing forever. `sync()` will use the latest sync token
// automatically.
client.sync(SyncSettings::default()).await;

Repeatedly call sync to synchronize the client state with the server.

Arguments
  • sync_settings - Settings for the sync call. Note that those settings will be only used for the first sync call. See the argument docs for Client::sync_once for more info.

  • callback - A callback that will be called every time a successful response has been fetched from the server. The callback must return a boolean which signalizes if the method should stop syncing. If the callback returns LoopCtrl::Continue the sync will continue, if the callback returns LoopCtrl::Break the sync will be stopped.

Examples

The following example demonstrates how to sync forever while sending all the interesting events through a mpsc channel to another thread e.g. a UI thread.


use tokio::sync::mpsc::channel;

let (tx, rx) = channel(100);

let sync_channel = &tx;
let sync_settings = SyncSettings::new()
    .timeout(Duration::from_secs(30));

client
    .sync_with_callback(sync_settings, |response| async move {
        let channel = sync_channel;

        for (room_id, room) in response.rooms.join {
            for event in room.timeline.events {
                channel.send(event).await.unwrap();
            }
        }

        LoopCtrl::Continue
    })
    .await;
})

This method will internally call Client::sync_once in a loop and is equivalent to the Client::sync method but the responses are provided as an async stream.

Arguments
  • sync_settings - Settings for the sync call. Note that those settings will be only used for the first sync call. See the argument docs for Client::sync_once for more info.
Examples
use futures::StreamExt;
use matrix_sdk::{Client, config::SyncSettings};

let client = Client::new(homeserver).await?;
client.login(&username, &password, None, None).await?;

let mut sync_stream = Box::pin(client.sync_stream(SyncSettings::default()).await);

while let Some(Ok(response)) = sync_stream.next().await {
    for room in response.rooms.join.values() {
        for e in &room.timeline.events {
            if let Ok(event) = e.event.deserialize() {
                println!("Received event {:?}", event);
            }
        }
    }
}

Get the current, if any, sync token of the client. This will be None if the client didn’t sync at least once.

Get a media file’s content.

If the content is encrypted and encryption is enabled, the content will be decrypted.

Arguments
  • request - The MediaRequest of the content.

  • use_cache - If we should use the media cache for this request.

Remove a media file’s content from the store.

Arguments
  • request - The MediaRequest of the content.

Delete all the media content corresponding to the given uri from the store.

Arguments
  • uri - The MxcUri of the files.

Get the file of the given media event content.

If the content is encrypted and encryption is enabled, the content will be decrypted.

Returns Ok(None) if the event content has no file.

This is a convenience method that calls the get_media_content method.

Arguments
  • event_content - The media event content.

  • use_cache - If we should use the media cache for this file.

Remove the file of the given media event content from the cache.

This is a convenience method that calls the remove_media_content method.

Arguments
  • event_content - The media event content.

Get a thumbnail of the given media event content.

If the content is encrypted and encryption is enabled, the content will be decrypted.

Returns Ok(None) if the event content has no thumbnail.

This is a convenience method that calls the get_media_content method.

Arguments
  • event_content - The media event content.

  • size - The desired size of the thumbnail. The actual thumbnail may not match the size specified.

  • use_cache - If we should use the media cache for this thumbnail.

Remove the thumbnail of the given media event content from the cache.

This is a convenience method that calls the remove_media_content method.

Arguments
  • event_content - The media event content.

  • size - The desired size of the thumbnail. Must match the size requested with get_thumbnail.

Gets information about the owner of a given access token.

Trait Implementations

Returns a copy of the value. Read more

Performs copy-assignment from source. Read more

Formats the value using the given formatter. Read more

Auto Trait Implementations

Blanket Implementations

Gets the TypeId of self. Read more

Immutably borrows from an owned value. Read more

Mutably borrows from an owned value. Read more

Returns the argument unchanged.

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more

Instruments this type with the current Span, returning an Instrumented wrapper. Read more

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

The alignment of pointer.

The type for initializers.

Initializes a with the given initializer. Read more

Dereferences the given pointer. Read more

Mutably dereferences the given pointer. Read more

Drops the object pointed to by the given pointer. Read more

Should always be Self

The resulting type after obtaining ownership.

Creates owned data from borrowed data, usually by cloning. Read more

🔬 This is a nightly-only experimental API. (toowned_clone_into)

Uses borrowed data to replace owned data, usually by cloning. Read more

The type returned in the event of a conversion error.

Performs the conversion.

The type returned in the event of a conversion error.

Performs the conversion.

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more