nostr

package module
v0.0.0-...-3d1c7b5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 5, 2025 License: MIT Imports: 35 Imported by: 0

README

Run Tests Go Reference Go Report Card

go-nostr

A set of useful things for Nostr Protocol implementations.

go get fiatjaf.com/nostrlib
Generating a key
package main

import (
    "fmt"

    "fiatjaf.com/nostrlib"
    "fiatjaf.com/nostrlib/nip19"
)

func main() {
    sk := nostr.GeneratePrivateKey()
    pk, _ := nostr.GetPublicKey(sk)
    nsec, _ := nip19.EncodePrivateKey(sk)
    npub, _ := nip19.EncodePublicKey(pk)

    fmt.Println("sk:", sk)
    fmt.Println("pk:", pk)
    fmt.Println(nsec)
    fmt.Println(npub)
}
Subscribing to a single relay
ctx := context.Background()
relay, err := nostr.RelayConnect(ctx, "wss://relay.stoner.com")
if err != nil {
	panic(err)
}

npub := "npub1422a7ws4yul24p0pf7cacn7cghqkutdnm35z075vy68ggqpqjcyswn8ekc"

var filters nostr.Filters
if _, v, err := nip19.Decode(npub); err == nil {
	pub := v.(string)
	filters = []nostr.Filter{{
		Kinds:   []int{nostr.KindTextNote},
		Authors: []string{pub},
		Limit:   1,
	}}
} else {
	panic(err)
}

ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel()

sub, err := relay.Subscribe(ctx, filters)
if err != nil {
	panic(err)
}

for ev := range sub.Events {
	// handle returned event.
	// channel will stay open until the ctx is cancelled (in this case, context timeout)
	fmt.Println(ev.ID)
}
Publishing to two relays
sk := nostr.GeneratePrivateKey()
pub, _ := nostr.GetPublicKey(sk)

ev := nostr.Event{
	PubKey:    pub,
	CreatedAt: nostr.Now(),
	Kind:      nostr.KindTextNote,
	Tags:      nil,
	Content:   "Hello World!",
}

// calling Sign sets the event ID field and the event Sig field
ev.Sign(sk)

// publish the event to two relays
ctx := context.Background()
for _, url := range []string{"wss://relay.stoner.com", "wss://nostr-pub.wellorder.net"} {
	relay, err := nostr.RelayConnect(ctx, url)
	if err != nil {
		fmt.Println(err)
		continue
	}
	if err := relay.Publish(ctx, ev); err != nil {
		fmt.Println(err)
		continue
	}

	fmt.Printf("published to %s\n", url)
}
Logging

To get more logs from the interaction with relays printed to STDOUT you can compile or run your program with -tags debug.

To remove the info logs completely, replace nostr.InfoLogger with something that prints nothing, like

nostr.InfoLogger = log.New(io.Discard, "", 0)
Example script
go run example/example.go
Using libsecp256k1

libsecp256k1 is very fast:

goos: linux
goarch: amd64
cpu: Intel(R) Core(TM) i5-2400 CPU @ 3.10GHz
BenchmarkWithoutLibsecp256k1/sign-4          	    2794	    434114 ns/op
BenchmarkWithoutLibsecp256k1/check-4         	    4352	    297416 ns/op
BenchmarkWithLibsecp256k1/sign-4             	   12559	     94607 ns/op
BenchmarkWithLibsecp256k1/check-4            	   13761	     84595 ns/op
PASS

But to use it you need the host to have it installed as a shared library and CGO to be supported, so we don't compile against it by default.

To use it, use -tags=libsecp256k1 whenever you're compiling your program that uses this library.

Test for Wasm

Install wasmbrowsertest, then run tests:

GOOS=js GOARCH=wasm go test -short ./...

Warning: risk of goroutine bloat (if used incorrectly)

Remember to cancel subscriptions, either by calling .Unsub() on them or ensuring their context.Context will be canceled at some point. If you don't do that they will keep creating a new goroutine for every new event that arrives and if you have stopped listening on the sub.Events channel that will cause chaos and doom in your program.

Contributing to this repository

Use NIP-34 to send your patches to naddr1qqyxwmeddehhxarjqy28wumn8ghj7un9d3shjtnyv9kh2uewd9hsz9nhwden5te0wfjkccte9ehx7um5wghxyctwvsq3vamnwvaz7tmjv4kxz7fwwpexjmtpdshxuet5qgsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8grqsqqqaueuwmljc.

Documentation

Index

Constants

