more list fetchers.

This commit is contained in:
fiatjaf 2025-01-01 18:16:36 -03:00
parent 159e5d21e6
commit c2c08ab6bc
11 changed files with 218 additions and 99 deletions

View File

@ -30,7 +30,8 @@ func fetchGenericList[I TagItemWithValue](
sys *System,
ctx context.Context,
pubkey string,
kind int,
actualKind int,
replaceableIndex replaceableIndex,
parseTag func(nostr.Tag) (I, bool),
cache cache.Cache32[GenericList[I]],
skipFetch bool,
@ -38,7 +39,7 @@ func fetchGenericList[I TagItemWithValue](
// we have 24 mutexes, so we can load up to 24 lists at the same time, but if we do the same exact
// call that will do it only once, the subsequent ones will wait for a result to be cached
// and then return it from cache -- 13 is an arbitrary index for the pubkey
lockIdx := (int(pubkey[13]) + kind) % 24
lockIdx := (int(pubkey[13]) + int(replaceableIndex)) % 24
genericListMutexes[lockIdx].Lock()
if valueWasJustCached[lockIdx] {
@ -56,7 +57,7 @@ func fetchGenericList[I TagItemWithValue](
v := GenericList[I]{PubKey: pubkey}
events, _ := sys.StoreRelay.QuerySync(ctx, nostr.Filter{Kinds: []int{kind}, Authors: []string{pubkey}})
events, _ := sys.StoreRelay.QuerySync(ctx, nostr.Filter{Kinds: []int{actualKind}, Authors: []string{pubkey}})
if len(events) != 0 {
items := parseItemsFromEventTags(events[0], parseTag)
v.Event = events[0]
@ -67,7 +68,7 @@ func fetchGenericList[I TagItemWithValue](
}
if !skipFetch {
thunk := sys.replaceableLoaders[kind].Load(ctx, pubkey)
thunk := sys.replaceableLoaders[replaceableIndex].Load(ctx, pubkey)
evt, err := thunk()
if err == nil {
items := parseItemsFromEventTags(evt, parseTag)

67
sdk/lists_event.go Normal file
View File

@ -0,0 +1,67 @@
package sdk
import (
"context"
"strconv"
"strings"
"github.com/nbd-wtf/go-nostr"
)
type EventRef struct{ nostr.Pointer }
func (e EventRef) Value() string { return e.Pointer.AsTagReference() }
func (sys *System) FetchBookmarkList(ctx context.Context, pubkey string) GenericList[EventRef] {
ml, _ := fetchGenericList(sys, ctx, pubkey, 10003, kind_10003, parseEventRef, sys.BookmarkListCache, false)
return ml
}
func (sys *System) FetchPinList(ctx context.Context, pubkey string) GenericList[EventRef] {
ml, _ := fetchGenericList(sys, ctx, pubkey, 10001, kind_10001, parseEventRef, sys.PinListCache, false)
return ml
}
func parseEventRef(tag nostr.Tag) (evr EventRef, ok bool) {
if len(tag) < 2 {
return evr, false
}
switch tag[0] {
case "e":
if !nostr.IsValid32ByteHex(tag[1]) {
return evr, false
}
pointer := nostr.EventPointer{
ID: tag[1],
}
if len(tag) >= 3 {
pointer.Relays = []string{nostr.NormalizeURL(tag[2])}
if len(tag) >= 4 {
pointer.Author = tag[3]
}
}
evr.Pointer = pointer
case "a":
spl := strings.SplitN(tag[1], ":", 3)
if len(spl) != 3 || !nostr.IsValidPublicKey(spl[1]) {
return evr, false
}
pointer := nostr.EntityPointer{
PublicKey: spl[1],
Identifier: spl[2],
}
if kind, err := strconv.Atoi(spl[0]); err != nil {
return evr, false
} else {
pointer.Kind = kind
}
if len(tag) >= 3 {
pointer.Relays = []string{nostr.NormalizeURL(tag[2])}
}
evr.Pointer = pointer
default:
return evr, false
}
return evr, false
}

View File

@ -8,22 +8,25 @@ import (
"github.com/nbd-wtf/go-nostr"
)
type FollowList = GenericList[Follow]
type Follow struct {
type ProfileRef struct {
Pubkey string
Relay string
Petname string
}
func (f Follow) Value() string { return f.Pubkey }
func (f ProfileRef) Value() string { return f.Pubkey }
func (sys *System) FetchFollowList(ctx context.Context, pubkey string) FollowList {
fl, _ := fetchGenericList(sys, ctx, pubkey, 3, parseFollow, sys.FollowListCache, false)
func (sys *System) FetchFollowList(ctx context.Context, pubkey string) GenericList[ProfileRef] {
fl, _ := fetchGenericList(sys, ctx, pubkey, 3, kind_3, parseProfileRef, sys.FollowListCache, false)
return fl
}
func parseFollow(tag nostr.Tag) (fw Follow, ok bool) {
func (sys *System) FetchMuteList(ctx context.Context, pubkey string) GenericList[ProfileRef] {
ml, _ := fetchGenericList(sys, ctx, pubkey, 10000, kind_10000, parseProfileRef, sys.MuteListCache, false)
return ml
}
func parseProfileRef(tag nostr.Tag) (fw ProfileRef, ok bool) {
if len(tag) < 2 {
return fw, false
}

72
sdk/lists_relay.go Normal file
View File

@ -0,0 +1,72 @@
package sdk
import (
"context"
"github.com/nbd-wtf/go-nostr"
)
type Relay struct {
URL string
Inbox bool
Outbox bool
}
func (r Relay) Value() string { return r.URL }
type RelayURL string
func (r RelayURL) Value() string { return string(r) }
func (sys *System) FetchRelayList(ctx context.Context, pubkey string) GenericList[Relay] {
ml, _ := fetchGenericList(sys, ctx, pubkey, 10002, kind_10002, parseRelayFromKind10002, sys.RelayListCache, false)
return ml
}
func (sys *System) FetchBlockedRelayList(ctx context.Context, pubkey string) GenericList[RelayURL] {
ml, _ := fetchGenericList(sys, ctx, pubkey, 10006, kind_10006, parseRelayURL, sys.BlockedRelayListCache, false)
return ml
}
func (sys *System) FetchSearchRelayList(ctx context.Context, pubkey string) GenericList[RelayURL] {
ml, _ := fetchGenericList(sys, ctx, pubkey, 10007, kind_10007, parseRelayURL, sys.SearchRelayListCache, false)
return ml
}
func parseRelayFromKind10002(tag nostr.Tag) (rl Relay, ok bool) {
if u := tag.Value(); u != "" && tag[0] == "r" {
if !nostr.IsValidRelayURL(u) {
return rl, false
}
u := nostr.NormalizeURL(u)
relay := Relay{
URL: u,
}
if len(tag) == 2 {
relay.Inbox = true
relay.Outbox = true
} else if tag[2] == "write" {
relay.Outbox = true
} else if tag[2] == "read" {
relay.Inbox = true
}
return relay, true
}
return rl, false
}
func parseRelayURL(tag nostr.Tag) (rl RelayURL, ok bool) {
if u := tag.Value(); u != "" && tag[0] == "relay" {
if !nostr.IsValidRelayURL(u) {
return rl, false
}
u := nostr.NormalizeURL(u)
return RelayURL(u), true
}
return rl, false
}

View File

@ -117,7 +117,7 @@ func (sys *System) FetchProfileMetadata(ctx context.Context, pubkey string) (pm
pm.PubKey = pubkey
thunk0 := sys.replaceableLoaders[0].Load(ctx, pubkey)
thunk0 := sys.replaceableLoaders[kind_0].Load(ctx, pubkey)
evt, err := thunk0()
if err == nil {
pm, _ = ParseMetadata(evt)

View File

@ -1,10 +0,0 @@
package sdk
import "context"
type MuteList = GenericList[Follow]
func (sys *System) FetchMuteList(ctx context.Context, pubkey string) MuteList {
ml, _ := fetchGenericList(sys, ctx, pubkey, 10000, parseFollow, sys.MuteListCache, false)
return ml
}

View File

@ -18,7 +18,7 @@ func (sys *System) FetchOutboxRelays(ctx context.Context, pubkey string, n int)
}
// if we have it cached that means we have at least tried to fetch recently and it won't be tried again
fetchGenericList(sys, ctx, pubkey, 10002, parseRelayFromKind10002, sys.RelayListCache, false)
fetchGenericList(sys, ctx, pubkey, 10002, kind_10002, parseRelayFromKind10002, sys.RelayListCache, false)
relays := sys.Hints.TopN(pubkey, 6)
if len(relays) == 0 {

View File

@ -1,41 +0,0 @@
package sdk
import (
"github.com/nbd-wtf/go-nostr"
)
type RelayList = GenericList[Relay]
type Relay struct {
URL string
Inbox bool
Outbox bool
}
func (r Relay) Value() string { return r.URL }
func parseRelayFromKind10002(tag nostr.Tag) (rl Relay, ok bool) {
if u := tag.Value(); u != "" && tag[0] == "r" {
if !nostr.IsValidRelayURL(u) {
return rl, false
}
u := nostr.NormalizeURL(u)
relay := Relay{
URL: u,
}
if len(tag) == 2 {
relay.Inbox = true
relay.Outbox = true
} else if tag[2] == "write" {
relay.Outbox = true
} else if tag[2] == "read" {
relay.Inbox = true
}
return relay, true
}
return rl, false
}

View File

@ -12,13 +12,38 @@ import (
"github.com/nbd-wtf/go-nostr"
)
type replaceableIndex int
const (
kind_0 replaceableIndex = 0
kind_3 replaceableIndex = 1
kind_10000 replaceableIndex = 2
kind_10001 replaceableIndex = 3
kind_10002 replaceableIndex = 4
kind_10003 replaceableIndex = 5
kind_10004 replaceableIndex = 6
kind_10005 replaceableIndex = 7
kind_10006 replaceableIndex = 8
kind_10007 replaceableIndex = 9
kind_10015 replaceableIndex = 10
kind_10030 replaceableIndex = 11
)
type EventResult dataloader.Result[*nostr.Event]
func (sys *System) initializeDataloaders() {
sys.replaceableLoaders = make(map[int]*dataloader.Loader[string, *nostr.Event])
for _, kind := range []int{0, 3, 10000, 10001, 10002, 10003, 10004, 10005, 10006, 10007, 10015, 10030} {
sys.replaceableLoaders[kind] = sys.createReplaceableDataloader(kind)
}
sys.replaceableLoaders[kind_0] = sys.createReplaceableDataloader(0)
sys.replaceableLoaders[kind_3] = sys.createReplaceableDataloader(3)
sys.replaceableLoaders[kind_10000] = sys.createReplaceableDataloader(10000)
sys.replaceableLoaders[kind_10001] = sys.createReplaceableDataloader(10001)
sys.replaceableLoaders[kind_10002] = sys.createReplaceableDataloader(10002)
sys.replaceableLoaders[kind_10003] = sys.createReplaceableDataloader(10003)
sys.replaceableLoaders[kind_10004] = sys.createReplaceableDataloader(10004)
sys.replaceableLoaders[kind_10005] = sys.createReplaceableDataloader(10005)
sys.replaceableLoaders[kind_10006] = sys.createReplaceableDataloader(10006)
sys.replaceableLoaders[kind_10007] = sys.createReplaceableDataloader(10007)
sys.replaceableLoaders[kind_10015] = sys.createReplaceableDataloader(10015)
sys.replaceableLoaders[kind_10030] = sys.createReplaceableDataloader(10030)
}
func (sys *System) createReplaceableDataloader(kind int) *dataloader.Loader[string, *nostr.Event] {

View File

@ -15,24 +15,28 @@ import (
)
type System struct {
RelayListCache cache.Cache32[RelayList]
FollowListCache cache.Cache32[FollowList]
MuteListCache cache.Cache32[FollowList]
MetadataCache cache.Cache32[ProfileMetadata]
Hints hints.HintsDB
Pool *nostr.SimplePool
RelayListRelays *RelayStream
FollowListRelays *RelayStream
MetadataRelays *RelayStream
FallbackRelays *RelayStream
JustIDRelays *RelayStream
UserSearchRelays *RelayStream
NoteSearchRelays *RelayStream
Store eventstore.Store
MetadataCache cache.Cache32[ProfileMetadata]
RelayListCache cache.Cache32[GenericList[Relay]]
FollowListCache cache.Cache32[GenericList[ProfileRef]]
MuteListCache cache.Cache32[GenericList[ProfileRef]]
BookmarkListCache cache.Cache32[GenericList[EventRef]]
PinListCache cache.Cache32[GenericList[EventRef]]
BlockedRelayListCache cache.Cache32[GenericList[RelayURL]]
SearchRelayListCache cache.Cache32[GenericList[RelayURL]]
Hints hints.HintsDB
Pool *nostr.SimplePool
RelayListRelays *RelayStream
FollowListRelays *RelayStream
MetadataRelays *RelayStream
FallbackRelays *RelayStream
JustIDRelays *RelayStream
UserSearchRelays *RelayStream
NoteSearchRelays *RelayStream
Store eventstore.Store
StoreRelay nostr.RelayStore
replaceableLoaders map[int]*dataloader.Loader[string, *nostr.Event]
replaceableLoaders []*dataloader.Loader[string, *nostr.Event]
outboxShortTermCache cache.Cache32[[]string]
}
@ -54,13 +58,17 @@ func (rs *RelayStream) Next() string {
func NewSystem(mods ...SystemModifier) *System {
sys := &System{
RelayListCache: cache_memory.New32[RelayList](1000),
FollowListCache: cache_memory.New32[FollowList](1000),
MuteListCache: cache_memory.New32[FollowList](1000),
MetadataCache: cache_memory.New32[ProfileMetadata](1000),
RelayListRelays: NewRelayStream("wss://purplepag.es", "wss://user.kindpag.es", "wss://relay.nos.social"),
FollowListRelays: NewRelayStream("wss://purplepag.es", "wss://user.kindpag.es", "wss://relay.nos.social"),
MetadataRelays: NewRelayStream("wss://purplepag.es", "wss://user.kindpag.es", "wss://relay.nos.social"),
MetadataCache: cache_memory.New32[ProfileMetadata](8000),
RelayListCache: cache_memory.New32[GenericList[Relay]](8000),
FollowListCache: cache_memory.New32[GenericList[ProfileRef]](1000),
MuteListCache: cache_memory.New32[GenericList[ProfileRef]](1000),
BookmarkListCache: cache_memory.New32[GenericList[EventRef]](1000),
PinListCache: cache_memory.New32[GenericList[EventRef]](1000),
BlockedRelayListCache: cache_memory.New32[GenericList[RelayURL]](1000),
SearchRelayListCache: cache_memory.New32[GenericList[RelayURL]](1000),
RelayListRelays: NewRelayStream("wss://purplepag.es", "wss://user.kindpag.es", "wss://relay.nos.social"),
FollowListRelays: NewRelayStream("wss://purplepag.es", "wss://user.kindpag.es", "wss://relay.nos.social"),
MetadataRelays: NewRelayStream("wss://purplepag.es", "wss://user.kindpag.es", "wss://relay.nos.social"),
FallbackRelays: NewRelayStream(
"wss://relay.damus.io",
"wss://nostr.mom",
@ -165,24 +173,18 @@ func WithStore(store eventstore.Store) SystemModifier {
}
}
func WithRelayListCache(cache cache.Cache32[RelayList]) SystemModifier {
func WithRelayListCache(cache cache.Cache32[GenericList[Relay]]) SystemModifier {
return func(sys *System) {
sys.RelayListCache = cache
}
}
func WithFollowListCache(cache cache.Cache32[FollowList]) SystemModifier {
func WithFollowListCache(cache cache.Cache32[GenericList[ProfileRef]]) SystemModifier {
return func(sys *System) {
sys.FollowListCache = cache
}
}
func WithMuteListCache(cache cache.Cache32[FollowList]) SystemModifier {
return func(sys *System) {
sys.MuteListCache = cache
}
}
func WithMetadataCache(cache cache.Cache32[ProfileMetadata]) SystemModifier {
return func(sys *System) {
sys.MetadataCache = cache

View File

@ -81,7 +81,7 @@ func (sys *System) TrackEventHints(ie nostr.RelayEvent) {
}
}
for _, ref := range ParseReferences(ie.Event) {
for ref := range ParseReferences(*ie.Event) {
if ref.Profile != nil {
for _, relay := range ref.Profile.Relays {
if IsVirtualRelay(relay) {