mirror of
https://github.com/nbd-wtf/go-nostr.git
synced 2025-03-17 21:32:56 +01:00
sdk: FetchSpecificEvent()
This commit is contained in:
parent
f08e4e9af7
commit
36c197af42
@ -1,12 +1,18 @@
|
||||
package sdk
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"slices"
|
||||
"time"
|
||||
)
|
||||
|
||||
var serial = 0
|
||||
|
||||
func pickNext(list []string) string {
|
||||
serial++
|
||||
return list[serial%len(list)]
|
||||
func appendUnique[I comparable](arr []I, item ...I) []I {
|
||||
for _, item := range item {
|
||||
if slices.Contains(arr, item) {
|
||||
return arr
|
||||
}
|
||||
arr = append(arr, item)
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
func doThisNotMoreThanOnceAnHour(key string) (doItNow bool) {
|
||||
|
@ -136,13 +136,13 @@ func (sys *System) determineRelaysToQuery(ctx context.Context, pubkey string, ki
|
||||
var next string
|
||||
switch kind {
|
||||
case 0:
|
||||
next = pickNext(sys.MetadataRelays)
|
||||
next = sys.MetadataRelays.Next()
|
||||
case 3:
|
||||
next = pickNext(sys.FollowListRelays)
|
||||
next = sys.FollowListRelays.Next()
|
||||
case 10002:
|
||||
next = pickNext(sys.RelayListRelays)
|
||||
next = sys.RelayListRelays.Next()
|
||||
default:
|
||||
next = pickNext(sys.FallbackRelays)
|
||||
next = sys.FallbackRelays.Next()
|
||||
}
|
||||
|
||||
if !slices.Contains(relays, next) {
|
||||
|
@ -8,9 +8,9 @@ import (
|
||||
|
||||
func (sys *System) SearchUsers(ctx context.Context, query string) []ProfileMetadata {
|
||||
limit := 10
|
||||
profiles := make([]ProfileMetadata, 0, limit*len(sys.UserSearchRelays))
|
||||
profiles := make([]ProfileMetadata, 0, limit*len(sys.UserSearchRelays.URLs))
|
||||
|
||||
for ie := range sys.Pool.SubManyEose(ctx, sys.UserSearchRelays, nostr.Filters{
|
||||
for ie := range sys.Pool.SubManyEose(ctx, sys.UserSearchRelays.URLs, nostr.Filters{
|
||||
{
|
||||
Search: query,
|
||||
Limit: limit,
|
||||
|
159
sdk/specific_event.go
Normal file
159
sdk/specific_event.go
Normal file
@ -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
|
||||
}
|
@ -19,12 +19,13 @@ type System struct {
|
||||
MetadataCache cache.Cache32[ProfileMetadata]
|
||||
Hints hints.HintsDB
|
||||
Pool *nostr.SimplePool
|
||||
RelayListRelays []string
|
||||
FollowListRelays []string
|
||||
MetadataRelays []string
|
||||
FallbackRelays []string
|
||||
UserSearchRelays []string
|
||||
NoteSearchRelays []string
|
||||
RelayListRelays *RelayStream
|
||||
FollowListRelays *RelayStream
|
||||
MetadataRelays *RelayStream
|
||||
FallbackRelays *RelayStream
|
||||
JustIDRelays *RelayStream
|
||||
UserSearchRelays *RelayStream
|
||||
NoteSearchRelays *RelayStream
|
||||
Store eventstore.Store
|
||||
|
||||
StoreRelay nostr.RelayStore
|
||||
@ -35,35 +36,50 @@ type System struct {
|
||||
|
||||
type SystemModifier func(sys *System)
|
||||
|
||||
type RelayStream struct {
|
||||
URLs []string
|
||||
serial int
|
||||
}
|
||||
|
||||
func NewRelayStream(urls ...string) *RelayStream {
|
||||
return &RelayStream{URLs: urls, serial: -1}
|
||||
}
|
||||
|
||||
func (rs *RelayStream) Next() string {
|
||||
rs.serial++
|
||||
return rs.URLs[rs.serial%len(rs.URLs)]
|
||||
}
|
||||
|
||||
func NewSystem(mods ...SystemModifier) *System {
|
||||
sys := &System{
|
||||
RelayListCache: cache_memory.New32[RelayList](1000),
|
||||
FollowListCache: cache_memory.New32[FollowList](1000),
|
||||
MetadataCache: cache_memory.New32[ProfileMetadata](1000),
|
||||
RelayListRelays: []string{"wss://purplepag.es", "wss://user.kindpag.es", "wss://relay.nos.social"},
|
||||
FollowListRelays: []string{"wss://purplepag.es", "wss://user.kindpag.es", "wss://relay.nos.social"},
|
||||
MetadataRelays: []string{"wss://purplepag.es", "wss://user.kindpag.es", "wss://relay.nos.social"},
|
||||
FallbackRelays: []string{
|
||||
"wss://relay.primal.net",
|
||||
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.wine",
|
||||
"wss://nostr.mom",
|
||||
"wss://offchain.pub",
|
||||
"wss://nos.lol",
|
||||
"wss://mostr.pub",
|
||||
"wss://relay.nostr.band",
|
||||
"wss://nostr21.com",
|
||||
},
|
||||
UserSearchRelays: []string{
|
||||
),
|
||||
JustIDRelays: NewRelayStream(
|
||||
"wss://cache2.primal.net/v1",
|
||||
"wss://relay.noswhere.com",
|
||||
"wss://relay.nostr.band",
|
||||
),
|
||||
UserSearchRelays: NewRelayStream(
|
||||
"wss://search.nos.today",
|
||||
"wss://nostr.wine",
|
||||
"wss://relay.nostr.band",
|
||||
"wss://relay.noswhere.com",
|
||||
},
|
||||
NoteSearchRelays: []string{
|
||||
),
|
||||
NoteSearchRelays: NewRelayStream(
|
||||
"wss://nostr.wine",
|
||||
"wss://relay.nostr.band",
|
||||
"wss://relay.noswhere.com",
|
||||
},
|
||||
"wss://search.nos.today",
|
||||
),
|
||||
Hints: memory_hints.NewHintDB(),
|
||||
|
||||
outboxShortTermCache: cache_memory.New32[[]string](1000),
|
||||
@ -99,37 +115,43 @@ func WithHintsDB(hdb hints.HintsDB) SystemModifier {
|
||||
|
||||
func WithRelayListRelays(list []string) SystemModifier {
|
||||
return func(sys *System) {
|
||||
sys.RelayListRelays = list
|
||||
sys.RelayListRelays.URLs = list
|
||||
}
|
||||
}
|
||||
|
||||
func WithMetadataRelays(list []string) SystemModifier {
|
||||
return func(sys *System) {
|
||||
sys.MetadataRelays = list
|
||||
sys.MetadataRelays.URLs = list
|
||||
}
|
||||
}
|
||||
|
||||
func WithFollowListRelays(list []string) SystemModifier {
|
||||
return func(sys *System) {
|
||||
sys.FollowListRelays = list
|
||||
sys.FollowListRelays.URLs = list
|
||||
}
|
||||
}
|
||||
|
||||
func WithFallbackRelays(list []string) SystemModifier {
|
||||
return func(sys *System) {
|
||||
sys.FallbackRelays = list
|
||||
sys.FallbackRelays.URLs = list
|
||||
}
|
||||
}
|
||||
|
||||
func WithJustIDRelays(list []string) SystemModifier {
|
||||
return func(sys *System) {
|
||||
sys.JustIDRelays.URLs = list
|
||||
}
|
||||
}
|
||||
|
||||
func WithUserSearchRelays(list []string) SystemModifier {
|
||||
return func(sys *System) {
|
||||
sys.UserSearchRelays = list
|
||||
sys.UserSearchRelays.URLs = list
|
||||
}
|
||||
}
|
||||
|
||||
func WithNoteSearchRelays(list []string) SystemModifier {
|
||||
return func(sys *System) {
|
||||
sys.NoteSearchRelays = list
|
||||
sys.NoteSearchRelays.URLs = list
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user