View Source
const (
	KindProfileMetadata          uint16 = 0
	KindTextNote                 uint16 = 1
	KindRecommendServer          uint16 = 2
	KindFollowList               uint16 = 3
	KindEncryptedDirectMessage   uint16 = 4
	KindDeletion                 uint16 = 5
	KindRepost                   uint16 = 6
	KindReaction                 uint16 = 7
	KindBadgeAward               uint16 = 8
	KindSimpleGroupChatMessage   uint16 = 9
	KindSimpleGroupThreadedReply uint16 = 10
	KindSimpleGroupThread        uint16 = 11
	KindSimpleGroupReply         uint16 = 12
	KindSeal                     uint16 = 13
	KindDirectMessage            uint16 = 14
	KindGenericRepost            uint16 = 16
	KindReactionToWebsite        uint16 = 17
	KindChannelCreation          uint16 = 40
	KindChannelMetadata          uint16 = 41
	KindChannelMessage           uint16 = 42
	KindChannelHideMessage       uint16 = 43
	KindChannelMuteUser          uint16 = 44
	KindChess                    uint16 = 64
	KindMergeRequests            uint16 = 818
	KindComment                  uint16 = 1111
	KindBid                      uint16 = 1021
	KindBidConfirmation          uint16 = 1022
	KindOpenTimestamps           uint16 = 1040
	KindGiftWrap                 uint16 = 1059
	KindFileMetadata             uint16 = 1063
	KindLiveChatMessage          uint16 = 1311
	KindPatch                    uint16 = 1617
	KindIssue                    uint16 = 1621
	KindReply                    uint16 = 1622
	KindStatusOpen               uint16 = 1630
	KindStatusApplied            uint16 = 1631
	KindStatusClosed             uint16 = 1632
	KindStatusDraft              uint16 = 1633
	KindProblemTracker           uint16 = 1971
	KindReporting                uint16 = 1984
	KindLabel                    uint16 = 1985
	KindRelayReviews             uint16 = 1986
	KindAIEmbeddings             uint16 = 1987
	KindTorrent                  uint16 = 2003
	KindTorrentComment           uint16 = 2004
	KindCoinjoinPool             uint16 = 2022
	KindCommunityPostApproval    uint16 = 4550
	KindJobFeedback              uint16 = 7000
	KindSimpleGroupPutUser       uint16 = 9000
	KindSimpleGroupRemoveUser    uint16 = 9001
	KindSimpleGroupEditMetadata  uint16 = 9002
	KindSimpleGroupDeleteEvent   uint16 = 9005
	KindSimpleGroupCreateGroup   uint16 = 9007
	KindSimpleGroupDeleteGroup   uint16 = 9008
	KindSimpleGroupCreateInvite  uint16 = 9009
	KindSimpleGroupJoinRequest   uint16 = 9021
	KindSimpleGroupLeaveRequest  uint16 = 9022
	KindZapGoal                  uint16 = 9041
	KindNutZap                   uint16 = 9321
	KindTidalLogin               uint16 = 9467
	KindZapRequest               uint16 = 9734
	KindZap                      uint16 = 9735
	KindHighlights               uint16 = 9802
	KindMuteList                 uint16 = 10000
	KindPinList                  uint16 = 10001
	KindRelayListMetadata        uint16 = 10002
	KindBookmarkList             uint16 = 10003
	KindCommunityList            uint16 = 10004
	KindPublicChatList           uint16 = 10005
	KindBlockedRelayList         uint16 = 10006
	KindSearchRelayList          uint16 = 10007
	KindSimpleGroupList          uint16 = 10009
	KindInterestList             uint16 = 10015
	KindNutZapInfo               uint16 = 10019
	KindEmojiList                uint16 = 10030
	KindDMRelayList              uint16 = 10050
	KindUserServerList           uint16 = 10063
	KindFileStorageServerList    uint16 = 10096
	KindGoodWikiAuthorList       uint16 = 10101
	KindGoodWikiRelayList        uint16 = 10102
	KindNWCWalletInfo            uint16 = 13194
	KindLightningPubRPC          uint16 = 21000
	KindClientAuthentication     uint16 = 22242
	KindNWCWalletRequest         uint16 = 23194
	KindNWCWalletResponse        uint16 = 23195
	KindNostrConnect             uint16 = 24133
	KindBlobs                    uint16 = 24242
	KindHTTPAuth                 uint16 = 27235
	KindCategorizedPeopleList    uint16 = 30000
	KindCategorizedBookmarksList uint16 = 30001
	KindRelaySets                uint16 = 30002
	KindBookmarkSets             uint16 = 30003
	KindCuratedSets              uint16 = 30004
	KindCuratedVideoSets         uint16 = 30005
	KindMuteSets                 uint16 = 30007
	KindProfileBadges            uint16 = 30008
	KindBadgeDefinition          uint16 = 30009
	KindInterestSets             uint16 = 30015
	KindStallDefinition          uint16 = 30017
	KindProductDefinition        uint16 = 30018
	KindMarketplaceUI            uint16 = 30019
	KindProductSoldAsAuction     uint16 = 30020
	KindArticle                  uint16 = 30023
	KindDraftArticle             uint16 = 30024
	KindEmojiSets                uint16 = 30030
	KindModularArticleHeader     uint16 = 30040
	KindModularArticleContent    uint16 = 30041
	KindReleaseArtifactSets      uint16 = 30063
	KindApplicationSpecificData  uint16 = 30078
	KindLiveEvent                uint16 = 30311
	KindUserStatuses             uint16 = 30315
	KindClassifiedListing        uint16 = 30402
	KindDraftClassifiedListing   uint16 = 30403
	KindRepositoryAnnouncement   uint16 = 30617
	KindRepositoryState          uint16 = 30618
	KindSimpleGroupMetadata      uint16 = 39000
	KindSimpleGroupAdmins        uint16 = 39001
	KindSimpleGroupMembers       uint16 = 39002
	KindSimpleGroupRoles         uint16 = 39003
	KindWikiArticle              uint16 = 30818
	KindRedirects                uint16 = 30819
	KindFeed                     uint16 = 31890
	KindDateCalendarEvent        uint16 = 31922
	KindTimeCalendarEvent        uint16 = 31923
	KindCalendar                 uint16 = 31924
	KindCalendarEventRSVP        uint16 = 31925
	KindHandlerRecommendation    uint16 = 31989
	KindHandlerInformation       uint16 = 31990
	KindVideoEvent               uint16 = 34235
	KindShortVideoEvent          uint16 = 34236
	KindVideoViewEvent           uint16 = 34237
	KindCommunityDefinition      uint16 = 34550
)
View Source
const MAX_LOCKS = 50

