diff --git a/fetch.go b/fetch.go index 6bd40b9..ea84418 100644 --- a/fetch.go +++ b/fetch.go @@ -8,11 +8,12 @@ import ( "github.com/nbd-wtf/go-nostr" "github.com/nbd-wtf/go-nostr/nip05" "github.com/nbd-wtf/go-nostr/nip19" + "github.com/nbd-wtf/go-nostr/sdk/hints" ) var fetch = &cli.Command{ Name: "fetch", - Usage: "fetches events related to the given nip19 or nip05 code from the included relay hints or the author's NIP-65 relays.", + Usage: "fetches events related to the given nip19 or nip05 code from the included relay hints or the author's outbox relays.", Description: `example usage: nak fetch nevent1qqsxrwm0hd3s3fddh4jc2574z3xzufq6qwuyz2rvv3n087zvym3dpaqprpmhxue69uhhqatzd35kxtnjv4kxz7tfdenju6t0xpnej4 echo npub1h8spmtw9m2huyv6v2j2qd5zv956z2zdugl6mgx02f2upffwpm3nqv0j4ps | nak fetch --relay wss://relay.nostr.band`, @@ -90,8 +91,11 @@ var fetch = &cli.Command{ } if authorHint != "" { - relays := sys.FetchOutboxRelays(ctx, authorHint, 3) for _, url := range relays { + sys.Hints.Save(authorHint, nostr.NormalizeURL(url), hints.LastInHint, nostr.Now()) + } + + for _, url := range sys.FetchOutboxRelays(ctx, authorHint, 3) { relays = append(relays, url) } } diff --git a/go.mod b/go.mod index 16a0e3f..13603d8 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/json-iterator/go v1.1.12 github.com/mailru/easyjson v0.9.0 github.com/markusmobius/go-dateparser v1.2.3 - github.com/nbd-wtf/go-nostr v0.47.3 + github.com/nbd-wtf/go-nostr v0.48.0 ) require ( diff --git a/go.sum b/go.sum index 7143878..7abdca9 100644 --- a/go.sum +++ b/go.sum @@ -117,8 +117,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/nbd-wtf/go-nostr v0.47.3 h1:KM86+0rLa8jPvt11akST1T0ffhrs6KpzvJ2CLYo765c= -github.com/nbd-wtf/go-nostr v0.47.3/go.mod h1:O6n8bv+KktkEs+4svL7KN/OSnOWB5LzcZbuKjxnpRD0= +github.com/nbd-wtf/go-nostr v0.48.0 h1:GYu6k6wRzSxYpra4pzMRk3R8xdUW8fac+trQtt6YD0o= +github.com/nbd-wtf/go-nostr v0.48.0/go.mod h1:O6n8bv+KktkEs+4svL7KN/OSnOWB5LzcZbuKjxnpRD0= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= diff --git a/helpers.go b/helpers.go index 40ce39b..e364d8a 100644 --- a/helpers.go +++ b/helpers.go @@ -19,18 +19,15 @@ import ( "github.com/nbd-wtf/go-nostr/sdk" ) -var sys = sdk.NewSystem() +var sys *sdk.System + +var ( + hintsFilePath string + hintsFileExists bool +) var json = jsoniter.ConfigFastest -func init() { - sys.Pool = nostr.NewSimplePool(context.Background(), - nostr.WithRelayOptions( - nostr.WithRequestHeader(http.Header{textproto.CanonicalMIMEHeaderKey("user-agent"): {"nak/b"}}), - ), - ) -} - const ( LINE_PROCESSING_ERROR = iota ) diff --git a/main.go b/main.go index aae4e37..9b4e98d 100644 --- a/main.go +++ b/main.go @@ -2,9 +2,15 @@ package main import ( "context" + "net/http" + "net/textproto" "os" + "path/filepath" "github.com/fiatjaf/cli/v3" + "github.com/nbd-wtf/go-nostr" + "github.com/nbd-wtf/go-nostr/sdk" + "github.com/nbd-wtf/go-nostr/sdk/hints/memoryh" ) var version string = "debug" @@ -30,9 +36,15 @@ var app = &cli.Command{ serve, encrypt, decrypt, + outbox, }, Version: version, Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "config-path", + Hidden: true, + Persistent: true, + }, &cli.BoolFlag{ Name: "quiet", Usage: "do not print logs and info messages to stderr, use -qq to also not print anything to stdout", @@ -50,6 +62,59 @@ var app = &cli.Command{ }, }, }, + Before: func(ctx context.Context, c *cli.Command) error { + configPath := c.String("config-path") + if configPath == "" { + if home, err := os.UserHomeDir(); err == nil { + configPath = filepath.Join(home, ".config/nak") + } + } + if configPath != "" { + hintsFilePath = filepath.Join(configPath, "outbox/hints.db") + } + if hintsFilePath != "" { + if _, err := os.Stat(hintsFilePath); !os.IsNotExist(err) { + hintsFileExists = true + } + } + + if hintsFilePath != "" { + if data, err := os.ReadFile(hintsFilePath); err == nil { + hintsdb := memoryh.NewHintDB() + if err := json.Unmarshal(data, &hintsdb); err == nil { + sys = sdk.NewSystem( + sdk.WithHintsDB(hintsdb), + ) + goto systemOperational + } + } + } + + sys = sdk.NewSystem() + + systemOperational: + sys.Pool = nostr.NewSimplePool(context.Background(), + nostr.WithAuthorKindQueryMiddleware(sys.TrackQueryAttempts), + nostr.WithEventMiddleware(sys.TrackEventHints), + nostr.WithRelayOptions( + nostr.WithRequestHeader(http.Header{textproto.CanonicalMIMEHeaderKey("user-agent"): {"nak/b"}}), + ), + ) + + return nil + }, + After: func(ctx context.Context, c *cli.Command) error { + // save hints database on exit + if hintsFileExists { + data, err := json.Marshal(sys.Hints) + if err != nil { + return err + } + return os.WriteFile(hintsFilePath, data, 0644) + } + + return nil + }, } func main() { diff --git a/outbox.go b/outbox.go new file mode 100644 index 0000000..7ee859e --- /dev/null +++ b/outbox.go @@ -0,0 +1,68 @@ +package main + +import ( + "context" + "fmt" + "os" + "path/filepath" + + "github.com/fiatjaf/cli/v3" + "github.com/nbd-wtf/go-nostr" +) + +var outbox = &cli.Command{ + Name: "outbox", + Usage: "manage outbox relay hints database", + DisableSliceFlagSeparator: true, + Commands: []*cli.Command{ + { + Name: "init", + Usage: "initialize the outbox hints database", + DisableSliceFlagSeparator: true, + Action: func(ctx context.Context, c *cli.Command) error { + if hintsFileExists { + return nil + } + if hintsFilePath == "" { + return fmt.Errorf("couldn't find a place to store the hints, pass --config-path to fix.") + } + + if err := os.MkdirAll(filepath.Dir(hintsFilePath), 0777); err == nil { + if err := os.WriteFile(hintsFilePath, []byte("{}"), 0644); err != nil { + return fmt.Errorf("failed to create hints database: %w", err) + } + } + + log("initialized hints database at %s\n", hintsFilePath) + return nil + }, + }, + { + Name: "list", + Usage: "list outbox relays for a given pubkey", + ArgsUsage: "", + DisableSliceFlagSeparator: true, + Action: func(ctx context.Context, c *cli.Command) error { + if !hintsFileExists { + log("running with temporary fragile data.\n") + log("call `nak outbox init` to setup persistence.\n") + } + + if c.Args().Len() != 1 { + return fmt.Errorf("expected exactly one argument (pubkey)") + } + + pubkey := c.Args().First() + if !nostr.IsValidPublicKey(pubkey) { + return fmt.Errorf("invalid public key: %s", pubkey) + } + + for _, relay := range sys.FetchOutboxRelays(ctx, pubkey, 6) { + stdout(relay) + } + + return nil + }, + }, + }, +}