nostr

package module
v0.0.0 Latest Latest
Warning

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

Go to latest
Published: Dec 25, 2022 License: MIT Imports: 21 Imported by: 0

README

go-nostr

A set of useful things for Nostr Protocol implementations.

GoDoc

Subscribing to a set of relays
pool := nostr.NewRelayPool()

pool.Add("wss://relay.nostr.com/",  nostr.SimplePolicy{Read: true, Write: true})
pool.Add("wss://nostrrelay.example.com/",  nostr.SimplePolicy{Read: true, Write: true})

for notice := range pool.Notices {
	log.Printf("%s has sent a notice: '%s'\n", notice.Relay, notice.Message)
}
Listening for events
subId, events, unsubscribe := pool.Sub(nostr.Filters{
	{
		Authors: []string{"0ded86bf80c76847320b16f22b7451c08169434837a51ad5fe3b178af6c35f5d"},
		Kinds:   []int{nostr.KindTextNote}, // or {1}
	},
})

go func() {
	for event := range nostr.Unique(events) {
		log.Print(event)
	}
}()

time.Sleep(5 * time.Second)
unsubscribe()
Publishing an event
secretKey := "3f06a81e0a0c2ad34ee9df2a30d87a810da9e3c3881f780755ace5e5e64d30a7"

pool.SecretKey = &secretKey

event, statuses, _ := pool.PublishEvent(&nostr.Event{
	CreatedAt: time.Now(),
	Kind:      nostr.KindTextNote,
	Tags:      make(nostr.Tags, 0),
	Content:   "hello",
})

log.Print(event.PubKey)
log.Print(event.ID)
log.Print(event.Sig)

for status := range statuses {
	switch status.Status {
	case nostr.PublishStatusSent:
		fmt.Printf("Sent event %s to '%s'.\n", event.ID, status.Relay)
	case nostr.PublishStatusFailed:
		fmt.Printf("Failed to send event %s to '%s'.\n", event.ID, status.Relay)
	case nostr.PublishStatusSucceeded:
		fmt.Printf("Event seen %s on '%s'.\n", event.ID, status.Relay)
	}
}
Generating a key
sk, _ := nostr.GenerateKey()

fmt.Println("sk:", sk)
fmt.Println("pk:", nostr.GetPublicKey(sk))
Example Program
go run example/example.go

Documentation

Index

Examples

Constants

View Source
const (
	KindSetMetadata            int = 0
	KindTextNote               int = 1
	KindRecommendServer        int = 2
	KindContactList            int = 3
	KindEncryptedDirectMessage int = 4
	KindDeletion               int = 5
	KindBoost                  int = 6
	KindReaction               int = 7
	KindChannelCreation        int = 40
	KindChannelMetadata        int = 41
	KindChannelMessage         int = 42
	KindChannelHideMessage     int = 43
	KindChannelMuteUser        int = 44
)

Variables

This section is empty.

Functions

func ContainsPrefixOf

func ContainsPrefixOf(haystack []string, needle string) bool

func FilterEqual

func FilterEqual(a Filter, b Filter) bool

func GeneratePrivateKey

func GeneratePrivateKey() string

func GetPublicKey

func GetPublicKey(sk string) (string, error)

func NormalizeURL

func NormalizeURL(u string) string
Example
fmt.Println(NormalizeURL(""))
fmt.Println(NormalizeURL("wss://x.com/y"))
fmt.Println(NormalizeURL("wss://x.com/y/"))
fmt.Println(NormalizeURL("http://x.com/y"))
fmt.Println(NormalizeURL(NormalizeURL("http://x.com/y")))
fmt.Println(NormalizeURL("wss://x.com"))
fmt.Println(NormalizeURL("wss://x.com/"))
fmt.Println(NormalizeURL(NormalizeURL(NormalizeURL("wss://x.com/"))))
fmt.Println(NormalizeURL("x.com"))
fmt.Println(NormalizeURL("x.com/"))
fmt.Println(NormalizeURL("x.com////"))
fmt.Println(NormalizeURL("x.com/?x=23"))
Output:


wss://x.com/y
wss://x.com/y
ws://x.com/y
ws://x.com/y
wss://x.com
wss://x.com
wss://x.com
wss://x.com
wss://x.com
wss://x.com
wss://x.com?x=23

func Similar

func Similar[E constraints.Ordered](as, bs []E) bool

func Unique

func Unique(all chan EventMessage) chan Event

Types

type Connection

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

func NewConnection

func NewConnection(socket *websocket.Conn) *Connection

func (*Connection) Close

func (c *Connection) Close() error

func (*Connection) WriteJSON

func (c *Connection) WriteJSON(v interface{}) error

func (*Connection) WriteMessage

func (c *Connection) WriteMessage(messageType int, data []byte) error

type Event

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

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

func (evt Event) GetExtra(key string) any

GetExtra tries to get a value under the given key that may be present in the event object but is hidden in the basic type since it is out of the spec.

func (Event) GetExtraBoolean

func (evt Event) GetExtraBoolean(key string) bool

GetExtraBoolean is like Event.GetExtra, but only works if the value is a boolean, otherwise returns the zero-value.

func (Event) GetExtraNumber

func (evt Event) GetExtraNumber(key string) float64

GetExtraNumber is like Event.GetExtra, but only works if the value is a float64, otherwise returns the zero-value.

func (Event) GetExtraString

func (evt Event) GetExtraString(key string) string

GetExtraString is like Event.GetExtra, but only works if the value is a string, otherwise returns the zero-value.

func (*Event) GetID

func (evt *Event) GetID() string

GetID serializes and returns the event ID as a string

func (Event) MarshalJSON

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

func (*Event) Serialize

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

