ruma_client_api/room/
create_room.rs

1//! `POST /_matrix/client/*/createRoom`
2//!
3//! Create a new room.
4
5use std::collections::BTreeMap;
6
7use js_int::Int;
8use ruma_common::{
9    OwnedUserId,
10    power_levels::NotificationPowerLevels,
11    serde::{JsonCastable, JsonObject},
12};
13use ruma_events::{TimelineEventType, room::power_levels::RoomPowerLevelsEventContent};
14use serde::Serialize;
15
16pub mod v3 {
17    //! `/v3/` ([spec])
18    //!
19    //! [spec]: https://spec.matrix.org/latest/client-server-api/#post_matrixclientv3createroom
20
21    use assign::assign;
22    use ruma_common::{
23        OwnedRoomId, OwnedUserId, RoomVersionId,
24        api::{auth_scheme::AccessToken, request, response},
25        metadata,
26        room::RoomType,
27        serde::{Raw, StringEnum},
28    };
29    use ruma_events::{
30        AnyInitialStateEvent,
31        room::create::{PreviousRoom, RoomCreateEventContent},
32    };
33    use serde::{Deserialize, Serialize};
34
35    use super::RoomPowerLevelsContentOverride;
36    use crate::{PrivOwnedStr, membership::Invite3pid, room::Visibility};
37
38    metadata! {
39        method: POST,
40        rate_limited: false,
41        authentication: AccessToken,
42        history: {
43            1.0 => "/_matrix/client/r0/createRoom",
44            1.1 => "/_matrix/client/v3/createRoom",
45        }
46    }
47
48    /// Request type for the `create_room` endpoint.
49    #[request(error = crate::Error)]
50    #[derive(Default)]
51    pub struct Request {
52        /// Extra keys to be added to the content of the `m.room.create`.
53        #[serde(default, skip_serializing_if = "Option::is_none")]
54        pub creation_content: Option<Raw<CreationContent>>,
55
56        /// List of state events to send to the new room.
57        ///
58        /// Takes precedence over events set by preset, but gets overridden by name and topic keys.
59        #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
60        pub initial_state: Vec<Raw<AnyInitialStateEvent>>,
61
62        /// A list of user IDs to invite to the room.
63        ///
64        /// This will tell the server to invite everyone in the list to the newly created room.
65        #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
66        pub invite: Vec<OwnedUserId>,
67
68        /// List of third party IDs of users to invite.
69        #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
70        pub invite_3pid: Vec<Invite3pid>,
71
72        /// If set, this sets the `is_direct` flag on room invites.
73        #[serde(default, skip_serializing_if = "ruma_common::serde::is_default")]
74        pub is_direct: bool,
75
76        /// If this is included, an `m.room.name` event will be sent into the room to indicate the
77        /// name of the room.
78        #[serde(skip_serializing_if = "Option::is_none")]
79        pub name: Option<String>,
80
81        /// Power level content to override in the default power level event.
82        #[serde(skip_serializing_if = "Option::is_none")]
83        pub power_level_content_override: Option<Raw<RoomPowerLevelsContentOverride>>,
84
85        /// Convenience parameter for setting various default state events based on a preset.
86        #[serde(skip_serializing_if = "Option::is_none")]
87        pub preset: Option<RoomPreset>,
88
89        /// The desired room alias local part.
90        #[serde(skip_serializing_if = "Option::is_none")]
91        pub room_alias_name: Option<String>,
92
93        /// Room version to set for the room.
94        ///
95        /// Defaults to homeserver's default if not specified.
96        #[serde(skip_serializing_if = "Option::is_none")]
97        pub room_version: Option<RoomVersionId>,
98
99        /// If this is included, an `m.room.topic` event will be sent into the room to indicate
100        /// the topic for the room.
101        #[serde(skip_serializing_if = "Option::is_none")]
102        pub topic: Option<String>,
103
104        /// A public visibility indicates that the room will be shown in the published room list.
105        ///
106        /// A private visibility will hide the room from the published room list. Defaults to
107        /// `Private`.
108        #[serde(default, skip_serializing_if = "ruma_common::serde::is_default")]
109        pub visibility: Visibility,
110    }
111
112    /// Response type for the `create_room` endpoint.
113    #[response(error = crate::Error)]
114    pub struct Response {
115        /// The created room's ID.
116        pub room_id: OwnedRoomId,
117    }
118
119    impl Request {
120        /// Creates a new `Request` will all-default parameters.
121        pub fn new() -> Self {
122            Default::default()
123        }
124    }
125
126    impl Response {
127        /// Creates a new `Response` with the given room id.
128        pub fn new(room_id: OwnedRoomId) -> Self {
129            Self { room_id }
130        }
131    }
132
133    /// Extra options to be added to the `m.room.create` event.
134    ///
135    /// This is the same as the event content struct for `m.room.create`, but without some fields
136    /// that servers are supposed to ignore.
137    #[derive(Clone, Debug, Deserialize, Serialize)]
138    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
139    pub struct CreationContent {
140        /// A list of user IDs to consider as additional creators, and hence grant an "infinite"
141        /// immutable power level, from room version 12 onwards.
142        #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
143        pub additional_creators: Vec<OwnedUserId>,
144
145        /// Whether users on other servers can join this room.
146        ///
147        /// Defaults to `true` if key does not exist.
148        #[serde(
149            rename = "m.federate",
150            default = "ruma_common::serde::default_true",
151            skip_serializing_if = "ruma_common::serde::is_true"
152        )]
153        pub federate: bool,
154
155        /// A reference to the room this room replaces, if the previous room was upgraded.
156        #[serde(skip_serializing_if = "Option::is_none")]
157        pub predecessor: Option<PreviousRoom>,
158
159        /// The room type.
160        ///
161        /// This is currently only used for spaces.
162        #[serde(skip_serializing_if = "Option::is_none", rename = "type")]
163        pub room_type: Option<RoomType>,
164    }
165
166    impl CreationContent {
167        /// Creates a new `CreationContent` with all fields defaulted.
168        pub fn new() -> Self {
169            Self {
170                additional_creators: Vec::new(),
171                federate: true,
172                predecessor: None,
173                room_type: None,
174            }
175        }
176
177        /// Given a `CreationContent` and the other fields that a homeserver has to fill, construct
178        /// a `RoomCreateEventContent`.
179        pub fn into_event_content(
180            self,
181            creator: OwnedUserId,
182            room_version: RoomVersionId,
183        ) -> RoomCreateEventContent {
184            assign!(RoomCreateEventContent::new_v1(creator), {
185                federate: self.federate,
186                room_version: room_version,
187                predecessor: self.predecessor,
188                room_type: self.room_type
189            })
190        }
191
192        /// Returns whether all fields have their default value.
193        pub fn is_empty(&self) -> bool {
194            self.federate && self.predecessor.is_none() && self.room_type.is_none()
195        }
196    }
197
198    impl Default for CreationContent {
199        fn default() -> Self {
200            Self::new()
201        }
202    }
203
204    /// A convenience parameter for setting a few default state events.
205    #[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
206    #[derive(Clone, StringEnum)]
207    #[ruma_enum(rename_all = "snake_case")]
208    #[non_exhaustive]
209    pub enum RoomPreset {
210        /// `join_rules` is set to `invite` and `history_visibility` is set to `shared`.
211        PrivateChat,
212
213        /// `join_rules` is set to `public` and `history_visibility` is set to `shared`.
214        PublicChat,
215
216        /// Same as `PrivateChat`, but all initial invitees get the same power level as the
217        /// creator.
218        TrustedPrivateChat,
219
220        #[doc(hidden)]
221        _Custom(PrivOwnedStr),
222    }
223}
224
225/// The power level values that can be overridden when creating a room.
226///
227/// This has the same fields as [`RoomPowerLevelsEventContent`], but most of them are `Option`s.
228/// Contrary to [`RoomPowerLevelsEventContent`] which doesn't serialize fields that are set to their
229/// default value defined in the Matrix specification, this type serializes all fields that are
230/// `Some(_)`, regardless of their value.
231///
232/// This type is used to allow clients to avoid server behavior observed in the wild that sets
233/// custom default values for fields that are not set in the `create_room` request, while a client
234/// wants the server to use the default value defined in the Matrix specification for that field.
235#[derive(Clone, Debug, Serialize, Default)]
236#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
237pub struct RoomPowerLevelsContentOverride {
238    /// The level required to ban a user.
239    #[serde(skip_serializing_if = "Option::is_none")]
240    pub ban: Option<Int>,
241
242    /// The level required to send specific event types.
243    ///
244    /// This is a mapping from event type to power level required.
245    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
246    pub events: BTreeMap<TimelineEventType, Int>,
247
248    /// The default level required to send message events.
249    #[serde(skip_serializing_if = "Option::is_none")]
250    pub events_default: Option<Int>,
251
252    /// The level required to invite a user.
253    #[serde(skip_serializing_if = "Option::is_none")]
254    pub invite: Option<Int>,
255
256    /// The level required to kick a user.
257    #[serde(skip_serializing_if = "Option::is_none")]
258    pub kick: Option<Int>,
259
260    /// The level required to redact an event.
261    #[serde(skip_serializing_if = "Option::is_none")]
262    pub redact: Option<Int>,
263
264    /// The default level required to send state events.
265    #[serde(skip_serializing_if = "Option::is_none")]
266    pub state_default: Option<Int>,
267
268    /// The power levels for specific users.
269    ///
270    /// This is a mapping from `user_id` to power level for that user.
271    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
272    pub users: BTreeMap<OwnedUserId, Int>,
273
274    /// The default power level for every user in the room.
275    #[serde(skip_serializing_if = "Option::is_none")]
276    pub users_default: Option<Int>,
277
278    /// The power level requirements for specific notification types.
279    ///
280    /// This is a mapping from `key` to power level for that notifications key.
281    #[serde(default, skip_serializing_if = "NotificationPowerLevels::is_default")]
282    pub notifications: NotificationPowerLevels,
283}
284
285impl RoomPowerLevelsContentOverride {
286    /// Creates a new, empty [`RoomPowerLevelsContentOverride`] instance.
287    pub fn new() -> Self {
288        Self::default()
289    }
290}
291
292impl From<RoomPowerLevelsEventContent> for RoomPowerLevelsContentOverride {
293    fn from(value: RoomPowerLevelsEventContent) -> Self {
294        let RoomPowerLevelsEventContent {
295            ban,
296            events,
297            events_default,
298            invite,
299            kick,
300            redact,
301            state_default,
302            users,
303            users_default,
304            notifications,
305            ..
306        } = value;
307
308        Self {
309            ban: Some(ban),
310            events,
311            events_default: Some(events_default),
312            invite: Some(invite),
313            kick: Some(kick),
314            redact: Some(redact),
315            state_default: Some(state_default),
316            users,
317            users_default: Some(users_default),
318            notifications,
319        }
320    }
321}
322
323impl JsonCastable<RoomPowerLevelsEventContent> for RoomPowerLevelsContentOverride {}
324
325impl JsonCastable<RoomPowerLevelsContentOverride> for RoomPowerLevelsEventContent {}
326
327impl JsonCastable<JsonObject> for RoomPowerLevelsContentOverride {}
328
329#[cfg(test)]
330mod tests {
331    use std::collections::BTreeMap;
332
333    use assign::assign;
334    use js_int::int;
335    use maplit::btreemap;
336    use ruma_common::{
337        canonical_json::assert_to_canonical_json_eq, power_levels::NotificationPowerLevels, user_id,
338    };
339    use serde_json::json;
340
341    use super::RoomPowerLevelsContentOverride;
342
343    #[test]
344    fn serialization_of_power_levels_overridden_values_with_optional_fields_as_none() {
345        let power_levels = RoomPowerLevelsContentOverride {
346            ban: None,
347            events: BTreeMap::new(),
348            events_default: None,
349            invite: None,
350            kick: None,
351            redact: None,
352            state_default: None,
353            users: BTreeMap::new(),
354            users_default: None,
355            notifications: NotificationPowerLevels::default(),
356        };
357
358        assert_to_canonical_json_eq!(power_levels, json!({}));
359    }
360
361    #[test]
362    fn serialization_of_power_levels_overridden_values_with_all_fields() {
363        let user = user_id!("@carl:example.com");
364        let power_levels_event = RoomPowerLevelsContentOverride {
365            ban: Some(int!(23)),
366            events: btreemap! {
367                "m.dummy".into() => int!(23)
368            },
369            events_default: Some(int!(23)),
370            invite: Some(int!(23)),
371            kick: Some(int!(23)),
372            redact: Some(int!(23)),
373            state_default: Some(int!(23)),
374            users: btreemap! {
375                user.to_owned() => int!(23)
376            },
377            users_default: Some(int!(23)),
378            notifications: assign!(NotificationPowerLevels::new(), { room: int!(23) }),
379        };
380
381        assert_to_canonical_json_eq!(
382            power_levels_event,
383            json!({
384                "ban": 23,
385                "events": {
386                    "m.dummy": 23
387                },
388                "events_default": 23,
389                "invite": 23,
390                "kick": 23,
391                "redact": 23,
392                "state_default": 23,
393                "users": {
394                    "@carl:example.com": 23
395                },
396                "users_default": 23,
397                "notifications": {
398                    "room": 23
399                },
400            })
401        );
402    }
403}