112 lines
2.4 KiB
Go

package main
import (
"context"
"fmt"
"strings"
"sync"
"time"
"github.com/fiatjaf/set"
"github.com/nbd-wtf/go-nostr"
"github.com/nbd-wtf/go-nostr/nip29"
nip29_relay "github.com/nbd-wtf/go-nostr/nip29/relay"
"golang.org/x/exp/slices"
"golang.org/x/time/rate"
)
type Group struct {
nip29.Group
bucket *rate.Limiter
}
var (
groups []*Group
groupsLock sync.Mutex
)
func newGroup(id string) *Group {
return &Group{
Group: nip29.Group{
ID: id,
Members: map[string]*nip29.Role{},
},
// very strict rate limits
bucket: rate.NewLimiter(rate.Every(time.Minute*2), 15),
}
}
// loadGroups loads all the group metadata from all the past action messages
func loadGroups(ctx context.Context) {
groupsLock.Lock()
defer groupsLock.Unlock()
groupMetadataEvents, _ := db.QueryEvents(ctx, nostr.Filter{Limit: db.MaxLimit, Kinds: nip29.ModerationEventKinds})
alreadySeen := set.NewSliceSet[string]()
for evt := range groupMetadataEvents {
gtag := evt.Tags.GetFirst([]string{"h", ""})
id := (*gtag)[1]
if alreadySeen.Has(id) {
continue
}
alreadySeen.Add(id)
group := newGroup(id)
ch, _ := db.QueryEvents(ctx, nostr.Filter{
Limit: 5000, Kinds: nip29.ModerationEventKinds, Tags: nostr.TagMap{"h": []string{id}},
})
events := make([]*nostr.Event, 0, 5000)
for event := range ch {
events = append(events, event)
}
for i := len(events) - 1; i >= 0; i-- {
evt := events[i]
act, _ := nip29_relay.GetModerationAction(evt)
act.Apply(&group.Group)
}
groups = append(groups, group)
}
slices.SortFunc(groups, func(a, b *Group) int { return strings.Compare(a.ID, b.ID) })
}
func getGroup(id string) *Group {
groupsLock.Lock()
defer groupsLock.Unlock()
idx, ok := slices.BinarySearchFunc(groups, id, groupComparator)
if !ok {
return nil
}
return groups[idx]
}
func addGroup(group *Group) error {
groupsLock.Lock()
defer groupsLock.Unlock()
idx, ok := slices.BinarySearchFunc(groups, group.ID, groupComparator)
if ok {
return fmt.Errorf("a group with this id already exists")
}
groups = append(groups[0:idx], nil) // bogus
copy(groups[idx+1:], groups[idx:])
groups[idx] = group
return nil
}
func getGroupFromEvent(event *nostr.Event) *Group {
gtag := event.Tags.GetFirst([]string{"h", ""})
groupId := (*gtag)[1]
return getGroup(groupId)
}
func groupComparator(g *Group, id string) int {
return strings.Compare(g.ID, id)
}