Struct matrix_sdk::Client
source · [−]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
sourceimpl Client
impl Client
sourcepub fn builder() -> ClientBuilder
pub fn builder() -> ClientBuilder
Create a new ClientBuilder
.
sourcepub async fn get_capabilities(&self) -> HttpResult<Capabilities>
pub async fn get_capabilities(&self) -> HttpResult<Capabilities>
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
}
sourcepub fn request_config(&self) -> RequestConfig
pub fn request_config(&self) -> RequestConfig
Get a copy of the default request config.
The default request config is what’s used when sending requests if no
RequestConfig
is explicitly passed to send
or another
function with such a parameter.
If the default request config was not customized through
ClientBuilder
when creating this Client
, the returned value will
be equivalent to RequestConfig::default()
.
sourcepub async fn homeserver(&self) -> Url
pub async fn homeserver(&self) -> Url
The Homeserver of the client.
sourcepub async fn authentication_issuer(&self) -> Option<Url>
pub async fn authentication_issuer(&self) -> Option<Url>
The OIDC Provider that is trusted by the homeserver.
sourcepub fn device_id(&self) -> Option<&DeviceId>
pub fn device_id(&self) -> Option<&DeviceId>
Get the device ID that identifies the current session.
sourcepub fn session_tokens(&self) -> Option<SessionTokens>
pub fn session_tokens(&self) -> Option<SessionTokens>
Get the current access token and optional refresh token for this session.
Will be None
if the client has not been logged in.
After login, the tokens should only change if support for refreshing access tokens has been enabled.
sourcepub fn access_token(&self) -> Option<String>
pub fn access_token(&self) -> Option<String>
Get the current access token for this session.
Will be None
if the client has not been logged in.
After login, this token should only change if support for refreshing access tokens has been enabled.
sourcepub fn refresh_token(&self) -> Option<String>
pub fn refresh_token(&self) -> Option<String>
Get the current refresh token for this session.
Will be None
if the client has not been logged in, or if the access
token doesn’t expire.
After login, this token should only change if support for refreshing access tokens has been enabled.
sourcepub fn session_tokens_changed_signal(&self) -> impl Signal<Item = ()>
pub fn session_tokens_changed_signal(&self) -> impl Signal<Item = ()>
Signal
to get notified when the current access token and optional
refresh token for this session change.
This can be used with Client::session()
to persist the Session
when the tokens change.
After login, the tokens should only change if support for refreshing access tokens has been enabled.
Example
use futures_signals::signal::SignalExt;
use matrix_sdk::Client;
let homeserver = "http://example.com";
let client = Client::builder()
.homeserver_url(homeserver)
.handle_refresh_tokens()
.build()
.await?;
let response = client
.login_username("user", "wordpass")
.initial_device_display_name("My App")
.request_refresh_token()
.send()
.await?;
persist_session(client.session());
// Handle when at least one of the tokens changed.
let future = client.session_tokens_changed_signal().for_each(move |_| {
let client = client.clone();
async move {
persist_session(client.session());
}
});
tokio::spawn(future);
sourcepub fn session_tokens_signal(&self) -> impl Signal<Item = Option<SessionTokens>>
pub fn session_tokens_signal(&self) -> impl Signal<Item = Option<SessionTokens>>
Get the current access token and optional refresh token for this
session as a Signal
.
This can be used to watch changes of the tokens by calling methods like
for_each()
or to_stream()
.
The value will be None
if the client has not been logged in.
After login, the tokens should only change if support for refreshing access tokens has been enabled.
Example
use futures::StreamExt;
use futures_signals::signal::SignalExt;
use matrix_sdk::Client;
let homeserver = "http://example.com";
let client = Client::builder()
.homeserver_url(homeserver)
.handle_refresh_tokens()
.build()
.await?;
client
.login_username("user", "wordpass")
.initial_device_display_name("My App")
.request_refresh_token()
.send()
.await?;
let mut session = client.session().expect("Client should be logged in");
persist_session(&session);
// Handle when at least one of the tokens changed.
let mut tokens_stream = client.session_tokens_signal().to_stream();
loop {
if let Some(tokens) = tokens_stream.next().await.flatten() {
session.access_token = tokens.access_token;
if let Some(refresh_token) = tokens.refresh_token {
session.refresh_token = Some(refresh_token);
}
persist_session(&session);
}
}
sourcepub fn session(&self) -> Option<Session>
pub fn session(&self) -> Option<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.
sourcepub fn store(&self) -> &dyn StateStore
pub fn store(&self) -> &dyn StateStore
Get a reference to the state store.
sourcepub fn encryption(&self) -> Encryption
Available on crate feature e2e-encryption
only.
pub fn encryption(&self) -> Encryption
e2e-encryption
only.Get the encryption manager of the client.
sourcepub fn add_event_handler<Ev, Ctx, H>(&self, handler: H) -> EventHandlerHandlewhere
Ev: SyncEvent + DeserializeOwned + Send + 'static,
H: EventHandler<Ev, Ctx>,
<H::Future as Future>::Output: EventHandlerResult,
pub fn add_event_handler<Ev, Ctx, H>(&self, handler: H) -> EventHandlerHandlewhere
Ev: SyncEvent + DeserializeOwned + Send + 'static,
H: EventHandler<Ev, Ctx>,
<H::Future as Future>::Output: EventHandlerResult,
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 two exceptions to this,
Client
and EventHandlerHandle
also implement the
EventHandlerContext
trait so you don’t have to clone your client
into the event handler manually and a handler can decide to remove
itself.
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.add_event_handler(
|ev: SyncRoomMessageEvent, room: Room, client: Client| async move {
// Common usage: Room event plus room and client.
},
);
client.add_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.
}
},
);
client.add_event_handler(|ev: SyncRoomTopicEvent| async move {
// You can omit any or all arguments after the first.
});
// Registering a temporary event handler:
let handle = client.add_event_handler(|ev: SyncRoomMessageEvent| async move {
/* Event handler */
});
client.remove_event_handler(handle);
// 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.add_event_handler(|ev: SyncTokenEvent, room: Room| async move {
todo!("Display the token");
});
// Adding your custom data to the handler can be done as well
let data = "MyCustomIdentifier".to_owned();
client.add_event_handler({
let data = data.clone();
move |ev: SyncRoomMessageEvent | {
let data = data.clone();
async move {
println!("Calling the handler with identifier {data}");
}
}
});
sourcepub fn add_room_event_handler<Ev, Ctx, H>(
&self,
room_id: &RoomId,
handler: H
) -> EventHandlerHandlewhere
Ev: SyncEvent + DeserializeOwned + Send + 'static,
H: EventHandler<Ev, Ctx>,
<H::Future as Future>::Output: EventHandlerResult,
pub fn add_room_event_handler<Ev, Ctx, H>(
&self,
room_id: &RoomId,
handler: H
) -> EventHandlerHandlewhere
Ev: SyncEvent + DeserializeOwned + Send + 'static,
H: EventHandler<Ev, Ctx>,
<H::Future as Future>::Output: EventHandlerResult,
Register a handler for a specific room, and event type.
This method works the same way as
add_event_handler
, except that the handler
will only be called for events in the room with the specified ID. See
that method for more details on event handler functions.
client.add_room_event_handler(room_id, hdl)
is equivalent to
room.add_event_handler(hdl)
. Use whichever one is more convenient in
your use case.
pub async fn register_event_handler<Ev, Ctx, H>(&self, handler: H) -> &Selfwhere
Ev: SyncEvent + DeserializeOwned + Send + 'static,
H: EventHandler<Ev, Ctx>,
<H::Future as Future>::Output: EventHandlerResult,
Client::add_event_handler
insteadsourcepub fn remove_event_handler(&self, handle: EventHandlerHandle)
pub fn remove_event_handler(&self, handle: EventHandlerHandle)
Remove the event handler associated with the handle.
Note that you must not call remove_event_handler
from the
non-async part of an event handler, that is:
client.add_event_handler(|ev: SomeEvent, client: Client, handle: EventHandlerHandle| {
// ⚠ this will cause a deadlock ⚠
client.remove_event_handler(handle);
async move {
// removing the event handler here is fine
client.remove_event_handler(handle);
}
})
Note also that handlers that remove themselves will still execute with events received in the same sync cycle.
Arguments
handle
- The EventHandlerHandle
that is returned when
registering the event handler with Client::add_event_handler
.
Examples
use matrix_sdk::{
event_handler::EventHandlerHandle,
ruma::events::room::member::SyncRoomMemberEvent, Client,
};
client.add_event_handler(
|ev: SyncRoomMemberEvent,
client: Client,
handle: EventHandlerHandle| async move {
// Common usage: Check arriving Event is the expected one
println!("Expected RoomMemberEvent received!");
client.remove_event_handler(handle);
},
);
sourcepub fn event_handler_drop_guard(
&self,
handle: EventHandlerHandle
) -> EventHandlerDropGuard
pub fn event_handler_drop_guard(
&self,
handle: EventHandlerHandle
) -> EventHandlerDropGuard
Create an EventHandlerDropGuard
for the event handler identified by
the given handle.
When the returned value is dropped, the event handler will be removed.
sourcepub fn add_event_handler_context<T>(&self, ctx: T)where
T: Clone + Send + Sync + 'static,
pub fn add_event_handler_context<T>(&self, ctx: T)where
T: Clone + Send + Sync + 'static,
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.add_event_handler_context(my_gui_handle.clone());
client.add_event_handler(
|ev: SyncRoomMessageEvent, room: Room, gui_handle: Ctx<SomeType>| {
async move {
// gui_handle.send(DisplayMessage { message: ev });
}
},
);
pub fn register_event_handler_context<T>(&self, ctx: T) -> &Selfwhere
T: Clone + Send + Sync + 'static,
Client::add_event_handler_context
insteadsourcepub async fn register_notification_handler<H, Fut>(&self, handler: H) -> &Selfwhere
H: Fn(Notification, Room, Client) -> Fut + SendOutsideWasm + SyncOutsideWasm + 'static,
Fut: Future<Output = ()> + SendOutsideWasm + 'static,
pub async fn register_notification_handler<H, Fut>(&self, handler: H) -> &Selfwhere
H: Fn(Notification, Room, Client) -> Fut + SendOutsideWasm + SyncOutsideWasm + 'static,
Fut: Future<Output = ()> + SendOutsideWasm + 'static,
Register a handler for a notification.
Similar to Client::add_event_handler
, but only allows functions
or closures with exactly the three arguments [Notification
],
room::Room
, Client
for now.
sourcepub fn rooms(&self) -> Vec<Room>ⓘNotable traits for Vec<u8, A>impl<A> Write for Vec<u8, A>where
A: Allocator,
pub fn rooms(&self) -> Vec<Room>ⓘNotable traits for Vec<u8, A>impl<A> Write for Vec<u8, A>where
A: Allocator,
A: Allocator,
Get all the rooms the client knows about.
This will return the list of joined, invited, and left rooms.
sourcepub fn joined_rooms(&self) -> Vec<Joined>ⓘNotable traits for Vec<u8, A>impl<A> Write for Vec<u8, A>where
A: Allocator,
pub fn joined_rooms(&self) -> Vec<Joined>ⓘNotable traits for Vec<u8, A>impl<A> Write for Vec<u8, A>where
A: Allocator,
A: Allocator,
Returns the joined rooms this client knows about.
sourcepub fn invited_rooms(&self) -> Vec<Invited>ⓘNotable traits for Vec<u8, A>impl<A> Write for Vec<u8, A>where
A: Allocator,
pub fn invited_rooms(&self) -> Vec<Invited>ⓘNotable traits for Vec<u8, A>impl<A> Write for Vec<u8, A>where
A: Allocator,
A: Allocator,
Returns the invited rooms this client knows about.
sourcepub fn left_rooms(&self) -> Vec<Left>ⓘNotable traits for Vec<u8, A>impl<A> Write for Vec<u8, A>where
A: Allocator,
pub fn left_rooms(&self) -> Vec<Left>ⓘNotable traits for Vec<u8, A>impl<A> Write for Vec<u8, A>where
A: Allocator,
A: Allocator,
Returns the left rooms this client knows about.
sourcepub fn get_room(&self, room_id: &RoomId) -> Option<Room>
pub fn get_room(&self, room_id: &RoomId) -> Option<Room>
Get a room with the given room id.
Arguments
room_id
- The unique id of the room that should be fetched.
sourcepub fn get_joined_room(&self, room_id: &RoomId) -> Option<Joined>
pub fn get_joined_room(&self, room_id: &RoomId) -> Option<Joined>
Get a joined room with the given room id.
Arguments
room_id
- The unique id of the room that should be fetched.
sourcepub fn get_invited_room(&self, room_id: &RoomId) -> Option<Invited>
pub fn get_invited_room(&self, room_id: &RoomId) -> Option<Invited>
Get an invited room with the given room id.
Arguments
room_id
- The unique id of the room that should be fetched.
sourcepub fn get_left_room(&self, room_id: &RoomId) -> Option<Left>
pub fn get_left_room(&self, room_id: &RoomId) -> Option<Left>
Get a left room with the given room id.
Arguments
room_id
- The unique id of the room that should be fetched.
sourcepub async fn resolve_room_alias(
&self,
room_alias: &RoomAliasId
) -> HttpResult<Response>
pub async fn resolve_room_alias(
&self,
room_alias: &RoomAliasId
) -> HttpResult<Response>
Resolve a room alias to a room id and a list of servers which know about it.
Arguments
room_alias
- The room alias to be resolved.
sourcepub async fn get_login_types(&self) -> HttpResult<Response>
pub async fn get_login_types(&self) -> HttpResult<Response>
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.
sourcepub async fn get_sso_login_url(
&self,
redirect_url: &str,
idp_id: Option<&str>
) -> Result<String>
pub async fn get_sso_login_url(
&self,
redirect_url: &str,
idp_id: Option<&str>
) -> Result<String>
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 aloginToken
after a successful SSO login. -
idp_id
- The optional ID of the identity provider to login with.
sourcepub fn login_username<'a>(
&self,
id: &'a impl AsRef<str> + ?Sized,
password: &'a str
) -> LoginBuilder<'a>
pub fn login_username<'a>(
&self,
id: &'a impl AsRef<str> + ?Sized,
password: &'a str
) -> LoginBuilder<'a>
Login to the server with a username and password.
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 through
LoginBuilder::device_id
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 ID or user ID localpart of the user that should be logged into the homeserver. -
password
- The password of the user.
Example
use matrix_sdk::Client;
let client = Client::new(homeserver).await?;
let user = "example";
let response = client
.login_username(user, "wordpass")
.initial_device_display_name("My bot")
.send()
.await?;
println!(
"Logged in as {user}, got device_id {} and access_token {}",
response.device_id, response.access_token,
);
sourcepub fn login_identifier<'a>(
&self,
id: UserIdentifier<'a>,
password: &'a str
) -> LoginBuilder<'a>
pub fn login_identifier<'a>(
&self,
id: UserIdentifier<'a>,
password: &'a str
) -> LoginBuilder<'a>
Login to the server with a user identifier and password.
This is more general form of login_username
that also accepts third-party identifiers instead of just the user ID or
its localpart.
sourcepub fn login_token<'a>(&self, token: &'a str) -> LoginBuilder<'a>
pub fn login_token<'a>(&self, token: &'a str) -> LoginBuilder<'a>
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 through LoginBuilder::device_id
to
restore the correct stores, if the device ID isn’t provided a new
device will be created.
Arguments
token
- A login token.
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_token(login_token)
.initial_device_display_name("My app")
.send()
.await
.unwrap();
println!(
"Logged in as {}, got device_id {} and access_token {}",
response.user_id, response.device_id, response.access_token,
);
sourcepub fn login_sso<'a, F, Fut>(
&self,
use_sso_login_url: F
) -> SsoLoginBuilder<'a, F>where
F: FnOnce(String) -> Fut + Send,
Fut: Future<Output = Result<()>> + Send,
Available on crate feature sso-login
only.
pub fn login_sso<'a, F, Fut>(
&self,
use_sso_login_url: F
) -> SsoLoginBuilder<'a, F>where
F: FnOnce(String) -> Fut + Send,
Fut: Future<Output = Result<()>> + Send,
sso-login
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_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_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.
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 returnOk(())
if the URL was successfully opened. If it returnsErr
, the error will be forwarded.
Example
let client = Client::new(homeserver).await.unwrap();
let response = client
.login_sso(|sso_url| async move {
// Open sso_url
Ok(())
})
.initial_device_display_name("My app")
.send()
.await
.unwrap();
println!(
"Logged in as {}, got device_id {} and access_token {}",
response.user_id, response.device_id, response.access_token
);
sourcepub async fn login(
&self,
user: impl AsRef<str>,
password: &str,
device_id: Option<&str>,
initial_device_display_name: Option<&str>
) -> Result<Response>
👎Deprecated: Replaced by Client::login_username
pub async fn login(
&self,
user: impl AsRef<str>,
password: &str,
device_id: Option<&str>,
initial_device_display_name: Option<&str>
) -> Result<Response>
Client::login_username
Login to the server with a username and password.
sourcepub async fn login_with_sso<C>(
&self,
use_sso_login_url: impl FnOnce(String) -> C + Send,
server_url: Option<&str>,
server_response: Option<&str>,
device_id: Option<&str>,
initial_device_display_name: Option<&str>,
idp_id: Option<&str>
) -> Result<Response>where
C: Future<Output = Result<()>> + Send,
👎Deprecated: Replaced by Client::login_sso
Available on crate feature sso-login
only.
pub async fn login_with_sso<C>(
&self,
use_sso_login_url: impl FnOnce(String) -> C + Send,
server_url: Option<&str>,
server_response: Option<&str>,
device_id: Option<&str>,
initial_device_display_name: Option<&str>,
idp_id: Option<&str>
) -> Result<Response>where
C: Future<Output = Result<()>> + Send,
Client::login_sso
sso-login
only.Login to the server via Single Sign-On.
sourcepub async fn login_with_token(
&self,
token: &str,
device_id: Option<&str>,
initial_device_display_name: Option<&str>
) -> Result<Response>
👎Deprecated: Replaced by Client::login_token
pub async fn login_with_token(
&self,
token: &str,
device_id: Option<&str>,
initial_device_display_name: Option<&str>
) -> Result<Response>
Client::login_token
Login to the server with a token.
sourcepub async fn restore_login(&self, session: Session) -> Result<()>
pub async fn restore_login(&self, session: Session) -> Result<()>
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::{
ruma::{device_id, user_id},
Client, Session,
};
let homeserver = Url::parse("http://example.com")?;
let client = Client::new(homeserver).await?;
let session = Session {
access_token: "My-Token".to_owned(),
refresh_token: None,
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?;
sourcepub async fn refresh_access_token(&self) -> HttpResult<Option<Response>>
pub async fn refresh_access_token(&self) -> HttpResult<Option<Response>>
Refresh the access token.
When support for refreshing access tokens is activated on both the
homeserver and the client, access tokens have an expiration date and
need to be refreshed periodically. To activate support for refresh
tokens in the Client
, it needs to be done at login with the
LoginBuilder::request_refresh_token()
method, or during account
registration.
This method doesn’t need to be called if
ClientBuilder::handle_refresh_tokens()
is called during construction
of the Client
. Otherwise, it should be called once when a refresh
token is available and an UnknownToken
error is received.
If this call fails with another UnknownToken
error, it means that
the session needs to be logged in again.
It can also be called at any time when a refresh token is available, it will invalidate the previous access token.
The new tokens in the response will be used by the Client
and should
be persisted to be able to restore the session. The response will
always contain an access token that replaces the previous one. It
can also contain a refresh token, in which case it will also replace
the previous one.
This method is protected behind a lock, so calling this method several
times at once will only call the endpoint once and all subsequent calls
will wait for the result of the first call. The first call will
return Ok(Some(response))
or the HttpError
returned by the
endpoint, while the others will return Ok(None)
if the token was
refreshed by the first call or a RefreshTokenError
error, if it
failed.
Example
use matrix_sdk::{Client, Error, Session};
use url::Url;
let homeserver = Url::parse("http://example.com")?;
let client = Client::new(homeserver).await?;
let (user, password) = get_credentials();
let response = client
.login_username(user, password)
.initial_device_display_name("My App")
.request_refresh_token()
.send()
.await?;
persist_session(client.session());
// Handle when an `M_UNKNOWN_TOKEN` error is encountered.
async fn on_unknown_token_err(client: &Client) -> Result<(), Error> {
if client.refresh_token().is_some()
&& client.refresh_access_token().await.is_ok()
{
persist_session(client.session());
return Ok(());
}
let (user, password) = get_credentials();
client
.login_username(user, password)
.request_refresh_token()
.send()
.await?;
persist_session(client.session());
Ok(())
}
sourcepub async fn register(
&self,
registration: impl Into<Request<'_>>
) -> HttpResult<Response>
pub async fn register(
&self,
registration: impl Into<Request<'_>>
) -> HttpResult<Response>
Register a user to the server.
Arguments
registration
- The easiest way to create this request is using the [register::v3::Request
] itself.
Examples
let mut request = RegistrationRequest::new();
request.username = Some("user");
request.password = Some("password");
request.auth = Some(uiaa::AuthData::FallbackAcknowledgement(
uiaa::FallbackAcknowledgement::new("foobar"),
));
let client = Client::new(homeserver).await.unwrap();
client.register(request).await;
sourcepub async fn get_or_upload_filter(
&self,
filter_name: &str,
definition: FilterDefinition<'_>
) -> Result<String>
pub async fn get_or_upload_filter(
&self,
filter_name: &str,
definition: FilterDefinition<'_>
) -> Result<String>
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();
sourcepub async fn join_room_by_id(&self, room_id: &RoomId) -> HttpResult<Response>
pub async fn join_room_by_id(&self, room_id: &RoomId) -> HttpResult<Response>
Join a room by RoomId
.
Returns a join_room_by_id::Response
consisting of the
joined rooms RoomId
.
Arguments
room_id
- TheRoomId
of the room to be joined.
sourcepub async fn join_room_by_id_or_alias(
&self,
alias: &RoomOrAliasId,
server_names: &[OwnedServerName]
) -> HttpResult<Response>
pub async fn join_room_by_id_or_alias(
&self,
alias: &RoomOrAliasId,
server_names: &[OwnedServerName]
) -> HttpResult<Response>
Join a room by RoomId
.
Returns a join_room_by_id_or_alias::Response
consisting of the
joined rooms RoomId
.
Arguments
alias
- TheRoomId
orRoomAliasId
of the room to be joined. An alias looks like#name:example.com
.
sourcepub async fn public_rooms(
&self,
limit: Option<u32>,
since: Option<&str>,
server: Option<&ServerName>
) -> HttpResult<Response>
pub async fn public_rooms(
&self,
limit: Option<u32>,
since: Option<&str>,
server: Option<&ServerName>
) -> HttpResult<Response>
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 ofPublicRoomsChunk
s in each response. -
since
- Pagination token from a previous request. -
server
- The name of the server, ifNone
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;
sourcepub async fn create_room(
&self,
room: impl Into<Request<'_>>
) -> HttpResult<Response>
pub async fn create_room(
&self,
room: impl Into<Request<'_>>
) -> HttpResult<Response>
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 thecreate_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());
sourcepub async fn public_rooms_filtered(
&self,
room_search: impl Into<Request<'_>>
) -> HttpResult<Response>
pub async fn public_rooms_filtered(
&self,
room_search: impl Into<Request<'_>>
) -> HttpResult<Response>
Search the homeserver’s directory for public rooms with a filter.
Arguments
room_search
- The easiest way to create this request is using theget_public_rooms_filtered::Request
itself.
Examples
use matrix_sdk::ruma::{
api::client::directory::get_public_rooms_filtered, directory::Filter,
};
let mut filter = Filter::new();
filter.generic_search_term = Some("rust");
let mut request = get_public_rooms_filtered::v3::Request::new();
request.filter = filter;
let response = client.public_rooms_filtered(request).await?;
for room in response.chunk {
println!("Found room {:?}", room);
}
sourcepub async fn send<Request>(
&self,
request: Request,
config: Option<RequestConfig>
) -> HttpResult<Request::IncomingResponse>where
Request: OutgoingRequest + Clone + Debug,
HttpError: From<FromHttpResponseError<Request::EndpointError>>,
pub async fn send<Request>(
&self,
request: Request,
config: Option<RequestConfig>
) -> HttpResult<Request::IncomingResponse>where
Request: OutgoingRequest + Clone + Debug,
HttpError: From<FromHttpResponseError<Request::EndpointError>>,
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
sourcepub async fn devices(&self) -> HttpResult<Response>
pub async fn devices(&self) -> HttpResult<Response>
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("")
);
}
sourcepub async fn delete_devices(
&self,
devices: &[OwnedDeviceId],
auth_data: Option<AuthData<'_>>
) -> HttpResult<Response>
pub async fn delete_devices(
&self,
devices: &[OwnedDeviceId],
auth_data: Option<AuthData<'_>>
) -> HttpResult<Response>
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 toNone
and will always fail with anUiaaResponse
. The response will contain information for the interactive auth and the same request needs to be made but this time with someauth_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 mut password = uiaa::Password::new(
uiaa::UserIdentifier::UserIdOrLocalpart("example"),
"wordpass",
);
password.session = info.session.as_deref();
client
.delete_devices(devices, Some(uiaa::AuthData::Password(password)))
.await?;
}
}
sourcepub async fn sync_once(
&self,
sync_settings: SyncSettings<'_>
) -> Result<SyncResponse>
pub async fn sync_once(
&self,
sync_settings: SyncSettings<'_>
) -> Result<SyncResponse>
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 servertimeout
- 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 configuredtoken
.
Examples
use matrix_sdk::{
config::SyncSettings,
ruma::events::room::message::OriginalSyncRoomMessageEvent, Client,
};
let client = Client::new(homeserver).await?;
client.login_username(username, password).send().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.add_event_handler(|ev: OriginalSyncRoomMessageEvent| async move {
println!("Received event {}: {:?}", ev.sender, ev.content);
});
// Now keep on syncing forever. `sync()` will use the stored sync token
// from our `sync_once()` call automatically.
client.sync(SyncSettings::default()).await;
sourcepub async fn sync(&self, sync_settings: SyncSettings<'_>) -> Result<(), Error>
pub async fn sync(&self, sync_settings: SyncSettings<'_>) -> Result<(), Error>
Repeatedly synchronize the client state with the server.
This method will only return on error, if cancellation is needed
the method should be wrapped in a cancelable task or the
Client::sync_with_callback
method can be used or
Client::sync_with_result_callback
if you want to handle error
cases in the loop, too.
This method will internally call Client::sync_once
in a loop.
This method can be used with the Client::add_event_handler
method to react to individual events. If you instead wish to handle
events in a bulk manner the Client::sync_with_callback
,
Client::sync_with_result_callback
and
Client::sync_stream
methods can be used instead. Those 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 forClient::sync_once
for more info.
Return
The sync runs until an error occurs, returning with Err(Error)
. It is
up to the user of the API to check the error and decide whether the sync
should continue or not.
Examples
use matrix_sdk::{
config::SyncSettings,
ruma::events::room::message::OriginalSyncRoomMessageEvent, Client,
};
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.add_event_handler(|ev: OriginalSyncRoomMessageEvent| async move {
println!("Received event {}: {:?}", ev.sender, ev.content);
});
// Now keep on syncing forever. `sync()` will use the latest sync token
// automatically.
client.sync(SyncSettings::default()).await?;
sourcepub async fn sync_with_callback<C>(
&self,
sync_settings: SyncSettings<'_>,
callback: impl Fn(SyncResponse) -> C
) -> Result<(), Error>where
C: Future<Output = LoopCtrl>,
pub async fn sync_with_callback<C>(
&self,
sync_settings: SyncSettings<'_>,
callback: impl Fn(SyncResponse) -> C
) -> Result<(), Error>where
C: Future<Output = LoopCtrl>,
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 forClient::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 returnsLoopCtrl::Continue
the sync will continue, if the callback returnsLoopCtrl::Break
the sync will be stopped.
Return
The sync runs until an error occurs or the
callback indicates that the Loop should stop. If the callback asked for
a regular stop, the result will be Ok(())
otherwise the
Err(Error)
is returned.
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;
})
sourcepub async fn sync_with_result_callback<C>(
&self,
sync_settings: SyncSettings<'_>,
callback: impl Fn(Result<SyncResponse, Error>) -> C
) -> Result<(), Error>where
C: Future<Output = Result<LoopCtrl, Error>>,
pub async fn sync_with_result_callback<C>(
&self,
sync_settings: SyncSettings<'_>,
callback: impl Fn(Result<SyncResponse, Error>) -> C
) -> Result<(), Error>where
C: Future<Output = Result<LoopCtrl, Error>>,
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 forClient::sync_once
for more info. -
callback
- A callback that will be called every time after a response has been received, failure or not. The callback returns aResult<LoopCtrl, Error>, too. When returning
Ok(LoopCtrl::Continue)the sync will continue, if the callback returns
Ok(LoopCtrl::Break)the sync will be stopped and the function returns
Ok(()). In case the callback can't handle the
Erroror has a different malfunction, it can return an
Err(Error), which results in the sync ending and the
Err(Error)` being returned.
Return
The sync runs until an error occurs that the callback can’t handle or
the callback indicates that the Loop should stop. If the callback
asked for a regular stop, the result will be Ok(())
otherwise the
Err(Error)
is returned.
Note: Lower-level configuration (e.g. for retries) are not changed by
this, and are handled first without sending the result to the
callback. Only after they have exceeded is the Result
handed to
the callback.
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_result_callback(sync_settings, |response| async move {
let channel = sync_channel;
let sync_response = response?;
for (room_id, room) in sync_response.rooms.join {
for event in room.timeline.events {
channel.send(event).await.unwrap();
}
}
Ok(LoopCtrl::Continue)
})
.await;
})
sourcepub async fn sync_stream<'a>(
&'a self,
sync_settings: SyncSettings<'a>
) -> impl Stream<Item = Result<SyncResponse>> + 'a
pub async fn sync_stream<'a>(
&'a self,
sync_settings: SyncSettings<'a>
) -> impl Stream<Item = Result<SyncResponse>> + 'a
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 forClient::sync_once
for more info.
Examples
use futures::StreamExt;
use matrix_sdk::{config::SyncSettings, Client};
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);
}
}
}
}
sourcepub async fn sync_token(&self) -> Option<String>
pub async fn sync_token(&self) -> Option<String>
Get the current, if any, sync token of the client. This will be None if the client didn’t sync at least once.
sourcepub async fn whoami(&self) -> HttpResult<Response>
pub async fn whoami(&self) -> HttpResult<Response>
Gets information about the owner of a given access token.
sourcepub async fn logout(&self) -> HttpResult<Response>
pub async fn logout(&self) -> HttpResult<Response>
Log out the current user