From 8adc9a0eb6a05f11bfad47948dd90fd7193dccd1 Mon Sep 17 00:00:00 2001 From: Adam <13720823+Frinksy@users.noreply.github.com> Date: Mon, 10 May 2021 09:06:40 +0100 Subject: [PATCH] events: Add helpers to construct the fallback for rich replies --- crates/ruma-events/Cargo.toml | 1 + crates/ruma-events/src/room/message.rs | 318 +++++++++++++++++++++++++ 2 files changed, 319 insertions(+) diff --git a/crates/ruma-events/Cargo.toml b/crates/ruma-events/Cargo.toml index 2c78efd6..f3789cff 100644 --- a/crates/ruma-events/Cargo.toml +++ b/crates/ruma-events/Cargo.toml @@ -13,6 +13,7 @@ edition = "2018" [dependencies] criterion = { version = "0.3.3", optional = true } +indoc = "1.0" js_int = { version = "0.2.0", features = ["serde"] } ruma-common = { version = "0.5.0", path = "../ruma-common" } ruma-events-macros = { version = "=0.22.0-alpha.3", path = "../ruma-events-macros" } diff --git a/crates/ruma-events/src/room/message.rs b/crates/ruma-events/src/room/message.rs index 24f9634a..f1cd263e 100644 --- a/crates/ruma-events/src/room/message.rs +++ b/crates/ruma-events/src/room/message.rs @@ -2,6 +2,7 @@ use std::borrow::Cow; +use indoc::formatdoc; use js_int::UInt; use ruma_events_macros::MessageEventContent; use ruma_identifiers::MxcUri; @@ -85,6 +86,73 @@ impl MessageEventContent { pub fn notice_html(body: impl Into, html_body: impl Into) -> Self { Self::new(MessageType::Notice(NoticeMessageEventContent::html(body, html_body))) } + + /// Creates a plain text reply to a message. + pub fn text_reply_plain(reply: impl Into, original_message: &MessageEvent) -> Self { + let quoted = get_plain_quote_fallback(original_message); + + let body = format!("{}\n\n{}", quoted, reply.into()); + + Self { + relates_to: Some(Relation::Reply { + in_reply_to: InReplyTo { event_id: original_message.event_id.clone() }, + }), + ..Self::text_plain(body) + } + } + + /// Creates a html text reply to a message. + pub fn text_reply_html( + reply: impl Into, + html_reply: impl Into, + original_message: &MessageEvent, + ) -> Self { + let quoted = get_plain_quote_fallback(original_message); + let quoted_html = get_html_quote_fallback(original_message); + + let body = format!("{}\n\n{}", quoted, reply.into()); + let html_body = format!("{}\n\n{}", quoted_html, html_reply.into()); + + Self { + relates_to: Some(Relation::Reply { + in_reply_to: InReplyTo { event_id: original_message.event_id.clone() }, + }), + ..Self::text_html(body, html_body) + } + } + + /// Creates a plain text notice reply to a message. + pub fn notice_reply_plain(reply: impl Into, original_message: &MessageEvent) -> Self { + let quoted = get_plain_quote_fallback(original_message); + + let body = format!("{}\n\n{}", quoted, reply.into()); + Self { + relates_to: Some(Relation::Reply { + in_reply_to: InReplyTo { event_id: original_message.event_id.clone() }, + }), + ..Self::notice_plain(body) + } + } + + /// Creates a html text notice reply to a message. + pub fn notice_reply_html( + reply: impl Into, + html_reply: impl Into, + original_message: &MessageEvent, + ) -> Self { + let quoted = get_plain_quote_fallback(original_message); + let quoted_html = get_html_quote_fallback(original_message); + + let body = format!("{}\n\n{}", quoted, reply.into()); + let html_body = format!("{}\n\n{}", quoted_html, html_reply.into()); + + Self { + relates_to: Some(Relation::Reply { + in_reply_to: InReplyTo { event_id: original_message.event_id.clone() }, + }), + ..Self::notice_html(body, html_body) + } + } } /// The content that is specific to each message type variant. @@ -690,3 +758,253 @@ pub struct CustomEventContent { #[serde(flatten)] pub data: JsonObject, } + +fn get_plain_quote_fallback(original_message: &MessageEvent) -> String { + match &original_message.content.msgtype { + MessageType::Audio(_) => { + format!("> <{:?}> sent an audio file.", original_message.sender) + } + MessageType::Emote(content) => { + format!("> * <{:?}> {}", original_message.sender, content.body) + } + MessageType::File(_) => { + format!("> <{:?}> sent a file.", original_message.sender) + } + MessageType::Image(_) => { + format!("> <{:?}> sent an image.", original_message.sender) + } + MessageType::Location(content) => { + format!("> <{:?}> {}", original_message.sender, content.body) + } + MessageType::Notice(content) => { + format!("> <{:?}> {}", original_message.sender, content.body) + } + MessageType::ServerNotice(content) => { + format!("> <{:?}> {}", original_message.sender, content.body) + } + MessageType::Text(content) => { + format!("> <{:?}> {}", original_message.sender, content.body) + } + MessageType::Video(_) => { + format!("> <{:?}> sent a video.", original_message.sender) + } + MessageType::_Custom(content) => { + format!( + "> <{:?}> {}", + original_message.sender, + content.data["body"].as_str().unwrap_or(""), + ) + } + #[cfg(feature = "unstable-pre-spec")] + MessageType::VerificationRequest(content) => { + format!("> <{:?}> {}", original_message.sender, content.body) + } + } +} + +fn get_html_quote_fallback(original_message: &MessageEvent) -> String { + match &original_message.content.msgtype { + MessageType::Audio(_) => { + formatdoc!( + " + +
+ In reply to + {sender} +
+ sent an audio file. +
+
+ ", + room_id = original_message.room_id, + event_id = original_message.event_id, + sender = original_message.sender, + ) + } + MessageType::Emote(content) => { + formatdoc!( + " + +
+ In reply to + * {sender} +
+ {body} +
+
+ ", + room_id = original_message.room_id, + event_id = original_message.event_id, + sender = original_message.sender, + body = formatted_or_plain_body(&content.formatted, &content.body), + ) + } + MessageType::File(_) => { + formatdoc!( + " + +
+ In reply to + {sender} +
+ sent a file. +
+
+ ", + room_id = original_message.room_id, + event_id = original_message.event_id, + sender = original_message.sender, + ) + } + MessageType::Image(_) => { + formatdoc!( + " + +
+ In reply to + {sender} +
+ sent an image. +
+
+ ", + room_id = original_message.room_id, + event_id = original_message.event_id, + sender = original_message.sender, + ) + } + MessageType::Location(_) => { + formatdoc!( + " + +
+ In reply to + {sender} +
+ sent a location. +
+
+ ", + room_id = original_message.room_id, + event_id = original_message.event_id, + sender = original_message.sender, + ) + } + MessageType::Notice(content) => { + formatdoc!( + " + +
+ In reply to + {sender} +
+ {body} +
+
+ ", + room_id = original_message.room_id, + event_id = original_message.event_id, + sender = original_message.sender, + body = formatted_or_plain_body(&content.formatted, &content.body), + ) + } + MessageType::ServerNotice(content) => { + formatdoc!( + " + +
+ In reply to + {sender} +
+ {body} +
+
+ ", + room_id = original_message.room_id, + event_id = original_message.event_id, + sender = original_message.sender, + body = content.body, + ) + } + MessageType::Text(content) => { + formatdoc!( + " + +
+ In reply to + {sender} +
+ {body} +
+
+ ", + room_id = original_message.room_id, + event_id = original_message.event_id, + sender = original_message.sender, + body = formatted_or_plain_body(&content.formatted, &content.body), + ) + } + MessageType::Video(_) => { + formatdoc!( + " + +
+ In reply to + {sender} +
+ sent a video. +
+
+ ", + room_id = original_message.room_id, + event_id = original_message.event_id, + sender = original_message.sender, + ) + } + MessageType::_Custom(content) => { + formatdoc!( + " + +
+ In reply to + {sender} +
+ {body} +
+
+ ", + room_id = original_message.room_id, + event_id = original_message.event_id, + sender = original_message.sender, + body = content.data["body"].as_str().unwrap_or(""), + ) + } + #[cfg(feature = "unstable-pre-spec")] + MessageType::VerificationRequest(content) => { + formatdoc!( + " + +
+ In reply to + {sender} +
+ {body} +
+
+ ", + server_name = original_message.room_id, + event_id = original_message.event_id, + sender = original_message.sender, + body = content.body, + ) + } + } +} + +fn formatted_or_plain_body<'a>(formatted: &'a Option, body: &'a str) -> &'a str { + if let Some(formatted_body) = formatted { + &formatted_body.body + } else { + body + } +}