matrix-pretix/vendor/maunium.net/go/mautrix/appservice/statestore.go

237 lines
7.4 KiB
Go

// 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 appservice
import (
"sync"
"time"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
type StateStore interface {
IsRegistered(userID id.UserID) bool
MarkRegistered(userID id.UserID)
IsTyping(roomID id.RoomID, userID id.UserID) bool
SetTyping(roomID id.RoomID, userID id.UserID, timeout int64)
IsInRoom(roomID id.RoomID, userID id.UserID) bool
IsInvited(roomID id.RoomID, userID id.UserID) bool
IsMembership(roomID id.RoomID, userID id.UserID, allowedMemberships ...event.Membership) bool
GetMember(roomID id.RoomID, userID id.UserID) *event.MemberEventContent
TryGetMember(roomID id.RoomID, userID id.UserID) (*event.MemberEventContent, bool)
SetMembership(roomID id.RoomID, userID id.UserID, membership event.Membership)
SetMember(roomID id.RoomID, userID id.UserID, member *event.MemberEventContent)
SetPowerLevels(roomID id.RoomID, levels *event.PowerLevelsEventContent)
GetPowerLevels(roomID id.RoomID) *event.PowerLevelsEventContent
GetPowerLevel(roomID id.RoomID, userID id.UserID) int
GetPowerLevelRequirement(roomID id.RoomID, eventType event.Type) int
HasPowerLevel(roomID id.RoomID, userID id.UserID, eventType event.Type) bool
}
func (as *AppService) UpdateState(evt *event.Event) {
switch content := evt.Content.Parsed.(type) {
case *event.MemberEventContent:
as.StateStore.SetMember(evt.RoomID, id.UserID(evt.GetStateKey()), content)
case *event.PowerLevelsEventContent:
as.StateStore.SetPowerLevels(evt.RoomID, content)
}
}
type TypingStateStore struct {
typing map[id.RoomID]map[id.UserID]int64
typingLock sync.RWMutex
}
func NewTypingStateStore() *TypingStateStore {
return &TypingStateStore{
typing: make(map[id.RoomID]map[id.UserID]int64),
}
}
func (store *TypingStateStore) IsTyping(roomID id.RoomID, userID id.UserID) bool {
store.typingLock.RLock()
defer store.typingLock.RUnlock()
roomTyping, ok := store.typing[roomID]
if !ok {
return false
}
typingEndsAt := roomTyping[userID]
return typingEndsAt >= time.Now().Unix()
}
func (store *TypingStateStore) SetTyping(roomID id.RoomID, userID id.UserID, timeout int64) {
store.typingLock.Lock()
defer store.typingLock.Unlock()
roomTyping, ok := store.typing[roomID]
if !ok {
if timeout >= 0 {
roomTyping = map[id.UserID]int64{
userID: time.Now().Unix() + timeout,
}
} else {
return
}
} else {
if timeout >= 0 {
roomTyping[userID] = time.Now().Unix() + timeout
} else {
delete(roomTyping, userID)
}
}
store.typing[roomID] = roomTyping
}
type BasicStateStore struct {
registrationsLock sync.RWMutex `json:"-"`
Registrations map[id.UserID]bool `json:"registrations"`
membersLock sync.RWMutex `json:"-"`
Members map[id.RoomID]map[id.UserID]*event.MemberEventContent `json:"memberships"`
powerLevelsLock sync.RWMutex `json:"-"`
PowerLevels map[id.RoomID]*event.PowerLevelsEventContent `json:"power_levels"`
*TypingStateStore
}
func NewBasicStateStore() StateStore {
return &BasicStateStore{
Registrations: make(map[id.UserID]bool),
Members: make(map[id.RoomID]map[id.UserID]*event.MemberEventContent),
PowerLevels: make(map[id.RoomID]*event.PowerLevelsEventContent),
TypingStateStore: NewTypingStateStore(),
}
}
func (store *BasicStateStore) IsRegistered(userID id.UserID) bool {
store.registrationsLock.RLock()
defer store.registrationsLock.RUnlock()
registered, ok := store.Registrations[userID]
return ok && registered
}
func (store *BasicStateStore) MarkRegistered(userID id.UserID) {
store.registrationsLock.Lock()
defer store.registrationsLock.Unlock()
store.Registrations[userID] = true
}
func (store *BasicStateStore) GetRoomMembers(roomID id.RoomID) map[id.UserID]*event.MemberEventContent {
store.membersLock.RLock()
members, ok := store.Members[roomID]
store.membersLock.RUnlock()
if !ok {
members = make(map[id.UserID]*event.MemberEventContent)
store.membersLock.Lock()
store.Members[roomID] = members
store.membersLock.Unlock()
}
return members
}
func (store *BasicStateStore) GetMembership(roomID id.RoomID, userID id.UserID) event.Membership {
return store.GetMember(roomID, userID).Membership
}
func (store *BasicStateStore) GetMember(roomID id.RoomID, userID id.UserID) *event.MemberEventContent {
member, ok := store.TryGetMember(roomID, userID)
if !ok {
member = &event.MemberEventContent{Membership: event.MembershipLeave}
}
return member
}
func (store *BasicStateStore) TryGetMember(roomID id.RoomID, userID id.UserID) (member *event.MemberEventContent, ok bool) {
store.membersLock.RLock()
defer store.membersLock.RUnlock()
members, membersOk := store.Members[roomID]
if !membersOk {
return
}
member, ok = members[userID]
return
}
func (store *BasicStateStore) IsInRoom(roomID id.RoomID, userID id.UserID) bool {
return store.IsMembership(roomID, userID, "join")
}
func (store *BasicStateStore) IsInvited(roomID id.RoomID, userID id.UserID) bool {
return store.IsMembership(roomID, userID, "join", "invite")
}
func (store *BasicStateStore) IsMembership(roomID id.RoomID, userID id.UserID, allowedMemberships ...event.Membership) bool {
membership := store.GetMembership(roomID, userID)
for _, allowedMembership := range allowedMemberships {
if allowedMembership == membership {
return true
}
}
return false
}
func (store *BasicStateStore) SetMembership(roomID id.RoomID, userID id.UserID, membership event.Membership) {
store.membersLock.Lock()
members, ok := store.Members[roomID]
if !ok {
members = map[id.UserID]*event.MemberEventContent{
userID: {Membership: membership},
}
} else {
member, ok := members[userID]
if !ok {
members[userID] = &event.MemberEventContent{Membership: membership}
} else {
member.Membership = membership
members[userID] = member
}
}
store.Members[roomID] = members
store.membersLock.Unlock()
}
func (store *BasicStateStore) SetMember(roomID id.RoomID, userID id.UserID, member *event.MemberEventContent) {
store.membersLock.Lock()
members, ok := store.Members[roomID]
if !ok {
members = map[id.UserID]*event.MemberEventContent{
userID: member,
}
} else {
members[userID] = member
}
store.Members[roomID] = members
store.membersLock.Unlock()
}
func (store *BasicStateStore) SetPowerLevels(roomID id.RoomID, levels *event.PowerLevelsEventContent) {
store.powerLevelsLock.Lock()
store.PowerLevels[roomID] = levels
store.powerLevelsLock.Unlock()
}
func (store *BasicStateStore) GetPowerLevels(roomID id.RoomID) (levels *event.PowerLevelsEventContent) {
store.powerLevelsLock.RLock()
levels = store.PowerLevels[roomID]
store.powerLevelsLock.RUnlock()
return
}
func (store *BasicStateStore) GetPowerLevel(roomID id.RoomID, userID id.UserID) int {
return store.GetPowerLevels(roomID).GetUserLevel(userID)
}
func (store *BasicStateStore) GetPowerLevelRequirement(roomID id.RoomID, eventType event.Type) int {
return store.GetPowerLevels(roomID).GetEventLevel(eventType)
}
func (store *BasicStateStore) HasPowerLevel(roomID id.RoomID, userID id.UserID, eventType event.Type) bool {
return store.GetPowerLevel(roomID, userID) >= store.GetPowerLevelRequirement(roomID, eventType)
}