mirror of
https://github.com/nbd-wtf/go-nostr.git
synced 2025-08-27 14:22:20 +02:00
docstrings for many functions.
This commit is contained in:
@@ -10,6 +10,8 @@ import (
|
||||
|
||||
const eventRelayPrefix = byte('r')
|
||||
|
||||
// makeEventRelayKey creates a key for storing event relay information.
|
||||
// It uses the first 8 bytes of the event ID to create a compact key.
|
||||
func makeEventRelayKey(eventID []byte) []byte {
|
||||
// format: 'r' + first 8 bytes of event ID
|
||||
key := make([]byte, 9)
|
||||
@@ -18,6 +20,8 @@ func makeEventRelayKey(eventID []byte) []byte {
|
||||
return key
|
||||
}
|
||||
|
||||
// encodeRelayList serializes a list of relay URLs into a compact binary format.
|
||||
// Each relay URL is prefixed with its length as a single byte.
|
||||
func encodeRelayList(relays []string) []byte {
|
||||
totalSize := 0
|
||||
for _, relay := range relays {
|
||||
@@ -43,6 +47,8 @@ func encodeRelayList(relays []string) []byte {
|
||||
return buf
|
||||
}
|
||||
|
||||
// decodeRelayList deserializes a binary-encoded list of relay URLs.
|
||||
// It expects each relay URL to be prefixed with its length as a single byte.
|
||||
func decodeRelayList(data []byte) []string {
|
||||
relays := make([]string, 0, 6)
|
||||
offset := 0
|
||||
@@ -67,6 +73,8 @@ func decodeRelayList(data []byte) []string {
|
||||
return relays
|
||||
}
|
||||
|
||||
// trackEventRelay records that an event was seen on a particular relay.
|
||||
// If onlyIfItExists is true, it will only update existing records and not create new ones.
|
||||
func (sys *System) trackEventRelay(eventID string, relay string, onlyIfItExists bool) {
|
||||
// decode the event ID hex into bytes
|
||||
idBytes, err := hex.DecodeString(eventID)
|
||||
@@ -101,7 +109,8 @@ func (sys *System) trackEventRelay(eventID string, relay string, onlyIfItExists
|
||||
})
|
||||
}
|
||||
|
||||
// GetEventRelays returns all known relay URLs that have been seen to carry the given event.
|
||||
// GetEventRelays returns all known relay URLs an event is known to be available on.
|
||||
// It is based on information kept on KVStore.
|
||||
func (sys *System) GetEventRelays(eventID string) ([]string, error) {
|
||||
// decode the event ID hex into bytes
|
||||
idBytes, err := hex.DecodeString(eventID)
|
||||
|
@@ -9,6 +9,8 @@ import (
|
||||
|
||||
var json = jsoniter.ConfigFastest
|
||||
|
||||
// appendUnique adds items to an array only if they don't already exist in the array.
|
||||
// Returns the modified array.
|
||||
func appendUnique[I comparable](arr []I, item ...I) []I {
|
||||
for _, item := range item {
|
||||
if slices.Contains(arr, item) {
|
||||
@@ -19,10 +21,19 @@ func appendUnique[I comparable](arr []I, item ...I) []I {
|
||||
return arr
|
||||
}
|
||||
|
||||
// doThisNotMoreThanOnceAnHour checks if an operation with the given key
|
||||
// has been performed in the last hour. If not, it returns true and records
|
||||
// the operation to prevent it from running again within the hour.
|
||||
func doThisNotMoreThanOnceAnHour(key string) (doItNow bool) {
|
||||
_dtnmtoahLock.Lock()
|
||||
defer _dtnmtoahLock.Unlock()
|
||||
|
||||
if _dtnmtoah == nil {
|
||||
// this runs only once for the lifetime of this library and
|
||||
// starts a long-running process of checking for expired items
|
||||
// and deleting them from this map every 10 minutes.
|
||||
_dtnmtoah = make(map[string]time.Time)
|
||||
go func() {
|
||||
_dtnmtoah = make(map[string]time.Time)
|
||||
for {
|
||||
time.Sleep(time.Minute * 10)
|
||||
_dtnmtoahLock.Lock()
|
||||
@@ -37,9 +48,11 @@ func doThisNotMoreThanOnceAnHour(key string) (doItNow bool) {
|
||||
}()
|
||||
}
|
||||
|
||||
_dtnmtoahLock.Lock()
|
||||
defer _dtnmtoahLock.Unlock()
|
||||
_, hasBeenPerformedInTheLastHour := _dtnmtoah[key]
|
||||
if hasBeenPerformedInTheLastHour {
|
||||
return false
|
||||
}
|
||||
|
||||
_, exists := _dtnmtoah[key]
|
||||
return !exists
|
||||
_dtnmtoah[key] = time.Now()
|
||||
return true
|
||||
}
|
||||
|
@@ -11,6 +11,8 @@ import (
|
||||
"github.com/nbd-wtf/go-nostr/sdk/hints"
|
||||
)
|
||||
|
||||
// ProfileMetadata represents user profile information from kind 0 events.
|
||||
// It contains both the raw event and parsed metadata fields.
|
||||
type ProfileMetadata struct {
|
||||
PubKey string `json:"-"` // must always be set otherwise things will break
|
||||
Event *nostr.Event `json:"-"` // may be empty if a profile metadata event wasn't found
|
||||
@@ -29,21 +31,28 @@ type ProfileMetadata struct {
|
||||
nip05LastAttempt time.Time
|
||||
}
|
||||
|
||||
// Npub returns the NIP-19 npub encoding of the profile's public key.
|
||||
func (p ProfileMetadata) Npub() string {
|
||||
v, _ := nip19.EncodePublicKey(p.PubKey)
|
||||
return v
|
||||
}
|
||||
|
||||
// NpubShort returns a shortened version of the NIP-19 npub encoding,
|
||||
// showing only the first 7 and last 5 characters.
|
||||
func (p ProfileMetadata) NpubShort() string {
|
||||
npub := p.Npub()
|
||||
return npub[0:7] + "…" + npub[58:]
|
||||
}
|
||||
|
||||
// Nprofile returns the NIP-19 nprofile encoding of the profile,
|
||||
// including relay hints from the user's outbox.
|
||||
func (p ProfileMetadata) Nprofile(ctx context.Context, sys *System, nrelays int) string {
|
||||
v, _ := nip19.EncodeProfile(p.PubKey, sys.FetchOutboxRelays(ctx, p.PubKey, 2))
|
||||
return v
|
||||
}
|
||||
|
||||
// ShortName returns the best available name for display purposes.
|
||||
// It tries Name, then DisplayName, and falls back to a shortened npub.
|
||||
func (p ProfileMetadata) ShortName() string {
|
||||
if p.Name != "" {
|
||||
return p.Name
|
||||
@@ -54,6 +63,7 @@ func (p ProfileMetadata) ShortName() string {
|
||||
return p.NpubShort()
|
||||
}
|
||||
|
||||
// NIP05Valid checks if the profile's NIP-05 identifier is valid.
|
||||
func (p *ProfileMetadata) NIP05Valid(ctx context.Context) bool {
|
||||
if p.NIP05 == "" {
|
||||
return false
|
||||
@@ -74,7 +84,8 @@ func (p *ProfileMetadata) NIP05Valid(ctx context.Context) bool {
|
||||
}
|
||||
|
||||
// FetchProfileFromInput takes an nprofile, npub, nip05 or hex pubkey and returns a ProfileMetadata,
|
||||
// updating the hintsDB in the process with any eventual relay hints
|
||||
// updating the hintsDB in the process with any eventual relay hints.
|
||||
// Returns an error if the profile reference couldn't be decoded.
|
||||
func (sys System) FetchProfileFromInput(ctx context.Context, nip19OrNip05Code string) (ProfileMetadata, error) {
|
||||
p := InputToProfile(ctx, nip19OrNip05Code)
|
||||
if p == nil {
|
||||
@@ -93,6 +104,7 @@ func (sys System) FetchProfileFromInput(ctx context.Context, nip19OrNip05Code st
|
||||
|
||||
// FetchProfileMetadata fetches metadata for a given user from the local cache, or from the local store,
|
||||
// or, failing these, from the target user's defined outbox relays -- then caches the result.
|
||||
// It always returns a ProfileMetadata, even if no metadata was found (in which case only the PubKey field is set).
|
||||
func (sys *System) FetchProfileMetadata(ctx context.Context, pubkey string) (pm ProfileMetadata) {
|
||||
if v, ok := sys.MetadataCache.Get(pubkey); ok {
|
||||
return v
|
||||
@@ -160,6 +172,8 @@ func (sys *System) tryFetchMetadataFromNetwork(ctx context.Context, pubkey strin
|
||||
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) {
|
||||
if event.Kind != 0 {
|
||||
err = fmt.Errorf("event %s is kind %d, not 0", event.ID, event.Kind)
|
||||
|
@@ -11,12 +11,19 @@ import (
|
||||
"github.com/nbd-wtf/go-nostr/sdk/hints"
|
||||
)
|
||||
|
||||
// FetchSpecificEventParameters contains options for fetching specific events.
|
||||
type FetchSpecificEventParameters struct {
|
||||
WithRelays bool
|
||||
// WithRelays indicates whether to include relay information in the response
|
||||
// (this causes the request to take longer as it will wait for all relays to respond).
|
||||
WithRelays bool
|
||||
|
||||
// SkipLocalStore indicates whether to skip checking the local store for the event
|
||||
// and storing the result in the local store.
|
||||
SkipLocalStore bool
|
||||
}
|
||||
|
||||
// FetchSpecificEventFromInput tries to get a specific event from a NIP-19 code using whatever means necessary.
|
||||
// FetchSpecificEventFromInput tries to get a specific event from a NIP-19 code or event ID.
|
||||
// It supports nevent, naddr, and note NIP-19 codes, as well as raw event IDs.
|
||||
func (sys *System) FetchSpecificEventFromInput(
|
||||
ctx context.Context,
|
||||
input string,
|
||||
@@ -47,7 +54,9 @@ func (sys *System) FetchSpecificEventFromInput(
|
||||
return sys.FetchSpecificEvent(ctx, pointer, params)
|
||||
}
|
||||
|
||||
// FetchSpecificEvent tries to get a specific event from a NIP-19 code using whatever means necessary.
|
||||
// FetchSpecificEvent tries to get a specific event using a Pointer (EventPointer or EntityPointer).
|
||||
// It first checks the local store, then queries relays associated with the event or author,
|
||||
// and finally falls back to general-purpose relays.
|
||||
func (sys *System) FetchSpecificEvent(
|
||||
ctx context.Context,
|
||||
pointer nostr.Pointer,
|
||||
|
@@ -16,6 +16,16 @@ import (
|
||||
kvstore_memory "github.com/nbd-wtf/go-nostr/sdk/kvstore/memory"
|
||||
)
|
||||
|
||||
// System represents the core functionality of the SDK, providing access to
|
||||
// various caches, relays, and dataloaders for efficient Nostr operations.
|
||||
//
|
||||
// Usually an application should have a single global instance of this and use
|
||||
// its internal Pool for all its operations.
|
||||
//
|
||||
// Store, KVStore and Hints are databases that should generally be persisted
|
||||
// for any application that is intended to be executed more than once. By
|
||||
// default they're set to in-memory stores, but ideally persisteable
|
||||
// implementations should be given (some alternatives are provided in subpackages).
|
||||
type System struct {
|
||||
KVStore kvstore.KVStore
|
||||
MetadataCache cache.Cache32[ProfileMetadata]
|
||||
@@ -47,22 +57,35 @@ type System struct {
|
||||
addressableLoaders []*dataloader.Loader[string, []*nostr.Event]
|
||||
}
|
||||
|
||||
// SystemModifier is a function that modifies a System instance.
|
||||
// It's used with NewSystem to configure the system during creation.
|
||||
type SystemModifier func(sys *System)
|
||||
|
||||
// RelayStream provides a rotating list of relay URLs.
|
||||
// It's used to distribute requests across multiple relays.
|
||||
type RelayStream struct {
|
||||
URLs []string
|
||||
serial int
|
||||
}
|
||||
|
||||
// NewRelayStream creates a new RelayStream with the provided URLs.
|
||||
func NewRelayStream(urls ...string) *RelayStream {
|
||||
return &RelayStream{URLs: urls, serial: rand.Int()}
|
||||
}
|
||||
|
||||
// Next returns the next URL in the rotation.
|
||||
func (rs *RelayStream) Next() string {
|
||||
rs.serial++
|
||||
return rs.URLs[rs.serial%len(rs.URLs)]
|
||||
}
|
||||
|
||||
// NewSystem creates a new System with default configuration,
|
||||
// which can be customized using the provided modifiers.
|
||||
//
|
||||
// The list of provided With* modifiers isn't exhaustive and
|
||||
// most internal fields of System can be modified after the System
|
||||
// creation -- and in many cases one or another of these will have
|
||||
// to be modified, so don't be afraid of doing that.
|
||||
func NewSystem(mods ...SystemModifier) *System {
|
||||
sys := &System{
|
||||
KVStore: kvstore_memory.NewStore(),
|
||||
@@ -123,84 +146,101 @@ func NewSystem(mods ...SystemModifier) *System {
|
||||
return sys
|
||||
}
|
||||
|
||||
// Close releases resources held by the System.
|
||||
func (sys *System) Close() {
|
||||
if sys.KVStore != nil {
|
||||
sys.KVStore.Close()
|
||||
}
|
||||
if sys.Pool != nil {
|
||||
sys.Pool.Close("sdk.System closed")
|
||||
}
|
||||
}
|
||||
|
||||
// WithHintsDB returns a SystemModifier that sets the HintsDB.
|
||||
func WithHintsDB(hdb hints.HintsDB) SystemModifier {
|
||||
return func(sys *System) {
|
||||
sys.Hints = hdb
|
||||
}
|
||||
}
|
||||
|
||||
// WithRelayListRelays returns a SystemModifier that sets the RelayListRelays.
|
||||
func WithRelayListRelays(list []string) SystemModifier {
|
||||
return func(sys *System) {
|
||||
sys.RelayListRelays.URLs = list
|
||||
}
|
||||
}
|
||||
|
||||
// WithMetadataRelays returns a SystemModifier that sets the MetadataRelays.
|
||||
func WithMetadataRelays(list []string) SystemModifier {
|
||||
return func(sys *System) {
|
||||
sys.MetadataRelays.URLs = list
|
||||
}
|
||||
}
|
||||
|
||||
// WithFollowListRelays returns a SystemModifier that sets the FollowListRelays.
|
||||
func WithFollowListRelays(list []string) SystemModifier {
|
||||
return func(sys *System) {
|
||||
sys.FollowListRelays.URLs = list
|
||||
}
|
||||
}
|
||||
|
||||
// WithFallbackRelays returns a SystemModifier that sets the FallbackRelays.
|
||||
func WithFallbackRelays(list []string) SystemModifier {
|
||||
return func(sys *System) {
|
||||
sys.FallbackRelays.URLs = list
|
||||
}
|
||||
}
|
||||
|
||||
// WithJustIDRelays returns a SystemModifier that sets the JustIDRelays.
|
||||
func WithJustIDRelays(list []string) SystemModifier {
|
||||
return func(sys *System) {
|
||||
sys.JustIDRelays.URLs = list
|
||||
}
|
||||
}
|
||||
|
||||
// WithUserSearchRelays returns a SystemModifier that sets the UserSearchRelays.
|
||||
func WithUserSearchRelays(list []string) SystemModifier {
|
||||
return func(sys *System) {
|
||||
sys.UserSearchRelays.URLs = list
|
||||
}
|
||||
}
|
||||
|
||||
// WithNoteSearchRelays returns a SystemModifier that sets the NoteSearchRelays.
|
||||
func WithNoteSearchRelays(list []string) SystemModifier {
|
||||
return func(sys *System) {
|
||||
sys.NoteSearchRelays.URLs = list
|
||||
}
|
||||
}
|
||||
|
||||
// WithStore returns a SystemModifier that sets the Store.
|
||||
func WithStore(store eventstore.Store) SystemModifier {
|
||||
return func(sys *System) {
|
||||
sys.Store = store
|
||||
}
|
||||
}
|
||||
|
||||
// WithRelayListCache returns a SystemModifier that sets the RelayListCache.
|
||||
func WithRelayListCache(cache cache.Cache32[GenericList[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 {
|
||||
return func(sys *System) {
|
||||
sys.FollowListCache = cache
|
||||
}
|
||||
}
|
||||
|
||||
// WithMetadataCache returns a SystemModifier that sets the MetadataCache.
|
||||
func WithMetadataCache(cache cache.Cache32[ProfileMetadata]) SystemModifier {
|
||||
return func(sys *System) {
|
||||
sys.MetadataCache = cache
|
||||
}
|
||||
}
|
||||
|
||||
// WithKVStore returns a SystemModifier that sets the KVStore.
|
||||
func WithKVStore(store kvstore.KVStore) SystemModifier {
|
||||
return func(sys *System) {
|
||||
if sys.KVStore != nil {
|
||||
|
@@ -8,7 +8,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
_dtnmtoah map[string]time.Time
|
||||
_dtnmtoah map[string]time.Time = make(map[string]time.Time)
|
||||
_dtnmtoahLock sync.Mutex
|
||||
)
|
||||
|
||||
@@ -21,6 +21,8 @@ func IsVirtualRelay(url string) bool {
|
||||
|
||||
if strings.HasPrefix(url, "wss://feeds.nostr.band") ||
|
||||
strings.HasPrefix(url, "wss://filter.nostr.wine") ||
|
||||
strings.HasPrefix(url, "ws://localhost") ||
|
||||
strings.HasPrefix(url, "ws://127.0.0.1") ||
|
||||
strings.HasPrefix(url, "wss://cache") {
|
||||
return true
|
||||
}
|
||||
@@ -29,7 +31,7 @@ func IsVirtualRelay(url string) bool {
|
||||
}
|
||||
|
||||
// PerQueryLimitInBatch tries to make an educated guess for the batch size given the total filter limit and
|
||||
// the number of abstract queries we'll be conducting at the same time
|
||||
// the number of abstract queries we'll be conducting at the same time.
|
||||
func PerQueryLimitInBatch(totalFilterLimit int, numberOfQueries int) int {
|
||||
if numberOfQueries == 1 || totalFilterLimit*numberOfQueries < 50 {
|
||||
return totalFilterLimit
|
||||
|
Reference in New Issue
Block a user