mirror of
https://github.com/fiatjaf/nak.git
synced 2025-12-09 00:58:50 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
54c4be10bd | ||
|
|
27f925c05e | ||
|
|
79cb63a1b4 | ||
|
|
5ee0036128 | ||
|
|
316d94166e | ||
|
|
2ca6bb0940 | ||
|
|
ac00c5065f |
@@ -50,7 +50,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)
|
_, relays := connectToAllRelays(ctx, 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)
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ var decode = &cli.Command{
|
|||||||
decodeResult.HexResult.PrivateKey = hex.EncodeToString(b)
|
decodeResult.HexResult.PrivateKey = hex.EncodeToString(b)
|
||||||
decodeResult.HexResult.PublicKey = hex.EncodeToString(b)
|
decodeResult.HexResult.PublicKey = hex.EncodeToString(b)
|
||||||
} else {
|
} else {
|
||||||
lineProcessingError(ctx, "hex string with invalid number of bytes: %d", len(b))
|
ctx = lineProcessingError(ctx, "hex string with invalid number of bytes: %d", len(b))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else if evp := sdk.InputToEventPointer(input); evp != nil {
|
} else if evp := sdk.InputToEventPointer(input); evp != nil {
|
||||||
@@ -64,7 +64,7 @@ var decode = &cli.Command{
|
|||||||
decodeResult.PrivateKey.PrivateKey = value.(string)
|
decodeResult.PrivateKey.PrivateKey = value.(string)
|
||||||
decodeResult.PrivateKey.PublicKey, _ = nostr.GetPublicKey(value.(string))
|
decodeResult.PrivateKey.PublicKey, _ = nostr.GetPublicKey(value.(string))
|
||||||
} else {
|
} else {
|
||||||
lineProcessingError(ctx, "couldn't decode input '%s': %s", input, err)
|
ctx = lineProcessingError(ctx, "couldn't decode input '%s': %s", input, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
12
encode.go
12
encode.go
@@ -32,7 +32,7 @@ var encode = &cli.Command{
|
|||||||
Action: func(ctx context.Context, c *cli.Command) error {
|
Action: func(ctx context.Context, c *cli.Command) error {
|
||||||
for target := range getStdinLinesOrArguments(c.Args()) {
|
for target := range getStdinLinesOrArguments(c.Args()) {
|
||||||
if ok := nostr.IsValidPublicKey(target); !ok {
|
if ok := nostr.IsValidPublicKey(target); !ok {
|
||||||
lineProcessingError(ctx, "invalid public key: %s", target)
|
ctx = lineProcessingError(ctx, "invalid public key: %s", target)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ var encode = &cli.Command{
|
|||||||
Action: func(ctx context.Context, c *cli.Command) error {
|
Action: func(ctx context.Context, c *cli.Command) error {
|
||||||
for target := range getStdinLinesOrArguments(c.Args()) {
|
for target := range getStdinLinesOrArguments(c.Args()) {
|
||||||
if ok := nostr.IsValid32ByteHex(target); !ok {
|
if ok := nostr.IsValid32ByteHex(target); !ok {
|
||||||
lineProcessingError(ctx, "invalid private key: %s", target)
|
ctx = lineProcessingError(ctx, "invalid private key: %s", target)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ var encode = &cli.Command{
|
|||||||
Action: func(ctx context.Context, c *cli.Command) error {
|
Action: func(ctx context.Context, c *cli.Command) error {
|
||||||
for target := range getStdinLinesOrArguments(c.Args()) {
|
for target := range getStdinLinesOrArguments(c.Args()) {
|
||||||
if ok := nostr.IsValid32ByteHex(target); !ok {
|
if ok := nostr.IsValid32ByteHex(target); !ok {
|
||||||
lineProcessingError(ctx, "invalid public key: %s", target)
|
ctx = lineProcessingError(ctx, "invalid public key: %s", target)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,7 +119,7 @@ var encode = &cli.Command{
|
|||||||
Action: func(ctx context.Context, c *cli.Command) error {
|
Action: func(ctx context.Context, c *cli.Command) error {
|
||||||
for target := range getStdinLinesOrArguments(c.Args()) {
|
for target := range getStdinLinesOrArguments(c.Args()) {
|
||||||
if ok := nostr.IsValid32ByteHex(target); !ok {
|
if ok := nostr.IsValid32ByteHex(target); !ok {
|
||||||
lineProcessingError(ctx, "invalid event id: %s", target)
|
ctx = lineProcessingError(ctx, "invalid event id: %s", target)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,7 +189,7 @@ var encode = &cli.Command{
|
|||||||
if d == "" {
|
if d == "" {
|
||||||
d = c.String("identifier")
|
d = c.String("identifier")
|
||||||
if d == "" {
|
if d == "" {
|
||||||
lineProcessingError(ctx, "\"d\" tag identifier can't be empty")
|
ctx = lineProcessingError(ctx, "\"d\" tag identifier can't be empty")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -216,7 +216,7 @@ var encode = &cli.Command{
|
|||||||
Action: func(ctx context.Context, c *cli.Command) error {
|
Action: func(ctx context.Context, c *cli.Command) error {
|
||||||
for target := range getStdinLinesOrArguments(c.Args()) {
|
for target := range getStdinLinesOrArguments(c.Args()) {
|
||||||
if ok := nostr.IsValid32ByteHex(target); !ok {
|
if ok := nostr.IsValid32ByteHex(target); !ok {
|
||||||
lineProcessingError(ctx, "invalid event id: %s", target)
|
ctx = lineProcessingError(ctx, "invalid event id: %s", target)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
4
event.go
4
event.go
@@ -145,7 +145,7 @@ example:
|
|||||||
// try to connect to the relays here
|
// try to connect to the relays here
|
||||||
var relays []*nostr.Relay
|
var relays []*nostr.Relay
|
||||||
if relayUrls := c.Args().Slice(); len(relayUrls) > 0 {
|
if relayUrls := c.Args().Slice(); len(relayUrls) > 0 {
|
||||||
_, relays = connectToAllRelays(ctx, relayUrls)
|
_, relays = connectToAllRelays(ctx, 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)
|
||||||
@@ -176,7 +176,7 @@ example:
|
|||||||
|
|
||||||
if stdinEvent != "" {
|
if stdinEvent != "" {
|
||||||
if err := easyjson.Unmarshal([]byte(stdinEvent), &evt); err != nil {
|
if err := easyjson.Unmarshal([]byte(stdinEvent), &evt); err != nil {
|
||||||
lineProcessingError(ctx, "invalid event received from stdin: %s", err)
|
ctx = lineProcessingError(ctx, "invalid event received from stdin: %s", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
kindWasSupplied = strings.Contains(stdinEvent, `"kind"`)
|
kindWasSupplied = strings.Contains(stdinEvent, `"kind"`)
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "os"
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ctx = context.Background()
|
||||||
|
|
||||||
func ExampleEventBasic() {
|
func ExampleEventBasic() {
|
||||||
app.Run([]string{"nak", "event", "--ts", "1699485669"})
|
app.Run(ctx, []string{"nak", "event", "--ts", "1699485669"})
|
||||||
// Output:
|
// Output:
|
||||||
// {"kind":1,"id":"36d88cf5fcc449f2390a424907023eda7a74278120eebab8d02797cd92e7e29c","pubkey":"79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798","created_at":1699485669,"tags":[],"content":"hello from the nostr army knife","sig":"68e71a192e8abcf8582a222434ac823ecc50607450ebe8cc4c145eb047794cc382dc3f888ce879d2f404f5ba6085a47601360a0fa2dd4b50d317bd0c6197c2c2"}
|
// {"kind":1,"id":"36d88cf5fcc449f2390a424907023eda7a74278120eebab8d02797cd92e7e29c","pubkey":"79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798","created_at":1699485669,"tags":[],"content":"hello from the nostr army knife","sig":"68e71a192e8abcf8582a222434ac823ecc50607450ebe8cc4c145eb047794cc382dc3f888ce879d2f404f5ba6085a47601360a0fa2dd4b50d317bd0c6197c2c2"}
|
||||||
}
|
}
|
||||||
@@ -15,22 +20,22 @@ func ExampleEventParsingFromStdin() {
|
|||||||
r, w, _ := os.Pipe()
|
r, w, _ := os.Pipe()
|
||||||
os.Stdin = r
|
os.Stdin = r
|
||||||
w.WriteString("{\"content\":\"hello world\"}\n{\"content\":\"hello sun\"}\n")
|
w.WriteString("{\"content\":\"hello world\"}\n{\"content\":\"hello sun\"}\n")
|
||||||
app.Run([]string{"nak", "event", "-t", "t=spam", "--ts", "1699485669"})
|
app.Run(ctx, []string{"nak", "event", "-t", "t=spam", "--ts", "1699485669"})
|
||||||
// Output:
|
// Output:
|
||||||
// {"id":"bda134f9077c11973afe6aa5a1cc6f5bcea01c40d318b8f91dcb8e50507cfa52","pubkey":"79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798","created_at":1699485669,"kind":1,"tags":[["t","spam"]],"content":"hello world","sig":"7552454bb8e7944230142634e3e34ac7468bad9b21ed6909da572c611018dff1d14d0792e98b5806f6330edc51e09efa6d0b66a9694dc34606c70f4e580e7493"}
|
// {"id":"bda134f9077c11973afe6aa5a1cc6f5bcea01c40d318b8f91dcb8e50507cfa52","pubkey":"79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798","created_at":1699485669,"kind":1,"tags":[["t","spam"]],"content":"hello world","sig":"7552454bb8e7944230142634e3e34ac7468bad9b21ed6909da572c611018dff1d14d0792e98b5806f6330edc51e09efa6d0b66a9694dc34606c70f4e580e7493"}
|
||||||
// {"id":"879c36ec73acca288825b53585389581d3836e7f0fe4d46e5eba237ca56d6af5","pubkey":"79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798","created_at":1699485669,"kind":1,"tags":[["t","spam"]],"content":"hello sun","sig":"6c7e6b13ebdf931d26acfdd00bec2ec1140ddaf8d1ed61453543a14e729a460fe36c40c488ccb194a0e1ab9511cb6c36741485f501bdb93c39ca4c51bc59cbd4"}
|
// {"id":"879c36ec73acca288825b53585389581d3836e7f0fe4d46e5eba237ca56d6af5","pubkey":"79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798","created_at":1699485669,"kind":1,"tags":[["t","spam"]],"content":"hello sun","sig":"6c7e6b13ebdf931d26acfdd00bec2ec1140ddaf8d1ed61453543a14e729a460fe36c40c488ccb194a0e1ab9511cb6c36741485f501bdb93c39ca4c51bc59cbd4"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleEventComplex() {
|
func ExampleEventComplex() {
|
||||||
app.Run([]string{"nak", "event", "--ts", "1699485669", "-k", "11", "-c", "skjdbaskd", "--sec", "17", "-t", "t=spam", "-e", "36d88cf5fcc449f2390a424907023eda7a74278120eebab8d02797cd92e7e29c", "-t", "r=https://abc.def?name=foobar;nothing"})
|
app.Run(ctx, []string{"nak", "event", "--ts", "1699485669", "-k", "11", "-c", "skjdbaskd", "--sec", "17", "-t", "t=spam", "-e", "36d88cf5fcc449f2390a424907023eda7a74278120eebab8d02797cd92e7e29c", "-t", "r=https://abc.def?name=foobar;nothing"})
|
||||||
// Output:
|
// Output:
|
||||||
// {"id":"19aba166dcf354bf5ef64f4afe69ada1eb851495001ee05e07d393ee8c8ea179","pubkey":"2fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f","created_at":1699485669,"kind":11,"tags":[["t","spam"],["r","https://abc.def?name=foobar","nothing"],["e","36d88cf5fcc449f2390a424907023eda7a74278120eebab8d02797cd92e7e29c"]],"content":"skjdbaskd","sig":"cf452def4a68341c897c3fc96fa34dc6895a5b8cc266d4c041bcdf758ec992ec5adb8b0179e98552aaaf9450526a26d7e62e413b15b1c57e0cfc8db6b29215d7"}
|
// {"id":"19aba166dcf354bf5ef64f4afe69ada1eb851495001ee05e07d393ee8c8ea179","pubkey":"2fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f","created_at":1699485669,"kind":11,"tags":[["t","spam"],["r","https://abc.def?name=foobar","nothing"],["e","36d88cf5fcc449f2390a424907023eda7a74278120eebab8d02797cd92e7e29c"]],"content":"skjdbaskd","sig":"cf452def4a68341c897c3fc96fa34dc6895a5b8cc266d4c041bcdf758ec992ec5adb8b0179e98552aaaf9450526a26d7e62e413b15b1c57e0cfc8db6b29215d7"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleEncode() {
|
func ExampleEncode() {
|
||||||
app.Run([]string{"nak", "encode", "npub", "a6a67ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179822"})
|
app.Run(ctx, []string{"nak", "encode", "npub", "a6a67ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179822"})
|
||||||
app.Run([]string{"nak", "encode", "nprofile", "-r", "wss://example.com", "a6a67ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179822"})
|
app.Run(ctx, []string{"nak", "encode", "nprofile", "-r", "wss://example.com", "a6a67ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179822"})
|
||||||
app.Run([]string{"nak", "encode", "nprofile", "-r", "wss://example.com", "a6a67ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179822", "a5592173975ded9f836a9572ea8b11a7e16ceb66464d66d50b27163f7f039d2c"})
|
app.Run(ctx, []string{"nak", "encode", "nprofile", "-r", "wss://example.com", "a6a67ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179822", "a5592173975ded9f836a9572ea8b11a7e16ceb66464d66d50b27163f7f039d2c"})
|
||||||
// npub156n8a7wuhwk9tgrzjh8gwzc8q2dlekedec5djk0js9d3d7qhnq3qjpdq28
|
// npub156n8a7wuhwk9tgrzjh8gwzc8q2dlekedec5djk0js9d3d7qhnq3qjpdq28
|
||||||
// nprofile1qqs2dfn7l8wthtz45p3ftn58pvrs9xlumvkuu2xet8egzkcklqtesgspz9mhxue69uhk27rpd4cxcefwvdhk6fl5jug
|
// nprofile1qqs2dfn7l8wthtz45p3ftn58pvrs9xlumvkuu2xet8egzkcklqtesgspz9mhxue69uhk27rpd4cxcefwvdhk6fl5jug
|
||||||
// nprofile1qqs2dfn7l8wthtz45p3ftn58pvrs9xlumvkuu2xet8egzkcklqtesgspz9mhxue69uhk27rpd4cxcefwvdhk6fl5jug
|
// nprofile1qqs2dfn7l8wthtz45p3ftn58pvrs9xlumvkuu2xet8egzkcklqtesgspz9mhxue69uhk27rpd4cxcefwvdhk6fl5jug
|
||||||
@@ -38,7 +43,7 @@ func ExampleEncode() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ExampleDecode() {
|
func ExampleDecode() {
|
||||||
app.Run([]string{"nak", "decode", "naddr1qqyrgcmyxe3kvefhqyxhwumn8ghj7mn0wvhxcmmvqgs9kqvr4dkruv3t7n2pc6e6a7v9v2s5fprmwjv4gde8c4fe5y29v0srqsqqql9ngrt6tu", "nevent1qyd8wumn8ghj7urewfsk66ty9enxjct5dfskvtnrdakj7qgmwaehxw309aex2mrp0yh8wetnw3jhymnzw33jucm0d5hszxthwden5te0wfjkccte9eekummjwsh8xmmrd9skctcpzamhxue69uhkzarvv9ejumn0wd68ytnvv9hxgtcqyqllp5v5j0nxr74fptqxkhvfv0h3uj870qpk3ln8a58agyxl3fka296ewr8"})
|
app.Run(ctx, []string{"nak", "decode", "naddr1qqyrgcmyxe3kvefhqyxhwumn8ghj7mn0wvhxcmmvqgs9kqvr4dkruv3t7n2pc6e6a7v9v2s5fprmwjv4gde8c4fe5y29v0srqsqqql9ngrt6tu", "nevent1qyd8wumn8ghj7urewfsk66ty9enxjct5dfskvtnrdakj7qgmwaehxw309aex2mrp0yh8wetnw3jhymnzw33jucm0d5hszxthwden5te0wfjkccte9eekummjwsh8xmmrd9skctcpzamhxue69uhkzarvv9ejumn0wd68ytnvv9hxgtcqyqllp5v5j0nxr74fptqxkhvfv0h3uj870qpk3ln8a58agyxl3fka296ewr8"})
|
||||||
// Output:
|
// Output:
|
||||||
// {
|
// {
|
||||||
// "pubkey": "5b0183ab6c3e322bf4d41c6b3aef98562a144847b7499543727c5539a114563e",
|
// "pubkey": "5b0183ab6c3e322bf4d41c6b3aef98562a144847b7499543727c5539a114563e",
|
||||||
@@ -60,39 +65,39 @@ func ExampleDecode() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ExampleReq() {
|
func ExampleReq() {
|
||||||
app.Run([]string{"nak", "req", "-k", "1", "-l", "18", "-a", "2fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f", "-e", "aec4de6d051a7c2b6ca2d087903d42051a31e07fb742f1240970084822de10a6"})
|
app.Run(ctx, []string{"nak", "req", "-k", "1", "-l", "18", "-a", "2fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f", "-e", "aec4de6d051a7c2b6ca2d087903d42051a31e07fb742f1240970084822de10a6"})
|
||||||
// Output:
|
// Output:
|
||||||
// ["REQ","nak",{"kinds":[1],"authors":["2fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f"],"limit":18,"#e":["aec4de6d051a7c2b6ca2d087903d42051a31e07fb742f1240970084822de10a6"]}]
|
// ["REQ","nak",{"kinds":[1],"authors":["2fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f"],"limit":18,"#e":["aec4de6d051a7c2b6ca2d087903d42051a31e07fb742f1240970084822de10a6"]}]
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleReqIdFromRelay() {
|
func ExampleReqIdFromRelay() {
|
||||||
app.Run([]string{"nak", "req", "-i", "3ff0d19493e661faa90ac06b5d8963ef1e48fe780368fe67ed0fd410df8a6dd5", "wss://nostr.wine"})
|
app.Run(ctx, []string{"nak", "req", "-i", "3ff0d19493e661faa90ac06b5d8963ef1e48fe780368fe67ed0fd410df8a6dd5", "wss://nostr.wine"})
|
||||||
// Output:
|
// Output:
|
||||||
// {"id":"3ff0d19493e661faa90ac06b5d8963ef1e48fe780368fe67ed0fd410df8a6dd5","pubkey":"3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d","created_at":1710759386,"kind":1,"tags":[],"content":"Nostr was coopted by our the corporate overlords. It is now featured in https://www.iana.org/assignments/well-known-uris/well-known-uris.xhtml.","sig":"faaec167cca4de50b562b7702e8854e2023f0ccd5f36d1b95b6eac20d352206342d6987e9516d283068c768e94dbe8858e2990c3e05405e707fb6fb771ef92f9"}
|
// {"id":"3ff0d19493e661faa90ac06b5d8963ef1e48fe780368fe67ed0fd410df8a6dd5","pubkey":"3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d","created_at":1710759386,"kind":1,"tags":[],"content":"Nostr was coopted by our the corporate overlords. It is now featured in https://www.iana.org/assignments/well-known-uris/well-known-uris.xhtml.","sig":"faaec167cca4de50b562b7702e8854e2023f0ccd5f36d1b95b6eac20d352206342d6987e9516d283068c768e94dbe8858e2990c3e05405e707fb6fb771ef92f9"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleMultipleFetch() {
|
func ExampleMultipleFetch() {
|
||||||
app.Run([]string{"nak", "fetch", "naddr1qqyrgcmyxe3kvefhqyxhwumn8ghj7mn0wvhxcmmvqgs9kqvr4dkruv3t7n2pc6e6a7v9v2s5fprmwjv4gde8c4fe5y29v0srqsqqql9ngrt6tu", "nevent1qyd8wumn8ghj7urewfsk66ty9enxjct5dfskvtnrdakj7qgmwaehxw309aex2mrp0yh8wetnw3jhymnzw33jucm0d5hszxthwden5te0wfjkccte9eekummjwsh8xmmrd9skctcpzamhxue69uhkzarvv9ejumn0wd68ytnvv9hxgtcqyqllp5v5j0nxr74fptqxkhvfv0h3uj870qpk3ln8a58agyxl3fka296ewr8"})
|
app.Run(ctx, []string{"nak", "fetch", "naddr1qqyrgcmyxe3kvefhqyxhwumn8ghj7mn0wvhxcmmvqgs9kqvr4dkruv3t7n2pc6e6a7v9v2s5fprmwjv4gde8c4fe5y29v0srqsqqql9ngrt6tu", "nevent1qyd8wumn8ghj7urewfsk66ty9enxjct5dfskvtnrdakj7qgmwaehxw309aex2mrp0yh8wetnw3jhymnzw33jucm0d5hszxthwden5te0wfjkccte9eekummjwsh8xmmrd9skctcpzamhxue69uhkzarvv9ejumn0wd68ytnvv9hxgtcqyqllp5v5j0nxr74fptqxkhvfv0h3uj870qpk3ln8a58agyxl3fka296ewr8"})
|
||||||
// Output:
|
// Output:
|
||||||
// {"id":"9ae5014573fc75ced00b343868d2cd9343ebcbbae50591c6fa8ae1cd99568f05","pubkey":"5b0183ab6c3e322bf4d41c6b3aef98562a144847b7499543727c5539a114563e","created_at":1707764605,"kind":31923,"tags":[["d","4cd6cfe7"],["name","Nostr PHX Presents Culture Shock"],["description","Nostr PHX presents Culture Shock the first Value 4 Value Cultural Event in Downtown Phoenix. We will showcase the power of Nostr + Bitcoin / Lightning with a full day of education, food, drinks, conversation, vendors and best of all, a live convert which will stream globally for the world to zap. "],["start","1708185600"],["end","1708228800"],["start_tzid","America/Phoenix"],["p","5b0183ab6c3e322bf4d41c6b3aef98562a144847b7499543727c5539a114563e","","host"],["location","Hello Merch, 850 W Lincoln St, Phoenix, AZ 85007, USA","Hello Merch","850 W Lincoln St, Phoenix, AZ 85007, USA"],["address","Hello Merch, 850 W Lincoln St, Phoenix, AZ 85007, USA","Hello Merch","850 W Lincoln St, Phoenix, AZ 85007, USA"],["g","9tbq1rzn"],["image","https://flockstr.s3.amazonaws.com/event/15vSaiscDhVH1KBXhA0i8"],["about","Nostr PHX presents Culture Shock : the first Value 4 Value Cultural Event in Downtown Phoenix. We will showcase the power of Nostr + Bitcoin / Lightning with a full day of education, conversation, food and goods which will be capped off with a live concert streamed globally for the world to boost \u0026 zap. \n\nWe strive to source local vendors, local artists, local partnerships. Please reach out to us if you are interested in participating in this historic event. "],["calendar","31924:5b0183ab6c3e322bf4d41c6b3aef98562a144847b7499543727c5539a114563e:1f238c94"]],"content":"Nostr PHX presents Culture Shock : the first Value 4 Value Cultural Event in Downtown Phoenix. We will showcase the power of Nostr + Bitcoin / Lightning with a full day of education, conversation, food and goods which will be capped off with a live concert streamed globally for the world to boost \u0026 zap. \n\nWe strive to source local vendors, local artists, local partnerships. Please reach out to us if you are interested in participating in this historic event. ","sig":"f676629d1414d96b464644de6babde0c96bd21ef9b41ba69ad886a1d13a942b855b715b22ccf38bc07fead18d3bdeee82d9e3825cf6f003fb5ff1766d95c70a0"}
|
// {"id":"9ae5014573fc75ced00b343868d2cd9343ebcbbae50591c6fa8ae1cd99568f05","pubkey":"5b0183ab6c3e322bf4d41c6b3aef98562a144847b7499543727c5539a114563e","created_at":1707764605,"kind":31923,"tags":[["d","4cd6cfe7"],["name","Nostr PHX Presents Culture Shock"],["description","Nostr PHX presents Culture Shock the first Value 4 Value Cultural Event in Downtown Phoenix. We will showcase the power of Nostr + Bitcoin / Lightning with a full day of education, food, drinks, conversation, vendors and best of all, a live convert which will stream globally for the world to zap. "],["start","1708185600"],["end","1708228800"],["start_tzid","America/Phoenix"],["p","5b0183ab6c3e322bf4d41c6b3aef98562a144847b7499543727c5539a114563e","","host"],["location","Hello Merch, 850 W Lincoln St, Phoenix, AZ 85007, USA","Hello Merch","850 W Lincoln St, Phoenix, AZ 85007, USA"],["address","Hello Merch, 850 W Lincoln St, Phoenix, AZ 85007, USA","Hello Merch","850 W Lincoln St, Phoenix, AZ 85007, USA"],["g","9tbq1rzn"],["image","https://flockstr.s3.amazonaws.com/event/15vSaiscDhVH1KBXhA0i8"],["about","Nostr PHX presents Culture Shock : the first Value 4 Value Cultural Event in Downtown Phoenix. We will showcase the power of Nostr + Bitcoin / Lightning with a full day of education, conversation, food and goods which will be capped off with a live concert streamed globally for the world to boost \u0026 zap. \n\nWe strive to source local vendors, local artists, local partnerships. Please reach out to us if you are interested in participating in this historic event. "],["calendar","31924:5b0183ab6c3e322bf4d41c6b3aef98562a144847b7499543727c5539a114563e:1f238c94"]],"content":"Nostr PHX presents Culture Shock : the first Value 4 Value Cultural Event in Downtown Phoenix. We will showcase the power of Nostr + Bitcoin / Lightning with a full day of education, conversation, food and goods which will be capped off with a live concert streamed globally for the world to boost \u0026 zap. \n\nWe strive to source local vendors, local artists, local partnerships. Please reach out to us if you are interested in participating in this historic event. ","sig":"f676629d1414d96b464644de6babde0c96bd21ef9b41ba69ad886a1d13a942b855b715b22ccf38bc07fead18d3bdeee82d9e3825cf6f003fb5ff1766d95c70a0"}
|
||||||
// {"id":"3ff0d19493e661faa90ac06b5d8963ef1e48fe780368fe67ed0fd410df8a6dd5","pubkey":"3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d","created_at":1710759386,"kind":1,"tags":[],"content":"Nostr was coopted by our the corporate overlords. It is now featured in https://www.iana.org/assignments/well-known-uris/well-known-uris.xhtml.","sig":"faaec167cca4de50b562b7702e8854e2023f0ccd5f36d1b95b6eac20d352206342d6987e9516d283068c768e94dbe8858e2990c3e05405e707fb6fb771ef92f9"}
|
// {"id":"3ff0d19493e661faa90ac06b5d8963ef1e48fe780368fe67ed0fd410df8a6dd5","pubkey":"3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d","created_at":1710759386,"kind":1,"tags":[],"content":"Nostr was coopted by our the corporate overlords. It is now featured in https://www.iana.org/assignments/well-known-uris/well-known-uris.xhtml.","sig":"faaec167cca4de50b562b7702e8854e2023f0ccd5f36d1b95b6eac20d352206342d6987e9516d283068c768e94dbe8858e2990c3e05405e707fb6fb771ef92f9"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleKeyPublic() {
|
func ExampleKeyPublic() {
|
||||||
app.Run([]string{"nak", "key", "public", "3ff0d19493e661faa90ac06b5d8963ef1e48fe780368fe67ed0fd410df8a6dd5", "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"})
|
app.Run(ctx, []string{"nak", "key", "public", "3ff0d19493e661faa90ac06b5d8963ef1e48fe780368fe67ed0fd410df8a6dd5", "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"})
|
||||||
// Output:
|
// Output:
|
||||||
// 70f7120d065870513a6bddb61c8d400ad1e43449b1900ffdb5551e4c421375c8
|
// 70f7120d065870513a6bddb61c8d400ad1e43449b1900ffdb5551e4c421375c8
|
||||||
// 718d756f60cf5179ef35b39dc6db3ff58f04c0734f81f6d4410f0b047ddf9029
|
// 718d756f60cf5179ef35b39dc6db3ff58f04c0734f81f6d4410f0b047ddf9029
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleKeyDecrypt() {
|
func ExampleKeyDecrypt() {
|
||||||
app.Run([]string{"nak", "key", "decrypt", "ncryptsec1qggfep0m5ythsegkmwfrhhx2zx5gazyhdygvlngcds4wsgdpzfy6nr0exy0pdk0ydwrqyhndt2trtwcgwwag0ja3aqclzptfxxqvprdyaz3qfrmazpecx2ff6dph5mfdjnh5sw8sgecul32eru6xet34", "banana"})
|
app.Run(ctx, []string{"nak", "key", "decrypt", "ncryptsec1qggfep0m5ythsegkmwfrhhx2zx5gazyhdygvlngcds4wsgdpzfy6nr0exy0pdk0ydwrqyhndt2trtwcgwwag0ja3aqclzptfxxqvprdyaz3qfrmazpecx2ff6dph5mfdjnh5sw8sgecul32eru6xet34", "banana"})
|
||||||
// Output:
|
// Output:
|
||||||
// nsec180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsgyumg0
|
// nsec180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsgyumg0
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleRelay() {
|
func ExampleRelay() {
|
||||||
app.Run([]string{"nak", "relay", "relay.nos.social", "pyramid.fiatjaf.com"})
|
app.Run(ctx, []string{"nak", "relay", "relay.nos.social", "pyramid.fiatjaf.com"})
|
||||||
// Output:
|
// Output:
|
||||||
// {
|
// {
|
||||||
// "name": "nos.social strfry relay",
|
// "name": "nos.social strfry relay",
|
||||||
|
|||||||
4
fetch.go
4
fetch.go
@@ -38,7 +38,7 @@ var fetch = &cli.Command{
|
|||||||
|
|
||||||
prefix, value, err := nip19.Decode(code)
|
prefix, value, err := nip19.Decode(code)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lineProcessingError(ctx, "failed to decode: %s", err)
|
ctx = lineProcessingError(ctx, "failed to decode: %s", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ var fetch = &cli.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(relays) == 0 {
|
if len(relays) == 0 {
|
||||||
lineProcessingError(ctx, "no relay hints found")
|
ctx = lineProcessingError(ctx, "no relay hints found")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
4
go.mod
4
go.mod
@@ -10,7 +10,7 @@ require (
|
|||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0
|
||||||
github.com/fatih/color v1.16.0
|
github.com/fatih/color v1.16.0
|
||||||
github.com/mailru/easyjson v0.7.7
|
github.com/mailru/easyjson v0.7.7
|
||||||
github.com/nbd-wtf/go-nostr v0.34.0
|
github.com/nbd-wtf/go-nostr v0.34.2
|
||||||
github.com/nbd-wtf/nostr-sdk v0.0.5
|
github.com/nbd-wtf/nostr-sdk v0.0.5
|
||||||
github.com/urfave/cli/v3 v3.0.0-alpha9
|
github.com/urfave/cli/v3 v3.0.0-alpha9
|
||||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
|
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
|
||||||
@@ -38,4 +38,4 @@ require (
|
|||||||
golang.org/x/text v0.8.0 // indirect
|
golang.org/x/text v0.8.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/urfave/cli/v3 => github.com/fiatjaf/cli/v3 v3.0.0-20240626022047-0fc2565ea728
|
replace github.com/urfave/cli/v3 => github.com/fiatjaf/cli/v3 v3.0.0-20240712212113-3a8b0280e2c5
|
||||||
|
|||||||
8
go.sum
8
go.sum
@@ -42,8 +42,8 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3
|
|||||||
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
|
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
|
||||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||||
github.com/fiatjaf/cli/v3 v3.0.0-20240626022047-0fc2565ea728 h1:MgEQQSCPDkpILGlJKCj/Gj0l8XwYFpBKYATPJC14ros=
|
github.com/fiatjaf/cli/v3 v3.0.0-20240712212113-3a8b0280e2c5 h1:yhTRU02Hn1jwq50uUKRxbPZQg0PODe37s73IJNsCJb0=
|
||||||
github.com/fiatjaf/cli/v3 v3.0.0-20240626022047-0fc2565ea728/go.mod h1:Z1ItyMma7t6I7zHG9OpbExhHQOSkFf/96n+mAZ9MtVI=
|
github.com/fiatjaf/cli/v3 v3.0.0-20240712212113-3a8b0280e2c5/go.mod h1:Z1ItyMma7t6I7zHG9OpbExhHQOSkFf/96n+mAZ9MtVI=
|
||||||
github.com/fiatjaf/eventstore v0.2.16 h1:NR64mnyUT5nJR8Sj2AwJTd1Hqs5kKJcCFO21ggUkvWg=
|
github.com/fiatjaf/eventstore v0.2.16 h1:NR64mnyUT5nJR8Sj2AwJTd1Hqs5kKJcCFO21ggUkvWg=
|
||||||
github.com/fiatjaf/eventstore v0.2.16/go.mod h1:rUc1KhVufVmC+HUOiuPweGAcvG6lEOQCkRCn2Xn5VRA=
|
github.com/fiatjaf/eventstore v0.2.16/go.mod h1:rUc1KhVufVmC+HUOiuPweGAcvG6lEOQCkRCn2Xn5VRA=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
@@ -79,8 +79,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
|
|||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/nbd-wtf/go-nostr v0.34.0 h1:E7tDHFx42gvWwFv1Eysn+NxJqGLmo21x/VEwj2+F21E=
|
github.com/nbd-wtf/go-nostr v0.34.2 h1:9b4qZ29DhQf9xEWN8/7zfDD868r1jFbpjrR3c+BHc+E=
|
||||||
github.com/nbd-wtf/go-nostr v0.34.0/go.mod h1:NZQkxl96ggbO8rvDpVjcsojJqKTPwqhP4i82O7K5DJs=
|
github.com/nbd-wtf/go-nostr v0.34.2/go.mod h1:NZQkxl96ggbO8rvDpVjcsojJqKTPwqhP4i82O7K5DJs=
|
||||||
github.com/nbd-wtf/nostr-sdk v0.0.5 h1:rec+FcDizDVO0W25PX0lgYMXvP7zNNOgI3Fu9UCm4BY=
|
github.com/nbd-wtf/nostr-sdk v0.0.5 h1:rec+FcDizDVO0W25PX0lgYMXvP7zNNOgI3Fu9UCm4BY=
|
||||||
github.com/nbd-wtf/nostr-sdk v0.0.5/go.mod h1:iJJsikesCGLNFZ9dLqhLPDzdt924EagUmdQxT3w2Lmk=
|
github.com/nbd-wtf/nostr-sdk v0.0.5/go.mod h1:iJJsikesCGLNFZ9dLqhLPDzdt924EagUmdQxT3w2Lmk=
|
||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
|
|||||||
46
helpers.go
46
helpers.go
@@ -9,6 +9,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/chzyer/readline"
|
"github.com/chzyer/readline"
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
@@ -117,13 +118,48 @@ func normalizeAndValidateRelayURLs(wsurls []string) error {
|
|||||||
func connectToAllRelays(
|
func connectToAllRelays(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
relayUrls []string,
|
relayUrls []string,
|
||||||
|
forcePreAuth bool,
|
||||||
opts ...nostr.PoolOption,
|
opts ...nostr.PoolOption,
|
||||||
) (*nostr.SimplePool, []*nostr.Relay) {
|
) (*nostr.SimplePool, []*nostr.Relay) {
|
||||||
relays := make([]*nostr.Relay, 0, len(relayUrls))
|
relays := make([]*nostr.Relay, 0, len(relayUrls))
|
||||||
pool := nostr.NewSimplePool(ctx, opts...)
|
pool := nostr.NewSimplePool(ctx, opts...)
|
||||||
|
relayLoop:
|
||||||
for _, url := range relayUrls {
|
for _, url := range relayUrls {
|
||||||
log("connecting to %s... ", url)
|
log("connecting to %s... ", url)
|
||||||
if relay, err := pool.EnsureRelay(url); err == nil {
|
if relay, err := 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(authEvent)
|
||||||
|
}); 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)
|
relays = append(relays, relay)
|
||||||
log("ok.\n")
|
log("ok.\n")
|
||||||
} else {
|
} else {
|
||||||
@@ -133,9 +169,9 @@ func connectToAllRelays(
|
|||||||
return pool, relays
|
return pool, relays
|
||||||
}
|
}
|
||||||
|
|
||||||
func lineProcessingError(ctx context.Context, msg string, args ...any) {
|
func lineProcessingError(ctx context.Context, msg string, args ...any) context.Context {
|
||||||
ctx = context.WithValue(ctx, LINE_PROCESSING_ERROR, true)
|
|
||||||
log(msg+"\n", args...)
|
log(msg+"\n", args...)
|
||||||
|
return context.WithValue(ctx, LINE_PROCESSING_ERROR, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func exitIfLineProcessingError(ctx context.Context) {
|
func exitIfLineProcessingError(ctx context.Context) {
|
||||||
@@ -174,7 +210,7 @@ func gatherSecretKeyOrBunkerFromArguments(ctx context.Context, c *cli.Command) (
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, fmt.Errorf("failed to decrypt: %w", err)
|
return "", nil, fmt.Errorf("failed to decrypt: %w", err)
|
||||||
}
|
}
|
||||||
} else if bsec, err := hex.DecodeString(strings.Repeat("0", 64-len(sec)) + sec); err == nil {
|
} else if bsec, err := hex.DecodeString(leftPadKey(sec)); err == nil {
|
||||||
sec = hex.EncodeToString(bsec)
|
sec = hex.EncodeToString(bsec)
|
||||||
} else if prefix, hexvalue, err := nip19.Decode(sec); err != nil {
|
} else if prefix, hexvalue, err := nip19.Decode(sec); err != nil {
|
||||||
return "", nil, fmt.Errorf("invalid nsec: %w", err)
|
return "", nil, fmt.Errorf("invalid nsec: %w", err)
|
||||||
@@ -248,3 +284,7 @@ func randString(n int) string {
|
|||||||
}
|
}
|
||||||
return string(b)
|
return string(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func leftPadKey(k string) string {
|
||||||
|
return strings.Repeat("0", 64-len(k)) + k
|
||||||
|
}
|
||||||
|
|||||||
11
key.go
11
key.go
@@ -50,7 +50,7 @@ var public = &cli.Command{
|
|||||||
for sec := range getSecretKeysFromStdinLinesOrSlice(ctx, c, c.Args().Slice()) {
|
for sec := range getSecretKeysFromStdinLinesOrSlice(ctx, c, c.Args().Slice()) {
|
||||||
pubkey, err := nostr.GetPublicKey(sec)
|
pubkey, err := nostr.GetPublicKey(sec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lineProcessingError(ctx, "failed to derive public key: %s", err)
|
ctx = lineProcessingError(ctx, "failed to derive public key: %s", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
stdout(pubkey)
|
stdout(pubkey)
|
||||||
@@ -88,7 +88,7 @@ var encrypt = &cli.Command{
|
|||||||
for sec := range getSecretKeysFromStdinLinesOrSlice(ctx, c, keys) {
|
for sec := range getSecretKeysFromStdinLinesOrSlice(ctx, c, keys) {
|
||||||
ncryptsec, err := nip49.Encrypt(sec, password, uint8(c.Int("logn")), 0x02)
|
ncryptsec, err := nip49.Encrypt(sec, password, uint8(c.Int("logn")), 0x02)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lineProcessingError(ctx, "failed to encrypt: %s", err)
|
ctx = lineProcessingError(ctx, "failed to encrypt: %s", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
stdout(ncryptsec)
|
stdout(ncryptsec)
|
||||||
@@ -133,7 +133,7 @@ var decrypt = &cli.Command{
|
|||||||
for ncryptsec := range getStdinLinesOrArgumentsFromSlice([]string{ncryptsec}) {
|
for ncryptsec := range getStdinLinesOrArgumentsFromSlice([]string{ncryptsec}) {
|
||||||
sec, err := nip49.Decrypt(ncryptsec, password)
|
sec, err := nip49.Decrypt(ncryptsec, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lineProcessingError(ctx, "failed to decrypt: %s", err)
|
ctx = lineProcessingError(ctx, "failed to decrypt: %s", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
nsec, _ := nip19.EncodePrivateKey(sec)
|
nsec, _ := nip19.EncodePrivateKey(sec)
|
||||||
@@ -264,13 +264,14 @@ func getSecretKeysFromStdinLinesOrSlice(ctx context.Context, c *cli.Command, key
|
|||||||
if strings.HasPrefix(sec, "nsec1") {
|
if strings.HasPrefix(sec, "nsec1") {
|
||||||
_, data, err := nip19.Decode(sec)
|
_, data, err := nip19.Decode(sec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lineProcessingError(ctx, "invalid nsec code: %s", err)
|
ctx = lineProcessingError(ctx, "invalid nsec code: %s", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
sec = data.(string)
|
sec = data.(string)
|
||||||
}
|
}
|
||||||
|
sec = leftPadKey(sec)
|
||||||
if !nostr.IsValid32ByteHex(sec) {
|
if !nostr.IsValid32ByteHex(sec) {
|
||||||
lineProcessingError(ctx, "invalid hex key")
|
ctx = lineProcessingError(ctx, "invalid hex key")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
ch <- sec
|
ch <- sec
|
||||||
|
|||||||
9
main.go
9
main.go
@@ -8,10 +8,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var app = &cli.Command{
|
var app = &cli.Command{
|
||||||
Name: "nak",
|
Name: "nak",
|
||||||
Suggest: true,
|
Suggest: true,
|
||||||
UseShortOptionHandling: true,
|
UseShortOptionHandling: true,
|
||||||
Usage: "the nostr army knife command-line tool",
|
AllowFlagsAfterArguments: true,
|
||||||
|
Usage: "the nostr army knife command-line tool",
|
||||||
Commands: []*cli.Command{
|
Commands: []*cli.Command{
|
||||||
req,
|
req,
|
||||||
count,
|
count,
|
||||||
|
|||||||
193
relay.go
193
relay.go
@@ -1,11 +1,20 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/nbd-wtf/go-nostr"
|
||||||
"github.com/nbd-wtf/go-nostr/nip11"
|
"github.com/nbd-wtf/go-nostr/nip11"
|
||||||
|
"github.com/nbd-wtf/go-nostr/nip86"
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -23,7 +32,7 @@ var relay = &cli.Command{
|
|||||||
|
|
||||||
info, err := nip11.Fetch(ctx, url)
|
info, err := nip11.Fetch(ctx, url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lineProcessingError(ctx, "failed to fetch '%s' information document: %w", url, err)
|
ctx = lineProcessingError(ctx, "failed to fetch '%s' information document: %w", url, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,4 +41,186 @@ var relay = &cli.Command{
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
Commands: (func() []*cli.Command {
|
||||||
|
commands := make([]*cli.Command, 0, 12)
|
||||||
|
|
||||||
|
for _, def := range []struct {
|
||||||
|
method string
|
||||||
|
args []string
|
||||||
|
}{
|
||||||
|
{"allowpubkey", []string{"pubkey", "reason"}},
|
||||||
|
{"banpubkey", []string{"pubkey", "reason"}},
|
||||||
|
{"listallowedpubkeys", nil},
|
||||||
|
{"allowpubkey", []string{"pubkey", "reason"}},
|
||||||
|
{"listallowedpubkeys", nil},
|
||||||
|
{"listeventsneedingmoderation", nil},
|
||||||
|
{"allowevent", []string{"id", "reason"}},
|
||||||
|
{"banevent", []string{"id", "reason"}},
|
||||||
|
{"listbannedevents", nil},
|
||||||
|
{"changerelayname", []string{"name"}},
|
||||||
|
{"changerelaydescription", []string{"description"}},
|
||||||
|
{"changerelayicon", []string{"icon"}},
|
||||||
|
{"allowkind", []string{"kind"}},
|
||||||
|
{"disallowkind", []string{"kind"}},
|
||||||
|
{"listallowedkinds", nil},
|
||||||
|
{"blockip", []string{"ip", "reason"}},
|
||||||
|
{"unblockip", []string{"ip", "reason"}},
|
||||||
|
{"listblockedips", nil},
|
||||||
|
} {
|
||||||
|
def := def
|
||||||
|
|
||||||
|
flags := make([]cli.Flag, len(def.args), len(def.args)+4)
|
||||||
|
for i, argName := range def.args {
|
||||||
|
flags[i] = declareFlag(argName)
|
||||||
|
}
|
||||||
|
|
||||||
|
flags = append(flags,
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "sec",
|
||||||
|
Usage: "secret key to sign the event, as nsec, ncryptsec or hex",
|
||||||
|
DefaultText: "the key '1'",
|
||||||
|
Value: "0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "prompt-sec",
|
||||||
|
Usage: "prompt the user to paste a hex or nsec with which to sign the event",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "connect",
|
||||||
|
Usage: "sign event using NIP-46, expects a bunker://... URL",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "connect-as",
|
||||||
|
Usage: "private key to when communicating with the bunker given on --connect",
|
||||||
|
DefaultText: "a random key",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
cmd := &cli.Command{
|
||||||
|
Name: def.method,
|
||||||
|
Usage: fmt.Sprintf(`the "%s" relay management RPC call`, def.method),
|
||||||
|
Description: fmt.Sprintf(
|
||||||
|
`the "%s" management RPC call, see https://nips.nostr.com/86 for more information`, def.method),
|
||||||
|
Action: func(ctx context.Context, c *cli.Command) error {
|
||||||
|
params := make([]any, len(def.args))
|
||||||
|
for i, argName := range def.args {
|
||||||
|
params[i] = getArgument(c, argName)
|
||||||
|
}
|
||||||
|
req := nip86.Request{Method: def.method, Params: params}
|
||||||
|
reqj, _ := json.Marshal(req)
|
||||||
|
|
||||||
|
relayUrls := c.Args().Slice()
|
||||||
|
if len(relayUrls) == 0 {
|
||||||
|
stdout(string(reqj))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
sec, bunker, err := gatherSecretKeyOrBunkerFromArguments(ctx, c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, relayUrl := range relayUrls {
|
||||||
|
httpUrl := "http" + nostr.NormalizeURL(relayUrl)[2:]
|
||||||
|
log("calling '%s' on %s... ", def.method, httpUrl)
|
||||||
|
body := bytes.NewBuffer(nil)
|
||||||
|
body.Write(reqj)
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "POST", httpUrl, body)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create request: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authorization
|
||||||
|
hostname := strings.Split(strings.Split(httpUrl, "://")[1], "/")[0]
|
||||||
|
payloadHash := sha256.Sum256(reqj)
|
||||||
|
authEvent := nostr.Event{
|
||||||
|
Kind: 27235,
|
||||||
|
CreatedAt: nostr.Now(),
|
||||||
|
Tags: nostr.Tags{
|
||||||
|
{"host", hostname},
|
||||||
|
{"payload", hex.EncodeToString(payloadHash[:])},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if bunker != nil {
|
||||||
|
if err := bunker.SignEvent(ctx, &authEvent); err != nil {
|
||||||
|
return fmt.Errorf("failed to sign with bunker: %w", err)
|
||||||
|
}
|
||||||
|
} else if err := authEvent.Sign(sec); err != nil {
|
||||||
|
return fmt.Errorf("error signing with provided key: %w", err)
|
||||||
|
}
|
||||||
|
evtj, _ := json.Marshal(authEvent)
|
||||||
|
req.Header.Set("Authorization", "Nostr "+base64.StdEncoding.EncodeToString(evtj))
|
||||||
|
|
||||||
|
// Content-Type
|
||||||
|
req.Header.Set("Content-Type", "application/nostr+json+rpc")
|
||||||
|
|
||||||
|
// make request to relay
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
log("failed: %s\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
b, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
log("failed to read response: %s\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if resp.StatusCode >= 300 {
|
||||||
|
log("failed with status %d\n", resp.StatusCode)
|
||||||
|
bodyPrintable := string(b)
|
||||||
|
if len(bodyPrintable) > 300 {
|
||||||
|
bodyPrintable = bodyPrintable[0:297] + "..."
|
||||||
|
}
|
||||||
|
log(bodyPrintable)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var response nip86.Response
|
||||||
|
if err := json.Unmarshal(b, &response); err != nil {
|
||||||
|
log("bad json response: %s\n", err)
|
||||||
|
bodyPrintable := string(b)
|
||||||
|
if len(bodyPrintable) > 300 {
|
||||||
|
bodyPrintable = bodyPrintable[0:297] + "..."
|
||||||
|
}
|
||||||
|
log(bodyPrintable)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
|
||||||
|
// print the result
|
||||||
|
log("\n")
|
||||||
|
pretty, _ := json.MarshalIndent(response, "", " ")
|
||||||
|
stdout(string(pretty))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
Flags: flags,
|
||||||
|
}
|
||||||
|
|
||||||
|
commands = append(commands, cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
return commands
|
||||||
|
})(),
|
||||||
|
}
|
||||||
|
|
||||||
|
func declareFlag(argName string) cli.Flag {
|
||||||
|
usage := "parameter for this management RPC call, see https://nips.nostr.com/86 for more information."
|
||||||
|
switch argName {
|
||||||
|
case "kind":
|
||||||
|
return &cli.IntFlag{Name: argName, Required: true, Usage: usage}
|
||||||
|
case "reason":
|
||||||
|
return &cli.StringFlag{Name: argName, Usage: usage}
|
||||||
|
default:
|
||||||
|
return &cli.StringFlag{Name: argName, Required: true, Usage: usage}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getArgument(c *cli.Command, argName string) any {
|
||||||
|
switch argName {
|
||||||
|
case "kind":
|
||||||
|
return c.Int(argName)
|
||||||
|
default:
|
||||||
|
return c.String(argName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
14
req.go
14
req.go
@@ -104,6 +104,11 @@ example:
|
|||||||
Name: "auth",
|
Name: "auth",
|
||||||
Usage: "always perform NIP-42 \"AUTH\" when facing an \"auth-required: \" rejection and try again",
|
Usage: "always perform NIP-42 \"AUTH\" when facing an \"auth-required: \" rejection and try again",
|
||||||
},
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "force-pre-auth",
|
||||||
|
Aliases: []string{"fpa"},
|
||||||
|
Usage: "after connecting, for a NIP-42 \"AUTH\" message to be received, act on it and only then send the \"REQ\"",
|
||||||
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "sec",
|
Name: "sec",
|
||||||
Usage: "secret key to sign the AUTH challenge, as hex or nsec",
|
Usage: "secret key to sign the AUTH challenge, as hex or nsec",
|
||||||
@@ -127,11 +132,12 @@ example:
|
|||||||
ArgsUsage: "[relay...]",
|
ArgsUsage: "[relay...]",
|
||||||
Action: func(ctx context.Context, c *cli.Command) error {
|
Action: func(ctx context.Context, c *cli.Command) error {
|
||||||
var pool *nostr.SimplePool
|
var pool *nostr.SimplePool
|
||||||
|
|
||||||
relayUrls := c.Args().Slice()
|
relayUrls := c.Args().Slice()
|
||||||
if len(relayUrls) > 0 {
|
if len(relayUrls) > 0 {
|
||||||
var relays []*nostr.Relay
|
var relays []*nostr.Relay
|
||||||
pool, relays = connectToAllRelays(ctx, relayUrls, nostr.WithAuthHandler(func(evt *nostr.Event) error {
|
pool, relays = connectToAllRelays(ctx, relayUrls, c.Bool("force-pre-auth"), nostr.WithAuthHandler(func(evt *nostr.Event) error {
|
||||||
if !c.Bool("auth") {
|
if !c.Bool("auth") && !c.Bool("force-pre-auth") {
|
||||||
return fmt.Errorf("auth not authorized")
|
return fmt.Errorf("auth not authorized")
|
||||||
}
|
}
|
||||||
sec, bunker, err := gatherSecretKeyOrBunkerFromArguments(ctx, c)
|
sec, bunker, err := gatherSecretKeyOrBunkerFromArguments(ctx, c)
|
||||||
@@ -148,7 +154,7 @@ example:
|
|||||||
} else {
|
} else {
|
||||||
pk, _ = nostr.GetPublicKey(sec)
|
pk, _ = nostr.GetPublicKey(sec)
|
||||||
}
|
}
|
||||||
log("performing auth as %s...\n", pk)
|
log("performing auth as %s... ", pk)
|
||||||
|
|
||||||
if bunker != nil {
|
if bunker != nil {
|
||||||
return bunker.SignEvent(ctx, evt)
|
return bunker.SignEvent(ctx, evt)
|
||||||
@@ -176,7 +182,7 @@ example:
|
|||||||
filter := nostr.Filter{}
|
filter := nostr.Filter{}
|
||||||
if stdinFilter != "" {
|
if stdinFilter != "" {
|
||||||
if err := easyjson.Unmarshal([]byte(stdinFilter), &filter); err != nil {
|
if err := easyjson.Unmarshal([]byte(stdinFilter), &filter); err != nil {
|
||||||
lineProcessingError(ctx, "invalid filter '%s' received from stdin: %s", stdinFilter, err)
|
ctx = lineProcessingError(ctx, "invalid filter '%s' received from stdin: %s", stdinFilter, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,18 +20,18 @@ it outputs nothing if the verification is successful.`,
|
|||||||
evt := nostr.Event{}
|
evt := nostr.Event{}
|
||||||
if stdinEvent != "" {
|
if stdinEvent != "" {
|
||||||
if err := json.Unmarshal([]byte(stdinEvent), &evt); err != nil {
|
if err := json.Unmarshal([]byte(stdinEvent), &evt); err != nil {
|
||||||
lineProcessingError(ctx, "invalid event: %s", err)
|
ctx = lineProcessingError(ctx, "invalid event: %s", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if evt.GetID() != evt.ID {
|
if evt.GetID() != evt.ID {
|
||||||
lineProcessingError(ctx, "invalid .id, expected %s, got %s", evt.GetID(), evt.ID)
|
ctx = lineProcessingError(ctx, "invalid .id, expected %s, got %s", evt.GetID(), evt.ID)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if ok, err := evt.CheckSignature(); !ok {
|
if ok, err := evt.CheckSignature(); !ok {
|
||||||
lineProcessingError(ctx, "invalid signature: %s", err)
|
ctx = lineProcessingError(ctx, "invalid signature: %s", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user