events: Allow to build reply to raw events
This commit is contained in:
parent
bc48eb2162
commit
5040aa2a93
@ -63,6 +63,7 @@ Improvements:
|
||||
- `RedactedRoomRedactionEventContent`,
|
||||
- `RedactedRoomPowerLevelsEventContent`,
|
||||
- `RedactedRoomMemberEventContent`
|
||||
- Add `RoomMessageEventContent::make_reply_to_raw` to build replies to any event
|
||||
|
||||
# 0.26.1
|
||||
|
||||
|
@ -2,11 +2,12 @@
|
||||
//!
|
||||
//! [`m.room.message`]: https://spec.matrix.org/latest/client-server-api/#mroommessage
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::{borrow::Cow, collections::BTreeSet};
|
||||
|
||||
use as_variant::as_variant;
|
||||
use ruma_common::{
|
||||
serde::{JsonObject, StringEnum},
|
||||
EventId, OwnedEventId,
|
||||
serde::{JsonObject, Raw, StringEnum},
|
||||
EventId, OwnedEventId, OwnedUserId, RoomId, UserId,
|
||||
};
|
||||
#[cfg(feature = "html")]
|
||||
use ruma_html::{sanitize_html, HtmlSanitizerMode, RemoveReplyFallback};
|
||||
@ -16,7 +17,7 @@ use serde_json::Value as JsonValue;
|
||||
|
||||
use crate::{
|
||||
relation::{CustomRelation, InReplyTo, RelationType, Replacement, Thread},
|
||||
Mentions, PrivOwnedStr,
|
||||
AnySyncTimelineEvent, Mentions, PrivOwnedStr,
|
||||
};
|
||||
|
||||
mod audio;
|
||||
@ -50,6 +51,8 @@ pub use server_notice::{LimitType, ServerNoticeMessageEventContent, ServerNotice
|
||||
pub use text::TextMessageEventContent;
|
||||
pub use video::{VideoInfo, VideoMessageEventContent};
|
||||
|
||||
use self::reply::OriginalEventData;
|
||||
|
||||
/// The content of an `m.room.message` event.
|
||||
///
|
||||
/// This event is used when sending messages in a room.
|
||||
@ -149,11 +152,132 @@ impl RoomMessageEventContent {
|
||||
/// Panics if `self` has a `formatted_body` with a format other than HTML.
|
||||
#[track_caller]
|
||||
pub fn make_reply_to(
|
||||
mut self,
|
||||
self,
|
||||
original_message: &OriginalRoomMessageEvent,
|
||||
forward_thread: ForwardThread,
|
||||
add_mentions: AddMentions,
|
||||
) -> Self {
|
||||
let reply = self.make_reply_fallback(original_message.into());
|
||||
|
||||
let original_thread_id = if forward_thread == ForwardThread::Yes {
|
||||
original_message
|
||||
.content
|
||||
.relates_to
|
||||
.as_ref()
|
||||
.and_then(as_variant!(Relation::Thread))
|
||||
.map(|thread| thread.event_id.clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let original_user_mentions = (add_mentions == AddMentions::Yes).then(|| {
|
||||
original_message
|
||||
.content
|
||||
.mentions
|
||||
.as_ref()
|
||||
.map(|m| m.user_ids.clone())
|
||||
.unwrap_or_default()
|
||||
});
|
||||
|
||||
reply.make_reply_tweaks(
|
||||
original_message.event_id.clone(),
|
||||
original_thread_id,
|
||||
original_user_mentions,
|
||||
Some(&original_message.sender),
|
||||
)
|
||||
}
|
||||
|
||||
/// Turns `self` into a reply to the given raw event.
|
||||
///
|
||||
/// Takes the `body` / `formatted_body` (if any) in `self` for the main text and prepends a
|
||||
/// quoted version of the `body` of `original_event` (if any). Also sets the `in_reply_to` field
|
||||
/// inside `relates_to`, and optionally the `rel_type` to `m.thread` if the
|
||||
/// `original_message is in a thread and thread forwarding is enabled.
|
||||
///
|
||||
/// It is recommended to use [`Self::make_reply_to()`] for replies to `m.room.message` events,
|
||||
/// as the generated fallback is better for some `msgtype`s.
|
||||
///
|
||||
/// Note that except for the panic below, this is infallible. Which means that if a field is
|
||||
/// missing when deserializing the data, the changes that require it will not be applied. It
|
||||
/// will still at least apply the `m.in_reply_to` relation to this content.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `self` has a `formatted_body` with a format other than HTML.
|
||||
#[track_caller]
|
||||
pub fn make_reply_to_raw(
|
||||
self,
|
||||
original_event: &Raw<AnySyncTimelineEvent>,
|
||||
original_event_id: OwnedEventId,
|
||||
room_id: &RoomId,
|
||||
forward_thread: ForwardThread,
|
||||
add_mentions: AddMentions,
|
||||
) -> Self {
|
||||
#[derive(Deserialize)]
|
||||
struct ContentDeHelper {
|
||||
body: Option<String>,
|
||||
#[serde(flatten)]
|
||||
formatted: Option<FormattedBody>,
|
||||
#[cfg(feature = "unstable-msc1767")]
|
||||
#[serde(rename = "org.matrix.msc1767")]
|
||||
text: Option<String>,
|
||||
#[serde(rename = "m.relates_to")]
|
||||
relates_to: Option<crate::room::encrypted::Relation>,
|
||||
#[serde(rename = "m.mentions")]
|
||||
mentions: Option<Mentions>,
|
||||
}
|
||||
|
||||
let sender = original_event.get_field::<OwnedUserId>("sender").ok().flatten();
|
||||
let content = original_event.get_field::<ContentDeHelper>("content").ok().flatten();
|
||||
let relates_to = content.as_ref().and_then(|c| c.relates_to.as_ref());
|
||||
|
||||
let content_body = content.as_ref().and_then(|c| {
|
||||
let body = c.body.as_deref();
|
||||
#[cfg(feature = "unstable-msc1767")]
|
||||
let body = body.or(c.text.as_deref());
|
||||
|
||||
Some((c, body?))
|
||||
});
|
||||
|
||||
// Only apply fallback if we managed to deserialize raw event.
|
||||
let reply = if let (Some(sender), Some((content, body))) = (&sender, content_body) {
|
||||
let is_reply =
|
||||
matches!(content.relates_to, Some(crate::room::encrypted::Relation::Reply { .. }));
|
||||
let data = OriginalEventData {
|
||||
body,
|
||||
formatted: content.formatted.as_ref(),
|
||||
is_emote: false,
|
||||
is_reply,
|
||||
room_id,
|
||||
event_id: &original_event_id,
|
||||
sender,
|
||||
};
|
||||
|
||||
self.make_reply_fallback(data)
|
||||
} else {
|
||||
self
|
||||
};
|
||||
|
||||
let original_thread_id = if forward_thread == ForwardThread::Yes {
|
||||
relates_to
|
||||
.and_then(as_variant!(crate::room::encrypted::Relation::Thread))
|
||||
.map(|thread| thread.event_id.clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let original_user_mentions = (add_mentions == AddMentions::Yes)
|
||||
.then(|| content.and_then(|c| c.mentions).map(|m| m.user_ids).unwrap_or_default());
|
||||
|
||||
reply.make_reply_tweaks(
|
||||
original_event_id,
|
||||
original_thread_id,
|
||||
original_user_mentions,
|
||||
sender.as_deref(),
|
||||
)
|
||||
}
|
||||
|
||||
fn make_reply_fallback(mut self, original_event: OriginalEventData<'_>) -> Self {
|
||||
let empty_formatted_body = || FormattedBody::html(String::new());
|
||||
|
||||
let (body, formatted) = {
|
||||
@ -190,32 +314,30 @@ impl RoomMessageEventContent {
|
||||
(*body, *formatted_body) = reply::plain_and_formatted_reply_body(
|
||||
body.as_str(),
|
||||
(!formatted_body.is_empty()).then_some(formatted_body.as_str()),
|
||||
original_message,
|
||||
original_event,
|
||||
);
|
||||
}
|
||||
|
||||
let relates_to = if let Some(Relation::Thread(Thread { event_id, .. })) = original_message
|
||||
.content
|
||||
.relates_to
|
||||
.as_ref()
|
||||
.filter(|_| forward_thread == ForwardThread::Yes)
|
||||
{
|
||||
Relation::Thread(Thread::plain(event_id.clone(), original_message.event_id.clone()))
|
||||
} else {
|
||||
Relation::Reply {
|
||||
in_reply_to: InReplyTo { event_id: original_message.event_id.clone() },
|
||||
self
|
||||
}
|
||||
|
||||
fn make_reply_tweaks(
|
||||
mut self,
|
||||
original_event_id: OwnedEventId,
|
||||
original_thread_id: Option<OwnedEventId>,
|
||||
original_user_mentions: Option<BTreeSet<OwnedUserId>>,
|
||||
original_sender: Option<&UserId>,
|
||||
) -> Self {
|
||||
let relates_to = if let Some(event_id) = original_thread_id {
|
||||
Relation::Thread(Thread::plain(event_id.to_owned(), original_event_id.to_owned()))
|
||||
} else {
|
||||
Relation::Reply { in_reply_to: InReplyTo { event_id: original_event_id.to_owned() } }
|
||||
};
|
||||
self.relates_to = Some(relates_to);
|
||||
|
||||
if add_mentions == AddMentions::Yes {
|
||||
// Copy the mentioned users.
|
||||
let mut user_ids = match &original_message.content.mentions {
|
||||
Some(m) => m.user_ids.clone(),
|
||||
None => Default::default(),
|
||||
};
|
||||
if let (Some(sender), Some(mut user_ids)) = (original_sender, original_user_mentions) {
|
||||
// Add the sender.
|
||||
user_ids.insert(original_message.sender.clone());
|
||||
user_ids.insert(sender.to_owned());
|
||||
self.mentions = Some(Mentions { user_ids, ..Default::default() });
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
use std::fmt::{self, Write};
|
||||
|
||||
use ruma_common::{EventId, RoomId, UserId};
|
||||
#[cfg(feature = "html")]
|
||||
use ruma_html::Html;
|
||||
|
||||
@ -8,10 +9,42 @@ use super::{
|
||||
Relation,
|
||||
};
|
||||
|
||||
fn get_message_quote_fallbacks(original_message: &OriginalRoomMessageEvent) -> (String, String) {
|
||||
let get_quotes = |body: &str, formatted: Option<&FormattedBody>, is_emote: bool| {
|
||||
let OriginalRoomMessageEvent { room_id, event_id, sender, content, .. } = original_message;
|
||||
pub(super) struct OriginalEventData<'a> {
|
||||
pub(super) body: &'a str,
|
||||
pub(super) formatted: Option<&'a FormattedBody>,
|
||||
pub(super) is_emote: bool,
|
||||
pub(super) is_reply: bool,
|
||||
pub(super) room_id: &'a RoomId,
|
||||
pub(super) event_id: &'a EventId,
|
||||
pub(super) sender: &'a UserId,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a OriginalRoomMessageEvent> for OriginalEventData<'a> {
|
||||
fn from(message: &'a OriginalRoomMessageEvent) -> Self {
|
||||
let OriginalRoomMessageEvent { room_id, event_id, sender, content, .. } = message;
|
||||
let is_reply = matches!(content.relates_to, Some(Relation::Reply { .. }));
|
||||
|
||||
let (body, formatted, is_emote) = match &content.msgtype {
|
||||
MessageType::Audio(_) => ("sent an audio file.", None, false),
|
||||
MessageType::Emote(c) => (&*c.body, c.formatted.as_ref(), true),
|
||||
MessageType::File(_) => ("sent a file.", None, false),
|
||||
MessageType::Image(_) => ("sent an image.", None, false),
|
||||
MessageType::Location(_) => ("sent a location.", None, false),
|
||||
MessageType::Notice(c) => (&*c.body, c.formatted.as_ref(), false),
|
||||
MessageType::ServerNotice(c) => (&*c.body, None, false),
|
||||
MessageType::Text(c) => (&*c.body, c.formatted.as_ref(), false),
|
||||
MessageType::Video(_) => ("sent a video.", None, false),
|
||||
MessageType::VerificationRequest(c) => (&*c.body, None, false),
|
||||
MessageType::_Custom(c) => (&*c.body, None, false),
|
||||
};
|
||||
|
||||
Self { body, formatted, is_emote, is_reply, room_id, event_id, sender }
|
||||
}
|
||||
}
|
||||
|
||||
fn get_message_quote_fallbacks(original_event: OriginalEventData<'_>) -> (String, String) {
|
||||
let OriginalEventData { body, formatted, is_emote, is_reply, room_id, event_id, sender } =
|
||||
original_event;
|
||||
let emote_sign = is_emote.then_some("* ").unwrap_or_default();
|
||||
let body = is_reply.then(|| remove_plain_reply_fallback(body)).unwrap_or(body);
|
||||
#[cfg(feature = "html")]
|
||||
@ -32,21 +65,6 @@ fn get_message_quote_fallbacks(original_message: &OriginalRoomMessageEvent) -> (
|
||||
</mx-reply>"
|
||||
),
|
||||
)
|
||||
};
|
||||
|
||||
match &original_message.content.msgtype {
|
||||
MessageType::Audio(_) => get_quotes("sent an audio file.", None, false),
|
||||
MessageType::Emote(c) => get_quotes(&c.body, c.formatted.as_ref(), true),
|
||||
MessageType::File(_) => get_quotes("sent a file.", None, false),
|
||||
MessageType::Image(_) => get_quotes("sent an image.", None, false),
|
||||
MessageType::Location(_) => get_quotes("sent a location.", None, false),
|
||||
MessageType::Notice(c) => get_quotes(&c.body, c.formatted.as_ref(), false),
|
||||
MessageType::ServerNotice(c) => get_quotes(&c.body, None, false),
|
||||
MessageType::Text(c) => get_quotes(&c.body, c.formatted.as_ref(), false),
|
||||
MessageType::Video(_) => get_quotes("sent a video.", None, false),
|
||||
MessageType::VerificationRequest(content) => get_quotes(&content.body, None, false),
|
||||
MessageType::_Custom(content) => get_quotes(&content.body, None, false),
|
||||
}
|
||||
}
|
||||
|
||||
struct EscapeHtmlEntities<'a>(&'a str);
|
||||
@ -108,12 +126,12 @@ impl fmt::Display for FormattedOrPlainBody<'_> {
|
||||
///
|
||||
/// [HTML tags and attributes]: https://spec.matrix.org/latest/client-server-api/#mroommessage-msgtypes
|
||||
/// [rich reply fallbacks]: https://spec.matrix.org/latest/client-server-api/#fallbacks-for-rich-replies
|
||||
pub(crate) fn plain_and_formatted_reply_body(
|
||||
pub(super) fn plain_and_formatted_reply_body(
|
||||
body: &str,
|
||||
formatted: Option<impl fmt::Display>,
|
||||
original_message: &OriginalRoomMessageEvent,
|
||||
original_event: OriginalEventData<'_>,
|
||||
) -> (String, String) {
|
||||
let (quoted, quoted_html) = get_message_quote_fallbacks(original_message);
|
||||
let (quoted, quoted_html) = get_message_quote_fallbacks(original_event);
|
||||
|
||||
let plain = format!("{quoted}\n\n{body}");
|
||||
let html = match formatted {
|
||||
@ -133,15 +151,17 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn fallback_multiline() {
|
||||
let (plain_quote, html_quote) =
|
||||
super::get_message_quote_fallbacks(&OriginalRoomMessageEvent {
|
||||
let (plain_quote, html_quote) = super::get_message_quote_fallbacks(
|
||||
(&OriginalRoomMessageEvent {
|
||||
content: RoomMessageEventContent::text_plain("multi\nline"),
|
||||
event_id: owned_event_id!("$1598361704261elfgc:localhost"),
|
||||
sender: owned_user_id!("@alice:example.com"),
|
||||
origin_server_ts: MilliSecondsSinceUnixEpoch::now(),
|
||||
room_id: owned_room_id!("!n8f893n9:example.com"),
|
||||
unsigned: MessageLikeUnsigned::new(),
|
||||
});
|
||||
})
|
||||
.into(),
|
||||
);
|
||||
|
||||
assert_eq!(plain_quote, "> <@alice:example.com> multi\n> line");
|
||||
assert_eq!(
|
||||
|
@ -1,10 +1,11 @@
|
||||
use std::borrow::Cow;
|
||||
use std::{borrow::Cow, collections::BTreeSet};
|
||||
|
||||
use assert_matches2::assert_matches;
|
||||
use js_int::uint;
|
||||
use ruma_common::{
|
||||
mxc_uri, owned_event_id, owned_room_id, owned_user_id, serde::Base64, user_id,
|
||||
MilliSecondsSinceUnixEpoch, OwnedDeviceId,
|
||||
mxc_uri, owned_event_id, owned_room_id, owned_user_id, room_id,
|
||||
serde::{Base64, Raw},
|
||||
user_id, MilliSecondsSinceUnixEpoch, OwnedDeviceId,
|
||||
};
|
||||
use ruma_events::{
|
||||
key::verification::VerificationMethod,
|
||||
@ -18,7 +19,7 @@ use ruma_events::{
|
||||
},
|
||||
EncryptedFileInit, JsonWebKeyInit, MediaSource,
|
||||
},
|
||||
Mentions, MessageLikeUnsigned,
|
||||
AnySyncTimelineEvent, Mentions, MessageLikeUnsigned,
|
||||
};
|
||||
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
|
||||
|
||||
@ -483,6 +484,215 @@ fn reply_add_mentions() {
|
||||
assert!(mentions.room);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reply_to_raw() {
|
||||
let room_id = room_id!("!roomid:notareal.hs");
|
||||
let event_id = owned_event_id!("$143273582443PhrSn");
|
||||
|
||||
let original_message: Raw<AnySyncTimelineEvent> = from_json_value(json!({
|
||||
"content": {
|
||||
"body": "Hello, World!",
|
||||
"msgtype": "m.text",
|
||||
},
|
||||
"event_id": event_id,
|
||||
"origin_server_ts": 134_829_848,
|
||||
"sender": "@user:notareal.hs",
|
||||
"type": "m.room.message",
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
let reply = RoomMessageEventContent::text_html(
|
||||
"This is **my** reply",
|
||||
"This is <strong>my</strong> reply",
|
||||
)
|
||||
.make_reply_to_raw(
|
||||
&original_message,
|
||||
event_id.clone(),
|
||||
room_id,
|
||||
ForwardThread::Yes,
|
||||
AddMentions::No,
|
||||
);
|
||||
|
||||
assert_matches!(reply.relates_to, Some(Relation::Reply { in_reply_to }));
|
||||
assert_eq!(in_reply_to.event_id, event_id);
|
||||
|
||||
assert_matches!(reply.msgtype, MessageType::Text(text_msg));
|
||||
assert_eq!(
|
||||
text_msg.body,
|
||||
"> <@user:notareal.hs> Hello, World!\n\
|
||||
\n\
|
||||
This is **my** reply"
|
||||
);
|
||||
assert_eq!(
|
||||
text_msg.formatted.unwrap().body,
|
||||
"<mx-reply>\
|
||||
<blockquote>\
|
||||
<a href=\"https://matrix.to/#/!roomid:notareal.hs/$143273582443PhrSn\">In reply to</a> \
|
||||
<a href=\"https://matrix.to/#/@user:notareal.hs\">@user:notareal.hs</a>\
|
||||
<br>\
|
||||
Hello, World!\
|
||||
</blockquote>\
|
||||
</mx-reply>\
|
||||
This is <strong>my</strong> reply"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reply_to_raw_no_body() {
|
||||
let room_id = room_id!("!roomid:notareal.hs");
|
||||
let event_id = owned_event_id!("$143273582443PhrSn");
|
||||
|
||||
let original_message: Raw<AnySyncTimelineEvent> = from_json_value(json!({
|
||||
"content": {},
|
||||
"event_id": event_id,
|
||||
"origin_server_ts": 134_829_848,
|
||||
"sender": "@user:notareal.hs",
|
||||
"type": "m.room.message",
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
let reply = RoomMessageEventContent::text_html(
|
||||
"This is **my** reply",
|
||||
"This is <strong>my</strong> reply",
|
||||
)
|
||||
.make_reply_to_raw(
|
||||
&original_message,
|
||||
event_id.clone(),
|
||||
room_id,
|
||||
ForwardThread::Yes,
|
||||
AddMentions::No,
|
||||
);
|
||||
|
||||
assert_matches!(reply.relates_to, Some(Relation::Reply { in_reply_to }));
|
||||
assert_eq!(in_reply_to.event_id, event_id);
|
||||
|
||||
assert_matches!(reply.msgtype, MessageType::Text(text_msg));
|
||||
assert_eq!(text_msg.body, "This is **my** reply");
|
||||
assert_eq!(text_msg.formatted.unwrap().body, "This is <strong>my</strong> reply");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reply_to_raw_no_sender() {
|
||||
let room_id = room_id!("!roomid:notareal.hs");
|
||||
let event_id = owned_event_id!("$143273582443PhrSn");
|
||||
|
||||
let original_message: Raw<AnySyncTimelineEvent> = from_json_value(json!({
|
||||
"content": {
|
||||
"body": "Hello, World!",
|
||||
"msgtype": "m.text",
|
||||
},
|
||||
"event_id": event_id,
|
||||
"origin_server_ts": 134_829_848,
|
||||
"type": "m.room.message",
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
let reply = RoomMessageEventContent::text_html(
|
||||
"This is **my** reply",
|
||||
"This is <strong>my</strong> reply",
|
||||
)
|
||||
.make_reply_to_raw(
|
||||
&original_message,
|
||||
event_id.clone(),
|
||||
room_id,
|
||||
ForwardThread::Yes,
|
||||
AddMentions::No,
|
||||
);
|
||||
|
||||
assert_matches!(reply.relates_to, Some(Relation::Reply { in_reply_to }));
|
||||
assert_eq!(in_reply_to.event_id, event_id);
|
||||
|
||||
assert_matches!(reply.msgtype, MessageType::Text(text_msg));
|
||||
assert_eq!(text_msg.body, "This is **my** reply");
|
||||
assert_eq!(text_msg.formatted.unwrap().body, "This is <strong>my</strong> reply");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reply_to_raw_forward_thread() {
|
||||
let room_id = room_id!("!roomid:notareal.hs");
|
||||
let event_id = owned_event_id!("$143273582443PhrSn");
|
||||
|
||||
let original_message: Raw<AnySyncTimelineEvent> = from_json_value(json!({
|
||||
"content": {
|
||||
"m.relates_to": {
|
||||
"rel_type": "m.thread",
|
||||
"event_id": "$threadroot",
|
||||
"m.in_reply_to": {
|
||||
"event_id": "$repliedto",
|
||||
},
|
||||
},
|
||||
},
|
||||
"event_id": event_id,
|
||||
"origin_server_ts": 134_829_848,
|
||||
"sender": "@user:notareal.hs",
|
||||
"type": "m.room.message",
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
let reply = RoomMessageEventContent::text_html(
|
||||
"This is **my** reply",
|
||||
"This is <strong>my</strong> reply",
|
||||
)
|
||||
.make_reply_to_raw(
|
||||
&original_message,
|
||||
event_id.clone(),
|
||||
room_id,
|
||||
ForwardThread::Yes,
|
||||
AddMentions::No,
|
||||
);
|
||||
|
||||
assert_matches!(reply.relates_to, Some(Relation::Thread(thread)));
|
||||
assert_eq!(thread.event_id, "$threadroot");
|
||||
assert_eq!(thread.in_reply_to.unwrap().event_id, event_id);
|
||||
|
||||
assert_matches!(reply.msgtype, MessageType::Text(text_msg));
|
||||
assert_eq!(text_msg.body, "This is **my** reply");
|
||||
assert_eq!(text_msg.formatted.unwrap().body, "This is <strong>my</strong> reply");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reply_to_raw_add_mentions() {
|
||||
let room_id = room_id!("!roomid:notareal.hs");
|
||||
let event_id = owned_event_id!("$143273582443PhrSn");
|
||||
|
||||
let user_id = owned_user_id!("@user:notareal.hs");
|
||||
let other_user_id = owned_user_id!("@other_user:notareal.hs");
|
||||
|
||||
let original_message: Raw<AnySyncTimelineEvent> = from_json_value(json!({
|
||||
"content": {
|
||||
"m.mentions": {
|
||||
"user_ids": [other_user_id],
|
||||
},
|
||||
},
|
||||
"event_id": event_id,
|
||||
"origin_server_ts": 134_829_848,
|
||||
"sender": user_id,
|
||||
"type": "m.room.message",
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
let reply = RoomMessageEventContent::text_html(
|
||||
"This is **my** reply",
|
||||
"This is <strong>my</strong> reply",
|
||||
)
|
||||
.make_reply_to_raw(
|
||||
&original_message,
|
||||
event_id.clone(),
|
||||
room_id,
|
||||
ForwardThread::Yes,
|
||||
AddMentions::Yes,
|
||||
);
|
||||
|
||||
assert_matches!(reply.relates_to, Some(Relation::Reply { in_reply_to }));
|
||||
assert_eq!(in_reply_to.event_id, event_id);
|
||||
|
||||
assert_matches!(reply.msgtype, MessageType::Text(text_msg));
|
||||
assert_eq!(text_msg.body, "This is **my** reply");
|
||||
assert_eq!(text_msg.formatted.unwrap().body, "This is <strong>my</strong> reply");
|
||||
|
||||
assert_eq!(reply.mentions.unwrap().user_ids, BTreeSet::from([user_id, other_user_id]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn make_replacement_no_reply() {
|
||||
let content = RoomMessageEventContent::text_html(
|
||||
|
Loading…
x
Reference in New Issue
Block a user