sdk finally compiles.

This commit is contained in:
fiatjaf
2025-04-17 00:36:33 -03:00
parent a7be696243
commit 0130725321
15 changed files with 141 additions and 149 deletions

View File

@@ -21,16 +21,16 @@ const (
)
func (sys *System) initializeAddressableDataloaders() {
sys.addressableLoaders = make([]*dataloader.Loader[nostr.PubKey, []*nostr.Event], 4)
sys.addressableLoaders = make([]*dataloader.Loader[nostr.PubKey, []nostr.Event], 4)
sys.addressableLoaders[kind_30000] = sys.createAddressableDataloader(30000)
sys.addressableLoaders[kind_30002] = sys.createAddressableDataloader(30002)
sys.addressableLoaders[kind_30015] = sys.createAddressableDataloader(30015)
sys.addressableLoaders[kind_30030] = sys.createAddressableDataloader(30030)
}
func (sys *System) createAddressableDataloader(kind uint16) *dataloader.Loader[nostr.PubKey, []*nostr.Event] {
func (sys *System) createAddressableDataloader(kind uint16) *dataloader.Loader[nostr.PubKey, []nostr.Event] {
return dataloader.NewBatchedLoader(
func(ctxs []context.Context, pubkeys []nostr.PubKey) map[nostr.PubKey]dataloader.Result[[]*nostr.Event] {
func(ctxs []context.Context, pubkeys []nostr.PubKey) map[nostr.PubKey]dataloader.Result[[]nostr.Event] {
return sys.batchLoadAddressableEvents(ctxs, kind, pubkeys)
},
dataloader.Options{
@@ -44,9 +44,9 @@ func (sys *System) batchLoadAddressableEvents(
ctxs []context.Context,
kind uint16,
pubkeys []nostr.PubKey,
) map[nostr.PubKey]dataloader.Result[[]*nostr.Event] {
) map[nostr.PubKey]dataloader.Result[[]nostr.Event] {
batchSize := len(pubkeys)
results := make(map[nostr.PubKey]dataloader.Result[[]*nostr.Event], batchSize)
results := make(map[nostr.PubKey]dataloader.Result[[]nostr.Event], batchSize)
relayFilter := make([]nostr.DirectedFilter, 0, max(3, batchSize*2))
relayFilterIndex := make(map[string]int, max(3, batchSize*2))
@@ -103,7 +103,9 @@ func (sys *System) batchLoadAddressableEvents(
wg.Wait()
// query all relays with the prepared filters
multiSubs := sys.Pool.BatchedSubManyEose(aggregatedContext, relayFilter)
multiSubs := sys.Pool.BatchedSubManyEose(aggregatedContext, relayFilter, nostr.SubscriptionOptions{
Label: "loadaddrs",
})
nextEvent:
for {
select {
@@ -115,7 +117,7 @@ nextEvent:
events := results[ie.PubKey].Data
if events == nil {
// no events found, so just add this and end
results[ie.PubKey] = dataloader.Result[[]*nostr.Event]{Data: []*nostr.Event{ie.Event}}
results[ie.PubKey] = dataloader.Result[[]nostr.Event]{Data: []nostr.Event{ie.Event}}
continue nextEvent
}
@@ -136,7 +138,7 @@ nextEvent:
}
events = append(events, ie.Event)
results[ie.PubKey] = dataloader.Result[[]*nostr.Event]{Data: events}
results[ie.PubKey] = dataloader.Result[[]nostr.Event]{Data: events}
case <-aggregatedContext.Done():
return results
}

View File

@@ -105,9 +105,9 @@ func (sys *System) FetchFeedPage(
kinds []uint16,
until nostr.Timestamp,
totalLimit int,
) ([]*nostr.Event, error) {
) ([]nostr.Event, error) {
limitPerKey := PerQueryLimitInBatch(totalLimit, len(pubkeys))
events := make([]*nostr.Event, 0, len(pubkeys)*limitPerKey)
events := make([]nostr.Event, 0, len(pubkeys)*limitPerKey)
wg := sync.WaitGroup{}
wg.Add(len(pubkeys))
@@ -153,8 +153,10 @@ func (sys *System) FetchFeedPage(
fUntil := oldestTimestamp + 1
filter.Until = &fUntil
filter.Since = nil
for ie := range sys.Pool.FetchMany(ctx, relays, filter, nostr.WithLabel("feedpage")) {
sys.StoreRelay.Publish(ctx, *ie.Event)
for ie := range sys.Pool.FetchMany(ctx, relays, filter, nostr.SubscriptionOptions{
Label: "feedpage",
}) {
sys.Publisher.Publish(ctx, ie.Event)
// we shouldn't need this check here, but against rogue relays we'll do it
if ie.Event.CreatedAt < oldestTimestamp {
@@ -173,7 +175,7 @@ func (sys *System) FetchFeedPage(
}
wg.Wait()
slices.SortFunc(events, nostr.CompareEventPtrReverse)
slices.SortFunc(events, nostr.CompareEventReverse)
return events, nil
}

View File

@@ -2,7 +2,6 @@ package sdk
import (
"context"
"encoding/hex"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/nip05"
@@ -12,18 +11,15 @@ import (
// InputToProfile turns any npub/nprofile/hex/nip05 input into a ProfilePointer (or nil).
func InputToProfile(ctx context.Context, input string) *nostr.ProfilePointer {
// handle if it is a hex string
if len(input) == 64 {
if _, err := hex.DecodeString(input); err == nil {
return &nostr.ProfilePointer{PublicKey: input}
}
if pk, err := nostr.PubKeyFromHex(input); err == nil {
return &nostr.ProfilePointer{PublicKey: pk}
}
// handle nip19 codes, if that's the case
prefix, data, _ := nip19.Decode(input)
switch prefix {
case "npub":
input = data.(string)
return &nostr.ProfilePointer{PublicKey: input}
return &nostr.ProfilePointer{PublicKey: data.(nostr.PubKey)}
case "nprofile":
pp := data.(nostr.ProfilePointer)
return &pp
@@ -41,19 +37,15 @@ func InputToProfile(ctx context.Context, input string) *nostr.ProfilePointer {
// InputToEventPointer turns any note/nevent/hex input into a EventPointer (or nil).
func InputToEventPointer(input string) *nostr.EventPointer {
// handle if it is a hex string
if len(input) == 64 {
if _, err := hex.DecodeString(input); err == nil {
return &nostr.EventPointer{ID: input}
}
if id, err := nostr.IDFromHex(input); err == nil {
return &nostr.EventPointer{ID: id}
}
// handle nip19 codes, if that's the case
prefix, data, _ := nip19.Decode(input)
switch prefix {
case "note":
if input, ok := data.(string); ok {
return &nostr.EventPointer{ID: input}
}
return &nostr.EventPointer{ID: data.([32]byte)}
case "nevent":
if ep, ok := data.(nostr.EventPointer); ok {
return &ep

View File

@@ -10,15 +10,15 @@ import (
"fiatjaf.com/nostr/sdk/cache"
)
type GenericList[I TagItemWithValue] struct {
type GenericList[V comparable, I TagItemWithValue[V]] struct {
PubKey nostr.PubKey `json:"-"` // must always be set otherwise things will break
Event *nostr.Event `json:"-"` // may be empty if a contact list event wasn't found
Items []I
}
type TagItemWithValue interface {
Value() any
type TagItemWithValue[V comparable] interface {
Value() V
}
var (
@@ -26,15 +26,15 @@ var (
valueWasJustCached = [60]bool{}
)
func fetchGenericList[I TagItemWithValue](
func fetchGenericList[V comparable, I TagItemWithValue[V]](
sys *System,
ctx context.Context,
pubkey nostr.PubKey,
actualKind uint16,
replaceableIndex replaceableIndex,
parseTag func(nostr.Tag) (I, bool),
cache cache.Cache32[GenericList[I]],
) (fl GenericList[I], fromInternal bool) {
cache cache.Cache32[GenericList[V, I]],
) (fl GenericList[V, I], fromInternal bool) {
// we have 60 mutexes, so we can load up to 60 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
@@ -55,13 +55,12 @@ func fetchGenericList[I TagItemWithValue](
return v, true
}
v := GenericList[I]{PubKey: pubkey}
v := GenericList[V, I]{PubKey: pubkey}
events, _ := sys.StoreRelay.QuerySync(ctx, nostr.Filter{Kinds: []uint16{actualKind}, Authors: []nostr.PubKey{pubkey}})
if len(events) != 0 {
for evt := range sys.Store.QueryEvents(nostr.Filter{Kinds: []uint16{actualKind}, Authors: []nostr.PubKey{pubkey}}) {
// ok, we found something locally
items := parseItemsFromEventTags(events[0], parseTag)
v.Event = events[0]
items := parseItemsFromEventTags(evt, parseTag)
v.Event = &evt
v.Items = items
// but if we haven't tried fetching from the network recently we should do it
@@ -100,30 +99,30 @@ func fetchGenericList[I TagItemWithValue](
return v, false
}
func tryFetchListFromNetwork[I TagItemWithValue](
func tryFetchListFromNetwork[V comparable, I TagItemWithValue[V]](
ctx context.Context,
sys *System,
pubkey nostr.PubKey,
replaceableIndex replaceableIndex,
parseTag func(nostr.Tag) (I, bool),
) *GenericList[I] {
) *GenericList[V, I] {
evt, err := sys.replaceableLoaders[replaceableIndex].Load(ctx, pubkey)
if err != nil {
return nil
}
v := &GenericList[I]{
v := &GenericList[V, I]{
PubKey: pubkey,
Event: evt,
Event: &evt,
Items: parseItemsFromEventTags(evt, parseTag),
}
sys.StoreRelay.Publish(ctx, *evt)
sys.Publisher.Publish(ctx, evt)
return v
}
func parseItemsFromEventTags[I TagItemWithValue](
evt *nostr.Event,
func parseItemsFromEventTags[V comparable, I TagItemWithValue[V]](
evt nostr.Event,
parseTag func(nostr.Tag) (I, bool),
) []I {
result := make([]I, 0, len(evt.Tags))

View File

@@ -11,18 +11,18 @@ 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] {
func (sys *System) FetchBookmarkList(ctx context.Context, pubkey nostr.PubKey) GenericList[string, EventRef] {
if sys.BookmarkListCache == nil {
sys.BookmarkListCache = cache_memory.New[GenericList[EventRef]](1000)
sys.BookmarkListCache = cache_memory.New[GenericList[string, EventRef]](1000)
}
ml, _ := fetchGenericList(sys, ctx, pubkey, 10003, kind_10003, parseEventRef, sys.BookmarkListCache)
return ml
}
func (sys *System) FetchPinList(ctx context.Context, pubkey string) GenericList[EventRef] {
func (sys *System) FetchPinList(ctx context.Context, pubkey nostr.PubKey) GenericList[string, EventRef] {
if sys.PinListCache == nil {
sys.PinListCache = cache_memory.New[GenericList[EventRef]](1000)
sys.PinListCache = cache_memory.New[GenericList[string, EventRef]](1000)
}
ml, _ := fetchGenericList(sys, ctx, pubkey, 10001, kind_10001, parseEventRef, sys.PinListCache)

View File

@@ -17,27 +17,27 @@ type ProfileRef struct {
func (f ProfileRef) Value() nostr.PubKey { return f.Pubkey }
func (sys *System) FetchFollowList(ctx context.Context, pubkey nostr.PubKey) GenericList[ProfileRef] {
func (sys *System) FetchFollowList(ctx context.Context, pubkey nostr.PubKey) GenericList[nostr.PubKey, ProfileRef] {
if sys.FollowListCache == nil {
sys.FollowListCache = cache_memory.New[GenericList[ProfileRef]](1000)
sys.FollowListCache = cache_memory.New[GenericList[nostr.PubKey, ProfileRef]](1000)
}
fl, _ := fetchGenericList(sys, ctx, pubkey, 3, kind_3, parseProfileRef, sys.FollowListCache)
return fl
}
func (sys *System) FetchMuteList(ctx context.Context, pubkey nostr.PubKey) GenericList[ProfileRef] {
func (sys *System) FetchMuteList(ctx context.Context, pubkey nostr.PubKey) GenericList[nostr.PubKey, ProfileRef] {
if sys.MuteListCache == nil {
sys.MuteListCache = cache_memory.New[GenericList[ProfileRef]](1000)
sys.MuteListCache = cache_memory.New[GenericList[nostr.PubKey, ProfileRef]](1000)
}
ml, _ := fetchGenericList(sys, ctx, pubkey, 10000, kind_10000, parseProfileRef, sys.MuteListCache)
return ml
}
func (sys *System) FetchFollowSets(ctx context.Context, pubkey nostr.PubKey) GenericSets[ProfileRef] {
func (sys *System) FetchFollowSets(ctx context.Context, pubkey nostr.PubKey) GenericSets[nostr.PubKey, ProfileRef] {
if sys.FollowSetsCache == nil {
sys.FollowSetsCache = cache_memory.New[GenericSets[ProfileRef]](1000)
sys.FollowSetsCache = cache_memory.New[GenericSets[nostr.PubKey, ProfileRef]](1000)
}
ml, _ := fetchGenericSets(sys, ctx, pubkey, 30000, kind_30000, parseProfileRef, sys.FollowSetsCache)
@@ -52,11 +52,10 @@ func parseProfileRef(tag nostr.Tag) (fw ProfileRef, ok bool) {
return fw, false
}
pubkey, err := nostr.PubKeyFromHex(fw.Pubkey)
pubkey, err := nostr.PubKeyFromHex(tag[1])
if err != nil {
return fw, false
}
fw.Pubkey = pubkey
if len(tag) > 2 {

View File

@@ -19,32 +19,32 @@ type RelayURL string
func (r RelayURL) Value() string { return string(r) }
func (sys *System) FetchRelayList(ctx context.Context, pubkey nostr.PubKey) GenericList[Relay] {
func (sys *System) FetchRelayList(ctx context.Context, pubkey nostr.PubKey) GenericList[string, Relay] {
ml, _ := fetchGenericList(sys, ctx, pubkey, 10002, kind_10002, parseRelayFromKind10002, sys.RelayListCache)
return ml
}
func (sys *System) FetchBlockedRelayList(ctx context.Context, pubkey nostr.PubKey) GenericList[RelayURL] {
func (sys *System) FetchBlockedRelayList(ctx context.Context, pubkey nostr.PubKey) GenericList[string, RelayURL] {
if sys.BlockedRelayListCache == nil {
sys.BlockedRelayListCache = cache_memory.New[GenericList[RelayURL]](1000)
sys.BlockedRelayListCache = cache_memory.New[GenericList[string, RelayURL]](1000)
}
ml, _ := fetchGenericList(sys, ctx, pubkey, 10006, kind_10006, parseRelayURL, sys.BlockedRelayListCache)
return ml
}
func (sys *System) FetchSearchRelayList(ctx context.Context, pubkey nostr.PubKey) GenericList[RelayURL] {
func (sys *System) FetchSearchRelayList(ctx context.Context, pubkey nostr.PubKey) GenericList[string, RelayURL] {
if sys.SearchRelayListCache == nil {
sys.SearchRelayListCache = cache_memory.New[GenericList[RelayURL]](1000)
sys.SearchRelayListCache = cache_memory.New[GenericList[string, RelayURL]](1000)
}
ml, _ := fetchGenericList(sys, ctx, pubkey, 10007, kind_10007, parseRelayURL, sys.SearchRelayListCache)
return ml
}
func (sys *System) FetchRelaySets(ctx context.Context, pubkey nostr.PubKey) GenericSets[RelayURL] {
func (sys *System) FetchRelaySets(ctx context.Context, pubkey nostr.PubKey) GenericSets[string, RelayURL] {
if sys.RelaySetsCache == nil {
sys.RelaySetsCache = cache_memory.New[GenericSets[RelayURL]](1000)
sys.RelaySetsCache = cache_memory.New[GenericSets[string, RelayURL]](1000)
}
ml, _ := fetchGenericSets(sys, ctx, pubkey, 30002, kind_30002, parseRelayURL, sys.RelaySetsCache)

View File

@@ -11,18 +11,18 @@ type Topic string
func (r Topic) Value() string { return string(r) }
func (sys *System) FetchTopicList(ctx context.Context, pubkey nostr.PubKey) GenericList[Topic] {
func (sys *System) FetchTopicList(ctx context.Context, pubkey nostr.PubKey) GenericList[string, Topic] {
if sys.TopicListCache == nil {
sys.TopicListCache = cache_memory.New[GenericList[Topic]](1000)
sys.TopicListCache = cache_memory.New[GenericList[string, Topic]](1000)
}
ml, _ := fetchGenericList(sys, ctx, pubkey, 10015, kind_10015, parseTopicString, sys.TopicListCache)
return ml
}
func (sys *System) FetchTopicSets(ctx context.Context, pubkey nostr.PubKey) GenericSets[Topic] {
func (sys *System) FetchTopicSets(ctx context.Context, pubkey nostr.PubKey) GenericSets[string, Topic] {
if sys.TopicSetsCache == nil {
sys.TopicSetsCache = cache_memory.New[GenericSets[Topic]](1000)
sys.TopicSetsCache = cache_memory.New[GenericSets[string, Topic]](1000)
}
ml, _ := fetchGenericSets(sys, ctx, pubkey, 30015, kind_30015, parseTopicString, sys.TopicSetsCache)

View File

@@ -110,12 +110,11 @@ func (sys *System) FetchProfileMetadata(ctx context.Context, pubkey nostr.PubKey
pm.PubKey = pubkey
res, _ := sys.StoreRelay.QuerySync(ctx, nostr.Filter{Kinds: []int{0}, Authors: []string{pubkey}})
if len(res) != 0 {
for evt := range sys.Store.QueryEvents(nostr.Filter{Kinds: []uint16{0}, Authors: []nostr.PubKey{pubkey}}) {
// ok, we found something locally
pm, _ = ParseMetadata(res[0])
pm, _ = ParseMetadata(evt)
pm.PubKey = pubkey
pm.Event = res[0]
pm.Event = &evt
// but if we haven't tried fetching from the network recently we should do it
lastFetchKey := makeLastFetchKey(0, pubkey)
@@ -151,7 +150,7 @@ func (sys *System) FetchProfileMetadata(ctx context.Context, pubkey nostr.PubKey
return pm
}
func (sys *System) tryFetchMetadataFromNetwork(ctx context.Context, pubkey string) *ProfileMetadata {
func (sys *System) tryFetchMetadataFromNetwork(ctx context.Context, pubkey nostr.PubKey) *ProfileMetadata {
evt, err := sys.replaceableLoaders[kind_0].Load(ctx, pubkey)
if err != nil {
return nil
@@ -163,15 +162,15 @@ func (sys *System) tryFetchMetadataFromNetwork(ctx context.Context, pubkey strin
}
pm.PubKey = pubkey
pm.Event = evt
sys.StoreRelay.Publish(ctx, *evt)
pm.Event = &evt
sys.Publisher.Publish(ctx, evt)
sys.MetadataCache.SetWithTTL(pubkey, pm, time.Hour*6)
return &pm
}
// ParseMetadata parses a kind 0 event into a ProfileMetadata struct.
// Returns an error if the event is not kind 0 or if the content is not valid JSON.
func ParseMetadata(event *nostr.Event) (meta ProfileMetadata, err error) {
func ParseMetadata(event nostr.Event) (meta ProfileMetadata, err error) {
if event.Kind != 0 {
err = fmt.Errorf("event %s is kind %d, not 0", event.ID, event.Kind)
} else if er := json.Unmarshal([]byte(event.Content), &meta); er != nil {
@@ -183,6 +182,6 @@ func ParseMetadata(event *nostr.Event) (meta ProfileMetadata, err error) {
}
meta.PubKey = event.PubKey
meta.Event = event
meta.Event = &event
return meta, err
}

View File

@@ -49,7 +49,7 @@ func (sys *System) FetchOutboxRelays(ctx context.Context, pubkey nostr.PubKey, n
// FetchWriteRelays just reads relays from a kind:10002, that's the only canonical place where a user reveals
// the relays they intend to receive notifications from.
func (sys *System) FetchInboxRelays(ctx context.Context, pubkey string, n int) []string {
func (sys *System) FetchInboxRelays(ctx context.Context, pubkey nostr.PubKey, n int) []string {
rl := sys.FetchRelayList(ctx, pubkey)
if len(rl.Items) == 0 || len(rl.Items) > 10 {
return []string{"wss://relay.damus.io", "wss://nos.lol"}
@@ -70,7 +70,7 @@ func (sys *System) FetchInboxRelays(ctx context.Context, pubkey string, n int) [
//
// Use FetchWriteRelays when deciding where to publish on behalf of a user, but FetchOutboxRelays when deciding
// from where to read notes authored by other users.
func (sys *System) FetchWriteRelays(ctx context.Context, pubkey string) []string {
func (sys *System) FetchWriteRelays(ctx context.Context, pubkey nostr.PubKey) []string {
rl := sys.FetchRelayList(ctx, pubkey)
relays := make([]string, 0, 7)

View File

@@ -33,7 +33,7 @@ const (
type EventResult dataloader.Result[*nostr.Event]
func (sys *System) initializeReplaceableDataloaders() {
sys.replaceableLoaders = make([]*dataloader.Loader[nostr.PubKey, *nostr.Event], 12)
sys.replaceableLoaders = make([]*dataloader.Loader[nostr.PubKey, nostr.Event], 12)
sys.replaceableLoaders[kind_0] = sys.createReplaceableDataloader(0)
sys.replaceableLoaders[kind_3] = sys.createReplaceableDataloader(3)
sys.replaceableLoaders[kind_10000] = sys.createReplaceableDataloader(10000)
@@ -48,9 +48,9 @@ func (sys *System) initializeReplaceableDataloaders() {
sys.replaceableLoaders[kind_10030] = sys.createReplaceableDataloader(10030)
}
func (sys *System) createReplaceableDataloader(kind uint16) *dataloader.Loader[nostr.PubKey, *nostr.Event] {
func (sys *System) createReplaceableDataloader(kind uint16) *dataloader.Loader[nostr.PubKey, nostr.Event] {
return dataloader.NewBatchedLoader(
func(ctxs []context.Context, pubkeys []nostr.PubKey) map[nostr.PubKey]dataloader.Result[*nostr.Event] {
func(ctxs []context.Context, pubkeys []nostr.PubKey) map[nostr.PubKey]dataloader.Result[nostr.Event] {
return sys.batchLoadReplaceableEvents(ctxs, kind, pubkeys)
},
dataloader.Options{
@@ -64,9 +64,9 @@ func (sys *System) batchLoadReplaceableEvents(
ctxs []context.Context,
kind uint16,
pubkeys []nostr.PubKey,
) map[nostr.PubKey]dataloader.Result[*nostr.Event] {
) map[nostr.PubKey]dataloader.Result[nostr.Event] {
batchSize := len(pubkeys)
results := make(map[nostr.PubKey]dataloader.Result[*nostr.Event], batchSize)
results := make(map[nostr.PubKey]dataloader.Result[nostr.Event], batchSize)
relayFilter := make([]nostr.DirectedFilter, 0, max(3, batchSize*2))
relayFilterIndex := make(map[string]int, max(3, batchSize*2))
@@ -121,9 +121,9 @@ func (sys *System) batchLoadReplaceableEvents(
// query all relays with the prepared filters
wg.Wait()
multiSubs := sys.Pool.BatchedSubManyEose(aggregatedContext, relayFilter,
nostr.WithLabel("repl~"+strconv.Itoa(int(kind))),
)
multiSubs := sys.Pool.BatchedSubManyEose(aggregatedContext, relayFilter, nostr.SubscriptionOptions{
Label: "repl~" + strconv.Itoa(int(kind)),
})
for {
select {
case ie, more := <-multiSubs:
@@ -132,8 +132,8 @@ func (sys *System) batchLoadReplaceableEvents(
}
// insert this event at the desired position
if val, ok := results[ie.PubKey]; !ok || val.Data == nil || val.Data.CreatedAt < ie.CreatedAt {
results[ie.PubKey] = dataloader.Result[*nostr.Event]{Data: ie.Event}
if val, ok := results[ie.PubKey]; !ok || val.Data.ID == nostr.ZeroID || val.Data.CreatedAt < ie.CreatedAt {
results[ie.PubKey] = dataloader.Result[nostr.Event]{Data: ie.Event}
}
case <-aggregatedContext.Done():
return results

View File

@@ -13,7 +13,9 @@ func (sys *System) SearchUsers(ctx context.Context, query string) []ProfileMetad
for ie := range sys.Pool.FetchMany(ctx, sys.UserSearchRelays.URLs, nostr.Filter{
Search: query,
Limit: limit,
}, nostr.WithLabel("search")) {
}, nostr.SubscriptionOptions{
Label: "search",
}) {
m, _ := ParseMetadata(ie.Event)
profiles = append(profiles, m)
}

View File

@@ -11,22 +11,22 @@ import (
// this is similar to list.go and inherits code from that.
type GenericSets[I TagItemWithValue] struct {
type GenericSets[V comparable, I TagItemWithValue[V]] struct {
PubKey nostr.PubKey `json:"-"`
Events []*nostr.Event `json:"-"`
Events []nostr.Event `json:"-"`
Sets map[string][]I
}
func fetchGenericSets[I TagItemWithValue](
func fetchGenericSets[V comparable, I TagItemWithValue[V]](
sys *System,
ctx context.Context,
pubkey nostr.PubKey,
actualKind uint16,
addressableIndex addressableIndex,
parseTag func(nostr.Tag) (I, bool),
cache cache.Cache32[GenericSets[I]],
) (fl GenericSets[I], fromInternal bool) {
cache cache.Cache32[GenericSets[V, I]],
) (fl GenericSets[V, I], fromInternal bool) {
n := pubkey[7]
lockIdx := (uint16(n) + actualKind) % 60
genericListMutexes[lockIdx].Lock()
@@ -44,9 +44,11 @@ func fetchGenericSets[I TagItemWithValue](
return v, true
}
v := GenericSets[I]{PubKey: pubkey}
v := GenericSets[V, I]{PubKey: pubkey}
events, _ := sys.StoreRelay.QuerySync(ctx, nostr.Filter{Kinds: []uint16{actualKind}, Authors: []nostr.PubKey{pubkey}})
events := slices.Collect(
sys.Store.QueryEvents(nostr.Filter{Kinds: []uint16{actualKind}, Authors: []nostr.PubKey{pubkey}}),
)
if len(events) != 0 {
// ok, we found something locally
sets := parseSetsFromEvents(events, parseTag)
@@ -89,31 +91,31 @@ func fetchGenericSets[I TagItemWithValue](
return v, false
}
func tryFetchSetsFromNetwork[I TagItemWithValue](
func tryFetchSetsFromNetwork[V comparable, I TagItemWithValue[V]](
ctx context.Context,
sys *System,
pubkey nostr.PubKey,
addressableIndex addressableIndex,
parseTag func(nostr.Tag) (I, bool),
) *GenericSets[I] {
) *GenericSets[V, I] {
events, err := sys.addressableLoaders[addressableIndex].Load(ctx, pubkey)
if err != nil {
return nil
}
v := &GenericSets[I]{
v := &GenericSets[V, I]{
PubKey: pubkey,
Events: events,
Sets: parseSetsFromEvents(events, parseTag),
}
for _, evt := range events {
sys.StoreRelay.Publish(ctx, *evt)
sys.Publisher.Publish(ctx, evt)
}
return v
}
func parseSetsFromEvents[I TagItemWithValue](
events []*nostr.Event,
func parseSetsFromEvents[V comparable, I TagItemWithValue[V]](
events []nostr.Event,
parseTag func(nostr.Tag) (I, bool),
) map[string][]I {
sets := make(map[string][]I, len(events))

View File

@@ -39,13 +39,13 @@ func (sys *System) FetchSpecificEventFromInput(
case "naddr":
pointer = data.(nostr.EntityPointer)
case "note":
pointer = nostr.EventPointer{ID: data.(string)}
pointer = nostr.EventPointer{ID: data.([32]byte)}
default:
return nil, nil, fmt.Errorf("invalid code '%s'", input)
}
} else {
if nostr.IsValid32ByteHex(input) {
pointer = nostr.EventPointer{ID: input}
if id, err := nostr.IDFromHex(input); err == nil {
pointer = nostr.EventPointer{ID: id}
} else {
return nil, nil, fmt.Errorf("failed to decode '%s': %w", input, err)
}
@@ -66,7 +66,7 @@ func (sys *System) FetchSpecificEvent(
priorityRelays := make([]string, 0, 8)
var filter nostr.Filter
author := ""
var author nostr.PubKey
relays := make([]string, 0, 10)
fallback := make([]string, 0, 10)
successRelays = make([]string, 0, 10)
@@ -74,7 +74,7 @@ func (sys *System) FetchSpecificEvent(
switch v := pointer.(type) {
case nostr.EventPointer:
author = v.Author
filter.IDs = []string{v.ID}
filter.IDs = []nostr.ID{v.ID}
relays = append(relays, v.Relays...)
relays = appendUnique(relays, sys.FallbackRelays.Next())
fallback = append(fallback, sys.JustIDRelays.URLs...)
@@ -82,9 +82,9 @@ func (sys *System) FetchSpecificEvent(
priorityRelays = append(priorityRelays, v.Relays...)
case nostr.EntityPointer:
author = v.PublicKey
filter.Authors = []string{v.PublicKey}
filter.Authors = []nostr.PubKey{v.PublicKey}
filter.Tags = nostr.TagMap{"d": []string{v.Identifier}}
filter.Kinds = []int{v.Kind}
filter.Kinds = []uint16{v.Kind}
relays = append(relays, v.Relays...)
relays = appendUnique(relays, sys.FallbackRelays.Next())
fallback = append(fallback, sys.FallbackRelays.Next(), sys.FallbackRelays.Next())
@@ -93,13 +93,12 @@ func (sys *System) FetchSpecificEvent(
// try to fetch in our internal eventstore first
if !params.SkipLocalStore {
if res, _ := sys.StoreRelay.QuerySync(ctx, filter); len(res) != 0 {
evt := res[0]
return evt, nil, nil
for evt := range sys.Store.QueryEvents(filter) {
return &evt, nil, nil
}
}
if author != "" {
if author != nostr.ZeroPK {
// fetch relays for author
authorRelays := sys.FetchOutboxRelays(ctx, author, 3)
@@ -141,19 +140,16 @@ attempts:
countdown := 6.0
subManyCtx := ctx
for ie := range sys.Pool.FetchMany(
subManyCtx,
attempt.relays,
filter,
nostr.WithLabel(attempt.label),
) {
for ie := range sys.Pool.FetchMany(subManyCtx, attempt.relays, filter, nostr.SubscriptionOptions{
Label: attempt.label,
}) {
fetchProfileOnce.Do(func() {
go sys.FetchProfileMetadata(ctx, ie.PubKey)
})
successRelays = append(successRelays, ie.Relay.URL)
if result == nil || ie.CreatedAt > result.CreatedAt {
result = ie.Event
result = &ie.Event
}
if !attempt.slowWithRelays {
@@ -170,7 +166,7 @@ attempts:
// save stuff in cache and in internal store
if !params.SkipLocalStore {
sys.StoreRelay.Publish(ctx, *result)
sys.Publisher.Publish(ctx, *result)
}
// put priority relays first so they get used in nevent and nprofile

View File

@@ -1,10 +1,11 @@
package sdk
import (
"context"
"math/rand/v2"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/eventstore"
"fiatjaf.com/nostr/eventstore/nullstore"
"fiatjaf.com/nostr/eventstore/wrappers"
"fiatjaf.com/nostr/sdk/cache"
cache_memory "fiatjaf.com/nostr/sdk/cache/memory"
@@ -13,8 +14,6 @@ import (
"fiatjaf.com/nostr/sdk/hints/memoryh"
"fiatjaf.com/nostr/sdk/kvstore"
kvstore_memory "fiatjaf.com/nostr/sdk/kvstore/memory"
"fiatjaf.com/nostr/eventstore"
"fiatjaf.com/nostr/eventstore/nullstore"
)
// System represents the core functionality of the SDK, providing access to
@@ -30,17 +29,17 @@ import (
type System struct {
KVStore kvstore.KVStore
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]]
TopicListCache cache.Cache32[GenericList[Topic]]
RelaySetsCache cache.Cache32[GenericSets[RelayURL]]
FollowSetsCache cache.Cache32[GenericSets[ProfileRef]]
TopicSetsCache cache.Cache32[GenericSets[Topic]]
RelayListCache cache.Cache32[GenericList[string, Relay]]
FollowListCache cache.Cache32[GenericList[nostr.PubKey, ProfileRef]]
MuteListCache cache.Cache32[GenericList[nostr.PubKey, ProfileRef]]
BookmarkListCache cache.Cache32[GenericList[string, EventRef]]
PinListCache cache.Cache32[GenericList[string, EventRef]]
BlockedRelayListCache cache.Cache32[GenericList[string, RelayURL]]
SearchRelayListCache cache.Cache32[GenericList[string, RelayURL]]
TopicListCache cache.Cache32[GenericList[string, Topic]]
RelaySetsCache cache.Cache32[GenericSets[string, RelayURL]]
FollowSetsCache cache.Cache32[GenericSets[nostr.PubKey, ProfileRef]]
TopicSetsCache cache.Cache32[GenericSets[string, Topic]]
Hints hints.HintsDB
Pool *nostr.Pool
RelayListRelays *RelayStream
@@ -54,8 +53,8 @@ type System struct {
Publisher wrappers.StorePublisher
replaceableLoaders []*dataloader.Loader[nostr.PubKey, *nostr.Event]
addressableLoaders []*dataloader.Loader[nostr.PubKey, []*nostr.Event]
replaceableLoaders []*dataloader.Loader[nostr.PubKey, nostr.Event]
addressableLoaders []*dataloader.Loader[nostr.PubKey, []nostr.Event]
}
// SystemModifier is a function that modifies a System instance.
@@ -119,12 +118,12 @@ func NewSystem(mods ...SystemModifier) *System {
Hints: memoryh.NewHintDB(),
}
sys.Pool = nostr.NewPool(context.Background(),
nostr.WithAuthorKindQueryMiddleware(sys.TrackQueryAttempts),
nostr.WithEventMiddleware(sys.TrackEventHintsAndRelays),
nostr.WithDuplicateMiddleware(sys.TrackEventRelaysD),
nostr.WithPenaltyBox(),
)
sys.Pool = nostr.NewPool(nostr.PoolOptions{
AuthorKindQueryMiddleware: sys.TrackQueryAttempts,
EventMiddleware: sys.TrackEventHintsAndRelays,
DuplicateMiddleware: sys.TrackEventRelaysD,
PenaltyBox: true,
})
for _, mod := range mods {
mod(sys)
@@ -134,14 +133,14 @@ func NewSystem(mods ...SystemModifier) *System {
sys.MetadataCache = cache_memory.New[ProfileMetadata](8000)
}
if sys.RelayListCache == nil {
sys.RelayListCache = cache_memory.New[GenericList[Relay]](8000)
sys.RelayListCache = cache_memory.New[GenericList[string, Relay]](8000)
}
if sys.Store == nil {
sys.Store = &nullstore.NullStore{}
sys.Store.Init()
}
sys.StoreRelay = eventstore.RelayWrapper{Store: sys.Store}
sys.Publisher = wrappers.StorePublisher{Store: sys.Store}
sys.initializeReplaceableDataloaders()
sys.initializeAddressableDataloaders()
@@ -223,14 +222,14 @@ func WithStore(store eventstore.Store) SystemModifier {
}
// WithRelayListCache returns a SystemModifier that sets the RelayListCache.
func WithRelayListCache(cache cache.Cache32[GenericList[Relay]]) SystemModifier {
func WithRelayListCache(cache cache.Cache32[GenericList[string, Relay]]) SystemModifier {
return func(sys *System) {
sys.RelayListCache = cache
}
}
// WithFollowListCache returns a SystemModifier that sets the FollowListCache.
func WithFollowListCache(cache cache.Cache32[GenericList[ProfileRef]]) SystemModifier {
func WithFollowListCache(cache cache.Cache32[GenericList[nostr.PubKey, ProfileRef]]) SystemModifier {
return func(sys *System) {
sys.FollowListCache = cache
}