mirror of
https://github.com/fiatjaf/nak.git
synced 2025-12-08 16:48:51 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f48c29d0f | ||
|
|
703c186958 | ||
|
|
7ae2e686cb | ||
|
|
9547711e8d | ||
|
|
50119e21e6 | ||
|
|
33f4272dd0 | ||
|
|
7b6f387aad | ||
|
|
b1a03800e6 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
nak
|
nak
|
||||||
mnt
|
mnt
|
||||||
|
nak.exe
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/urfave/cli/v3"
|
|
||||||
"github.com/nbd-wtf/go-nostr"
|
"github.com/nbd-wtf/go-nostr"
|
||||||
"github.com/nbd-wtf/go-nostr/nip19"
|
"github.com/nbd-wtf/go-nostr/nip19"
|
||||||
"github.com/nbd-wtf/go-nostr/nip46"
|
"github.com/nbd-wtf/go-nostr/nip46"
|
||||||
|
"github.com/urfave/cli/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var bunker = &cli.Command{
|
var bunker = &cli.Command{
|
||||||
@@ -49,7 +49,7 @@ var bunker = &cli.Command{
|
|||||||
qs := url.Values{}
|
qs := url.Values{}
|
||||||
relayURLs := make([]string, 0, c.Args().Len())
|
relayURLs := make([]string, 0, c.Args().Len())
|
||||||
if relayUrls := c.Args().Slice(); len(relayUrls) > 0 {
|
if relayUrls := c.Args().Slice(); len(relayUrls) > 0 {
|
||||||
relays := connectToAllRelays(ctx, relayUrls, false)
|
relays := connectToAllRelays(ctx, c, relayUrls, nil)
|
||||||
if len(relays) == 0 {
|
if len(relays) == 0 {
|
||||||
log("failed to connect to any of the given relays.\n")
|
log("failed to connect to any of the given relays.\n")
|
||||||
os.Exit(3)
|
os.Exit(3)
|
||||||
|
|||||||
7
count.go
7
count.go
@@ -6,10 +6,10 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/urfave/cli/v3"
|
|
||||||
"github.com/nbd-wtf/go-nostr"
|
"github.com/nbd-wtf/go-nostr"
|
||||||
"github.com/nbd-wtf/go-nostr/nip45"
|
"github.com/nbd-wtf/go-nostr/nip45"
|
||||||
"github.com/nbd-wtf/go-nostr/nip45/hyperloglog"
|
"github.com/nbd-wtf/go-nostr/nip45/hyperloglog"
|
||||||
|
"github.com/urfave/cli/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var count = &cli.Command{
|
var count = &cli.Command{
|
||||||
@@ -70,10 +70,7 @@ var count = &cli.Command{
|
|||||||
biggerUrlSize := 0
|
biggerUrlSize := 0
|
||||||
relayUrls := c.Args().Slice()
|
relayUrls := c.Args().Slice()
|
||||||
if len(relayUrls) > 0 {
|
if len(relayUrls) > 0 {
|
||||||
relays := connectToAllRelays(ctx,
|
relays := connectToAllRelays(ctx, c, relayUrls, nil)
|
||||||
relayUrls,
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
if len(relays) == 0 {
|
if len(relays) == 0 {
|
||||||
log("failed to connect to any of the given relays.\n")
|
log("failed to connect to any of the given relays.\n")
|
||||||
os.Exit(3)
|
os.Exit(3)
|
||||||
|
|||||||
12
dvm.go
12
dvm.go
@@ -7,7 +7,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
|
||||||
"github.com/nbd-wtf/go-nostr"
|
"github.com/nbd-wtf/go-nostr"
|
||||||
"github.com/nbd-wtf/go-nostr/nip90"
|
"github.com/nbd-wtf/go-nostr/nip90"
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
@@ -60,7 +59,7 @@ var dvm = &cli.Command{
|
|||||||
Flags: flags,
|
Flags: flags,
|
||||||
Action: func(ctx context.Context, c *cli.Command) error {
|
Action: func(ctx context.Context, c *cli.Command) error {
|
||||||
relayUrls := c.StringSlice("relay")
|
relayUrls := c.StringSlice("relay")
|
||||||
relays := connectToAllRelays(ctx, relayUrls, false)
|
relays := connectToAllRelays(ctx, c, relayUrls, nil)
|
||||||
if len(relays) == 0 {
|
if len(relays) == 0 {
|
||||||
log("failed to connect to any of the given relays.\n")
|
log("failed to connect to any of the given relays.\n")
|
||||||
os.Exit(3)
|
os.Exit(3)
|
||||||
@@ -103,10 +102,7 @@ var dvm = &cli.Command{
|
|||||||
log("- publishing job request... ")
|
log("- publishing job request... ")
|
||||||
first := true
|
first := true
|
||||||
for res := range sys.Pool.PublishMany(ctx, relayUrls, evt) {
|
for res := range sys.Pool.PublishMany(ctx, relayUrls, evt) {
|
||||||
cleanUrl, ok := strings.CutPrefix(res.RelayURL, "wss://")
|
cleanUrl, _ := strings.CutPrefix(res.RelayURL, "wss://")
|
||||||
if !ok {
|
|
||||||
cleanUrl = res.RelayURL
|
|
||||||
}
|
|
||||||
|
|
||||||
if !first {
|
if !first {
|
||||||
log(", ")
|
log(", ")
|
||||||
@@ -114,9 +110,9 @@ var dvm = &cli.Command{
|
|||||||
first = false
|
first = false
|
||||||
|
|
||||||
if res.Error != nil {
|
if res.Error != nil {
|
||||||
log("%s: %s", color.RedString(cleanUrl), res.Error)
|
log("%s: %s", colors.errorf(cleanUrl), res.Error)
|
||||||
} else {
|
} else {
|
||||||
log("%s: ok", color.GreenString(cleanUrl))
|
log("%s: ok", colors.successf(cleanUrl))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
141
event.go
141
event.go
@@ -8,6 +8,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/fatih/color"
|
||||||
"github.com/mailru/easyjson"
|
"github.com/mailru/easyjson"
|
||||||
"github.com/nbd-wtf/go-nostr"
|
"github.com/nbd-wtf/go-nostr"
|
||||||
"github.com/nbd-wtf/go-nostr/nip13"
|
"github.com/nbd-wtf/go-nostr/nip13"
|
||||||
@@ -133,8 +134,17 @@ example:
|
|||||||
Action: func(ctx context.Context, c *cli.Command) error {
|
Action: func(ctx context.Context, c *cli.Command) error {
|
||||||
// try to connect to the relays here
|
// try to connect to the relays here
|
||||||
var relays []*nostr.Relay
|
var relays []*nostr.Relay
|
||||||
|
|
||||||
|
// these are defaults, they will be replaced if we use the magic dynamic thing
|
||||||
|
logthis := func(relayUrl string, s string, args ...any) { log(s, args...) }
|
||||||
|
colorizethis := func(relayUrl string, colorize func(string, ...any) string) {}
|
||||||
|
|
||||||
if relayUrls := c.Args().Slice(); len(relayUrls) > 0 {
|
if relayUrls := c.Args().Slice(); len(relayUrls) > 0 {
|
||||||
relays = connectToAllRelays(ctx, relayUrls, false)
|
relays = connectToAllRelays(ctx, c, relayUrls, nil,
|
||||||
|
nostr.WithAuthHandler(func(ctx context.Context, authEvent nostr.RelayEvent) error {
|
||||||
|
return authSigner(ctx, c, func(s string, args ...any) {}, authEvent)
|
||||||
|
}),
|
||||||
|
)
|
||||||
if len(relays) == 0 {
|
if len(relays) == 0 {
|
||||||
log("failed to connect to any of the given relays.\n")
|
log("failed to connect to any of the given relays.\n")
|
||||||
os.Exit(3)
|
os.Exit(3)
|
||||||
@@ -209,13 +219,19 @@ example:
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, etag := range c.StringSlice("e") {
|
for _, etag := range c.StringSlice("e") {
|
||||||
tags = tags.AppendUnique([]string{"e", etag})
|
if tags.FindWithValue("e", etag) == nil {
|
||||||
|
tags = append(tags, nostr.Tag{"e", etag})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for _, ptag := range c.StringSlice("p") {
|
for _, ptag := range c.StringSlice("p") {
|
||||||
tags = tags.AppendUnique([]string{"p", ptag})
|
if tags.FindWithValue("p", ptag) == nil {
|
||||||
|
tags = append(tags, nostr.Tag{"p", ptag})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for _, dtag := range c.StringSlice("d") {
|
for _, dtag := range c.StringSlice("d") {
|
||||||
tags = tags.AppendUnique([]string{"d", dtag})
|
if tags.FindWithValue("d", dtag) == nil {
|
||||||
|
tags = append(tags, nostr.Tag{"d", dtag})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if len(tags) > 0 {
|
if len(tags) > 0 {
|
||||||
for _, tag := range tags {
|
for _, tag := range tags {
|
||||||
@@ -295,37 +311,104 @@ example:
|
|||||||
successRelays := make([]string, 0, len(relays))
|
successRelays := make([]string, 0, len(relays))
|
||||||
if len(relays) > 0 {
|
if len(relays) > 0 {
|
||||||
os.Stdout.Sync()
|
os.Stdout.Sync()
|
||||||
for _, relay := range relays {
|
|
||||||
publish:
|
if supportsDynamicMultilineMagic() {
|
||||||
log("publishing to %s... ", relay.URL)
|
// overcomplicated multiline rendering magic
|
||||||
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
err := relay.Publish(ctx, evt)
|
urls := make([]string, len(relays))
|
||||||
if err == nil {
|
lines := make([][][]byte, len(urls))
|
||||||
// published fine
|
flush := func() {
|
||||||
log("success.\n")
|
for _, line := range lines {
|
||||||
successRelays = append(successRelays, relay.URL)
|
for _, part := range line {
|
||||||
continue // continue to next relay
|
os.Stderr.Write(part)
|
||||||
}
|
}
|
||||||
|
os.Stderr.Write([]byte{'\n'})
|
||||||
// error publishing
|
|
||||||
if strings.HasPrefix(err.Error(), "msg: auth-required:") && kr != nil && doAuth {
|
|
||||||
// if the relay is requesting auth and we can auth, let's do it
|
|
||||||
pk, _ := kr.GetPublicKey(ctx)
|
|
||||||
log("performing auth as %s... ", pk)
|
|
||||||
if err := relay.Auth(ctx, func(authEvent *nostr.Event) error {
|
|
||||||
return kr.SignEvent(ctx, authEvent)
|
|
||||||
}); err == nil {
|
|
||||||
// try to publish again, but this time don't try to auth again
|
|
||||||
doAuth = false
|
|
||||||
goto publish
|
|
||||||
} else {
|
|
||||||
log("auth error: %s. ", err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log("failed: %s\n", err)
|
render := func() {
|
||||||
|
clearLines(len(lines))
|
||||||
|
flush()
|
||||||
|
}
|
||||||
|
flush()
|
||||||
|
|
||||||
|
logthis = func(relayUrl, s string, args ...any) {
|
||||||
|
idx := slices.Index(urls, relayUrl)
|
||||||
|
lines[idx] = append(lines[idx], []byte(fmt.Sprintf(s, args...)))
|
||||||
|
render()
|
||||||
|
}
|
||||||
|
colorizethis = func(relayUrl string, colorize func(string, ...any) string) {
|
||||||
|
cleanUrl, _ := strings.CutPrefix(relayUrl, "wss://")
|
||||||
|
idx := slices.Index(urls, relayUrl)
|
||||||
|
lines[idx][0] = []byte(fmt.Sprintf("publishing to %s... ", colorize(cleanUrl)))
|
||||||
|
render()
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, relay := range relays {
|
||||||
|
urls[i] = relay.URL
|
||||||
|
lines[i] = make([][]byte, 1, 3)
|
||||||
|
colorizethis(relay.URL, color.CyanString)
|
||||||
|
}
|
||||||
|
render()
|
||||||
|
|
||||||
|
for res := range sys.Pool.PublishMany(ctx, urls, evt) {
|
||||||
|
if res.Error == nil {
|
||||||
|
colorizethis(res.RelayURL, colors.successf)
|
||||||
|
logthis(res.RelayURL, "success.")
|
||||||
|
successRelays = append(successRelays, res.RelayURL)
|
||||||
|
} else {
|
||||||
|
colorizethis(res.RelayURL, colors.errorf)
|
||||||
|
|
||||||
|
// in this case it's likely that the lowest-level error is the one that will be more helpful
|
||||||
|
low := unwrapAll(res.Error)
|
||||||
|
|
||||||
|
// hack for some messages such as from relay.westernbtc.com
|
||||||
|
msg := strings.ReplaceAll(low.Error(), evt.PubKey, "author")
|
||||||
|
|
||||||
|
// do not allow the message to overflow the term window
|
||||||
|
msg = clampMessage(msg, 20+len(res.RelayURL))
|
||||||
|
|
||||||
|
logthis(res.RelayURL, msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// normal dumb flow
|
||||||
|
for _, relay := range relays {
|
||||||
|
publish:
|
||||||
|
cleanUrl, _ := strings.CutPrefix(relay.URL, "wss://")
|
||||||
|
log("publishing to %s... ", color.CyanString(cleanUrl))
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
err := relay.Publish(ctx, evt)
|
||||||
|
if err == nil {
|
||||||
|
// published fine
|
||||||
|
log("success.\n")
|
||||||
|
successRelays = append(successRelays, relay.URL)
|
||||||
|
continue // continue to next relay
|
||||||
|
}
|
||||||
|
|
||||||
|
// error publishing
|
||||||
|
if strings.HasPrefix(err.Error(), "msg: auth-required:") && kr != nil && doAuth {
|
||||||
|
// if the relay is requesting auth and we can auth, let's do it
|
||||||
|
pk, _ := kr.GetPublicKey(ctx)
|
||||||
|
npub, _ := nip19.EncodePublicKey(pk)
|
||||||
|
log("authenticating as %s... ", color.YellowString("%s…%s", npub[0:7], npub[58:]))
|
||||||
|
if err := relay.Auth(ctx, func(authEvent *nostr.Event) error {
|
||||||
|
return kr.SignEvent(ctx, authEvent)
|
||||||
|
}); err == nil {
|
||||||
|
// try to publish again, but this time don't try to auth again
|
||||||
|
doAuth = false
|
||||||
|
goto publish
|
||||||
|
} else {
|
||||||
|
log("auth error: %s. ", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log("failed: %s\n", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(successRelays) > 0 && c.Bool("nevent") {
|
if len(successRelays) > 0 && c.Bool("nevent") {
|
||||||
nevent, _ := nip19.EncodeEvent(evt.ID, successRelays, evt.PubKey)
|
nevent, _ := nip19.EncodeEvent(evt.ID, successRelays, evt.PubKey)
|
||||||
log(nevent + "\n")
|
log(nevent + "\n")
|
||||||
|
|||||||
20
fs_windows.go
Normal file
20
fs_windows.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
var fsCmd = &cli.Command{
|
||||||
|
Name: "fs",
|
||||||
|
Usage: "mount a FUSE filesystem that exposes Nostr events as files.",
|
||||||
|
Description: `doesn't work on Windows.`,
|
||||||
|
DisableSliceFlagSeparator: true,
|
||||||
|
Action: func(ctx context.Context, c *cli.Command) error {
|
||||||
|
return fmt.Errorf("this doesn't work on Windows.")
|
||||||
|
},
|
||||||
|
}
|
||||||
8
go.mod
8
go.mod
@@ -17,9 +17,10 @@ require (
|
|||||||
github.com/mailru/easyjson v0.9.0
|
github.com/mailru/easyjson v0.9.0
|
||||||
github.com/mark3labs/mcp-go v0.8.3
|
github.com/mark3labs/mcp-go v0.8.3
|
||||||
github.com/markusmobius/go-dateparser v1.2.3
|
github.com/markusmobius/go-dateparser v1.2.3
|
||||||
github.com/nbd-wtf/go-nostr v0.51.6
|
github.com/nbd-wtf/go-nostr v0.51.8
|
||||||
github.com/urfave/cli/v3 v3.0.0-beta1
|
github.com/urfave/cli/v3 v3.0.0-beta1
|
||||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394
|
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394
|
||||||
|
golang.org/x/term v0.30.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -28,13 +29,13 @@ require (
|
|||||||
github.com/btcsuite/btcd v0.24.2 // indirect
|
github.com/btcsuite/btcd v0.24.2 // indirect
|
||||||
github.com/btcsuite/btcd/btcutil v1.1.5 // indirect
|
github.com/btcsuite/btcd/btcutil v1.1.5 // indirect
|
||||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect
|
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect
|
||||||
github.com/bytedance/sonic v1.13.1 // indirect
|
github.com/bytedance/sonic v1.13.2 // indirect
|
||||||
github.com/bytedance/sonic/loader v0.2.4 // indirect
|
github.com/bytedance/sonic/loader v0.2.4 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/chzyer/logex v1.1.10 // indirect
|
github.com/chzyer/logex v1.1.10 // indirect
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 // indirect
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 // indirect
|
||||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||||
github.com/coder/websocket v1.8.12 // indirect
|
github.com/coder/websocket v1.8.13 // indirect
|
||||||
github.com/decred/dcrd/crypto/blake256 v1.1.0 // indirect
|
github.com/decred/dcrd/crypto/blake256 v1.1.0 // indirect
|
||||||
github.com/dgraph-io/ristretto v1.0.0 // indirect
|
github.com/dgraph-io/ristretto v1.0.0 // indirect
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
@@ -43,7 +44,6 @@ require (
|
|||||||
github.com/fasthttp/websocket v1.5.12 // indirect
|
github.com/fasthttp/websocket v1.5.12 // indirect
|
||||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/graph-gophers/dataloader/v7 v7.1.0 // indirect
|
|
||||||
github.com/hablullah/go-hijri v1.0.2 // indirect
|
github.com/hablullah/go-hijri v1.0.2 // indirect
|
||||||
github.com/hablullah/go-juliandays v1.0.0 // indirect
|
github.com/hablullah/go-juliandays v1.0.0 // indirect
|
||||||
github.com/jalaali/go-jalaali v0.0.0-20210801064154-80525e88d958 // indirect
|
github.com/jalaali/go-jalaali v0.0.0-20210801064154-80525e88d958 // indirect
|
||||||
|
|||||||
16
go.sum
16
go.sum
@@ -33,8 +33,8 @@ github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku
|
|||||||
github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
|
github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
|
||||||
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
|
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
|
||||||
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
|
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
|
||||||
github.com/bytedance/sonic v1.13.1 h1:Jyd5CIvdFnkOWuKXr+wm4Nyk2h0yAFsr8ucJgEasO3g=
|
github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
|
||||||
github.com/bytedance/sonic v1.13.1/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
|
github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
|
||||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||||
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
|
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
|
||||||
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||||
@@ -49,8 +49,8 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn
|
|||||||
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
|
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
|
||||||
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||||
github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo=
|
github.com/coder/websocket v1.8.13 h1:f3QZdXy7uGVz+4uCJy2nTZyM0yTBj8yANEHhqlXZ9FE=
|
||||||
github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
|
github.com/coder/websocket v1.8.13/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
|
||||||
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
@@ -102,8 +102,6 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
|
|||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/graph-gophers/dataloader/v7 v7.1.0 h1:Wn8HGF/q7MNXcvfaBnLEPEFJttVHR8zuEqP1obys/oc=
|
|
||||||
github.com/graph-gophers/dataloader/v7 v7.1.0/go.mod h1:1bKE0Dm6OUcTB/OAuYVOZctgIz7Q3d0XrYtlIzTgg6Q=
|
|
||||||
github.com/hablullah/go-hijri v1.0.2 h1:drT/MZpSZJQXo7jftf5fthArShcaMtsal0Zf/dnmp6k=
|
github.com/hablullah/go-hijri v1.0.2 h1:drT/MZpSZJQXo7jftf5fthArShcaMtsal0Zf/dnmp6k=
|
||||||
github.com/hablullah/go-hijri v1.0.2/go.mod h1:OS5qyYLDjORXzK4O1adFw9Q5WfhOcMdAKglDkcTxgWQ=
|
github.com/hablullah/go-hijri v1.0.2/go.mod h1:OS5qyYLDjORXzK4O1adFw9Q5WfhOcMdAKglDkcTxgWQ=
|
||||||
github.com/hablullah/go-juliandays v1.0.0 h1:A8YM7wIj16SzlKT0SRJc9CD29iiaUzpBLzh5hr0/5p0=
|
github.com/hablullah/go-juliandays v1.0.0 h1:A8YM7wIj16SzlKT0SRJc9CD29iiaUzpBLzh5hr0/5p0=
|
||||||
@@ -151,8 +149,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/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 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/nbd-wtf/go-nostr v0.51.6 h1:H51l39mp4dJztvtxjTNfNqNIxQyMoJMLSKt+1aGq3UU=
|
github.com/nbd-wtf/go-nostr v0.51.8 h1:CIoS+YqChcm4e1L1rfMZ3/mIwTz4CwApM2qx7MHNzmE=
|
||||||
github.com/nbd-wtf/go-nostr v0.51.6/go.mod h1:so45r53GkZq+8vkdIW2jBlKEjdHyxEMrl/1g9tEoSFQ=
|
github.com/nbd-wtf/go-nostr v0.51.8/go.mod h1:d6+DfvMWYG5pA3dmNMBJd6WCHVDDhkXbHqvfljf0Gzg=
|
||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
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.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
@@ -239,6 +237,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
|
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
|
||||||
|
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
|||||||
256
helpers.go
256
helpers.go
@@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"iter"
|
"iter"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
@@ -10,15 +11,19 @@ import (
|
|||||||
"net/textproto"
|
"net/textproto"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
jsoniter "github.com/json-iterator/go"
|
jsoniter "github.com/json-iterator/go"
|
||||||
"github.com/nbd-wtf/go-nostr"
|
"github.com/nbd-wtf/go-nostr"
|
||||||
|
"github.com/nbd-wtf/go-nostr/nip19"
|
||||||
"github.com/nbd-wtf/go-nostr/sdk"
|
"github.com/nbd-wtf/go-nostr/sdk"
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
|
"golang.org/x/term"
|
||||||
)
|
)
|
||||||
|
|
||||||
var sys *sdk.System
|
var sys *sdk.System
|
||||||
@@ -148,8 +153,9 @@ func normalizeAndValidateRelayURLs(wsurls []string) error {
|
|||||||
|
|
||||||
func connectToAllRelays(
|
func connectToAllRelays(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
|
c *cli.Command,
|
||||||
relayUrls []string,
|
relayUrls []string,
|
||||||
forcePreAuth bool,
|
preAuthSigner func(ctx context.Context, c *cli.Command, log func(s string, args ...any), authEvent nostr.RelayEvent) (err error), // if this exists we will force preauth
|
||||||
opts ...nostr.PoolOption,
|
opts ...nostr.PoolOption,
|
||||||
) []*nostr.Relay {
|
) []*nostr.Relay {
|
||||||
sys.Pool = nostr.NewSimplePool(context.Background(),
|
sys.Pool = nostr.NewSimplePool(context.Background(),
|
||||||
@@ -163,52 +169,176 @@ func connectToAllRelays(
|
|||||||
)
|
)
|
||||||
|
|
||||||
relays := make([]*nostr.Relay, 0, len(relayUrls))
|
relays := make([]*nostr.Relay, 0, len(relayUrls))
|
||||||
relayLoop:
|
|
||||||
for _, url := range relayUrls {
|
|
||||||
log("connecting to %s... ", url)
|
|
||||||
if relay, err := sys.Pool.EnsureRelay(url); err == nil {
|
|
||||||
if forcePreAuth {
|
|
||||||
log("waiting for auth challenge... ")
|
|
||||||
signer := opts[0].(nostr.WithAuthHandler)
|
|
||||||
time.Sleep(time.Millisecond * 200)
|
|
||||||
challengeWaitLoop:
|
|
||||||
for {
|
|
||||||
// beginhack
|
|
||||||
// here starts the biggest and ugliest hack of this codebase
|
|
||||||
if err := relay.Auth(ctx, func(authEvent *nostr.Event) error {
|
|
||||||
challengeTag := authEvent.Tags.GetFirst([]string{"challenge", ""})
|
|
||||||
if (*challengeTag)[1] == "" {
|
|
||||||
return fmt.Errorf("auth not received yet *****")
|
|
||||||
}
|
|
||||||
return signer(ctx, nostr.RelayEvent{Event: authEvent, Relay: relay})
|
|
||||||
}); err == nil {
|
|
||||||
// auth succeeded
|
|
||||||
break challengeWaitLoop
|
|
||||||
} else {
|
|
||||||
// auth failed
|
|
||||||
if strings.HasSuffix(err.Error(), "auth not received yet *****") {
|
|
||||||
// it failed because we didn't receive the challenge yet, so keep waiting
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
continue challengeWaitLoop
|
|
||||||
} else {
|
|
||||||
// it failed for some other reason, so skip this relay
|
|
||||||
log(err.Error() + "\n")
|
|
||||||
continue relayLoop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// endhack
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
relays = append(relays, relay)
|
if supportsDynamicMultilineMagic() {
|
||||||
log("ok.\n")
|
// overcomplicated multiline rendering magic
|
||||||
} else {
|
lines := make([][][]byte, len(relayUrls))
|
||||||
log(err.Error() + "\n")
|
flush := func() {
|
||||||
|
for _, line := range lines {
|
||||||
|
for _, part := range line {
|
||||||
|
os.Stderr.Write(part)
|
||||||
|
}
|
||||||
|
os.Stderr.Write([]byte{'\n'})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
render := func() {
|
||||||
|
clearLines(len(lines))
|
||||||
|
flush()
|
||||||
|
}
|
||||||
|
flush()
|
||||||
|
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(len(relayUrls))
|
||||||
|
for i, url := range relayUrls {
|
||||||
|
lines[i] = make([][]byte, 1, 2)
|
||||||
|
logthis := func(s string, args ...any) {
|
||||||
|
lines[i] = append(lines[i], []byte(fmt.Sprintf(s, args...)))
|
||||||
|
render()
|
||||||
|
}
|
||||||
|
colorizepreamble := func(c func(string, ...any) string) {
|
||||||
|
lines[i][0] = []byte(fmt.Sprintf("%s... ", c(url)))
|
||||||
|
}
|
||||||
|
colorizepreamble(color.CyanString)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
relay := connectToSingleRelay(ctx, c, url, preAuthSigner, colorizepreamble, logthis)
|
||||||
|
if relay != nil {
|
||||||
|
relays = append(relays, relay)
|
||||||
|
}
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
} else {
|
||||||
|
// simple flow
|
||||||
|
for _, url := range relayUrls {
|
||||||
|
log("connecting to %s... ", color.CyanString(url))
|
||||||
|
relay := connectToSingleRelay(ctx, c, url, preAuthSigner, nil, log)
|
||||||
|
if relay != nil {
|
||||||
|
relays = append(relays, relay)
|
||||||
|
}
|
||||||
|
log("\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return relays
|
return relays
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func connectToSingleRelay(
|
||||||
|
ctx context.Context,
|
||||||
|
c *cli.Command,
|
||||||
|
url string,
|
||||||
|
preAuthSigner func(ctx context.Context, c *cli.Command, log func(s string, args ...any), authEvent nostr.RelayEvent) (err error),
|
||||||
|
colorizepreamble func(c func(string, ...any) string),
|
||||||
|
logthis func(s string, args ...any),
|
||||||
|
) *nostr.Relay {
|
||||||
|
if relay, err := sys.Pool.EnsureRelay(url); err == nil {
|
||||||
|
if preAuthSigner != nil {
|
||||||
|
if colorizepreamble != nil {
|
||||||
|
colorizepreamble(color.YellowString)
|
||||||
|
}
|
||||||
|
logthis("waiting for auth challenge... ")
|
||||||
|
time.Sleep(time.Millisecond * 200)
|
||||||
|
|
||||||
|
for range 5 {
|
||||||
|
if err := relay.Auth(ctx, func(authEvent *nostr.Event) error {
|
||||||
|
challengeTag := authEvent.Tags.Find("challenge")
|
||||||
|
if challengeTag[1] == "" {
|
||||||
|
return fmt.Errorf("auth not received yet *****") // what a giant hack
|
||||||
|
}
|
||||||
|
return preAuthSigner(ctx, c, logthis, nostr.RelayEvent{Event: authEvent, Relay: relay})
|
||||||
|
}); err == nil {
|
||||||
|
// auth succeeded
|
||||||
|
goto preauthSuccess
|
||||||
|
} else {
|
||||||
|
// auth failed
|
||||||
|
if strings.HasSuffix(err.Error(), "auth not received yet *****") {
|
||||||
|
// it failed because we didn't receive the challenge yet, so keep waiting
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
// it failed for some other reason, so skip this relay
|
||||||
|
if colorizepreamble != nil {
|
||||||
|
colorizepreamble(colors.errorf)
|
||||||
|
}
|
||||||
|
logthis(err.Error())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if colorizepreamble != nil {
|
||||||
|
colorizepreamble(colors.errorf)
|
||||||
|
}
|
||||||
|
logthis("failed to get an AUTH challenge in enough time.")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
preauthSuccess:
|
||||||
|
if colorizepreamble != nil {
|
||||||
|
colorizepreamble(colors.successf)
|
||||||
|
}
|
||||||
|
logthis("ok.")
|
||||||
|
return relay
|
||||||
|
} else {
|
||||||
|
if colorizepreamble != nil {
|
||||||
|
colorizepreamble(colors.errorf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we're here that means we've failed to connect, this may be a huge message
|
||||||
|
// but we're likely to only be interested in the lowest level error (although we can leave space)
|
||||||
|
logthis(clampError(err, len(url)+12))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearLines(lineCount int) {
|
||||||
|
for i := 0; i < lineCount; i++ {
|
||||||
|
os.Stderr.Write([]byte("\033[0A\033[2K\r"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func supportsDynamicMultilineMagic() bool {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !term.IsTerminal(0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
width, _, err := term.GetSize(0)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if width < 110 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func authSigner(ctx context.Context, c *cli.Command, log func(s string, args ...any), authEvent nostr.RelayEvent) (err error) {
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
cleanUrl, _ := strings.CutPrefix(authEvent.Relay.URL, "wss://")
|
||||||
|
log("%s auth failed: %s", colors.errorf(cleanUrl), err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if !c.Bool("auth") && !c.Bool("force-pre-auth") {
|
||||||
|
return fmt.Errorf("auth required, but --auth flag not given")
|
||||||
|
}
|
||||||
|
kr, _, err := gatherKeyerFromArguments(ctx, c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
pk, _ := kr.GetPublicKey(ctx)
|
||||||
|
npub, _ := nip19.EncodePublicKey(pk)
|
||||||
|
log("authenticating as %s... ", color.YellowString("%s…%s", npub[0:7], npub[58:]))
|
||||||
|
|
||||||
|
return kr.SignEvent(ctx, authEvent.Event)
|
||||||
|
}
|
||||||
|
|
||||||
func lineProcessingError(ctx context.Context, msg string, args ...any) context.Context {
|
func lineProcessingError(ctx context.Context, msg string, args ...any) context.Context {
|
||||||
log(msg+"\n", args...)
|
log(msg+"\n", args...)
|
||||||
return context.WithValue(ctx, LINE_PROCESSING_ERROR, true)
|
return context.WithValue(ctx, LINE_PROCESSING_ERROR, true)
|
||||||
@@ -234,16 +364,50 @@ func leftPadKey(k string) string {
|
|||||||
return strings.Repeat("0", 64-len(k)) + k
|
return strings.Repeat("0", 64-len(k)) + k
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func unwrapAll(err error) error {
|
||||||
|
low := err
|
||||||
|
for n := low; n != nil; n = errors.Unwrap(low) {
|
||||||
|
low = n
|
||||||
|
}
|
||||||
|
return low
|
||||||
|
}
|
||||||
|
|
||||||
|
func clampMessage(msg string, prefixAlreadyPrinted int) string {
|
||||||
|
termSize, _, _ := term.GetSize(0)
|
||||||
|
if len(msg) > termSize-prefixAlreadyPrinted {
|
||||||
|
msg = msg[0:termSize-prefixAlreadyPrinted-1] + "…"
|
||||||
|
}
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func clampError(err error, prefixAlreadyPrinted int) string {
|
||||||
|
termSize, _, _ := term.GetSize(0)
|
||||||
|
msg := err.Error()
|
||||||
|
if len(msg) > termSize-prefixAlreadyPrinted {
|
||||||
|
err = unwrapAll(err)
|
||||||
|
msg = clampMessage(err.Error(), prefixAlreadyPrinted)
|
||||||
|
}
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
var colors = struct {
|
var colors = struct {
|
||||||
reset func(...any) (int, error)
|
reset func(...any) (int, error)
|
||||||
italic func(...any) string
|
italic func(...any) string
|
||||||
italicf func(string, ...any) string
|
italicf func(string, ...any) string
|
||||||
bold func(...any) string
|
bold func(...any) string
|
||||||
boldf func(string, ...any) string
|
boldf func(string, ...any) string
|
||||||
|
error func(...any) string
|
||||||
|
errorf func(string, ...any) string
|
||||||
|
success func(...any) string
|
||||||
|
successf func(string, ...any) string
|
||||||
}{
|
}{
|
||||||
color.New(color.Reset).Print,
|
color.New(color.Reset).Print,
|
||||||
color.New(color.Italic).Sprint,
|
color.New(color.Italic).Sprint,
|
||||||
color.New(color.Italic).Sprintf,
|
color.New(color.Italic).Sprintf,
|
||||||
color.New(color.Bold).Sprint,
|
color.New(color.Bold).Sprint,
|
||||||
color.New(color.Bold).Sprintf,
|
color.New(color.Bold).Sprintf,
|
||||||
|
color.New(color.Bold, color.FgHiRed).Sprint,
|
||||||
|
color.New(color.Bold, color.FgHiRed).Sprintf,
|
||||||
|
color.New(color.Bold, color.FgHiGreen).Sprint,
|
||||||
|
color.New(color.Bold, color.FgHiGreen).Sprintf,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -348,11 +348,7 @@ func (e *EntityDir) handleWrite() {
|
|||||||
success := false
|
success := false
|
||||||
first := true
|
first := true
|
||||||
for res := range e.root.sys.Pool.PublishMany(e.root.ctx, relays, evt) {
|
for res := range e.root.sys.Pool.PublishMany(e.root.ctx, relays, evt) {
|
||||||
cleanUrl, ok := strings.CutPrefix(res.RelayURL, "wss://")
|
cleanUrl, _ := strings.CutPrefix(res.RelayURL, "wss://")
|
||||||
if !ok {
|
|
||||||
cleanUrl = res.RelayURL
|
|
||||||
}
|
|
||||||
|
|
||||||
if !first {
|
if !first {
|
||||||
log(", ")
|
log(", ")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import (
|
|||||||
"github.com/nbd-wtf/go-nostr/nip19"
|
"github.com/nbd-wtf/go-nostr/nip19"
|
||||||
"github.com/nbd-wtf/go-nostr/nip22"
|
"github.com/nbd-wtf/go-nostr/nip22"
|
||||||
"github.com/nbd-wtf/go-nostr/nip27"
|
"github.com/nbd-wtf/go-nostr/nip27"
|
||||||
|
"github.com/nbd-wtf/go-nostr/nip73"
|
||||||
"github.com/nbd-wtf/go-nostr/nip92"
|
"github.com/nbd-wtf/go-nostr/nip92"
|
||||||
sdk "github.com/nbd-wtf/go-nostr/sdk"
|
sdk "github.com/nbd-wtf/go-nostr/sdk"
|
||||||
)
|
)
|
||||||
@@ -191,7 +192,7 @@ func (r *NostrRoot) CreateEventDir(
|
|||||||
}
|
}
|
||||||
} else if event.Kind == 1111 {
|
} else if event.Kind == 1111 {
|
||||||
if pointer := nip22.GetThreadRoot(event.Tags); pointer != nil {
|
if pointer := nip22.GetThreadRoot(event.Tags); pointer != nil {
|
||||||
if xp, ok := pointer.(nostr.ExternalPointer); ok {
|
if xp, ok := pointer.(nip73.ExternalPointer); ok {
|
||||||
h.AddChild("@root", h.NewPersistentInode(
|
h.AddChild("@root", h.NewPersistentInode(
|
||||||
r.ctx,
|
r.ctx,
|
||||||
&fs.MemRegularFile{
|
&fs.MemRegularFile{
|
||||||
@@ -211,7 +212,7 @@ func (r *NostrRoot) CreateEventDir(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if pointer := nip22.GetImmediateParent(event.Tags); pointer != nil {
|
if pointer := nip22.GetImmediateParent(event.Tags); pointer != nil {
|
||||||
if xp, ok := pointer.(nostr.ExternalPointer); ok {
|
if xp, ok := pointer.(nip73.ExternalPointer); ok {
|
||||||
h.AddChild("@parent", h.NewPersistentInode(
|
h.AddChild("@parent", h.NewPersistentInode(
|
||||||
r.ctx,
|
r.ctx,
|
||||||
&fs.MemRegularFile{
|
&fs.MemRegularFile{
|
||||||
|
|||||||
@@ -179,11 +179,7 @@ func (n *ViewDir) publishNote() {
|
|||||||
success := false
|
success := false
|
||||||
first := true
|
first := true
|
||||||
for res := range n.root.sys.Pool.PublishMany(n.root.ctx, relays, evt) {
|
for res := range n.root.sys.Pool.PublishMany(n.root.ctx, relays, evt) {
|
||||||
cleanUrl, ok := strings.CutPrefix(res.RelayURL, "wss://")
|
cleanUrl, _ := strings.CutPrefix(res.RelayURL, "wss://")
|
||||||
if !ok {
|
|
||||||
cleanUrl = res.RelayURL
|
|
||||||
}
|
|
||||||
|
|
||||||
if !first {
|
if !first {
|
||||||
log(", ")
|
log(", ")
|
||||||
}
|
}
|
||||||
|
|||||||
48
req.go
48
req.go
@@ -6,6 +6,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/fatih/color"
|
||||||
"github.com/mailru/easyjson"
|
"github.com/mailru/easyjson"
|
||||||
"github.com/nbd-wtf/go-nostr"
|
"github.com/nbd-wtf/go-nostr"
|
||||||
"github.com/nbd-wtf/go-nostr/nip77"
|
"github.com/nbd-wtf/go-nostr/nip77"
|
||||||
@@ -76,35 +77,29 @@ example:
|
|||||||
Action: func(ctx context.Context, c *cli.Command) error {
|
Action: func(ctx context.Context, c *cli.Command) error {
|
||||||
relayUrls := c.Args().Slice()
|
relayUrls := c.Args().Slice()
|
||||||
if len(relayUrls) > 0 {
|
if len(relayUrls) > 0 {
|
||||||
relays := connectToAllRelays(ctx,
|
// this is used both for the normal AUTH (after "auth-required:" is received) or forced pre-auth
|
||||||
|
// connect to all relays we expect to use in this call in parallel
|
||||||
|
forcePreAuthSigner := authSigner
|
||||||
|
if !c.Bool("force-pre-auth") {
|
||||||
|
forcePreAuthSigner = nil
|
||||||
|
}
|
||||||
|
relays := connectToAllRelays(
|
||||||
|
ctx,
|
||||||
|
c,
|
||||||
relayUrls,
|
relayUrls,
|
||||||
c.Bool("force-pre-auth"),
|
forcePreAuthSigner,
|
||||||
nostr.WithAuthHandler(
|
nostr.WithAuthHandler(func(ctx context.Context, authEvent nostr.RelayEvent) error {
|
||||||
func(ctx context.Context, authEvent nostr.RelayEvent) (err error) {
|
return authSigner(ctx, c, func(s string, args ...any) {
|
||||||
defer func() {
|
if strings.HasPrefix(s, "authenticating as") {
|
||||||
if err != nil {
|
cleanUrl, _ := strings.CutPrefix(authEvent.Relay.URL, "wss://")
|
||||||
log("auth to %s failed: %s\n",
|
s = "authenticating to " + color.CyanString(cleanUrl) + " as" + s[len("authenticating as"):]
|
||||||
(*authEvent.Tags.GetFirst([]string{"relay", ""}))[1],
|
|
||||||
err,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if !c.Bool("auth") && !c.Bool("force-pre-auth") {
|
|
||||||
return fmt.Errorf("auth not authorized")
|
|
||||||
}
|
}
|
||||||
kr, _, err := gatherKeyerFromArguments(ctx, c)
|
log(s+"\n", args...)
|
||||||
if err != nil {
|
}, authEvent)
|
||||||
return err
|
}),
|
||||||
}
|
|
||||||
|
|
||||||
pk, _ := kr.GetPublicKey(ctx)
|
|
||||||
log("performing auth as %s... ", pk)
|
|
||||||
|
|
||||||
return kr.SignEvent(ctx, authEvent.Event)
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// stop here already if all connections failed
|
||||||
if len(relays) == 0 {
|
if len(relays) == 0 {
|
||||||
log("failed to connect to any of the given relays.\n")
|
log("failed to connect to any of the given relays.\n")
|
||||||
os.Exit(3)
|
os.Exit(3)
|
||||||
@@ -121,6 +116,7 @@ example:
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// go line by line from stdin or run once with input from flags
|
||||||
for stdinFilter := range getJsonsOrBlank() {
|
for stdinFilter := range getJsonsOrBlank() {
|
||||||
filter := nostr.Filter{}
|
filter := nostr.Filter{}
|
||||||
if stdinFilter != "" {
|
if stdinFilter != "" {
|
||||||
|
|||||||
30
wallet.go
30
wallet.go
@@ -6,7 +6,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
|
||||||
"github.com/nbd-wtf/go-nostr"
|
"github.com/nbd-wtf/go-nostr"
|
||||||
"github.com/nbd-wtf/go-nostr/nip60"
|
"github.com/nbd-wtf/go-nostr/nip60"
|
||||||
"github.com/nbd-wtf/go-nostr/nip61"
|
"github.com/nbd-wtf/go-nostr/nip61"
|
||||||
@@ -60,20 +59,16 @@ func prepareWallet(ctx context.Context, c *cli.Command) (*nip60.Wallet, func(),
|
|||||||
log("- saving kind:%d event (%s)... ", event.Kind, desc)
|
log("- saving kind:%d event (%s)... ", event.Kind, desc)
|
||||||
first := true
|
first := true
|
||||||
for res := range sys.Pool.PublishMany(ctx, relays, event) {
|
for res := range sys.Pool.PublishMany(ctx, relays, event) {
|
||||||
cleanUrl, ok := strings.CutPrefix(res.RelayURL, "wss://")
|
cleanUrl, _ := strings.CutPrefix(res.RelayURL, "wss://")
|
||||||
if !ok {
|
|
||||||
cleanUrl = res.RelayURL
|
|
||||||
}
|
|
||||||
|
|
||||||
if !first {
|
if !first {
|
||||||
log(", ")
|
log(", ")
|
||||||
}
|
}
|
||||||
first = false
|
first = false
|
||||||
|
|
||||||
if res.Error != nil {
|
if res.Error != nil {
|
||||||
log("%s: %s", color.RedString(cleanUrl), res.Error)
|
log("%s: %s", colors.errorf(cleanUrl), res.Error)
|
||||||
} else {
|
} else {
|
||||||
log("%s: ok", color.GreenString(cleanUrl))
|
log("%s: ok", colors.successf(cleanUrl))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log("\n")
|
log("\n")
|
||||||
@@ -376,19 +371,15 @@ var wallet = &cli.Command{
|
|||||||
log("- publishing nutzap... ")
|
log("- publishing nutzap... ")
|
||||||
first := true
|
first := true
|
||||||
for res := range results {
|
for res := range results {
|
||||||
cleanUrl, ok := strings.CutPrefix(res.RelayURL, "wss://")
|
cleanUrl, _ := strings.CutPrefix(res.RelayURL, "wss://")
|
||||||
if !ok {
|
|
||||||
cleanUrl = res.RelayURL
|
|
||||||
}
|
|
||||||
|
|
||||||
if !first {
|
if !first {
|
||||||
log(", ")
|
log(", ")
|
||||||
}
|
}
|
||||||
first = false
|
first = false
|
||||||
if res.Error != nil {
|
if res.Error != nil {
|
||||||
log("%s: %s", color.RedString(cleanUrl), res.Error)
|
log("%s: %s", colors.errorf(cleanUrl), res.Error)
|
||||||
} else {
|
} else {
|
||||||
log("%s: ok", color.GreenString(cleanUrl))
|
log("%s: ok", colors.successf(cleanUrl))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -463,10 +454,7 @@ var wallet = &cli.Command{
|
|||||||
log("- saving kind:10019 event... ")
|
log("- saving kind:10019 event... ")
|
||||||
first := true
|
first := true
|
||||||
for res := range sys.Pool.PublishMany(ctx, relays, evt) {
|
for res := range sys.Pool.PublishMany(ctx, relays, evt) {
|
||||||
cleanUrl, ok := strings.CutPrefix(res.RelayURL, "wss://")
|
cleanUrl, _ := strings.CutPrefix(res.RelayURL, "wss://")
|
||||||
if !ok {
|
|
||||||
cleanUrl = res.RelayURL
|
|
||||||
}
|
|
||||||
|
|
||||||
if !first {
|
if !first {
|
||||||
log(", ")
|
log(", ")
|
||||||
@@ -474,9 +462,9 @@ var wallet = &cli.Command{
|
|||||||
first = false
|
first = false
|
||||||
|
|
||||||
if res.Error != nil {
|
if res.Error != nil {
|
||||||
log("%s: %s", color.RedString(cleanUrl), res.Error)
|
log("%s: %s", colors.errorf(cleanUrl), res.Error)
|
||||||
} else {
|
} else {
|
||||||
log("%s: ok", color.GreenString(cleanUrl))
|
log("%s: ok", colors.successf(cleanUrl))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user