Variables

View Source
var (
	// call SetOutput on InfoLogger to enable info logging
	InfoLogger = log.New(os.Stderr, "[go-nostr][info] ", log.LstdFlags)

	// call SetOutput on DebugLogger to enable debug logging
	DebugLogger = log.New(os.Stderr, "[go-nostr][debug] ", log.LstdFlags)
)

Functions

func CompareEvent

func CompareEvent(a, b Event) int

func CompareEventPtr

func CompareEventPtr(a, b *Event) int

func CompareEventPtrReverse

func CompareEventPtrReverse(b, a *Event) int

func CompareEventReverse

func CompareEventReverse(b, a Event) int

func FilterEqual

func FilterEqual(a Filter, b Filter) bool

func GeneratePrivateKey

func GeneratePrivateKey() [32]byte

func GetPublicKey

func GetPublicKey(sk [32]byte) [32]byte

func GetTheoreticalLimit

func GetTheoreticalLimit(filter Filter) int

GetTheoreticalLimit gets the maximum number of events that a normal filter would ever return, for example, if there is a number of "ids" in the filter, the theoretical limit will be that number of ids.

It returns -1 if there are no theoretical limits.

The given .Limit present in the filter is ignored.

func IsAddressableKind

func IsAddressableKind(kind uint16) bool

func IsEphemeralKind

func IsEphemeralKind(kind uint16) bool

func IsRegularKind

func IsRegularKind(kind uint16) bool

func IsReplaceableKind

func IsReplaceableKind(kind uint16) bool

func IsValid32ByteHex

func IsValid32ByteHex(thing string) bool

func IsValidPublicKey

func IsValidPublicKey(pk [32]byte) bool

func IsValidRelayURL

func IsValidRelayURL(u string) bool

func NormalizeHTTPURL

func NormalizeHTTPURL(s string) (string, error)

NormalizeHTTPURL does normalization of http(s):// URLs according to rfc3986. Don't use for relay URLs.

func NormalizeOKMessage

func NormalizeOKMessage(reason string, prefix string) string

NormalizeOKMessage takes a string message that is to be sent in an `OK` or `CLOSED` command and prefixes it with "<prefix>: " if it doesn't already have an acceptable prefix.

func NormalizeURL

func NormalizeURL(u string) string

NormalizeURL normalizes the url and replaces http://, https:// schemes with ws://, wss:// and normalizes the path.

func WithPenaltyBox

func WithPenaltyBox() withPenaltyBoxOpt

WithPenaltyBox just sets the penalty box mechanism so relays that fail to connect or that disconnect will be ignored for a while and we won't attempt to connect again.

func WithRelayOptions

func WithRelayOptions(ropts ...RelayOption) withRelayOptionsOpt

WithRelayOptions sets options that will be used on every relay instance created by this pool.

Types

type AuthEnvelope

type AuthEnvelope struct {
	Challenge *string
	Event     Event
}

func (AuthEnvelope) Label

func (_ AuthEnvelope) Label() string

func (AuthEnvelope) MarshalJSON

func (v AuthEnvelope) MarshalJSON() ([]byte, error)

func (AuthEnvelope) String

func (a AuthEnvelope) String() string

func (*AuthEnvelope) UnmarshalJSON

func (v *AuthEnvelope) UnmarshalJSON(data []byte) error

type Cipher

type Cipher interface {
	Encrypt(ctx context.Context, plaintext string, recipientPublicKey string) (base64ciphertext string, err error)
	Decrypt(ctx context.Context, base64ciphertext string, senderPublicKey string) (plaintext string, err error)
}

