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