tuwunel_service/rooms/pdu_metadata/
references.rs1use futures::{Stream, StreamExt, TryFutureExt};
2use ruma::{EventId, OwnedEventId, RoomId};
3use tuwunel_core::{
4 PduId, Result, implement,
5 matrix::{Event, Pdu},
6 trace,
7 utils::{
8 stream::{ReadyExt, TryIgnore, WidebandExt},
9 u64_from_u8,
10 },
11};
12use tuwunel_database::Interfix;
13
14use super::{
15 Service,
16 typed_relations::{Tag, prefix},
17};
18
19const BUNDLE_MAX: usize = 100;
21
22#[implement(Service)]
32#[tracing::instrument(skip_all, level = "trace")]
33pub(super) async fn references(&self, parent: &Pdu) -> Vec<OwnedEventId> {
34 if parent.is_redacted() {
35 return Vec::new();
36 }
37
38 let Ok(parent_id) = self
39 .services
40 .timeline
41 .get_pdu_id(parent.event_id())
42 .map_ok(PduId::from)
43 .await
44 else {
45 return Vec::new();
46 };
47
48 self.referenced_children(parent_id)
49 .take(BUNDLE_MAX)
50 .collect()
51 .await
52}
53
54#[implement(Service)]
55fn referenced_children(&self, parent_id: PduId) -> impl Stream<Item = OwnedEventId> + Send + '_ {
56 let prefix = prefix(parent_id.shortroomid, parent_id.count, Tag::Reference);
57 let seek = prefix.clone();
58
59 self.db
60 .relatesto_typed
61 .raw_stream_from(seek.as_slice())
62 .ignore_err()
63 .ready_take_while(move |(key, _)| key.starts_with(&prefix))
64 .map(|(_, val)| u64_from_u8(val))
65 .wide_filter_map(async |short| {
66 self.services
67 .short
68 .get_eventid_from_short(short)
69 .await
70 .ok()
71 })
72}
73
74#[implement(Service)]
75#[tracing::instrument(skip_all, level = "debug")]
76pub fn mark_as_referenced<'a, I>(&self, room_id: &RoomId, event_ids: I)
77where
78 I: Iterator<Item = &'a EventId>,
79{
80 for event_id in event_ids {
81 let key = (room_id, event_id);
82
83 self.db.referencedevents.put_raw(key, []);
84 }
85}
86
87#[implement(Service)]
88#[tracing::instrument(skip(self), level = "debug", ret)]
89pub async fn is_event_referenced(&self, room_id: &RoomId, event_id: &EventId) -> bool {
90 let key = (room_id, event_id);
91
92 self.db.referencedevents.qry(&key).await.is_ok()
93}
94
95#[implement(Service)]
96#[tracing::instrument(skip(self), level = "debug")]
97pub fn mark_event_soft_failed(&self, event_id: &EventId) {
98 self.db.softfailedeventids.insert(event_id, []);
99}
100
101#[implement(Service)]
102#[tracing::instrument(skip(self), level = "debug", ret)]
103pub async fn is_event_soft_failed(&self, event_id: &EventId) -> bool {
104 self.db
105 .softfailedeventids
106 .get(event_id)
107 .await
108 .is_ok()
109}
110
111#[implement(Service)]
115pub fn soft_failed_event_ids(&self) -> impl Stream<Item = OwnedEventId> + Send + '_ {
116 self.db
117 .softfailedeventids
118 .keys()
119 .ignore_err()
120 .map(|event_id: &EventId| event_id.to_owned())
121}
122
123#[implement(Service)]
127pub fn clear_event_soft_failed(&self, event_id: &EventId) {
128 self.db.softfailedeventids.remove(event_id);
129}
130
131#[implement(Service)]
132#[tracing::instrument(skip(self), level = "debug")]
133pub async fn delete_all_referenced_for_room(&self, room_id: &RoomId) -> Result {
134 let prefix = (room_id, Interfix);
135
136 self.db
137 .referencedevents
138 .keys_prefix_raw(&prefix)
139 .ignore_err()
140 .ready_for_each(|key| {
141 trace!(?key, "Removing key");
142 self.db.referencedevents.remove(key);
143 })
144 .await;
145
146 Ok(())
147}