mirror of
https://github.com/fiatjaf/nak.git
synced 2025-04-26 02:59:55 -04:00
nice dynamic UI when connecting to relays, and go much faster concurrently.
This commit is contained in:
parent
50119e21e6
commit
9547711e8d
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
nak
|
||||
mnt
|
||||
nak.exe
|
||||
|
@ -11,10 +11,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/urfave/cli/v3"
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
"github.com/nbd-wtf/go-nostr/nip19"
|
||||
"github.com/nbd-wtf/go-nostr/nip46"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
var bunker = &cli.Command{
|
||||
@ -49,7 +49,7 @@ var bunker = &cli.Command{
|
||||
qs := url.Values{}
|
||||
relayURLs := make([]string, 0, c.Args().Len())
|
||||
if relayUrls := c.Args().Slice(); len(relayUrls) > 0 {
|
||||
relays := connectToAllRelays(ctx, relayUrls, false)
|
||||
relays := connectToAllRelays(ctx, relayUrls, nil)
|
||||
if len(relays) == 0 {
|
||||
log("failed to connect to any of the given relays.\n")
|
||||
os.Exit(3)
|
||||
|
7
count.go
7
count.go
@ -6,10 +6,10 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
"github.com/nbd-wtf/go-nostr/nip45"
|
||||
"github.com/nbd-wtf/go-nostr/nip45/hyperloglog"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
var count = &cli.Command{
|
||||
@ -70,10 +70,7 @@ var count = &cli.Command{
|
||||
biggerUrlSize := 0
|
||||
relayUrls := c.Args().Slice()
|
||||
if len(relayUrls) > 0 {
|
||||
relays := connectToAllRelays(ctx,
|
||||
relayUrls,
|
||||
false,
|
||||
)
|
||||
relays := connectToAllRelays(ctx, relayUrls, nil)
|
||||
if len(relays) == 0 {
|
||||
log("failed to connect to any of the given relays.\n")
|
||||
os.Exit(3)
|
||||
|
2
dvm.go
2
dvm.go
@ -60,7 +60,7 @@ var dvm = &cli.Command{
|
||||
Flags: flags,
|
||||
Action: func(ctx context.Context, c *cli.Command) error {
|
||||
relayUrls := c.StringSlice("relay")
|
||||
relays := connectToAllRelays(ctx, relayUrls, false)
|
||||
relays := connectToAllRelays(ctx, relayUrls, nil)
|
||||
if len(relays) == 0 {
|
||||
log("failed to connect to any of the given relays.\n")
|
||||
os.Exit(3)
|
||||
|
14
event.go
14
event.go
@ -134,7 +134,7 @@ example:
|
||||
// try to connect to the relays here
|
||||
var relays []*nostr.Relay
|
||||
if relayUrls := c.Args().Slice(); len(relayUrls) > 0 {
|
||||
relays = connectToAllRelays(ctx, relayUrls, false)
|
||||
relays = connectToAllRelays(ctx, relayUrls, nil)
|
||||
if len(relays) == 0 {
|
||||
log("failed to connect to any of the given relays.\n")
|
||||
os.Exit(3)
|
||||
@ -209,13 +209,19 @@ example:
|
||||
}
|
||||
|
||||
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") {
|
||||
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") {
|
||||
tags = tags.AppendUnique([]string{"d", dtag})
|
||||
if tags.FindWithValue("d", dtag) == nil {
|
||||
tags = append(tags, nostr.Tag{"d", dtag})
|
||||
}
|
||||
}
|
||||
if len(tags) > 0 {
|
||||
for _, tag := range tags {
|
||||
|
1
go.mod
1
go.mod
@ -20,6 +20,7 @@ require (
|
||||
github.com/nbd-wtf/go-nostr v0.51.8
|
||||
github.com/urfave/cli/v3 v3.0.0-beta1
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394
|
||||
golang.org/x/term v0.30.0
|
||||
)
|
||||
|
||||
require (
|
||||
|
2
go.sum
2
go.sum
@ -247,6 +247,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.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
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.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
|
182
helpers.go
182
helpers.go
@ -10,8 +10,10 @@ import (
|
||||
"net/textproto"
|
||||
"net/url"
|
||||
"os"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fatih/color"
|
||||
@ -19,6 +21,7 @@ import (
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
"github.com/nbd-wtf/go-nostr/sdk"
|
||||
"github.com/urfave/cli/v3"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
var sys *sdk.System
|
||||
@ -149,7 +152,7 @@ func normalizeAndValidateRelayURLs(wsurls []string) error {
|
||||
func connectToAllRelays(
|
||||
ctx context.Context,
|
||||
relayUrls []string,
|
||||
forcePreAuth bool,
|
||||
preAuthSigner func(ctx context.Context, log func(s string, args ...any), authEvent nostr.RelayEvent) (err error), // if this exists we will force preauth
|
||||
opts ...nostr.PoolOption,
|
||||
) []*nostr.Relay {
|
||||
sys.Pool = nostr.NewSimplePool(context.Background(),
|
||||
@ -163,52 +166,149 @@ func connectToAllRelays(
|
||||
)
|
||||
|
||||
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.Find("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)
|
||||
log("ok.\n")
|
||||
} else {
|
||||
log(err.Error() + "\n")
|
||||
if supportsDynamicMultilineMagic() {
|
||||
// overcomplicated multiline rendering magic
|
||||
lines := make([][][]byte, len(relayUrls))
|
||||
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, 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... ", url)
|
||||
relay := connectToSingleRelay(ctx, url, preAuthSigner, nil, log)
|
||||
if relay != nil {
|
||||
relays = append(relays, relay)
|
||||
}
|
||||
log("\n")
|
||||
}
|
||||
}
|
||||
|
||||
return relays
|
||||
}
|
||||
|
||||
func connectToSingleRelay(
|
||||
ctx context.Context,
|
||||
url string,
|
||||
preAuthSigner func(ctx context.Context, 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, 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(color.RedString)
|
||||
}
|
||||
logthis(err.Error())
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if colorizepreamble != nil {
|
||||
colorizepreamble(color.RedString)
|
||||
}
|
||||
logthis("failed to get an AUTH challenge in enough time.")
|
||||
return nil
|
||||
}
|
||||
|
||||
preauthSuccess:
|
||||
if colorizepreamble != nil {
|
||||
colorizepreamble(color.GreenString)
|
||||
}
|
||||
logthis("ok.")
|
||||
return relay
|
||||
} else {
|
||||
if colorizepreamble != nil {
|
||||
colorizepreamble(color.RedString)
|
||||
}
|
||||
logthis(err.Error())
|
||||
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 lineProcessingError(ctx context.Context, msg string, args ...any) context.Context {
|
||||
log(msg+"\n", args...)
|
||||
return context.WithValue(ctx, LINE_PROCESSING_ERROR, true)
|
||||
|
65
req.go
65
req.go
@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/mailru/easyjson"
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
"github.com/nbd-wtf/go-nostr/nip19"
|
||||
"github.com/nbd-wtf/go-nostr/nip77"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
@ -76,35 +77,46 @@ example:
|
||||
Action: func(ctx context.Context, c *cli.Command) error {
|
||||
relayUrls := c.Args().Slice()
|
||||
if len(relayUrls) > 0 {
|
||||
// this is used both for the normal AUTH (after "auth-required:" is received) or forced pre-auth
|
||||
authSigner := func(ctx context.Context, log func(s string, args ...any), authEvent nostr.RelayEvent) (err error) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log("auth to %s failed: %s",
|
||||
authEvent.Tags.Find("relay")[1],
|
||||
err,
|
||||
)
|
||||
}
|
||||
}()
|
||||
|
||||
if !c.Bool("auth") && !c.Bool("force-pre-auth") {
|
||||
return fmt.Errorf("auth not authorized")
|
||||
}
|
||||
kr, _, err := gatherKeyerFromArguments(ctx, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pk, _ := kr.GetPublicKey(ctx)
|
||||
npub, _ := nip19.EncodePublicKey(pk)
|
||||
log("performing auth as %s…%s... ", npub[0:7], npub[58:])
|
||||
|
||||
return kr.SignEvent(ctx, authEvent.Event)
|
||||
}
|
||||
|
||||
// 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,
|
||||
relayUrls,
|
||||
c.Bool("force-pre-auth"),
|
||||
nostr.WithAuthHandler(
|
||||
func(ctx context.Context, authEvent nostr.RelayEvent) (err error) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log("auth to %s failed: %s\n",
|
||||
authEvent.Tags.Find("relay")[1],
|
||||
err,
|
||||
)
|
||||
}
|
||||
}()
|
||||
|
||||
if !c.Bool("auth") && !c.Bool("force-pre-auth") {
|
||||
return fmt.Errorf("auth not authorized")
|
||||
}
|
||||
kr, _, err := gatherKeyerFromArguments(ctx, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pk, _ := kr.GetPublicKey(ctx)
|
||||
log("performing auth as %s... ", pk)
|
||||
|
||||
return kr.SignEvent(ctx, authEvent.Event)
|
||||
},
|
||||
),
|
||||
forcePreAuthSigner,
|
||||
nostr.WithAuthHandler(func(ctx context.Context, authEvent nostr.RelayEvent) error {
|
||||
return authSigner(ctx, func(s string, args ...any) { log(s+"\n", args...) }, authEvent)
|
||||
}),
|
||||
)
|
||||
|
||||
// stop here already if all connections failed
|
||||
if len(relays) == 0 {
|
||||
log("failed to connect to any of the given relays.\n")
|
||||
os.Exit(3)
|
||||
@ -121,6 +133,7 @@ example:
|
||||
}()
|
||||
}
|
||||
|
||||
// go line by line from stdin or run once with input from flags
|
||||
for stdinFilter := range getJsonsOrBlank() {
|
||||
filter := nostr.Filter{}
|
||||
if stdinFilter != "" {
|
||||
|
Loading…
x
Reference in New Issue
Block a user