Skip to main content

tuwunel_core/matrix/event/
redact.rs

1use ruma::{
2	OwnedEventId,
3	events::{TimelineEventType, room::redaction::RoomRedactionEventContent},
4	room_version_rules::RoomVersionRules,
5};
6use serde::Deserialize;
7use serde_json::value::to_raw_value;
8
9use super::Event;
10use crate::matrix::pdu::Content;
11
12/// Copies the `redacts` property of the event to the `content` dict and
13/// vice-versa.
14///
15/// This follows the specification's
16/// [recommendation](https://spec.matrix.org/v1.10/rooms/v11/#moving-the-redacts-property-of-mroomredaction-events-to-a-content-property):
17///
18/// > For backwards-compatibility with older clients, servers should add a
19/// > redacts property to the top level of m.room.redaction events in when
20/// > serving such events over the Client-Server API.
21///
22/// > For improved compatibility with newer clients, servers should add a
23/// > redacts property to the content of m.room.redaction events in older
24/// > room versions when serving such events over the Client-Server API.
25#[must_use]
26pub(super) fn copy<E: Event>(event: &E) -> (Option<OwnedEventId>, Content) {
27	if *event.event_type() != TimelineEventType::RoomRedaction {
28		return (
29			event.redacts().map(ToOwned::to_owned),
30			Content::from_raw_value(event.content()),
31		);
32	}
33
34	let Ok(mut content) = event.get_content::<RoomRedactionEventContent>() else {
35		return (
36			event.redacts().map(ToOwned::to_owned),
37			Content::from_raw_value(event.content()),
38		);
39	};
40
41	if let Some(redacts) = content.redacts {
42		return (Some(redacts), Content::from_raw_value(event.content()));
43	}
44
45	if let Some(redacts) = event.redacts().map(ToOwned::to_owned) {
46		content.redacts = Some(redacts);
47		return (
48			event.redacts().map(ToOwned::to_owned),
49			Content::from_json(
50				to_raw_value(&content).expect("Must be valid, we only added redacts field"),
51			),
52		);
53	}
54
55	(event.redacts().map(ToOwned::to_owned), Content::from_raw_value(event.content()))
56}
57
58#[must_use]
59pub(super) fn is_redacted<E: Event>(event: &E) -> bool {
60	let Some(unsigned) = event.unsigned() else {
61		return false;
62	};
63
64	let Ok(unsigned) = ExtractRedactedBecause::deserialize(unsigned) else {
65		return false;
66	};
67
68	unsigned.redacted_because.is_some()
69}
70
71#[must_use]
72pub(super) fn redacts_id<E: Event>(
73	event: &E,
74	room_rules: &RoomVersionRules,
75) -> Option<OwnedEventId> {
76	if *event.kind() != TimelineEventType::RoomRedaction {
77		return None;
78	}
79
80	if room_rules.redaction.content_field_redacts {
81		event
82			.get_content::<RoomRedactionEventContent>()
83			.ok()?
84			.redacts
85	} else {
86		event.redacts().map(ToOwned::to_owned)
87	}
88}
89
90#[derive(Deserialize)]
91struct ExtractRedactedBecause {
92	redacted_because: Option<serde::de::IgnoredAny>,
93}