sdk finally compiles.
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
12
sdk/feeds.go
12
sdk/feeds.go
@@ -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
|
||||
}
|
||||
|
||||
20
sdk/input.go
20
sdk/input.go
@@ -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
|
||||
|
||||
35
sdk/list.go
35
sdk/list.go
@@ -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))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
30
sdk/set.go
30
sdk/set.go
@@ -11,22 +11,22 @@ import (
|
||||
|
||||
// this is similar to list.go and inherits code from that.
|
||||
|
||||
type GenericSets[I TagItemWithValue] struct {
|
||||
PubKey nostr.PubKey `json:"-"`
|
||||
Events []*nostr.Event `json:"-"`
|
||||
type GenericSets[V comparable, I TagItemWithValue[V]] struct {
|
||||
PubKey nostr.PubKey `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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user