Skip to main content

ruma_client_api/message/
send_message_event.rs

1//! `PUT /_matrix/client/*/rooms/{roomId}/send/{eventType}/{txnId}`
2//!
3//! Send a message event to a room.
4
5pub mod v3 {
6    //! `/v3/` ([spec])
7    //!
8    //! [spec]: https://spec.matrix.org/v1.19/client-server-api/#put_matrixclientv3roomsroomidsendeventtypetxnid
9
10    use ruma_common::{
11        MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId, OwnedTransactionId,
12        api::{auth_scheme::AccessToken, request, response},
13        metadata,
14        serde::Raw,
15    };
16    #[cfg(feature = "unstable-msc4354")]
17    use ruma_events::sticky::StickyDurationMs;
18    use ruma_events::{AnyMessageLikeEventContent, MessageLikeEventContent, MessageLikeEventType};
19    use serde_json::value::to_raw_value as to_raw_json_value;
20
21    metadata! {
22        method: PUT,
23        rate_limited: false,
24        authentication: AccessToken,
25        history: {
26            1.0 => "/_matrix/client/r0/rooms/{room_id}/send/{event_type}/{txn_id}",
27            1.1 => "/_matrix/client/v3/rooms/{room_id}/send/{event_type}/{txn_id}",
28        }
29    }
30
31    /// Request type for the `create_message_event` endpoint.
32    #[request]
33    pub struct Request {
34        /// The room to send the event to.
35        #[ruma_api(path)]
36        pub room_id: OwnedRoomId,
37
38        /// The type of event to send.
39        #[ruma_api(path)]
40        pub event_type: MessageLikeEventType,
41
42        /// The transaction ID for this event.
43        ///
44        /// Clients should generate a unique ID across requests within the
45        /// same session. A session is identified by an access token, and
46        /// persists when the [access token is refreshed].
47        ///
48        /// It will be used by the server to ensure idempotency of requests.
49        ///
50        /// [access token is refreshed]: https://spec.matrix.org/v1.19/client-server-api/#refreshing-access-tokens
51        #[ruma_api(path)]
52        pub txn_id: OwnedTransactionId,
53
54        /// The event content to send.
55        #[ruma_api(body)]
56        #[serde(deserialize_with = "ruma_common::serde::deserialize_raw_object")]
57        pub body: Raw<AnyMessageLikeEventContent>,
58
59        /// Timestamp to use for the `origin_server_ts` of the event.
60        ///
61        /// This is called [timestamp massaging] and can only be used by Appservices.
62        ///
63        /// Note that this does not change the position of the event in the timeline.
64        ///
65        /// [timestamp massaging]: https://spec.matrix.org/v1.19/application-service-api/#timestamp-massaging
66        #[ruma_api(query)]
67        #[serde(skip_serializing_if = "Option::is_none", rename = "ts")]
68        pub timestamp: Option<MilliSecondsSinceUnixEpoch>,
69
70        /// The duration to stick the event for.
71        ///
72        /// Valid values are the integer range 0-3600000 (1 hour).
73        /// The presence of this field indicates that the event should be sticky, this
74        /// will give this event additional delivery guarantees.
75        ///
76        /// Caller must first check that the server supports sticky events (via `/versions`),
77        /// or it will be no-op.
78        ///
79        /// See [MSC4354 sticky events](https://github.com/matrix-org/matrix-spec-proposals/pull/4354).
80        #[cfg(feature = "unstable-msc4354")]
81        #[ruma_api(query)]
82        #[serde(
83            skip_serializing_if = "Option::is_none",
84            rename = "org.matrix.msc4354.sticky_duration_ms"
85        )]
86        pub sticky_duration_ms: Option<StickyDurationMs>,
87    }
88
89    /// Response type for the `create_message_event` endpoint.
90    #[response]
91    pub struct Response {
92        /// A unique identifier for the event.
93        pub event_id: OwnedEventId,
94    }
95
96    impl Request {
97        /// Creates a new `Request` with the given room id, transaction id and event content.
98        ///
99        /// # Errors
100        ///
101        /// Since `Request` stores the request body in serialized form, this function can fail if
102        /// `T`s [`Serialize`][serde::Serialize] implementation can fail.
103        pub fn new<T>(
104            room_id: OwnedRoomId,
105            txn_id: OwnedTransactionId,
106            content: &T,
107        ) -> serde_json::Result<Self>
108        where
109            T: MessageLikeEventContent,
110        {
111            Ok(Self {
112                room_id,
113                txn_id,
114                event_type: content.event_type(),
115                body: Raw::from_json(to_raw_json_value(content)?),
116                timestamp: None,
117                #[cfg(feature = "unstable-msc4354")]
118                sticky_duration_ms: None,
119            })
120        }
121
122        /// Creates a new `Request` with the given room id, transaction id, event type and raw event
123        /// content.
124        pub fn new_raw(
125            room_id: OwnedRoomId,
126            txn_id: OwnedTransactionId,
127            event_type: MessageLikeEventType,
128            body: Raw<AnyMessageLikeEventContent>,
129        ) -> Self {
130            Self {
131                room_id,
132                event_type,
133                txn_id,
134                body,
135                timestamp: None,
136                #[cfg(feature = "unstable-msc4354")]
137                sticky_duration_ms: None,
138            }
139        }
140    }
141
142    impl Response {
143        /// Creates a new `Response` with the given event id.
144        pub fn new(event_id: OwnedEventId) -> Self {
145            Self { event_id }
146        }
147    }
148}
149
150#[cfg(all(test, feature = "client", feature = "unstable-msc4354"))]
151mod tests {
152    use std::borrow::Cow;
153
154    use ruma_common::{
155        api::{
156            MatrixVersion, OutgoingRequestExt as _, SupportedVersions, auth_scheme::SendAccessToken,
157        },
158        owned_room_id,
159        serde::Raw,
160    };
161    use ruma_events::{MessageLikeEventType, sticky::StickyDurationMs};
162    use serde_json::json;
163
164    #[test]
165    fn test_sticky_send_message_request() {
166        let supported = SupportedVersions {
167            versions: [MatrixVersion::V1_1].into(),
168            features: Default::default(),
169        };
170
171        let mut request = crate::message::send_message_event::v3::Request::new_raw(
172            owned_room_id!("!roomid:example.org"),
173            "0000".into(),
174            MessageLikeEventType::RoomMessage,
175            Raw::new(&json!({ "body": "Hello" })).unwrap().cast_unchecked(),
176        );
177        request.sticky_duration_ms = Some(StickyDurationMs::new_clamped(123_456_u32));
178        let http_request: http::Request<Vec<u8>> = request
179            .try_into_http_request(
180                "https://homeserver.tld",
181                SendAccessToken::IfRequired("auth_tok"),
182                Cow::Owned(supported),
183            )
184            .unwrap();
185
186        assert_eq!(
187            http_request.uri().query().unwrap(),
188            "org.matrix.msc4354.sticky_duration_ms=123456"
189        );
190    }
191}