Skip to main content

ruma_client_api/delayed_events/
delayed_message_event.rs

1//! `PUT /_matrix/client/*/rooms/{roomId}/send/{eventType}/{txnId}`
2//!
3//! Send a delayed event (a scheduled message) to a room.
4//!
5//! This endpoint implements a previous iteration of MSC4140 at commit [`3ee73ab`].
6//! At the time of writing, this matches the current implementation of Synapse but the latest
7//! iteration of the MSC uses the `send_delayed_event` endpoint instead.
8//!
9//! [`3ee73ab`]: https://github.com/matrix-org/matrix-spec-proposals/blob/3ee73abe5f81252b00877cfb5db941ee9aa6c18d/proposals/4140-delayed-events-futures.md
10
11pub mod unstable {
12    //! `msc4140` ([MSC])
13    //!
14    //! [MSC]: https://github.com/matrix-org/matrix-spec-proposals/pull/4140
15
16    use ruma_common::{
17        OwnedRoomId, OwnedTransactionId,
18        api::{auth_scheme::AccessToken, request, response},
19        metadata,
20        serde::Raw,
21    };
22    use ruma_events::{AnyMessageLikeEventContent, MessageLikeEventContent, MessageLikeEventType};
23    use serde_json::value::to_raw_value as to_raw_json_value;
24
25    use crate::delayed_events::DelayParameters;
26
27    metadata! {
28        method: PUT,
29        rate_limited: false,
30        authentication: AccessToken,
31        history: {
32            // We use the unstable prefix for the delay query parameter but the stable v3 endpoint.
33            unstable => "/_matrix/client/v3/rooms/{room_id}/send/{event_type}/{txn_id}",
34        }
35    }
36    /// Request type for the [`delayed_message_event`](crate::delayed_events::delayed_message_event)
37    /// endpoint.
38    #[request]
39    pub struct Request {
40        /// The room to send the event to.
41        #[ruma_api(path)]
42        pub room_id: OwnedRoomId,
43
44        /// The type of event to send.
45        #[ruma_api(path)]
46        pub event_type: MessageLikeEventType,
47
48        /// The transaction ID for this event.
49        ///
50        /// Clients should generate a unique ID across requests within the
51        /// same session. A session is identified by an access token, and
52        /// persists when the [access token is refreshed].
53        ///
54        /// It will be used by the server to ensure idempotency of requests.
55        ///
56        /// [access token is refreshed]: https://spec.matrix.org/v1.18/client-server-api/#refreshing-access-tokens
57        #[ruma_api(path)]
58        pub txn_id: OwnedTransactionId,
59
60        /// The timeout duration for this delayed event.
61        #[ruma_api(query_all)]
62        pub delay_parameters: DelayParameters,
63
64        /// The event content to send.
65        #[ruma_api(body)]
66        pub body: Raw<AnyMessageLikeEventContent>,
67    }
68
69    /// Response type for the
70    /// [`delayed_message_event`](crate::delayed_events::delayed_message_event) endpoint.
71    #[response]
72    pub struct Response {
73        /// The `delay_id` generated for this delayed event. Used to interact with delayed events.
74        pub delay_id: String,
75    }
76
77    impl Request {
78        /// Creates a new `Request` with the given room id, transaction id, `delay_parameters` and
79        /// event content.
80        ///
81        /// # Errors
82        ///
83        /// Since `Request` stores the request body in serialized form, this function can fail if
84        /// `T`s [`::serde::Serialize`] implementation can fail.
85        pub fn new<T>(
86            room_id: OwnedRoomId,
87            txn_id: OwnedTransactionId,
88            delay_parameters: DelayParameters,
89            content: &T,
90        ) -> serde_json::Result<Self>
91        where
92            T: MessageLikeEventContent,
93        {
94            Ok(Self {
95                room_id,
96                txn_id,
97                event_type: content.event_type(),
98                delay_parameters,
99                body: Raw::from_json(to_raw_json_value(content)?),
100            })
101        }
102
103        /// Creates a new `Request` with the given room id, transaction id, event type,
104        /// `delay_parameters` and raw event content.
105        pub fn new_raw(
106            room_id: OwnedRoomId,
107            txn_id: OwnedTransactionId,
108            event_type: MessageLikeEventType,
109            delay_parameters: DelayParameters,
110            body: Raw<AnyMessageLikeEventContent>,
111        ) -> Self {
112            Self { room_id, event_type, txn_id, delay_parameters, body }
113        }
114    }
115
116    impl Response {
117        /// Creates a new `Response` with the tokens required to control the delayed event using the
118        /// [`crate::delayed_events::update_delayed_event::unstable_v1::Request`] request.
119        pub fn new(delay_id: String) -> Self {
120            Self { delay_id }
121        }
122    }
123
124    #[cfg(all(test, feature = "client"))]
125    mod tests {
126        use std::borrow::Cow;
127
128        use ruma_common::{
129            api::{
130                MatrixVersion, OutgoingRequest, SupportedVersions, auth_scheme::SendAccessToken,
131            },
132            owned_room_id,
133        };
134        use ruma_events::room::message::RoomMessageEventContent;
135        use serde_json::{Value as JsonValue, json};
136        use web_time::Duration;
137
138        use super::Request;
139        use crate::delayed_events::delayed_message_event::unstable::DelayParameters;
140
141        #[test]
142        fn serialize_delayed_message_request() {
143            let room_id = owned_room_id!("!roomid:example.org");
144            let supported = SupportedVersions {
145                versions: [MatrixVersion::V1_1].into(),
146                features: Default::default(),
147            };
148
149            let req = Request::new(
150                room_id,
151                "1234".into(),
152                DelayParameters::Timeout { timeout: Duration::from_millis(103) },
153                &RoomMessageEventContent::text_plain("test"),
154            )
155            .unwrap();
156            let request: http::Request<Vec<u8>> = req
157                .try_into_http_request(
158                    "https://homeserver.tld",
159                    SendAccessToken::IfRequired("auth_tok"),
160                    Cow::Owned(supported),
161                )
162                .unwrap();
163            let (parts, body) = request.into_parts();
164            assert_eq!(
165                "https://homeserver.tld/_matrix/client/v3/rooms/!roomid:example.org/send/m.room.message/1234?org.matrix.msc4140.delay=103",
166                parts.uri.to_string()
167            );
168            assert_eq!("PUT", parts.method.to_string());
169            assert_eq!(
170                json!({"msgtype":"m.text","body":"test"}),
171                serde_json::from_str::<JsonValue>(std::str::from_utf8(&body).unwrap()).unwrap()
172            );
173        }
174    }
175}