use matrix_sdk::{
Room,
ruma::{
OwnedEventId,
events::{AnySyncMessageLikeEvent, AnySyncTimelineEvent, reaction::ReactionEventContent},
},
};
use tracing::Instrument;
use crate::CallbackError;
#[derive(Clone)]
pub struct Reacting {
matrix_link: super::MatrixLink,
}
impl Reacting {
pub(super) fn new(matrix_link: super::MatrixLink) -> Self {
Self { matrix_link }
}
pub async fn react(
&self,
room: &Room,
target_event_id: OwnedEventId,
reaction_key: String,
) -> Result<
matrix_sdk::ruma::api::client::message::send_message_event::v3::Response,
matrix_sdk::Error,
> {
let content =
ReactionEventContent::new(matrix_sdk::ruma::events::relation::Annotation::new(
target_event_id,
reaction_key.to_owned(),
));
room.send(content.clone()).await.map(|r| r.response)
}
pub fn on_actionable_reaction<F, Fut>(&self, callback: F)
where
F: FnOnce(AnySyncTimelineEvent, Room, ReactionEventContent) -> Fut
+ Send
+ 'static
+ Clone
+ Sync,
Fut: std::future::Future<Output = Result<(), CallbackError>> + Send + 'static,
{
let own_user_id = self.matrix_link.user_id().to_owned();
self.matrix_link.client().add_event_handler(
move |ev: AnySyncTimelineEvent, room: Room| async move {
let event_span = tracing::error_span!(
"on_actionable_reaction",
event_id = ev.event_id().as_str(),
room_id = room.room_id().as_str(),
sender_id = ev.sender().as_str(),
reaction = tracing::field::Empty,
reacted_to_event_id = tracing::field::Empty,
);
let reaction_content;
{
let _enter = event_span.enter();
tracing::trace!(
"Sync room message event handler (on_actionable_reaction) for event: {:?}",
ev
);
let AnySyncTimelineEvent::MessageLike(AnySyncMessageLikeEvent::Reaction(
reaction,
)) = ev.clone()
else {
tracing::trace!("Ignoring non-reaction event");
return;
};
if ev.sender() == own_user_id {
tracing::debug!("Ignoring own reaction");
return;
}
let Some(original) = reaction.as_original() else {
tracing::debug!("Ignoring redacted reaction");
return;
};
reaction_content = original.content.clone();
event_span.record("reaction", reaction_content.relates_to.key.clone());
event_span.record(
"reacted_to_event_id",
reaction_content.relates_to.event_id.as_str(),
);
}
tokio::spawn(
async move {
if let Err(err) = callback(ev, room, reaction_content).await {
tracing::error!(?err, "Error in callback");
}
}
.instrument(event_span),
);
},
);
}
}