ruma_events/room/message/
without_relation.rs1use serde::Serialize;
2
3use super::{
4 AddMentions, ForwardThread, MessageType, Relation, ReplacementMetadata, ReplyMetadata,
5 ReplyWithinThread, RoomMessageEventContent,
6};
7#[cfg(feature = "unstable-msc4471")]
8use crate::stream::StreamDescriptor;
9use crate::{
10 Mentions,
11 relation::{InReplyTo, Replacement, Reply, Thread},
12};
13
14#[derive(Clone, Debug, Serialize)]
16#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
17pub struct RoomMessageEventContentWithoutRelation {
18 #[serde(flatten)]
22 pub msgtype: MessageType,
23
24 #[serde(rename = "m.mentions", skip_serializing_if = "Option::is_none")]
28 pub mentions: Option<Mentions>,
29
30 #[cfg(feature = "unstable-msc4471")]
34 #[serde(rename = "org.matrix.msc4471.stream", skip_serializing_if = "Option::is_none")]
35 pub stream: Option<StreamDescriptor>,
36}
37
38impl RoomMessageEventContentWithoutRelation {
39 pub fn new(msgtype: MessageType) -> Self {
41 Self {
42 msgtype,
43 mentions: None,
44 #[cfg(feature = "unstable-msc4471")]
45 stream: None,
46 }
47 }
48
49 pub fn text_plain(body: impl Into<String>) -> Self {
51 Self::new(MessageType::text_plain(body))
52 }
53
54 pub fn text_html(body: impl Into<String>, html_body: impl Into<String>) -> Self {
56 Self::new(MessageType::text_html(body, html_body))
57 }
58
59 #[cfg(feature = "markdown")]
61 pub fn text_markdown(body: impl AsRef<str> + Into<String>) -> Self {
62 Self::new(MessageType::text_markdown(body))
63 }
64
65 pub fn notice_plain(body: impl Into<String>) -> Self {
67 Self::new(MessageType::notice_plain(body))
68 }
69
70 pub fn notice_html(body: impl Into<String>, html_body: impl Into<String>) -> Self {
72 Self::new(MessageType::notice_html(body, html_body))
73 }
74
75 #[cfg(feature = "markdown")]
77 pub fn notice_markdown(body: impl AsRef<str> + Into<String>) -> Self {
78 Self::new(MessageType::notice_markdown(body))
79 }
80
81 pub fn emote_plain(body: impl Into<String>) -> Self {
83 Self::new(MessageType::emote_plain(body))
84 }
85
86 pub fn emote_html(body: impl Into<String>, html_body: impl Into<String>) -> Self {
88 Self::new(MessageType::emote_html(body, html_body))
89 }
90
91 #[cfg(feature = "markdown")]
93 pub fn emote_markdown(body: impl AsRef<str> + Into<String>) -> Self {
94 Self::new(MessageType::emote_markdown(body))
95 }
96
97 pub fn with_relation(
99 self,
100 relates_to: Option<Relation<RoomMessageEventContentWithoutRelation>>,
101 ) -> RoomMessageEventContent {
102 let Self {
103 msgtype,
104 mentions,
105 #[cfg(feature = "unstable-msc4471")]
106 stream,
107 } = self;
108 RoomMessageEventContent {
109 msgtype,
110 relates_to,
111 mentions,
112 #[cfg(feature = "unstable-msc4471")]
113 stream,
114 }
115 }
116
117 #[track_caller]
126 pub fn make_reply_to<'a>(
127 mut self,
128 metadata: impl Into<ReplyMetadata<'a>>,
129 forward_thread: ForwardThread,
130 add_mentions: AddMentions,
131 ) -> RoomMessageEventContent {
132 let metadata = metadata.into();
133 let original_event_id = metadata.event_id.to_owned();
134
135 let original_thread_id = metadata
136 .thread
137 .filter(|_| forward_thread == ForwardThread::Yes)
138 .map(|thread| thread.event_id.clone());
139 let relates_to = if let Some(event_id) = original_thread_id {
140 Relation::Thread(Thread::plain(event_id.to_owned(), original_event_id.to_owned()))
141 } else {
142 Relation::Reply(Reply::with_event_id(original_event_id.to_owned()))
143 };
144
145 if add_mentions == AddMentions::Yes {
146 self.mentions
147 .get_or_insert_with(Mentions::new)
148 .user_ids
149 .insert(metadata.sender.to_owned());
150 }
151
152 self.with_relation(Some(relates_to))
153 }
154
155 pub fn make_for_thread<'a>(
170 self,
171 metadata: impl Into<ReplyMetadata<'a>>,
172 is_reply: ReplyWithinThread,
173 add_mentions: AddMentions,
174 ) -> RoomMessageEventContent {
175 let metadata = metadata.into();
176
177 let mut content = if is_reply == ReplyWithinThread::Yes {
178 self.make_reply_to(metadata, ForwardThread::No, add_mentions)
179 } else {
180 self.into()
181 };
182
183 let thread_root = if let Some(Thread { event_id, .. }) = &metadata.thread {
184 event_id.to_owned()
185 } else {
186 metadata.event_id.to_owned()
187 };
188
189 content.relates_to = Some(Relation::Thread(Thread {
190 event_id: thread_root,
191 in_reply_to: Some(InReplyTo { event_id: metadata.event_id.to_owned() }),
192 is_falling_back: is_reply == ReplyWithinThread::No,
193 }));
194
195 content
196 }
197
198 #[track_caller]
217 pub fn make_replacement(
218 mut self,
219 metadata: impl Into<ReplacementMetadata>,
220 ) -> RoomMessageEventContent {
221 let metadata = metadata.into();
222
223 let mentions = self.mentions.take();
224
225 if let Some(mentions) = &mentions {
227 let new_mentions = metadata
228 .mentions
229 .map(|old_mentions| {
230 let mut new_mentions = Mentions::new();
231
232 new_mentions.user_ids = mentions
233 .user_ids
234 .iter()
235 .filter(|u| !old_mentions.user_ids.contains(*u))
236 .cloned()
237 .collect();
238
239 new_mentions.room = mentions.room && !old_mentions.room;
240
241 new_mentions
242 })
243 .unwrap_or_else(|| mentions.clone());
244
245 self.mentions = Some(new_mentions);
246 }
247
248 let relates_to = Relation::Replacement(Replacement {
250 event_id: metadata.event_id,
251 new_content: RoomMessageEventContentWithoutRelation {
252 msgtype: self.msgtype.clone(),
253 mentions,
254 #[cfg(feature = "unstable-msc4471")]
255 stream: self.stream.clone(),
256 },
257 });
258
259 self.msgtype.make_replacement_body();
260
261 let mut content = RoomMessageEventContent::from(self);
262 content.relates_to = Some(relates_to);
263
264 content
265 }
266
267 pub fn add_mentions(mut self, mentions: Mentions) -> Self {
275 self.mentions.get_or_insert_with(Mentions::new).add(mentions);
276 self
277 }
278}
279
280impl From<MessageType> for RoomMessageEventContentWithoutRelation {
281 fn from(msgtype: MessageType) -> Self {
282 Self::new(msgtype)
283 }
284}
285
286impl From<RoomMessageEventContent> for RoomMessageEventContentWithoutRelation {
287 fn from(value: RoomMessageEventContent) -> Self {
288 let RoomMessageEventContent {
289 msgtype,
290 mentions,
291 #[cfg(feature = "unstable-msc4471")]
292 stream,
293 ..
294 } = value;
295 Self {
296 msgtype,
297 mentions,
298 #[cfg(feature = "unstable-msc4471")]
299 stream,
300 }
301 }
302}
303
304impl From<RoomMessageEventContentWithoutRelation> for RoomMessageEventContent {
305 fn from(value: RoomMessageEventContentWithoutRelation) -> Self {
306 let RoomMessageEventContentWithoutRelation {
307 msgtype,
308 mentions,
309 #[cfg(feature = "unstable-msc4471")]
310 stream,
311 } = value;
312 Self {
313 msgtype,
314 relates_to: None,
315 mentions,
316 #[cfg(feature = "unstable-msc4471")]
317 stream,
318 }
319 }
320}