diff --git a/src/async_client.rs b/src/async_client.rs index 7557536c..9d9635e0 100644 --- a/src/async_client.rs +++ b/src/async_client.rs @@ -40,7 +40,7 @@ pub use ruma_events::EventType; use ruma_identifiers::{RoomId, RoomIdOrAliasId, UserId}; #[cfg(feature = "encryption")] -use ruma_identifiers::{DeviceId, UserId}; +use ruma_identifiers::DeviceId; use crate::api; use crate::base_client::Client as BaseClient; @@ -190,6 +190,7 @@ use api::r0::membership::{ Invite3pid, }; use api::r0::message::create_message_event; +use api::r0::message::get_message_events; use api::r0::room::create_room; use api::r0::session::login; use api::r0::sync::sync_events; @@ -472,7 +473,7 @@ impl AsyncClient { /// /// # Arguments /// - /// * room - the easiest way to create this request is using `RoomBuilder` struct. + /// * room - the easiest way to create this request is using the `RoomBuilder`. /// /// # Examples /// ```ignore @@ -497,6 +498,34 @@ impl AsyncClient { 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 + /// + /// * request - The easiest way to create a `Request` is using the `GetMessageBuilder` + pub async fn get_message_events>( + &mut self, + request: R, + ) -> Result { + let req = request.into(); + let room_id = req.room_id.clone(); + let mut res = self.send(req).await?; + let mut client = self.base_client.write().await; + // TODO should we support this event? to keep emitting these msg events this is needed + for mut event in &mut res.chunk { + client + .receive_joined_timeline_event(&room_id, &mut event) + .await; + + if let EventResult::Ok(e) = event { + client.emit_timeline_event(&room_id, e).await; + } + } + Ok(res) + } + /// Synchronize the client's state with the latest state on the server. /// /// # Arguments diff --git a/src/lib.rs b/src/lib.rs index 3cbf4379..1e9412ca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,6 +51,6 @@ pub use async_client::{AsyncClient, AsyncClientConfig, SyncSettings}; pub use base_client::Client; pub use event_emitter::EventEmitter; pub use models::Room; -pub use request_builder::RoomBuilder; +pub use request_builder::{GetMessageBuilder, RoomBuilder}; pub(crate) const VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/src/request_builder.rs b/src/request_builder.rs index e174f8b7..1553dee8 100644 --- a/src/request_builder.rs +++ b/src/request_builder.rs @@ -1,20 +1,28 @@ -use crate::events::room::power_levels::PowerLevelsEventContent; -use crate::identifiers::UserId; use crate::api; +use crate::events::room::power_levels::PowerLevelsEventContent; +use crate::identifiers::{RoomId, UserId}; +use api::r0::filter::RoomEventFilter; use api::r0::membership::Invite3pid; +use api::r0::message::get_message_events::{self, Direction}; use api::r0::room::{ create_room::{self, CreationContent, InitialStateEvent, RoomPreset}, Visibility, }; +use js_int::UInt; + /// A builder used to create rooms. /// /// # Examples /// ``` +/// # use std::convert::TryFrom; /// # use matrix_sdk::{AsyncClient, RoomBuilder}; /// # use matrix_sdk::api::r0::room::Visibility; +/// # use matrix_sdk::identifiers::UserId; /// # 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 bldr = RoomBuilder::default(); /// bldr.creation_content(false) /// .initial_state(vec![]) @@ -22,9 +30,7 @@ use api::r0::room::{ /// .name("name") /// .room_version("v1.0"); /// let mut cli = AsyncClient::new(homeserver, None).unwrap(); -/// # use futures::executor::block_on; -/// # block_on(async { -/// assert!(cli.create_room(bldr).await.is_err()); +/// cli.create_room(bldr).await; /// # }) /// ``` #[derive(Clone, Default)] @@ -80,70 +86,60 @@ impl RoomBuilder { } /// Set the `InitialStateEvent` vector. - /// pub fn initial_state(&mut self, state: Vec) -> &mut Self { self.initial_state = state; self } /// Set the vec of `UserId`s. - /// pub fn invite(&mut self, invite: Vec) -> &mut Self { self.invite = invite; self } /// Set the vec of `Invite3pid`s. - /// pub fn invite_3pid(&mut self, invite: Vec) -> &mut Self { self.invite_3pid = invite; self } /// Set the vec of `Invite3pid`s. - /// pub fn is_direct(&mut self, direct: bool) -> &mut Self { self.is_direct = Some(direct); self } /// Set the room name. A `m.room.name` event will be sent to the room. - /// pub fn name>(&mut self, name: S) -> &mut Self { self.name = Some(name.into()); self } /// Set the room's power levels. - /// pub fn power_level_override(&mut self, power: PowerLevelsEventContent) -> &mut Self { self.power_level_content_override = Some(power); self } /// Convenience for setting various default state events based on a preset. - /// pub fn preset(&mut self, preset: RoomPreset) -> &mut Self { self.preset = Some(preset); self } /// The local part of a room alias. - /// pub fn room_alias_name>(&mut self, alias: S) -> &mut Self { self.room_alias_name = Some(alias.into()); self } /// Room version, defaults to homeserver's version if left unspecified. - /// pub fn room_version>(&mut self, version: S) -> &mut Self { self.room_version = Some(version.into()); self } /// If included, a `m.room.topic` event will be sent to the room. - /// pub fn topic>(&mut self, topic: S) -> &mut Self { self.topic = Some(topic.into()); self @@ -152,7 +148,6 @@ impl RoomBuilder { /// A public visibility indicates that the room will be shown in the published /// room list. A private visibility will hide the room from the published room list. /// Rooms default to private visibility if this key is not included. - /// pub fn visibility(&mut self, vis: Visibility) -> &mut Self { self.visibility = Some(vis); self @@ -178,14 +173,131 @@ impl Into for RoomBuilder { } } +/// Create a builder for making get_message_event requests. +#[derive(Clone, Default)] +pub struct GetMessageBuilder { + /// The room to get events from. + room_id: Option, + /// The token to start returning events from. + /// + /// This token can be obtained from a + /// prev_batch token returned for each room by the sync API, or from a start or end token + /// returned by a previous request to this endpoint. + from: Option, + /// The token to stop returning events at. + /// + /// This token can be obtained from a prev_batch + /// token returned for each room by the sync endpoint, or from a start or end token returned + /// by a previous request to this endpoint. + to: Option, + /// The direction to return events from. + direction: Option, + /// The maximum number of events to return. + /// + /// Default: 10. + limit: Option, + /// A filter of the returned events with. + filter: Option, +} + +impl GetMessageBuilder { + /// Create a `GetMessageBuilder` builder to make a `get_message_events::Request`. + /// + /// The `room_id` and `from`` fields **need to be set** to create the request. + /// + /// # Examples + /// ``` + /// # use matrix_sdk::{AsyncClient, GetMessageBuilder}; + /// # use matrix_sdk::api::r0::message::get_message_events::{self, Direction}; + /// # use matrix_sdk::identifiers::RoomId; + /// # use url::Url; + /// # let homeserver = Url::parse("http://example.com").unwrap(); + /// # let mut rt = tokio::runtime::Runtime::new().unwrap(); + /// # rt.block_on(async { + /// # let room_id = RoomId::new(homeserver.as_str()).unwrap(); + /// # let last_sync_token = "".to_string();; + /// let mut cli = AsyncClient::new(homeserver, None).unwrap(); + /// + /// let mut bldr = GetMessageBuilder::new(); + /// bldr.room_id(room_id) + /// .from(last_sync_token) + /// .direction(Direction::Forward); + /// + /// cli.get_message_events(bldr).await.is_err(); + /// # }) + /// ``` + pub fn new() -> Self { + Self::default() + } + + /// RoomId is required to create a `get_message_events::Request`. + pub fn room_id(&mut self, room_id: RoomId) -> &mut Self { + self.room_id = Some(room_id); + self + } + + /// A `next_batch` token or `start` or `end` from a previous `get_message_events` request. + /// + /// This is required to create a `get_message_events::Request`. + pub fn from(&mut self, from: String) -> &mut Self { + self.from = Some(from); + self + } + + /// A `next_batch` token or `start` or `end` from a previous `get_message_events` request. + /// + /// This token signals when to stop receiving events. + pub fn to(&mut self, to: String) -> &mut Self { + self.to = Some(to); + self + } + + /// The direction to return events from. + /// + /// If not specified `Direction::Backward` is used. + pub fn direction(&mut self, direction: Direction) -> &mut Self { + self.direction = Some(direction); + self + } + + /// The maximum number of events to return. + pub fn limit(&mut self, limit: UInt) -> &mut Self { + self.limit = Some(limit); + self + } + + /// Filter events by the given `RoomEventFilter`. + pub fn filter(&mut self, filter: RoomEventFilter) -> &mut Self { + self.filter = Some(filter); + self + } +} + +impl Into for GetMessageBuilder { + fn into(self) -> get_message_events::Request { + get_message_events::Request { + room_id: self.room_id.expect("`room_id` and `from` need to be set"), + from: self.from.expect("`room_id` and `from` need to be set"), + to: self.to, + dir: self.direction.unwrap_or(Direction::Backward), + limit: self.limit, + filter: self.filter, + } + } +} + #[cfg(test)] mod test { - use super::*; + use std::collections::HashMap; - use crate::{AsyncClient, Session}; - use mockito::mock; - use url::Url; + use super::*; + use crate::{AsyncClient, Session, identifiers::RoomId}; + use crate::events::room::power_levels::NotificationPowerLevels; + + use js_int::Int; + use mockito::{mock, Matcher}; use std::convert::TryFrom; + use url::Url; #[tokio::test] async fn create_room_builder() { @@ -202,13 +314,60 @@ mod test { device_id: "DEVICEID".to_owned(), }; - let mut bldr = RoomBuilder::default(); + let mut bldr = RoomBuilder::new(); bldr.creation_content(false) .initial_state(vec![]) .visibility(Visibility::Public) - .name("name") - .room_version("v1.0"); + .name("room_name") + .room_version("v1.0") + .invite_3pid(vec![]) + .is_direct(true) + .power_level_override(PowerLevelsEventContent { + ban: Int::max_value(), + events: HashMap::default(), + events_default: Int::min_value(), + invite: Int::min_value(), + kick: Int::min_value(), + redact: Int::max_value(), + state_default: Int::min_value(), + users_default: Int::min_value(), + notifications: NotificationPowerLevels { room: Int::min_value() }, + users: HashMap::default(), + }) + .preset(RoomPreset::PrivateChat) + .room_alias_name("room_alias") + .topic("room topic") + .visibility(Visibility::Private); let mut cli = AsyncClient::new(homeserver, Some(session)).unwrap(); assert!(cli.create_room(bldr).await.is_ok()); } + + #[tokio::test] + async fn get_message_events() { + let homeserver = Url::parse(&mockito::server_url()).unwrap(); + + let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/messages".to_string())) + .with_status(200) + .with_body_from_file("./tests/data/room_messages.json") + .create(); + + let session = Session { + access_token: "1234".to_owned(), + user_id: UserId::try_from("@example:localhost").unwrap(), + device_id: "DEVICEID".to_owned(), + }; + + let mut bldr = GetMessageBuilder::new(); + bldr + .room_id(RoomId::try_from("!roomid:example.com").unwrap()) + .from("t47429-4392820_219380_26003_2265".to_string()) + .to("t4357353_219380_26003_2265".to_string()) + .direction(Direction::Backward) + .limit(UInt::new(10).unwrap()); + // TODO this makes ruma error `Err(IntoHttp(IntoHttpError(Query(Custom("unsupported value")))))`?? + // .filter(RoomEventFilter::default()); + + let mut cli = AsyncClient::new(homeserver, Some(session)).unwrap(); + assert!(cli.get_message_events(bldr).await.is_ok()); + } }