Skip to main content

ruma_appservice_api/event/
push_events.rs

1//! `PUT /_matrix/app/*/transactions/{txnId}`
2//!
3//! Endpoint to push an event (or batch of events) to the application service.
4
5pub mod v1 {
6    //! `/v1/` ([spec])
7    //!
8    //! [spec]: https://spec.matrix.org/v1.19/application-service-api/#put_matrixappv1transactionstxnid
9
10    use std::borrow::Cow;
11    #[cfg(feature = "unstable-msc3202")]
12    use std::collections::BTreeMap;
13
14    #[cfg(feature = "unstable-msc3202")]
15    use js_int::UInt;
16    #[cfg(feature = "unstable-msc3202")]
17    use ruma_common::OneTimeKeyAlgorithm;
18    #[cfg(any(feature = "unstable-msc3202", feature = "unstable-msc4203"))]
19    use ruma_common::{OwnedDeviceId, OwnedUserId};
20    use ruma_common::{
21        OwnedTransactionId,
22        api::{request, response},
23        metadata,
24        serde::{JsonObject, Raw, from_raw_json_value},
25    };
26    #[cfg(feature = "unstable-msc4203")]
27    use ruma_common::{UserId, serde::JsonCastable};
28    use ruma_events::{
29        AnyTimelineEvent, presence::PresenceEvent, receipt::ReceiptEvent, typing::TypingEvent,
30    };
31    #[cfg(feature = "unstable-msc4203")]
32    use ruma_events::{AnyToDeviceEvent, AnyToDeviceEventContent, ToDeviceEventType};
33    use serde::{Deserialize, Deserializer, Serialize};
34    use serde_json::value::{RawValue as RawJsonValue, Value as JsonValue};
35
36    use crate::HomeserverToken;
37
38    metadata! {
39        method: PUT,
40        rate_limited: false,
41        authentication: HomeserverToken,
42        path: "/_matrix/app/v1/transactions/{txn_id}",
43    }
44
45    /// Request type for the `push_events` endpoint.
46    #[request]
47    pub struct Request {
48        /// The transaction ID for this set of events.
49        ///
50        /// Homeservers generate these IDs and they are used to ensure idempotency of results.
51        #[ruma_api(path)]
52        pub txn_id: OwnedTransactionId,
53
54        /// A list of events.
55        pub events: Vec<Raw<AnyTimelineEvent>>,
56
57        /// Information on E2E device updates.
58        #[cfg(feature = "unstable-msc3202")]
59        #[serde(
60            default,
61            skip_serializing_if = "DeviceLists::is_empty",
62            rename = "org.matrix.msc3202.device_lists"
63        )]
64        pub device_lists: DeviceLists,
65
66        /// The number of unclaimed one-time keys currently held on the server for this device, for
67        /// each algorithm.
68        #[cfg(feature = "unstable-msc3202")]
69        #[serde(
70            default,
71            skip_serializing_if = "BTreeMap::is_empty",
72            rename = "org.matrix.msc3202.device_one_time_keys_count"
73        )]
74        pub device_one_time_keys_count:
75            BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceId, BTreeMap<OneTimeKeyAlgorithm, UInt>>>,
76
77        /// A list of key algorithms for which the server has an unused fallback key for the
78        /// device.
79        #[cfg(feature = "unstable-msc3202")]
80        #[serde(
81            default,
82            skip_serializing_if = "BTreeMap::is_empty",
83            rename = "org.matrix.msc3202.device_unused_fallback_key_types"
84        )]
85        pub device_unused_fallback_key_types:
86            BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceId, Vec<OneTimeKeyAlgorithm>>>,
87
88        /// A list of ephemeral data.
89        #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
90        pub ephemeral: Vec<Raw<EphemeralData>>,
91
92        /// A list of to-device messages.
93        #[cfg(feature = "unstable-msc4203")]
94        #[serde(
95            default,
96            skip_serializing_if = "<[_]>::is_empty",
97            rename = "de.sorunome.msc2409.to_device"
98        )]
99        pub to_device: Vec<Raw<AnyAppserviceToDeviceEvent>>,
100    }
101
102    /// Response type for the `push_events` endpoint.
103    #[response]
104    #[derive(Default)]
105    pub struct Response {}
106
107    impl Request {
108        /// Creates an `Request` with the given transaction ID and list of events.
109        pub fn new(txn_id: OwnedTransactionId, events: Vec<Raw<AnyTimelineEvent>>) -> Request {
110            Request {
111                txn_id,
112                events,
113                #[cfg(feature = "unstable-msc3202")]
114                device_lists: DeviceLists::new(),
115                #[cfg(feature = "unstable-msc3202")]
116                device_one_time_keys_count: BTreeMap::new(),
117                #[cfg(feature = "unstable-msc3202")]
118                device_unused_fallback_key_types: BTreeMap::new(),
119                ephemeral: Vec::new(),
120                #[cfg(feature = "unstable-msc4203")]
121                to_device: Vec::new(),
122            }
123        }
124    }
125
126    impl Response {
127        /// Creates an empty `Response`.
128        pub fn new() -> Self {
129            Self {}
130        }
131    }
132
133    /// Information on E2E device updates.
134    #[derive(Clone, Debug, Default, Deserialize, Serialize)]
135    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
136    #[cfg(feature = "unstable-msc3202")]
137    pub struct DeviceLists {
138        /// List of users who have updated their device identity keys or who now
139        /// share an encrypted room with the client since the previous sync.
140        #[serde(default, skip_serializing_if = "Vec::is_empty")]
141        pub changed: Vec<OwnedUserId>,
142
143        /// List of users who no longer share encrypted rooms since the previous sync
144        /// response.
145        #[serde(default, skip_serializing_if = "Vec::is_empty")]
146        pub left: Vec<OwnedUserId>,
147    }
148
149    #[cfg(feature = "unstable-msc3202")]
150    impl DeviceLists {
151        /// Creates an empty `DeviceLists`.
152        pub fn new() -> Self {
153            Default::default()
154        }
155
156        /// Returns true if there are no device list updates.
157        pub fn is_empty(&self) -> bool {
158            let Self { changed, left } = self;
159            changed.is_empty() && left.is_empty()
160        }
161    }
162
163    /// Type for passing ephemeral data to application services.
164    #[derive(Clone, Debug, Serialize)]
165    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
166    #[serde(untagged)]
167    pub enum EphemeralData {
168        /// A presence update for a user.
169        Presence(PresenceEvent),
170
171        /// A receipt update for a room.
172        Receipt(ReceiptEvent),
173
174        /// A typing notification update for a room.
175        Typing(TypingEvent),
176
177        #[doc(hidden)]
178        _Custom(_CustomEphemeralData),
179    }
180
181    impl EphemeralData {
182        /// A reference to the `type` string of the data.
183        pub fn data_type(&self) -> &str {
184            match self {
185                Self::Presence(_) => "m.presence",
186                Self::Receipt(_) => "m.receipt",
187                Self::Typing(_) => "m.typing",
188                Self::_Custom(c) => &c.data_type,
189            }
190        }
191
192        /// The data as a JSON object.
193        ///
194        /// Prefer to use the public variants of `EphemeralData` where possible; this method is
195        /// meant to be used for unsupported data types only.
196        pub fn data(&self) -> Cow<'_, JsonObject> {
197            fn serialize<T: Serialize>(obj: &T) -> JsonObject {
198                match serde_json::to_value(obj).expect("ephemeral data serialization to succeed") {
199                    JsonValue::Object(obj) => obj,
200                    _ => panic!("all ephemeral data types must serialize to objects"),
201                }
202            }
203
204            match self {
205                Self::Presence(d) => Cow::Owned(serialize(d)),
206                Self::Receipt(d) => Cow::Owned(serialize(d)),
207                Self::Typing(d) => Cow::Owned(serialize(d)),
208                Self::_Custom(c) => Cow::Borrowed(&c.data),
209            }
210        }
211    }
212
213    impl<'de> Deserialize<'de> for EphemeralData {
214        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
215        where
216            D: Deserializer<'de>,
217        {
218            #[derive(Deserialize)]
219            struct EphemeralDataDeHelper {
220                /// The data type.
221                #[serde(rename = "type")]
222                data_type: String,
223            }
224
225            let json = Box::<RawJsonValue>::deserialize(deserializer)?;
226            let EphemeralDataDeHelper { data_type } = from_raw_json_value(&json)?;
227
228            Ok(match data_type.as_ref() {
229                "m.presence" => Self::Presence(from_raw_json_value(&json)?),
230                "m.receipt" => Self::Receipt(from_raw_json_value(&json)?),
231                "m.typing" => Self::Typing(from_raw_json_value(&json)?),
232                _ => Self::_Custom(_CustomEphemeralData {
233                    data_type,
234                    data: from_raw_json_value(&json)?,
235                }),
236            })
237        }
238    }
239
240    /// Ephemeral data with an unknown type.
241    #[doc(hidden)]
242    #[derive(Debug, Clone)]
243    pub struct _CustomEphemeralData {
244        /// The type of the data.
245        data_type: String,
246        /// The data.
247        data: JsonObject,
248    }
249
250    impl Serialize for _CustomEphemeralData {
251        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
252        where
253            S: serde::Serializer,
254        {
255            self.data.serialize(serializer)
256        }
257    }
258
259    /// An event sent using send-to-device messaging with additional fields when pushed to an
260    /// application service.
261    #[derive(Clone, Debug)]
262    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
263    #[cfg(feature = "unstable-msc4203")]
264    pub struct AnyAppserviceToDeviceEvent {
265        /// The to-device event.
266        pub event: AnyToDeviceEvent,
267
268        /// The fully-qualified user ID of the intended recipient.
269        pub to_user_id: OwnedUserId,
270
271        /// The device ID of the intended recipient.
272        pub to_device_id: OwnedDeviceId,
273    }
274
275    #[cfg(feature = "unstable-msc4203")]
276    impl AnyAppserviceToDeviceEvent {
277        /// Construct a new `AnyAppserviceToDeviceEvent` with the given event and recipient
278        /// information.
279        pub fn new(
280            event: AnyToDeviceEvent,
281            to_user_id: OwnedUserId,
282            to_device_id: OwnedDeviceId,
283        ) -> Self {
284            Self { event, to_user_id, to_device_id }
285        }
286
287        /// The fully-qualified ID of the user who sent this event.
288        pub fn sender(&self) -> &UserId {
289            self.event.sender()
290        }
291
292        /// The event type of the to-device event.
293        pub fn event_type(&self) -> ToDeviceEventType {
294            self.event.event_type()
295        }
296
297        /// The content of the to-device event.
298        pub fn content(&self) -> AnyToDeviceEventContent {
299            self.event.content()
300        }
301    }
302
303    #[cfg(feature = "unstable-msc4203")]
304    impl<'de> Deserialize<'de> for AnyAppserviceToDeviceEvent {
305        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
306        where
307            D: Deserializer<'de>,
308        {
309            #[derive(Deserialize)]
310            struct AppserviceFields {
311                to_user_id: OwnedUserId,
312                to_device_id: OwnedDeviceId,
313            }
314
315            let json = Box::<RawJsonValue>::deserialize(deserializer)?;
316
317            let event = from_raw_json_value(&json)?;
318
319            let AppserviceFields { to_user_id, to_device_id } = from_raw_json_value(&json)?;
320
321            Ok(AnyAppserviceToDeviceEvent::new(event, to_user_id, to_device_id))
322        }
323    }
324
325    #[cfg(feature = "unstable-msc4203")]
326    impl JsonCastable<JsonObject> for AnyAppserviceToDeviceEvent {}
327    #[cfg(feature = "unstable-msc4203")]
328    impl JsonCastable<AnyToDeviceEvent> for AnyAppserviceToDeviceEvent {}
329
330    #[cfg(test)]
331    mod tests {
332        use assert_matches2::assert_let;
333        use js_int::uint;
334        use ruma_common::{
335            MilliSecondsSinceUnixEpoch, canonical_json::assert_to_canonical_json_eq, event_id,
336            room_id, user_id,
337        };
338        use ruma_events::receipt::ReceiptType;
339        use serde_json::{from_value as from_json_value, json};
340
341        use super::EphemeralData;
342
343        #[cfg(feature = "client")]
344        #[test]
345        fn request_contains_events_field() {
346            use ruma_common::api::OutgoingRequestExt as _;
347
348            let dummy_event_json = json!({
349                "type": "m.room.message",
350                "event_id": "$143273582443PhrSn:example.com",
351                "origin_server_ts": 1,
352                "room_id": "!roomid:room.com",
353                "sender": "@user:example.com",
354                "content": {
355                    "body": "test",
356                    "msgtype": "m.text",
357                },
358            });
359            let dummy_event = from_json_value(dummy_event_json.clone()).unwrap();
360            let events = vec![dummy_event];
361
362            let req = super::Request::new("any_txn_id".into(), events)
363                .try_into_http_request::<Vec<u8>>("https://homeserver.tld", "auth_tok", ())
364                .unwrap();
365            let json_body: serde_json::Value = serde_json::from_slice(req.body()).unwrap();
366
367            assert_eq!(
368                json_body,
369                json!({
370                    "events": [
371                        dummy_event_json,
372                    ]
373                })
374            );
375        }
376
377        #[test]
378        fn serde_ephemeral_data() {
379            let room_id = room_id!("!jEsUZKDJdhlrceRyVU:server.local");
380            let user_id = user_id!("@alice:server.local");
381            let event_id = event_id!("$1435641916114394fHBL");
382
383            // Test m.typing serde.
384            let typing_json = json!({
385                "type": "m.typing",
386                "room_id": room_id,
387                "content": {
388                    "user_ids": [user_id],
389                },
390            });
391
392            let data = from_json_value::<EphemeralData>(typing_json.clone()).unwrap();
393            assert_let!(EphemeralData::Typing(typing) = &data);
394            assert_eq!(typing.room_id, room_id);
395            assert_eq!(typing.content.user_ids, &[user_id.to_owned()]);
396
397            assert_to_canonical_json_eq!(data, typing_json);
398
399            // Test m.receipt serde.
400            let receipt_json = json!({
401                "type": "m.receipt",
402                "room_id": room_id,
403                "content": {
404                    event_id: {
405                        "m.read": {
406                            user_id: {
407                                "ts": 453,
408                            },
409                        },
410                    },
411                },
412            });
413
414            let data = from_json_value::<EphemeralData>(receipt_json.clone()).unwrap();
415            assert_let!(EphemeralData::Receipt(receipt) = &data);
416            assert_eq!(receipt.room_id, room_id);
417            let event_receipts = receipt.content.get(event_id).unwrap();
418            let event_read_receipts = event_receipts.get(&ReceiptType::Read).unwrap();
419            let event_user_read_receipt = event_read_receipts.get(user_id).unwrap();
420            assert_eq!(event_user_read_receipt.ts, Some(MilliSecondsSinceUnixEpoch(uint!(453))));
421
422            assert_to_canonical_json_eq!(data, receipt_json);
423
424            // Test m.presence serde.
425            let presence_json = json!({
426                "type": "m.presence",
427                "sender": user_id,
428                "content": {
429                    "avatar_url": "mxc://localhost/wefuiwegh8742w",
430                    "currently_active": false,
431                    "last_active_ago": 785,
432                    "presence": "online",
433                    "status_msg": "Making cupcakes",
434                },
435            });
436
437            let data = from_json_value::<EphemeralData>(presence_json.clone()).unwrap();
438            assert_let!(EphemeralData::Presence(presence) = &data);
439            assert_eq!(presence.sender, user_id);
440            assert_eq!(presence.content.currently_active, Some(false));
441
442            assert_to_canonical_json_eq!(data, presence_json);
443
444            // Test custom serde.
445            let custom_json = json!({
446                "type": "dev.ruma.custom",
447                "key": "value",
448                "content": {
449                    "foo": "bar",
450                },
451            });
452
453            let data = from_json_value::<EphemeralData>(custom_json.clone()).unwrap();
454
455            assert_to_canonical_json_eq!(data, custom_json);
456        }
457
458        #[test]
459        #[cfg(feature = "unstable-msc4203")]
460        fn serde_any_appservice_to_device_event() {
461            use ruma_common::{device_id, user_id};
462
463            use super::AnyAppserviceToDeviceEvent;
464
465            let event_json = json!({
466                "type": "m.key.verification.request",
467                "sender": "@alice:example.org",
468                "content": {
469                    "from_device": "AliceDevice2",
470                    "methods": [
471                        "m.sas.v1"
472                    ],
473                    "timestamp": 1_559_598_944_869_i64,
474                    "transaction_id": "S0meUniqueAndOpaqueString"
475                },
476                "to_user_id": "@bob:example.org",
477                "to_device_id": "DEVICEID"
478            });
479
480            // Test deserialization
481            let event = from_json_value::<AnyAppserviceToDeviceEvent>(event_json.clone()).unwrap();
482            assert_eq!(event.sender(), user_id!("@alice:example.org"));
483            assert_eq!(event.to_user_id, user_id!("@bob:example.org"));
484            assert_eq!(event.to_device_id, device_id!("DEVICEID"));
485            assert_eq!(event.event_type().to_string(), "m.key.verification.request");
486        }
487    }
488}