A Cipher provides NIP-44 encryption and decryption methods.

type CloseEnvelope

type CloseEnvelope string

func (CloseEnvelope) Label

func (_ CloseEnvelope) Label() string

func (CloseEnvelope) MarshalJSON

func (v CloseEnvelope) MarshalJSON() ([]byte, error)

func (CloseEnvelope) String

func (c CloseEnvelope) String() string

func (*CloseEnvelope) UnmarshalJSON

func (v *CloseEnvelope) UnmarshalJSON(data []byte) error

type ClosedEnvelope

type ClosedEnvelope struct {
	SubscriptionID string
	Reason         string
}

func (ClosedEnvelope) Label

func (_ ClosedEnvelope) Label() string

func (ClosedEnvelope) MarshalJSON

func (v ClosedEnvelope) MarshalJSON() ([]byte, error)

func (ClosedEnvelope) String

func (c ClosedEnvelope) String() string

func (*ClosedEnvelope) UnmarshalJSON

func (v *ClosedEnvelope) UnmarshalJSON(data []byte) error

type Connection

type Connection struct {
	// contains filtered or unexported fields
}

func NewConnection

func NewConnection(ctx context.Context, url string, requestHeader http.Header, tlsConfig *tls.Config) (*Connection, error)

func (*Connection) Close

func (c *Connection) Close() error

func (*Connection) Ping

func (c *Connection) Ping(ctx context.Context) error

func (*Connection) ReadMessage

func (c *Connection) ReadMessage(ctx context.Context, buf io.Writer) error

func (*Connection) WriteMessage

func (c *Connection) WriteMessage(ctx context.Context, data []byte) error

type CountEnvelope

type CountEnvelope struct {
	SubscriptionID string
	Filters
	Count       *int64
	HyperLogLog []byte
}

func (CountEnvelope) Label

func (_ CountEnvelope) Label() string

func (CountEnvelope) MarshalJSON

func (v CountEnvelope) MarshalJSON() ([]byte, error)

func (CountEnvelope) String

func (c CountEnvelope) String() string

func (*CountEnvelope) UnmarshalJSON

func (v *CountEnvelope) UnmarshalJSON(data []byte) error

type DirectedFilter

type DirectedFilter struct {
	Filter
	Relay string
}

type EOSEEnvelope

type EOSEEnvelope string

func (EOSEEnvelope) Label

func (_ EOSEEnvelope) Label() string

func (EOSEEnvelope) MarshalJSON

func (v EOSEEnvelope) MarshalJSON() ([]byte, error)

func (EOSEEnvelope) String

func (e EOSEEnvelope) String() string

func (*EOSEEnvelope) UnmarshalJSON

func (v *EOSEEnvelope) UnmarshalJSON(data []byte) error

type EntityPointer

type EntityPointer struct {
	PublicKey  PubKey   `json:"pubkey"`
	Kind       uint16   `json:"kind,omitempty"`
	Identifier string   `json:"identifier,omitempty"`
	Relays     []string `json:"relays,omitempty"`
}

func (EntityPointer) AsTag

func (ep EntityPointer) AsTag() Tag

func (EntityPointer) AsTagReference

func (ep EntityPointer) AsTagReference() string

func (EntityPointer) MatchesEvent

func (ep EntityPointer) MatchesEvent(evt Event) bool

type Envelope

type Envelope interface {
	Label() string
	UnmarshalJSON([]byte) error
	MarshalJSON() ([]byte, error)
	String() string
}

func ParseMessage

func ParseMessage(message []byte) Envelope

type Event

type Event struct {
	ID        ID
	PubKey    PubKey
	CreatedAt Timestamp
	Kind      uint16
	Tags      Tags
	Content   string
	Sig       [64]byte
}

func (*Event) CheckID

func (evt *Event) CheckID() bool

CheckID checks if the implied ID matches the given ID

func (Event) CheckSignature

func (evt Event) CheckSignature() (bool, error)

CheckSignature checks if the signature is valid for the id (which is a hash of the serialized event content). returns an error if the signature itself is invalid.

func (*Event) GetID

func (evt *Event) GetID() ID

GetID serializes and returns the event ID as a string.

func (Event) MarshalEasyJSON

func (v Event) MarshalEasyJSON(w *jwriter.Writer)

MarshalEasyJSON supports easyjson.Marshaler interface

func (Event) MarshalJSON

func (v Event) MarshalJSON() ([]byte, error)

MarshalJSON supports json.Marshaler interface

func (*Event) Serialize

func (evt *Event) Serialize() []byte

Serialize outputs a byte array that can be hashed/signed to identify/authenticate. JSON encoding as defined in RFC4627.

func (*Event) Sign

func (evt *Event) Sign(secretKey [32]byte) error

Sign signs an event with a given privateKey.

func (Event) String

