nostr

package module
v0.45.0 Latest Latest
Warning

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

Go to latest
Published: Dec 24, 2024 License: MIT Imports: 40 Imported by: 404

README

Run Tests Go Reference Go Report Card

go-nostr

A set of useful things for Nostr Protocol implementations.

go get github.com/nbd-wtf/go-nostr
Generating a key
package main

import (
    "fmt"

    "github.com/nbd-wtf/go-nostr"
    "github.com/nbd-wtf/go-nostr/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.

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

func CompareEvent(a, b Event) int

func CompareEventPtr added in v0.38.1

func CompareEventPtr(a, b *Event) int

func CompareEventPtrReverse added in v0.38.1

func CompareEventPtrReverse(b, a *Event) int

func CompareEventReverse added in v0.38.1

func CompareEventReverse(b, a Event) int

func FilterEqual

func FilterEqual(a Filter, b Filter) bool

func GeneratePrivateKey

func GeneratePrivateKey() string

func GetPublicKey

func GetPublicKey(sk string) (string, error)

func GetTheoreticalLimit added in v0.42.0

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 added in v0.42.0

func IsAddressableKind(kind int) bool

func IsEphemeralKind added in v0.42.0

func IsEphemeralKind(kind int) bool

func IsRegularKind added in v0.42.0

func IsRegularKind(kind int) bool

func IsReplaceableKind added in v0.42.0

func IsReplaceableKind(kind int) bool

func IsValid32ByteHex added in v0.28.1

func IsValid32ByteHex(thing string) bool

func IsValidPublicKey added in v0.28.1

func IsValidPublicKey(pk string) bool

func IsValidRelayURL added in v0.19.3

func IsValidRelayURL(u string) bool

func NormalizeOKMessage added in v0.26.0

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 added in v0.34.5

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 WithUserAgent added in v0.37.5

func WithUserAgent(userAgent string) withUserAgentOpt

WithUserAgent sets the user-agent header for all relay connections in the pool.

Types

type AuthEnvelope added in v0.18.1

type AuthEnvelope struct {
	Challenge *string
	Event     Event
}

func (AuthEnvelope) Label added in v0.18.1

func (_ AuthEnvelope) Label() string

func (AuthEnvelope) MarshalJSON added in v0.18.1

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

func (AuthEnvelope) String added in v0.27.0

func (a AuthEnvelope) String() string

func (*AuthEnvelope) UnmarshalJSON added in v0.18.1

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

type Cipher added in v0.39.0

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 added in v0.18.1

type CloseEnvelope string

func (CloseEnvelope) Label added in v0.18.1

func (_ CloseEnvelope) Label() string

func (CloseEnvelope) MarshalJSON added in v0.18.1

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

func (CloseEnvelope) String added in v0.27.0

func (c CloseEnvelope) String() string

func (*CloseEnvelope) UnmarshalJSON added in v0.18.1

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

type ClosedEnvelope added in v0.26.0

type ClosedEnvelope struct {
	SubscriptionID string
	Reason         string
}

func (ClosedEnvelope) Label added in v0.26.0

func (_ ClosedEnvelope) Label() string

func (ClosedEnvelope) MarshalJSON added in v0.26.0

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

func (ClosedEnvelope) String added in v0.27.0

func (c ClosedEnvelope) String() string

func (*ClosedEnvelope) UnmarshalJSON added in v0.26.0

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) ReadMessage added in v0.18.0

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 added in v0.19.4

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

func (CountEnvelope) Label added in v0.19.4

func (_ CountEnvelope) Label() string

func (CountEnvelope) MarshalJSON added in v0.19.4

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

func (CountEnvelope) String added in v0.27.0

func (c CountEnvelope) String() string

func (*CountEnvelope) UnmarshalJSON added in v0.19.4

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

type DirectedFilters added in v0.29.0

type DirectedFilters struct {
	Filters
	Relay string
}

type EOSEEnvelope added in v0.18.1

type EOSEEnvelope string

func (EOSEEnvelope) Label added in v0.18.1

func (_ EOSEEnvelope) Label() string

func (EOSEEnvelope) MarshalJSON added in v0.18.1

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

func (EOSEEnvelope) String added in v0.27.0

func (e EOSEEnvelope) String() string

func (*EOSEEnvelope) UnmarshalJSON added in v0.18.1

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

