nip29: "hidden" and "private" metadata fields.

https://github.com/nostr-protocol/nips/pull/2106
This commit is contained in:
fiatjaf
2025-11-19 09:19:00 -03:00
parent 5efd8c6485
commit 76abd031d2
3 changed files with 64 additions and 43 deletions

View File

@@ -42,8 +42,18 @@ type Group struct {
Picture string Picture string
About string About string
Members map[nostr.PubKey][]*Role Members map[nostr.PubKey][]*Role
// indicates that only members can read group messages
Private bool Private bool
Closed bool
// indicates that only members can write messages to the group
Restricted bool
// indicates that relays should hide group metadata from non-members
Hidden bool
// indicates that join requests are ignored unless they include an invite code
Closed bool
Roles []*Role Roles []*Role
InviteCodes []string InviteCodes []string
@@ -56,11 +66,19 @@ type Group struct {
func (group Group) String() string { func (group Group) String() string {
maybePrivate := "" maybePrivate := ""
maybeRestricted := ""
maybeHidden := ""
maybeClosed := "" maybeClosed := ""
if group.Private { if group.Private {
maybePrivate = " private" maybePrivate = " private"
} }
if group.Restricted {
maybeRestricted = " restricted"
}
if group.Hidden {
maybeHidden = " hidden"
}
if group.Closed { if group.Closed {
maybeClosed = " closed" maybeClosed = " closed"
} }
@@ -83,10 +101,12 @@ func (group Group) String() string {
i++ i++
} }
return fmt.Sprintf(`<Group %s name="%s"%s%s picture="%s" about="%s" members=[%v]>`, return fmt.Sprintf(`<Group %s name="%s"%s%s%s%s picture="%s" about="%s" members=[%v]>`,
group.Address, group.Address,
group.Name, group.Name,
maybePrivate, maybePrivate,
maybeRestricted,
maybeHidden,
maybeClosed, maybeClosed,
group.Picture, group.Picture,
group.About, group.About,
@@ -143,13 +163,15 @@ func (group Group) ToMetadataEvent() nostr.Event {
// status // status
if group.Private { if group.Private {
evt.Tags = append(evt.Tags, nostr.Tag{"private"}) evt.Tags = append(evt.Tags, nostr.Tag{"private"})
} else { }
evt.Tags = append(evt.Tags, nostr.Tag{"public"}) if group.Restricted {
evt.Tags = append(evt.Tags, nostr.Tag{"restricted"})
}
if group.Hidden {
evt.Tags = append(evt.Tags, nostr.Tag{"hidden"})
} }
if group.Closed { if group.Closed {
evt.Tags = append(evt.Tags, nostr.Tag{"closed"}) evt.Tags = append(evt.Tags, nostr.Tag{"closed"})
} else {
evt.Tags = append(evt.Tags, nostr.Tag{"open"})
} }
return evt return evt
@@ -238,6 +260,12 @@ func (group *Group) MergeInMetadataEvent(evt *nostr.Event) error {
if tag := evt.Tags.Find("private"); tag != nil { if tag := evt.Tags.Find("private"); tag != nil {
group.Private = true group.Private = true
} }
if tag := evt.Tags.Find("restricted"); tag != nil {
group.Restricted = true
}
if tag := evt.Tags.Find("hidden"); tag != nil {
group.Hidden = true
}
if tag := evt.Tags.Find("closed"); tag != nil { if tag := evt.Tags.Find("closed"); tag != nil {
group.Closed = true group.Closed = true
} }

View File

@@ -92,20 +92,11 @@ var moderationActionFactories = map[nostr.Kind]func(nostr.Event) (Action, error)
} }
y := true y := true
n := false if evt.Tags.Has("private") {
if evt.Tags.Has("public") {
edit.PrivateValue = &n
ok = true
} else if evt.Tags.Has("private") {
edit.PrivateValue = &y edit.PrivateValue = &y
ok = true ok = true
} }
if evt.Tags.Has("closed") {
if evt.Tags.Has("open") {
edit.ClosedValue = &n
ok = true
} else if evt.Tags.Has("closed") {
edit.ClosedValue = &y edit.ClosedValue = &y
ok = true ok = true
} }
@@ -205,12 +196,14 @@ func (a RemoveUser) Apply(group *Group) {
} }
type EditMetadata struct { type EditMetadata struct {
NameValue *string NameValue *string
PictureValue *string PictureValue *string
AboutValue *string AboutValue *string
PrivateValue *bool PrivateValue *bool
ClosedValue *bool RestrictedValue *bool
When nostr.Timestamp HiddenValue *bool
ClosedValue *bool
When nostr.Timestamp
} }
func (_ EditMetadata) Name() string { return "edit-metadata" } func (_ EditMetadata) Name() string { return "edit-metadata" }
@@ -228,6 +221,12 @@ func (a EditMetadata) Apply(group *Group) {
if a.PrivateValue != nil { if a.PrivateValue != nil {
group.Private = *a.PrivateValue group.Private = *a.PrivateValue
} }
if a.RestrictedValue != nil {
group.Restricted = *a.RestrictedValue
}
if a.HiddenValue != nil {
group.Hidden = *a.HiddenValue
}
if a.ClosedValue != nil { if a.ClosedValue != nil {
group.Closed = *a.ClosedValue group.Closed = *a.ClosedValue
} }
@@ -254,6 +253,8 @@ func (a DeleteGroup) Apply(group *Group) {
group.Members = make(map[nostr.PubKey][]*Role) group.Members = make(map[nostr.PubKey][]*Role)
group.Closed = true group.Closed = true
group.Private = true group.Private = true
group.Restricted = true
group.Hidden = true
group.Name = "[deleted]" group.Name = "[deleted]"
group.About = "" group.About = ""
group.Picture = "" group.Picture = ""

View File

@@ -3,6 +3,7 @@ package nip29
import ( import (
"testing" "testing"
"fiatjaf.com/nostr"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@@ -31,37 +32,28 @@ func TestGroupEventBackAndForth(t *testing.T) {
require.True(t, hasPrivate, "translation of group1 to metadata event failed: %s", meta1) require.True(t, hasPrivate, "translation of group1 to metadata event failed: %s", meta1)
group2, _ := NewGroup("groups.com'abc") group2, _ := NewGroup("groups.com'abc")
group2.Members[ALICE] = []*Role{{Name: "nada"}} alicePub, _ := nostr.PubKeyFromHex(ALICE)
group2.Members[BOB] = []*Role{{Name: "nada"}} group2.Members[alicePub] = []*Role{{Name: "nada"}}
group2.Members[CAROL] = nil
group2.Members[DEREK] = nil
admins2 := group2.ToAdminsEvent() admins2 := group2.ToAdminsEvent()
require.Equal(t, "abc", admins2.Tags.GetD(), "translation of group2 to admins event failed") require.Equal(t, "abc", admins2.Tags.GetD(), "translation of group2 to admins event failed")
require.Equal(t, 3, len(admins2.Tags), "translation of group2 to admins event failed") require.Equal(t, 2, len(admins2.Tags), "translation of group2 to admins event failed")
require.True(t, admins2.Tags.FindWithValue("p", ALICE)[2] == "nada", "translation of group2 to admins event failed") require.True(t, admins2.Tags.FindWithValue("p", ALICE)[2] == "nada", "translation of group2 to admins event failed")
require.True(t, admins2.Tags.FindWithValue("p", BOB)[2] == "nada", "translation of group2 to admins event failed")
members2 := group2.ToMembersEvent() members2 := group2.ToMembersEvent()
require.Equal(t, "abc", members2.Tags.GetD(), "translation of group2 to members2 event failed") require.Equal(t, 2, len(members2.Tags), "translation of group2 to members2 event failed")
require.Equal(t, 5, len(members2.Tags), "translation of group2 to members2 event failed")
require.NotNil(t, members2.Tags.FindWithValue("p", ALICE), "translation of group2 to members2 event failed") require.NotNil(t, members2.Tags.FindWithValue("p", ALICE), "translation of group2 to members2 event failed")
require.NotNil(t, members2.Tags.FindWithValue("p", BOB), "translation of group2 to members2 event failed")
require.NotNil(t, members2.Tags.FindWithValue("p", CAROL), "translation of group2 to members2 event failed")
require.NotNil(t, members2.Tags.FindWithValue("p", DEREK), "translation of group2 to members2 event failed")
group1.MergeInMembersEvent(members2) group1.MergeInMembersEvent(&members2)
require.Equal(t, 4, len(group1.Members), "merge of members2 into group1 failed") require.Equal(t, 1, len(group1.Members), "merge of members2 into group1 failed")
require.Len(t, group1.Members[ALICE], 0, "merge of members2 into group1 failed") require.Len(t, group1.Members[alicePub], 0, "merge of members2 into group1 failed")
require.Len(t, group1.Members[DEREK], 0, "merge of members2 into group1 failed")
group1.MergeInAdminsEvent(admins2) group1.MergeInAdminsEvent(&admins2)
require.Equal(t, 4, len(group1.Members), "merge of admins2 into group1 failed") require.Equal(t, 1, len(group1.Members), "merge of admins2 into group1 failed")
require.Equal(t, "nada", group1.Members[ALICE][0].Name, "merge of admins2 into group1 failed") require.Equal(t, "nada", group1.Members[alicePub][0].Name, "merge of admins2 into group1 failed")
require.Len(t, group1.Members[DEREK], 0, "merge of admins2 into group1 failed")
group2.MergeInMetadataEvent(meta1) group2.MergeInMetadataEvent(&meta1)
require.Equal(t, "banana", group2.Name, "merge of meta1 into group2 failed") require.Equal(t, "banana", group2.Name, "merge of meta1 into group2 failed")
require.Equal(t, "abc", group2.Address.ID, "merge of meta1 into group2 failed") require.Equal(t, "abc", group2.Address.ID, "merge of meta1 into group2 failed")
} }