func (evt Event) String() string

Event Stringer interface, just returns the raw JSON as a string.

func (*Event) UnmarshalEasyJSON

func (v *Event) UnmarshalEasyJSON(l *jlexer.Lexer)

UnmarshalEasyJSON supports easyjson.Unmarshaler interface

func (*Event) UnmarshalJSON

func (v *Event) UnmarshalJSON(data []byte) error

UnmarshalJSON supports json.Unmarshaler interface

type EventEnvelope

type EventEnvelope struct {
	SubscriptionID *string
	Event
}

func (EventEnvelope) Label

func (_ EventEnvelope) Label() string

func (EventEnvelope) MarshalJSON

func (v EventEnvelope) MarshalJSON() ([]byte, error)

func (*EventEnvelope) UnmarshalJSON

func (v *EventEnvelope) UnmarshalJSON(data []byte) error

type EventMessage

type EventMessage struct {
	Event Event
	Relay string
}

type EventPointer

type EventPointer struct {
	ID     ID       `json:"id"`
	Relays []string `json:"relays,omitempty"`
	Author PubKey   `json:"author,omitempty"`
	Kind   uint16   `json:"kind,omitempty"`
}

func (EventPointer) AsTag

func (ep EventPointer) AsTag() Tag

func (EventPointer) AsTagReference

func (ep EventPointer) AsTagReference() string

func (EventPointer) MatchesEvent

func (ep EventPointer) MatchesEvent(evt Event) bool

type Filter

type Filter struct {
	IDs     []ID
	Kinds   []uint16
	Authors []PubKey
	Tags    TagMap
	Since   *Timestamp
	Until   *Timestamp
	Limit   int
	Search  string

	// LimitZero is or must be set when there is a "limit":0 in the filter, and not when "limit" is just omitted
	LimitZero bool `json:"-"`
}

func (Filter) Clone

func (ef Filter) Clone() Filter

func (Filter) MarshalEasyJSON

func (v Filter) MarshalEasyJSON(w *jwriter.Writer)

MarshalEasyJSON supports easyjson.Marshaler interface

func (Filter) MarshalJSON

func (v Filter) MarshalJSON() ([]byte, error)

MarshalJSON supports json.Marshaler interface

func (Filter) Matches

func (ef Filter) Matches(event *Event) bool

func (Filter) MatchesIgnoringTimestampConstraints

func (ef Filter) MatchesIgnoringTimestampConstraints(event *Event) bool

func (Filter) String

func (ef Filter) String() string

func (*Filter) UnmarshalEasyJSON

func (v *Filter) UnmarshalEasyJSON(l *jlexer.Lexer)

UnmarshalEasyJSON supports easyjson.Unmarshaler interface

func (*Filter) UnmarshalJSON

func (v *Filter) UnmarshalJSON(data []byte) error

UnmarshalJSON supports json.Unmarshaler interface

type Filters

type Filters []Filter

func (Filters) Match

func (eff Filters) Match(event *Event) bool

func (Filters) MatchIgnoringTimestampConstraints

func (eff Filters) MatchIgnoringTimestampConstraints(event *Event) bool

func (Filters) String

func (eff Filters) String() string

type ID

type ID [32]byte

func IDFromHex

func IDFromHex(h string) ID

func (ID) String

func (id ID) String() string

type Keyer

type Keyer interface {
	Signer
	Cipher
}

type MultiStore

type MultiStore []RelayStore

func (MultiStore) Publish

func (multi MultiStore) Publish(ctx context.Context, event Event) error

func (MultiStore) QueryEvents

func (multi MultiStore) QueryEvents(ctx context.Context, filter Filter) (chan *Event, error)

func (MultiStore) QuerySync

func (multi MultiStore) QuerySync(ctx context.Context, filter Filter) ([]*Event, error)

type NoticeEnvelope

type NoticeEnvelope string

func (NoticeEnvelope) Label

func (_ NoticeEnvelope) Label() string

func (NoticeEnvelope) MarshalJSON

func (v NoticeEnvelope) MarshalJSON() ([]byte, error)

func (NoticeEnvelope) String

func (n NoticeEnvelope) String() string

func (*NoticeEnvelope) UnmarshalJSON

func (v *NoticeEnvelope) UnmarshalJSON(data []byte) error

type OKEnvelope

type OKEnvelope struct {
	EventID ID
	OK      bool
	Reason  string
}

func (OKEnvelope) Label

func (_ OKEnvelope) Label() string

func (OKEnvelope) MarshalJSON

func (v OKEnvelope) MarshalJSON() ([]byte, error)

func (OKEnvelope) String

func (o OKEnvelope) String() string

func (*OKEnvelope) UnmarshalJSON

func (v *OKEnvelope) UnmarshalJSON(data []byte) error

type Pointer

type Pointer interface {
	AsTagReference() string
	AsTag() Tag
	MatchesEvent(Event) bool
}

type PoolOption