type EntityPointer added in v0.14.0

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

type Envelope added in v0.18.1

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

func ParseMessage added in v0.18.1

func ParseMessage(message []byte) Envelope

type Event

type Event struct {
	ID        string
	PubKey    string
	CreatedAt Timestamp
	Kind      int
	Tags      Tags
	Content   string
	Sig       string
	// contains filtered or unexported fields
}

func (*Event) CheckID added in v0.36.1

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) GetExtra deprecated added in v0.8.2

func (evt Event) GetExtra(key string) any

Deprecated: this was never a good idea, stop using.

func (Event) GetExtraBoolean deprecated added in v0.8.2

func (evt Event) GetExtraBoolean(key string) bool

Deprecated: this was never a good idea, stop using.

func (Event) GetExtraNumber deprecated added in v0.8.2

func (evt Event) GetExtraNumber(key string) float64

Deprecated: this was never a good idea, stop using.

func (Event) GetExtraString deprecated added in v0.8.2

func (evt Event) GetExtraString(key string) string

Deprecated: this was never a good idea, stop using.

func (*Event) GetID

func (evt *Event) GetID() string

GetID serializes and returns the event ID as a string.

func (Event) MarshalEasyJSON added in v0.17.0

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) RemoveExtra deprecated added in v0.24.0

func (evt *Event) RemoveExtra(key string)

Deprecated: this was never a good idea, stop using.

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) SetExtra deprecated added in v0.8.2

func (evt *Event) SetExtra(key string, value any)

Deprecated: this was never a good idea, stop using.

func (*Event) Sign

func (evt *Event) Sign(secretKey string) error

Sign signs an event with a given privateKey.

func (Event) String added in v0.16.3

func (evt Event) String() string

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

func (*Event) UnmarshalEasyJSON added in v0.17.0

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 added in v0.18.1

type EventEnvelope struct {
	SubscriptionID *string
	Event
}

func (EventEnvelope) Label added in v0.18.1

func (_ EventEnvelope) Label() string

func (EventEnvelope) MarshalJSON added in v0.18.1

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

func (*EventEnvelope) UnmarshalJSON added in v0.18.1

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

type EventMessage

type EventMessage struct {
	Event Event
	Relay string
}

type EventPointer added in v0.13.0

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

type Filter