Serialize outputs a byte array that can be hashed/signed to identify/authenticate

func (*Event) SetExtra

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

SetExtra sets an out-of-the-spec value under the given key into the event object.

func (*Event) Sign

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

Sign signs an event with a given privateKey

func (*Event) UnmarshalJSON

func (evt *Event) UnmarshalJSON(payload []byte) error

type EventMessage

type EventMessage struct {
	Event Event
	Relay string
}

type Filter

type Filter struct {
	IDs     []string
	Kinds   []int
	Authors []string
	Tags    TagMap
	Since   *time.Time
	Until   *time.Time
	Limit   int
}

func (Filter) MarshalJSON

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

func (Filter) Matches

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

func (Filter) String

func (ef Filter) String() string

func (*Filter) UnmarshalJSON

func (f *Filter) UnmarshalJSON(payload []byte) error

type Filters

type Filters []Filter

func (Filters) Match

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

func (Filters) String

func (eff Filters) String() string

type NoticeMessage

type NoticeMessage struct {
	Message string
	Relay   string
}

type ProfileMetadata

type ProfileMetadata struct {
	Name    string `json:"name,omitempty"`
	About   string `json:"about,omitempty"`
	Picture string `json:"picture,omitempty"`
	NIP05   string `json:"nip05,omitempty"`
}

func ParseMetadata

func ParseMetadata(event Event) (*ProfileMetadata, error)

type PublishStatus

type PublishStatus struct {
	Relay  string
	Status Status
}

type Relay

type Relay struct {
	URL string

	Connection *Connection

	Notices         chan string
	ConnectionError chan error
	// contains filtered or unexported fields
}

func RelayConnect

func RelayConnect(url string) (*Relay, error)

RelayConnect forwards calls to RelayConnectContext with a background context.

func RelayConnectContext

func RelayConnectContext(ctx context.Context, url string) (*Relay, error)

RelayConnectContext creates a new relay client and connects to a canonical URL using Relay.ConnectContext, passing ctx as is.

func (*Relay) Close

func (r *Relay) Close() error

func (*Relay) Connect

func (r *Relay) Connect() error

Connect calls ConnectContext with a background context.

func (*Relay) ConnectContext

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

ConnectContext 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.

func (*Relay) PrepareSubscription

func (r *Relay) PrepareSubscription() *Subscription

func (Relay) Publish

func (r Relay) Publish(event Event) chan Status

func (*Relay) QuerySync

func (r *Relay) QuerySync(filter Filter, timeout time.Duration) []Event

func (*Relay) String

func (r *Relay) String() string

func (*Relay) Subscribe

func (r *Relay) Subscribe(filters Filters) *Subscription

type RelayPool

type RelayPool struct {
	SecretKey *string

	Policies s.MapOf[string, RelayPoolPolicy]
	Relays   s.MapOf[string, *Relay]

	Notices chan *NoticeMessage
	// contains filtered or unexported fields
}

func NewRelayPool

func NewRelayPool() *RelayPool

New creates a new RelayPool with no relays in it

func (*RelayPool) Add

func (r *RelayPool) Add(url string, policy RelayPoolPolicy) <-chan error

Add calls AddContext with background context in a separate goroutine, sending any connection error over the returned channel.

The returned channel is closed once the connection is successfully established or RelayConnectContext returned an error.

func (*RelayPool) AddContext

func (r *RelayPool) AddContext(ctx context.Context, url string, policy RelayPoolPolicy) error

AddContext connects to a relay at a canonical version specified by the url and adds it to the pool. The returned error is non-nil only on connection errors, including an expired context before the connection is complete.

Once successfully connected, AddContext returns and the context expiration has no effect: call r.Remove to close the connection and delete a relay from the pool.

func (*RelayPool) PublishEvent

func (r *RelayPool) PublishEvent(evt *Event) (*Event, chan PublishStatus, error)

func (*RelayPool) Remove

func (r *RelayPool) Remove(url string)

Remove removes a relay from the pool.

func (*RelayPool) Sub

func (r *RelayPool) Sub(filters Filters) (subID string, events chan EventMessage, unsubscribe func())

Sub subscribes to events matching the passed filters and returns the subscription ID, a channel which you should pass into Unique to get unique events, and a function which you should call to clean up and close your subscription so that the relay doesn't block you.

type RelayPoolPolicy

type RelayPoolPolicy interface {
	ShouldRead(Filters) bool
	ShouldWrite(*Event) bool
}

type SimplePolicy

type SimplePolicy struct {
	Read  bool
	Write bool
}

func (SimplePolicy) ShouldRead

func (s SimplePolicy) ShouldRead(_ Filters) bool

func (SimplePolicy) ShouldWrite

func (s SimplePolicy) ShouldWrite(_ *Event) bool

type Status

type Status int
const (
	PublishStatusSent      Status = 0
	PublishStatusFailed    Status = -1
	PublishStatusSucceeded Status = 1
)

func (Status) String

func (s Status) String() string

type Subscription

type Subscription struct {
	Relay             *Relay
	Filters           Filters
	Events            chan Event
	EndOfStoredEvents chan struct{}
	// contains filtered or unexported fields
}

func (*Subscription) Fire

func (sub *Subscription) Fire()

func (*Subscription) Sub

func (sub *Subscription) Sub(filters Filters)

func (*Subscription) Unsub

func (sub *Subscription) Unsub()

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) AppendUnique

func (tags Tags) AppendUnique(tag Tag) Tags

AppendUnique appends a tag if it doesn't exist yet, otherwise does nothing

func (Tags) ContainsAny

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

func (Tags) FilterOut

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

FilterOut removes all tags that match the prefix, see Tag.StartsWith

func (Tags) GetAll

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

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

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 interface{}) error

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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