use std::fmt;
use ruma::{
OwnedEventId,
events::{
AnySyncMessageLikeEvent, AnySyncTimelineEvent, BundledMessageLikeRelations, Mentions,
poll::unstable_start::{
NewUnstablePollStartEventContentWithoutRelation, SyncUnstablePollStartEvent,
UnstablePollStartEventContent,
},
room::message::{
MessageType, Relation, RoomMessageEventContentWithoutRelation, SyncRoomMessageEvent,
},
},
html::RemoveReplyFallback,
serde::Raw,
};
use tracing::{error, trace};
use crate::DEFAULT_SANITIZER_MODE;
#[derive(Clone)]
pub struct Message {
pub(in crate::timeline) msgtype: MessageType,
pub(in crate::timeline) edited: bool,
pub(in crate::timeline) mentions: Option<Mentions>,
}
impl Message {
pub(in crate::timeline) fn from_event(
mut msgtype: MessageType,
mentions: Option<Mentions>,
edit: Option<RoomMessageEventContentWithoutRelation>,
remove_reply_fallback: RemoveReplyFallback,
) -> Self {
msgtype.sanitize(DEFAULT_SANITIZER_MODE, remove_reply_fallback);
let mut ret = Self { msgtype, edited: false, mentions };
if let Some(edit) = edit {
ret.apply_edit(edit);
}
ret
}
pub(crate) fn apply_edit(&mut self, mut new_content: RoomMessageEventContentWithoutRelation) {
trace!("applying edit to a Message");
new_content.msgtype.sanitize(DEFAULT_SANITIZER_MODE, RemoveReplyFallback::No);
self.msgtype = new_content.msgtype;
self.mentions = new_content.mentions;
self.edited = true;
}
pub fn msgtype(&self) -> &MessageType {
&self.msgtype
}
pub fn body(&self) -> &str {
self.msgtype.body()
}
pub fn is_edited(&self) -> bool {
self.edited
}
pub fn mentions(&self) -> Option<&Mentions> {
self.mentions.as_ref()
}
}
pub(crate) fn extract_bundled_edit_event_json(
raw: &Raw<AnySyncTimelineEvent>,
) -> Option<Raw<AnySyncTimelineEvent>> {
let raw_unsigned: Raw<serde_json::Value> = raw.get_field("unsigned").ok()??;
let raw_relations: Raw<serde_json::Value> = raw_unsigned.get_field("m.relations").ok()??;
raw_relations.get_field::<Raw<AnySyncTimelineEvent>>("m.replace").ok()?
}
pub(crate) fn extract_room_msg_edit_content(
relations: BundledMessageLikeRelations<AnySyncMessageLikeEvent>,
) -> Option<(OwnedEventId, RoomMessageEventContentWithoutRelation)> {
match *relations.replace? {
AnySyncMessageLikeEvent::RoomMessage(SyncRoomMessageEvent::Original(ev)) => match ev
.content
.relates_to
{
Some(Relation::Replacement(re)) => {
trace!("found a bundled edit event in a room message");
Some((ev.event_id, re.new_content))
}
_ => {
error!("got m.room.message event with an edit without a valid m.replace relation");
None
}
},
AnySyncMessageLikeEvent::RoomMessage(SyncRoomMessageEvent::Redacted(_)) => None,
_ => {
error!("got m.room.message event with an edit of a different event type");
None
}
}
}
pub(crate) fn extract_poll_edit_content(
relations: BundledMessageLikeRelations<AnySyncMessageLikeEvent>,
) -> Option<(OwnedEventId, NewUnstablePollStartEventContentWithoutRelation)> {
match *relations.replace? {
AnySyncMessageLikeEvent::UnstablePollStart(SyncUnstablePollStartEvent::Original(ev)) => {
match ev.content {
UnstablePollStartEventContent::Replacement(re) => {
trace!("found a bundled edit event in a poll");
Some((ev.event_id, re.relates_to.new_content))
}
_ => {
error!("got new poll start event in a bundled edit");
None
}
}
}
AnySyncMessageLikeEvent::UnstablePollStart(SyncUnstablePollStartEvent::Redacted(_)) => None,
_ => {
error!("got poll edit event with an edit of a different event type");
None
}
}
}
#[cfg(not(tarpaulin_include))]
impl fmt::Debug for Message {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { msgtype: _, edited, mentions: _ } = self;
f.debug_struct("Message").field("edited", edited).finish_non_exhaustive()
}
}