type PoolOption interface {
	ApplyPoolOption(*SimplePool)
}

type ProfilePointer

type ProfilePointer struct {
	PublicKey PubKey   `json:"pubkey"`
	Relays    []string `json:"relays,omitempty"`
}

func (ProfilePointer) AsTag

func (ep ProfilePointer) AsTag() Tag

func (ProfilePointer) AsTagReference

func (ep ProfilePointer) AsTagReference() string

func (ProfilePointer) MatchesEvent

func (ep ProfilePointer) MatchesEvent(_ Event) bool

type PubKey

type PubKey [32]byte

func PubKeyFromHex

func PubKeyFromHex(h string) PubKey

func (PubKey) String

func (pk PubKey) String() string

type PublishResult

type PublishResult struct {
	Error    error
	RelayURL string
	Relay    *Relay
}

type Relay

type Relay struct {
	URL string

	Connection    *Connection
	Subscriptions *xsync.MapOf[int64, *Subscription]

	ConnectionError error

	// custom things that aren't often used
	//
	AssumeValid bool // this will skip verifying signatures for events received from this relay
	// contains filtered or unexported fields
}

func NewRelay

func NewRelay(ctx context.Context, url string, opts ...RelayOption) *Relay

NewRelay returns a new relay. The relay connection will be closed when the context is canceled.

func RelayConnect

func RelayConnect(ctx context.Context, url string, opts ...RelayOption) (*Relay, error)

RelayConnect returns a relay object connected to url. Once successfully connected, cancelling ctx has no effect. To close the connection, call r.Close().

func (*Relay) Auth

func (r *Relay) Auth(ctx context.Context, sign func(event *Event) error) error

Auth sends an "AUTH" command client->relay as in NIP-42 and waits for an OK response.

func (*Relay) Close

func (r *Relay) Close() error

func (*Relay) Connect

func (r *Relay) Connect(ctx context.Context) error

Connect tries to establish a websocket connection to r.URL. If the context expires before the connection is complete, an error is returned. Once successfully connected, context expiration has no effect: call r.Close to close the connection.

The underlying relay connection will use a background context. If you want to pass a custom context to the underlying relay connection, use NewRelay() and then Relay.Connect().

func (*Relay) ConnectWithTLS

func (r *Relay) ConnectWithTLS(ctx context.Context, tlsConfig *tls.Config) error

