2020-02-21 15:54:05 +00:00
|
|
|
// Copyright 2020 Damir Jelić
|
|
|
|
// Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
2020-04-23 10:43:59 +00:00
|
|
|
#[cfg(feature = "encryption")]
|
|
|
|
use std::collections::BTreeMap;
|
2020-07-30 23:22:48 +00:00
|
|
|
use std::{
|
|
|
|
collections::HashMap,
|
|
|
|
convert::{TryFrom, TryInto},
|
|
|
|
fmt::{self, Debug},
|
|
|
|
path::Path,
|
|
|
|
result::Result as StdResult,
|
|
|
|
sync::Arc,
|
|
|
|
};
|
|
|
|
|
|
|
|
use matrix_sdk_common::{
|
2020-08-04 09:23:24 +00:00
|
|
|
identifiers::ServerName,
|
2020-07-30 23:22:48 +00:00
|
|
|
instant::{Duration, Instant},
|
|
|
|
js_int::UInt,
|
|
|
|
locks::RwLock,
|
|
|
|
presence::PresenceState,
|
|
|
|
uuid::Uuid,
|
|
|
|
};
|
2020-04-09 14:19:32 +00:00
|
|
|
|
2020-05-08 14:12:21 +00:00
|
|
|
use futures_timer::Delay as sleep;
|
|
|
|
use std::future::Future;
|
2020-07-15 12:05:22 +00:00
|
|
|
#[cfg(feature = "encryption")]
|
|
|
|
use tracing::{debug, warn};
|
2020-07-29 08:56:18 +00:00
|
|
|
use tracing::{error, info, instrument};
|
2019-10-23 20:47:00 +00:00
|
|
|
|
2020-07-29 08:56:18 +00:00
|
|
|
use reqwest::header::{HeaderValue, InvalidHeaderValue};
|
2019-10-23 20:47:00 +00:00
|
|
|
use url::Url;
|
|
|
|
|
2020-08-04 09:23:24 +00:00
|
|
|
use crate::{
|
|
|
|
events::{room::message::MessageEventContent, EventType},
|
|
|
|
identifiers::{EventId, RoomId, RoomIdOrAliasId, UserId},
|
|
|
|
Endpoint,
|
|
|
|
};
|
2019-10-23 20:47:00 +00:00
|
|
|
|
2020-04-01 13:37:00 +00:00
|
|
|
#[cfg(feature = "encryption")]
|
2020-08-04 09:41:20 +00:00
|
|
|
use crate::{identifiers::DeviceId, sas::Sas};
|
2020-04-01 13:37:00 +00:00
|
|
|
|
2020-05-13 13:02:28 +00:00
|
|
|
#[cfg(not(target_arch = "wasm32"))]
|
2019-11-17 18:55:59 +00:00
|
|
|
use crate::VERSION;
|
2020-08-04 09:41:20 +00:00
|
|
|
use crate::{api, http_client::HttpClient, EventEmitter, Result};
|
2020-05-25 12:21:04 +00:00
|
|
|
use matrix_sdk_base::{BaseClient, BaseClientConfig, Room, Session, StateStore};
|
2020-03-29 12:05:40 +00:00
|
|
|
|
2020-03-24 15:18:56 +00:00
|
|
|
const DEFAULT_SYNC_TIMEOUT: Duration = Duration::from_secs(30);
|
2020-02-28 09:33:17 +00:00
|
|
|
|
2020-02-21 13:29:25 +00:00
|
|
|
/// An async/await enabled Matrix client.
|
2020-04-15 11:52:29 +00:00
|
|
|
///
|
2020-05-08 12:02:49 +00:00
|
|
|
/// All of the state is held in an `Arc` so the `Client` can be cloned freely.
|
2020-05-06 13:36:55 +00:00
|
|
|
#[derive(Clone)]
|
2020-05-08 12:02:49 +00:00
|
|
|
pub struct Client {
|
2019-10-23 20:47:00 +00:00
|
|
|
/// The URL of the homeserver to connect to.
|
2020-07-29 08:56:18 +00:00
|
|
|
homeserver: Arc<Url>,
|
2019-10-23 20:47:00 +00:00
|
|
|
/// The underlying HTTP client.
|
2020-07-29 08:56:18 +00:00
|
|
|
http_client: HttpClient,
|
2019-10-23 20:47:00 +00:00
|
|
|
/// User session data.
|
2020-05-06 13:36:55 +00:00
|
|
|
pub(crate) base_client: BaseClient,
|
2019-10-23 20:47:00 +00:00
|
|
|
}
|
|
|
|
|
2020-06-24 09:25:31 +00:00
|
|
|
// #[cfg_attr(tarpaulin, skip)]
|
2020-06-02 18:57:56 +00:00
|
|
|
impl Debug for Client {
|
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> StdResult<(), fmt::Error> {
|
2020-05-08 12:02:49 +00:00
|
|
|
write!(fmt, "Client {{ homeserver: {} }}", self.homeserver)
|
2020-03-19 12:55:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-08 12:02:49 +00:00
|
|
|
/// Configuration for the creation of the `Client`.
|
2020-02-21 13:29:25 +00:00
|
|
|
///
|
2020-04-23 23:37:27 +00:00
|
|
|
/// When setting the `StateStore` it is up to the user to open/connect
|
|
|
|
/// the storage backend before client creation.
|
|
|
|
///
|
2020-02-21 13:29:25 +00:00
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
2020-05-08 12:02:49 +00:00
|
|
|
/// # use matrix_sdk::ClientConfig;
|
2020-02-21 13:29:25 +00:00
|
|
|
/// // To pass all the request through mitmproxy set the proxy and disable SSL
|
|
|
|
/// // verification
|
2020-05-08 12:02:49 +00:00
|
|
|
/// let client_config = ClientConfig::new()
|
2020-02-21 13:29:25 +00:00
|
|
|
/// .proxy("http://localhost:8080")
|
|
|
|
/// .unwrap()
|
|
|
|
/// .disable_ssl_verification();
|
|
|
|
/// ```
|
2020-05-08 12:02:49 +00:00
|
|
|
/// An example of adding a default `JsonStore` to the `Client`.
|
2020-04-23 23:37:27 +00:00
|
|
|
/// ```no_run
|
2020-05-08 12:02:49 +00:00
|
|
|
/// # use matrix_sdk::{ClientConfig, JsonStore};
|
2020-04-23 23:37:27 +00:00
|
|
|
///
|
|
|
|
/// let store = JsonStore::open("path/to/json").unwrap();
|
2020-05-08 12:02:49 +00:00
|
|
|
/// let client_config = ClientConfig::new()
|
2020-04-26 21:13:55 +00:00
|
|
|
/// .state_store(Box::new(store));
|
2020-04-23 23:37:27 +00:00
|
|
|
/// ```
|
2020-05-22 13:23:58 +00:00
|
|
|
#[derive(Default)]
|
2020-05-08 12:02:49 +00:00
|
|
|
pub struct ClientConfig {
|
2020-05-08 14:12:21 +00:00
|
|
|
#[cfg(not(target_arch = "wasm32"))]
|
2019-10-23 20:47:00 +00:00
|
|
|
proxy: Option<reqwest::Proxy>,
|
2019-10-24 20:34:58 +00:00
|
|
|
user_agent: Option<HeaderValue>,
|
2019-10-23 20:47:00 +00:00
|
|
|
disable_ssl_verification: bool,
|
2020-05-25 12:21:04 +00:00
|
|
|
base_config: BaseClientConfig,
|
2020-07-16 23:06:26 +00:00
|
|
|
timeout: Option<Duration>,
|
2020-04-22 21:39:57 +00:00
|
|
|
}
|
|
|
|
|
2020-06-24 09:25:31 +00:00
|
|
|
// #[cfg_attr(tarpaulin, skip)]
|
2020-06-02 18:57:56 +00:00
|
|
|
impl Debug for ClientConfig {
|
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
2020-05-08 14:12:21 +00:00
|
|
|
let mut res = fmt.debug_struct("ClientConfig");
|
|
|
|
|
|
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
|
|
let res = res.field("proxy", &self.proxy);
|
|
|
|
|
|
|
|
res.field("user_agent", &self.user_agent)
|
2020-04-22 21:39:57 +00:00
|
|
|
.field("disable_ssl_verification", &self.disable_ssl_verification)
|
|
|
|
.finish()
|
|
|
|
}
|
2019-10-23 20:47:00 +00:00
|
|
|
}
|
|
|
|
|
2020-05-08 12:02:49 +00:00
|
|
|
impl ClientConfig {
|
|
|
|
/// Create a new default `ClientConfig`.
|
2019-10-23 20:47:00 +00:00
|
|
|
pub fn new() -> Self {
|
|
|
|
Default::default()
|
|
|
|
}
|
|
|
|
|
2020-02-21 13:29:25 +00:00
|
|
|
/// Set the proxy through which all the HTTP requests should go.
|
|
|
|
///
|
|
|
|
/// Note, only HTTP proxies are supported.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
|
|
|
/// * `proxy` - The HTTP URL of the proxy.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
2020-05-08 12:02:49 +00:00
|
|
|
/// use matrix_sdk::ClientConfig;
|
2020-02-21 13:29:25 +00:00
|
|
|
///
|
2020-05-08 12:02:49 +00:00
|
|
|
/// let client_config = ClientConfig::new()
|
2020-02-21 13:29:25 +00:00
|
|
|
/// .proxy("http://localhost:8080")
|
|
|
|
/// .unwrap();
|
|
|
|
/// ```
|
2020-05-08 14:12:21 +00:00
|
|
|
#[cfg(not(target_arch = "wasm32"))]
|
2020-03-18 13:15:56 +00:00
|
|
|
pub fn proxy(mut self, proxy: &str) -> Result<Self> {
|
2019-10-23 20:47:00 +00:00
|
|
|
self.proxy = Some(reqwest::Proxy::all(proxy)?);
|
|
|
|
Ok(self)
|
|
|
|
}
|
|
|
|
|
2020-02-21 13:29:25 +00:00
|
|
|
/// Disable SSL verification for the HTTP requests.
|
2019-10-23 20:47:00 +00:00
|
|
|
pub fn disable_ssl_verification(mut self) -> Self {
|
|
|
|
self.disable_ssl_verification = true;
|
|
|
|
self
|
|
|
|
}
|
2019-10-24 20:34:58 +00:00
|
|
|
|
2020-02-21 13:29:25 +00:00
|
|
|
/// Set a custom HTTP user agent for the client.
|
2020-03-18 13:15:56 +00:00
|
|
|
pub fn user_agent(mut self, user_agent: &str) -> StdResult<Self, InvalidHeaderValue> {
|
2019-10-24 20:34:58 +00:00
|
|
|
self.user_agent = Some(HeaderValue::from_str(user_agent)?);
|
|
|
|
Ok(self)
|
|
|
|
}
|
2020-04-22 21:39:57 +00:00
|
|
|
|
|
|
|
/// Set a custom implementation of a `StateStore`.
|
|
|
|
///
|
2020-04-24 23:57:51 +00:00
|
|
|
/// The state store should be opened before being set.
|
2020-04-22 21:39:57 +00:00
|
|
|
pub fn state_store(mut self, store: Box<dyn StateStore>) -> Self {
|
2020-05-25 12:21:04 +00:00
|
|
|
self.base_config = self.base_config.state_store(store);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Set the path for storage.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
|
|
|
/// * `path` - The path where the stores should save data in. It is the
|
|
|
|
/// callers responsibility to make sure that the path exists.
|
|
|
|
///
|
|
|
|
/// In the default configuration the client will open default
|
|
|
|
/// implementations for the crypto store and the state store. It will use
|
|
|
|
/// the given path to open the stores. If no path is provided no store will
|
|
|
|
/// be opened
|
|
|
|
pub fn store_path<P: AsRef<Path>>(mut self, path: P) -> Self {
|
|
|
|
self.base_config = self.base_config.store_path(path);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Set the passphrase to encrypt the crypto store.
|
|
|
|
///
|
|
|
|
/// # Argument
|
|
|
|
///
|
|
|
|
/// * `passphrase` - The passphrase that will be used to encrypt the data in
|
|
|
|
/// the cryptostore.
|
|
|
|
///
|
|
|
|
/// This is only used if no custom cryptostore is set.
|
|
|
|
pub fn passphrase(mut self, passphrase: String) -> Self {
|
|
|
|
self.base_config = self.base_config.passphrase(passphrase);
|
2020-04-22 21:39:57 +00:00
|
|
|
self
|
|
|
|
}
|
2020-07-16 21:16:30 +00:00
|
|
|
|
2020-07-16 21:40:52 +00:00
|
|
|
/// Set a timeout duration for all HTTP requests. The default is no timeout.
|
|
|
|
pub fn timeout(mut self, timeout: Duration) -> Self {
|
2020-07-16 23:06:26 +00:00
|
|
|
self.timeout = Some(timeout);
|
2020-07-16 21:40:52 +00:00
|
|
|
self
|
|
|
|
}
|
2019-10-23 20:47:00 +00:00
|
|
|
}
|
|
|
|
|
2019-12-04 18:31:33 +00:00
|
|
|
#[derive(Debug, Default, Clone)]
|
2020-02-21 13:29:25 +00:00
|
|
|
/// Settings for a sync call.
|
2019-10-23 20:47:00 +00:00
|
|
|
pub struct SyncSettings {
|
2020-07-16 13:13:35 +00:00
|
|
|
pub(crate) filter: Option<sync_events::Filter>,
|
2020-03-24 15:18:56 +00:00
|
|
|
pub(crate) timeout: Option<Duration>,
|
2019-10-23 20:47:00 +00:00
|
|
|
pub(crate) token: Option<String>,
|
2020-04-03 12:09:56 +00:00
|
|
|
pub(crate) full_state: bool,
|
2019-10-23 20:47:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl SyncSettings {
|
2020-02-21 13:29:25 +00:00
|
|
|
/// Create new default sync settings.
|
2019-10-23 20:47:00 +00:00
|
|
|
pub fn new() -> Self {
|
|
|
|
Default::default()
|
|
|
|
}
|
|
|
|
|
2020-02-21 13:29:25 +00:00
|
|
|
/// Set the sync token.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
|
|
|
/// * `token` - The sync token that should be used for the sync call.
|
2019-10-23 20:47:00 +00:00
|
|
|
pub fn token<S: Into<String>>(mut self, token: S) -> Self {
|
|
|
|
self.token = Some(token.into());
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-02-21 13:29:25 +00:00
|
|
|
/// Set the maximum time the server can wait, in milliseconds, before
|
|
|
|
/// responding to the sync request.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
|
|
|
/// * `timeout` - The time the server is allowed to wait.
|
2020-03-24 15:18:56 +00:00
|
|
|
pub fn timeout(mut self, timeout: Duration) -> Self {
|
|
|
|
self.timeout = Some(timeout);
|
|
|
|
self
|
2019-10-23 20:47:00 +00:00
|
|
|
}
|
|
|
|
|
2020-07-16 13:13:35 +00:00
|
|
|
/// Set the sync filter.
|
|
|
|
/// It can be either the filter ID, or the definition for the filter.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
|
|
|
/// * `filter` - The filter configuration that should be used for the sync call.
|
|
|
|
pub fn filter(mut self, filter: sync_events::Filter) -> Self {
|
|
|
|
self.filter = Some(filter);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-02-21 13:29:25 +00:00
|
|
|
/// Should the server return the full state from the start of the timeline.
|
|
|
|
///
|
|
|
|
/// This does nothing if no sync token is set.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
/// * `full_state` - A boolean deciding if the server should return the full
|
|
|
|
/// state or not.
|
2019-10-23 20:47:00 +00:00
|
|
|
pub fn full_state(mut self, full_state: bool) -> Self {
|
2020-04-03 12:09:56 +00:00
|
|
|
self.full_state = full_state;
|
2019-10-23 20:47:00 +00:00
|
|
|
self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-01 13:37:00 +00:00
|
|
|
#[cfg(feature = "encryption")]
|
2020-04-03 08:27:30 +00:00
|
|
|
use api::r0::keys::{claim_keys, get_keys, upload_keys, KeyAlgorithm};
|
2020-05-05 13:29:25 +00:00
|
|
|
#[cfg(feature = "encryption")]
|
|
|
|
use api::r0::to_device::send_event_to_device;
|
2020-07-30 23:22:48 +00:00
|
|
|
use api::r0::{
|
|
|
|
account::register,
|
|
|
|
directory::{get_public_rooms, get_public_rooms_filtered},
|
|
|
|
membership::{
|
|
|
|
ban_user, forget_room,
|
|
|
|
invite_user::{self, InvitationRecipient},
|
|
|
|
join_room_by_id, join_room_by_id_or_alias, kick_user, leave_room, Invite3pid,
|
|
|
|
},
|
|
|
|
message::{create_message_event, get_message_events},
|
|
|
|
read_marker::set_read_marker,
|
|
|
|
receipt::create_receipt,
|
|
|
|
room::create_room,
|
|
|
|
session::login,
|
|
|
|
sync::sync_events,
|
|
|
|
typing::create_typing_event,
|
|
|
|
uiaa::UiaaResponse,
|
|
|
|
};
|
2019-10-23 20:47:00 +00:00
|
|
|
|
2020-05-08 12:02:49 +00:00
|
|
|
impl Client {
|
2019-10-23 20:47:00 +00:00
|
|
|
/// Creates a new client for making HTTP requests to the given homeserver.
|
2020-02-21 13:29:25 +00:00
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
|
|
|
/// * `homeserver_url` - The homeserver that the client should connect to.
|
2020-05-22 13:23:58 +00:00
|
|
|
pub fn new<U: TryInto<Url>>(homeserver_url: U) -> Result<Self> {
|
2020-05-08 12:02:49 +00:00
|
|
|
let config = ClientConfig::new();
|
2020-05-22 13:23:58 +00:00
|
|
|
Client::new_with_config(homeserver_url, config)
|
2019-10-23 20:47:00 +00:00
|
|
|
}
|
|
|
|
|
2020-02-21 13:29:25 +00:00
|
|
|
/// Create a new client with the given configuration.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
|
|
|
/// * `homeserver_url` - The homeserver that the client should connect to.
|
2020-05-22 13:23:58 +00:00
|
|
|
///
|
2020-02-21 13:29:25 +00:00
|
|
|
/// * `config` - Configuration for the client.
|
2019-11-10 10:44:03 +00:00
|
|
|
pub fn new_with_config<U: TryInto<Url>>(
|
|
|
|
homeserver_url: U,
|
2020-05-08 12:02:49 +00:00
|
|
|
config: ClientConfig,
|
2020-03-18 13:15:56 +00:00
|
|
|
) -> Result<Self> {
|
2020-07-31 00:05:49 +00:00
|
|
|
let homeserver = if let Ok(u) = homeserver_url.try_into() {
|
|
|
|
u
|
|
|
|
} else {
|
|
|
|
panic!("Error parsing homeserver url")
|
2019-11-10 10:44:03 +00:00
|
|
|
};
|
|
|
|
|
2019-10-23 20:47:00 +00:00
|
|
|
let http_client = reqwest::Client::builder();
|
|
|
|
|
2020-05-08 14:12:21 +00:00
|
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
|
|
let http_client = {
|
2020-07-16 23:53:09 +00:00
|
|
|
let http_client = match config.timeout {
|
|
|
|
Some(x) => http_client.timeout(x),
|
|
|
|
None => http_client,
|
|
|
|
};
|
|
|
|
|
2020-05-08 14:12:21 +00:00
|
|
|
let http_client = if config.disable_ssl_verification {
|
|
|
|
http_client.danger_accept_invalid_certs(true)
|
|
|
|
} else {
|
|
|
|
http_client
|
|
|
|
};
|
2019-10-23 20:47:00 +00:00
|
|
|
|
2020-05-08 14:12:21 +00:00
|
|
|
let http_client = match config.proxy {
|
|
|
|
Some(p) => http_client.proxy(p),
|
|
|
|
None => http_client,
|
|
|
|
};
|
2019-10-23 20:47:00 +00:00
|
|
|
|
2020-05-08 14:12:21 +00:00
|
|
|
let mut headers = reqwest::header::HeaderMap::new();
|
2019-10-23 20:47:00 +00:00
|
|
|
|
2020-05-08 14:12:21 +00:00
|
|
|
let user_agent = match config.user_agent {
|
|
|
|
Some(a) => a,
|
|
|
|
None => HeaderValue::from_str(&format!("matrix-rust-sdk {}", VERSION)).unwrap(),
|
|
|
|
};
|
2019-10-24 20:34:58 +00:00
|
|
|
|
2020-05-08 14:12:21 +00:00
|
|
|
headers.insert(reqwest::header::USER_AGENT, user_agent);
|
2019-10-23 20:47:00 +00:00
|
|
|
|
2020-05-08 14:12:21 +00:00
|
|
|
http_client.default_headers(headers)
|
|
|
|
};
|
|
|
|
|
2020-07-29 08:56:18 +00:00
|
|
|
let homeserver = Arc::new(homeserver);
|
|
|
|
|
|
|
|
let http_client = HttpClient {
|
|
|
|
homeserver: homeserver.clone(),
|
|
|
|
inner: http_client.build()?,
|
|
|
|
};
|
2019-10-23 20:47:00 +00:00
|
|
|
|
2020-05-25 12:21:04 +00:00
|
|
|
let base_client = BaseClient::new_with_config(config.base_config)?;
|
2020-04-22 21:39:57 +00:00
|
|
|
|
2019-10-23 20:47:00 +00:00
|
|
|
Ok(Self {
|
|
|
|
homeserver,
|
|
|
|
http_client,
|
2020-05-06 13:36:55 +00:00
|
|
|
base_client,
|
2019-10-23 20:47:00 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-01-11 21:24:32 +00:00
|
|
|
/// Is the client logged in.
|
2020-03-11 10:42:59 +00:00
|
|
|
pub async fn logged_in(&self) -> bool {
|
2020-05-06 13:36:55 +00:00
|
|
|
self.base_client.logged_in().await
|
2019-11-17 18:55:27 +00:00
|
|
|
}
|
|
|
|
|
2020-01-11 21:24:32 +00:00
|
|
|
/// The Homeserver of the client.
|
|
|
|
pub fn homeserver(&self) -> &Url {
|
|
|
|
&self.homeserver
|
|
|
|
}
|
|
|
|
|
2020-07-05 14:47:38 +00:00
|
|
|
/// Get the user id of the current owner of the client.
|
|
|
|
pub async fn user_id(&self) -> Option<UserId> {
|
|
|
|
let session = self.base_client.session().read().await;
|
|
|
|
session.as_ref().cloned().map(|s| s.user_id)
|
|
|
|
}
|
|
|
|
|
2020-05-08 12:02:49 +00:00
|
|
|
/// Add `EventEmitter` to `Client`.
|
2020-03-31 23:34:11 +00:00
|
|
|
///
|
|
|
|
/// The methods of `EventEmitter` are called when the respective `RoomEvents` occur.
|
2020-04-14 22:10:10 +00:00
|
|
|
pub async fn add_event_emitter(&mut self, emitter: Box<dyn EventEmitter>) {
|
2020-05-06 13:36:55 +00:00
|
|
|
self.base_client.add_event_emitter(emitter).await;
|
2020-03-31 23:34:11 +00:00
|
|
|
}
|
|
|
|
|
2020-05-06 23:45:27 +00:00
|
|
|
/// Returns the joined rooms this client knows about.
|
2020-05-08 14:12:21 +00:00
|
|
|
pub fn joined_rooms(&self) -> Arc<RwLock<HashMap<RoomId, Arc<RwLock<Room>>>>> {
|
2020-05-06 13:55:18 +00:00
|
|
|
self.base_client.joined_rooms()
|
2020-03-27 21:22:11 +00:00
|
|
|
}
|
|
|
|
|
2020-05-06 23:45:27 +00:00
|
|
|
/// Returns the invited rooms this client knows about.
|
2020-05-08 14:12:21 +00:00
|
|
|
pub fn invited_rooms(&self) -> Arc<RwLock<HashMap<RoomId, Arc<RwLock<Room>>>>> {
|
2020-05-07 00:35:15 +00:00
|
|
|
self.base_client.invited_rooms()
|
2020-05-06 23:45:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the left rooms this client knows about.
|
2020-05-08 14:12:21 +00:00
|
|
|
pub fn left_rooms(&self) -> Arc<RwLock<HashMap<RoomId, Arc<RwLock<Room>>>>> {
|
2020-05-07 00:35:15 +00:00
|
|
|
self.base_client.left_rooms()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get a joined room with the given room id.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
|
|
|
/// `room_id` - The unique id of the room that should be fetched.
|
|
|
|
pub async fn get_joined_room(&self, room_id: &RoomId) -> Option<Arc<RwLock<Room>>> {
|
|
|
|
self.base_client.get_joined_room(room_id).await
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get an invited room with the given room id.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
|
|
|
/// `room_id` - The unique id of the room that should be fetched.
|
|
|
|
pub async fn get_invited_room(&self, room_id: &RoomId) -> Option<Arc<RwLock<Room>>> {
|
|
|
|
self.base_client.get_invited_room(room_id).await
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get a left room with the given room id.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
|
|
|
/// `room_id` - The unique id of the room that should be fetched.
|
|
|
|
pub async fn get_left_room(&self, room_id: &RoomId) -> Option<Arc<RwLock<Room>>> {
|
|
|
|
self.base_client.get_left_room(room_id).await
|
2020-05-06 23:45:27 +00:00
|
|
|
}
|
|
|
|
|
2020-05-13 08:06:08 +00:00
|
|
|
/// This allows `Client` to manually store `Room` state with the provided
|
|
|
|
/// `StateStore`.
|
2020-05-09 11:33:57 +00:00
|
|
|
///
|
|
|
|
/// Returns Ok when a successful `Room` store occurs.
|
|
|
|
pub async fn store_room_state(&self, room_id: &RoomId) -> Result<()> {
|
|
|
|
self.base_client
|
|
|
|
.store_room_state(room_id)
|
|
|
|
.await
|
|
|
|
.map_err(Into::into)
|
|
|
|
}
|
|
|
|
|
2020-02-21 13:29:25 +00:00
|
|
|
/// Login to the server.
|
|
|
|
///
|
2020-07-07 13:47:34 +00:00
|
|
|
/// 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.
|
|
|
|
///
|
2020-02-21 13:29:25 +00:00
|
|
|
/// # Arguments
|
|
|
|
///
|
2020-03-02 10:31:03 +00:00
|
|
|
/// * `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
|
2020-03-30 18:18:08 +00:00
|
|
|
/// not given the homeserver will create one. Can be an existing
|
2020-02-21 13:29:25 +00:00
|
|
|
/// device_id from a previous login call. Note that this should be done
|
2020-03-30 18:18:08 +00:00
|
|
|
/// only if the client also holds the encryption keys for this device.
|
2020-03-19 12:55:04 +00:00
|
|
|
#[instrument(skip(password))]
|
2020-06-02 18:57:56 +00:00
|
|
|
pub async fn login<S: Into<String> + Debug>(
|
2020-04-15 12:29:34 +00:00
|
|
|
&self,
|
2019-10-23 20:47:00 +00:00
|
|
|
user: S,
|
|
|
|
password: S,
|
|
|
|
device_id: Option<S>,
|
2020-03-27 08:22:29 +00:00
|
|
|
initial_device_display_name: Option<S>,
|
2020-03-18 13:15:56 +00:00
|
|
|
) -> Result<login::Response> {
|
2020-03-19 12:55:04 +00:00
|
|
|
info!("Logging in to {} as {:?}", self.homeserver, user);
|
|
|
|
|
2019-10-23 20:47:00 +00:00
|
|
|
let request = login::Request {
|
2020-02-21 15:33:08 +00:00
|
|
|
user: login::UserInfo::MatrixId(user.into()),
|
|
|
|
login_info: login::LoginInfo::Password {
|
|
|
|
password: password.into(),
|
|
|
|
},
|
2020-07-22 18:43:47 +00:00
|
|
|
device_id: device_id.map(|d| d.into().into()),
|
2020-03-27 08:22:29 +00:00
|
|
|
initial_device_display_name: initial_device_display_name.map(|d| d.into()),
|
2019-10-23 20:47:00 +00:00
|
|
|
};
|
|
|
|
|
2019-10-30 18:30:55 +00:00
|
|
|
let response = self.send(request).await?;
|
2020-05-06 13:36:55 +00:00
|
|
|
self.base_client.receive_login_response(&response).await?;
|
2019-10-23 20:47:00 +00:00
|
|
|
|
|
|
|
Ok(response)
|
|
|
|
}
|
|
|
|
|
2020-05-22 13:23:58 +00:00
|
|
|
/// Restore a previously logged in session.
|
|
|
|
///
|
2020-07-07 13:47:34 +00:00
|
|
|
/// 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.
|
|
|
|
///
|
2020-05-22 13:23:58 +00:00
|
|
|
/// # Arguments
|
|
|
|
///
|
2020-06-01 11:50:45 +00:00
|
|
|
/// * `session` - A session that the user already has from a
|
2020-05-22 13:23:58 +00:00
|
|
|
/// previous login call.
|
|
|
|
pub async fn restore_login(&self, session: Session) -> Result<()> {
|
|
|
|
Ok(self.base_client.restore_login(session).await?)
|
|
|
|
}
|
|
|
|
|
2020-06-02 11:55:24 +00:00
|
|
|
/// Register a user to the server.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
|
|
|
/// * `registration` - The easiest way to create this request is using the `RegistrationBuilder`.
|
|
|
|
///
|
2020-06-02 18:57:56 +00:00
|
|
|
///
|
2020-06-02 11:55:24 +00:00
|
|
|
/// # Examples
|
|
|
|
/// ```
|
2020-06-02 18:57:56 +00:00
|
|
|
/// # use std::convert::TryFrom;
|
|
|
|
/// # use matrix_sdk::{Client, RegistrationBuilder};
|
|
|
|
/// # use matrix_sdk::api::r0::account::register::RegistrationKind;
|
|
|
|
/// # use matrix_sdk::identifiers::DeviceId;
|
|
|
|
/// # use url::Url;
|
|
|
|
/// # let homeserver = Url::parse("http://example.com").unwrap();
|
|
|
|
/// # let mut rt = tokio::runtime::Runtime::new().unwrap();
|
|
|
|
/// # rt.block_on(async {
|
|
|
|
/// let mut builder = RegistrationBuilder::default();
|
|
|
|
/// builder.password("pass")
|
|
|
|
/// .username("user")
|
|
|
|
/// .kind(RegistrationKind::User);
|
|
|
|
/// let mut client = Client::new(homeserver).unwrap();
|
|
|
|
/// client.register_user(builder).await;
|
|
|
|
/// # })
|
2020-06-02 11:55:24 +00:00
|
|
|
/// ```
|
|
|
|
#[instrument(skip(registration))]
|
|
|
|
pub async fn register_user<R: Into<register::Request>>(
|
|
|
|
&self,
|
|
|
|
registration: R,
|
|
|
|
) -> Result<register::Response> {
|
|
|
|
info!("Registering to {}", self.homeserver);
|
|
|
|
|
|
|
|
let request = registration.into();
|
|
|
|
self.send_uiaa(request).await
|
|
|
|
}
|
|
|
|
|
2020-04-10 20:32:28 +00:00
|
|
|
/// Join a room by `RoomId`.
|
|
|
|
///
|
|
|
|
/// Returns a `join_room_by_id::Response` consisting of the
|
|
|
|
/// joined rooms `RoomId`.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
2020-05-06 12:36:28 +00:00
|
|
|
/// * `room_id` - The `RoomId` of the room to be joined.
|
|
|
|
pub async fn join_room_by_id(&self, room_id: &RoomId) -> Result<join_room_by_id::Response> {
|
2020-04-10 20:32:28 +00:00
|
|
|
let request = join_room_by_id::Request {
|
|
|
|
room_id: room_id.clone(),
|
|
|
|
third_party_signed: None,
|
|
|
|
};
|
|
|
|
self.send(request).await
|
|
|
|
}
|
|
|
|
|
2020-05-15 10:32:36 +00:00
|
|
|
/// 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.
|
2020-06-01 11:50:45 +00:00
|
|
|
/// An alias looks like `#name:example.com`.
|
2020-05-15 10:32:36 +00:00
|
|
|
pub async fn join_room_by_id_or_alias(
|
|
|
|
&self,
|
|
|
|
alias: &RoomIdOrAliasId,
|
2020-07-22 18:43:47 +00:00
|
|
|
server_names: &[Box<ServerName>],
|
2020-05-15 10:32:36 +00:00
|
|
|
) -> Result<join_room_by_id_or_alias::Response> {
|
|
|
|
let request = join_room_by_id_or_alias::Request {
|
|
|
|
room_id_or_alias: alias.clone(),
|
|
|
|
server_name: server_names.to_owned(),
|
|
|
|
third_party_signed: None,
|
|
|
|
};
|
|
|
|
self.send(request).await
|
|
|
|
}
|
2020-04-10 20:32:28 +00:00
|
|
|
|
2020-05-05 12:33:31 +00:00
|
|
|
/// Forget a room by `RoomId`.
|
|
|
|
///
|
|
|
|
/// Returns a `forget_room::Response`, an empty response.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
2020-05-06 12:36:28 +00:00
|
|
|
/// * `room_id` - The `RoomId` of the room to be forget.
|
2020-05-05 12:33:31 +00:00
|
|
|
pub async fn forget_room_by_id(&self, room_id: &RoomId) -> Result<forget_room::Response> {
|
|
|
|
let request = forget_room::Request {
|
|
|
|
room_id: room_id.clone(),
|
|
|
|
};
|
|
|
|
self.send(request).await
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Ban a user from a room by `RoomId` and `UserId`.
|
|
|
|
///
|
|
|
|
/// Returns a `ban_user::Response`, an empty response.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
2020-05-06 12:36:28 +00:00
|
|
|
/// * `room_id` - The `RoomId` of the room to ban the user from.
|
2020-05-05 12:33:31 +00:00
|
|
|
///
|
2020-05-06 12:36:28 +00:00
|
|
|
/// * `user_id` - The user to ban by `UserId`.
|
2020-05-05 12:33:31 +00:00
|
|
|
///
|
2020-05-06 12:36:28 +00:00
|
|
|
/// * `reason` - The reason for banning this user.
|
2020-05-05 12:33:31 +00:00
|
|
|
pub async fn ban_user(
|
|
|
|
&self,
|
2020-05-06 12:36:28 +00:00
|
|
|
room_id: &RoomId,
|
|
|
|
user_id: &UserId,
|
2020-05-05 12:33:31 +00:00
|
|
|
reason: Option<String>,
|
|
|
|
) -> Result<ban_user::Response> {
|
|
|
|
let request = ban_user::Request {
|
|
|
|
reason,
|
2020-05-06 12:36:28 +00:00
|
|
|
room_id: room_id.clone(),
|
|
|
|
user_id: user_id.clone(),
|
2020-05-05 12:33:31 +00:00
|
|
|
};
|
|
|
|
self.send(request).await
|
|
|
|
}
|
|
|
|
|
2020-04-10 20:32:28 +00:00
|
|
|
/// Kick a user out of the specified room.
|
|
|
|
///
|
|
|
|
/// Returns a `kick_user::Response`, an empty response.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
2020-05-06 12:36:28 +00:00
|
|
|
/// * `room_id` - The `RoomId` of the room the user should be kicked out of.
|
2020-04-10 20:32:28 +00:00
|
|
|
///
|
2020-05-06 12:36:28 +00:00
|
|
|
/// * `user_id` - The `UserId` of the user that should be kicked out of the room.
|
2020-04-10 20:32:28 +00:00
|
|
|
///
|
2020-05-06 12:36:28 +00:00
|
|
|
/// * `reason` - Optional reason why the room member is being kicked out.
|
2020-04-10 20:32:28 +00:00
|
|
|
pub async fn kick_user(
|
2020-04-15 12:29:34 +00:00
|
|
|
&self,
|
2020-04-10 20:32:28 +00:00
|
|
|
room_id: &RoomId,
|
|
|
|
user_id: &UserId,
|
|
|
|
reason: Option<String>,
|
|
|
|
) -> Result<kick_user::Response> {
|
|
|
|
let request = kick_user::Request {
|
|
|
|
reason,
|
|
|
|
room_id: room_id.clone(),
|
|
|
|
user_id: user_id.clone(),
|
|
|
|
};
|
|
|
|
self.send(request).await
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Leave the specified room.
|
|
|
|
///
|
|
|
|
/// Returns a `leave_room::Response`, an empty response.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
2020-05-06 12:36:28 +00:00
|
|
|
/// * `room_id` - The `RoomId` of the room to leave.
|
2020-04-15 12:29:34 +00:00
|
|
|
pub async fn leave_room(&self, room_id: &RoomId) -> Result<leave_room::Response> {
|
2020-04-10 20:32:28 +00:00
|
|
|
let request = leave_room::Request {
|
|
|
|
room_id: room_id.clone(),
|
|
|
|
};
|
|
|
|
self.send(request).await
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Invite the specified user by `UserId` to the given room.
|
|
|
|
///
|
|
|
|
/// Returns a `invite_user::Response`, an empty response.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
2020-05-06 12:36:28 +00:00
|
|
|
/// * `room_id` - The `RoomId` of the room to invite the specified user to.
|
2020-04-10 20:32:28 +00:00
|
|
|
///
|
2020-05-06 12:36:28 +00:00
|
|
|
/// * `user_id` - The `UserId` of the user to invite to the room.
|
2020-04-10 20:32:28 +00:00
|
|
|
pub async fn invite_user_by_id(
|
2020-04-15 12:29:34 +00:00
|
|
|
&self,
|
2020-04-10 20:32:28 +00:00
|
|
|
room_id: &RoomId,
|
|
|
|
user_id: &UserId,
|
|
|
|
) -> Result<invite_user::Response> {
|
|
|
|
let request = invite_user::Request {
|
|
|
|
room_id: room_id.clone(),
|
|
|
|
recipient: InvitationRecipient::UserId {
|
|
|
|
user_id: user_id.clone(),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
self.send(request).await
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Invite the specified user by third party id to the given room.
|
|
|
|
///
|
|
|
|
/// Returns a `invite_user::Response`, an empty response.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
2020-05-06 12:36:28 +00:00
|
|
|
/// * `room_id` - The `RoomId` of the room to invite the specified user to.
|
2020-04-10 20:32:28 +00:00
|
|
|
///
|
2020-05-06 12:36:28 +00:00
|
|
|
/// * `invite_id` - A third party id of a user to invite to the room.
|
2020-04-10 20:32:28 +00:00
|
|
|
pub async fn invite_user_by_3pid(
|
2020-04-15 12:29:34 +00:00
|
|
|
&self,
|
2020-04-10 20:32:28 +00:00
|
|
|
room_id: &RoomId,
|
|
|
|
invite_id: &Invite3pid,
|
|
|
|
) -> Result<invite_user::Response> {
|
|
|
|
let request = invite_user::Request {
|
|
|
|
room_id: room_id.clone(),
|
|
|
|
recipient: InvitationRecipient::ThirdPartyId(invite_id.clone()),
|
|
|
|
};
|
|
|
|
self.send(request).await
|
|
|
|
}
|
|
|
|
|
2020-06-23 21:31:58 +00:00
|
|
|
/// Search the homeserver's directory of public rooms.
|
|
|
|
///
|
2020-06-25 12:31:51 +00:00
|
|
|
/// Sends a request to "_matrix/client/r0/publicRooms", returns
|
|
|
|
/// a `get_public_rooms::Response`.
|
2020-06-23 21:31:58 +00:00
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
|
|
|
/// * `limit` - The number of `PublicRoomsChunk`s in each response.
|
|
|
|
///
|
|
|
|
/// * `since` - Pagination token from a previous request.
|
|
|
|
///
|
|
|
|
/// * `server` - The name of the server, if `None` the requested server is used.
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
/// ```no_run
|
2020-06-24 11:46:40 +00:00
|
|
|
/// use matrix_sdk::Client;
|
2020-06-23 21:31:58 +00:00
|
|
|
/// # use url::Url;
|
|
|
|
/// # let homeserver = Url::parse("http://example.com").unwrap();
|
|
|
|
/// # let limit = Some(10);
|
|
|
|
/// # let since = Some("since token");
|
|
|
|
/// # let server = Some("server name");
|
|
|
|
///
|
|
|
|
/// let mut cli = Client::new(homeserver).unwrap();
|
|
|
|
/// # use futures::executor::block_on;
|
|
|
|
/// # block_on(async {
|
2020-06-25 12:31:51 +00:00
|
|
|
///
|
|
|
|
/// cli.public_rooms(limit, since, server).await;
|
2020-06-23 21:31:58 +00:00
|
|
|
/// # });
|
|
|
|
/// ```
|
2020-06-24 11:46:40 +00:00
|
|
|
pub async fn public_rooms(
|
2020-06-23 21:31:58 +00:00
|
|
|
&self,
|
|
|
|
limit: Option<u32>,
|
|
|
|
since: Option<&str>,
|
|
|
|
server: Option<&str>,
|
|
|
|
) -> Result<get_public_rooms::Response> {
|
|
|
|
let limit = limit.map(|n| UInt::try_from(n).ok()).flatten();
|
|
|
|
let since = since.map(ToString::to_string);
|
|
|
|
let server = server.map(ToString::to_string);
|
|
|
|
|
|
|
|
let request = get_public_rooms::Request {
|
|
|
|
limit,
|
|
|
|
since,
|
|
|
|
server,
|
|
|
|
};
|
|
|
|
self.send(request).await
|
|
|
|
}
|
|
|
|
|
2020-06-24 11:46:40 +00:00
|
|
|
/// Search the homeserver's directory of public rooms with a filter.
|
2020-06-23 21:31:58 +00:00
|
|
|
///
|
2020-06-25 12:31:51 +00:00
|
|
|
/// Sends a request to "_matrix/client/r0/publicRooms", returns
|
|
|
|
/// a `get_public_rooms_filtered::Response`.
|
2020-06-23 21:31:58 +00:00
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
2020-06-24 11:46:40 +00:00
|
|
|
/// * `room_search` - The easiest way to create this request is using the `RoomListFilterBuilder`.
|
2020-06-23 21:31:58 +00:00
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
/// ```
|
|
|
|
/// # use std::convert::TryFrom;
|
2020-06-24 11:46:40 +00:00
|
|
|
/// # use matrix_sdk::{Client, RoomListFilterBuilder};
|
2020-06-23 21:31:58 +00:00
|
|
|
/// # use matrix_sdk::api::r0::directory::get_public_rooms_filtered::{self, RoomNetwork, Filter};
|
|
|
|
/// # use url::Url;
|
|
|
|
/// # let homeserver = Url::parse("http://example.com").unwrap();
|
|
|
|
/// # let mut rt = tokio::runtime::Runtime::new().unwrap();
|
|
|
|
/// # rt.block_on(async {
|
|
|
|
/// # let last_sync_token = "".to_string();
|
|
|
|
/// let mut client = Client::new(homeserver).unwrap();
|
|
|
|
///
|
|
|
|
/// let generic_search_term = Some("matrix-rust-sdk".to_string());
|
2020-06-24 11:46:40 +00:00
|
|
|
/// let mut builder = RoomListFilterBuilder::new();
|
2020-06-23 21:31:58 +00:00
|
|
|
/// builder
|
|
|
|
/// .filter(Filter { generic_search_term, })
|
|
|
|
/// .since(last_sync_token)
|
|
|
|
/// .room_network(RoomNetwork::Matrix);
|
|
|
|
///
|
2020-06-25 12:31:51 +00:00
|
|
|
/// client.public_rooms_filtered(builder).await;
|
2020-06-23 21:31:58 +00:00
|
|
|
/// # })
|
|
|
|
/// ```
|
2020-06-24 11:46:40 +00:00
|
|
|
pub async fn public_rooms_filtered<R: Into<get_public_rooms_filtered::Request>>(
|
2020-06-23 21:31:58 +00:00
|
|
|
&self,
|
|
|
|
room_search: R,
|
|
|
|
) -> Result<get_public_rooms_filtered::Response> {
|
|
|
|
let request = room_search.into();
|
|
|
|
self.send(request).await
|
|
|
|
}
|
|
|
|
|
2020-04-14 10:36:03 +00:00
|
|
|
/// Create a room using the `RoomBuilder` and send the request.
|
2020-04-10 20:32:28 +00:00
|
|
|
///
|
2020-04-14 10:36:03 +00:00
|
|
|
/// Sends a request to `/_matrix/client/r0/createRoom`, returns a `create_room::Response`,
|
|
|
|
/// this is an empty response.
|
2020-04-10 20:32:28 +00:00
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
2020-05-06 12:36:28 +00:00
|
|
|
/// * `room` - The easiest way to create this request is using the `RoomBuilder`.
|
2020-04-10 20:32:28 +00:00
|
|
|
///
|
|
|
|
/// # Examples
|
2020-04-14 10:36:03 +00:00
|
|
|
/// ```no_run
|
2020-05-08 12:02:49 +00:00
|
|
|
/// use matrix_sdk::{Client, RoomBuilder};
|
2020-04-14 10:36:03 +00:00
|
|
|
/// # use matrix_sdk::api::r0::room::Visibility;
|
|
|
|
/// # use url::Url;
|
2020-04-14 10:42:42 +00:00
|
|
|
///
|
2020-04-14 10:36:03 +00:00
|
|
|
/// # let homeserver = Url::parse("http://example.com").unwrap();
|
2020-07-04 19:28:36 +00:00
|
|
|
/// let mut builder = RoomBuilder::new();
|
2020-07-03 19:29:10 +00:00
|
|
|
/// builder.federate(false)
|
2020-04-10 20:32:28 +00:00
|
|
|
/// .initial_state(vec![])
|
|
|
|
/// .visibility(Visibility::Public)
|
|
|
|
/// .name("name")
|
|
|
|
/// .room_version("v1.0");
|
|
|
|
///
|
2020-05-22 13:23:58 +00:00
|
|
|
/// let mut cli = Client::new(homeserver).unwrap();
|
2020-04-14 10:36:03 +00:00
|
|
|
/// # use futures::executor::block_on;
|
|
|
|
/// # block_on(async {
|
2020-04-13 18:08:51 +00:00
|
|
|
/// assert!(cli.create_room(builder).await.is_ok());
|
2020-04-14 10:36:03 +00:00
|
|
|
/// # });
|
2020-04-10 20:32:28 +00:00
|
|
|
/// ```
|
|
|
|
pub async fn create_room<R: Into<create_room::Request>>(
|
2020-04-15 12:29:34 +00:00
|
|
|
&self,
|
2020-04-10 20:32:28 +00:00
|
|
|
room: R,
|
|
|
|
) -> Result<create_room::Response> {
|
|
|
|
let request = room.into();
|
|
|
|
self.send(request).await
|
|
|
|
}
|
|
|
|
|
2020-04-14 10:36:03 +00:00
|
|
|
/// Get messages starting at a specific sync point using the
|
|
|
|
/// `MessagesRequestBuilder`s `from` field as a starting point.
|
2020-04-11 12:46:45 +00:00
|
|
|
///
|
2020-04-14 10:36:03 +00:00
|
|
|
/// Sends a request to `/_matrix/client/r0/rooms/{room_id}/messages` and
|
|
|
|
/// returns a `get_message_events::IncomingResponse` that contains chunks
|
|
|
|
/// of `RoomEvents`.
|
2020-04-11 12:46:45 +00:00
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
2020-06-01 11:50:45 +00:00
|
|
|
/// * `request` - The easiest way to create this request is using the
|
2020-04-14 10:36:03 +00:00
|
|
|
/// `MessagesRequestBuilder`.
|
2020-04-13 18:46:54 +00:00
|
|
|
///
|
2020-04-13 18:08:51 +00:00
|
|
|
/// # Examples
|
2020-04-14 10:36:03 +00:00
|
|
|
/// ```no_run
|
|
|
|
/// # use std::convert::TryFrom;
|
2020-05-08 12:02:49 +00:00
|
|
|
/// use matrix_sdk::{Client, MessagesRequestBuilder};
|
2020-04-14 10:36:03 +00:00
|
|
|
/// # use matrix_sdk::identifiers::RoomId;
|
|
|
|
/// # use matrix_sdk::api::r0::filter::RoomEventFilter;
|
|
|
|
/// # use matrix_sdk::api::r0::message::get_message_events::Direction;
|
|
|
|
/// # use url::Url;
|
2020-04-29 08:40:27 +00:00
|
|
|
/// # use matrix_sdk::js_int::UInt;
|
2020-04-14 10:42:42 +00:00
|
|
|
///
|
2020-04-14 10:36:03 +00:00
|
|
|
/// # let homeserver = Url::parse("http://example.com").unwrap();
|
2020-06-24 11:46:40 +00:00
|
|
|
/// let mut builder = MessagesRequestBuilder::new(
|
|
|
|
/// RoomId::try_from("!roomid:example.com").unwrap(),
|
|
|
|
/// "t47429-4392820_219380_26003_2265".to_string(),
|
|
|
|
/// );
|
|
|
|
///
|
|
|
|
/// builder.to("t4357353_219380_26003_2265".to_string())
|
2020-04-13 18:08:51 +00:00
|
|
|
/// .direction(Direction::Backward)
|
2020-06-24 11:46:40 +00:00
|
|
|
/// .limit(10);
|
2020-04-13 18:46:54 +00:00
|
|
|
///
|
2020-05-22 13:23:58 +00:00
|
|
|
/// let mut client = Client::new(homeserver).unwrap();
|
2020-04-14 10:36:03 +00:00
|
|
|
/// # use futures::executor::block_on;
|
|
|
|
/// # block_on(async {
|
2020-05-22 13:23:58 +00:00
|
|
|
/// assert!(client.room_messages(builder).await.is_ok());
|
2020-04-14 10:36:03 +00:00
|
|
|
/// # });
|
2020-04-13 18:08:51 +00:00
|
|
|
/// ```
|
2020-04-12 12:21:23 +00:00
|
|
|
pub async fn room_messages<R: Into<get_message_events::Request>>(
|
2020-04-15 12:29:34 +00:00
|
|
|
&self,
|
2020-04-11 12:46:45 +00:00
|
|
|
request: R,
|
2020-04-23 08:52:47 +00:00
|
|
|
) -> Result<get_message_events::Response> {
|
2020-04-11 12:46:45 +00:00
|
|
|
let req = request.into();
|
2020-04-13 18:08:51 +00:00
|
|
|
self.send(req).await
|
2020-04-11 12:46:45 +00:00
|
|
|
}
|
|
|
|
|
2020-05-05 12:33:31 +00:00
|
|
|
/// Send a request to notify the room of a user typing.
|
|
|
|
///
|
|
|
|
/// Returns a `create_typing_event::Response`, an empty response.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
2020-05-06 12:36:28 +00:00
|
|
|
/// * `room_id` - The `RoomId` the user is typing in.
|
2020-05-05 12:33:31 +00:00
|
|
|
///
|
2020-05-06 12:36:28 +00:00
|
|
|
/// * `user_id` - The `UserId` of the user that is typing.
|
2020-05-05 12:33:31 +00:00
|
|
|
///
|
2020-05-06 12:36:28 +00:00
|
|
|
/// * `typing` - Whether the user is typing, if false `timeout` is not needed.
|
2020-05-05 12:33:31 +00:00
|
|
|
///
|
2020-05-06 12:36:28 +00:00
|
|
|
/// * `timeout` - Length of time in milliseconds to mark user is typing.
|
2020-05-05 12:33:31 +00:00
|
|
|
pub async fn typing_notice(
|
|
|
|
&self,
|
|
|
|
room_id: &RoomId,
|
|
|
|
user_id: &UserId,
|
|
|
|
typing: bool,
|
|
|
|
timeout: Option<Duration>,
|
|
|
|
) -> Result<create_typing_event::Response> {
|
|
|
|
let request = create_typing_event::Request {
|
|
|
|
room_id: room_id.clone(),
|
|
|
|
user_id: user_id.clone(),
|
|
|
|
timeout,
|
|
|
|
typing,
|
|
|
|
};
|
|
|
|
self.send(request).await
|
|
|
|
}
|
|
|
|
|
2020-05-22 22:05:12 +00:00
|
|
|
/// Send a request to notify the room the user has read specific event.
|
2020-05-05 12:33:31 +00:00
|
|
|
///
|
|
|
|
/// Returns a `create_receipt::Response`, an empty response.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
2020-05-25 11:46:50 +00:00
|
|
|
/// * `room_id` - The `RoomId` the user is currently in.
|
2020-05-05 12:33:31 +00:00
|
|
|
///
|
2020-05-25 11:46:50 +00:00
|
|
|
/// * `event_id` - The `EventId` specifies the event to set the read receipt on.
|
2020-05-05 12:33:31 +00:00
|
|
|
pub async fn read_receipt(
|
|
|
|
&self,
|
|
|
|
room_id: &RoomId,
|
|
|
|
event_id: &EventId,
|
|
|
|
) -> Result<create_receipt::Response> {
|
|
|
|
let request = create_receipt::Request {
|
|
|
|
room_id: room_id.clone(),
|
|
|
|
event_id: event_id.clone(),
|
|
|
|
receipt_type: create_receipt::ReceiptType::Read,
|
|
|
|
};
|
|
|
|
self.send(request).await
|
|
|
|
}
|
|
|
|
|
2020-05-22 22:05:12 +00:00
|
|
|
/// Send a request to notify the room user has read up to specific event.
|
|
|
|
///
|
2020-05-25 11:46:50 +00:00
|
|
|
/// Returns a `set_read_marker::Response`, an empty response.
|
2020-05-22 22:05:12 +00:00
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
2020-05-25 11:46:50 +00:00
|
|
|
/// * room_id - The `RoomId` the user is currently in.
|
2020-05-22 22:05:12 +00:00
|
|
|
///
|
|
|
|
/// * fully_read - The `EventId` of the event the user has read to.
|
|
|
|
///
|
2020-05-25 11:46:50 +00:00
|
|
|
/// * read_receipt - An `EventId` to specify the event to set the read receipt on.
|
2020-05-22 22:05:12 +00:00
|
|
|
pub async fn read_marker(
|
|
|
|
&self,
|
|
|
|
room_id: &RoomId,
|
|
|
|
fully_read: &EventId,
|
|
|
|
read_receipt: Option<&EventId>,
|
|
|
|
) -> Result<set_read_marker::Response> {
|
|
|
|
let request = set_read_marker::Request {
|
|
|
|
room_id: room_id.clone(),
|
|
|
|
fully_read: fully_read.clone(),
|
|
|
|
read_receipt: read_receipt.cloned(),
|
|
|
|
};
|
|
|
|
self.send(request).await
|
|
|
|
}
|
|
|
|
|
2020-06-01 11:50:45 +00:00
|
|
|
/// Send a room message to the homeserver.
|
|
|
|
///
|
|
|
|
/// Returns the parsed response from the server.
|
|
|
|
///
|
|
|
|
/// If the encryption feature is enabled this method will transparently
|
|
|
|
/// encrypt the room message if the given room is encrypted.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
|
|
|
/// * `room_id` - The id of the room that should receive the message.
|
|
|
|
///
|
|
|
|
/// * `content` - The content of the message event.
|
|
|
|
///
|
|
|
|
/// * `txn_id` - A unique `Uuid` that can be attached to a `MessageEvent` held
|
|
|
|
/// in it's unsigned field as `transaction_id`. If not given one is created for the
|
|
|
|
/// message.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
/// ```no_run
|
|
|
|
/// # use matrix_sdk::Room;
|
|
|
|
/// # use std::sync::{Arc, RwLock};
|
|
|
|
/// # use matrix_sdk::{Client, SyncSettings};
|
|
|
|
/// # use url::Url;
|
|
|
|
/// # use futures::executor::block_on;
|
2020-06-20 21:18:20 +00:00
|
|
|
/// # use matrix_sdk::identifiers::RoomId;
|
2020-06-01 11:50:45 +00:00
|
|
|
/// # use std::convert::TryFrom;
|
2020-07-25 00:23:10 +00:00
|
|
|
/// use matrix_sdk::events::room::message::{MessageEventContent, TextMessageEventContent};
|
2020-06-01 11:50:45 +00:00
|
|
|
/// # block_on(async {
|
|
|
|
/// # let homeserver = Url::parse("http://localhost:8080").unwrap();
|
|
|
|
/// # let mut client = Client::new(homeserver).unwrap();
|
|
|
|
/// # let room_id = RoomId::try_from("!test:localhost").unwrap();
|
|
|
|
/// use matrix_sdk_common::uuid::Uuid;
|
|
|
|
///
|
2020-07-25 00:23:10 +00:00
|
|
|
/// let content = MessageEventContent::Text(TextMessageEventContent::plain("Hello world"));
|
2020-06-01 11:50:45 +00:00
|
|
|
/// let txn_id = Uuid::new_v4();
|
|
|
|
/// client.room_send(&room_id, content, Some(txn_id)).await.unwrap();
|
2020-06-02 09:20:47 +00:00
|
|
|
/// # })
|
2020-06-01 11:50:45 +00:00
|
|
|
/// ```
|
|
|
|
pub async fn room_send(
|
|
|
|
&self,
|
|
|
|
room_id: &RoomId,
|
|
|
|
content: MessageEventContent,
|
|
|
|
txn_id: Option<Uuid>,
|
|
|
|
) -> Result<create_message_event::Response> {
|
|
|
|
#[allow(unused_mut)]
|
|
|
|
let mut event_type = EventType::RoomMessage;
|
|
|
|
#[allow(unused_mut)]
|
|
|
|
let mut raw_content = serde_json::value::to_raw_value(&content)?;
|
|
|
|
|
|
|
|
#[cfg(feature = "encryption")]
|
|
|
|
{
|
|
|
|
let encrypted = {
|
|
|
|
let room = self.base_client.get_joined_room(room_id).await;
|
|
|
|
|
|
|
|
match room {
|
|
|
|
Some(r) => r.read().await.is_encrypted(),
|
|
|
|
None => false,
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if encrypted {
|
|
|
|
let missing_sessions = {
|
|
|
|
let room = self.base_client.get_joined_room(room_id).await;
|
|
|
|
let room = room.as_ref().unwrap().read().await;
|
2020-06-10 16:12:27 +00:00
|
|
|
let members = room
|
|
|
|
.joined_members
|
|
|
|
.keys()
|
|
|
|
.chain(room.invited_members.keys());
|
|
|
|
self.base_client.get_missing_sessions(members).await?
|
2020-06-01 11:50:45 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
if !missing_sessions.is_empty() {
|
|
|
|
self.claim_one_time_keys(missing_sessions).await?;
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.base_client.should_share_group_session(room_id).await {
|
|
|
|
// TODO we need to make sure that only one such request is
|
|
|
|
// in flight per room at a time.
|
|
|
|
let response = self.share_group_session(room_id).await;
|
|
|
|
|
|
|
|
// If one of the responses failed invalidate the group
|
|
|
|
// session as using it would end up in undecryptable
|
|
|
|
// messages.
|
|
|
|
if let Err(r) = response {
|
|
|
|
self.base_client.invalidate_group_session(room_id).await;
|
|
|
|
return Err(r);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
raw_content = serde_json::value::to_raw_value(
|
|
|
|
&self.base_client.encrypt(room_id, content).await?,
|
|
|
|
)?;
|
|
|
|
event_type = EventType::RoomEncrypted;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let request = create_message_event::Request {
|
|
|
|
room_id: room_id.clone(),
|
|
|
|
event_type,
|
|
|
|
txn_id: txn_id.unwrap_or_else(Uuid::new_v4).to_string(),
|
|
|
|
data: raw_content,
|
|
|
|
};
|
|
|
|
|
|
|
|
let response = self.send(request).await?;
|
|
|
|
Ok(response)
|
|
|
|
}
|
|
|
|
|
2020-06-09 14:28:24 +00:00
|
|
|
/// 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 than 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
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```no_run
|
|
|
|
/// # use matrix_sdk::{Client, SyncSettings};
|
|
|
|
/// # use futures::executor::block_on;
|
|
|
|
/// # use url::Url;
|
|
|
|
/// # use std::convert::TryFrom;
|
|
|
|
/// # block_on(async {
|
|
|
|
/// # let homeserver = Url::parse("http://localhost:8080").unwrap();
|
|
|
|
/// # let mut client = Client::new(homeserver).unwrap();
|
|
|
|
/// use matrix_sdk::api::r0::profile;
|
|
|
|
/// use matrix_sdk::identifiers::UserId;
|
|
|
|
///
|
|
|
|
/// // 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 request = profile::get_profile::Request {
|
|
|
|
/// user_id: UserId::try_from("@example:localhost").unwrap(),
|
|
|
|
/// };
|
|
|
|
///
|
|
|
|
/// // Start the request using Client::send()
|
|
|
|
/// let response = client.send(request).await.unwrap();
|
|
|
|
///
|
|
|
|
/// // Check the corresponding Response struct to find out what types are
|
|
|
|
/// // returned
|
|
|
|
/// # })
|
|
|
|
/// ```
|
|
|
|
pub async fn send<Request: Endpoint<ResponseError = crate::api::Error> + Debug>(
|
|
|
|
&self,
|
|
|
|
request: Request,
|
|
|
|
) -> Result<Request::Response> {
|
2020-07-29 08:56:18 +00:00
|
|
|
self.http_client
|
2020-07-29 11:50:01 +00:00
|
|
|
.send(request, self.base_client.session().clone())
|
2020-07-29 08:56:18 +00:00
|
|
|
.await
|
2020-06-01 11:50:45 +00:00
|
|
|
}
|
|
|
|
|
2020-06-02 18:57:56 +00:00
|
|
|
/// Send an arbitrary request to the server, without updating client state.
|
|
|
|
///
|
|
|
|
/// This version allows the client to make registration requests.
|
|
|
|
///
|
|
|
|
/// **Warning:** Because this method *does not* update the client state, it is
|
|
|
|
/// important to make sure than 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` - This version of send is for dealing with types that return
|
|
|
|
/// a `UiaaResponse` as the `Endpoint<ResponseError = UiaaResponse>` associated type.
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
/// ```
|
|
|
|
/// # use std::convert::TryFrom;
|
|
|
|
/// # use matrix_sdk::{Client, RegistrationBuilder};
|
|
|
|
/// # use matrix_sdk::api::r0::account::register::{RegistrationKind, Request};
|
|
|
|
/// # use matrix_sdk::identifiers::DeviceId;
|
|
|
|
/// # use url::Url;
|
|
|
|
/// # let homeserver = Url::parse("http://example.com").unwrap();
|
|
|
|
/// # let mut rt = tokio::runtime::Runtime::new().unwrap();
|
|
|
|
/// # rt.block_on(async {
|
|
|
|
/// let mut builder = RegistrationBuilder::default();
|
|
|
|
/// builder.password("pass")
|
|
|
|
/// .username("user")
|
|
|
|
/// .kind(RegistrationKind::User);
|
|
|
|
/// let mut client = Client::new(homeserver).unwrap();
|
|
|
|
/// let req: Request = builder.into();
|
|
|
|
/// client.send_uiaa(req).await;
|
|
|
|
/// # })
|
|
|
|
/// ```
|
|
|
|
pub async fn send_uiaa<Request: Endpoint<ResponseError = UiaaResponse> + Debug>(
|
2020-06-02 11:55:24 +00:00
|
|
|
&self,
|
|
|
|
request: Request,
|
|
|
|
) -> Result<Request::Response> {
|
2020-07-29 08:56:18 +00:00
|
|
|
self.http_client
|
2020-07-29 11:50:01 +00:00
|
|
|
.send_uiaa(request, self.base_client.session().clone())
|
2020-07-29 08:56:18 +00:00
|
|
|
.await
|
2020-06-02 11:55:24 +00:00
|
|
|
}
|
|
|
|
|
2020-03-30 18:18:08 +00:00
|
|
|
/// Synchronize the client's state with the latest state on the server.
|
2020-02-21 13:29:25 +00:00
|
|
|
///
|
2020-04-23 23:37:27 +00:00
|
|
|
/// If a `StateStore` is provided and this is the initial sync state will
|
|
|
|
/// be loaded from the state store.
|
|
|
|
///
|
2020-02-21 13:29:25 +00:00
|
|
|
/// # Arguments
|
|
|
|
///
|
|
|
|
/// * `sync_settings` - Settings for the sync call.
|
2020-03-19 12:55:04 +00:00
|
|
|
#[instrument]
|
2020-07-16 13:04:26 +00:00
|
|
|
pub async fn sync(&self, sync_settings: SyncSettings) -> Result<sync_events::Response> {
|
2019-10-23 20:47:00 +00:00
|
|
|
let request = sync_events::Request {
|
2020-07-16 13:13:35 +00:00
|
|
|
filter: sync_settings.filter,
|
2019-10-23 20:47:00 +00:00
|
|
|
since: sync_settings.token,
|
|
|
|
full_state: sync_settings.full_state,
|
2020-07-26 14:58:27 +00:00
|
|
|
set_presence: PresenceState::Online,
|
2019-10-23 20:47:00 +00:00
|
|
|
timeout: sync_settings.timeout,
|
|
|
|
};
|
|
|
|
|
2020-03-12 14:41:11 +00:00
|
|
|
let mut response = self.send(request).await?;
|
2019-10-23 20:47:00 +00:00
|
|
|
|
2020-05-06 13:36:55 +00:00
|
|
|
self.base_client
|
|
|
|
.receive_sync_response(&mut response)
|
|
|
|
.await?;
|
2020-05-05 20:13:14 +00:00
|
|
|
|
|
|
|
Ok(response)
|
|
|
|
}
|
|
|
|
|
2020-02-21 13:29:25 +00:00
|
|
|
/// 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.
|
2020-03-02 10:31:03 +00:00
|
|
|
///
|
2020-02-21 13:29:25 +00:00
|
|
|
/// * `callback` - A callback that will be called every time a successful
|
|
|
|
/// response has been fetched from the server.
|
|
|
|
///
|
|
|
|
/// # 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.
|
|
|
|
///
|
2020-03-02 10:31:03 +00:00
|
|
|
/// ```compile_fail,E0658
|
|
|
|
/// # use matrix_sdk::events::{
|
|
|
|
/// # collections::all::RoomEvent,
|
|
|
|
/// # room::message::{MessageEvent, MessageEventContent, TextMessageEventContent},
|
|
|
|
/// # EventResult,
|
|
|
|
/// # };
|
|
|
|
/// # use matrix_sdk::Room;
|
|
|
|
/// # use std::sync::{Arc, RwLock};
|
2020-05-08 12:02:49 +00:00
|
|
|
/// # use matrix_sdk::{Client, SyncSettings};
|
2020-03-02 10:31:03 +00:00
|
|
|
/// # use url::Url;
|
|
|
|
/// # use futures::executor::block_on;
|
|
|
|
/// # block_on(async {
|
|
|
|
/// # let homeserver = Url::parse("http://localhost:8080").unwrap();
|
2020-05-08 12:02:49 +00:00
|
|
|
/// # let mut client = Client::new(homeserver, None).unwrap();
|
2020-03-02 10:31:03 +00:00
|
|
|
///
|
|
|
|
/// use async_std::sync::channel;
|
|
|
|
///
|
|
|
|
/// let (tx, rx) = channel(100);
|
|
|
|
///
|
|
|
|
/// let sync_channel = &tx;
|
|
|
|
/// let sync_settings = SyncSettings::new()
|
|
|
|
/// .timeout(30_000)
|
|
|
|
/// .unwrap();
|
|
|
|
///
|
2020-02-21 13:29:25 +00:00
|
|
|
/// client
|
|
|
|
/// .sync_forever(sync_settings, async move |response| {
|
|
|
|
/// let channel = sync_channel;
|
2020-03-02 10:31:03 +00:00
|
|
|
///
|
2020-02-21 13:29:25 +00:00
|
|
|
/// for (room_id, room) in response.rooms.join {
|
|
|
|
/// for event in room.timeline.events {
|
|
|
|
/// if let EventResult::Ok(e) = event {
|
|
|
|
/// channel.send(e).await;
|
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
/// })
|
|
|
|
/// .await;
|
2020-03-02 10:31:03 +00:00
|
|
|
/// })
|
2020-02-21 13:29:25 +00:00
|
|
|
/// ```
|
2020-03-19 12:55:04 +00:00
|
|
|
#[instrument(skip(callback))]
|
2019-12-04 21:33:26 +00:00
|
|
|
pub async fn sync_forever<C>(
|
2020-04-15 12:29:34 +00:00
|
|
|
&self,
|
2019-12-04 21:33:26 +00:00
|
|
|
sync_settings: SyncSettings,
|
2020-05-29 07:29:53 +00:00
|
|
|
callback: impl Fn(sync_events::Response) -> C,
|
2019-12-04 21:33:26 +00:00
|
|
|
) where
|
|
|
|
C: Future<Output = ()>,
|
|
|
|
{
|
|
|
|
let mut sync_settings = sync_settings;
|
2020-07-16 22:55:55 +00:00
|
|
|
let filter = sync_settings.filter.clone();
|
2020-02-28 09:33:17 +00:00
|
|
|
let mut last_sync_time: Option<Instant> = None;
|
2019-12-04 21:33:26 +00:00
|
|
|
|
2020-05-22 13:23:58 +00:00
|
|
|
if sync_settings.token.is_none() {
|
|
|
|
sync_settings.token = self.sync_token().await;
|
|
|
|
}
|
|
|
|
|
2019-12-04 21:33:26 +00:00
|
|
|
loop {
|
2020-07-16 13:04:26 +00:00
|
|
|
let response = self.sync(sync_settings.clone()).await;
|
2019-12-04 21:33:26 +00:00
|
|
|
|
2020-07-15 07:43:58 +00:00
|
|
|
let response = match response {
|
|
|
|
Ok(r) => r,
|
|
|
|
Err(e) => {
|
|
|
|
error!("Received an invalid response: {}", e);
|
|
|
|
sleep::new(Duration::from_secs(1)).await;
|
|
|
|
continue;
|
|
|
|
}
|
2019-12-04 21:33:26 +00:00
|
|
|
};
|
|
|
|
|
2020-03-11 10:43:58 +00:00
|
|
|
#[cfg(feature = "encryption")]
|
|
|
|
{
|
2020-05-06 13:36:55 +00:00
|
|
|
if self.base_client.should_upload_keys().await {
|
2020-05-05 13:29:25 +00:00
|
|
|
let response = self.keys_upload().await;
|
|
|
|
|
|
|
|
if let Err(e) = response {
|
|
|
|
warn!("Error while uploading E2EE keys {:?}", e);
|
|
|
|
}
|
2020-03-11 10:43:58 +00:00
|
|
|
}
|
2020-04-01 13:37:00 +00:00
|
|
|
|
2020-05-06 13:36:55 +00:00
|
|
|
if self.base_client.should_query_keys().await {
|
2020-05-05 13:29:25 +00:00
|
|
|
let response = self.keys_query().await;
|
|
|
|
|
|
|
|
if let Err(e) = response {
|
|
|
|
warn!("Error while querying device keys {:?}", e);
|
|
|
|
}
|
2020-04-01 13:37:00 +00:00
|
|
|
}
|
2020-07-30 13:53:55 +00:00
|
|
|
|
|
|
|
for request in self.base_client.outgoing_to_device_requests().await {
|
|
|
|
let transaction_id = request.txn_id.clone();
|
|
|
|
|
2020-07-31 13:18:03 +00:00
|
|
|
if self.send(request).await.is_ok() {
|
2020-07-30 13:53:55 +00:00
|
|
|
self.base_client
|
|
|
|
.mark_to_device_request_as_sent(&transaction_id)
|
|
|
|
.await;
|
|
|
|
}
|
|
|
|
}
|
2020-03-11 10:43:58 +00:00
|
|
|
}
|
|
|
|
|
2020-05-12 12:23:57 +00:00
|
|
|
callback(response).await;
|
|
|
|
|
2020-02-28 09:33:17 +00:00
|
|
|
let now = Instant::now();
|
|
|
|
|
|
|
|
// If the last sync happened less than a second ago, sleep for a
|
|
|
|
// while to not hammer out requests if the server doesn't respect
|
|
|
|
// the sync timeout.
|
2020-05-24 14:05:02 +00:00
|
|
|
if let Some(t) = last_sync_time {
|
|
|
|
if now - t <= Duration::from_secs(1) {
|
|
|
|
sleep::new(Duration::from_secs(1)).await;
|
2020-02-28 09:33:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
last_sync_time = Some(now);
|
|
|
|
|
2020-03-24 15:18:56 +00:00
|
|
|
sync_settings = SyncSettings::new().timeout(DEFAULT_SYNC_TIMEOUT).token(
|
|
|
|
self.sync_token()
|
|
|
|
.await
|
|
|
|
.expect("No sync token found after initial sync"),
|
|
|
|
);
|
2020-07-16 22:55:55 +00:00
|
|
|
if let Some(f) = filter.as_ref() {
|
|
|
|
sync_settings = sync_settings.filter(f.clone());
|
|
|
|
}
|
2019-12-04 21:33:26 +00:00
|
|
|
}
|
|
|
|
}
|
2019-11-26 19:34:11 +00:00
|
|
|
|
2020-04-03 08:27:30 +00:00
|
|
|
/// Claim one-time keys creating new Olm sessions.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
|
|
|
/// * `users` - The list of user/device pairs that we should claim keys for.
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// Panics if the client isn't logged in, or if no encryption keys need to
|
|
|
|
/// be uploaded.
|
|
|
|
#[cfg(feature = "encryption")]
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "encryption")))]
|
|
|
|
#[instrument]
|
|
|
|
async fn claim_one_time_keys(
|
|
|
|
&self,
|
2020-07-18 12:51:19 +00:00
|
|
|
one_time_keys: BTreeMap<UserId, BTreeMap<Box<DeviceId>, KeyAlgorithm>>,
|
2020-04-03 08:27:30 +00:00
|
|
|
) -> Result<claim_keys::Response> {
|
|
|
|
let request = claim_keys::Request {
|
|
|
|
timeout: None,
|
|
|
|
one_time_keys,
|
|
|
|
};
|
|
|
|
|
|
|
|
let response = self.send(request).await?;
|
|
|
|
self.base_client
|
|
|
|
.receive_keys_claim_response(&response)
|
|
|
|
.await?;
|
|
|
|
Ok(response)
|
|
|
|
}
|
|
|
|
|
2020-04-09 14:29:03 +00:00
|
|
|
/// Share a group session for a room.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
|
|
|
/// * `room_id` - The ID of the room for which we want to share a group
|
|
|
|
/// session.
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// Panics if the client isn't logged in.
|
|
|
|
#[cfg(feature = "encryption")]
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "encryption")))]
|
|
|
|
#[instrument]
|
|
|
|
async fn share_group_session(&self, room_id: &RoomId) -> Result<()> {
|
|
|
|
let mut requests = self
|
|
|
|
.base_client
|
|
|
|
.share_group_session(room_id)
|
|
|
|
.await
|
|
|
|
.expect("Keys don't need to be uploaded");
|
|
|
|
|
|
|
|
for request in requests.drain(..) {
|
2020-04-10 09:50:18 +00:00
|
|
|
let _response: send_event_to_device::Response = self.send(request).await?;
|
2020-04-09 14:29:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-03-11 10:42:59 +00:00
|
|
|
/// Upload the E2E encryption keys.
|
|
|
|
///
|
|
|
|
/// This uploads the long lived device keys as well as the required amount
|
|
|
|
/// of one-time keys.
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// Panics if the client isn't logged in, or if no encryption keys need to
|
|
|
|
/// be uploaded.
|
|
|
|
#[cfg(feature = "encryption")]
|
2020-03-19 12:55:04 +00:00
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "encryption")))]
|
|
|
|
#[instrument]
|
2020-03-18 13:15:56 +00:00
|
|
|
async fn keys_upload(&self) -> Result<upload_keys::Response> {
|
2020-03-11 10:42:59 +00:00
|
|
|
let (device_keys, one_time_keys) = self
|
|
|
|
.base_client
|
|
|
|
.keys_for_upload()
|
|
|
|
.await
|
|
|
|
.expect("Keys don't need to be uploaded");
|
2020-03-19 12:55:04 +00:00
|
|
|
|
|
|
|
debug!(
|
|
|
|
"Uploading encryption keys device keys: {}, one-time-keys: {}",
|
|
|
|
device_keys.is_some(),
|
|
|
|
one_time_keys.as_ref().map_or(0, |k| k.len())
|
|
|
|
);
|
|
|
|
|
2020-03-11 10:42:59 +00:00
|
|
|
let request = upload_keys::Request {
|
|
|
|
device_keys,
|
|
|
|
one_time_keys,
|
|
|
|
};
|
|
|
|
|
|
|
|
let response = self.send(request).await?;
|
|
|
|
self.base_client
|
|
|
|
.receive_keys_upload_response(&response)
|
2020-03-18 14:50:32 +00:00
|
|
|
.await?;
|
2020-03-11 10:42:59 +00:00
|
|
|
Ok(response)
|
|
|
|
}
|
|
|
|
|
2020-02-21 13:29:25 +00:00
|
|
|
/// Get the current, if any, sync token of the client.
|
|
|
|
/// This will be None if the client didn't sync at least once.
|
2020-03-11 10:42:59 +00:00
|
|
|
pub async fn sync_token(&self) -> Option<String> {
|
2020-05-06 13:36:55 +00:00
|
|
|
self.base_client.sync_token().await
|
2019-11-24 21:40:52 +00:00
|
|
|
}
|
2020-04-01 13:37:00 +00:00
|
|
|
|
|
|
|
/// Query the server for users device keys.
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// Panics if no key query needs to be done.
|
2020-04-03 08:42:03 +00:00
|
|
|
#[cfg(feature = "encryption")]
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "encryption")))]
|
|
|
|
#[instrument]
|
2020-04-01 13:37:00 +00:00
|
|
|
async fn keys_query(&self) -> Result<get_keys::Response> {
|
|
|
|
let mut users_for_query = self
|
|
|
|
.base_client
|
|
|
|
.users_for_key_query()
|
|
|
|
.await
|
|
|
|
.expect("Keys don't need to be uploaded");
|
|
|
|
|
|
|
|
debug!(
|
|
|
|
"Querying device keys device for users: {:?}",
|
|
|
|
users_for_query
|
|
|
|
);
|
|
|
|
|
2020-07-18 12:51:19 +00:00
|
|
|
let mut device_keys: BTreeMap<UserId, Vec<Box<DeviceId>>> = BTreeMap::new();
|
2020-04-01 13:37:00 +00:00
|
|
|
|
|
|
|
for user in users_for_query.drain() {
|
2020-04-03 15:00:37 +00:00
|
|
|
device_keys.insert(user, Vec::new());
|
2020-04-01 13:37:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let request = get_keys::Request {
|
|
|
|
timeout: None,
|
|
|
|
device_keys,
|
|
|
|
token: None,
|
|
|
|
};
|
|
|
|
|
|
|
|
let response = self.send(request).await?;
|
|
|
|
self.base_client
|
|
|
|
.receive_keys_query_response(&response)
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
Ok(response)
|
|
|
|
}
|
2020-07-29 12:19:47 +00:00
|
|
|
|
2020-08-04 09:41:20 +00:00
|
|
|
/// Get a `Sas` verification object with the given flow id.
|
2020-07-29 12:19:47 +00:00
|
|
|
#[cfg(feature = "encryption")]
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "encryption")))]
|
|
|
|
#[instrument]
|
|
|
|
pub async fn get_verification(&self, flow_id: &str) -> Option<Sas> {
|
|
|
|
self.base_client
|
|
|
|
.get_verification(flow_id)
|
|
|
|
.await
|
|
|
|
.map(|sas| Sas {
|
|
|
|
inner: sas,
|
|
|
|
session: self.base_client.session().clone(),
|
|
|
|
http_client: self.http_client.clone(),
|
|
|
|
homeserver: self.homeserver.clone(),
|
|
|
|
})
|
|
|
|
}
|
2019-10-23 20:47:00 +00:00
|
|
|
}
|
2020-04-07 00:59:44 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
2020-05-06 12:36:28 +00:00
|
|
|
use super::{
|
2020-06-23 21:31:58 +00:00
|
|
|
api::r0::uiaa::AuthData,
|
|
|
|
ban_user, create_receipt, create_typing_event, forget_room, get_public_rooms,
|
|
|
|
get_public_rooms_filtered::{self, Filter},
|
|
|
|
invite_user, kick_user, leave_room,
|
|
|
|
register::RegistrationKind,
|
2020-07-30 23:22:48 +00:00
|
|
|
set_read_marker, Client, ClientConfig, Invite3pid, MessageEventContent, Session,
|
|
|
|
SyncSettings, Url,
|
2020-05-06 12:36:28 +00:00
|
|
|
};
|
2020-05-07 19:21:06 +00:00
|
|
|
use crate::events::room::message::TextMessageEventContent;
|
2020-06-20 21:18:20 +00:00
|
|
|
|
2020-07-30 23:22:48 +00:00
|
|
|
use crate::{
|
|
|
|
identifiers::{EventId, RoomId, RoomIdOrAliasId, UserId},
|
|
|
|
RegistrationBuilder, RoomListFilterBuilder,
|
|
|
|
};
|
2020-05-08 07:57:42 +00:00
|
|
|
|
2020-05-07 14:22:18 +00:00
|
|
|
use matrix_sdk_base::JsonStore;
|
2020-06-22 19:40:51 +00:00
|
|
|
use matrix_sdk_test::{test_json, EventBuilder, EventsJson};
|
2020-05-06 12:36:28 +00:00
|
|
|
use mockito::{mock, Matcher};
|
2020-05-18 20:26:27 +00:00
|
|
|
use tempfile::tempdir;
|
|
|
|
|
2020-07-30 23:22:48 +00:00
|
|
|
use std::{
|
|
|
|
convert::{TryFrom, TryInto},
|
|
|
|
path::Path,
|
|
|
|
str::FromStr,
|
|
|
|
time::Duration,
|
|
|
|
};
|
2020-05-07 10:51:53 +00:00
|
|
|
|
2020-05-18 20:26:27 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn test_join_leave_room() {
|
|
|
|
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
|
|
|
|
|
|
|
|
let room_id = RoomId::try_from("!SVkFJHzfwvuaIEawgC:localhost").unwrap();
|
|
|
|
|
|
|
|
let session = Session {
|
|
|
|
access_token: "1234".to_owned(),
|
|
|
|
user_id: UserId::try_from("@example:localhost").unwrap(),
|
2020-07-22 18:43:47 +00:00
|
|
|
device_id: "DEVICEID".into(),
|
2020-05-18 20:26:27 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let _m = mock(
|
|
|
|
"GET",
|
|
|
|
Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_string()),
|
|
|
|
)
|
|
|
|
.with_status(200)
|
2020-06-22 19:40:51 +00:00
|
|
|
.with_body(test_json::SYNC.to_string())
|
2020-05-18 20:26:27 +00:00
|
|
|
.create();
|
|
|
|
|
|
|
|
let dir = tempdir().unwrap();
|
|
|
|
let path: &Path = dir.path();
|
|
|
|
let store = Box::new(JsonStore::open(path).unwrap());
|
|
|
|
|
|
|
|
let config = ClientConfig::default().state_store(store);
|
2020-05-22 13:23:58 +00:00
|
|
|
let client = Client::new_with_config(homeserver.clone(), config).unwrap();
|
|
|
|
client.restore_login(session.clone()).await.unwrap();
|
2020-05-18 20:26:27 +00:00
|
|
|
|
|
|
|
let room = client.get_joined_room(&room_id).await;
|
|
|
|
assert!(room.is_none());
|
|
|
|
|
2020-07-16 13:04:26 +00:00
|
|
|
client.sync(SyncSettings::default()).await.unwrap();
|
2020-05-18 20:26:27 +00:00
|
|
|
|
|
|
|
let room = client.get_left_room(&room_id).await;
|
|
|
|
assert!(room.is_none());
|
|
|
|
|
|
|
|
let room = client.get_joined_room(&room_id).await;
|
|
|
|
assert!(room.is_some());
|
|
|
|
|
|
|
|
// test store reloads with correct room state from JsonStore
|
|
|
|
let store = Box::new(JsonStore::open(path).unwrap());
|
|
|
|
let config = ClientConfig::default().state_store(store);
|
2020-05-22 13:23:58 +00:00
|
|
|
let joined_client = Client::new_with_config(homeserver, config).unwrap();
|
|
|
|
joined_client.restore_login(session).await.unwrap();
|
2020-05-18 20:26:27 +00:00
|
|
|
|
|
|
|
// joined room reloaded from state store
|
2020-07-16 13:04:26 +00:00
|
|
|
joined_client.sync(SyncSettings::default()).await.unwrap();
|
2020-05-18 20:26:27 +00:00
|
|
|
let room = joined_client.get_joined_room(&room_id).await;
|
|
|
|
assert!(room.is_some());
|
|
|
|
|
|
|
|
let _m = mock(
|
|
|
|
"GET",
|
|
|
|
Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_string()),
|
|
|
|
)
|
|
|
|
.with_status(200)
|
2020-06-22 19:40:51 +00:00
|
|
|
.with_body(test_json::LEAVE_SYNC_EVENT.to_string())
|
2020-05-18 20:26:27 +00:00
|
|
|
.create();
|
|
|
|
|
2020-07-16 13:04:26 +00:00
|
|
|
joined_client.sync(SyncSettings::default()).await.unwrap();
|
2020-05-18 20:26:27 +00:00
|
|
|
|
|
|
|
let room = joined_client.get_joined_room(&room_id).await;
|
|
|
|
assert!(room.is_none());
|
|
|
|
|
|
|
|
let room = joined_client.get_left_room(&room_id).await;
|
|
|
|
assert!(room.is_some());
|
|
|
|
}
|
|
|
|
|
2020-05-07 10:51:53 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn account_data() {
|
|
|
|
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
|
|
|
|
|
|
|
|
let session = Session {
|
|
|
|
access_token: "1234".to_owned(),
|
|
|
|
user_id: UserId::try_from("@example:example.com").unwrap(),
|
2020-07-22 18:43:47 +00:00
|
|
|
device_id: "DEVICEID".into(),
|
2020-05-07 10:51:53 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let _m = mock(
|
|
|
|
"GET",
|
|
|
|
Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_string()),
|
|
|
|
)
|
|
|
|
.with_status(200)
|
2020-06-22 19:40:51 +00:00
|
|
|
.with_body(test_json::SYNC.to_string())
|
2020-05-07 10:51:53 +00:00
|
|
|
.create();
|
|
|
|
|
2020-05-22 13:23:58 +00:00
|
|
|
let client = Client::new(homeserver).unwrap();
|
|
|
|
client.restore_login(session).await.unwrap();
|
2020-05-07 10:51:53 +00:00
|
|
|
|
|
|
|
let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000));
|
|
|
|
|
2020-07-16 13:04:26 +00:00
|
|
|
let _response = client.sync(sync_settings).await.unwrap();
|
2020-05-07 10:51:53 +00:00
|
|
|
|
2020-05-07 14:22:18 +00:00
|
|
|
// let bc = &client.base_client;
|
|
|
|
// let ignored_users = bc.ignored_users.read().await;
|
|
|
|
// assert_eq!(1, ignored_users.len())
|
2020-05-07 10:51:53 +00:00
|
|
|
}
|
2020-04-07 00:59:44 +00:00
|
|
|
|
2020-05-08 07:57:42 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn room_creation() {
|
2020-05-13 10:47:24 +00:00
|
|
|
let session = Session {
|
2020-05-08 07:57:42 +00:00
|
|
|
access_token: "12345".to_owned(),
|
|
|
|
user_id: UserId::try_from("@example:localhost").unwrap(),
|
2020-07-22 18:43:47 +00:00
|
|
|
device_id: "DEVICEID".into(),
|
2020-05-08 07:57:42 +00:00
|
|
|
};
|
|
|
|
let homeserver = url::Url::parse(&mockito::server_url()).unwrap();
|
2020-05-22 13:23:58 +00:00
|
|
|
let client = Client::new(homeserver).unwrap();
|
|
|
|
client.restore_login(session).await.unwrap();
|
2020-05-08 07:57:42 +00:00
|
|
|
|
|
|
|
let mut response = EventBuilder::default()
|
2020-06-25 00:30:53 +00:00
|
|
|
.add_state_event(EventsJson::Member)
|
|
|
|
.add_state_event(EventsJson::PowerLevels)
|
2020-05-08 07:57:42 +00:00
|
|
|
.build_sync_response();
|
|
|
|
|
|
|
|
client
|
|
|
|
.base_client
|
|
|
|
.receive_sync_response(&mut response)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
let room_id = RoomId::try_from("!SVkFJHzfwvuaIEawgC:localhost").unwrap();
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
client.homeserver(),
|
|
|
|
&Url::parse(&mockito::server_url()).unwrap()
|
|
|
|
);
|
|
|
|
|
|
|
|
let room = client.get_joined_room(&room_id).await;
|
|
|
|
assert!(room.is_some());
|
|
|
|
}
|
2020-04-16 21:03:40 +00:00
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn login_error() {
|
|
|
|
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
|
|
|
|
|
|
|
|
let _m = mock("POST", "/_matrix/client/r0/login")
|
|
|
|
.with_status(403)
|
2020-06-22 19:40:51 +00:00
|
|
|
.with_body(test_json::LOGIN_RESPONSE_ERR.to_string())
|
2020-04-16 21:03:40 +00:00
|
|
|
.create();
|
|
|
|
|
2020-05-22 13:23:58 +00:00
|
|
|
let client = Client::new(homeserver).unwrap();
|
2020-04-16 21:03:40 +00:00
|
|
|
|
|
|
|
if let Err(err) = client.login("example", "wordpass", None, None).await {
|
2020-04-29 08:40:27 +00:00
|
|
|
if let crate::Error::RumaResponse(crate::FromHttpResponseError::Http(
|
|
|
|
crate::ServerError::Known(crate::api::Error {
|
2020-04-16 21:03:40 +00:00
|
|
|
kind,
|
|
|
|
message,
|
|
|
|
status_code,
|
|
|
|
}),
|
|
|
|
)) = err
|
|
|
|
{
|
2020-04-29 08:40:27 +00:00
|
|
|
if let crate::api::error::ErrorKind::Forbidden = kind {
|
2020-04-16 21:03:40 +00:00
|
|
|
} else {
|
|
|
|
panic!(
|
|
|
|
"found the wrong `ErrorKind` {:?}, expected `Forbidden",
|
|
|
|
kind
|
|
|
|
);
|
|
|
|
}
|
|
|
|
assert_eq!(message, "Invalid password".to_string());
|
|
|
|
assert_eq!(status_code, http::StatusCode::from_u16(403).unwrap());
|
|
|
|
} else {
|
|
|
|
panic!(
|
|
|
|
"found the wrong `Error` type {:?}, expected `Error::RumaResponse",
|
|
|
|
err
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
panic!("this request should return an `Err` variant")
|
|
|
|
}
|
|
|
|
}
|
2020-05-06 12:36:28 +00:00
|
|
|
|
2020-06-02 18:57:56 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn register_error() {
|
|
|
|
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
|
|
|
|
|
|
|
|
let _m = mock("POST", "/_matrix/client/r0/register")
|
|
|
|
.with_status(403)
|
2020-06-22 19:40:51 +00:00
|
|
|
.with_body(test_json::REGISTRATION_RESPONSE_ERR.to_string())
|
2020-06-02 18:57:56 +00:00
|
|
|
.create();
|
|
|
|
|
|
|
|
let mut user = RegistrationBuilder::default();
|
|
|
|
|
|
|
|
user.username("user")
|
|
|
|
.password("password")
|
|
|
|
.auth(AuthData::FallbackAcknowledgement {
|
|
|
|
session: "foobar".to_string(),
|
|
|
|
})
|
|
|
|
.kind(RegistrationKind::User);
|
|
|
|
|
|
|
|
let client = Client::new(homeserver).unwrap();
|
|
|
|
|
|
|
|
if let Err(err) = client.register_user(user).await {
|
|
|
|
if let crate::Error::UiaaError(crate::FromHttpResponseError::Http(
|
|
|
|
// TODO this should be a UiaaError need to investigate
|
|
|
|
crate::ServerError::Unknown(e),
|
|
|
|
)) = err
|
|
|
|
{
|
|
|
|
assert!(e.to_string().starts_with("EOF while parsing"))
|
|
|
|
} else {
|
|
|
|
panic!(
|
|
|
|
"found the wrong `Error` type {:#?}, expected `ServerError::Unknown",
|
|
|
|
err
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
panic!("this request should return an `Err` variant")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-06 12:36:28 +00:00
|
|
|
#[tokio::test]
|
2020-05-07 19:21:06 +00:00
|
|
|
async fn join_room_by_id() {
|
2020-05-06 12:36:28 +00:00
|
|
|
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
|
|
|
|
|
|
|
|
let session = Session {
|
|
|
|
access_token: "1234".to_owned(),
|
|
|
|
user_id: UserId::try_from("@example:localhost").unwrap(),
|
2020-07-22 18:43:47 +00:00
|
|
|
device_id: "DEVICEID".into(),
|
2020-05-06 12:36:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let _m = mock(
|
|
|
|
"POST",
|
|
|
|
Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/join".to_string()),
|
|
|
|
)
|
|
|
|
.with_status(200)
|
2020-06-22 19:40:51 +00:00
|
|
|
.with_body(test_json::ROOM_ID.to_string())
|
2020-05-06 12:36:28 +00:00
|
|
|
.create();
|
|
|
|
|
2020-05-22 13:23:58 +00:00
|
|
|
let client = Client::new(homeserver).unwrap();
|
|
|
|
client.restore_login(session).await.unwrap();
|
2020-05-06 12:36:28 +00:00
|
|
|
let room_id = RoomId::try_from("!testroom:example.org").unwrap();
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
// this is the `join_by_room_id::Response` but since no PartialEq we check the RoomId field
|
|
|
|
client.join_room_by_id(&room_id).await.unwrap().room_id,
|
|
|
|
room_id
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-05-15 10:32:36 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn join_room_by_id_or_alias() {
|
|
|
|
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
|
|
|
|
|
|
|
|
let session = Session {
|
|
|
|
access_token: "1234".to_owned(),
|
|
|
|
user_id: UserId::try_from("@example:localhost").unwrap(),
|
2020-07-22 18:43:47 +00:00
|
|
|
device_id: "DEVICEID".into(),
|
2020-05-15 10:32:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let _m = mock(
|
|
|
|
"POST",
|
|
|
|
Matcher::Regex(r"^/_matrix/client/r0/join/".to_string()),
|
|
|
|
)
|
|
|
|
.with_status(200)
|
2020-06-22 19:40:51 +00:00
|
|
|
.with_body(test_json::ROOM_ID.to_string())
|
2020-05-15 10:32:36 +00:00
|
|
|
.create();
|
|
|
|
|
2020-05-22 13:23:58 +00:00
|
|
|
let client = Client::new(homeserver).unwrap();
|
|
|
|
client.restore_login(session).await.unwrap();
|
2020-05-15 10:32:36 +00:00
|
|
|
let room_id = RoomIdOrAliasId::try_from("!testroom:example.org").unwrap();
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
// this is the `join_by_room_id::Response` but since no PartialEq we check the RoomId field
|
|
|
|
client
|
2020-07-22 18:43:47 +00:00
|
|
|
.join_room_by_id_or_alias(&room_id, &["server.com".try_into().unwrap()])
|
2020-05-15 10:32:36 +00:00
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.room_id,
|
|
|
|
RoomId::try_from("!testroom:example.org").unwrap()
|
|
|
|
);
|
|
|
|
}
|
2020-05-07 19:21:06 +00:00
|
|
|
|
2020-05-06 12:36:28 +00:00
|
|
|
#[tokio::test]
|
|
|
|
#[allow(irrefutable_let_patterns)]
|
2020-05-07 19:21:06 +00:00
|
|
|
async fn invite_user_by_id() {
|
2020-05-06 12:36:28 +00:00
|
|
|
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
|
|
|
|
let user = UserId::try_from("@example:localhost").unwrap();
|
|
|
|
let room_id = RoomId::try_from("!testroom:example.org").unwrap();
|
|
|
|
|
|
|
|
let session = Session {
|
|
|
|
access_token: "1234".to_owned(),
|
|
|
|
user_id: user.clone(),
|
2020-07-22 18:43:47 +00:00
|
|
|
device_id: "DEVICEID".into(),
|
2020-05-06 12:36:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let _m = mock(
|
|
|
|
"POST",
|
|
|
|
Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/invite".to_string()),
|
|
|
|
)
|
|
|
|
.with_status(200)
|
2020-06-22 19:40:51 +00:00
|
|
|
.with_body(test_json::LOGOUT.to_string())
|
2020-05-06 12:36:28 +00:00
|
|
|
.create();
|
|
|
|
|
2020-05-22 13:23:58 +00:00
|
|
|
let client = Client::new(homeserver).unwrap();
|
|
|
|
client.restore_login(session).await.unwrap();
|
2020-05-06 12:36:28 +00:00
|
|
|
|
|
|
|
if let invite_user::Response = client.invite_user_by_id(&room_id, &user).await.unwrap() {}
|
|
|
|
}
|
|
|
|
|
2020-05-07 19:21:06 +00:00
|
|
|
#[tokio::test]
|
|
|
|
#[allow(irrefutable_let_patterns)]
|
|
|
|
async fn invite_user_by_3pid() {
|
|
|
|
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
|
|
|
|
let user = UserId::try_from("@example:localhost").unwrap();
|
|
|
|
let room_id = RoomId::try_from("!testroom:example.org").unwrap();
|
|
|
|
|
|
|
|
let session = Session {
|
|
|
|
access_token: "1234".to_owned(),
|
|
|
|
user_id: user.clone(),
|
2020-07-22 18:43:47 +00:00
|
|
|
device_id: "DEVICEID".into(),
|
2020-05-07 19:21:06 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let _m = mock(
|
|
|
|
"POST",
|
|
|
|
Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/invite".to_string()),
|
|
|
|
)
|
|
|
|
.with_status(200)
|
2020-06-22 19:40:51 +00:00
|
|
|
// empty JSON object
|
|
|
|
.with_body(test_json::LOGOUT.to_string())
|
2020-05-07 19:21:06 +00:00
|
|
|
.create();
|
|
|
|
|
2020-05-22 13:23:58 +00:00
|
|
|
let client = Client::new(homeserver).unwrap();
|
|
|
|
client.restore_login(session).await.unwrap();
|
2020-05-07 19:21:06 +00:00
|
|
|
|
|
|
|
if let invite_user::Response = client
|
|
|
|
.invite_user_by_3pid(
|
|
|
|
&room_id,
|
|
|
|
&Invite3pid {
|
|
|
|
id_server: "example.org".to_string(),
|
|
|
|
id_access_token: "IdToken".to_string(),
|
|
|
|
medium: crate::api::r0::thirdparty::Medium::Email,
|
|
|
|
address: "address".to_string(),
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
{}
|
|
|
|
}
|
|
|
|
|
2020-06-23 21:31:58 +00:00
|
|
|
#[tokio::test]
|
|
|
|
#[allow(irrefutable_let_patterns)]
|
|
|
|
async fn room_search_all() {
|
|
|
|
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
|
|
|
|
|
|
|
|
let _m = mock(
|
|
|
|
"GET",
|
|
|
|
Matcher::Regex(r"^/_matrix/client/r0/publicRooms".to_string()),
|
|
|
|
)
|
|
|
|
.with_status(200)
|
2020-06-25 12:31:51 +00:00
|
|
|
.with_body(test_json::PUBLIC_ROOMS.to_string())
|
2020-06-23 21:31:58 +00:00
|
|
|
.create();
|
|
|
|
|
|
|
|
let client = Client::new(homeserver).unwrap();
|
|
|
|
|
2020-06-24 11:46:40 +00:00
|
|
|
if let get_public_rooms::Response { chunk, .. } =
|
|
|
|
client.public_rooms(Some(10), None, None).await.unwrap()
|
2020-06-23 21:31:58 +00:00
|
|
|
{
|
|
|
|
assert_eq!(chunk.len(), 1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
#[allow(irrefutable_let_patterns)]
|
|
|
|
async fn room_search_filtered() {
|
|
|
|
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
|
|
|
|
let user = UserId::try_from("@example:localhost").unwrap();
|
|
|
|
|
|
|
|
let session = Session {
|
|
|
|
access_token: "1234".to_owned(),
|
|
|
|
user_id: user.clone(),
|
2020-07-22 18:43:47 +00:00
|
|
|
device_id: "DEVICEID".into(),
|
2020-06-23 21:31:58 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let _m = mock(
|
|
|
|
"POST",
|
|
|
|
Matcher::Regex(r"^/_matrix/client/r0/publicRooms".to_string()),
|
|
|
|
)
|
|
|
|
.with_status(200)
|
2020-06-25 12:31:51 +00:00
|
|
|
.with_body(test_json::PUBLIC_ROOMS.to_string())
|
2020-06-23 21:31:58 +00:00
|
|
|
.create();
|
|
|
|
|
|
|
|
let client = Client::new(homeserver).unwrap();
|
|
|
|
client.restore_login(session).await.unwrap();
|
|
|
|
|
|
|
|
let generic_search_term = Some("cheese".to_string());
|
2020-06-24 11:46:40 +00:00
|
|
|
let mut request = RoomListFilterBuilder::default();
|
2020-06-23 21:31:58 +00:00
|
|
|
request.filter(Filter {
|
|
|
|
generic_search_term,
|
|
|
|
});
|
|
|
|
|
|
|
|
if let get_public_rooms_filtered::Response { chunk, .. } =
|
2020-06-24 11:46:40 +00:00
|
|
|
client.public_rooms_filtered(request).await.unwrap()
|
2020-06-23 21:31:58 +00:00
|
|
|
{
|
|
|
|
assert_eq!(chunk.len(), 1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-06 12:36:28 +00:00
|
|
|
#[tokio::test]
|
|
|
|
#[allow(irrefutable_let_patterns)]
|
|
|
|
async fn leave_room() {
|
|
|
|
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
|
|
|
|
|
|
|
|
let session = Session {
|
|
|
|
access_token: "1234".to_owned(),
|
|
|
|
user_id: UserId::try_from("@example:localhost").unwrap(),
|
2020-07-22 18:43:47 +00:00
|
|
|
device_id: "DEVICEID".into(),
|
2020-05-06 12:36:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let _m = mock(
|
|
|
|
"POST",
|
|
|
|
Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/leave".to_string()),
|
|
|
|
)
|
|
|
|
.with_status(200)
|
|
|
|
// this is an empty JSON object
|
2020-06-22 19:40:51 +00:00
|
|
|
.with_body(test_json::LOGOUT.to_string())
|
2020-05-06 12:36:28 +00:00
|
|
|
.create();
|
|
|
|
|
2020-05-22 13:23:58 +00:00
|
|
|
let client = Client::new(homeserver).unwrap();
|
|
|
|
client.restore_login(session).await.unwrap();
|
2020-05-06 12:36:28 +00:00
|
|
|
let room_id = RoomId::try_from("!testroom:example.org").unwrap();
|
|
|
|
|
|
|
|
let response = client.leave_room(&room_id).await.unwrap();
|
|
|
|
if let leave_room::Response = response {
|
|
|
|
} else {
|
|
|
|
panic!(
|
|
|
|
"expected `ruma_client_api::leave_room::Response` found {:?}",
|
|
|
|
response
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
#[allow(irrefutable_let_patterns)]
|
|
|
|
async fn ban_user() {
|
|
|
|
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
|
|
|
|
let user = UserId::try_from("@example:localhost").unwrap();
|
|
|
|
let room_id = RoomId::try_from("!testroom:example.org").unwrap();
|
|
|
|
|
|
|
|
let session = Session {
|
|
|
|
access_token: "1234".to_owned(),
|
|
|
|
user_id: user.clone(),
|
2020-07-22 18:43:47 +00:00
|
|
|
device_id: "DEVICEID".into(),
|
2020-05-06 12:36:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let _m = mock(
|
|
|
|
"POST",
|
|
|
|
Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/ban".to_string()),
|
|
|
|
)
|
|
|
|
.with_status(200)
|
|
|
|
// this is an empty JSON object
|
2020-06-22 19:40:51 +00:00
|
|
|
.with_body(test_json::LOGOUT.to_string())
|
2020-05-06 12:36:28 +00:00
|
|
|
.create();
|
|
|
|
|
2020-05-22 13:23:58 +00:00
|
|
|
let client = Client::new(homeserver).unwrap();
|
|
|
|
client.restore_login(session).await.unwrap();
|
2020-05-06 12:36:28 +00:00
|
|
|
|
|
|
|
let response = client.ban_user(&room_id, &user, None).await.unwrap();
|
|
|
|
if let ban_user::Response = response {
|
|
|
|
} else {
|
|
|
|
panic!(
|
|
|
|
"expected `ruma_client_api::ban_user::Response` found {:?}",
|
|
|
|
response
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
#[allow(irrefutable_let_patterns)]
|
|
|
|
async fn kick_user() {
|
|
|
|
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
|
|
|
|
let user = UserId::try_from("@example:localhost").unwrap();
|
|
|
|
let room_id = RoomId::try_from("!testroom:example.org").unwrap();
|
|
|
|
|
|
|
|
let session = Session {
|
|
|
|
access_token: "1234".to_owned(),
|
|
|
|
user_id: user.clone(),
|
2020-07-22 18:43:47 +00:00
|
|
|
device_id: "DEVICEID".into(),
|
2020-05-06 12:36:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let _m = mock(
|
|
|
|
"POST",
|
|
|
|
Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/kick".to_string()),
|
|
|
|
)
|
|
|
|
.with_status(200)
|
|
|
|
// this is an empty JSON object
|
2020-06-22 19:40:51 +00:00
|
|
|
.with_body(test_json::LOGOUT.to_string())
|
2020-05-06 12:36:28 +00:00
|
|
|
.create();
|
|
|
|
|
2020-05-22 13:23:58 +00:00
|
|
|
let client = Client::new(homeserver).unwrap();
|
|
|
|
client.restore_login(session).await.unwrap();
|
2020-05-06 12:36:28 +00:00
|
|
|
|
|
|
|
let response = client.kick_user(&room_id, &user, None).await.unwrap();
|
|
|
|
if let kick_user::Response = response {
|
|
|
|
} else {
|
|
|
|
panic!(
|
|
|
|
"expected `ruma_client_api::kick_user::Response` found {:?}",
|
|
|
|
response
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
#[allow(irrefutable_let_patterns)]
|
|
|
|
async fn forget_room() {
|
|
|
|
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
|
|
|
|
let user = UserId::try_from("@example:localhost").unwrap();
|
|
|
|
let room_id = RoomId::try_from("!testroom:example.org").unwrap();
|
|
|
|
|
|
|
|
let session = Session {
|
|
|
|
access_token: "1234".to_owned(),
|
|
|
|
user_id: user.clone(),
|
2020-07-22 18:43:47 +00:00
|
|
|
device_id: "DEVICEID".into(),
|
2020-05-06 12:36:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let _m = mock(
|
|
|
|
"POST",
|
|
|
|
Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/forget".to_string()),
|
|
|
|
)
|
|
|
|
.with_status(200)
|
|
|
|
// this is an empty JSON object
|
2020-06-22 19:40:51 +00:00
|
|
|
.with_body(test_json::LOGOUT.to_string())
|
2020-05-06 12:36:28 +00:00
|
|
|
.create();
|
|
|
|
|
2020-05-22 13:23:58 +00:00
|
|
|
let client = Client::new(homeserver).unwrap();
|
|
|
|
client.restore_login(session).await.unwrap();
|
2020-05-06 12:36:28 +00:00
|
|
|
|
|
|
|
let response = client.forget_room_by_id(&room_id).await.unwrap();
|
|
|
|
if let forget_room::Response = response {
|
|
|
|
} else {
|
|
|
|
panic!(
|
|
|
|
"expected `ruma_client_api::forget_room::Response` found {:?}",
|
|
|
|
response
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
#[allow(irrefutable_let_patterns)]
|
|
|
|
async fn read_receipt() {
|
|
|
|
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
|
|
|
|
let user_id = UserId::try_from("@example:localhost").unwrap();
|
|
|
|
let room_id = RoomId::try_from("!testroom:example.org").unwrap();
|
2020-06-20 21:18:20 +00:00
|
|
|
let event_id = EventId::try_from("$xxxxxx:example.org").unwrap();
|
2020-05-06 12:36:28 +00:00
|
|
|
|
|
|
|
let session = Session {
|
|
|
|
access_token: "1234".to_owned(),
|
|
|
|
user_id,
|
2020-07-22 18:43:47 +00:00
|
|
|
device_id: "DEVICEID".into(),
|
2020-05-06 12:36:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let _m = mock(
|
|
|
|
"POST",
|
|
|
|
Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/receipt".to_string()),
|
|
|
|
)
|
|
|
|
.with_status(200)
|
|
|
|
// this is an empty JSON object
|
2020-06-22 19:40:51 +00:00
|
|
|
.with_body(test_json::LOGOUT.to_string())
|
2020-05-06 12:36:28 +00:00
|
|
|
.create();
|
|
|
|
|
2020-05-22 13:23:58 +00:00
|
|
|
let client = Client::new(homeserver).unwrap();
|
|
|
|
client.restore_login(session).await.unwrap();
|
2020-05-06 12:36:28 +00:00
|
|
|
|
|
|
|
let response = client.read_receipt(&room_id, &event_id).await.unwrap();
|
|
|
|
if let create_receipt::Response = response {
|
|
|
|
} else {
|
|
|
|
panic!(
|
|
|
|
"expected `ruma_client_api::create_receipt::Response` found {:?}",
|
|
|
|
response
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-23 10:38:55 +00:00
|
|
|
#[tokio::test]
|
|
|
|
#[allow(irrefutable_let_patterns)]
|
|
|
|
async fn read_marker() {
|
|
|
|
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
|
|
|
|
let user_id = UserId::try_from("@example:localhost").unwrap();
|
|
|
|
let room_id = RoomId::try_from("!testroom:example.org").unwrap();
|
2020-06-20 21:18:20 +00:00
|
|
|
let event_id = EventId::try_from("$xxxxxx:example.org").unwrap();
|
2020-05-23 10:38:55 +00:00
|
|
|
|
|
|
|
let session = Session {
|
|
|
|
access_token: "1234".to_owned(),
|
|
|
|
user_id,
|
2020-07-22 18:43:47 +00:00
|
|
|
device_id: "DEVICEID".into(),
|
2020-05-23 10:38:55 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let _m = mock(
|
|
|
|
"POST",
|
|
|
|
Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/read_markers".to_string()),
|
|
|
|
)
|
|
|
|
.with_status(200)
|
|
|
|
// this is an empty JSON object
|
2020-06-22 19:40:51 +00:00
|
|
|
.with_body(test_json::LOGOUT.to_string())
|
2020-05-23 10:38:55 +00:00
|
|
|
.create();
|
|
|
|
|
|
|
|
let client = Client::new(homeserver).unwrap();
|
|
|
|
client.restore_login(session).await.unwrap();
|
|
|
|
|
|
|
|
let response = client.read_marker(&room_id, &event_id, None).await.unwrap();
|
|
|
|
if let set_read_marker::Response = response {
|
|
|
|
} else {
|
|
|
|
panic!(
|
|
|
|
"expected `ruma_client_api::set_read_marker::Response` found {:?}",
|
|
|
|
response
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-06 12:36:28 +00:00
|
|
|
#[tokio::test]
|
|
|
|
#[allow(irrefutable_let_patterns)]
|
|
|
|
async fn typing_notice() {
|
|
|
|
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
|
|
|
|
let user = UserId::try_from("@example:localhost").unwrap();
|
|
|
|
let room_id = RoomId::try_from("!testroom:example.org").unwrap();
|
|
|
|
|
|
|
|
let session = Session {
|
|
|
|
access_token: "1234".to_owned(),
|
|
|
|
user_id: user.clone(),
|
2020-07-22 18:43:47 +00:00
|
|
|
device_id: "DEVICEID".into(),
|
2020-05-06 12:36:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let _m = mock(
|
|
|
|
"PUT",
|
|
|
|
Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/typing".to_string()),
|
|
|
|
)
|
|
|
|
.with_status(200)
|
|
|
|
// this is an empty JSON object
|
2020-06-22 19:40:51 +00:00
|
|
|
.with_body(test_json::LOGOUT.to_string())
|
2020-05-06 12:36:28 +00:00
|
|
|
.create();
|
|
|
|
|
2020-05-22 13:23:58 +00:00
|
|
|
let client = Client::new(homeserver).unwrap();
|
|
|
|
client.restore_login(session).await.unwrap();
|
2020-05-06 12:36:28 +00:00
|
|
|
|
|
|
|
let response = client
|
|
|
|
.typing_notice(
|
|
|
|
&room_id,
|
|
|
|
&user,
|
|
|
|
true,
|
|
|
|
Some(std::time::Duration::from_secs(1)),
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
if let create_typing_event::Response = response {
|
|
|
|
} else {
|
|
|
|
panic!(
|
|
|
|
"expected `ruma_client_api::create_typing_event::Response` found {:?}",
|
|
|
|
response
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
2020-05-07 10:51:53 +00:00
|
|
|
|
2020-05-07 19:21:06 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn room_message_send() {
|
2020-05-08 14:12:21 +00:00
|
|
|
use matrix_sdk_common::uuid::Uuid;
|
2020-05-07 19:21:06 +00:00
|
|
|
|
|
|
|
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
|
|
|
|
let user = UserId::try_from("@example:localhost").unwrap();
|
|
|
|
let room_id = RoomId::try_from("!testroom:example.org").unwrap();
|
|
|
|
|
|
|
|
let session = Session {
|
|
|
|
access_token: "1234".to_owned(),
|
|
|
|
user_id: user.clone(),
|
2020-07-22 18:43:47 +00:00
|
|
|
device_id: "DEVICEID".into(),
|
2020-05-07 19:21:06 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let _m = mock(
|
|
|
|
"PUT",
|
|
|
|
Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/send/".to_string()),
|
|
|
|
)
|
|
|
|
.with_status(200)
|
2020-06-22 19:40:51 +00:00
|
|
|
.with_body(test_json::EVENT_ID.to_string())
|
2020-05-07 19:21:06 +00:00
|
|
|
.create();
|
|
|
|
|
2020-05-22 13:23:58 +00:00
|
|
|
let client = Client::new(homeserver).unwrap();
|
|
|
|
client.restore_login(session).await.unwrap();
|
2020-05-07 19:21:06 +00:00
|
|
|
|
|
|
|
let content = MessageEventContent::Text(TextMessageEventContent {
|
|
|
|
body: "Hello world".to_owned(),
|
|
|
|
relates_to: None,
|
2020-06-20 21:18:20 +00:00
|
|
|
formatted: None,
|
2020-05-07 19:21:06 +00:00
|
|
|
});
|
|
|
|
let txn_id = Uuid::new_v4();
|
|
|
|
let response = client
|
|
|
|
.room_send(&room_id, content, Some(txn_id))
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
assert_eq!(
|
2020-05-25 12:21:04 +00:00
|
|
|
EventId::try_from("$h29iv0s8:example.com").unwrap(),
|
2020-05-07 19:21:06 +00:00
|
|
|
response.event_id
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2020-05-07 10:51:53 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn user_presence() {
|
|
|
|
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
|
|
|
|
|
|
|
|
let session = Session {
|
|
|
|
access_token: "1234".to_owned(),
|
|
|
|
user_id: UserId::try_from("@example:localhost").unwrap(),
|
2020-07-22 18:43:47 +00:00
|
|
|
device_id: "DEVICEID".into(),
|
2020-05-07 10:51:53 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let _m = mock(
|
|
|
|
"GET",
|
|
|
|
Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_string()),
|
|
|
|
)
|
|
|
|
.with_status(200)
|
2020-06-22 19:40:51 +00:00
|
|
|
.with_body(test_json::SYNC.to_string())
|
2020-05-07 10:51:53 +00:00
|
|
|
.create();
|
|
|
|
|
2020-05-22 13:23:58 +00:00
|
|
|
let client = Client::new(homeserver).unwrap();
|
|
|
|
client.restore_login(session).await.unwrap();
|
2020-05-07 10:51:53 +00:00
|
|
|
|
|
|
|
let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000));
|
|
|
|
|
2020-07-16 13:04:26 +00:00
|
|
|
let _response = client.sync(sync_settings).await.unwrap();
|
2020-05-07 10:51:53 +00:00
|
|
|
|
|
|
|
let rooms_lock = &client.base_client.joined_rooms();
|
|
|
|
let rooms = rooms_lock.read().await;
|
|
|
|
let room = &rooms
|
|
|
|
.get(&RoomId::try_from("!SVkFJHzfwvuaIEawgC:localhost").unwrap())
|
|
|
|
.unwrap()
|
|
|
|
.read()
|
|
|
|
.await;
|
|
|
|
|
2020-06-15 15:04:10 +00:00
|
|
|
assert_eq!(1, room.joined_members.len());
|
2020-05-07 10:51:53 +00:00
|
|
|
assert!(room.power_levels.is_some())
|
|
|
|
}
|
|
|
|
|
2020-05-07 19:21:06 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn calculate_room_names_from_summary() {
|
|
|
|
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
|
2020-05-07 14:22:18 +00:00
|
|
|
|
2020-05-07 19:21:06 +00:00
|
|
|
let session = Session {
|
|
|
|
access_token: "1234".to_owned(),
|
|
|
|
user_id: UserId::try_from("@example:localhost").unwrap(),
|
2020-07-22 18:43:47 +00:00
|
|
|
device_id: "DEVICEID".into(),
|
2020-05-07 19:21:06 +00:00
|
|
|
};
|
2020-05-07 14:22:18 +00:00
|
|
|
|
2020-05-07 19:21:06 +00:00
|
|
|
let _m = mock(
|
|
|
|
"GET",
|
|
|
|
Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_string()),
|
|
|
|
)
|
|
|
|
.with_status(200)
|
2020-06-22 19:40:51 +00:00
|
|
|
.with_body(test_json::DEFAULT_SYNC_SUMMARY.to_string())
|
2020-05-07 19:21:06 +00:00
|
|
|
.create();
|
2020-05-07 14:22:18 +00:00
|
|
|
|
2020-05-22 13:23:58 +00:00
|
|
|
let client = Client::new(homeserver).unwrap();
|
|
|
|
client.restore_login(session).await.unwrap();
|
2020-05-07 14:22:18 +00:00
|
|
|
|
2020-05-07 19:21:06 +00:00
|
|
|
let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000));
|
2020-07-16 13:04:26 +00:00
|
|
|
let _response = client.sync(sync_settings).await.unwrap();
|
2020-05-07 19:21:06 +00:00
|
|
|
|
|
|
|
let mut room_names = vec![];
|
|
|
|
for room in client.joined_rooms().read().await.values() {
|
|
|
|
room_names.push(room.read().await.display_name())
|
|
|
|
}
|
|
|
|
|
2020-06-10 20:45:08 +00:00
|
|
|
assert_eq!(vec!["example2"], room_names);
|
2020-05-07 19:21:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn invited_rooms() {
|
|
|
|
use std::convert::TryFrom;
|
|
|
|
|
2020-05-13 10:47:24 +00:00
|
|
|
let session = Session {
|
2020-05-07 19:21:06 +00:00
|
|
|
access_token: "12345".to_owned(),
|
|
|
|
user_id: UserId::try_from("@example:localhost").unwrap(),
|
2020-07-22 18:43:47 +00:00
|
|
|
device_id: "DEVICEID".into(),
|
2020-05-07 19:21:06 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let homeserver = url::Url::parse(&mockito::server_url()).unwrap();
|
2020-05-22 13:23:58 +00:00
|
|
|
let client = Client::new(homeserver).unwrap();
|
|
|
|
client.restore_login(session).await.unwrap();
|
2020-05-07 19:21:06 +00:00
|
|
|
|
|
|
|
let _m = mock(
|
|
|
|
"GET",
|
|
|
|
Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_string()),
|
|
|
|
)
|
|
|
|
.with_status(200)
|
2020-06-22 19:40:51 +00:00
|
|
|
.with_body(test_json::INVITE_SYNC.to_string())
|
2020-05-07 19:21:06 +00:00
|
|
|
.create();
|
|
|
|
|
2020-07-16 13:04:26 +00:00
|
|
|
let _response = client.sync(SyncSettings::default()).await.unwrap();
|
2020-05-07 19:21:06 +00:00
|
|
|
|
|
|
|
assert!(client.joined_rooms().read().await.is_empty());
|
|
|
|
assert!(client.left_rooms().read().await.is_empty());
|
|
|
|
assert!(!client.invited_rooms().read().await.is_empty());
|
|
|
|
|
|
|
|
assert!(client
|
|
|
|
.get_invited_room(&RoomId::try_from("!696r7674:example.com").unwrap())
|
|
|
|
.await
|
|
|
|
.is_some());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn left_rooms() {
|
|
|
|
use std::convert::TryFrom;
|
|
|
|
|
2020-05-13 10:47:24 +00:00
|
|
|
let session = Session {
|
2020-05-07 19:21:06 +00:00
|
|
|
access_token: "12345".to_owned(),
|
|
|
|
user_id: UserId::try_from("@example:localhost").unwrap(),
|
2020-07-22 18:43:47 +00:00
|
|
|
device_id: "DEVICEID".into(),
|
2020-05-07 19:21:06 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let homeserver = url::Url::parse(&mockito::server_url()).unwrap();
|
2020-05-22 13:23:58 +00:00
|
|
|
let client = Client::new(homeserver).unwrap();
|
|
|
|
client.restore_login(session).await.unwrap();
|
2020-05-07 19:21:06 +00:00
|
|
|
|
|
|
|
let _m = mock(
|
|
|
|
"GET",
|
|
|
|
Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_string()),
|
|
|
|
)
|
|
|
|
.with_status(200)
|
2020-06-22 19:40:51 +00:00
|
|
|
.with_body(test_json::LEAVE_SYNC.to_string())
|
2020-05-07 19:21:06 +00:00
|
|
|
.create();
|
|
|
|
|
2020-07-16 13:04:26 +00:00
|
|
|
let _response = client.sync(SyncSettings::default()).await.unwrap();
|
2020-05-07 19:21:06 +00:00
|
|
|
|
|
|
|
assert!(client.joined_rooms().read().await.is_empty());
|
|
|
|
assert!(!client.left_rooms().read().await.is_empty());
|
|
|
|
assert!(client.invited_rooms().read().await.is_empty());
|
|
|
|
|
|
|
|
assert!(client
|
|
|
|
.get_left_room(&RoomId::try_from("!SVkFJHzfwvuaIEawgC:localhost").unwrap())
|
|
|
|
.await
|
|
|
|
.is_some())
|
|
|
|
}
|
2020-05-07 14:22:18 +00:00
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn test_client_sync_store() {
|
|
|
|
let homeserver = url::Url::from_str(&mockito::server_url()).unwrap();
|
2020-05-07 10:51:53 +00:00
|
|
|
|
|
|
|
let session = Session {
|
|
|
|
access_token: "1234".to_owned(),
|
2020-05-07 14:22:18 +00:00
|
|
|
user_id: UserId::try_from("@cheeky_monkey:matrix.org").unwrap(),
|
2020-07-22 18:43:47 +00:00
|
|
|
device_id: "DEVICEID".into(),
|
2020-05-07 10:51:53 +00:00
|
|
|
};
|
|
|
|
|
2020-05-07 14:22:18 +00:00
|
|
|
let _m = mock(
|
|
|
|
"GET",
|
|
|
|
Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_string()),
|
|
|
|
)
|
|
|
|
.with_status(200)
|
2020-06-22 19:40:51 +00:00
|
|
|
.with_body(test_json::SYNC.to_string())
|
2020-05-07 14:22:18 +00:00
|
|
|
.create();
|
2020-05-07 10:51:53 +00:00
|
|
|
|
2020-05-07 14:22:18 +00:00
|
|
|
let _m = mock("POST", "/_matrix/client/r0/login")
|
|
|
|
.with_status(200)
|
2020-06-22 19:40:51 +00:00
|
|
|
.with_body(test_json::LOGIN.to_string())
|
2020-05-07 14:22:18 +00:00
|
|
|
.create();
|
2020-05-07 11:18:13 +00:00
|
|
|
|
2020-05-18 20:26:27 +00:00
|
|
|
let dir = tempdir().unwrap();
|
2020-05-07 14:22:18 +00:00
|
|
|
// a sync response to populate our JSON store
|
2020-05-08 12:02:49 +00:00
|
|
|
let config =
|
|
|
|
ClientConfig::default().state_store(Box::new(JsonStore::open(dir.path()).unwrap()));
|
2020-05-22 13:23:58 +00:00
|
|
|
let client = Client::new_with_config(homeserver.clone(), config).unwrap();
|
|
|
|
client.restore_login(session.clone()).await.unwrap();
|
2020-05-07 14:22:18 +00:00
|
|
|
let sync_settings = SyncSettings::new().timeout(std::time::Duration::from_millis(3000));
|
|
|
|
|
|
|
|
// gather state to save to the db, the first time through loading will be skipped
|
2020-07-16 13:04:26 +00:00
|
|
|
let _ = client.sync(sync_settings.clone()).await.unwrap();
|
2020-05-07 14:22:18 +00:00
|
|
|
|
|
|
|
// now syncing the client will update from the state store
|
2020-05-08 12:02:49 +00:00
|
|
|
let config =
|
|
|
|
ClientConfig::default().state_store(Box::new(JsonStore::open(dir.path()).unwrap()));
|
2020-05-22 13:23:58 +00:00
|
|
|
let client = Client::new_with_config(homeserver, config).unwrap();
|
|
|
|
client.restore_login(session.clone()).await.unwrap();
|
2020-07-16 13:04:26 +00:00
|
|
|
client.sync(sync_settings).await.unwrap();
|
2020-05-07 14:22:18 +00:00
|
|
|
|
|
|
|
let base_client = &client.base_client;
|
|
|
|
|
|
|
|
// assert the synced client and the logged in client are equal
|
|
|
|
assert_eq!(*base_client.session().read().await, Some(session));
|
|
|
|
assert_eq!(
|
|
|
|
base_client.sync_token().await,
|
|
|
|
Some("s526_47314_0_7_1_1_1_11444_1".to_string())
|
|
|
|
);
|
2020-06-25 00:30:53 +00:00
|
|
|
|
|
|
|
// This is commented out because this field is private...
|
2020-05-07 14:22:18 +00:00
|
|
|
// assert_eq!(
|
|
|
|
// *base_client.ignored_users.read().await,
|
|
|
|
// vec![UserId::try_from("@someone:example.org").unwrap()]
|
|
|
|
// );
|
2020-05-07 10:51:53 +00:00
|
|
|
}
|
2020-05-08 09:27:33 +00:00
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn login() {
|
|
|
|
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
|
|
|
|
|
|
|
|
let _m = mock("POST", "/_matrix/client/r0/login")
|
|
|
|
.with_status(200)
|
2020-06-22 19:40:51 +00:00
|
|
|
.with_body(test_json::LOGIN.to_string())
|
2020-05-08 09:27:33 +00:00
|
|
|
.create();
|
|
|
|
|
2020-05-22 13:23:58 +00:00
|
|
|
let client = Client::new(homeserver).unwrap();
|
2020-05-08 09:27:33 +00:00
|
|
|
|
|
|
|
client
|
|
|
|
.login("example", "wordpass", None, None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let logged_in = client.logged_in().await;
|
|
|
|
assert!(logged_in, "Clint should be logged in");
|
|
|
|
}
|
|
|
|
|
2020-08-04 09:23:24 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn login_with_device() {
|
|
|
|
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
|
|
|
|
|
|
|
|
let _m = mock("POST", "/_matrix/client/r0/login")
|
|
|
|
.with_status(200)
|
|
|
|
.with_body(test_json::LOGIN.to_string())
|
|
|
|
.create();
|
|
|
|
|
|
|
|
let client = Client::new(homeserver).unwrap();
|
|
|
|
|
|
|
|
client
|
|
|
|
.login("example", "wordpass", None, None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let logged_in = client.logged_in().await;
|
|
|
|
assert!(logged_in, "Clint should be logged in");
|
|
|
|
}
|
|
|
|
|
2020-05-08 09:27:33 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn sync() {
|
|
|
|
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
|
|
|
|
|
|
|
|
let session = Session {
|
|
|
|
access_token: "1234".to_owned(),
|
|
|
|
user_id: UserId::try_from("@example:localhost").unwrap(),
|
2020-07-22 18:43:47 +00:00
|
|
|
device_id: "DEVICEID".into(),
|
2020-05-08 09:27:33 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let _m = mock(
|
|
|
|
"GET",
|
|
|
|
Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_string()),
|
|
|
|
)
|
|
|
|
.with_status(200)
|
2020-06-22 19:40:51 +00:00
|
|
|
.with_body(test_json::SYNC.to_string())
|
2020-05-08 09:27:33 +00:00
|
|
|
.create();
|
|
|
|
|
2020-05-22 13:23:58 +00:00
|
|
|
let client = Client::new(homeserver).unwrap();
|
|
|
|
client.restore_login(session).await.unwrap();
|
2020-05-08 09:27:33 +00:00
|
|
|
|
|
|
|
let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000));
|
|
|
|
|
2020-07-16 13:04:26 +00:00
|
|
|
let response = client.sync(sync_settings).await.unwrap();
|
2020-05-08 09:27:33 +00:00
|
|
|
|
|
|
|
assert_ne!(response.next_batch, "");
|
|
|
|
|
|
|
|
assert!(client.sync_token().await.is_some());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn room_names() {
|
|
|
|
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
|
|
|
|
|
|
|
|
let session = Session {
|
|
|
|
access_token: "1234".to_owned(),
|
|
|
|
user_id: UserId::try_from("@example:localhost").unwrap(),
|
2020-07-22 18:43:47 +00:00
|
|
|
device_id: "DEVICEID".into(),
|
2020-05-08 09:27:33 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let _m = mock(
|
|
|
|
"GET",
|
|
|
|
Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_string()),
|
|
|
|
)
|
|
|
|
.with_status(200)
|
2020-06-22 19:40:51 +00:00
|
|
|
.with_body(test_json::SYNC.to_string())
|
2020-05-08 09:27:33 +00:00
|
|
|
.create();
|
|
|
|
|
2020-05-22 13:23:58 +00:00
|
|
|
let client = Client::new(homeserver).unwrap();
|
|
|
|
client.restore_login(session).await.unwrap();
|
2020-05-08 09:27:33 +00:00
|
|
|
|
|
|
|
let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000));
|
|
|
|
|
2020-07-16 13:04:26 +00:00
|
|
|
let _response = client.sync(sync_settings).await.unwrap();
|
2020-05-08 09:27:33 +00:00
|
|
|
|
|
|
|
let mut names = vec![];
|
|
|
|
for r in client.joined_rooms().read().await.values() {
|
|
|
|
names.push(r.read().await.display_name());
|
|
|
|
}
|
|
|
|
assert_eq!(vec!["tutorial"], names);
|
|
|
|
let room = client
|
|
|
|
.get_joined_room(&RoomId::try_from("!SVkFJHzfwvuaIEawgC:localhost").unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
assert_eq!("tutorial".to_string(), room.read().await.display_name());
|
|
|
|
}
|
2020-04-07 00:59:44 +00:00
|
|
|
}
|