use std::sync::Arc;
use matrix_sdk_common::locks::RwLock;
use serde_json::value::RawValue as RawJsonValue;
use crate::{
events::{
call::{
answer::AnswerEventContent, candidates::CandidatesEventContent,
hangup::HangupEventContent, invite::InviteEventContent,
},
custom::CustomEventContent,
fully_read::FullyReadEventContent,
ignored_user_list::IgnoredUserListEventContent,
presence::PresenceEvent,
push_rules::PushRulesEventContent,
receipt::ReceiptEventContent,
room::{
aliases::AliasesEventContent,
avatar::AvatarEventContent,
canonical_alias::CanonicalAliasEventContent,
join_rules::JoinRulesEventContent,
member::MemberEventContent,
message::{feedback::FeedbackEventContent, MessageEventContent as MsgEventContent},
name::NameEventContent,
power_levels::PowerLevelsEventContent,
redaction::SyncRedactionEvent,
tombstone::TombstoneEventContent,
},
typing::TypingEventContent,
BasicEvent, StrippedStateEvent, SyncEphemeralRoomEvent, SyncMessageEvent, SyncStateEvent,
},
Room, RoomState,
};
use matrix_sdk_common::async_trait;
pub type SyncRoom = RoomState<Arc<RwLock<Room>>>;
#[derive(Clone, Copy, Debug)]
pub enum CustomEvent<'c> {
Basic(&'c BasicEvent<CustomEventContent>),
EphemeralRoom(&'c SyncEphemeralRoomEvent<CustomEventContent>),
Message(&'c SyncMessageEvent<CustomEventContent>),
State(&'c SyncStateEvent<CustomEventContent>),
StrippedState(&'c StrippedStateEvent<CustomEventContent>),
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait EventEmitter: Send + Sync {
async fn on_room_member(&self, _: SyncRoom, _: &SyncStateEvent<MemberEventContent>) {}
async fn on_room_name(&self, _: SyncRoom, _: &SyncStateEvent<NameEventContent>) {}
async fn on_room_canonical_alias(
&self,
_: SyncRoom,
_: &SyncStateEvent<CanonicalAliasEventContent>,
) {
}
async fn on_room_aliases(&self, _: SyncRoom, _: &SyncStateEvent<AliasesEventContent>) {}
async fn on_room_avatar(&self, _: SyncRoom, _: &SyncStateEvent<AvatarEventContent>) {}
async fn on_room_message(&self, _: SyncRoom, _: &SyncMessageEvent<MsgEventContent>) {}
async fn on_room_message_feedback(
&self,
_: SyncRoom,
_: &SyncMessageEvent<FeedbackEventContent>,
) {
}
async fn on_room_call_invite(&self, _: SyncRoom, _: &SyncMessageEvent<InviteEventContent>) {}
async fn on_room_call_answer(&self, _: SyncRoom, _: &SyncMessageEvent<AnswerEventContent>) {}
async fn on_room_call_candidates(
&self,
_: SyncRoom,
_: &SyncMessageEvent<CandidatesEventContent>,
) {
}
async fn on_room_call_hangup(&self, _: SyncRoom, _: &SyncMessageEvent<HangupEventContent>) {}
async fn on_room_redaction(&self, _: SyncRoom, _: &SyncRedactionEvent) {}
async fn on_room_power_levels(&self, _: SyncRoom, _: &SyncStateEvent<PowerLevelsEventContent>) {
}
async fn on_room_join_rules(&self, _: SyncRoom, _: &SyncStateEvent<JoinRulesEventContent>) {}
async fn on_room_tombstone(&self, _: SyncRoom, _: &SyncStateEvent<TombstoneEventContent>) {}
async fn on_state_member(&self, _: SyncRoom, _: &SyncStateEvent<MemberEventContent>) {}
async fn on_state_name(&self, _: SyncRoom, _: &SyncStateEvent<NameEventContent>) {}
async fn on_state_canonical_alias(
&self,
_: SyncRoom,
_: &SyncStateEvent<CanonicalAliasEventContent>,
) {
}
async fn on_state_aliases(&self, _: SyncRoom, _: &SyncStateEvent<AliasesEventContent>) {}
async fn on_state_avatar(&self, _: SyncRoom, _: &SyncStateEvent<AvatarEventContent>) {}
async fn on_state_power_levels(
&self,
_: SyncRoom,
_: &SyncStateEvent<PowerLevelsEventContent>,
) {
}
async fn on_state_join_rules(&self, _: SyncRoom, _: &SyncStateEvent<JoinRulesEventContent>) {}
async fn on_stripped_state_member(
&self,
_: SyncRoom,
_: &StrippedStateEvent<MemberEventContent>,
_: Option<MemberEventContent>,
) {
}
async fn on_stripped_state_name(&self, _: SyncRoom, _: &StrippedStateEvent<NameEventContent>) {}
async fn on_stripped_state_canonical_alias(
&self,
_: SyncRoom,
_: &StrippedStateEvent<CanonicalAliasEventContent>,
) {
}
async fn on_stripped_state_aliases(
&self,
_: SyncRoom,
_: &StrippedStateEvent<AliasesEventContent>,
) {
}
async fn on_stripped_state_avatar(
&self,
_: SyncRoom,
_: &StrippedStateEvent<AvatarEventContent>,
) {
}
async fn on_stripped_state_power_levels(
&self,
_: SyncRoom,
_: &StrippedStateEvent<PowerLevelsEventContent>,
) {
}
async fn on_stripped_state_join_rules(
&self,
_: SyncRoom,
_: &StrippedStateEvent<JoinRulesEventContent>,
) {
}
async fn on_non_room_presence(&self, _: SyncRoom, _: &PresenceEvent) {}
async fn on_non_room_ignored_users(
&self,
_: SyncRoom,
_: &BasicEvent<IgnoredUserListEventContent>,
) {
}
async fn on_non_room_push_rules(&self, _: SyncRoom, _: &BasicEvent<PushRulesEventContent>) {}
async fn on_non_room_fully_read(
&self,
_: SyncRoom,
_: &SyncEphemeralRoomEvent<FullyReadEventContent>,
) {
}
async fn on_non_room_typing(
&self,
_: SyncRoom,
_: &SyncEphemeralRoomEvent<TypingEventContent>,
) {
}
async fn on_non_room_receipt(
&self,
_: SyncRoom,
_: &SyncEphemeralRoomEvent<ReceiptEventContent>,
) {
}
async fn on_presence_event(&self, _: SyncRoom, _: &PresenceEvent) {}
async fn on_unrecognized_event(&self, _: SyncRoom, _: &RawJsonValue) {}
async fn on_custom_event(&self, _: SyncRoom, _: &CustomEvent<'_>) {}
}
#[cfg(test)]
mod test {
use super::*;
use matrix_sdk_common::{async_trait, locks::Mutex};
use matrix_sdk_test::{async_test, sync_response, SyncResponseFile};
use std::sync::Arc;
#[cfg(target_arch = "wasm32")]
pub use wasm_bindgen_test::*;
#[derive(Clone)]
pub struct EvEmitterTest(Arc<Mutex<Vec<String>>>);
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl EventEmitter for EvEmitterTest {
async fn on_room_member(&self, _: SyncRoom, _: &SyncStateEvent<MemberEventContent>) {
self.0.lock().await.push("member".to_string())
}
async fn on_room_name(&self, _: SyncRoom, _: &SyncStateEvent<NameEventContent>) {
self.0.lock().await.push("name".to_string())
}
async fn on_room_canonical_alias(
&self,
_: SyncRoom,
_: &SyncStateEvent<CanonicalAliasEventContent>,
) {
self.0.lock().await.push("canonical".to_string())
}
async fn on_room_aliases(&self, _: SyncRoom, _: &SyncStateEvent<AliasesEventContent>) {
self.0.lock().await.push("aliases".to_string())
}
async fn on_room_avatar(&self, _: SyncRoom, _: &SyncStateEvent<AvatarEventContent>) {
self.0.lock().await.push("avatar".to_string())
}
async fn on_room_message(&self, _: SyncRoom, _: &SyncMessageEvent<MsgEventContent>) {
self.0.lock().await.push("message".to_string())
}
async fn on_room_message_feedback(
&self,
_: SyncRoom,
_: &SyncMessageEvent<FeedbackEventContent>,
) {
self.0.lock().await.push("feedback".to_string())
}
async fn on_room_call_invite(&self, _: SyncRoom, _: &SyncMessageEvent<InviteEventContent>) {
self.0.lock().await.push("call invite".to_string())
}
async fn on_room_call_answer(&self, _: SyncRoom, _: &SyncMessageEvent<AnswerEventContent>) {
self.0.lock().await.push("call answer".to_string())
}
async fn on_room_call_candidates(
&self,
_: SyncRoom,
_: &SyncMessageEvent<CandidatesEventContent>,
) {
self.0.lock().await.push("call candidates".to_string())
}
async fn on_room_call_hangup(&self, _: SyncRoom, _: &SyncMessageEvent<HangupEventContent>) {
self.0.lock().await.push("call hangup".to_string())
}
async fn on_room_redaction(&self, _: SyncRoom, _: &SyncRedactionEvent) {
self.0.lock().await.push("redaction".to_string())
}
async fn on_room_power_levels(
&self,
_: SyncRoom,
_: &SyncStateEvent<PowerLevelsEventContent>,
) {
self.0.lock().await.push("power".to_string())
}
async fn on_room_tombstone(&self, _: SyncRoom, _: &SyncStateEvent<TombstoneEventContent>) {
self.0.lock().await.push("tombstone".to_string())
}
async fn on_state_member(&self, _: SyncRoom, _: &SyncStateEvent<MemberEventContent>) {
self.0.lock().await.push("state member".to_string())
}
async fn on_state_name(&self, _: SyncRoom, _: &SyncStateEvent<NameEventContent>) {
self.0.lock().await.push("state name".to_string())
}
async fn on_state_canonical_alias(
&self,
_: SyncRoom,
_: &SyncStateEvent<CanonicalAliasEventContent>,
) {
self.0.lock().await.push("state canonical".to_string())
}
async fn on_state_aliases(&self, _: SyncRoom, _: &SyncStateEvent<AliasesEventContent>) {
self.0.lock().await.push("state aliases".to_string())
}
async fn on_state_avatar(&self, _: SyncRoom, _: &SyncStateEvent<AvatarEventContent>) {
self.0.lock().await.push("state avatar".to_string())
}
async fn on_state_power_levels(
&self,
_: SyncRoom,
_: &SyncStateEvent<PowerLevelsEventContent>,
) {
self.0.lock().await.push("state power".to_string())
}
async fn on_state_join_rules(
&self,
_: SyncRoom,
_: &SyncStateEvent<JoinRulesEventContent>,
) {
self.0.lock().await.push("state rules".to_string())
}
async fn on_stripped_state_member(
&self,
_: SyncRoom,
_: &StrippedStateEvent<MemberEventContent>,
_: Option<MemberEventContent>,
) {
self.0
.lock()
.await
.push("stripped state member".to_string())
}
async fn on_stripped_state_name(
&self,
_: SyncRoom,
_: &StrippedStateEvent<NameEventContent>,
) {
self.0.lock().await.push("stripped state name".to_string())
}
async fn on_stripped_state_canonical_alias(
&self,
_: SyncRoom,
_: &StrippedStateEvent<CanonicalAliasEventContent>,
) {
self.0
.lock()
.await
.push("stripped state canonical".to_string())
}
async fn on_stripped_state_aliases(
&self,
_: SyncRoom,
_: &StrippedStateEvent<AliasesEventContent>,
) {
self.0
.lock()
.await
.push("stripped state aliases".to_string())
}
async fn on_stripped_state_avatar(
&self,
_: SyncRoom,
_: &StrippedStateEvent<AvatarEventContent>,
) {
self.0
.lock()
.await
.push("stripped state avatar".to_string())
}
async fn on_stripped_state_power_levels(
&self,
_: SyncRoom,
_: &StrippedStateEvent<PowerLevelsEventContent>,
) {
self.0.lock().await.push("stripped state power".to_string())
}
async fn on_stripped_state_join_rules(
&self,
_: SyncRoom,
_: &StrippedStateEvent<JoinRulesEventContent>,
) {
self.0.lock().await.push("stripped state rules".to_string())
}
async fn on_non_room_presence(&self, _: SyncRoom, _: &PresenceEvent) {
self.0.lock().await.push("presence".to_string())
}
async fn on_non_room_ignored_users(
&self,
_: SyncRoom,
_: &BasicEvent<IgnoredUserListEventContent>,
) {
self.0.lock().await.push("account ignore".to_string())
}
async fn on_non_room_push_rules(&self, _: SyncRoom, _: &BasicEvent<PushRulesEventContent>) {
self.0.lock().await.push("account push rules".to_string())
}
async fn on_non_room_fully_read(
&self,
_: SyncRoom,
_: &SyncEphemeralRoomEvent<FullyReadEventContent>,
) {
self.0.lock().await.push("account read".to_string())
}
async fn on_non_room_typing(
&self,
_: SyncRoom,
_: &SyncEphemeralRoomEvent<TypingEventContent>,
) {
self.0.lock().await.push("typing event".to_string())
}
async fn on_non_room_receipt(
&self,
_: SyncRoom,
_: &SyncEphemeralRoomEvent<ReceiptEventContent>,
) {
self.0.lock().await.push("receipt event".to_string())
}
async fn on_presence_event(&self, _: SyncRoom, _: &PresenceEvent) {
self.0.lock().await.push("presence event".to_string())
}
async fn on_unrecognized_event(&self, _: SyncRoom, _: &RawJsonValue) {
self.0.lock().await.push("unrecognized event".to_string())
}
async fn on_custom_event(&self, _: SyncRoom, _: &CustomEvent<'_>) {
self.0.lock().await.push("custom event".to_string())
}
}
use crate::{identifiers::user_id, BaseClient, Session};
async fn get_client() -> BaseClient {
let session = Session {
access_token: "1234".to_owned(),
user_id: user_id!("@example:example.com"),
device_id: "DEVICEID".into(),
};
let client = BaseClient::new().unwrap();
client.restore_login(session).await.unwrap();
client
}
#[async_test]
async fn event_emitter_joined() {
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::Default);
client.receive_sync_response(&mut response).await.unwrap();
let v = test_vec.lock().await;
assert_eq!(
v.as_slice(),
[
"state rules",
"state member",
"state aliases",
"state power",
"state canonical",
"state member",
"state member",
"message",
"account ignore",
"presence event",
"receipt event",
"account read",
],
)
}
#[async_test]
async fn event_emitter_invite() {
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::Invite);
client.receive_sync_response(&mut response).await.unwrap();
let v = test_vec.lock().await;
assert_eq!(
v.as_slice(),
["stripped state name", "stripped state member"],
)
}
#[async_test]
async fn event_emitter_leave() {
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::Leave);
client.receive_sync_response(&mut response).await.unwrap();
let v = test_vec.lock().await;
assert_eq!(
v.as_slice(),
[
"state rules",
"state member",
"state aliases",
"state power",
"state canonical",
"state member",
"state member",
"message"
],
)
}
#[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",
"message", "redaction",
"unrecognized event",
"receipt event",
"typing event"
],
)
}
#[async_test]
async fn event_emitter_voip() {
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::Voip);
client.receive_sync_response(&mut response).await.unwrap();
let v = test_vec.lock().await;
assert_eq!(
v.as_slice(),
[
"call invite",
"call answer",
"call candidates",
"call hangup",
],
)
}
}