Skip to main content

tuwunel_api/client/
to_device.rs

1use std::collections::BTreeMap;
2
3use axum::extract::State;
4use ruma::{
5	api::{
6		client::to_device::send_event_to_device,
7		error::ErrorKind,
8		federation::{self, transactions::edu::DirectDeviceContent},
9	},
10	to_device::DeviceIdOrAllDevices,
11};
12use tuwunel_core::{Error, Result, utils::ReadyExt};
13use tuwunel_service::sending::EduBuf;
14
15use crate::Ruma;
16
17/// # `PUT /_matrix/client/r0/sendToDevice/{eventType}/{txnId}`
18///
19/// Send a to-device event to a set of client devices.
20pub(crate) async fn send_event_to_device_route(
21	State(services): State<crate::State>,
22	body: Ruma<send_event_to_device::v3::Request>,
23) -> Result<send_event_to_device::v3::Response> {
24	let sender_user = body.sender_user();
25	let sender_device = body.sender_device.as_deref();
26
27	// Check if this is a new transaction id
28	if services
29		.transaction_ids
30		.existing_txnid(sender_user, sender_device, &body.txn_id)
31		.await
32		.is_ok()
33	{
34		return Ok(send_event_to_device::v3::Response {});
35	}
36
37	for (target_user_id, map) in &body.messages {
38		for (target_device_id_maybe, event) in map {
39			if !services.globals.user_is_local(target_user_id) {
40				let mut map = BTreeMap::new();
41				map.insert(target_device_id_maybe.clone(), event.clone());
42				let mut messages = BTreeMap::new();
43				messages.insert(target_user_id.clone(), map);
44
45				let mut buf = EduBuf::new();
46				serde_json::to_writer(
47					&mut buf,
48					&federation::transactions::edu::Edu::DirectToDevice(DirectDeviceContent {
49						sender: sender_user.to_owned(),
50						ev_type: body.event_type.clone(),
51						message_id: services.globals.next_count().to_string().into(),
52						messages,
53					}),
54				)
55				.expect("DirectToDevice EDU can be serialized");
56
57				services
58					.sending
59					.send_edu_server(target_user_id.server_name(), buf)?;
60
61				continue;
62			}
63
64			let event_type = &body.event_type.to_string();
65
66			let event = event
67				.deserialize_as()
68				.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Event is invalid"))?;
69
70			match target_device_id_maybe {
71				| DeviceIdOrAllDevices::DeviceId(target_device_id) => {
72					services.users.add_to_device_event(
73						sender_user,
74						target_user_id,
75						target_device_id,
76						event_type,
77						&event,
78					);
79				},
80
81				| DeviceIdOrAllDevices::AllDevices => {
82					services
83						.users
84						.all_device_ids(target_user_id)
85						.ready_for_each(|target_device_id| {
86							services.users.add_to_device_event(
87								sender_user,
88								target_user_id,
89								target_device_id,
90								event_type,
91								&event,
92							);
93						})
94						.await;
95				},
96			}
97		}
98	}
99
100	// Save transaction id with empty data
101	services
102		.transaction_ids
103		.add_txnid(sender_user, sender_device, &body.txn_id, &[]);
104
105	Ok(send_event_to_device::v3::Response {})
106}