sdk: FetchSpecificEvent()
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
package sdk
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
"github.com/nbd-wtf/go-nostr/nip19"
|
||||
)
|
||||
|
||||
// FetchSpecificEvent tries to get a specific event from a NIP-19 code using whatever means necessary.
|
||||
func (sys *System) FetchSpecificEvent(
|
||||
ctx context.Context,
|
||||
code string,
|
||||
withRelays bool,
|
||||
) (event *nostr.Event, successRelays []string, err error) {
|
||||
// this is for deciding what relays will go on nevent and nprofile later
|
||||
priorityRelays := make([]string, 0, 8)
|
||||
|
||||
prefix, data, err := nip19.Decode(code)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to decode %w", err)
|
||||
}
|
||||
|
||||
author := ""
|
||||
|
||||
var filter nostr.Filter
|
||||
relays := make([]string, 0, 10)
|
||||
fallback := make([]string, 0, 10)
|
||||
successRelays = make([]string, 0, 10)
|
||||
|
||||
switch v := data.(type) {
|
||||
case nostr.EventPointer:
|
||||
author = v.Author
|
||||
filter.IDs = []string{v.ID}
|
||||
relays = append(relays, v.Relays...)
|
||||
relays = appendUnique(relays, sys.FallbackRelays.Next())
|
||||
fallback = append(fallback, sys.JustIDRelays.URLs...)
|
||||
fallback = appendUnique(fallback, sys.FallbackRelays.Next())
|
||||
for _, r := range v.Relays {
|
||||
priorityRelays = append(priorityRelays, r)
|
||||
}
|
||||
case nostr.EntityPointer:
|
||||
author = v.PublicKey
|
||||
filter.Authors = []string{v.PublicKey}
|
||||
filter.Tags = nostr.TagMap{"d": []string{v.Identifier}}
|
||||
filter.Kinds = []int{v.Kind}
|
||||
relays = append(relays, v.Relays...)
|
||||
relays = appendUnique(relays, sys.FallbackRelays.Next())
|
||||
fallback = append(fallback, sys.FallbackRelays.Next(), sys.FallbackRelays.Next())
|
||||
case string:
|
||||
if prefix == "note" {
|
||||
filter.IDs = []string{v}
|
||||
relays = append(relays, sys.JustIDRelays.Next(), sys.JustIDRelays.Next())
|
||||
fallback = appendUnique(fallback,
|
||||
sys.FallbackRelays.Next(), sys.JustIDRelays.Next(), sys.FallbackRelays.Next())
|
||||
}
|
||||
}
|
||||
|
||||
// try to fetch in our internal eventstore first
|
||||
if res, _ := sys.StoreRelay.QuerySync(ctx, filter); len(res) != 0 {
|
||||
evt := res[0]
|
||||
return evt, nil, nil
|
||||
}
|
||||
|
||||
if author != "" {
|
||||
// fetch relays for author
|
||||
authorRelays := sys.FetchOutboxRelays(ctx, author, 3)
|
||||
relays = appendUnique(relays, authorRelays...)
|
||||
priorityRelays = appendUnique(priorityRelays, authorRelays...)
|
||||
}
|
||||
|
||||
var result *nostr.Event
|
||||
|
||||
for _, attempt := range []struct {
|
||||
label string
|
||||
relays []string
|
||||
nonUnique bool
|
||||
}{
|
||||
{
|
||||
label: "fetch-" + prefix,
|
||||
relays: relays,
|
||||
// set this to true if the caller wants relays, so we won't return immediately
|
||||
// but will instead wait a little while to see if more relays respond
|
||||
nonUnique: withRelays,
|
||||
},
|
||||
{
|
||||
label: "fetchf-" + prefix,
|
||||
relays: fallback,
|
||||
nonUnique: false,
|
||||
},
|
||||
} {
|
||||
// actually fetch the event here
|
||||
countdown := 6.0
|
||||
subManyCtx := ctx
|
||||
|
||||
if attempt.nonUnique {
|
||||
// keep track of where we have actually found the event so we can show that
|
||||
var cancel context.CancelFunc
|
||||
subManyCtx, cancel = context.WithTimeout(ctx, time.Second*6)
|
||||
defer cancel()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
if countdown <= 0 {
|
||||
cancel()
|
||||
break
|
||||
}
|
||||
countdown -= 0.1
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
fetchProfileOnce := sync.Once{}
|
||||
|
||||
for ie := range sys.Pool.SubManyEoseNonUnique(
|
||||
subManyCtx,
|
||||
attempt.relays,
|
||||
nostr.Filters{filter},
|
||||
nostr.WithLabel(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
|
||||
}
|
||||
countdown = min(countdown-0.5, 1)
|
||||
}
|
||||
}
|
||||
|
||||
if result == nil {
|
||||
return nil, nil, fmt.Errorf("couldn't find this %s", prefix)
|
||||
}
|
||||
|
||||
// save stuff in cache and in internal store
|
||||
sys.StoreRelay.Publish(ctx, *result)
|
||||
|
||||
// put priority relays first so they get used in nevent and nprofile
|
||||
slices.SortFunc(successRelays, func(a, b string) int {
|
||||
vpa := slices.Contains(priorityRelays, a)
|
||||
vpb := slices.Contains(priorityRelays, b)
|
||||
if vpa == vpb {
|
||||
return 1
|
||||
}
|
||||
if vpa && !vpb {
|
||||
return 1
|
||||
}
|
||||
return -1
|
||||
})
|
||||
|
||||
return result, successRelays, nil
|
||||
}
|
||||
Reference in New Issue
Block a user