type Filter struct {
	IDs     []string
	Kinds   []int
	Authors []string
	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 added in v0.25.3

func (ef Filter) Clone() Filter

func (Filter) MarshalEasyJSON added in v0.17.0

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 added in v0.34.15

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

func (Filter) String added in v0.8.2

func (ef Filter) String() string

func (*Filter) UnmarshalEasyJSON added in v0.17.0

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 added in v0.34.15

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

func (Filters) String added in v0.8.2

func (eff Filters) String() string

type Keyer added in v0.39.0

type Keyer interface {
	Signer
	Cipher
}

type MultiStore added in v0.28.5

type MultiStore []RelayStore

func (MultiStore) Publish added in v0.28.5

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

func (MultiStore) QueryEvents added in v0.36.0

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

func (MultiStore) QuerySync added in v0.28.5

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

type NoticeEnvelope added in v0.18.1

type NoticeEnvelope string

func (NoticeEnvelope) Label added in v0.18.1

func (_ NoticeEnvelope) Label() string

func (NoticeEnvelope) MarshalJSON added in v0.18.1

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

func (NoticeEnvelope) String added in v0.27.0

func (n NoticeEnvelope) String() string

func (*NoticeEnvelope) UnmarshalJSON added in v0.18.1

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

type OKEnvelope added in v0.18.1

type OKEnvelope struct {
	EventID string
	OK      bool
	Reason  string
}

func (OKEnvelope) Label added in v0.18.1

func (_ OKEnvelope) Label() string

func (OKEnvelope) MarshalJSON added in v0.18.1

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

func (OKEnvelope) String added in v0.27.0

func (o OKEnvelope) String() string

func (*OKEnvelope) UnmarshalJSON added in v0.18.1

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

type PoolOption added in v0.27.0

type PoolOption interface {
	ApplyPoolOption(*SimplePool)
}

type ProfilePointer added in v0.13.0

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

type PublishResult added in v0.39.1

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

type Relay

type Relay struct {
	URL           string
	RequestHeader http.Header // e.g. for origin header

	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 added in v0.8.1

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 added in v0.12.0

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 added in v0.30.0

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 added in v0.18.1

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

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

func (*Relay) Count added in v0.19.5

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

func (*Relay) IsConnected added in v0.19.0

func (r *Relay) IsConnected() bool

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

func (*Relay) PrepareSubscription added in v0.8.2

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 added in v0.36.0

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

func (*Relay) QuerySync added in v0.9.0

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

func (*Relay) String added in v0.8.2

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 added in v0.19.0

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

Write queues a message to be sent to the relay.

type RelayEvent added in v0.37.0

type RelayEvent struct {
	*Event
	Relay *Relay
}

func (RelayEvent) String added in v0.37.0

func (ie RelayEvent) String() string

type RelayOption added in v0.19.0

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 added in v0.28.5

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

type ReqEnvelope added in v0.18.1

type ReqEnvelope struct {
	SubscriptionID string
	Filters
}

func (ReqEnvelope) Label added in v0.18.1

func (_ ReqEnvelope) Label() string

func (ReqEnvelope) MarshalJSON added in v0.18.1

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

func (*ReqEnvelope) UnmarshalJSON added in v0.18.1

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

type Signer added in v0.39.0

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

A Signer provides basic public key signing methods.

type SimplePool added in v0.18.0

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

func NewSimplePool added in v0.18.0

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

func (*SimplePool) BatchedSubMany added in v0.29.0

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

BatchedSubMany fires subscriptions only to specific relays, but batches them when they are the same.

func (*SimplePool) BatchedSubManyEose added in v0.29.0

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

BatchedSubManyEose is like BatchedSubMany, but ends upon receiving EOSE from all relays.

func (*SimplePool) CountMany added in v0.43.0

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 added in v0.18.0

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

func (*SimplePool) PublishMany added in v0.39.1

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

func (*SimplePool) QuerySingle added in v0.20.0

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 added in v0.18.0

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 added in v0.18.0

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) SubManyEoseNonUnique added in v0.24.0

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

SubManyEoseNonUnique is like SubManyEose, but returns duplicate events if they come from different relays

func (*SimplePool) SubManyNonUnique added in v0.24.0

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

SubManyNonUnique is like SubMany, but returns duplicate events if they come from different relays

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 added in v0.19.0

func (sub *Subscription) Close()

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

func (*Subscription) Fire added in v0.8.2

func (sub *Subscription) Fire() error

Fire sends the "REQ" command to the relay.

func (*Subscription) GetID added in v0.14.0

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 added in v0.19.0

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 added in v0.8.2

func (tag Tag) Key() string

func (Tag) Relay added in v0.8.2

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 added in v0.8.2

func (tag Tag) Value() string

type TagMap

type TagMap map[string][]string

type Tags

type Tags []Tag

func (Tags) All added in v0.38.3

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 added in v0.38.3

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 added in v0.27.4

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 added in v0.17.0

type Timestamp int64

func Now added in v0.17.0

func Now() Timestamp

func (Timestamp) Time added in v0.17.0

func (t Timestamp) Time() time.Time

type WithAuthHandler added in v0.19.0

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 added in v0.34.0

func (h WithAuthHandler) ApplyPoolOption(pool *SimplePool)

type WithCustomHandler added in v0.36.0

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 added in v0.36.0

func (ch WithCustomHandler) ApplyRelayOption(r *Relay)

type WithEventMiddleware added in v0.34.0

type WithEventMiddleware func(RelayEvent)

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

func (WithEventMiddleware) ApplyPoolOption added in v0.34.0

func (h WithEventMiddleware) ApplyPoolOption(pool *SimplePool)

type WithLabel added in v0.19.0

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 added in v0.19.0

func (_ WithLabel) IsSubscriptionOption()

type WithNoticeHandler added in v0.19.0

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 added in v0.32.0

func (nh WithNoticeHandler) ApplyRelayOption(r *Relay)

type WithQueryMiddleware added in v0.45.0

type WithQueryMiddleware func(relay string, pubkey string, kind int)

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 (WithQueryMiddleware) ApplyPoolOption added in v0.45.0

func (h WithQueryMiddleware) ApplyPoolOption(pool *SimplePool)

Jump to

Keyboard shortcuts

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