ConnectWithTLS tries to establish a secured websocket connection to r.URL using customized tls.Config (CA's, etc).

func (*Relay) Context

func (r *Relay) Context() context.Context

Context retrieves the context that is associated with this relay connection.

func (*Relay) Count

func (r *Relay) Count(
	ctx context.Context,
	filters Filters,
	opts ...SubscriptionOption,
) (int64, []byte, error)

func (*Relay) IsConnected

func (r *Relay) IsConnected() bool

IsConnected returns true if the connection to this relay seems to be active.

func (*Relay) PrepareSubscription

func (r *Relay) PrepareSubscription(ctx context.Context, filters Filters, opts ...SubscriptionOption) *Subscription

PrepareSubscription creates a subscription, but doesn't fire it.

Remember to cancel subscriptions, either by calling `.Unsub()` on them or ensuring their `context.Context` will be canceled at some point. Failure to do that will result in a huge number of halted goroutines being created.

func (*Relay) Publish

func (r *Relay) Publish(ctx context.Context, event Event) error

Publish sends an "EVENT" command to the relay r as in NIP-01 and waits for an OK response.

func (*Relay) QueryEvents

func (r *Relay) QueryEvents(ctx context.Context, filter Filter) (chan *Event, error)

func (*Relay) QuerySync

func (r *Relay) QuerySync(ctx context.Context, filter Filter) ([]*Event, error)

func (*Relay) String

func (r *Relay) String() string

String just returns the relay URL.

func (*Relay) Subscribe

func (r *Relay) Subscribe(ctx context.Context, filters Filters, opts ...SubscriptionOption) (*Subscription, error)

Subscribe sends a "REQ" command to the relay r as in NIP-01. Events are returned through the channel sub.Events. The subscription is closed when context ctx is cancelled ("CLOSE" in NIP-01).

Remember to cancel subscriptions, either by calling `.Unsub()` on them or ensuring their `context.Context` will be canceled at some point. Failure to do that will result in a huge number of halted goroutines being created.

func (*Relay) Write

func (r *Relay) Write(msg []byte) <-chan error

Write queues a message to be sent to the relay.

type RelayEvent

type RelayEvent struct {
	*Event
	Relay *Relay
}

func (RelayEvent) String

func (ie RelayEvent) String() string

type RelayOption

type RelayOption interface {
	ApplyRelayOption(*Relay)
}

When instantiating relay connections, some options may be passed. RelayOption is the type of the argument passed for that.

type RelayStore

type RelayStore interface {
	Publish(context.Context, Event) error
	QueryEvents(context.Context, Filter) (chan *Event, error)
	QuerySync(context.Context, Filter) ([]*Event, error)
}

type ReqEnvelope

type ReqEnvelope struct {
	SubscriptionID string
	Filters
}

func (ReqEnvelope) Label

func (_ ReqEnvelope) Label() string

func (ReqEnvelope) MarshalJSON

func (v ReqEnvelope) MarshalJSON() ([]byte, error)

func (*ReqEnvelope) UnmarshalJSON

func (v *ReqEnvelope) UnmarshalJSON(data []byte) error

type Signer

type Signer interface {
	GetPublicKey(context.Context) (string, error)
	SignEvent(context.Context, *Event) error
}

A Signer provides basic public key signing methods.

type SimplePool

type SimplePool struct {
	Relays  *xsync.MapOf[string, *Relay]
	Context context.Context
	// contains filtered or unexported fields
}

func NewSimplePool

func NewSimplePool(ctx context.Context, opts ...PoolOption) *SimplePool

func (*SimplePool) BatchedSubManyEose

func (pool *SimplePool) BatchedSubManyEose(
	ctx context.Context,
	dfs []DirectedFilter,
	opts ...SubscriptionOption,
) chan RelayEvent

func (*SimplePool) Close

func (pool *SimplePool) Close(reason string)

func (*SimplePool) CountMany

func (pool *SimplePool) CountMany(
	ctx context.Context,
	urls []string,
	filter Filter,
	opts []SubscriptionOption,
) int

CountMany aggregates count results from multiple relays using HyperLogLog

func (*SimplePool) EnsureRelay

func (pool *SimplePool) EnsureRelay(url string) (*Relay, error)

func (*SimplePool) PublishMany

func (pool *SimplePool) PublishMany(ctx context.Context, urls []string, evt Event) chan PublishResult

func (*SimplePool) QuerySingle

func (pool *SimplePool) QuerySingle(ctx context.Context, urls []string, filter Filter) *RelayEvent

QuerySingle returns the first event returned by the first relay, cancels everything else.

func (*SimplePool) SubMany

func (pool *SimplePool) SubMany(
	ctx context.Context,
	urls []string,
	filters Filters,
	opts ...SubscriptionOption,
) chan RelayEvent

SubMany opens a subscription with the given filters to multiple relays the subscriptions only end when the context is canceled

func (*SimplePool) SubManyEose

func (pool *SimplePool) SubManyEose(
	ctx context.Context,
	urls []string,
	filters Filters,
	opts ...SubscriptionOption,
) chan RelayEvent

SubManyEose is like SubMany, but it stops subscriptions and closes the channel when gets a EOSE

func (*SimplePool) SubManyNotifyEOSE

func (pool *SimplePool) SubManyNotifyEOSE(
	ctx context.Context,
	urls []string,
	filters Filters,
	eoseChan chan struct{},
	opts ...SubscriptionOption,
) chan RelayEvent

SubManyNotifyEOSE is like SubMany, but takes a channel that is closed when all subscriptions have received an EOSE

type Status

type Status int

type Subscription

type Subscription struct {
	Relay   *Relay
	Filters Filters

	// the Events channel emits all EVENTs that come in a Subscription
	// will be closed when the subscription ends
	Events chan *Event

	// the EndOfStoredEvents channel gets closed when an EOSE comes for that subscription
	EndOfStoredEvents chan struct{}

	// the ClosedReason channel emits the reason when a CLOSED message is received
	ClosedReason chan string

	// Context will be .Done() when the subscription ends
	Context context.Context
	// contains filtered or unexported fields
}

func (*Subscription) Close

func (sub *Subscription) Close()

Close just sends a CLOSE message. You probably want Unsub() instead.

func (*Subscription) Fire

func (sub *Subscription) Fire() error

Fire sends the "REQ" command to the relay.

func (*Subscription) GetID

func (sub *Subscription) GetID() string

func (*Subscription) Sub

func (sub *Subscription) Sub(_ context.Context, filters Filters)

Sub sets sub.Filters and then calls sub.Fire(ctx). The subscription will be closed if the context expires.

func (*Subscription) Unsub

func (sub *Subscription) Unsub()

Unsub closes the subscription, sending "CLOSE" to relay as in NIP-01. Unsub() also closes the channel sub.Events and makes a new one.

type SubscriptionOption

type SubscriptionOption interface {
	IsSubscriptionOption()
}

When instantiating relay connections, some options may be passed. SubscriptionOption is the type of the argument passed for that. Some examples are WithLabel.

type Tag

type Tag []string

func (Tag) Key

func (tag Tag) Key() string

func (Tag) Relay

func (tag Tag) Relay() string

func (Tag) StartsWith

func (tag Tag) StartsWith(prefix []string) bool

StartsWith checks if a tag contains a prefix. for example,

["p", "abcdef...", "wss://relay.com"]

would match against

["p", "abcdef..."]

or even

["p", "abcdef...", "wss://"]

func (Tag) Value

func (tag Tag) Value() string

type TagMap

type TagMap map[string][]string

type Tags

type Tags []Tag

func (Tags) All

func (tags Tags) All(tagPrefix []string) iter.Seq2[int, Tag]

All returns an iterator for all the tags that match the prefix, see Tag.StartsWith

func (Tags) AppendUnique

func (tags Tags) AppendUnique(tag Tag) Tags

AppendUnique appends a tag if it doesn't exist yet, otherwise does nothing. the uniqueness comparison is done based only on the first 2 elements of the tag.

func (Tags) ContainsAny

func (tags Tags) ContainsAny(tagName string, values []string) bool

func (Tags) FilterOut

func (tags Tags) FilterOut(tagPrefix []string) Tags

FilterOut returns a new slice with only the elements that match the prefix, see Tag.StartsWith

func (*Tags) FilterOutInPlace

func (tags *Tags) FilterOutInPlace(tagPrefix []string)

FilterOutInPlace removes all tags that match the prefix, but potentially reorders the tags in unpredictable ways, see Tag.StartsWith

func (Tags) GetAll

func (tags Tags) GetAll(tagPrefix []string) Tags

GetAll gets all the tags that match the prefix, see Tag.StartsWith

func (Tags) GetD

func (tags Tags) GetD() string

GetD gets the first "d" tag (for parameterized replaceable events) value or ""

func (Tags) GetFirst

func (tags Tags) GetFirst(tagPrefix []string) *Tag

GetFirst gets the first tag in tags that matches the prefix, see Tag.StartsWith

func (Tags) GetLast

func (tags Tags) GetLast(tagPrefix []string) *Tag

GetLast gets the last tag in tags that matches the prefix, see Tag.StartsWith

func (*Tags) Scan

func (t *Tags) Scan(src any) error

type Timestamp

type Timestamp int64

func Now

func Now() Timestamp

func (Timestamp) Time

func (t Timestamp) Time() time.Time

type WithAuthHandler

type WithAuthHandler func(ctx context.Context, authEvent RelayEvent) error

WithAuthHandler must be a function that signs the auth event when called. it will be called whenever any relay in the pool returns a `CLOSED` message with the "auth-required:" prefix, only once for each relay

func (WithAuthHandler) ApplyPoolOption

func (h WithAuthHandler) ApplyPoolOption(pool *SimplePool)

type WithAuthorKindQueryMiddleware

type WithAuthorKindQueryMiddleware func(relay string, pubkey PubKey, kind uint16)

WithQueryMiddleware is a function that will be called with every combination of relay+pubkey+kind queried in a .SubMany*() call -- when applicable (i.e. when the query contains a pubkey and a kind).

func (WithAuthorKindQueryMiddleware) ApplyPoolOption

func (h WithAuthorKindQueryMiddleware) ApplyPoolOption(pool *SimplePool)

type WithCheckDuplicate

type WithCheckDuplicate func(id ID, relay string) bool

WithCheckDuplicate sets checkDuplicate on the subscription

func (WithCheckDuplicate) IsSubscriptionOption

func (_ WithCheckDuplicate) IsSubscriptionOption()

type WithCustomHandler

type WithCustomHandler func(data []byte)

WithCustomHandler must be a function that handles any relay message that couldn't be parsed as a standard envelope.

func (WithCustomHandler) ApplyRelayOption

func (ch WithCustomHandler) ApplyRelayOption(r *Relay)

type WithDuplicateMiddleware

type WithDuplicateMiddleware func(relay string, id ID)

WithDuplicateMiddleware is a function that will be called with all duplicate ids received.

func (WithDuplicateMiddleware) ApplyPoolOption

func (h WithDuplicateMiddleware) ApplyPoolOption(pool *SimplePool)

type WithEventMiddleware

type WithEventMiddleware func(RelayEvent)

WithEventMiddleware is a function that will be called with all events received.

func (WithEventMiddleware) ApplyPoolOption

func (h WithEventMiddleware) ApplyPoolOption(pool *SimplePool)

type WithLabel

type WithLabel string

WithLabel puts a label on the subscription (it is prepended to the automatic id) that is sent to relays.

func (WithLabel) IsSubscriptionOption

func (_ WithLabel) IsSubscriptionOption()

type WithNoticeHandler

type WithNoticeHandler func(notice string)

WithNoticeHandler just takes notices and is expected to do something with them. when not given, defaults to logging the notices.

func (WithNoticeHandler) ApplyRelayOption

func (nh WithNoticeHandler) ApplyRelayOption(r *Relay)

type WithRequestHeader

type WithRequestHeader http.Header

WithRequestHeader sets the HTTP request header of the websocket preflight request.

func (WithRequestHeader) ApplyRelayOption

func (ch WithRequestHeader) ApplyRelayOption(r *Relay)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL