telegabber/telegram/muc.go

189 lines
3.6 KiB
Go

package telegram
import (
"sync"
"github.com/zelenin/go-tdlib/client"
log "github.com/sirupsen/logrus"
)
const MUCOccupantsLimit int32 = 200
// MUCState holds MUC metadata
type MUCState struct {
Resources map[string]bool
Occupants *MUCOccupantsLRU
}
// MUCOccupant represents a MUC occupant
type MUCOccupant struct {
Nickname string
Affiliation string
Role string
Status client.ChatMemberStatus
prev *MUCOccupant
next *MUCOccupant
key int64
}
func (o *MUCOccupant) cutOut() (prev, next *MUCOccupant) {
prev = o.prev
next = o.next
// -- * --- * -X- * -X- * --- * --
o.prev = nil
o.next = nil
if prev != nil {
prev.next = next
}
if next != nil {
next.prev = prev
}
return
}
func NewMUCState() *MUCState {
return &MUCState{
Resources: make(map[string]bool),
Occupants: NewMUCOccupantsLRU(),
}
}
type MUCOccupantsLRU struct {
m map[int64]*MUCOccupant
oldest *MUCOccupant
newest *MUCOccupant
lock sync.Mutex
}
func NewMUCOccupantsLRU() *MUCOccupantsLRU {
return &MUCOccupantsLRU{
m: make(map[int64]*MUCOccupant),
}
}
func (lru *MUCOccupantsLRU) Get(key int64) (*MUCOccupant, bool) {
lru.lock.Lock()
defer lru.lock.Unlock()
occupant, ok := lru.m[key]
return occupant, ok
}
func (lru *MUCOccupantsLRU) cutOut(oldOccupant *MUCOccupant) (prev, next *MUCOccupant) {
prev, next = oldOccupant.cutOut()
if lru.oldest == oldOccupant {
lru.oldest = next
}
if lru.newest == oldOccupant {
lru.newest = prev
}
return
}
func (lru *MUCOccupantsLRU) insertNewest(occupant *MUCOccupant) {
lru.newest.next = occupant
occupant.prev = lru.newest
occupant.next = nil
lru.newest = occupant
}
// Set adds or replaces an occupant and possibly returns an occupant removed instead because of overflow
func (lru *MUCOccupantsLRU) Set(key int64, occupant *MUCOccupant) (deleted *MUCOccupant) {
lru.lock.Lock()
defer lru.lock.Unlock()
occupant.key = key
oldOccupant, oldOk := lru.m[key]
lru.m[key] = occupant
if oldOk {
lru.cutOut(oldOccupant)
}
if (lru.oldest == nil) != (lru.newest == nil) {
log.Fatal("MRD MUDAQ")
}
if lru.oldest == nil && lru.newest == nil {
lru.oldest = occupant
lru.newest = occupant
occupant.prev = nil
occupant.next = nil
} else {
lru.insertNewest(occupant)
}
if len(lru.m) > int(MUCOccupantsLimit) && lru.oldest != nil {
deleted = lru.oldest
delete(lru.m, lru.oldest.key)
lru.cutOut(lru.oldest)
}
return
}
// Delete occupant by member ID
func (lru *MUCOccupantsLRU) Delete(key int64) {
lru.lock.Lock()
defer lru.lock.Unlock()
oldOccupant, oldOk := lru.m[key]
delete(lru.m, key)
if oldOk {
lru.cutOut(oldOccupant)
}
}
// Bump raises the occupant in LRU
func (lru *MUCOccupantsLRU) Bump(occupant *MUCOccupant) {
lru.lock.Lock()
defer lru.lock.Unlock()
if lru.newest == occupant {
// already at the top, nothing to do
return
}
lru.cutOut(occupant)
lru.insertNewest(occupant)
}
// Range loops over all occupants
func (lru *MUCOccupantsLRU) Range() chan *MUCOccupant {
lru.lock.Lock()
occupantChan := make(chan *MUCOccupant, 1)
go func() {
defer func() {
lru.lock.Unlock()
close(occupantChan)
}()
for _, occupant := range lru.m {
occupantChan <- occupant
}
}()
return occupantChan
}
// Clear properly removes all occupants and their possible mutual references (not necessary in Golang, yet still)
func (lru *MUCOccupantsLRU) Clear() {
lru.lock.Lock()
defer lru.lock.Unlock()
for _, occupant := range lru.m {
occupant.prev = nil
occupant.next = nil
}
lru.m = make(map[int64]*MUCOccupant)
lru.oldest = nil
lru.newest = nil
}