Skip to main content

palpo_core/events/room/
power_levels.rs

1//! Types for the [`m.room.power_levels`] event.
2//!
3//! [`m.room.power_levels`]: https://spec.matrix.org/latest/client-server-api/#mroompower_levels
4
5use std::{cmp::max, collections::BTreeMap};
6
7use palpo_macros::EventContent;
8use salvo::oapi::ToSchema;
9use serde::{Deserialize, Serialize};
10
11use crate::events::{
12    EmptyStateKey, EventContent, EventContentFromType, MessageLikeEventType, RedactContent, RedactedStateEventContent,
13    StateEventType, StaticEventContent, TimelineEventType,
14};
15use crate::power_levels::{NotificationPowerLevels, default_power_level};
16use crate::serde::RawJsonValue;
17use crate::{OwnedUserId, RoomVersionId, UserId, push::PushConditionPowerLevelsCtx};
18
19/// The content of an `m.room.power_levels` event.
20///
21/// Defines the power levels (privileges) of users in the room.
22#[derive(ToSchema, Deserialize, Serialize, Clone, Debug, EventContent)]
23#[palpo_event(type = "m.room.power_levels", kind = State, state_key_type = EmptyStateKey, custom_redacted)]
24pub struct RoomPowerLevelsEventContent {
25    /// The level required to ban a user.
26    #[serde(
27        default = "default_power_level",
28        // skip_serializing_if = "is_default_power_level",
29        deserialize_with = "palpo_core::serde::deserialize_v1_powerlevel"
30    )]
31    pub ban: i64,
32
33    /// The level required to send specific event types.
34    ///
35    /// This is a mapping from event type to power level required.
36    #[serde(
37        default,
38        // skip_serializing_if = "BTreeMap::is_empty",
39        deserialize_with = "palpo_core::serde::btreemap_deserialize_v1_powerlevel_values"
40    )]
41    pub events: BTreeMap<TimelineEventType, i64>,
42
43    /// The default level required to send message events.
44    #[serde(
45        default,
46        // skip_serializing_if = "palpo_core::serde::is_default",
47        deserialize_with = "palpo_core::serde::deserialize_v1_powerlevel"
48    )]
49    pub events_default: i64,
50
51    /// The level required to invite a user.
52    #[serde(
53        default,
54        // skip_serializing_if = "palpo_core::serde::is_default",
55        deserialize_with = "palpo_core::serde::deserialize_v1_powerlevel"
56    )]
57    pub invite: i64,
58
59    /// The level required to kick a user.
60    #[serde(
61        default = "default_power_level",
62        // skip_serializing_if = "is_default_power_level",
63        deserialize_with = "palpo_core::serde::deserialize_v1_powerlevel"
64    )]
65    pub kick: i64,
66
67    /// The level required to redact an event.
68    #[serde(
69        default = "default_power_level",
70        // skip_serializing_if = "is_default_power_level",
71        deserialize_with = "palpo_core::serde::deserialize_v1_powerlevel"
72    )]
73    pub redact: i64,
74
75    /// The default level required to send state events.
76    #[serde(
77        default = "default_power_level",
78        // skip_serializing_if = "is_default_power_level",
79        deserialize_with = "palpo_core::serde::deserialize_v1_powerlevel"
80    )]
81    pub state_default: i64,
82
83    /// The power levels for specific users.
84    ///
85    /// This is a mapping from `user_id` to power level for that user.
86    #[serde(
87        default,
88        // skip_serializing_if = "BTreeMap::is_empty",
89        deserialize_with = "palpo_core::serde::btreemap_deserialize_v1_powerlevel_values"
90    )]
91    pub users: BTreeMap<OwnedUserId, i64>,
92
93    /// The default power level for every user in the room.
94    #[serde(
95        default,
96        // skip_serializing_if = "palpo_core::serde::is_default",
97        deserialize_with = "palpo_core::serde::deserialize_v1_powerlevel"
98    )]
99    pub users_default: i64,
100
101    /// The power level requirements for specific notification types.
102    ///
103    /// This is a mapping from `key` to power level for that notifications key.
104    // #[serde(default, skip_serializing_if = "NotificationPowerLevels::is_default")]
105    #[serde(default)]
106    pub notifications: NotificationPowerLevels,
107}
108
109impl RoomPowerLevelsEventContent {
110    /// Creates a new `RoomPowerLevelsEventContent` with all-default values.
111    pub fn new() -> Self {
112        // events_default, users_default and invite having a default of 0 while the others have a
113        // default of 50 is not an oversight, these defaults are from the Matrix specification.
114        Self {
115            ban: default_power_level(),
116            events: BTreeMap::new(),
117            events_default: 0,
118            invite: 0,
119            kick: default_power_level(),
120            redact: default_power_level(),
121            state_default: default_power_level(),
122            users: BTreeMap::new(),
123            users_default: 0,
124            notifications: NotificationPowerLevels::default(),
125        }
126    }
127}
128
129impl Default for RoomPowerLevelsEventContent {
130    fn default() -> Self {
131        Self::new()
132    }
133}
134
135impl RedactContent for RoomPowerLevelsEventContent {
136    type Redacted = RedactedRoomPowerLevelsEventContent;
137
138    fn redact(self, version: &RoomVersionId) -> Self::Redacted {
139        let Self {
140            ban,
141            events,
142            events_default,
143            invite,
144            kick,
145            redact,
146            state_default,
147            users,
148            users_default,
149            ..
150        } = self;
151
152        let invite = match version {
153            RoomVersionId::V1
154            | RoomVersionId::V2
155            | RoomVersionId::V3
156            | RoomVersionId::V4
157            | RoomVersionId::V5
158            | RoomVersionId::V6
159            | RoomVersionId::V7
160            | RoomVersionId::V8
161            | RoomVersionId::V9
162            | RoomVersionId::V10 => 0,
163            _ => invite,
164        };
165
166        RedactedRoomPowerLevelsEventContent {
167            ban,
168            events,
169            events_default,
170            invite,
171            kick,
172            redact,
173            state_default,
174            users,
175            users_default,
176        }
177    }
178}
179
180/// Used with `#[serde(skip_serializing_if)]` to omit default power levels.
181#[allow(clippy::trivially_copy_pass_by_ref)]
182fn is_default_power_level(l: &i64) -> bool {
183    *l == 50
184}
185
186impl RoomPowerLevelsEvent {
187    /// Obtain the effective power levels, regardless of whether this event is redacted.
188    pub fn power_levels(&self) -> RoomPowerLevels {
189        match self {
190            Self::Original(ev) => ev.content.clone().into(),
191            Self::Redacted(ev) => ev.content.clone().into(),
192        }
193    }
194}
195
196impl SyncRoomPowerLevelsEvent {
197    /// Obtain the effective power levels, regardless of whether this event is redacted.
198    pub fn power_levels(&self) -> RoomPowerLevels {
199        match self {
200            Self::Original(ev) => ev.content.clone().into(),
201            Self::Redacted(ev) => ev.content.clone().into(),
202        }
203    }
204}
205
206impl StrippedRoomPowerLevelsEvent {
207    /// Obtain the effective power levels from this event.
208    pub fn power_levels(&self) -> RoomPowerLevels {
209        self.content.clone().into()
210    }
211}
212
213/// Redacted form of [`RoomPowerLevelsEventContent`].
214#[derive(ToSchema, Deserialize, Serialize, Clone, Debug)]
215pub struct RedactedRoomPowerLevelsEventContent {
216    /// The level required to ban a user.
217    #[serde(
218        default = "default_power_level",
219        skip_serializing_if = "is_default_power_level",
220        deserialize_with = "palpo_core::serde::deserialize_v1_powerlevel"
221    )]
222    pub ban: i64,
223
224    /// The level required to send specific event types.
225    ///
226    /// This is a mapping from event type to power level required.
227    #[serde(
228        default,
229        skip_serializing_if = "BTreeMap::is_empty",
230        deserialize_with = "palpo_core::serde::btreemap_deserialize_v1_powerlevel_values"
231    )]
232    pub events: BTreeMap<TimelineEventType, i64>,
233
234    /// The default level required to send message events.
235    #[serde(
236        default,
237        skip_serializing_if = "palpo_core::serde::is_default",
238        deserialize_with = "palpo_core::serde::deserialize_v1_powerlevel"
239    )]
240    pub events_default: i64,
241
242    /// The level required to invite a user.
243    ///
244    /// This field was redacted in room versions 1 through 10. Starting from room version 11 it is
245    /// preserved.
246    #[serde(
247        default,
248        skip_serializing_if = "palpo_core::serde::is_default",
249        deserialize_with = "palpo_core::serde::deserialize_v1_powerlevel"
250    )]
251    pub invite: i64,
252
253    /// The level required to kick a user.
254    #[serde(
255        default = "default_power_level",
256        skip_serializing_if = "is_default_power_level",
257        deserialize_with = "palpo_core::serde::deserialize_v1_powerlevel"
258    )]
259    pub kick: i64,
260
261    /// The level required to redact an event.
262    #[serde(
263        default = "default_power_level",
264        skip_serializing_if = "is_default_power_level",
265        deserialize_with = "palpo_core::serde::deserialize_v1_powerlevel"
266    )]
267    pub redact: i64,
268
269    /// The default level required to send state events.
270    #[serde(
271        default = "default_power_level",
272        skip_serializing_if = "is_default_power_level",
273        deserialize_with = "palpo_core::serde::deserialize_v1_powerlevel"
274    )]
275    pub state_default: i64,
276
277    /// The power levels for specific users.
278    ///
279    /// This is a mapping from `user_id` to power level for that user.
280    #[serde(
281        default,
282        skip_serializing_if = "BTreeMap::is_empty",
283        deserialize_with = "palpo_core::serde::btreemap_deserialize_v1_powerlevel_values"
284    )]
285    pub users: BTreeMap<OwnedUserId, i64>,
286
287    /// The default power level for every user in the room.
288    #[serde(
289        default,
290        skip_serializing_if = "palpo_core::serde::is_default",
291        deserialize_with = "palpo_core::serde::deserialize_v1_powerlevel"
292    )]
293    pub users_default: i64,
294}
295
296impl EventContent for RedactedRoomPowerLevelsEventContent {
297    type EventType = StateEventType;
298
299    fn event_type(&self) -> Self::EventType {
300        StateEventType::RoomPowerLevels
301    }
302}
303
304impl StaticEventContent for RedactedRoomPowerLevelsEventContent {
305    const TYPE: &'static str = "m.room.power_levels";
306}
307
308impl RedactedStateEventContent for RedactedRoomPowerLevelsEventContent {
309    type StateKey = EmptyStateKey;
310}
311
312impl EventContentFromType for RedactedRoomPowerLevelsEventContent {
313    fn from_parts(_ev_type: &str, content: &RawJsonValue) -> serde_json::Result<Self> {
314        serde_json::from_str(content.get())
315    }
316}
317
318/// The effective power levels of a room.
319///
320/// This struct contains the same fields as [`RoomPowerLevelsEventContent`] and be created from that
321/// using a `From` trait implementation, but it is also implements
322/// `From<`[`RedactedRoomPowerLevelsEventContent`]`>`, so can be used when wanting to inspect the
323/// power levels of a room, regardless of whether the most recent power-levels event is redacted or
324/// not.
325#[derive(Clone, Debug)]
326pub struct RoomPowerLevels {
327    /// The level required to ban a user.
328    pub ban: i64,
329
330    /// The level required to send specific event types.
331    ///
332    /// This is a mapping from event type to power level required.
333    pub events: BTreeMap<TimelineEventType, i64>,
334
335    /// The default level required to send message events.
336    pub events_default: i64,
337
338    /// The level required to invite a user.
339    pub invite: i64,
340
341    /// The level required to kick a user.
342    pub kick: i64,
343
344    /// The level required to redact an event.
345    pub redact: i64,
346
347    /// The default level required to send state events.
348    pub state_default: i64,
349
350    /// The power levels for specific users.
351    ///
352    /// This is a mapping from `user_id` to power level for that user.
353    pub users: BTreeMap<OwnedUserId, i64>,
354
355    /// The default power level for every user in the room.
356    pub users_default: i64,
357
358    /// The power level requirements for specific notification types.
359    ///
360    /// This is a mapping from `key` to power level for that notifications key.
361    pub notifications: NotificationPowerLevels,
362}
363
364impl RoomPowerLevels {
365    /// Get the power level of a specific user.
366    pub fn for_user(&self, user_id: &UserId) -> i64 {
367        self.users.get(user_id).map_or(self.users_default, |pl| *pl)
368    }
369
370    /// Whether the given user can ban other users based on the power levels.
371    ///
372    /// Shorthand for `power_levels.user_can_do(user_id, PowerLevelAction::Ban)`.
373    pub fn user_can_ban(&self, user_id: &UserId) -> bool {
374        self.for_user(user_id) >= self.ban
375    }
376
377    /// Whether the acting user can ban the target user based on the power levels.
378    ///
379    /// On top of `power_levels.user_can_ban(acting_user_id)`, this performs an extra check
380    /// to make sure the acting user has at greater power level than the target user.
381    ///
382    /// Shorthand for `power_levels.user_can_do_to_user(acting_user_id, target_user_id,
383    /// PowerLevelUserAction::Ban)`.
384    pub fn user_can_ban_user(&self, acting_user_id: &UserId, target_user_id: &UserId) -> bool {
385        let acting_pl = self.for_user(acting_user_id);
386        let target_pl = self.for_user(target_user_id);
387        acting_pl >= self.ban && target_pl < acting_pl
388    }
389
390    /// Whether the given user can unban other users based on the power levels.
391    ///
392    /// This action requires to be allowed to ban and to kick.
393    ///
394    /// Shorthand for `power_levels.user_can_do(user_id, PowerLevelAction::Unban)`.
395    pub fn user_can_unban(&self, user_id: &UserId) -> bool {
396        let pl = self.for_user(user_id);
397        pl >= self.ban && pl >= self.kick
398    }
399
400    /// Whether the acting user can unban the target user based on the power levels.
401    ///
402    /// This action requires to be allowed to ban and to kick.
403    ///
404    /// On top of `power_levels.user_can_unban(acting_user_id)`, this performs an extra check
405    /// to make sure the acting user has at greater power level than the target user.
406    ///
407    /// Shorthand for `power_levels.user_can_do_to_user(acting_user_id, target_user_id,
408    /// PowerLevelUserAction::Unban)`.
409    pub fn user_can_unban_user(&self, acting_user_id: &UserId, target_user_id: &UserId) -> bool {
410        let acting_pl = self.for_user(acting_user_id);
411        let target_pl = self.for_user(target_user_id);
412        acting_pl >= self.ban && acting_pl >= self.kick && target_pl < acting_pl
413    }
414
415    /// Whether the given user can invite other users based on the power levels.
416    ///
417    /// Shorthand for `power_levels.user_can_do(user_id, PowerLevelAction::Invite)`.
418    pub fn user_can_invite(&self, user_id: &UserId) -> bool {
419        self.for_user(user_id) >= self.invite
420    }
421
422    /// Whether the given user can kick other users based on the power levels.
423    ///
424    /// Shorthand for `power_levels.user_can_do(user_id, PowerLevelAction::Kick)`.
425    pub fn user_can_kick(&self, user_id: &UserId) -> bool {
426        self.for_user(user_id) >= self.kick
427    }
428
429    /// Whether the acting user can kick the target user based on the power levels.
430    ///
431    /// On top of `power_levels.user_can_kick(acting_user_id)`, this performs an extra check
432    /// to make sure the acting user has at least the same power level as the target user.
433    ///
434    /// Shorthand for `power_levels.user_can_do_to_user(acting_user_id, target_user_id,
435    /// PowerLevelUserAction::Kick)`.
436    pub fn user_can_kick_user(&self, acting_user_id: &UserId, target_user_id: &UserId) -> bool {
437        let acting_pl = self.for_user(acting_user_id);
438        let target_pl = self.for_user(target_user_id);
439        acting_pl >= self.kick && target_pl < acting_pl
440    }
441
442    /// Whether the given user can redact events based on the power levels.
443    ///
444    /// Shorthand for `power_levels.user_can_do(user_id, PowerLevelAction::Redact)`.
445    pub fn user_can_redact(&self, user_id: &UserId) -> bool {
446        self.for_user(user_id) >= self.redact
447    }
448
449    /// Whether the given user can send message events based on the power levels.
450    ///
451    /// Shorthand for `power_levels.user_can_do(user_id, PowerLevelAction::SendMessage(msg_type))`.
452    pub fn user_can_send_message(&self, user_id: &UserId, msg_type: MessageLikeEventType) -> bool {
453        self.for_user(user_id)
454            >= self
455                .events
456                .get(&msg_type.into())
457                .map(ToOwned::to_owned)
458                .unwrap_or(self.events_default)
459    }
460
461    /// Whether the given user can send state events based on the power levels.
462    ///
463    /// Shorthand for `power_levels.user_can_do(user_id, PowerLevelAction::SendState(state_type))`.
464    pub fn user_can_send_state(&self, user_id: &UserId, state_type: StateEventType) -> bool {
465        self.for_user(user_id)
466            >= self
467                .events
468                .get(&state_type.into())
469                .map(ToOwned::to_owned)
470                .unwrap_or(self.state_default)
471    }
472
473    /// Whether the given user can notify everybody in the room by writing `@room` in a message.
474    ///
475    /// Shorthand for `power_levels.user_can_do(user_id,
476    /// PowerLevelAction::TriggerNotification(NotificationPowerLevelType::Room))`.
477    pub fn user_can_trigger_room_notification(&self, user_id: &UserId) -> bool {
478        self.for_user(user_id) >= self.notifications.room
479    }
480
481    /// Whether the acting user can change the power level of the target user.
482    ///
483    /// Shorthand for `power_levels.user_can_do_to_user(acting_user_id, target_user_id,
484    /// PowerLevelUserAction::ChangePowerLevel`.
485    pub fn user_can_change_user_power_level(&self, acting_user_id: &UserId, target_user_id: &UserId) -> bool {
486        // Check that the user can change the power levels first.
487        if !self.user_can_send_state(acting_user_id, StateEventType::RoomPowerLevels) {
488            return false;
489        }
490
491        // A user can change their own power level.
492        if acting_user_id == target_user_id {
493            return true;
494        }
495
496        // The permission is different whether the target user is added or changed/removed, so
497        // we need to check that.
498        if let Some(target_pl) = self.users.get(target_user_id).copied() {
499            self.for_user(acting_user_id) > target_pl
500        } else {
501            true
502        }
503    }
504
505    /// Whether the given user can do the given action based on the power levels.
506    pub fn user_can_do(&self, user_id: &UserId, action: PowerLevelAction) -> bool {
507        match action {
508            PowerLevelAction::Ban => self.user_can_ban(user_id),
509            PowerLevelAction::Unban => self.user_can_unban(user_id),
510            PowerLevelAction::Invite => self.user_can_invite(user_id),
511            PowerLevelAction::Kick => self.user_can_kick(user_id),
512            PowerLevelAction::Redact => self.user_can_redact(user_id),
513            PowerLevelAction::SendMessage(message_type) => self.user_can_send_message(user_id, message_type),
514            PowerLevelAction::SendState(state_type) => self.user_can_send_state(user_id, state_type),
515            PowerLevelAction::TriggerNotification(NotificationPowerLevelType::Room) => {
516                self.user_can_trigger_room_notification(user_id)
517            }
518        }
519    }
520
521    /// Whether the acting user can do the given action to the target user based on the power
522    /// levels.
523    pub fn user_can_do_to_user(
524        &self,
525        acting_user_id: &UserId,
526        target_user_id: &UserId,
527        action: PowerLevelUserAction,
528    ) -> bool {
529        match action {
530            PowerLevelUserAction::Ban => self.user_can_ban_user(acting_user_id, target_user_id),
531            PowerLevelUserAction::Unban => self.user_can_unban_user(acting_user_id, target_user_id),
532            PowerLevelUserAction::Invite => self.user_can_invite(acting_user_id),
533            PowerLevelUserAction::Kick => self.user_can_kick_user(acting_user_id, target_user_id),
534            PowerLevelUserAction::ChangePowerLevel => {
535                self.user_can_change_user_power_level(acting_user_id, target_user_id)
536            }
537        }
538    }
539
540    /// Get the maximum power level of any user.
541    pub fn max(&self) -> i64 {
542        self.users
543            .values()
544            .fold(self.users_default, |max_pl, user_pl| max(max_pl, *user_pl))
545    }
546}
547
548impl From<RoomPowerLevelsEventContent> for RoomPowerLevels {
549    fn from(c: RoomPowerLevelsEventContent) -> Self {
550        Self {
551            ban: c.ban,
552            events: c.events,
553            events_default: c.events_default,
554            invite: c.invite,
555            kick: c.kick,
556            redact: c.redact,
557            state_default: c.state_default,
558            users: c.users,
559            users_default: c.users_default,
560            notifications: c.notifications,
561        }
562    }
563}
564
565impl From<RedactedRoomPowerLevelsEventContent> for RoomPowerLevels {
566    fn from(c: RedactedRoomPowerLevelsEventContent) -> Self {
567        Self {
568            ban: c.ban,
569            events: c.events,
570            events_default: c.events_default,
571            invite: c.invite,
572            kick: c.kick,
573            redact: c.redact,
574            state_default: c.state_default,
575            users: c.users,
576            users_default: c.users_default,
577            notifications: NotificationPowerLevels::default(),
578        }
579    }
580}
581
582impl From<RoomPowerLevels> for RoomPowerLevelsEventContent {
583    fn from(c: RoomPowerLevels) -> Self {
584        Self {
585            ban: c.ban,
586            events: c.events,
587            events_default: c.events_default,
588            invite: c.invite,
589            kick: c.kick,
590            redact: c.redact,
591            state_default: c.state_default,
592            users: c.users,
593            users_default: c.users_default,
594            notifications: c.notifications,
595        }
596    }
597}
598
599impl From<RoomPowerLevels> for PushConditionPowerLevelsCtx {
600    fn from(c: RoomPowerLevels) -> Self {
601        Self {
602            users: c.users,
603            users_default: c.users_default,
604            notifications: c.notifications,
605        }
606    }
607}
608
609/// The actions that can be limited by power levels.
610#[derive(Clone, Debug, PartialEq, Eq)]
611#[non_exhaustive]
612pub enum PowerLevelAction {
613    /// Ban a user.
614    Ban,
615
616    /// Unban a user.
617    Unban,
618
619    /// Invite a user.
620    Invite,
621
622    /// Kick a user.
623    Kick,
624
625    /// Redact an event.
626    Redact,
627
628    /// Send a message-like event.
629    SendMessage(MessageLikeEventType),
630
631    /// Send a state event.
632    SendState(StateEventType),
633
634    /// Trigger a notification.
635    TriggerNotification(NotificationPowerLevelType),
636}
637
638/// The notification types that can be limited by power levels.
639#[derive(Clone, Debug, PartialEq, Eq)]
640#[non_exhaustive]
641pub enum NotificationPowerLevelType {
642    /// `@room` notifications.
643    Room,
644}
645
646/// The actions to other users that can be limited by power levels.
647#[derive(Clone, Debug, PartialEq, Eq)]
648#[non_exhaustive]
649pub enum PowerLevelUserAction {
650    /// Ban a user.
651    Ban,
652
653    /// Unban a user.
654    Unban,
655
656    /// Invite a user.
657    Invite,
658
659    /// Kick a user.
660    Kick,
661
662    /// Change a user's power level.
663    ChangePowerLevel,
664}
665
666// #[cfg(test)]
667// mod tests {
668//     use std::collections::BTreeMap;
669
670//     use crate::user_id;
671//     use assign::assign;
672//     use maplit::btreemap;
673//     use serde_json::{json, to_value as to_json_value};
674
675//     use super::{default_power_level, NotificationPowerLevels, RoomPowerLevelsEventContent};
676
677//     #[test]
678//     fn serialization_with_optional_fields_as_none() {
679//         let default = default_power_level();
680
681//         let power_levels = RoomPowerLevelsEventContent {
682//             ban: default,
683//             events: BTreeMap::new(),
684//             events_default: 0,
685//             invite: 0,
686//             kick: default,
687//             redact: default,
688//             state_default: default,
689//             users: BTreeMap::new(),
690//             users_default: 0,
691//             notifications: NotificationPowerLevels::default(),
692//         };
693
694//         let actual = to_json_value(&power_levels).unwrap();
695//         let expected = json!({});
696
697//         assert_eq!(actual, expected);
698//     }
699
700//     #[test]
701//     fn serialization_with_all_fields() {
702//         let user = user_id!("@carl:example.com");
703//         let power_levels_event = RoomPowerLevelsEventContent {
704//             ban: 23,
705//             events: btreemap! {
706//                 "m.dummy".into() => 23
707//             },
708//             events_default: 23,
709//             invite: 23,
710//             kick: 23,
711//             redact: 23,
712//             state_default: 23,
713//             users: btreemap! {
714//                 user.to_owned() => 23
715//             },
716//             users_default: 23,
717//             notifications: assign!(NotificationPowerLevels::new(), { room: 23 }),
718//         };
719
720//         let actual = to_json_value(&power_levels_event).unwrap();
721//         let expected = json!({
722//             "ban": 23,
723//             "events": {
724//                 "m.dummy": 23
725//             },
726//             "events_default": 23,
727//             "invite": 23,
728//             "kick": 23,
729//             "redact": 23,
730//             "state_default": 23,
731//             "users": {
732//                 "@carl:example.com": 23
733//             },
734//             "users_default": 23,
735//             "notifications": {
736//                 "room": 23
737//             },
738//         });
739
740//         assert_eq!(actual, expected);
741//     }
742// }