Merge branch 'master' into feature/display-name
commit
1174ccfc89
|
@ -6,4 +6,5 @@ members = [
|
||||||
"matrix_sdk_test_macros",
|
"matrix_sdk_test_macros",
|
||||||
"matrix_sdk_crypto",
|
"matrix_sdk_crypto",
|
||||||
"matrix_sdk_common",
|
"matrix_sdk_common",
|
||||||
|
"matrix_sdk_common_macros",
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[package]
|
[package]
|
||||||
authors = ["Damir Jelić <poljar@termina.org.uk"]
|
authors = ["Damir Jelić <poljar@termina.org.uk>"]
|
||||||
description = "A high level Matrix client-server library."
|
description = "A high level Matrix client-server library."
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
homepage = "https://github.com/matrix-org/matrix-rust-sdk"
|
homepage = "https://github.com/matrix-org/matrix-rust-sdk"
|
||||||
|
@ -25,7 +25,7 @@ tracing = "0.1.14"
|
||||||
url = "2.1.1"
|
url = "2.1.1"
|
||||||
futures-timer = { version = "3.0.2", features = ["wasm-bindgen"] }
|
futures-timer = { version = "3.0.2", features = ["wasm-bindgen"] }
|
||||||
|
|
||||||
|
matrix-sdk-common-macros = { version = "0.1.0", path = "../matrix_sdk_common_macros" }
|
||||||
matrix-sdk-common = { version = "0.1.0", path = "../matrix_sdk_common" }
|
matrix-sdk-common = { version = "0.1.0", path = "../matrix_sdk_common" }
|
||||||
|
|
||||||
[dependencies.matrix-sdk-base]
|
[dependencies.matrix-sdk-base]
|
||||||
|
|
|
@ -5,6 +5,7 @@ use matrix_sdk::{
|
||||||
events::room::message::{MessageEvent, MessageEventContent, TextMessageEventContent},
|
events::room::message::{MessageEvent, MessageEventContent, TextMessageEventContent},
|
||||||
Client, ClientConfig, EventEmitter, JsonStore, SyncRoom, SyncSettings,
|
Client, ClientConfig, EventEmitter, JsonStore, SyncRoom, SyncSettings,
|
||||||
};
|
};
|
||||||
|
use matrix_sdk_common_macros::async_trait;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
struct CommandBot {
|
struct CommandBot {
|
||||||
|
@ -19,7 +20,7 @@ impl CommandBot {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait]
|
||||||
impl EventEmitter for CommandBot {
|
impl EventEmitter for CommandBot {
|
||||||
async fn on_room_message(&self, room: SyncRoom, event: &MessageEvent) {
|
async fn on_room_message(&self, room: SyncRoom, event: &MessageEvent) {
|
||||||
if let SyncRoom::Joined(room) = room {
|
if let SyncRoom::Joined(room) = room {
|
||||||
|
|
|
@ -6,10 +6,11 @@ use matrix_sdk::{
|
||||||
events::room::message::{MessageEvent, MessageEventContent, TextMessageEventContent},
|
events::room::message::{MessageEvent, MessageEventContent, TextMessageEventContent},
|
||||||
Client, ClientConfig, EventEmitter, SyncRoom, SyncSettings,
|
Client, ClientConfig, EventEmitter, SyncRoom, SyncSettings,
|
||||||
};
|
};
|
||||||
|
use matrix_sdk_common_macros::async_trait;
|
||||||
|
|
||||||
struct EventCallback;
|
struct EventCallback;
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait]
|
||||||
impl EventEmitter for EventCallback {
|
impl EventEmitter for EventCallback {
|
||||||
async fn on_room_message(&self, room: SyncRoom, event: &MessageEvent) {
|
async fn on_room_message(&self, room: SyncRoom, event: &MessageEvent) {
|
||||||
if let SyncRoom::Joined(room) = room {
|
if let SyncRoom::Joined(room) = room {
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::{TryFrom, TryInto};
|
use std::convert::{TryFrom, TryInto};
|
||||||
|
use std::fmt::{self, Debug};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::result::Result as StdResult;
|
use std::result::Result as StdResult;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -66,8 +67,8 @@ pub struct Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(tarpaulin, skip)]
|
#[cfg_attr(tarpaulin, skip)]
|
||||||
impl std::fmt::Debug for Client {
|
impl Debug for Client {
|
||||||
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> StdResult<(), std::fmt::Error> {
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> StdResult<(), fmt::Error> {
|
||||||
write!(fmt, "Client {{ homeserver: {} }}", self.homeserver)
|
write!(fmt, "Client {{ homeserver: {} }}", self.homeserver)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,8 +107,8 @@ pub struct ClientConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(tarpaulin, skip)]
|
#[cfg_attr(tarpaulin, skip)]
|
||||||
impl std::fmt::Debug for ClientConfig {
|
impl Debug for ClientConfig {
|
||||||
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> StdResult<(), std::fmt::Error> {
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let mut res = fmt.debug_struct("ClientConfig");
|
let mut res = fmt.debug_struct("ClientConfig");
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
@ -246,6 +247,7 @@ impl SyncSettings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use api::r0::account::register;
|
||||||
#[cfg(feature = "encryption")]
|
#[cfg(feature = "encryption")]
|
||||||
use api::r0::keys::{claim_keys, get_keys, upload_keys, KeyAlgorithm};
|
use api::r0::keys::{claim_keys, get_keys, upload_keys, KeyAlgorithm};
|
||||||
use api::r0::membership::{
|
use api::r0::membership::{
|
||||||
|
@ -263,6 +265,7 @@ use api::r0::sync::sync_events;
|
||||||
#[cfg(feature = "encryption")]
|
#[cfg(feature = "encryption")]
|
||||||
use api::r0::to_device::send_event_to_device;
|
use api::r0::to_device::send_event_to_device;
|
||||||
use api::r0::typing::create_typing_event;
|
use api::r0::typing::create_typing_event;
|
||||||
|
use api::r0::uiaa::UiaaResponse;
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
/// Creates a new client for making HTTP requests to the given homeserver.
|
/// Creates a new client for making HTTP requests to the given homeserver.
|
||||||
|
@ -413,7 +416,7 @@ impl Client {
|
||||||
/// device_id from a previous login call. Note that this should be done
|
/// device_id from a previous login call. Note that this should be done
|
||||||
/// only if the client also holds the encryption keys for this device.
|
/// only if the client also holds the encryption keys for this device.
|
||||||
#[instrument(skip(password))]
|
#[instrument(skip(password))]
|
||||||
pub async fn login<S: Into<String> + std::fmt::Debug>(
|
pub async fn login<S: Into<String> + Debug>(
|
||||||
&self,
|
&self,
|
||||||
user: S,
|
user: S,
|
||||||
password: S,
|
password: S,
|
||||||
|
@ -447,6 +450,42 @@ impl Client {
|
||||||
Ok(self.base_client.restore_login(session).await?)
|
Ok(self.base_client.restore_login(session).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Register a user to the server.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `registration` - The easiest way to create this request is using the `RegistrationBuilder`.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// # 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;
|
||||||
|
/// # })
|
||||||
|
/// ```
|
||||||
|
#[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
|
||||||
|
}
|
||||||
|
|
||||||
/// Join a room by `RoomId`.
|
/// Join a room by `RoomId`.
|
||||||
///
|
///
|
||||||
/// Returns a `join_room_by_id::Response` consisting of the
|
/// Returns a `join_room_by_id::Response` consisting of the
|
||||||
|
@ -876,7 +915,79 @@ impl Client {
|
||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send an arbitrary request to the server, without updating client state
|
async fn send_request(
|
||||||
|
&self,
|
||||||
|
requires_auth: bool,
|
||||||
|
method: HttpMethod,
|
||||||
|
request: http::Request<Vec<u8>>,
|
||||||
|
) -> Result<reqwest::Response> {
|
||||||
|
let url = request.uri();
|
||||||
|
let path_and_query = url.path_and_query().unwrap();
|
||||||
|
let mut url = self.homeserver.clone();
|
||||||
|
|
||||||
|
url.set_path(path_and_query.path());
|
||||||
|
url.set_query(path_and_query.query());
|
||||||
|
|
||||||
|
let request_builder = match method {
|
||||||
|
HttpMethod::GET => self.http_client.get(url),
|
||||||
|
HttpMethod::POST => {
|
||||||
|
let body = request.body().clone();
|
||||||
|
self.http_client
|
||||||
|
.post(url)
|
||||||
|
.body(body)
|
||||||
|
.header(reqwest::header::CONTENT_TYPE, "application/json")
|
||||||
|
}
|
||||||
|
HttpMethod::PUT => {
|
||||||
|
let body = request.body().clone();
|
||||||
|
self.http_client
|
||||||
|
.put(url)
|
||||||
|
.body(body)
|
||||||
|
.header(reqwest::header::CONTENT_TYPE, "application/json")
|
||||||
|
}
|
||||||
|
HttpMethod::DELETE => {
|
||||||
|
let body = request.body().clone();
|
||||||
|
self.http_client
|
||||||
|
.delete(url)
|
||||||
|
.body(body)
|
||||||
|
.header(reqwest::header::CONTENT_TYPE, "application/json")
|
||||||
|
}
|
||||||
|
method => panic!("Unsuported method {}", method),
|
||||||
|
};
|
||||||
|
|
||||||
|
let request_builder = if requires_auth {
|
||||||
|
let session = self.base_client.session().read().await;
|
||||||
|
|
||||||
|
if let Some(session) = session.as_ref() {
|
||||||
|
let header_value = format!("Bearer {}", &session.access_token);
|
||||||
|
request_builder.header(AUTHORIZATION, header_value)
|
||||||
|
} else {
|
||||||
|
return Err(Error::AuthenticationRequired);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
request_builder
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(request_builder.send().await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn response_to_http_response(
|
||||||
|
&self,
|
||||||
|
mut response: reqwest::Response,
|
||||||
|
) -> Result<http::Response<Vec<u8>>> {
|
||||||
|
let status = response.status();
|
||||||
|
let mut http_builder = HttpResponse::builder().status(status);
|
||||||
|
let headers = http_builder.headers_mut().unwrap();
|
||||||
|
|
||||||
|
for (k, v) in response.headers_mut().drain() {
|
||||||
|
if let Some(key) = k {
|
||||||
|
headers.insert(key, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let body = response.bytes().await?.as_ref().to_owned();
|
||||||
|
Ok(http_builder.body(body).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send an arbitrary request to the server, without updating client state.
|
||||||
///
|
///
|
||||||
/// **Warning:** Because this method *does not* update the client state, it is
|
/// **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
|
/// important to make sure than you account for this yourself, and use wrapper methods
|
||||||
|
@ -914,69 +1025,79 @@ impl Client {
|
||||||
/// // returned
|
/// // returned
|
||||||
/// # })
|
/// # })
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn send<Request: Endpoint<ResponseError = crate::api::Error> + std::fmt::Debug>(
|
pub async fn send<Request: Endpoint<ResponseError = crate::api::Error> + Debug>(
|
||||||
&self,
|
&self,
|
||||||
request: Request,
|
request: Request,
|
||||||
) -> Result<Request::Response> {
|
) -> Result<Request::Response> {
|
||||||
let request: http::Request<Vec<u8>> = request.try_into()?;
|
let request: http::Request<Vec<u8>> = request.try_into()?;
|
||||||
let url = request.uri();
|
let response = self
|
||||||
let path_and_query = url.path_and_query().unwrap();
|
.send_request(
|
||||||
let mut url = self.homeserver.clone();
|
Request::METADATA.requires_authentication,
|
||||||
|
Request::METADATA.method,
|
||||||
url.set_path(path_and_query.path());
|
request,
|
||||||
url.set_query(path_and_query.query());
|
)
|
||||||
|
.await?;
|
||||||
trace!("Doing request {:?}", url);
|
|
||||||
|
|
||||||
let request_builder = match Request::METADATA.method {
|
|
||||||
HttpMethod::GET => self.http_client.get(url),
|
|
||||||
HttpMethod::POST => {
|
|
||||||
let body = request.body().clone();
|
|
||||||
self.http_client
|
|
||||||
.post(url)
|
|
||||||
.body(body)
|
|
||||||
.header(reqwest::header::CONTENT_TYPE, "application/json")
|
|
||||||
}
|
|
||||||
HttpMethod::PUT => {
|
|
||||||
let body = request.body().clone();
|
|
||||||
self.http_client
|
|
||||||
.put(url)
|
|
||||||
.body(body)
|
|
||||||
.header(reqwest::header::CONTENT_TYPE, "application/json")
|
|
||||||
}
|
|
||||||
HttpMethod::DELETE => unimplemented!(),
|
|
||||||
_ => panic!("Unsuported method"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let request_builder = if Request::METADATA.requires_authentication {
|
|
||||||
let session = self.base_client.session().read().await;
|
|
||||||
|
|
||||||
if let Some(session) = session.as_ref() {
|
|
||||||
let header_value = format!("Bearer {}", &session.access_token);
|
|
||||||
request_builder.header(AUTHORIZATION, header_value)
|
|
||||||
} else {
|
|
||||||
return Err(Error::AuthenticationRequired);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
request_builder
|
|
||||||
};
|
|
||||||
let mut response = request_builder.send().await?;
|
|
||||||
|
|
||||||
trace!("Got response: {:?}", response);
|
trace!("Got response: {:?}", response);
|
||||||
|
|
||||||
let status = response.status();
|
let response = self.response_to_http_response(response).await?;
|
||||||
let mut http_builder = HttpResponse::builder().status(status);
|
|
||||||
let headers = http_builder.headers_mut().unwrap();
|
|
||||||
|
|
||||||
for (k, v) in response.headers_mut().drain() {
|
Ok(<Request::Response>::try_from(response)?)
|
||||||
if let Some(key) = k {
|
|
||||||
headers.insert(key, v);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
let body = response.bytes().await?.as_ref().to_owned();
|
|
||||||
let http_response = http_builder.body(body).unwrap();
|
|
||||||
|
|
||||||
Ok(<Request::Response>::try_from(http_response)?)
|
/// 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>(
|
||||||
|
&self,
|
||||||
|
request: Request,
|
||||||
|
) -> Result<Request::Response> {
|
||||||
|
let request: http::Request<Vec<u8>> = request.try_into()?;
|
||||||
|
let response = self
|
||||||
|
.send_request(
|
||||||
|
Request::METADATA.requires_authentication,
|
||||||
|
Request::METADATA.method,
|
||||||
|
request,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
trace!("Got response: {:?}", response);
|
||||||
|
|
||||||
|
let response = self.response_to_http_response(response).await?;
|
||||||
|
|
||||||
|
let uiaa: Result<_> = <Request::Response>::try_from(response).map_err(Into::into);
|
||||||
|
|
||||||
|
Ok(uiaa?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Synchronize the client's state with the latest state on the server.
|
/// Synchronize the client's state with the latest state on the server.
|
||||||
|
@ -1274,13 +1395,15 @@ impl Client {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::{
|
use super::{
|
||||||
ban_user, create_receipt, create_typing_event, forget_room, invite_user, kick_user,
|
api::r0::uiaa::AuthData, ban_user, create_receipt, create_typing_event, forget_room,
|
||||||
leave_room, set_read_marker, Invite3pid, MessageEventContent,
|
invite_user, kick_user, leave_room, register::RegistrationKind, set_read_marker,
|
||||||
|
Invite3pid, MessageEventContent,
|
||||||
};
|
};
|
||||||
use super::{Client, ClientConfig, Session, SyncSettings, Url};
|
use super::{Client, ClientConfig, Session, SyncSettings, Url};
|
||||||
use crate::events::collections::all::RoomEvent;
|
use crate::events::collections::all::RoomEvent;
|
||||||
use crate::events::room::message::TextMessageEventContent;
|
use crate::events::room::message::TextMessageEventContent;
|
||||||
use crate::identifiers::{EventId, RoomId, RoomIdOrAliasId, UserId};
|
use crate::identifiers::{EventId, RoomId, RoomIdOrAliasId, UserId};
|
||||||
|
use crate::RegistrationBuilder;
|
||||||
|
|
||||||
use matrix_sdk_base::JsonStore;
|
use matrix_sdk_base::JsonStore;
|
||||||
use matrix_sdk_test::{EventBuilder, EventsFile};
|
use matrix_sdk_test::{EventBuilder, EventsFile};
|
||||||
|
@ -1461,6 +1584,44 @@ mod test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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)
|
||||||
|
.with_body_from_file("../test_data/registration_response_error.json")
|
||||||
|
.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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn join_room_by_id() {
|
async fn join_room_by_id() {
|
||||||
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
|
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
|
||||||
|
|
|
@ -20,6 +20,7 @@ use thiserror::Error;
|
||||||
|
|
||||||
use matrix_sdk_base::Error as MatrixError;
|
use matrix_sdk_base::Error as MatrixError;
|
||||||
|
|
||||||
|
use crate::api::r0::uiaa::UiaaResponse as UiaaError;
|
||||||
use crate::api::Error as RumaClientError;
|
use crate::api::Error as RumaClientError;
|
||||||
use crate::FromHttpResponseError as RumaResponseError;
|
use crate::FromHttpResponseError as RumaResponseError;
|
||||||
use crate::IntoHttpError as RumaIntoHttpError;
|
use crate::IntoHttpError as RumaIntoHttpError;
|
||||||
|
@ -50,9 +51,23 @@ pub enum Error {
|
||||||
#[error("can't convert between ruma_client_api and hyper types.")]
|
#[error("can't convert between ruma_client_api and hyper types.")]
|
||||||
IntoHttp(RumaIntoHttpError),
|
IntoHttp(RumaIntoHttpError),
|
||||||
|
|
||||||
/// An error occured in the Matrix client library.
|
/// An error occurred in the Matrix client library.
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
MatrixError(#[from] MatrixError),
|
MatrixError(#[from] MatrixError),
|
||||||
|
|
||||||
|
/// An error occurred while authenticating.
|
||||||
|
///
|
||||||
|
/// When registering or authenticating the Matrix server can send a `UiaaResponse`
|
||||||
|
/// as the error type, this is a User-Interactive Authentication API response. This
|
||||||
|
/// represents an error with information about how to authenticate the user.
|
||||||
|
#[error("User-Interactive Authentication required.")]
|
||||||
|
UiaaError(RumaResponseError<UiaaError>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RumaResponseError<UiaaError>> for Error {
|
||||||
|
fn from(error: RumaResponseError<UiaaError>) -> Self {
|
||||||
|
Self::UiaaError(error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<RumaResponseError<RumaClientError>> for Error {
|
impl From<RumaResponseError<RumaClientError>> for Error {
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
unused_qualifications
|
unused_qualifications
|
||||||
)]
|
)]
|
||||||
|
|
||||||
|
pub use matrix_sdk_base::Error as BaseError;
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub use matrix_sdk_base::JsonStore;
|
pub use matrix_sdk_base::JsonStore;
|
||||||
pub use matrix_sdk_base::{CustomOrRawEvent, EventEmitter, Room, Session, SyncRoom};
|
pub use matrix_sdk_base::{CustomOrRawEvent, EventEmitter, Room, Session, SyncRoom};
|
||||||
|
@ -51,7 +52,7 @@ mod error;
|
||||||
mod request_builder;
|
mod request_builder;
|
||||||
pub use client::{Client, ClientConfig, SyncSettings};
|
pub use client::{Client, ClientConfig, SyncSettings};
|
||||||
pub use error::{Error, Result};
|
pub use error::{Error, Result};
|
||||||
pub use request_builder::{MessagesRequestBuilder, RoomBuilder};
|
pub use request_builder::{MessagesRequestBuilder, RegistrationBuilder, RoomBuilder};
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub(crate) const VERSION: &str = env!("CARGO_PKG_VERSION");
|
pub(crate) const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
use crate::api;
|
use crate::api;
|
||||||
use crate::events::room::power_levels::PowerLevelsEventContent;
|
use crate::events::room::power_levels::PowerLevelsEventContent;
|
||||||
use crate::events::EventJson;
|
use crate::events::EventJson;
|
||||||
use crate::identifiers::{RoomId, UserId};
|
use crate::identifiers::{DeviceId, RoomId, UserId};
|
||||||
|
use api::r0::account::register;
|
||||||
|
use api::r0::account::register::RegistrationKind;
|
||||||
use api::r0::filter::RoomEventFilter;
|
use api::r0::filter::RoomEventFilter;
|
||||||
use api::r0::membership::Invite3pid;
|
use api::r0::membership::Invite3pid;
|
||||||
use api::r0::message::get_message_events::{self, Direction};
|
use api::r0::message::get_message_events::{self, Direction};
|
||||||
|
@ -9,6 +11,7 @@ use api::r0::room::{
|
||||||
create_room::{self, CreationContent, InitialStateEvent, RoomPreset},
|
create_room::{self, CreationContent, InitialStateEvent, RoomPreset},
|
||||||
Visibility,
|
Visibility,
|
||||||
};
|
};
|
||||||
|
use api::r0::uiaa::AuthData;
|
||||||
|
|
||||||
use crate::js_int::UInt;
|
use crate::js_int::UInt;
|
||||||
|
|
||||||
|
@ -288,6 +291,120 @@ impl Into<get_message_events::Request> for MessagesRequestBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A builder used to register users.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// # 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;
|
||||||
|
/// # })
|
||||||
|
/// ```
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct RegistrationBuilder {
|
||||||
|
password: Option<String>,
|
||||||
|
username: Option<String>,
|
||||||
|
device_id: Option<DeviceId>,
|
||||||
|
initial_device_display_name: Option<String>,
|
||||||
|
auth: Option<AuthData>,
|
||||||
|
kind: Option<RegistrationKind>,
|
||||||
|
inhibit_login: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RegistrationBuilder {
|
||||||
|
/// Create a `RegistrationBuilder` builder to make a `register::Request`.
|
||||||
|
///
|
||||||
|
/// The `room_id` and `from`` fields **need to be set** to create the request.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The desired password for the account.
|
||||||
|
///
|
||||||
|
/// May be empty for accounts that should not be able to log in again
|
||||||
|
/// with a password, e.g., for guest or application service accounts.
|
||||||
|
pub fn password(&mut self, password: &str) -> &mut Self {
|
||||||
|
self.password = Some(password.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// local part of the desired Matrix ID.
|
||||||
|
///
|
||||||
|
/// If omitted, the homeserver MUST generate a Matrix ID local part.
|
||||||
|
pub fn username(&mut self, username: &str) -> &mut Self {
|
||||||
|
self.username = Some(username.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ID of the client device.
|
||||||
|
///
|
||||||
|
/// If this does not correspond to a known client device, a new device will be created.
|
||||||
|
/// The server will auto-generate a device_id if this is not specified.
|
||||||
|
pub fn device_id(&mut self, device_id: &str) -> &mut Self {
|
||||||
|
self.device_id = Some(device_id.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A display name to assign to the newly-created device.
|
||||||
|
///
|
||||||
|
/// Ignored if `device_id` corresponds to a known device.
|
||||||
|
pub fn initial_device_display_name(&mut self, initial_device_display_name: &str) -> &mut Self {
|
||||||
|
self.initial_device_display_name = Some(initial_device_display_name.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Additional authentication information for the user-interactive authentication API.
|
||||||
|
///
|
||||||
|
/// Note that this information is not used to define how the registered user should be
|
||||||
|
/// authenticated, but is instead used to authenticate the register call itself.
|
||||||
|
/// It should be left empty, or omitted, unless an earlier call returned an response
|
||||||
|
/// with status code 401.
|
||||||
|
pub fn auth(&mut self, auth: AuthData) -> &mut Self {
|
||||||
|
self.auth = Some(auth);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Kind of account to register
|
||||||
|
///
|
||||||
|
/// Defaults to `User` if omitted.
|
||||||
|
pub fn kind(&mut self, kind: RegistrationKind) -> &mut Self {
|
||||||
|
self.kind = Some(kind);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If `true`, an `access_token` and `device_id` should not be returned
|
||||||
|
/// from this call, therefore preventing an automatic login.
|
||||||
|
pub fn inhibit_login(&mut self, inhibit_login: bool) -> &mut Self {
|
||||||
|
self.inhibit_login = inhibit_login;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<register::Request> for RegistrationBuilder {
|
||||||
|
fn into(self) -> register::Request {
|
||||||
|
register::Request {
|
||||||
|
password: self.password,
|
||||||
|
username: self.username,
|
||||||
|
device_id: self.device_id,
|
||||||
|
initial_device_display_name: self.initial_device_display_name,
|
||||||
|
auth: self.auth,
|
||||||
|
kind: self.kind,
|
||||||
|
inhibit_login: self.inhibit_login,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[package]
|
[package]
|
||||||
authors = ["Damir Jelić <poljar@termina.org.uk"]
|
authors = ["Damir Jelić <poljar@termina.org.uk>"]
|
||||||
description = "The base component to build a Matrix client library."
|
description = "The base component to build a Matrix client library."
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
homepage = "https://github.com/matrix-org/matrix-rust-sdk"
|
homepage = "https://github.com/matrix-org/matrix-rust-sdk"
|
||||||
|
@ -22,6 +22,7 @@ serde = "1.0.110"
|
||||||
serde_json = "1.0.53"
|
serde_json = "1.0.53"
|
||||||
zeroize = "1.1.0"
|
zeroize = "1.1.0"
|
||||||
|
|
||||||
|
matrix-sdk-common-macros = { version = "0.1.0", path = "../matrix_sdk_common_macros" }
|
||||||
matrix-sdk-common = { version = "0.1.0", path = "../matrix_sdk_common" }
|
matrix-sdk-common = { version = "0.1.0", path = "../matrix_sdk_common" }
|
||||||
matrix-sdk-crypto = { version = "0.1.0", path = "../matrix_sdk_crypto", optional = true }
|
matrix-sdk-crypto = { version = "0.1.0", path = "../matrix_sdk_crypto", optional = true }
|
||||||
|
|
||||||
|
|
|
@ -1676,6 +1676,7 @@ impl BaseClient {
|
||||||
NonRoomEvent::FullyRead(full_read) => {
|
NonRoomEvent::FullyRead(full_read) => {
|
||||||
event_emitter.on_non_room_fully_read(room, &full_read).await
|
event_emitter.on_non_room_fully_read(room, &full_read).await
|
||||||
}
|
}
|
||||||
|
NonRoomEvent::Typing(typing) => event_emitter.on_non_room_typing(room, &typing).await,
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1732,6 +1733,7 @@ impl BaseClient {
|
||||||
NonRoomEvent::FullyRead(full_read) => {
|
NonRoomEvent::FullyRead(full_read) => {
|
||||||
event_emitter.on_non_room_fully_read(room, &full_read).await
|
event_emitter.on_non_room_fully_read(room, &full_read).await
|
||||||
}
|
}
|
||||||
|
NonRoomEvent::Typing(typing) => event_emitter.on_non_room_typing(room, &typing).await,
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1813,6 +1815,7 @@ mod test {
|
||||||
events::{collections::all::RoomEvent, stripped::AnyStrippedStateEvent},
|
events::{collections::all::RoomEvent, stripped::AnyStrippedStateEvent},
|
||||||
BaseClient, Session,
|
BaseClient, Session,
|
||||||
};
|
};
|
||||||
|
use matrix_sdk_common_macros::async_trait;
|
||||||
use matrix_sdk_test::{async_test, EventBuilder, EventsFile};
|
use matrix_sdk_test::{async_test, EventBuilder, EventsFile};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
@ -1979,7 +1982,7 @@ mod test {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EE(Arc<AtomicBool>);
|
struct EE(Arc<AtomicBool>);
|
||||||
#[async_trait::async_trait]
|
#[async_trait]
|
||||||
impl EventEmitter for EE {
|
impl EventEmitter for EE {
|
||||||
async fn on_room_member(&self, room: SyncRoom, event: &MemberEvent) {
|
async fn on_room_member(&self, room: SyncRoom, event: &MemberEvent) {
|
||||||
if let SyncRoom::Joined(_) = room {
|
if let SyncRoom::Joined(_) = room {
|
||||||
|
@ -2075,7 +2078,7 @@ mod test {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EE(Arc<AtomicBool>);
|
struct EE(Arc<AtomicBool>);
|
||||||
#[async_trait::async_trait]
|
#[async_trait]
|
||||||
impl EventEmitter for EE {
|
impl EventEmitter for EE {
|
||||||
async fn on_unrecognized_event(&self, room: SyncRoom, event: &CustomOrRawEvent<'_>) {
|
async fn on_unrecognized_event(&self, room: SyncRoom, event: &CustomOrRawEvent<'_>) {
|
||||||
if let SyncRoom::Joined(_) = room {
|
if let SyncRoom::Joined(_) = room {
|
||||||
|
@ -2171,7 +2174,7 @@ mod test {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EE(Arc<AtomicBool>);
|
struct EE(Arc<AtomicBool>);
|
||||||
#[async_trait::async_trait]
|
#[async_trait]
|
||||||
impl EventEmitter for EE {
|
impl EventEmitter for EE {
|
||||||
async fn on_unrecognized_event(&self, room: SyncRoom, event: &CustomOrRawEvent<'_>) {
|
async fn on_unrecognized_event(&self, room: SyncRoom, event: &CustomOrRawEvent<'_>) {
|
||||||
if let SyncRoom::Joined(_) = room {
|
if let SyncRoom::Joined(_) = room {
|
||||||
|
|
|
@ -43,6 +43,7 @@ use crate::events::{
|
||||||
CustomEvent, CustomRoomEvent, CustomStateEvent,
|
CustomEvent, CustomRoomEvent, CustomStateEvent,
|
||||||
};
|
};
|
||||||
use crate::{Room, RoomState};
|
use crate::{Room, RoomState};
|
||||||
|
use matrix_sdk_common_macros::async_trait;
|
||||||
|
|
||||||
/// Type alias for `RoomState` enum when passed to `EventEmitter` methods.
|
/// Type alias for `RoomState` enum when passed to `EventEmitter` methods.
|
||||||
pub type SyncRoom = RoomState<Arc<RwLock<Room>>>;
|
pub type SyncRoom = RoomState<Arc<RwLock<Room>>>;
|
||||||
|
@ -78,10 +79,11 @@ pub enum CustomOrRawEvent<'c> {
|
||||||
/// # EventEmitter, SyncRoom
|
/// # EventEmitter, SyncRoom
|
||||||
/// # };
|
/// # };
|
||||||
/// # use matrix_sdk_common::locks::RwLock;
|
/// # use matrix_sdk_common::locks::RwLock;
|
||||||
|
/// # use matrix_sdk_common_macros::async_trait;
|
||||||
///
|
///
|
||||||
/// struct EventCallback;
|
/// struct EventCallback;
|
||||||
///
|
///
|
||||||
/// #[async_trait::async_trait]
|
/// #[async_trait]
|
||||||
/// impl EventEmitter for EventCallback {
|
/// impl EventEmitter for EventCallback {
|
||||||
/// async fn on_room_message(&self, room: SyncRoom, event: &MessageEvent) {
|
/// async fn on_room_message(&self, room: SyncRoom, event: &MessageEvent) {
|
||||||
/// if let SyncRoom::Joined(room) = room {
|
/// if let SyncRoom::Joined(room) = room {
|
||||||
|
@ -106,7 +108,7 @@ pub enum CustomOrRawEvent<'c> {
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[async_trait::async_trait]
|
#[async_trait]
|
||||||
pub trait EventEmitter: Send + Sync {
|
pub trait EventEmitter: Send + Sync {
|
||||||
// ROOM EVENTS from `IncomingTimeline`
|
// ROOM EVENTS from `IncomingTimeline`
|
||||||
/// Fires when `Client` receives a `RoomEvent::RoomMember` event.
|
/// Fires when `Client` receives a `RoomEvent::RoomMember` event.
|
||||||
|
@ -200,6 +202,7 @@ pub trait EventEmitter: Send + Sync {
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use matrix_sdk_common::locks::Mutex;
|
use matrix_sdk_common::locks::Mutex;
|
||||||
|
use matrix_sdk_common_macros::async_trait;
|
||||||
use matrix_sdk_test::{async_test, sync_response, SyncResponseFile};
|
use matrix_sdk_test::{async_test, sync_response, SyncResponseFile};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -209,7 +212,7 @@ mod test {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct EvEmitterTest(Arc<Mutex<Vec<String>>>);
|
pub struct EvEmitterTest(Arc<Mutex<Vec<String>>>);
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait]
|
||||||
impl EventEmitter for EvEmitterTest {
|
impl EventEmitter for EvEmitterTest {
|
||||||
async fn on_room_member(&self, _: SyncRoom, _: &MemberEvent) {
|
async fn on_room_member(&self, _: SyncRoom, _: &MemberEvent) {
|
||||||
self.0.lock().await.push("member".to_string())
|
self.0.lock().await.push("member".to_string())
|
||||||
|
@ -319,9 +322,15 @@ mod test {
|
||||||
async fn on_non_room_fully_read(&self, _: SyncRoom, _: &FullyReadEvent) {
|
async fn on_non_room_fully_read(&self, _: SyncRoom, _: &FullyReadEvent) {
|
||||||
self.0.lock().await.push("account read".to_string())
|
self.0.lock().await.push("account read".to_string())
|
||||||
}
|
}
|
||||||
|
async fn on_non_room_typing(&self, _: SyncRoom, _: &TypingEvent) {
|
||||||
|
self.0.lock().await.push("typing event".to_string())
|
||||||
|
}
|
||||||
async fn on_presence_event(&self, _: SyncRoom, _: &PresenceEvent) {
|
async fn on_presence_event(&self, _: SyncRoom, _: &PresenceEvent) {
|
||||||
self.0.lock().await.push("presence event".to_string())
|
self.0.lock().await.push("presence event".to_string())
|
||||||
}
|
}
|
||||||
|
async fn on_unrecognized_event(&self, _: SyncRoom, _: &CustomOrRawEvent<'_>) {
|
||||||
|
self.0.lock().await.push("unrecognized event".to_string())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use crate::identifiers::UserId;
|
use crate::identifiers::UserId;
|
||||||
|
@ -417,4 +426,30 @@ mod test {
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_test]
|
||||||
|
async fn event_emitter_more_events() {
|
||||||
|
let vec = Arc::new(Mutex::new(Vec::new()));
|
||||||
|
let test_vec = Arc::clone(&vec);
|
||||||
|
let emitter = Box::new(EvEmitterTest(vec));
|
||||||
|
|
||||||
|
let client = get_client().await;
|
||||||
|
client.add_event_emitter(emitter).await;
|
||||||
|
|
||||||
|
let mut response = sync_response(SyncResponseFile::All);
|
||||||
|
client.receive_sync_response(&mut response).await.unwrap();
|
||||||
|
|
||||||
|
let v = test_vec.lock().await;
|
||||||
|
assert_eq!(
|
||||||
|
v.as_slice(),
|
||||||
|
[
|
||||||
|
"message",
|
||||||
|
"unrecognized event",
|
||||||
|
"redaction",
|
||||||
|
"unrecognized event",
|
||||||
|
"unrecognized event",
|
||||||
|
"typing event"
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,8 +43,7 @@ use crate::identifiers::{RoomAliasId, RoomId, UserId};
|
||||||
|
|
||||||
use crate::js_int::{Int, UInt};
|
use crate::js_int::{Int, UInt};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
#[derive(Debug, Default, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Default, PartialEq, Serialize, Deserialize, Clone)]
|
||||||
#[cfg_attr(test, derive(Clone))]
|
|
||||||
/// `RoomName` allows the calculation of a text room name.
|
/// `RoomName` allows the calculation of a text room name.
|
||||||
pub struct RoomName {
|
pub struct RoomName {
|
||||||
/// The displayed name of the room.
|
/// The displayed name of the room.
|
||||||
|
@ -66,8 +65,7 @@ pub struct RoomName {
|
||||||
pub invited_member_count: Option<UInt>,
|
pub invited_member_count: Option<UInt>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)]
|
||||||
#[cfg_attr(test, derive(Clone))]
|
|
||||||
pub struct PowerLevels {
|
pub struct PowerLevels {
|
||||||
/// The level required to ban a user.
|
/// The level required to ban a user.
|
||||||
pub ban: Int,
|
pub ban: Int,
|
||||||
|
@ -138,8 +136,7 @@ impl From<&EncryptionEvent> for EncryptionInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)]
|
||||||
#[cfg_attr(test, derive(Clone))]
|
|
||||||
pub struct Tombstone {
|
pub struct Tombstone {
|
||||||
/// A server-defined message.
|
/// A server-defined message.
|
||||||
body: String,
|
body: String,
|
||||||
|
@ -153,8 +150,7 @@ enum MemberDirection {
|
||||||
Exiting,
|
Exiting,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
|
||||||
#[cfg_attr(test, derive(Clone))]
|
|
||||||
/// A Matrix room.
|
/// A Matrix room.
|
||||||
pub struct Room {
|
pub struct Room {
|
||||||
/// The unique id of the room.
|
/// The unique id of the room.
|
||||||
|
|
|
@ -27,7 +27,7 @@ use crate::js_int::{Int, UInt};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
// Notes: if Alice invites Bob into a room we will get an event with the sender as Alice and the state key as Bob.
|
// Notes: if Alice invites Bob into a room we will get an event with the sender as Alice and the state key as Bob.
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
/// A Matrix room member.
|
/// A Matrix room member.
|
||||||
///
|
///
|
||||||
pub struct RoomMember {
|
pub struct RoomMember {
|
||||||
|
|
|
@ -27,6 +27,9 @@ use crate::events::push_rules::Ruleset;
|
||||||
use crate::identifiers::{RoomId, UserId};
|
use crate::identifiers::{RoomId, UserId};
|
||||||
use crate::{Result, Room, RoomState, Session};
|
use crate::{Result, Room, RoomState, Session};
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
use matrix_sdk_common_macros::send_sync;
|
||||||
|
|
||||||
/// `ClientState` holds all the information to restore a `BaseClient`
|
/// `ClientState` holds all the information to restore a `BaseClient`
|
||||||
/// except the `access_token` as the default store is not secure.
|
/// except the `access_token` as the default store is not secure.
|
||||||
///
|
///
|
||||||
|
@ -85,7 +88,8 @@ pub struct AllRooms {
|
||||||
|
|
||||||
/// Abstraction around the data store to avoid unnecessary request on client initialization.
|
/// Abstraction around the data store to avoid unnecessary request on client initialization.
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
pub trait StateStore: Send + Sync {
|
#[cfg_attr(not(target_arch = "wasm32"), send_sync)]
|
||||||
|
pub trait StateStore {
|
||||||
/// Loads the state of `BaseClient` through `ClientState` type.
|
/// Loads the state of `BaseClient` through `ClientState` type.
|
||||||
///
|
///
|
||||||
/// An `Option::None` should be returned only if the `StateStore` tries to
|
/// An `Option::None` should be returned only if the `StateStore` tries to
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[package]
|
[package]
|
||||||
authors = ["Damir Jelić <poljar@termina.org.uk"]
|
authors = ["Damir Jelić <poljar@termina.org.uk>"]
|
||||||
description = "Collection of common types used in the matrix-sdk"
|
description = "Collection of common types used in the matrix-sdk"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
homepage = "https://github.com/matrix-org/matrix-rust-sdk"
|
homepage = "https://github.com/matrix-org/matrix-rust-sdk"
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
[package]
|
||||||
|
description = "Helper macros for the Matrix SDK"
|
||||||
|
authors = ["MTRnord <mtrnord1@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
homepage = "https://github.com/matrix-org/matrix-rust-sdk"
|
||||||
|
keywords = ["matrix", "chat", "messaging", "ruma"]
|
||||||
|
license = "Apache-2.0"
|
||||||
|
name = "matrix-sdk-common-macros"
|
||||||
|
readme = "README.md"
|
||||||
|
repository = "https://github.com/matrix-org/matrix-rust-sdk"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
syn = "1.0"
|
||||||
|
quote = "1.0"
|
|
@ -0,0 +1,30 @@
|
||||||
|
use proc_macro::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
|
use syn::{parse_macro_input, parse_quote, ItemTrait};
|
||||||
|
|
||||||
|
/// Attribute to use `Send + Sync` for everything but wasm32
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
pub fn send_sync(_attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
|
// Parse the input tokens into a syntax tree
|
||||||
|
let mut input = parse_macro_input!(input as ItemTrait);
|
||||||
|
|
||||||
|
let send_trait_bound = parse_quote!(std::marker::Send);
|
||||||
|
let sync_trait_bound = parse_quote!(std::marker::Sync);
|
||||||
|
input.supertraits.push(send_trait_bound);
|
||||||
|
input.supertraits.push(sync_trait_bound);
|
||||||
|
|
||||||
|
TokenStream::from(quote!(#input))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A wasm32 compatible wrapper for the async_trait::async_trait macro
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
pub fn async_trait(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
|
let attrs = r#"
|
||||||
|
#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
|
||||||
|
#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let mut out: TokenStream = attrs.parse().unwrap();
|
||||||
|
out.extend(item);
|
||||||
|
out
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
[package]
|
[package]
|
||||||
authors = ["Damir Jelić <poljar@termina.org.uk"]
|
authors = ["Damir Jelić <poljar@termina.org.uk>"]
|
||||||
description = "Matrix encryption library"
|
description = "Matrix encryption library"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
homepage = "https://github.com/matrix-org/matrix-rust-sdk"
|
homepage = "https://github.com/matrix-org/matrix-rust-sdk"
|
||||||
|
@ -17,6 +17,7 @@ sqlite-cryptostore = ["sqlx"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-trait = "0.1.31"
|
async-trait = "0.1.31"
|
||||||
|
|
||||||
|
matrix-sdk-common-macros = { version = "0.1.0", path = "../matrix_sdk_common_macros" }
|
||||||
matrix-sdk-common = { version = "0.1.0", path = "../matrix_sdk_common" }
|
matrix-sdk-common = { version = "0.1.0", path = "../matrix_sdk_common" }
|
||||||
|
|
||||||
olm-rs = { version = "0.5.0", features = ["serde"] }
|
olm-rs = { version = "0.5.0", features = ["serde"] }
|
||||||
|
|
|
@ -215,6 +215,12 @@ impl OlmMachine {
|
||||||
match &self.uploaded_signed_key_count {
|
match &self.uploaded_signed_key_count {
|
||||||
Some(count) => {
|
Some(count) => {
|
||||||
let max_keys = self.account.max_one_time_keys().await as u64;
|
let max_keys = self.account.max_one_time_keys().await as u64;
|
||||||
|
// If there are more keys already uploaded than max_key / 2
|
||||||
|
// bail out returning false, this also avoids overflow.
|
||||||
|
if count.load(Ordering::Relaxed) > (max_keys / 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
let key_count = (max_keys / 2) - count.load(Ordering::Relaxed);
|
let key_count = (max_keys / 2) - count.load(Ordering::Relaxed);
|
||||||
key_count > 0
|
key_count > 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,12 +27,16 @@ use super::device::Device;
|
||||||
use super::memory_stores::UserDevices;
|
use super::memory_stores::UserDevices;
|
||||||
use super::olm::{Account, InboundGroupSession, Session};
|
use super::olm::{Account, InboundGroupSession, Session};
|
||||||
use matrix_sdk_common::identifiers::{DeviceId, RoomId, UserId};
|
use matrix_sdk_common::identifiers::{DeviceId, RoomId, UserId};
|
||||||
|
use matrix_sdk_common_macros::send_sync;
|
||||||
use olm_rs::errors::{OlmAccountError, OlmGroupSessionError, OlmSessionError};
|
use olm_rs::errors::{OlmAccountError, OlmGroupSessionError, OlmSessionError};
|
||||||
|
|
||||||
pub mod memorystore;
|
pub mod memorystore;
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
#[cfg(feature = "sqlite-cryptostore")]
|
#[cfg(feature = "sqlite-cryptostore")]
|
||||||
pub mod sqlite;
|
pub mod sqlite;
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
#[cfg(feature = "sqlite-cryptostore")]
|
#[cfg(feature = "sqlite-cryptostore")]
|
||||||
use sqlx::Error as SqlxError;
|
use sqlx::Error as SqlxError;
|
||||||
|
|
||||||
|
@ -83,9 +87,10 @@ pub enum CryptoStoreError {
|
||||||
pub type Result<T> = std::result::Result<T, CryptoStoreError>;
|
pub type Result<T> = std::result::Result<T, CryptoStoreError>;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
#[cfg_attr(not(target_arch = "wasm32"), send_sync)]
|
||||||
/// Trait abstracting a store that the `OlmMachine` uses to store cryptographic
|
/// Trait abstracting a store that the `OlmMachine` uses to store cryptographic
|
||||||
/// keys.
|
/// keys.
|
||||||
pub trait CryptoStore: Debug + Send + Sync {
|
pub trait CryptoStore: Debug {
|
||||||
/// Load an account that was previously stored.
|
/// Load an account that was previously stored.
|
||||||
async fn load_account(&mut self) -> Result<Option<Account>>;
|
async fn load_account(&mut self) -> Result<Option<Account>>;
|
||||||
|
|
||||||
|
|
|
@ -345,6 +345,7 @@ impl EventBuilder {
|
||||||
|
|
||||||
/// Embedded sync reponse files
|
/// Embedded sync reponse files
|
||||||
pub enum SyncResponseFile {
|
pub enum SyncResponseFile {
|
||||||
|
All,
|
||||||
Default,
|
Default,
|
||||||
DefaultWithSummary,
|
DefaultWithSummary,
|
||||||
Invite,
|
Invite,
|
||||||
|
@ -354,6 +355,7 @@ pub enum SyncResponseFile {
|
||||||
/// Get specific API responses for testing
|
/// Get specific API responses for testing
|
||||||
pub fn sync_response(kind: SyncResponseFile) -> SyncResponse {
|
pub fn sync_response(kind: SyncResponseFile) -> SyncResponse {
|
||||||
let data = match kind {
|
let data = match kind {
|
||||||
|
SyncResponseFile::All => include_bytes!("../test_data/more_sync.json").to_vec(),
|
||||||
SyncResponseFile::Default => include_bytes!("../test_data/sync.json").to_vec(),
|
SyncResponseFile::Default => include_bytes!("../test_data/sync.json").to_vec(),
|
||||||
SyncResponseFile::DefaultWithSummary => {
|
SyncResponseFile::DefaultWithSummary => {
|
||||||
include_bytes!("../test_data/sync_with_summary.json").to_vec()
|
include_bytes!("../test_data/sync_with_summary.json").to_vec()
|
||||||
|
|
|
@ -0,0 +1,151 @@
|
||||||
|
{
|
||||||
|
"device_one_time_keys_count": {},
|
||||||
|
"next_batch": "s526_47314_0_7_1_1_1_11444_1",
|
||||||
|
"device_lists": {
|
||||||
|
"changed": [
|
||||||
|
"@example:example.org"
|
||||||
|
],
|
||||||
|
"left": []
|
||||||
|
},
|
||||||
|
"rooms": {
|
||||||
|
"invite": {},
|
||||||
|
"join": {
|
||||||
|
"!SVkFJHzfwvuaIEawgC:localhost": {
|
||||||
|
"summary": {},
|
||||||
|
"account_data": {
|
||||||
|
"events": []
|
||||||
|
},
|
||||||
|
"ephemeral": {
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"$151680659217152dPKjd:localhost": {
|
||||||
|
"m.read": {
|
||||||
|
"@example:localhost": {
|
||||||
|
"ts": 1516809890615
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "m.receipt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"user_ids": [
|
||||||
|
"@alice:matrix.org",
|
||||||
|
"@bob:example.com"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||||
|
"type": "m.typing"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"state": {
|
||||||
|
"events": []
|
||||||
|
},
|
||||||
|
"timeline": {
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"body": "baba",
|
||||||
|
"format": "org.matrix.custom.html",
|
||||||
|
"formatted_body": "<strong>baba</strong>",
|
||||||
|
"msgtype": "m.text"
|
||||||
|
},
|
||||||
|
"event_id": "$152037280074GZeOm:localhost",
|
||||||
|
"origin_server_ts": 1520372800469,
|
||||||
|
"sender": "@example:localhost",
|
||||||
|
"type": "m.room.message",
|
||||||
|
"unsigned": {
|
||||||
|
"age": 598971425
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"body": " * edited message",
|
||||||
|
"m.new_content": {
|
||||||
|
"body": "edited message",
|
||||||
|
"msgtype": "m.text"
|
||||||
|
},
|
||||||
|
"m.relates_to": {
|
||||||
|
"event_id": "some event id",
|
||||||
|
"rel_type": "m.replace"
|
||||||
|
},
|
||||||
|
"msgtype": "m.text"
|
||||||
|
},
|
||||||
|
"event_id": "edit event id",
|
||||||
|
"origin_server_ts": 1590262659984,
|
||||||
|
"sender": "@alice:matrix.org",
|
||||||
|
"type": "m.room.message",
|
||||||
|
"unsigned": {
|
||||||
|
"age": 85
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"reason": "😀"
|
||||||
|
},
|
||||||
|
"event_id": "$151957878228ssqrJ:localhost",
|
||||||
|
"origin_server_ts": 1519578782185,
|
||||||
|
"sender": "@example:localhost",
|
||||||
|
"type": "m.room.redaction",
|
||||||
|
"redacts": "$151957878228ssqrj:localhost"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {},
|
||||||
|
"event_id": "$15275046980maRLj:localhost",
|
||||||
|
"origin_server_ts": 1527504698685,
|
||||||
|
"sender": "@example:localhost",
|
||||||
|
"type": "m.room.message",
|
||||||
|
"unsigned": {
|
||||||
|
"age": 19334,
|
||||||
|
"redacted_because": {
|
||||||
|
"content": {},
|
||||||
|
"event_id": "$15275047031IXQRi:localhost",
|
||||||
|
"origin_server_ts": 1527504703496,
|
||||||
|
"redacts": "$15275046980maRLj:localhost",
|
||||||
|
"sender": "@example:localhost",
|
||||||
|
"type": "m.room.redaction",
|
||||||
|
"unsigned": {
|
||||||
|
"age": 14523
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"redacted_by": "$15275047031IXQRi:localhost"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"m.relates_to": {
|
||||||
|
"event_id": "some event id",
|
||||||
|
"key": "👍",
|
||||||
|
"rel_type": "m.annotation"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"event_id": "event id",
|
||||||
|
"origin_server_ts": 1590275813161,
|
||||||
|
"sender": "@alice:matrix.org",
|
||||||
|
"type": "m.reaction",
|
||||||
|
"unsigned": {
|
||||||
|
"age": 85
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"limited": true,
|
||||||
|
"prev_batch": "t392-516_47314_0_7_1_1_1_11444_1"
|
||||||
|
},
|
||||||
|
"unread_notifications": {
|
||||||
|
"highlight_count": 0,
|
||||||
|
"notification_count": 11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"leave": {}
|
||||||
|
},
|
||||||
|
"to_device": {
|
||||||
|
"events": []
|
||||||
|
},
|
||||||
|
"presence": {
|
||||||
|
"events": []
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,19 +1,19 @@
|
||||||
{
|
{
|
||||||
"content": {
|
"content": {
|
||||||
"body": " * f fjkdslasdf $$$$$$$$$$$$$$$$$$$$$$$$$$$$",
|
"body": " * edited message",
|
||||||
"m.new_content": {
|
"m.new_content": {
|
||||||
"body": "f fjkdslasdf $$$$$$$$$$$$$$$$$$$$$$$$$$$$",
|
"body": "edited message",
|
||||||
"msgtype": "m.text"
|
"msgtype": "m.text"
|
||||||
},
|
},
|
||||||
"m.relates_to": {
|
"m.relates_to": {
|
||||||
"event_id": "$MbS0nMfvub-CPbytp7KRmExAp3oVfdjWOvf2ifG1zWI",
|
"event_id": "some event id",
|
||||||
"rel_type": "m.replace"
|
"rel_type": "m.replace"
|
||||||
},
|
},
|
||||||
"msgtype": "m.text"
|
"msgtype": "m.text"
|
||||||
},
|
},
|
||||||
"event_id": "$xXL9cVB_10jkpxUFTsubeusygV0yv5b_63ADjgiQnOA",
|
"event_id": "edit event id",
|
||||||
"origin_server_ts": 1590262659984,
|
"origin_server_ts": 1590262659984,
|
||||||
"sender": "@devinr528:matrix.org",
|
"sender": "@alice:matrix.org",
|
||||||
"type": "m.room.message",
|
"type": "m.room.message",
|
||||||
"unsigned": {
|
"unsigned": {
|
||||||
"age": 85
|
"age": 85
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"content": {
|
"content": {
|
||||||
"m.relates_to": {
|
"m.relates_to": {
|
||||||
"event_id": "$MDit176PkuBlpP7S6c64iuf74KC2HqZ3peV1NrV4PKA",
|
"event_id": "$MDitXXXXXXuBlpP7S6c6XXXXXXXC2HqZ3peV1NrV4PKA",
|
||||||
"key": "👍",
|
"key": "👍",
|
||||||
"rel_type": "m.annotation"
|
"rel_type": "m.annotation"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"event_id": "$QZn9xEx72PUfd2tAGFH-FFgsffZlVMobk47Tl5Lpdtg",
|
"event_id": "$QZn9xEXXXXXfd2tAGFH-XXgsffZlVMobk47Tl5Lpdtg",
|
||||||
"origin_server_ts": 1590275813161,
|
"origin_server_ts": 1590275813161,
|
||||||
"sender": "@devinr528:matrix.org",
|
"sender": "@devinr528:matrix.org",
|
||||||
"type": "m.reaction",
|
"type": "m.reaction",
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
{
|
||||||
|
"device_one_time_keys_count": {},
|
||||||
|
"next_batch": "s526_47314_0_7_1_1_1_11444_1",
|
||||||
|
"device_lists": {
|
||||||
|
"changed": [
|
||||||
|
"@example:example.org"
|
||||||
|
],
|
||||||
|
"left": []
|
||||||
|
},
|
||||||
|
"rooms": {
|
||||||
|
"invite": {},
|
||||||
|
"join": {
|
||||||
|
"!SVkFJHzfwvuaIEawgC:localhost": {
|
||||||
|
"summary": {},
|
||||||
|
"account_data": {
|
||||||
|
"events": []
|
||||||
|
},
|
||||||
|
"ephemeral": {
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"$151680659217152dPKjd:localhost": {
|
||||||
|
"m.read": {
|
||||||
|
"@example:localhost": {
|
||||||
|
"ts": 1516809890615
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "m.receipt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"user_ids": [
|
||||||
|
"@alice:matrix.org",
|
||||||
|
"@bob:example.com"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||||
|
"type": "m.typing"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"state": {
|
||||||
|
"events": []
|
||||||
|
},
|
||||||
|
"timeline": {
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"body": "baba",
|
||||||
|
"format": "org.matrix.custom.html",
|
||||||
|
"formatted_body": "<strong>baba</strong>",
|
||||||
|
"msgtype": "m.text"
|
||||||
|
},
|
||||||
|
"event_id": "$152037280074GZeOm:localhost",
|
||||||
|
"origin_server_ts": 1520372800469,
|
||||||
|
"sender": "@example:localhost",
|
||||||
|
"type": "m.room.message",
|
||||||
|
"unsigned": {
|
||||||
|
"age": 598971425
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"body": " * edited message",
|
||||||
|
"m.new_content": {
|
||||||
|
"body": "edited message",
|
||||||
|
"msgtype": "m.text"
|
||||||
|
},
|
||||||
|
"m.relates_to": {
|
||||||
|
"event_id": "some event id",
|
||||||
|
"rel_type": "m.replace"
|
||||||
|
},
|
||||||
|
"msgtype": "m.text"
|
||||||
|
},
|
||||||
|
"event_id": "edit event id",
|
||||||
|
"origin_server_ts": 1590262659984,
|
||||||
|
"sender": "@alice:matrix.org",
|
||||||
|
"type": "m.room.message",
|
||||||
|
"unsigned": {
|
||||||
|
"age": 85
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"reason": "😀"
|
||||||
|
},
|
||||||
|
"event_id": "$151957878228ssqrJ:localhost",
|
||||||
|
"origin_server_ts": 1519578782185,
|
||||||
|
"sender": "@example:localhost",
|
||||||
|
"type": "m.room.redaction",
|
||||||
|
"redacts": "$151957878228ssqrj:localhost"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"m.relates_to": {
|
||||||
|
"event_id": "some event id",
|
||||||
|
"key": "👍",
|
||||||
|
"rel_type": "m.annotation"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"event_id": "event id",
|
||||||
|
"origin_server_ts": 1590275813161,
|
||||||
|
"sender": "@alice:matrix.org",
|
||||||
|
"type": "m.reaction",
|
||||||
|
"unsigned": {
|
||||||
|
"age": 85
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"limited": true,
|
||||||
|
"prev_batch": "t392-516_47314_0_7_1_1_1_11444_1"
|
||||||
|
},
|
||||||
|
"unread_notifications": {
|
||||||
|
"highlight_count": 0,
|
||||||
|
"notification_count": 11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"leave": {}
|
||||||
|
},
|
||||||
|
"to_device": {
|
||||||
|
"events": []
|
||||||
|
},
|
||||||
|
"presence": {
|
||||||
|
"events": []
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"errcode": "M_FORBIDDEN",
|
||||||
|
"error": "Invalid password",
|
||||||
|
"completed": ["example.type.foo"],
|
||||||
|
"flows": [
|
||||||
|
{
|
||||||
|
"stages": ["example.type.foo", "example.type.bar"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"stages": ["example.type.foo", "example.type.baz"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"params": {
|
||||||
|
"example.type.baz": {
|
||||||
|
"example_key": "foobar"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"session": "xxxxxx"
|
||||||
|
}
|
Loading…
Reference in New Issue