nip29: livekit group live participants.

This commit is contained in:
fiatjaf
2026-03-11 16:06:30 -03:00
parent 681bd55e55
commit 3bd059d1f9
4 changed files with 223 additions and 158 deletions
+135 -132
View File
@@ -246,6 +246,8 @@ func (kind Kind) Name() string {
return "SimpleGroupMembers"
case KindSimpleGroupRoles:
return "SimpleGroupRoles"
case KindSimpleGroupLiveKitParticipants:
return "SimpleGroupLiveKitParticipants"
case KindWikiArticle:
return "WikiArticle"
case KindRedirects:
@@ -277,138 +279,139 @@ func (kind Kind) Name() string {
}
const (
KindProfileMetadata Kind = 0
KindTextNote Kind = 1
KindRecommendServer Kind = 2
KindFollowList Kind = 3
KindEncryptedDirectMessage Kind = 4
KindDeletion Kind = 5
KindRepost Kind = 6
KindReaction Kind = 7
KindBadgeAward Kind = 8
KindSimpleGroupChatMessage Kind = 9
KindSimpleGroupThreadedReply Kind = 10
KindSimpleGroupThread Kind = 11
KindSimpleGroupReply Kind = 12
KindSeal Kind = 13
KindDirectMessage Kind = 14
KindGenericRepost Kind = 16
KindReactionToWebsite Kind = 17
KindChannelCreation Kind = 40
KindChannelMetadata Kind = 41
KindChannelMessage Kind = 42
KindChannelHideMessage Kind = 43
KindChannelMuteUser Kind = 44
KindChess Kind = 64
KindMergeRequests Kind = 818
KindComment Kind = 1111
KindBid Kind = 1021
KindBidConfirmation Kind = 1022
KindOpenTimestamps Kind = 1040
KindGiftWrap Kind = 1059
KindFileMetadata Kind = 1063
KindLiveChatMessage Kind = 1311
KindPatch Kind = 1617
KindIssue Kind = 1621
KindReply Kind = 1622
KindStatusOpen Kind = 1630
KindStatusApplied Kind = 1631
KindStatusClosed Kind = 1632
KindStatusDraft Kind = 1633
KindProblemTracker Kind = 1971
KindReporting Kind = 1984
KindLabel Kind = 1985
KindRelayReviews Kind = 1986
KindAIEmbeddings Kind = 1987
KindTorrent Kind = 2003
KindTorrentComment Kind = 2004
KindCoinjoinPool Kind = 2022
KindCommunityPostApproval Kind = 4550
KindJobFeedback Kind = 7000
KindSimpleGroupPutUser Kind = 9000
KindSimpleGroupRemoveUser Kind = 9001
KindSimpleGroupEditMetadata Kind = 9002
KindSimpleGroupDeleteEvent Kind = 9005
KindSimpleGroupCreateGroup Kind = 9007
KindSimpleGroupDeleteGroup Kind = 9008
KindSimpleGroupCreateInvite Kind = 9009
KindSimpleGroupJoinRequest Kind = 9021
KindSimpleGroupLeaveRequest Kind = 9022
KindZapGoal Kind = 9041
KindNutZap Kind = 9321
KindTidalLogin Kind = 9467
KindZapRequest Kind = 9734
KindZap Kind = 9735
KindHighlights Kind = 9802
KindMuteList Kind = 10000
KindPinList Kind = 10001
KindRelayListMetadata Kind = 10002
KindBookmarkList Kind = 10003
KindCommunityList Kind = 10004
KindPublicChatList Kind = 10005
KindBlockedRelayList Kind = 10006
KindSearchRelayList Kind = 10007
KindSimpleGroupList Kind = 10009
KindInterestList Kind = 10015
KindNutZapInfo Kind = 10019
KindEmojiList Kind = 10030
KindDMRelayList Kind = 10050
KindUserServerList Kind = 10063
KindFileStorageServerList Kind = 10096
KindGoodWikiAuthorList Kind = 10101
KindGoodWikiRelayList Kind = 10102
KindNWCWalletInfo Kind = 13194
KindLightningPubRPC Kind = 21000
KindClientAuthentication Kind = 22242
KindNWCWalletRequest Kind = 23194
KindNWCWalletResponse Kind = 23195
KindNostrConnect Kind = 24133
KindBlobs Kind = 24242
KindHTTPAuth Kind = 27235
KindCategorizedPeopleList Kind = 30000
KindCategorizedBookmarksList Kind = 30001
KindRelaySets Kind = 30002
KindBookmarkSets Kind = 30003
KindCuratedSets Kind = 30004
KindCuratedVideoSets Kind = 30005
KindMuteSets Kind = 30007
KindProfileBadges Kind = 30008
KindBadgeDefinition Kind = 30009
KindInterestSets Kind = 30015
KindStallDefinition Kind = 30017
KindProductDefinition Kind = 30018
KindMarketplaceUI Kind = 30019
KindProductSoldAsAuction Kind = 30020
KindArticle Kind = 30023
KindDraftArticle Kind = 30024
KindEmojiSets Kind = 30030
KindModularArticleHeader Kind = 30040
KindModularArticleContent Kind = 30041
KindReleaseArtifactSets Kind = 30063
KindApplicationSpecificData Kind = 30078
KindLiveEvent Kind = 30311
KindUserStatuses Kind = 30315
KindClassifiedListing Kind = 30402
KindDraftClassifiedListing Kind = 30403
KindRepositoryAnnouncement Kind = 30617
KindRepositoryState Kind = 30618
KindSimpleGroupMetadata Kind = 39000
KindSimpleGroupAdmins Kind = 39001
KindSimpleGroupMembers Kind = 39002
KindSimpleGroupRoles Kind = 39003
KindWikiArticle Kind = 30818
KindRedirects Kind = 30819
KindFeed Kind = 31890
KindDateCalendarEvent Kind = 31922
KindTimeCalendarEvent Kind = 31923
KindCalendar Kind = 31924
KindCalendarEventRSVP Kind = 31925
KindHandlerRecommendation Kind = 31989
KindHandlerInformation Kind = 31990
KindVideoEvent Kind = 34235
KindShortVideoEvent Kind = 34236
KindVideoViewEvent Kind = 34237
KindCommunityDefinition Kind = 34550
KindProfileMetadata Kind = 0
KindTextNote Kind = 1
KindRecommendServer Kind = 2
KindFollowList Kind = 3
KindEncryptedDirectMessage Kind = 4
KindDeletion Kind = 5
KindRepost Kind = 6
KindReaction Kind = 7
KindBadgeAward Kind = 8
KindSimpleGroupChatMessage Kind = 9
KindSimpleGroupThreadedReply Kind = 10
KindSimpleGroupThread Kind = 11
KindSimpleGroupReply Kind = 12
KindSeal Kind = 13
KindDirectMessage Kind = 14
KindGenericRepost Kind = 16
KindReactionToWebsite Kind = 17
KindChannelCreation Kind = 40
KindChannelMetadata Kind = 41
KindChannelMessage Kind = 42
KindChannelHideMessage Kind = 43
KindChannelMuteUser Kind = 44
KindChess Kind = 64
KindMergeRequests Kind = 818
KindComment Kind = 1111
KindBid Kind = 1021
KindBidConfirmation Kind = 1022
KindOpenTimestamps Kind = 1040
KindGiftWrap Kind = 1059
KindFileMetadata Kind = 1063
KindLiveChatMessage Kind = 1311
KindPatch Kind = 1617
KindIssue Kind = 1621
KindReply Kind = 1622
KindStatusOpen Kind = 1630
KindStatusApplied Kind = 1631
KindStatusClosed Kind = 1632
KindStatusDraft Kind = 1633
KindProblemTracker Kind = 1971
KindReporting Kind = 1984
KindLabel Kind = 1985
KindRelayReviews Kind = 1986
KindAIEmbeddings Kind = 1987
KindTorrent Kind = 2003
KindTorrentComment Kind = 2004
KindCoinjoinPool Kind = 2022
KindCommunityPostApproval Kind = 4550
KindJobFeedback Kind = 7000
KindSimpleGroupPutUser Kind = 9000
KindSimpleGroupRemoveUser Kind = 9001
KindSimpleGroupEditMetadata Kind = 9002
KindSimpleGroupDeleteEvent Kind = 9005
KindSimpleGroupCreateGroup Kind = 9007
KindSimpleGroupDeleteGroup Kind = 9008
KindSimpleGroupCreateInvite Kind = 9009
KindSimpleGroupJoinRequest Kind = 9021
KindSimpleGroupLeaveRequest Kind = 9022
KindZapGoal Kind = 9041
KindNutZap Kind = 9321
KindTidalLogin Kind = 9467
KindZapRequest Kind = 9734
KindZap Kind = 9735
KindHighlights Kind = 9802
KindMuteList Kind = 10000
KindPinList Kind = 10001
KindRelayListMetadata Kind = 10002
KindBookmarkList Kind = 10003
KindCommunityList Kind = 10004
KindPublicChatList Kind = 10005
KindBlockedRelayList Kind = 10006
KindSearchRelayList Kind = 10007
KindSimpleGroupList Kind = 10009
KindInterestList Kind = 10015
KindNutZapInfo Kind = 10019
KindEmojiList Kind = 10030
KindDMRelayList Kind = 10050
KindUserServerList Kind = 10063
KindFileStorageServerList Kind = 10096
KindGoodWikiAuthorList Kind = 10101
KindGoodWikiRelayList Kind = 10102
KindNWCWalletInfo Kind = 13194
KindLightningPubRPC Kind = 21000
KindClientAuthentication Kind = 22242
KindNWCWalletRequest Kind = 23194
KindNWCWalletResponse Kind = 23195
KindNostrConnect Kind = 24133
KindBlobs Kind = 24242
KindHTTPAuth Kind = 27235
KindCategorizedPeopleList Kind = 30000
KindCategorizedBookmarksList Kind = 30001
KindRelaySets Kind = 30002
KindBookmarkSets Kind = 30003
KindCuratedSets Kind = 30004
KindCuratedVideoSets Kind = 30005
KindMuteSets Kind = 30007
KindProfileBadges Kind = 30008
KindBadgeDefinition Kind = 30009
KindInterestSets Kind = 30015
KindStallDefinition Kind = 30017
KindProductDefinition Kind = 30018
KindMarketplaceUI Kind = 30019
KindProductSoldAsAuction Kind = 30020
KindArticle Kind = 30023
KindDraftArticle Kind = 30024
KindEmojiSets Kind = 30030
KindModularArticleHeader Kind = 30040
KindModularArticleContent Kind = 30041
KindReleaseArtifactSets Kind = 30063
KindApplicationSpecificData Kind = 30078
KindLiveEvent Kind = 30311
KindUserStatuses Kind = 30315
KindClassifiedListing Kind = 30402
KindDraftClassifiedListing Kind = 30403
KindRepositoryAnnouncement Kind = 30617
KindRepositoryState Kind = 30618
KindSimpleGroupMetadata Kind = 39000
KindSimpleGroupAdmins Kind = 39001
KindSimpleGroupMembers Kind = 39002
KindSimpleGroupRoles Kind = 39003
KindSimpleGroupLiveKitParticipants Kind = 39004
KindWikiArticle Kind = 30818
KindRedirects Kind = 30819
KindFeed Kind = 31890
KindDateCalendarEvent Kind = 31922
KindTimeCalendarEvent Kind = 31923
KindCalendar Kind = 31924
KindCalendarEventRSVP Kind = 31925
KindHandlerRecommendation Kind = 31989
KindHandlerInformation Kind = 31990
KindVideoEvent Kind = 34235
KindShortVideoEvent Kind = 34236
KindVideoViewEvent Kind = 34237
KindCommunityDefinition Kind = 34550
)
func (kind Kind) IsRegular() bool {
+78 -20
View File
@@ -39,10 +39,11 @@ func ParseGroupAddress(raw string) (GroupAddress, error) {
type Group struct {
Address GroupAddress
Name string
Picture string
About string
Members map[nostr.PubKey][]*Role
Name string
Picture string
About string
Members map[nostr.PubKey][]*Role
LiveKitParticipants map[nostr.PubKey]string
// indicates that only members can read group messages
Private bool
@@ -57,7 +58,7 @@ type Group struct {
Hidden bool
// indicates that the group supports audio/video live chat
Livekit bool
LiveKit bool
// indicates which event kinds this group supports
SupportedKinds []nostr.Kind
@@ -65,10 +66,11 @@ type Group struct {
Roles []*Role
InviteCodes []string
LastMetadataUpdate nostr.Timestamp
LastAdminsUpdate nostr.Timestamp
LastMembersUpdate nostr.Timestamp
LastRolesUpdate nostr.Timestamp
LastMetadataUpdate nostr.Timestamp
LastAdminsUpdate nostr.Timestamp
LastMembersUpdate nostr.Timestamp
LastRolesUpdate nostr.Timestamp
LastLiveKitParticipantsUpdate nostr.Timestamp
}
func (group Group) String() string {
@@ -90,9 +92,9 @@ func (group Group) String() string {
maybeClosed = " closed"
}
maybeLivekit := ""
if group.Livekit {
maybeLivekit = " livekit"
maybeLiveKit := ""
if group.LiveKit {
maybeLiveKit = " livekit"
}
members := make([]string, len(group.Members))
@@ -120,7 +122,7 @@ func (group Group) String() string {
maybeRestricted,
maybeHidden,
maybeClosed,
maybeLivekit,
maybeLiveKit,
group.Picture,
group.About,
strings.Join(members, " "),
@@ -135,9 +137,10 @@ func NewGroup(gadstr string) (Group, error) {
}
return Group{
Address: gad,
Name: gad.ID,
Members: make(map[nostr.PubKey][]*Role),
Address: gad,
Name: gad.ID,
Members: make(map[nostr.PubKey][]*Role),
LiveKitParticipants: make(map[nostr.PubKey]string),
}, nil
}
@@ -147,8 +150,9 @@ func NewGroupFromMetadataEvent(relayURL string, evt *nostr.Event) (Group, error)
Relay: relayURL,
ID: evt.Tags.GetD(),
},
Name: evt.Tags.GetD(),
Members: make(map[nostr.PubKey][]*Role),
Name: evt.Tags.GetD(),
Members: make(map[nostr.PubKey][]*Role),
LiveKitParticipants: make(map[nostr.PubKey]string),
}
err := g.MergeInMetadataEvent(evt)
@@ -186,7 +190,7 @@ func (group Group) ToMetadataEvent() nostr.Event {
if group.Closed {
evt.Tags = append(evt.Tags, nostr.Tag{"closed"})
}
if group.Livekit {
if group.LiveKit {
evt.Tags = append(evt.Tags, nostr.Tag{"livekit"})
}
@@ -261,6 +265,27 @@ func (group Group) ToRolesEvent() nostr.Event {
return evt
}
func (group Group) ToLiveKitParticipantsEvent() nostr.Event {
evt := nostr.Event{
Kind: nostr.KindSimpleGroupLiveKitParticipants,
CreatedAt: group.LastLiveKitParticipantsUpdate,
Tags: make(nostr.Tags, 1, 1+len(group.LiveKitParticipants)),
}
evt.Tags[0] = nostr.Tag{"d", group.Address.ID}
for member, identity := range group.LiveKitParticipants {
tag := nostr.Tag{"participant", member.Hex()}
if identity != "" {
tag = append(tag, identity)
} else {
tag = append(tag, "")
}
evt.Tags = append(evt.Tags, tag)
}
return evt
}
func (group *Group) MergeInMetadataEvent(evt *nostr.Event) error {
if evt.Kind != nostr.KindSimpleGroupMetadata {
return fmt.Errorf("expected kind %d, got %d", nostr.KindSimpleGroupMetadata, evt.Kind)
@@ -284,7 +309,7 @@ func (group *Group) MergeInMetadataEvent(evt *nostr.Event) error {
case "hidden":
group.Hidden = true
case "livekit":
group.Livekit = true
group.LiveKit = true
case "supported_kinds":
kinds := make([]nostr.Kind, 0, len(tag)-1)
for _, raw := range tag[1:] {
@@ -408,3 +433,36 @@ func (group *Group) MergeInRolesEvent(evt *nostr.Event) error {
return nil
}
func (group *Group) MergeInLiveKitParticipantsEvent(evt *nostr.Event) error {
if evt.Kind != nostr.KindSimpleGroupLiveKitParticipants {
return fmt.Errorf("expected kind %d, got %d", nostr.KindSimpleGroupLiveKitParticipants, evt.Kind)
}
if evt.CreatedAt < group.LastLiveKitParticipantsUpdate {
return fmt.Errorf("event is older than our last update (%d vs %d)", evt.CreatedAt, group.LastLiveKitParticipantsUpdate)
}
group.LastLiveKitParticipantsUpdate = evt.CreatedAt
group.LiveKitParticipants = make(map[nostr.PubKey]string)
for _, tag := range evt.Tags {
if len(tag) < 2 {
continue
}
if tag[0] != "participant" {
continue
}
member, err := nostr.PubKeyFromHex(tag[1])
if err != nil {
continue
}
identity := ""
if len(tag) >= 3 {
identity = tag[2]
}
group.LiveKitParticipants[member] = identity
}
return nil
}
+9 -6
View File
@@ -124,10 +124,10 @@ var moderationActionFactories = map[nostr.Kind]func(nostr.Event) (Action, error)
edit.PrivateValue = &n
ok = true
case "livekit":
edit.LivekitValue = &y
edit.LiveKitValue = &y
ok = true
case "no-livekit":
edit.LivekitValue = &n
edit.LiveKitValue = &n
ok = true
}
}
@@ -244,7 +244,7 @@ type EditMetadata struct {
ClosedValue *bool
HiddenValue *bool
PrivateValue *bool
LivekitValue *bool
LiveKitValue *bool
When nostr.Timestamp
}
@@ -272,8 +272,8 @@ func (a EditMetadata) Apply(group *Group) {
if a.PrivateValue != nil {
group.Private = *a.PrivateValue
}
if a.LivekitValue != nil {
group.Livekit = *a.LivekitValue
if a.LiveKitValue != nil {
group.LiveKit = *a.LiveKitValue
}
}
@@ -287,6 +287,7 @@ func (a CreateGroup) Apply(group *Group) {
group.LastMetadataUpdate = a.When
group.LastAdminsUpdate = a.When
group.LastMembersUpdate = a.When
group.LastLiveKitParticipantsUpdate = a.When
}
type DeleteGroup struct {
@@ -296,6 +297,7 @@ type DeleteGroup struct {
func (_ DeleteGroup) Name() string { return "delete-group" }
func (a DeleteGroup) Apply(group *Group) {
group.Members = make(map[nostr.PubKey][]*Role)
group.LiveKitParticipants = make(map[nostr.PubKey]string)
group.Closed = true
group.Private = true
group.Restricted = true
@@ -303,10 +305,11 @@ func (a DeleteGroup) Apply(group *Group) {
group.Name = "[deleted]"
group.About = ""
group.Picture = ""
group.Livekit = false
group.LiveKit = false
group.LastMetadataUpdate = a.When
group.LastAdminsUpdate = a.When
group.LastMembersUpdate = a.When
group.LastLiveKitParticipantsUpdate = a.When
}
type CreateInvite struct {
+1
View File
@@ -28,6 +28,7 @@ var MetadataEventKinds = KindRange{
nostr.KindSimpleGroupAdmins,
nostr.KindSimpleGroupMembers,
nostr.KindSimpleGroupRoles,
nostr.KindSimpleGroupLiveKitParticipants,
}
func (kr KindRange) Includes(kind nostr.Kind) bool {