Generate member lists

This commit is contained in:
Jon Staab
2025-10-24 11:36:56 -07:00
parent b04c8e99cb
commit 21ee8c7361
6 changed files with 194 additions and 63 deletions
+9 -9
View File
@@ -79,6 +79,10 @@ func LoadConfig(filename string) (*Config, error) {
return &config, nil return &config, nil
} }
func (config *Config) Sign(event *nostr.Event) error {
return event.Sign(config.secret)
}
func (config *Config) GetSelf() nostr.PubKey { func (config *Config) GetSelf() nostr.PubKey {
return config.secret.Public() return config.secret.Public()
} }
@@ -87,16 +91,12 @@ func (config *Config) IsSelf(pubkey nostr.PubKey) bool {
return pubkey == config.GetSelf() return pubkey == config.GetSelf()
} }
func (config *Config) Sign(event *nostr.Event) error { func (config *Config) GetOwner() nostr.PubKey {
return event.Sign(config.secret) return nostr.MustPubKeyFromHex(config.Info.Pubkey)
} }
func (config *Config) IsOwner(pubkey nostr.PubKey) bool { func (config *Config) IsOwner(pubkey nostr.PubKey) bool {
return pubkey.Hex() == config.Info.Pubkey return pubkey == config.GetOwner()
}
func (config *Config) IsAdmin(pubkey nostr.PubKey) bool {
return config.IsOwner(pubkey) || config.IsSelf(pubkey)
} }
func (config *Config) GetAssignedRoles(pubkey nostr.PubKey) []Role { func (config *Config) GetAssignedRoles(pubkey nostr.PubKey) []Role {
@@ -124,7 +124,7 @@ func (config *Config) GetAllRoles(pubkey nostr.PubKey) []Role {
} }
func (config *Config) CanInvite(pubkey nostr.PubKey) bool { func (config *Config) CanInvite(pubkey nostr.PubKey) bool {
if config.IsAdmin(pubkey) { if config.IsOwner(pubkey) || config.IsSelf(pubkey) {
return true return true
} }
@@ -138,7 +138,7 @@ func (config *Config) CanInvite(pubkey nostr.PubKey) bool {
} }
func (config *Config) CanManage(pubkey nostr.PubKey) bool { func (config *Config) CanManage(pubkey nostr.PubKey) bool {
if config.IsAdmin(pubkey) { if config.IsOwner(pubkey) || config.IsSelf(pubkey) {
return true return true
} }
+1 -1
View File
@@ -387,7 +387,7 @@ func (events *EventStore) GetOrCreateApplicationSpecificData(d string) nostr.Eve
} }
} }
func (events *EventStore) GetOrCreateMemberList() nostr.Event { func (events *EventStore) GetOrCreateRelayMembersList() nostr.Event {
filter := nostr.Filter{ filter := nostr.Filter{
Kinds: []nostr.Kind{RELAY_MEMBERS}, Kinds: []nostr.Kind{RELAY_MEMBERS},
} }
+140 -44
View File
@@ -4,6 +4,8 @@ import (
"fiatjaf.com/nostr" "fiatjaf.com/nostr"
) )
// Utils
func GetGroupIDFromEvent(event nostr.Event) string { func GetGroupIDFromEvent(event nostr.Event) string {
tag := event.Tags.Find("h") tag := event.Tags.Find("h")
@@ -14,11 +16,16 @@ func GetGroupIDFromEvent(event nostr.Event) string {
return "" return ""
} }
// Struct definition
type GroupStore struct { type GroupStore struct {
Config *Config Config *Config
Events *EventStore Events *EventStore
Management *ManagementStore
} }
// Metadata
func (g *GroupStore) GetMetadata(h string) nostr.Event { func (g *GroupStore) GetMetadata(h string) nostr.Event {
filter := nostr.Filter{ filter := nostr.Filter{
Kinds: []nostr.Kind{nostr.KindSimpleGroupMetadata}, Kinds: []nostr.Kind{nostr.KindSimpleGroupMetadata},
@@ -34,6 +41,81 @@ func (g *GroupStore) GetMetadata(h string) nostr.Event {
return nostr.Event{} return nostr.Event{}
} }
func (g *GroupStore) SetMetadataFromEvent(event nostr.Event) error {
tags := nostr.Tags{}
for _, tag := range event.Tags {
if len(tag) >= 2 && tag[0] == "h" {
tags = append(tags, nostr.Tag{"d", tag[1]})
} else {
tags = append(tags, tag)
}
}
metadataEvent := nostr.Event{
Kind: nostr.KindSimpleGroupMetadata,
CreatedAt: event.CreatedAt,
Tags: tags,
}
return g.Events.SignAndStoreEvent(&metadataEvent, true)
}
// Deletion
func (g *GroupStore) DeleteGroup(h string) {
filters := []nostr.Filter{
{
Tags: nostr.TagMap{
"d": []string{h},
},
},
{
Tags: nostr.TagMap{
"h": []string{h},
},
},
}
for _, filter := range filters {
for event := range g.Events.QueryEvents(filter, 0) {
g.Events.DeleteEvent(event.ID)
}
}
}
// Admins
func (g *GroupStore) IsAdmin(h string, pubkey nostr.PubKey) bool {
return g.Management.IsAdmin(pubkey)
}
func (g *GroupStore) GetAdmins(h string) []nostr.PubKey {
return g.Management.GetAdmins()
}
func (g *GroupStore) GenerateAdminsEvent(h string) nostr.Event {
tags := nostr.Tags{
nostr.Tag{"-"},
}
for _, pubkey := range g.GetAdmins(h) {
tags = append(tags, nostr.Tag{"p", pubkey.Hex()})
}
event := nostr.Event{
Kind: nostr.KindSimpleGroupAdmins,
CreatedAt: nostr.Now(),
Tags: tags,
}
g.Config.Sign(&event)
return event
}
// Membership
func (g *GroupStore) AddMember(h string, pubkey nostr.PubKey) error { func (g *GroupStore) AddMember(h string, pubkey nostr.PubKey) error {
event := nostr.Event{ event := nostr.Event{
Kind: nostr.KindSimpleGroupPutUser, Kind: nostr.KindSimpleGroupPutUser,
@@ -60,47 +142,6 @@ func (g *GroupStore) RemoveMember(h string, pubkey nostr.PubKey) error {
return g.Events.SignAndStoreEvent(&event, true) return g.Events.SignAndStoreEvent(&event, true)
} }
func (g *GroupStore) SetMetadataFromEvent(event nostr.Event) error {
tags := nostr.Tags{}
for _, tag := range event.Tags {
if len(tag) >= 2 && tag[0] == "h" {
tags = append(tags, nostr.Tag{"d", tag[1]})
} else {
tags = append(tags, tag)
}
}
metadataEvent := nostr.Event{
Kind: nostr.KindSimpleGroupMetadata,
CreatedAt: event.CreatedAt,
Tags: tags,
}
return g.Events.SignAndStoreEvent(&metadataEvent, true)
}
func (g *GroupStore) DeleteGroup(h string) {
filters := []nostr.Filter{
{
Tags: nostr.TagMap{
"d": []string{h},
},
},
{
Tags: nostr.TagMap{
"h": []string{h},
},
},
}
for _, filter := range filters {
for event := range g.Events.QueryEvents(filter, 0) {
g.Events.DeleteEvent(event.ID)
}
}
}
func (g *GroupStore) IsMember(h string, pubkey nostr.PubKey) bool { func (g *GroupStore) IsMember(h string, pubkey nostr.PubKey) bool {
filter := nostr.Filter{ filter := nostr.Filter{
Kinds: []nostr.Kind{nostr.KindSimpleGroupPutUser, nostr.KindSimpleGroupRemoveUser}, Kinds: []nostr.Kind{nostr.KindSimpleGroupPutUser, nostr.KindSimpleGroupRemoveUser},
@@ -123,10 +164,65 @@ func (g *GroupStore) IsMember(h string, pubkey nostr.PubKey) bool {
return false return false
} }
func (g *GroupStore) GetMembers(h string) []nostr.PubKey {
filter := nostr.Filter{
Kinds: []nostr.Kind{nostr.KindSimpleGroupPutUser, nostr.KindSimpleGroupRemoveUser},
Tags: nostr.TagMap{
"h": []string{h},
},
}
members := make([]nostr.PubKey, 0)
for event := range g.Events.QueryEvents(filter, 0) {
for hex := range event.Tags.FindAll("p") {
if pubkey, err := nostr.PubKeyFromHex(hex[1]); err != nil {
if event.Kind == nostr.KindSimpleGroupPutUser {
members = append(members, pubkey)
} else {
members = Remove(members, pubkey)
}
}
}
}
return members
}
func (g *GroupStore) GenerateMembersEvent(h string) nostr.Event {
tags := nostr.Tags{
nostr.Tag{"-"},
}
for _, pubkey := range g.GetMembers(h) {
tags = append(tags, nostr.Tag{"p", pubkey.Hex()})
}
event := nostr.Event{
Kind: nostr.KindSimpleGroupMembers,
CreatedAt: nostr.Now(),
Tags: tags,
}
g.Config.Sign(&event)
return event
}
// Other stuff
func (g *GroupStore) HasAccess(h string, pubkey nostr.PubKey) bool { func (g *GroupStore) HasAccess(h string, pubkey nostr.PubKey) bool {
if !HasTag(g.GetMetadata(h).Tags, "closed") { if !HasTag(g.GetMetadata(h).Tags, "closed") {
return true return true
} }
return g.IsMember(h, pubkey) if g.IsAdmin(h, pubkey) {
return true
}
if g.IsMember(h, pubkey) {
return true
}
return false
} }
+3 -2
View File
@@ -50,8 +50,9 @@ func MakeInstance(filename string) (*Instance, error) {
} }
groups := &GroupStore{ groups := &GroupStore{
Config: config, Config: config,
Events: events, Events: events,
Management: management,
} }
instance := &Instance{ instance := &Instance{
+31 -7
View File
@@ -120,11 +120,35 @@ func (m *ManagementStore) PubkeyIsBanned(pubkey nostr.PubKey) bool {
return tag != nil return tag != nil
} }
// Admins
func (m *ManagementStore) IsAdmin(pubkey nostr.PubKey) bool {
return m.Config.IsOwner(pubkey) || m.Config.IsSelf(pubkey)
}
func (m *ManagementStore) GetAdmins() []nostr.PubKey {
members := make([]nostr.PubKey, 0)
members = append(members, m.Config.GetOwner())
members = append(members, m.Config.GetSelf())
for _, role := range m.Config.Roles {
if role.CanManage {
for _, pubkey := range role.Pubkeys {
members = append(members, nostr.MustPubKeyFromHex(pubkey))
}
}
}
return members
}
// Membership // Membership
func (m *ManagementStore) GetMembers() []nostr.PubKey { func (m *ManagementStore) GetMembers() []nostr.PubKey {
pubkeys := make([]nostr.PubKey, 0) pubkeys := make([]nostr.PubKey, 0)
for tag := range m.Events.GetOrCreateMemberList().Tags.FindAll("member") { for tag := range m.Events.GetOrCreateRelayMembersList().Tags.FindAll("member") {
pubkey, err := nostr.PubKeyFromHex(tag[1]) pubkey, err := nostr.PubKeyFromHex(tag[1])
if err == nil { if err == nil {
@@ -136,11 +160,11 @@ func (m *ManagementStore) GetMembers() []nostr.PubKey {
} }
func (m *ManagementStore) IsMember(pubkey nostr.PubKey) bool { func (m *ManagementStore) IsMember(pubkey nostr.PubKey) bool {
return m.Events.GetOrCreateMemberList().Tags.FindWithValue("member", pubkey.Hex()) != nil return m.Events.GetOrCreateRelayMembersList().Tags.FindWithValue("member", pubkey.Hex()) != nil
} }
func (m *ManagementStore) AddMember(pubkey nostr.PubKey) error { func (m *ManagementStore) AddMember(pubkey nostr.PubKey) error {
membersEvent := m.Events.GetOrCreateMemberList() membersEvent := m.Events.GetOrCreateRelayMembersList()
if membersEvent.Tags.FindWithValue("member", pubkey.Hex()) == nil { if membersEvent.Tags.FindWithValue("member", pubkey.Hex()) == nil {
addMemberEvent := nostr.Event{ addMemberEvent := nostr.Event{
@@ -167,7 +191,7 @@ func (m *ManagementStore) AddMember(pubkey nostr.PubKey) error {
} }
func (m *ManagementStore) RemoveMember(pubkey nostr.PubKey) error { func (m *ManagementStore) RemoveMember(pubkey nostr.PubKey) error {
membersEvent := m.Events.GetOrCreateMemberList() membersEvent := m.Events.GetOrCreateRelayMembersList()
if membersEvent.Tags.FindWithValue("member", pubkey.Hex()) != nil { if membersEvent.Tags.FindWithValue("member", pubkey.Hex()) != nil {
removeMemberEvent := nostr.Event{ removeMemberEvent := nostr.Event{
@@ -224,7 +248,7 @@ func (m *ManagementStore) GetAllowedPubkeyItems() []nip86.PubKeyReason {
reasons := make([]nip86.PubKeyReason, 0) reasons := make([]nip86.PubKeyReason, 0)
reasons = append(reasons, nip86.PubKeyReason{ reasons = append(reasons, nip86.PubKeyReason{
PubKey: nostr.MustPubKeyFromHex(m.Config.Info.Pubkey), PubKey: m.Config.GetOwner(),
Reason: "relay owner", Reason: "relay owner",
}) })
@@ -242,7 +266,7 @@ func (m *ManagementStore) GetAllowedPubkeyItems() []nip86.PubKeyReason {
} }
} }
for tag := range m.Events.GetOrCreateMemberList().Tags.FindAll("member") { for tag := range m.Events.GetOrCreateRelayMembersList().Tags.FindAll("member") {
pubkey, err := nostr.PubKeyFromHex(tag[1]) pubkey, err := nostr.PubKeyFromHex(tag[1])
if err != nil { if err != nil {
@@ -276,7 +300,7 @@ func (m *ManagementStore) AllowPubkey(pubkey nostr.PubKey) error {
} }
func (m *ManagementStore) HasAccess(pubkey nostr.PubKey) bool { func (m *ManagementStore) HasAccess(pubkey nostr.PubKey) bool {
if m.Config.IsAdmin(pubkey) { if m.IsAdmin(pubkey) {
return true return true
} }
+10
View File
@@ -48,6 +48,16 @@ func Filter[T any](ss []T, test func(T) bool) (ret []T) {
return return
} }
func Remove[T comparable](slice []T, element T) []T {
for i, v := range slice {
if v == element {
return append(slice[:i], slice[i+1:]...)
}
}
return slice
}
const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
func RandomString(n int) string { func RandomString(n int) string {