base: Re-introduce the event emitter.

master
Damir Jelić 2020-12-19 20:20:39 +01:00
parent f9af880176
commit a370eb1e37
9 changed files with 264 additions and 54 deletions

View File

@ -4,7 +4,7 @@ use tokio::time::{delay_for, Duration};
use matrix_sdk::{
self,
events::{room::member::MemberEventContent, StrippedStateEvent},
Client, ClientConfig, EventEmitter, SyncRoom, SyncSettings,
Client, ClientConfig, EventEmitter, RoomState, SyncSettings,
};
use matrix_sdk_common_macros::async_trait;
use url::Url;
@ -23,7 +23,7 @@ impl AutoJoinBot {
impl EventEmitter for AutoJoinBot {
async fn on_stripped_state_member(
&self,
room: SyncRoom,
room: RoomState,
room_member: &StrippedStateEvent<MemberEventContent>,
_: Option<MemberEventContent>,
) {
@ -31,29 +31,30 @@ impl EventEmitter for AutoJoinBot {
return;
}
if let SyncRoom::Invited(room) = room {
let room = room.read().await;
println!("Autojoining room {}", room.room_id);
if let RoomState::Invited(room) = room {
println!("Autojoining room {}", room.room_id());
let mut delay = 2;
while let Err(err) = self.client.join_room_by_id(&room.room_id).await {
while let Err(err) = self.client.join_room_by_id(room.room_id()).await {
// retry autojoin due to synapse sending invites, before the
// invited user can join for more information see
// https://github.com/matrix-org/synapse/issues/4345
eprintln!(
"Failed to join room {} ({:?}), retrying in {}s",
room.room_id, err, delay
room.room_id(),
err,
delay
);
delay_for(Duration::from_secs(delay)).await;
delay *= 2;
if delay > 3600 {
eprintln!("Can't join room {} ({:?})", room.room_id, err);
eprintln!("Can't join room {} ({:?})", room.room_id(), err);
break;
}
}
println!("Successfully joined room {}", room.room_id);
println!("Successfully joined room {}", room.room_id());
}
}
}
@ -69,7 +70,7 @@ async fn login_and_sync(
let client_config = ClientConfig::new().store_path(home);
let homeserver_url = Url::parse(&homeserver_url).expect("Couldn't parse the homeserver URL");
let mut client = Client::new_with_config(homeserver_url, client_config).unwrap();
let client = Client::new_with_config(homeserver_url, client_config).unwrap();
client
.login(username, password, None, Some("autojoin bot"))

View File

@ -6,7 +6,7 @@ use matrix_sdk::{
room::message::{MessageEventContent, TextMessageEventContent},
AnyMessageEventContent, SyncMessageEvent,
},
Client, ClientConfig, EventEmitter, JsonStore, SyncRoom, SyncSettings,
Client, ClientConfig, EventEmitter, RoomState, SyncSettings,
};
use matrix_sdk_common_macros::async_trait;
use url::Url;
@ -25,8 +25,12 @@ impl CommandBot {
#[async_trait]
impl EventEmitter for CommandBot {
async fn on_room_message(&self, room: SyncRoom, event: &SyncMessageEvent<MessageEventContent>) {
if let SyncRoom::Joined(room) = room {
async fn on_room_message(
&self,
room: RoomState,
event: &SyncMessageEvent<MessageEventContent>,
) {
if let RoomState::Joined(room) = room {
let msg_body = if let SyncMessageEvent {
content: MessageEventContent::Text(TextMessageEventContent { body: msg_body, .. }),
..
@ -41,15 +45,13 @@ impl EventEmitter for CommandBot {
let content = AnyMessageEventContent::RoomMessage(MessageEventContent::text_plain(
"🎉🎊🥳 let's PARTY!! 🥳🎊🎉",
));
// we clone here to hold the lock for as little time as possible.
let room_id = room.read().await.room_id.clone();
println!("sending");
self.client
// send our message to the room we found the "!party" command in
// the last parameter is an optional Uuid which we don't care about.
.room_send(&room_id, content, None)
.room_send(room.room_id(), content, None)
.await
.unwrap();
@ -68,15 +70,14 @@ async fn login_and_sync(
let mut home = dirs::home_dir().expect("no home directory found");
home.push("party_bot");
let store = JsonStore::open(&home)?;
let client_config = ClientConfig::new()
.proxy("http://localhost:8080")?
.disable_ssl_verification()
.state_store(Box::new(store));
.store_path(home);
let homeserver_url = Url::parse(&homeserver_url).expect("Couldn't parse the homeserver URL");
// create a new Client with the given homeserver url and config
let mut client = Client::new_with_config(homeserver_url, client_config).unwrap();
let client = Client::new_with_config(homeserver_url, client_config).unwrap();
client
.login(&username, &password, None, Some("command bot"))

View File

@ -14,7 +14,7 @@ use matrix_sdk::{
room::message::{MessageEventContent, TextMessageEventContent},
SyncMessageEvent,
},
Client, ClientConfig, EventEmitter, SyncRoom, SyncSettings,
Client, ClientConfig, EventEmitter, RoomState, SyncSettings,
};
use matrix_sdk_common_macros::async_trait;
use url::Url;
@ -33,8 +33,12 @@ impl ImageBot {
#[async_trait]
impl EventEmitter for ImageBot {
async fn on_room_message(&self, room: SyncRoom, event: &SyncMessageEvent<MessageEventContent>) {
if let SyncRoom::Joined(room) = room {
async fn on_room_message(
&self,
room: RoomState,
event: &SyncMessageEvent<MessageEventContent>,
) {
if let RoomState::Joined(room) = room {
let msg_body = if let SyncMessageEvent {
content: MessageEventContent::Text(TextMessageEventContent { body: msg_body, .. }),
..
@ -46,13 +50,17 @@ impl EventEmitter for ImageBot {
};
if msg_body.contains("!image") {
let room_id = room.read().await.room_id.clone();
println!("sending image");
let mut image = self.image.lock().await;
self.client
.room_send_attachment(&room_id, "cat", &mime::IMAGE_JPEG, &mut *image, None)
.room_send_attachment(
room.room_id(),
"cat",
&mime::IMAGE_JPEG,
&mut *image,
None,
)
.await
.unwrap();
@ -75,7 +83,7 @@ async fn login_and_sync(
.disable_ssl_verification();
let homeserver_url = Url::parse(&homeserver_url).expect("Couldn't parse the homeserver URL");
let mut client = Client::new_with_config(homeserver_url, client_config).unwrap();
let client = Client::new_with_config(homeserver_url, client_config).unwrap();
client
.login(&username, &password, None, Some("command bot"))

View File

@ -7,7 +7,7 @@ use matrix_sdk::{
room::message::{MessageEventContent, TextMessageEventContent},
SyncMessageEvent,
},
Client, ClientConfig, EventEmitter, SyncRoom, SyncSettings,
Client, ClientConfig, EventEmitter, RoomState, SyncSettings,
};
use matrix_sdk_common_macros::async_trait;
@ -15,21 +15,22 @@ struct EventCallback;
#[async_trait]
impl EventEmitter for EventCallback {
async fn on_room_message(&self, room: SyncRoom, event: &SyncMessageEvent<MessageEventContent>) {
if let SyncRoom::Joined(room) = room {
async fn on_room_message(
&self,
room: RoomState,
event: &SyncMessageEvent<MessageEventContent>,
) {
if let RoomState::Joined(room) = room {
if let SyncMessageEvent {
content: MessageEventContent::Text(TextMessageEventContent { body: msg_body, .. }),
sender,
..
} = event
{
let name = {
// any reads should be held for the shortest time possible to
// avoid dead locks
let room = room.read().await;
let member = room.joined_members.get(&sender).unwrap();
member.name()
};
let member = room.get_member(&sender).await.unwrap();
let name = member
.display_name()
.unwrap_or_else(|| member.user_id().as_str());
println!("{}: {}", name, msg_body);
}
}
@ -45,7 +46,7 @@ async fn login(
.proxy("http://localhost:8080")?
.disable_ssl_verification();
let homeserver_url = Url::parse(&homeserver_url).expect("Couldn't parse the homeserver URL");
let mut client = Client::new_with_config(homeserver_url, client_config).unwrap();
let client = Client::new_with_config(homeserver_url, client_config).unwrap();
client.add_event_emitter(Box::new(EventCallback)).await;

View File

@ -41,7 +41,7 @@ use tracing::{debug, warn};
use tracing::{error, info, instrument};
use matrix_sdk_base::{
responses::SyncResponse, BaseClient, BaseClientConfig, JoinedRoom, Room, Session, Store,
responses::SyncResponse, BaseClient, BaseClientConfig, EventEmitter, JoinedRoom, Session, Store,
};
#[cfg(feature = "encryption")]
@ -549,11 +549,12 @@ impl Client {
Ok(())
}
///// Add `EventEmitter` to `Client`.
/////
///// The methods of `EventEmitter` are called when the respective `RoomEvents` occur.
//pub async fn add_event_emitter(&mut self, emitter: Box<dyn EventEmitter>) {
//}
/// Add `EventEmitter` to `Client`.
///
/// The methods of `EventEmitter` are called when the respective `RoomEvents` occur.
pub async fn add_event_emitter(&self, emitter: Box<dyn EventEmitter>) {
self.base_client.add_event_emitter(emitter).await;
}
// /// Returns the joined rooms this client knows about.
// pub fn joined_rooms(&self) -> Arc<RwLock<HashMap<RoomId, Arc<RwLock<Room>>>>> {

View File

@ -68,7 +68,7 @@ compile_error!("only one of 'native-tls' or 'rustls-tls' features can be enabled
pub use matrix_sdk_base::crypto::LocalTrust;
pub use matrix_sdk_base::{
Error as BaseError, EventEmitter, InvitedRoom, JoinedRoom, LeftRoom, RoomInfo, RoomMember,
Session,
RoomState, Session,
};
pub use matrix_sdk_common::*;

View File

@ -58,6 +58,7 @@ use zeroize::Zeroizing;
use crate::{
error::Result,
event_emitter::Emitter,
responses::{
AccountData, Ephemeral, InviteState, InvitedRoom as InvitedRoomResponse,
JoinedRoom as JoinedRoomResponse, LeftRoom as LeftRoomResponse, MemberEvent, Presence,
@ -188,7 +189,7 @@ pub struct BaseClient {
store_passphrase: Arc<Zeroizing<String>>,
/// Any implementor of EventEmitter will act as the callbacks for various
/// events.
event_emitter: Arc<RwLock<Option<Box<dyn EventEmitter>>>>,
event_emitter: Arc<RwLock<Option<Emitter>>>,
}
#[cfg(not(tarpaulin_include))]
@ -419,6 +420,7 @@ impl BaseClient {
///
/// The methods of `EventEmitter` are called when the respective `RoomEvents` occur.
pub async fn add_event_emitter(&self, emitter: Box<dyn EventEmitter>) {
let emitter = Emitter { inner: emitter };
*self.event_emitter.write().await = Some(emitter);
}
@ -832,7 +834,7 @@ impl BaseClient {
info!("Processed a sync response in {:?}", now.elapsed().unwrap());
Ok(SyncResponse {
let response = SyncResponse {
next_batch: response.next_batch,
rooms,
presence: Presence {
@ -849,11 +851,16 @@ impl BaseClient {
.collect(),
..Default::default()
})
};
if let Some(emitter) = self.event_emitter.read().await.as_ref() {
emitter.emit_sync(&response).await;
}
Ok(response)
}
async fn apply_changes(&self, changes: &StateChanges) {
// TODO emit room changes here
for (room_id, room_info) in &changes.room_infos {
if let Some(room) = self.get_bare_room(&room_id) {
room.update_summary(room_info.clone())

View File

@ -12,9 +12,9 @@
// 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.
use std::sync::Arc;
use std::ops::Deref;
use matrix_sdk_common::locks::RwLock;
use matrix_sdk_common::{events::AnySyncRoomEvent, identifiers::RoomId};
use serde_json::value::RawValue as RawJsonValue;
use crate::{
@ -38,13 +38,203 @@ use crate::{
tombstone::TombstoneEventContent,
},
typing::TypingEventContent,
BasicEvent, StrippedStateEvent, SyncEphemeralRoomEvent, SyncMessageEvent, SyncStateEvent,
AnyBasicEvent, AnyStrippedStateEvent, AnySyncEphemeralRoomEvent, AnySyncMessageEvent,
AnySyncStateEvent, BasicEvent, StrippedStateEvent, SyncEphemeralRoomEvent,
SyncMessageEvent, SyncStateEvent,
},
responses::SyncResponse,
rooms::RoomState,
Room,
};
use matrix_sdk_common_macros::async_trait;
pub(crate) struct Emitter {
pub(crate) inner: Box<dyn EventEmitter>,
}
impl Deref for Emitter {
type Target = dyn EventEmitter;
fn deref(&self) -> &Self::Target {
&*self.inner
}
}
impl Emitter {
fn get_room(&self, _room_id: &RoomId) -> Option<RoomState> {
todo!()
}
pub(crate) async fn emit_sync(&self, response: &SyncResponse) {
for (room_id, room_info) in &response.rooms.join {
if let Some(room) = self.get_room(room_id) {
for event in &room_info.ephemeral.events {
self.emit_ephemeral_event(room.clone(), event).await;
}
for event in &room_info.account_data.events {
self.emit_account_data_event(room.clone(), event).await;
}
for event in &room_info.state.events {
self.emit_state_event(room.clone(), event).await;
}
for event in &room_info.timeline.events {
self.emit_timeline_event(room.clone(), event).await;
}
}
}
for (room_id, room_info) in &response.rooms.leave {
if let Some(room) = self.get_room(room_id) {
for event in &room_info.account_data.events {
self.emit_account_data_event(room.clone(), event).await;
}
for event in &room_info.state.events {
self.emit_state_event(room.clone(), event).await;
}
for event in &room_info.timeline.events {
self.emit_timeline_event(room.clone(), event).await;
}
}
}
for (room_id, room_info) in &response.rooms.invite {
if let Some(room) = self.get_room(room_id) {
for event in &room_info.invite_state.events {
self.emit_stripped_state_event(room.clone(), event).await;
}
}
}
for event in &response.presence.events {
self.on_presence_event(event).await;
}
}
async fn emit_timeline_event(&self, room: RoomState, event: &AnySyncRoomEvent) {
match event {
AnySyncRoomEvent::State(event) => match event {
AnySyncStateEvent::RoomMember(e) => self.on_room_member(room, e).await,
AnySyncStateEvent::RoomName(e) => self.on_room_name(room, e).await,
AnySyncStateEvent::RoomCanonicalAlias(e) => {
self.on_room_canonical_alias(room, e).await
}
AnySyncStateEvent::RoomAliases(e) => self.on_room_aliases(room, e).await,
AnySyncStateEvent::RoomAvatar(e) => self.on_room_avatar(room, e).await,
AnySyncStateEvent::RoomPowerLevels(e) => self.on_room_power_levels(room, e).await,
AnySyncStateEvent::RoomTombstone(e) => self.on_room_tombstone(room, e).await,
AnySyncStateEvent::RoomJoinRules(e) => self.on_room_join_rules(room, e).await,
AnySyncStateEvent::Custom(e) => {
self.on_custom_event(room, &CustomEvent::State(e)).await
}
_ => {}
},
AnySyncRoomEvent::Message(event) => match event {
AnySyncMessageEvent::RoomMessage(e) => self.on_room_message(room, e).await,
AnySyncMessageEvent::RoomMessageFeedback(e) => {
self.on_room_message_feedback(room, e).await
}
AnySyncMessageEvent::RoomRedaction(e) => self.on_room_redaction(room, e).await,
AnySyncMessageEvent::Custom(e) => {
self.on_custom_event(room, &CustomEvent::Message(e)).await
}
_ => {}
},
AnySyncRoomEvent::RedactedState(_event) => {}
AnySyncRoomEvent::RedactedMessage(_event) => {}
}
}
async fn emit_state_event(&self, room: RoomState, event: &AnySyncStateEvent) {
match event {
AnySyncStateEvent::RoomMember(member) => self.on_state_member(room, &member).await,
AnySyncStateEvent::RoomName(name) => self.on_state_name(room, &name).await,
AnySyncStateEvent::RoomCanonicalAlias(canonical) => {
self.on_state_canonical_alias(room, &canonical).await
}
AnySyncStateEvent::RoomAliases(aliases) => self.on_state_aliases(room, &aliases).await,
AnySyncStateEvent::RoomAvatar(avatar) => self.on_state_avatar(room, &avatar).await,
AnySyncStateEvent::RoomPowerLevels(power) => {
self.on_state_power_levels(room, &power).await
}
AnySyncStateEvent::RoomJoinRules(rules) => self.on_state_join_rules(room, &rules).await,
AnySyncStateEvent::RoomTombstone(tomb) => {
// TODO make `on_state_tombstone` method
self.on_room_tombstone(room, &tomb).await
}
AnySyncStateEvent::Custom(custom) => {
self.on_custom_event(room, &CustomEvent::State(custom))
.await
}
_ => {}
}
}
pub(crate) async fn emit_stripped_state_event(
&self,
// TODO these events are only emitted in invited rooms.
room: RoomState,
event: &AnyStrippedStateEvent,
) {
match event {
AnyStrippedStateEvent::RoomMember(member) => {
self.on_stripped_state_member(room, &member, None).await
}
AnyStrippedStateEvent::RoomName(name) => self.on_stripped_state_name(room, &name).await,
AnyStrippedStateEvent::RoomCanonicalAlias(canonical) => {
self.on_stripped_state_canonical_alias(room, &canonical)
.await
}
AnyStrippedStateEvent::RoomAliases(aliases) => {
self.on_stripped_state_aliases(room, &aliases).await
}
AnyStrippedStateEvent::RoomAvatar(avatar) => {
self.on_stripped_state_avatar(room, &avatar).await
}
AnyStrippedStateEvent::RoomPowerLevels(power) => {
self.on_stripped_state_power_levels(room, &power).await
}
AnyStrippedStateEvent::RoomJoinRules(rules) => {
self.on_stripped_state_join_rules(room, &rules).await
}
_ => {}
}
}
pub(crate) async fn emit_account_data_event(&self, room: RoomState, event: &AnyBasicEvent) {
match event {
AnyBasicEvent::Presence(presence) => self.on_non_room_presence(room, &presence).await,
AnyBasicEvent::IgnoredUserList(ignored) => {
self.on_non_room_ignored_users(room, &ignored).await
}
AnyBasicEvent::PushRules(rules) => self.on_non_room_push_rules(room, &rules).await,
_ => {}
}
}
pub(crate) async fn emit_ephemeral_event(
&self,
room: RoomState,
event: &AnySyncEphemeralRoomEvent,
) {
match event {
AnySyncEphemeralRoomEvent::FullyRead(full_read) => {
self.on_non_room_fully_read(room, full_read).await
}
AnySyncEphemeralRoomEvent::Typing(typing) => {
self.on_non_room_typing(room, typing).await
}
AnySyncEphemeralRoomEvent::Receipt(receipt) => {
self.on_non_room_receipt(room, receipt).await
}
_ => {}
}
}
}
/// This represents the various "unrecognized" events.
#[derive(Clone, Copy, Debug)]
pub enum CustomEvent<'c> {
@ -259,7 +449,7 @@ pub trait EventEmitter: Send + Sync {
// `PresenceEvent` is a struct so there is only the one method
/// Fires when `Client` receives a `NonRoomEvent::RoomAliases` event.
async fn on_presence_event(&self, _: RoomState, _: &PresenceEvent) {}
async fn on_presence_event(&self, _: &PresenceEvent) {}
/// Fires when `Client` receives a `Event::Custom` event or if deserialization fails
/// because the event was unknown to ruma.
@ -477,7 +667,7 @@ mod test {
) {
self.0.lock().await.push("receipt event".to_string())
}
async fn on_presence_event(&self, _: RoomState, _: &PresenceEvent) {
async fn on_presence_event(&self, _: &PresenceEvent) {
self.0.lock().await.push("presence event".to_string())
}
async fn on_unrecognized_event(&self, _: RoomState, _: &RawJsonValue) {

View File

@ -64,6 +64,7 @@ pub struct JoinedRoom {
pub(crate) inner: Room,
}
// TODO do we wan't to deref here or have separate implementations.
impl Deref for JoinedRoom {
type Target = Room;