mirror of
https://github.com/nbd-wtf/go-nostr.git
synced 2025-11-18 18:16:50 +01:00
Added some NIP-42 functionality to the client (relay.go) (#38)
This commit is contained in:
78
relay.go
78
relay.go
@@ -7,6 +7,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
s "github.com/SaveTheRbtz/generic-sync-map-go"
|
||||
@@ -40,15 +41,16 @@ type Relay struct {
|
||||
Connection *Connection
|
||||
subscriptions s.MapOf[string, *Subscription]
|
||||
|
||||
Challenges chan string // NIP-42 Challenges
|
||||
Notices chan string
|
||||
ConnectionError chan error
|
||||
|
||||
okCallbacks s.MapOf[string, func(bool)]
|
||||
}
|
||||
|
||||
// RelayConnect returns a relay object connected to url
|
||||
// Once successfully connected, cancelling ctx has no effect
|
||||
// To close the connection, call r.Close()
|
||||
// RelayConnect returns a relay object connected to url.
|
||||
// Once successfully connected, cancelling ctx has no effect.
|
||||
// To close the connection, call r.Close().
|
||||
func RelayConnect(ctx context.Context, url string) (*Relay, error) {
|
||||
r := &Relay{URL: NormalizeURL(url)}
|
||||
err := r.Connect(ctx)
|
||||
@@ -80,6 +82,7 @@ func (r *Relay) Connect(ctx context.Context) error {
|
||||
return fmt.Errorf("error opening websocket to '%s': %w", r.URL, err)
|
||||
}
|
||||
|
||||
r.Challenges = make(chan string)
|
||||
r.Notices = make(chan string)
|
||||
r.ConnectionError = make(chan error)
|
||||
|
||||
@@ -121,6 +124,12 @@ func (r *Relay) Connect(ctx context.Context) error {
|
||||
var content string
|
||||
json.Unmarshal(jsonMessage[1], &content)
|
||||
r.Notices <- content
|
||||
case "AUTH":
|
||||
var challenge string
|
||||
json.Unmarshal(jsonMessage[1], &challenge)
|
||||
go func() {
|
||||
r.Challenges <- challenge
|
||||
}()
|
||||
case "EVENT":
|
||||
if len(jsonMessage) < 3 {
|
||||
continue
|
||||
@@ -185,8 +194,8 @@ func (r *Relay) Connect(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Publish sends an "EVENT" command to the relay r as in NIP-01
|
||||
// status can be: success, failed, or sent (no response from relay before ctx times out)
|
||||
// Publish sends an "EVENT" command to the relay r as in NIP-01.
|
||||
// Status can be: success, failed, or sent (no response from relay before ctx times out).
|
||||
func (r *Relay) Publish(ctx context.Context, event Event) Status {
|
||||
status := PublishStatusFailed
|
||||
|
||||
@@ -242,9 +251,62 @@ func (r *Relay) Publish(ctx context.Context, event Event) Status {
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
// Auth sends an "AUTH" command client -> relay as in NIP-42.
|
||||
// Status can be: success, failed, or sent (no response from relay before ctx times out).
|
||||
func (r *Relay) Auth(ctx context.Context, event Event) Status {
|
||||
status := PublishStatusFailed
|
||||
|
||||
// data races on status variable without this mutex
|
||||
var mu sync.Mutex
|
||||
|
||||
if _, ok := ctx.Deadline(); !ok {
|
||||
// if no timeout is set, force it to 3 seconds
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(ctx, 3*time.Second)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
// make it cancellable so we can stop everything upon receiving an "OK"
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
// listen for an OK callback
|
||||
okCallback := func(ok bool) {
|
||||
mu.Lock()
|
||||
if ok {
|
||||
status = PublishStatusSucceeded
|
||||
} else {
|
||||
status = PublishStatusFailed
|
||||
}
|
||||
mu.Unlock()
|
||||
cancel()
|
||||
}
|
||||
r.okCallbacks.Store(event.ID, okCallback)
|
||||
defer r.okCallbacks.Delete(event.ID)
|
||||
|
||||
// send AUTH
|
||||
if err := r.Connection.WriteJSON([]interface{}{"AUTH", event}); err != nil {
|
||||
// status will be "failed"
|
||||
return status
|
||||
} else {
|
||||
// use mu.Lock() just in case the okCallback got called, extremely unlikely.
|
||||
mu.Lock()
|
||||
status = PublishStatusSent
|
||||
mu.Unlock()
|
||||
}
|
||||
// the context either times out, and the status is "sent"
|
||||
// or the okCallback is called and the status is set to "succeeded" or "failed"
|
||||
// NIP-42 does not mandate an "OK" reply to an "AUTH" message
|
||||
<-ctx.Done()
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
return status
|
||||
}
|
||||
|
||||
// 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).
|
||||
func (r *Relay) Subscribe(ctx context.Context, filters Filters) *Subscription {
|
||||
if r.Connection == nil {
|
||||
panic(fmt.Errorf("must call .Connect() first before calling .Subscribe()"))
|
||||
|
||||
Reference in New Issue
Block a user