Skip to main content

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/v1.19/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]
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]
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        #[serde(skip_serializing_if = "Option::is_none", rename = "type")]
161        pub room_type: Option<RoomType>,
162    }
163
164    impl CreationContent {
165        /// Creates a new `CreationContent` with all fields defaulted.
166        pub fn new() -> Self {
167            Self {
168                additional_creators: Vec::new(),
169                federate: true,
170                predecessor: None,
171                room_type: None,
172            }
173        }
174
175        /// Given a `CreationContent` and the other fields that a homeserver has to fill, construct
176        /// a `RoomCreateEventContent`.
177        pub fn into_event_content(
178            self,
179            creator: OwnedUserId,
180            room_version: RoomVersionId,
181        ) -> RoomCreateEventContent {
182            assign!(RoomCreateEventContent::new_v1(creator), {
183                federate: self.federate,
184                room_version: room_version,
185                predecessor: self.predecessor,
186                room_type: self.room_type
187            })
188        }
189
190        /// Returns whether all fields have their default value.
191        pub fn is_empty(&self) -> bool {
192            let Self { additional_creators, federate, predecessor, room_type } = self;
193            additional_creators.is_empty()
194                && *federate
195                && predecessor.is_none()
196                && room_type.is_none()
197        }
198    }
199
200    impl Default for CreationContent {
201        fn default() -> Self {
202            Self::new()
203        }
204    }
205
206    /// A convenience parameter for setting a few default state events.
207    #[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
208    #[derive(Clone, StringEnum)]
209    #[ruma_enum(rename_all = "snake_case")]
210    #[non_exhaustive]
211    pub enum RoomPreset {
212        /// `join_rules` is set to `invite` and `history_visibility` is set to `shared`.
213        PrivateChat,
214
215        /// `join_rules` is set to `public` and `history_visibility` is set to `shared`.
216        PublicChat,
217
218        /// Same as `PrivateChat`, but all initial invitees get the same power level as the
219        /// creator.
220        TrustedPrivateChat,
221
222        #[doc(hidden)]
223        _Custom(PrivOwnedStr),
224    }
225}
226
227/// The power level values that can be overridden when creating a room.
228///
229/// This has the same fields as [`RoomPowerLevelsEventContent`], but most of them are `Option`s.
230/// Contrary to [`RoomPowerLevelsEventContent`] which doesn't serialize fields that are set to their
231/// default value defined in the Matrix specification, this type serializes all fields that are
232/// `Some(_)`, regardless of their value.
233///
234/// This type is used to allow clients to avoid server behavior observed in the wild that sets
235/// custom default values for fields that are not set in the `create_room` request, while a client
236/// wants the server to use the default value defined in the Matrix specification for that field.
237#[derive(Clone, Debug, Serialize, Default)]
238#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
239pub struct RoomPowerLevelsContentOverride {
240    /// The level required to ban a user.
241    #[serde(skip_serializing_if = "Option::is_none")]
242    pub ban: Option<Int>,
243
244    /// The level required to send specific event types.
245    ///
246    /// This is a mapping from event type to power level required.
247    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
248    pub events: BTreeMap<TimelineEventType, Int>,
249
250    /// The default level required to send message events.
251    #[serde(skip_serializing_if = "Option::is_none")]
252    pub events_default: Option<Int>,
253
254    /// The level required to invite a user.
255    #[serde(skip_serializing_if = "Option::is_none")]
256    pub invite: Option<Int>,
257
258    /// The level required to kick a user.
259    #[serde(skip_serializing_if = "Option::is_none")]
260    pub kick: Option<Int>,
261
262    /// The level required to redact an event.
263    #[serde(skip_serializing_if = "Option::is_none")]
264    pub redact: Option<Int>,
265
266    /// The default level required to send state events.
267    #[serde(skip_serializing_if = "Option::is_none")]
268    pub state_default: Option<Int>,
269
270    /// The power levels for specific users.
271    ///
272    /// This is a mapping from `user_id` to power level for that user.
273    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
274    pub users: BTreeMap<OwnedUserId, Int>,
275
276    /// The default power level for every user in the room.
277    #[serde(skip_serializing_if = "Option::is_none")]
278    pub users_default: Option<Int>,
279
280    /// The power level requirements for specific notification types.
281    ///
282    /// This is a mapping from `key` to power level for that notifications key.
283    #[serde(default, skip_serializing_if = "NotificationPowerLevels::is_default")]
284    pub notifications: NotificationPowerLevels,
285}
286
287impl RoomPowerLevelsContentOverride {
288    /// Creates a new, empty [`RoomPowerLevelsContentOverride`] instance.
289    pub fn new() -> Self {
290        Self::default()
291    }
292}
293
294impl From<RoomPowerLevelsEventContent> for RoomPowerLevelsContentOverride {
295    fn from(value: RoomPowerLevelsEventContent) -> Self {
296        let RoomPowerLevelsEventContent {
297            ban,
298            events,
299            events_default,
300            invite,
301            kick,
302            redact,
303            state_default,
304            users,
305            users_default,
306            notifications,
307            ..
308        } = value;
309
310        Self {
311            ban: Some(ban),
312            events,
313            events_default: Some(events_default),
314            invite: Some(invite),
315            kick: Some(kick),
316            redact: Some(redact),
317            state_default: Some(state_default),
318            users,
319            users_default: Some(users_default),
320            notifications,
321        }
322    }
323}
324
325impl JsonCastable<RoomPowerLevelsEventContent> for RoomPowerLevelsContentOverride {}
326
327impl JsonCastable<RoomPowerLevelsContentOverride> for RoomPowerLevelsEventContent {}
328
329impl JsonCastable<JsonObject> for RoomPowerLevelsContentOverride {}
330
331#[cfg(test)]
332mod tests {
333    use std::collections::BTreeMap;
334
335    use assign::assign;
336    use js_int::int;
337    use maplit::btreemap;
338    use ruma_common::{
339        canonical_json::assert_to_canonical_json_eq, owned_user_id,
340        power_levels::NotificationPowerLevels,
341    };
342    use serde_json::json;
343
344    use super::RoomPowerLevelsContentOverride;
345
346    #[test]
347    fn serialization_of_power_levels_overridden_values_with_optional_fields_as_none() {
348        let power_levels = RoomPowerLevelsContentOverride {
349            ban: None,
350            events: BTreeMap::new(),
351            events_default: None,
352            invite: None,
353            kick: None,
354            redact: None,
355            state_default: None,
356            users: BTreeMap::new(),
357            users_default: None,
358            notifications: NotificationPowerLevels::default(),
359        };
360
361        assert_to_canonical_json_eq!(power_levels, json!({}));
362    }
363
364    #[test]
365    fn serialization_of_power_levels_overridden_values_with_all_fields() {
366        let user = owned_user_id!("@carl:example.com");
367        let power_levels_event = RoomPowerLevelsContentOverride {
368            ban: Some(int!(23)),
369            events: btreemap! {
370                "m.dummy".into() => int!(23)
371            },
372            events_default: Some(int!(23)),
373            invite: Some(int!(23)),
374            kick: Some(int!(23)),
375            redact: Some(int!(23)),
376            state_default: Some(int!(23)),
377            users: btreemap! {
378                user => int!(23)
379            },
380            users_default: Some(int!(23)),
381            notifications: assign!(NotificationPowerLevels::new(), { room: int!(23) }),
382        };
383
384        assert_to_canonical_json_eq!(
385            power_levels_event,
386            json!({
387                "ban": 23,
388                "events": {
389                    "m.dummy": 23
390                },
391                "events_default": 23,
392                "invite": 23,
393                "kick": 23,
394                "redact": 23,
395                "state_default": 23,
396                "users": {
397                    "@carl:example.com": 23
398                },
399                "users_default": 23,
400                "notifications": {
401                    "room": 23
402                },
403            })
404        );
405    }
406}