Skip to main content

tuwunel_api/client/read_marker/
read_markers.rs

1use std::collections::BTreeMap;
2
3use axum::extract::State;
4use ruma::{
5	MilliSecondsSinceUnixEpoch,
6	api::client::read_marker::set_read_marker,
7	events::{
8		RoomAccountDataEventType,
9		fully_read::{FullyReadEvent, FullyReadEventContent},
10		receipt::{Receipt, ReceiptEvent, ReceiptEventContent, ReceiptThread, ReceiptType},
11	},
12	presence::PresenceState,
13};
14use tuwunel_core::{Err, PduCount, Result, err};
15
16use crate::{ClientIp, Ruma};
17
18/// # `POST /_matrix/client/r0/rooms/{roomId}/read_markers`
19///
20/// Sets different types of read markers.
21///
22/// - Updates fully-read account data event to `fully_read`
23/// - If `read_receipt` is set: Update private marker and public read receipt
24///   EDU
25pub(crate) async fn set_read_marker_route(
26	State(services): State<crate::State>,
27	ClientIp(client): ClientIp,
28	body: Ruma<set_read_marker::v3::Request>,
29) -> Result<set_read_marker::v3::Response> {
30	let sender_user = body.sender_user();
31
32	if body.private_read_receipt.is_some() || body.read_receipt.is_some() {
33		// Route through the dispatcher so per-thread counts are also cleared;
34		// `/read_markers` predates MSC3771 and carries no thread field.
35		services
36			.pusher
37			.reset_notification_counts_for_thread(
38				sender_user,
39				&body.room_id,
40				&ReceiptThread::Unthreaded,
41			)
42			.await;
43	}
44
45	if let Some(event) = &body.fully_read {
46		let fully_read_event = FullyReadEvent {
47			content: FullyReadEventContent { event_id: event.clone() },
48		};
49
50		services
51			.account_data
52			.update(
53				Some(&body.room_id),
54				sender_user,
55				RoomAccountDataEventType::FullyRead,
56				&serde_json::to_value(fully_read_event)?,
57			)
58			.await
59			.ok();
60	}
61
62	if let Some(event) = &body.private_read_receipt {
63		let count = services
64			.timeline
65			.get_pdu_count(event)
66			.await
67			.map_err(|_| err!(Request(NotFound("Event not found."))))?;
68
69		let PduCount::Normal(count) = count else {
70			return Err!(Request(InvalidParam(
71				"Event is a backfilled PDU and cannot be marked as read."
72			)));
73		};
74
75		services
76			.read_receipt
77			.private_read_set(&body.room_id, sender_user, count, &ReceiptThread::Unthreaded)
78			.await;
79	}
80
81	if let Some(event) = &body.read_receipt {
82		let receipt_content = BTreeMap::from_iter([(
83			event.to_owned(),
84			BTreeMap::from_iter([(
85				ReceiptType::Read,
86				BTreeMap::from_iter([(sender_user.to_owned(), Receipt {
87					ts: Some(MilliSecondsSinceUnixEpoch::now()),
88					thread: ReceiptThread::Unthreaded,
89				})]),
90			)]),
91		)]);
92
93		services
94			.read_receipt
95			.readreceipt_update(sender_user, &body.room_id, &ReceiptEvent {
96				content: ReceiptEventContent(receipt_content),
97				room_id: body.room_id.clone(),
98			})
99			.await;
100
101		services
102			.presence
103			.maybe_ping_presence(
104				sender_user,
105				body.sender_device.as_deref(),
106				Some(client),
107				&PresenceState::Online,
108			)
109			.await
110			.ok();
111	}
112
113	Ok(set_read_marker::v3::Response {})
114}