1#![allow(rustdoc::private_intra_doc_links)]
16#![doc = include_str!("README.md")]
17
18use std::{fmt, time::Duration};
19
20use async_channel::{Receiver, Sender};
21use futures_util::StreamExt;
22use matrix_sdk_common::executor::spawn;
23use ruma::api::client::delayed_events::DelayParameters;
24use serde::de::{self, Deserialize, Deserializer, Visitor};
25use tokio::sync::mpsc::{UnboundedSender, unbounded_channel};
26use tokio_stream::wrappers::UnboundedReceiverStream;
27use tokio_util::sync::{CancellationToken, DropGuard};
28
29use self::{
30 machine::{
31 Action, IncomingMessage, MatrixDriverRequestData, MatrixDriverResponse, SendEventRequest,
32 WidgetMachine,
33 },
34 matrix::MatrixDriver,
35};
36use crate::{Result, room::Room, widget::machine::DownloadFileResponse};
37
38mod capabilities;
39mod filter;
40mod machine;
41mod matrix;
42mod settings;
43
44pub use self::{
45 capabilities::{Capabilities, CapabilitiesProvider},
46 filter::{Filter, MessageLikeEventFilter, StateEventFilter, ToDeviceEventFilter},
47 settings::{
48 ClientProperties, EncryptionSystem, Intent, VirtualElementCallWidgetConfig,
49 VirtualElementCallWidgetProperties, WidgetSettings,
50 },
51};
52
53#[derive(Debug)]
56pub struct WidgetDriver {
57 settings: WidgetSettings,
58
59 from_widget_rx: Receiver<String>,
63
64 to_widget_tx: Sender<String>,
69
70 event_forwarding_guard: Option<DropGuard>,
75}
76
77#[derive(Clone, Debug)]
80pub struct WidgetDriverHandle {
81 to_widget_rx: Receiver<String>,
88
89 from_widget_tx: Sender<String>,
96}
97
98impl WidgetDriverHandle {
99 pub async fn recv(&self) -> Option<String> {
105 self.to_widget_rx.recv().await.ok()
106 }
107
108 pub async fn send(&self, message: String) -> bool {
112 self.from_widget_tx.send(message).await.is_ok()
113 }
114}
115
116impl WidgetDriver {
117 pub fn new(settings: WidgetSettings) -> (Self, WidgetDriverHandle) {
120 let (from_widget_tx, from_widget_rx) = async_channel::unbounded();
121 let (to_widget_tx, to_widget_rx) = async_channel::unbounded();
122
123 let driver = Self { settings, from_widget_rx, to_widget_tx, event_forwarding_guard: None };
124 let channels = WidgetDriverHandle { from_widget_tx, to_widget_rx };
125
126 (driver, channels)
127 }
128
129 #[expect(clippy::result_unit_err)]
134 pub async fn run(
135 mut self,
136 room: Room,
137 capabilities_provider: impl CapabilitiesProvider,
138 ) -> Result<(), ()> {
139 let (incoming_msg_tx, incoming_msg_rx) = unbounded_channel();
146
147 spawn({
154 let incoming_msg_tx = incoming_msg_tx.clone();
155 let from_widget_rx = self.from_widget_rx.clone();
156
157 async move {
158 while let Ok(msg) = from_widget_rx.recv().await {
159 let _ = incoming_msg_tx.send(IncomingMessage::WidgetMessage(msg));
160 }
161 }
162 });
163
164 let (mut widget_machine, initial_actions) = WidgetMachine::new(
168 self.settings.widget_id().to_owned(),
169 room.room_id().to_owned(),
170 self.settings.init_on_content_load(),
171 );
172
173 let matrix_driver = MatrixDriver::new(room.clone());
174
175 let stream = UnboundedReceiverStream::new(incoming_msg_rx)
177 .flat_map(|message| tokio_stream::iter(widget_machine.process(message)));
178
179 let mut combined = tokio_stream::iter(initial_actions).chain(stream);
181
182 while let Some(action) = combined.next().await {
184 self.process_action(&matrix_driver, &incoming_msg_tx, &capabilities_provider, action)
185 .await?;
186 }
187
188 Ok(())
189 }
190
191 async fn process_action(
193 &mut self,
194 matrix_driver: &MatrixDriver,
195 incoming_msg_tx: &UnboundedSender<IncomingMessage>,
196 capabilities_provider: &impl CapabilitiesProvider,
197 action: Action,
198 ) -> Result<(), ()> {
199 match action {
200 Action::SendToWidget(msg) => {
201 self.to_widget_tx.send(msg).await.map_err(|_| ())?;
202 }
203
204 Action::MatrixDriverRequest { request_id, data } => {
205 let response = match data {
206 MatrixDriverRequestData::AcquireCapabilities(cmd) => {
207 let obtained = capabilities_provider
208 .acquire_capabilities(cmd.desired_capabilities)
209 .await;
210 Ok(MatrixDriverResponse::CapabilitiesAcquired(obtained))
211 }
212
213 MatrixDriverRequestData::GetOpenId => {
214 matrix_driver.get_open_id().await.map(MatrixDriverResponse::OpenIdReceived)
215 }
216
217 MatrixDriverRequestData::ReadEvents(cmd) => matrix_driver
218 .read_events(cmd.event_type.into(), cmd.state_key, cmd.limit)
219 .await
220 .map(MatrixDriverResponse::EventsRead),
221
222 MatrixDriverRequestData::ReadState(cmd) => matrix_driver
223 .read_state(cmd.event_type.into(), &cmd.state_key)
224 .await
225 .map(MatrixDriverResponse::StateRead),
226
227 MatrixDriverRequestData::SendEvent(req) => {
228 let SendEventRequest { event_type, state_key, content, delay } = req;
229 let delay_event_parameter = delay.map(|d| DelayParameters::Timeout {
234 timeout: Duration::from_millis(d),
235 });
236 matrix_driver
237 .send(event_type.into(), state_key, content, delay_event_parameter)
238 .await
239 .map(MatrixDriverResponse::EventSent)
240 }
241
242 MatrixDriverRequestData::UpdateDelayedEvent(req) => matrix_driver
243 .update_delayed_event(req.delay_id, req.action)
244 .await
245 .map(MatrixDriverResponse::DelayedEventUpdated),
246
247 MatrixDriverRequestData::SendToDeviceEvent(send_to_device_request) => {
248 matrix_driver
249 .send_to_device(
250 send_to_device_request.event_type.into(),
251 send_to_device_request.messages,
252 )
253 .await
254 .map(MatrixDriverResponse::ToDeviceSent)
255 }
256 MatrixDriverRequestData::DownloadFile(req) => matrix_driver
257 .download_attachment(req.content_uri)
258 .await
259 .map(|file_data_base64| {
260 MatrixDriverResponse::FileDownloaded(DownloadFileResponse {
261 file_data_base64,
262 })
263 }),
264 };
265
266 incoming_msg_tx
268 .send(IncomingMessage::MatrixDriverResponse { request_id, response })
269 .map_err(|_| ())?;
270 }
271
272 Action::Subscribe => {
273 if self.event_forwarding_guard.is_some() {
275 return Ok(());
276 }
277
278 let (stop_forwarding, guard) = {
279 let token = CancellationToken::new();
280 (token.child_token(), token.drop_guard())
281 };
282
283 self.event_forwarding_guard = Some(guard);
284
285 let mut events = matrix_driver.events();
286 let mut state_updates = matrix_driver.state_updates();
287 let mut to_device_events = matrix_driver.to_device_events();
288 let incoming_msg_tx = incoming_msg_tx.clone();
289
290 spawn(async move {
291 loop {
292 tokio::select! {
293 _ = stop_forwarding.cancelled() => {
294 return;
296 }
297
298 Some(event) = events.recv() => {
299 let _ = incoming_msg_tx.send(IncomingMessage::MatrixEventReceived(event));
301 }
302
303 Ok(state) = state_updates.recv() => {
304 let _ = incoming_msg_tx.send(IncomingMessage::StateUpdateReceived(state));
306 }
307
308 Some(event) = to_device_events.recv() => {
309 let _ = incoming_msg_tx.send(IncomingMessage::ToDeviceReceived(event));
311 }
312 }
313 }
314 });
315 }
316
317 Action::Unsubscribe => {
318 self.event_forwarding_guard = None;
319 }
320 }
321
322 Ok(())
323 }
324}
325
326#[derive(Clone, Debug)]
328pub(crate) enum StateKeySelector {
329 Key(String),
330 Any,
331}
332
333impl<'de> Deserialize<'de> for StateKeySelector {
334 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
335 where
336 D: Deserializer<'de>,
337 {
338 struct StateKeySelectorVisitor;
339
340 impl Visitor<'_> for StateKeySelectorVisitor {
341 type Value = StateKeySelector;
342
343 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
344 write!(f, "a string or `true`")
345 }
346
347 fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
348 where
349 E: de::Error,
350 {
351 if v {
352 Ok(StateKeySelector::Any)
353 } else {
354 Err(E::invalid_value(de::Unexpected::Bool(v), &self))
355 }
356 }
357
358 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
359 where
360 E: de::Error,
361 {
362 self.visit_string(v.to_owned())
363 }
364
365 fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
366 where
367 E: de::Error,
368 {
369 Ok(StateKeySelector::Key(v))
370 }
371 }
372
373 deserializer.deserialize_any(StateKeySelectorVisitor)
374 }
375}
376
377#[cfg(test)]
378mod tests {
379 use assert_matches::assert_matches;
380 use serde_json::json;
381
382 use super::StateKeySelector;
383
384 #[test]
385 fn state_key_selector_from_true() {
386 let state_key = serde_json::from_value(json!(true)).unwrap();
387 assert_matches!(state_key, StateKeySelector::Any);
388 }
389
390 #[test]
391 fn state_key_selector_from_string() {
392 let state_key = serde_json::from_value(json!("test")).unwrap();
393 assert_matches!(state_key, StateKeySelector::Key(k) if k == "test");
394 }
395
396 #[test]
397 fn state_key_selector_from_false() {
398 let result = serde_json::from_value::<StateKeySelector>(json!(false));
399 assert_matches!(result, Err(e) if e.is_data());
400 }
401
402 #[test]
403 fn state_key_selector_from_number() {
404 let result = serde_json::from_value::<StateKeySelector>(json!(5));
405 assert_matches!(result, Err(e) if e.is_data());
406 }
407}