// Copyright (c) 2020 Tulir Asokan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package event import ( "sync" "go.mau.fi/util/ptr" "golang.org/x/exp/maps" "maunium.net/go/mautrix/id" ) // PowerLevelsEventContent represents the content of a m.room.power_levels state event content. // https://spec.matrix.org/v1.5/client-server-api/#mroompower_levels type PowerLevelsEventContent struct { usersLock sync.RWMutex Users map[id.UserID]int `json:"users,omitempty"` UsersDefault int `json:"users_default,omitempty"` eventsLock sync.RWMutex Events map[string]int `json:"events,omitempty"` EventsDefault int `json:"events_default,omitempty"` Notifications *NotificationPowerLevels `json:"notifications,omitempty"` StateDefaultPtr *int `json:"state_default,omitempty"` InvitePtr *int `json:"invite,omitempty"` KickPtr *int `json:"kick,omitempty"` BanPtr *int `json:"ban,omitempty"` RedactPtr *int `json:"redact,omitempty"` } func (pl *PowerLevelsEventContent) Clone() *PowerLevelsEventContent { if pl == nil { return nil } return &PowerLevelsEventContent{ Users: maps.Clone(pl.Users), UsersDefault: pl.UsersDefault, Events: maps.Clone(pl.Events), EventsDefault: pl.EventsDefault, StateDefaultPtr: ptr.Clone(pl.StateDefaultPtr), Notifications: pl.Notifications.Clone(), InvitePtr: ptr.Clone(pl.InvitePtr), KickPtr: ptr.Clone(pl.KickPtr), BanPtr: ptr.Clone(pl.BanPtr), RedactPtr: ptr.Clone(pl.RedactPtr), } } type NotificationPowerLevels struct { RoomPtr *int `json:"room,omitempty"` } func (npl *NotificationPowerLevels) Clone() *NotificationPowerLevels { if npl == nil { return nil } return &NotificationPowerLevels{ RoomPtr: ptr.Clone(npl.RoomPtr), } } func (npl *NotificationPowerLevels) Room() int { if npl != nil && npl.RoomPtr != nil { return *npl.RoomPtr } return 50 } func (pl *PowerLevelsEventContent) Invite() int { if pl.InvitePtr != nil { return *pl.InvitePtr } return 0 } func (pl *PowerLevelsEventContent) Kick() int { if pl.KickPtr != nil { return *pl.KickPtr } return 50 } func (pl *PowerLevelsEventContent) Ban() int { if pl.BanPtr != nil { return *pl.BanPtr } return 50 } func (pl *PowerLevelsEventContent) Redact() int { if pl.RedactPtr != nil { return *pl.RedactPtr } return 50 } func (pl *PowerLevelsEventContent) StateDefault() int { if pl.StateDefaultPtr != nil { return *pl.StateDefaultPtr } return 50 } func (pl *PowerLevelsEventContent) GetUserLevel(userID id.UserID) int { pl.usersLock.RLock() defer pl.usersLock.RUnlock() level, ok := pl.Users[userID] if !ok { return pl.UsersDefault } return level } func (pl *PowerLevelsEventContent) SetUserLevel(userID id.UserID, level int) { pl.usersLock.Lock() defer pl.usersLock.Unlock() if level == pl.UsersDefault { delete(pl.Users, userID) } else { if pl.Users == nil { pl.Users = make(map[id.UserID]int) } pl.Users[userID] = level } } func (pl *PowerLevelsEventContent) EnsureUserLevel(target id.UserID, level int) bool { return pl.EnsureUserLevelAs("", target, level) } func (pl *PowerLevelsEventContent) EnsureUserLevelAs(actor, target id.UserID, level int) bool { existingLevel := pl.GetUserLevel(target) if actor != "" { actorLevel := pl.GetUserLevel(actor) if actorLevel <= existingLevel || actorLevel < level { return false } } if existingLevel != level { pl.SetUserLevel(target, level) return true } return false } func (pl *PowerLevelsEventContent) GetEventLevel(eventType Type) int { pl.eventsLock.RLock() defer pl.eventsLock.RUnlock() level, ok := pl.Events[eventType.String()] if !ok { if eventType.IsState() { return pl.StateDefault() } return pl.EventsDefault } return level } func (pl *PowerLevelsEventContent) SetEventLevel(eventType Type, level int) { pl.eventsLock.Lock() defer pl.eventsLock.Unlock() if (eventType.IsState() && level == pl.StateDefault()) || (!eventType.IsState() && level == pl.EventsDefault) { delete(pl.Events, eventType.String()) } else { if pl.Events == nil { pl.Events = make(map[string]int) } pl.Events[eventType.String()] = level } } func (pl *PowerLevelsEventContent) EnsureEventLevel(eventType Type, level int) bool { return pl.EnsureEventLevelAs("", eventType, level) } func (pl *PowerLevelsEventContent) EnsureEventLevelAs(actor id.UserID, eventType Type, level int) bool { existingLevel := pl.GetEventLevel(eventType) if actor != "" { actorLevel := pl.GetUserLevel(actor) if existingLevel > actorLevel || level > actorLevel { return false } } if existingLevel != level { pl.SetEventLevel(eventType, level) return true } return false }