Compare commits

..

4 Commits

3 changed files with 88 additions and 149 deletions

View File

@@ -418,6 +418,13 @@ var bunker = &cli.Command{
return true return true
} }
if slices.Contains(authorizedSecrets, secret) { if slices.Contains(authorizedSecrets, secret) {
// add client to authorized list for subsequent requests
if !slices.ContainsFunc(config.Clients, func(c BunkerConfigClient) bool { return c.PubKey == from }) {
config.Clients = append(config.Clients, BunkerConfigClient{PubKey: from})
if persist != nil {
persist()
}
}
return true return true
} }

111
count.go
View File

@@ -9,62 +9,16 @@ import (
"fiatjaf.com/nostr" "fiatjaf.com/nostr"
"fiatjaf.com/nostr/nip45" "fiatjaf.com/nostr/nip45"
"fiatjaf.com/nostr/nip45/hyperloglog" "fiatjaf.com/nostr/nip45/hyperloglog"
"github.com/mailru/easyjson"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v3"
) )
var count = &cli.Command{ var count = &cli.Command{
Name: "count", Name: "count",
Usage: "generates encoded COUNT messages and optionally use them to talk to relays", Usage: "generates encoded COUNT messages and optionally use them to talk to relays",
Description: `outputs a nip45 request (the flags are mostly the same as 'nak req').`, Description: `like 'nak req', but does a "COUNT" call instead. Will attempt to perform HyperLogLog aggregation if more than one relay is specified.`,
DisableSliceFlagSeparator: true, DisableSliceFlagSeparator: true,
Flags: []cli.Flag{ Flags: reqFilterFlags,
&PubKeySliceFlag{
Name: "author",
Aliases: []string{"a"},
Usage: "only accept events from these authors",
Category: CATEGORY_FILTER_ATTRIBUTES,
},
&cli.IntSliceFlag{
Name: "kind",
Aliases: []string{"k"},
Usage: "only accept events with these kind numbers",
Category: CATEGORY_FILTER_ATTRIBUTES,
},
&cli.StringSliceFlag{
Name: "tag",
Aliases: []string{"t"},
Usage: "takes a tag like -t e=<id>, only accept events with these tags",
Category: CATEGORY_FILTER_ATTRIBUTES,
},
&cli.StringSliceFlag{
Name: "e",
Usage: "shortcut for --tag e=<value>",
Category: CATEGORY_FILTER_ATTRIBUTES,
},
&cli.StringSliceFlag{
Name: "p",
Usage: "shortcut for --tag p=<value>",
Category: CATEGORY_FILTER_ATTRIBUTES,
},
&NaturalTimeFlag{
Name: "since",
Aliases: []string{"s"},
Usage: "only accept events newer than this (unix timestamp)",
Category: CATEGORY_FILTER_ATTRIBUTES,
},
&NaturalTimeFlag{
Name: "until",
Aliases: []string{"u"},
Usage: "only accept events older than this (unix timestamp)",
Category: CATEGORY_FILTER_ATTRIBUTES,
},
&cli.IntFlag{
Name: "limit",
Aliases: []string{"l"},
Usage: "only accept up to this number of events",
Category: CATEGORY_FILTER_ATTRIBUTES,
},
},
ArgsUsage: "[relay...]", ArgsUsage: "[relay...]",
Action: func(ctx context.Context, c *cli.Command) error { Action: func(ctx context.Context, c *cli.Command) error {
biggerUrlSize := 0 biggerUrlSize := 0
@@ -84,53 +38,18 @@ var count = &cli.Command{
} }
} }
// go line by line from stdin or run once with input from flags
for stdinFilter := range getJsonsOrBlank() {
filter := nostr.Filter{} filter := nostr.Filter{}
if stdinFilter != "" {
if authors := getPubKeySlice(c, "author"); len(authors) > 0 { if err := easyjson.Unmarshal([]byte(stdinFilter), &filter); err != nil {
filter.Authors = authors ctx = lineProcessingError(ctx, "invalid filter '%s' received from stdin: %s", stdinFilter, err)
} continue
if kinds64 := c.IntSlice("kind"); len(kinds64) > 0 {
kinds := make([]nostr.Kind, len(kinds64))
for i, v := range kinds64 {
kinds[i] = nostr.Kind(v)
}
filter.Kinds = kinds
}
tags := make([][]string, 0, 5)
for _, tagFlag := range c.StringSlice("tag") {
spl := strings.SplitN(tagFlag, "=", 2)
if len(spl) == 2 {
tags = append(tags, []string{spl[0], decodeTagValue(spl[1])})
} else {
return fmt.Errorf("invalid --tag '%s'", tagFlag)
}
}
for _, etag := range c.StringSlice("e") {
tags = append(tags, []string{"e", decodeTagValue(etag)})
}
for _, ptag := range c.StringSlice("p") {
tags = append(tags, []string{"p", decodeTagValue(ptag)})
}
if len(tags) > 0 {
filter.Tags = make(nostr.TagMap)
for _, tag := range tags {
if _, ok := filter.Tags[tag[0]]; !ok {
filter.Tags[tag[0]] = make([]string, 0, 3)
}
filter.Tags[tag[0]] = append(filter.Tags[tag[0]], tag[1])
} }
} }
if c.IsSet("since") { if err := applyFlagsToFilter(c, &filter); err != nil {
filter.Since = getNaturalDate(c, "since") return err
}
if c.IsSet("until") {
filter.Until = getNaturalDate(c, "until")
}
if limit := c.Int("limit"); limit != 0 {
filter.Limit = int(limit)
} }
successes := 0 successes := 0
@@ -147,14 +66,14 @@ var count = &cli.Command{
fmt.Fprintf(os.Stderr, "%s%s: ", strings.Repeat(" ", biggerUrlSize-len(relayUrl)), relayUrl) fmt.Fprintf(os.Stderr, "%s%s: ", strings.Repeat(" ", biggerUrlSize-len(relayUrl)), relayUrl)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, " %s\n", err) fmt.Fprintf(os.Stderr, "error: %s\n", err)
continue continue
} }
var hasHLLStr string var hasHLLStr string
if hll != nil && len(hllRegisters) == 256 { if hll != nil && len(hllRegisters) == 256 {
hll.MergeRegisters(hllRegisters) hll.MergeRegisters(hllRegisters)
hasHLLStr = " 📋" hasHLLStr = " (hll)"
} }
fmt.Fprintf(os.Stderr, "%d%s\n", count, hasHLLStr) fmt.Fprintf(os.Stderr, "%d%s\n", count, hasHLLStr)
@@ -163,7 +82,7 @@ var count = &cli.Command{
if successes == 0 { if successes == 0 {
return fmt.Errorf("all relays have failed") return fmt.Errorf("all relays have failed")
} else if hll != nil { } else if hll != nil {
fmt.Fprintf(os.Stderr, "📋 HyperLogLog sum: %d\n", hll.Count()) fmt.Fprintf(os.Stderr, "HyperLogLog sum: %d\n", hll.Count())
} }
} else { } else {
// no relays given, will just print the filter // no relays given, will just print the filter
@@ -172,7 +91,9 @@ var count = &cli.Command{
result = string(j) result = string(j)
stdout(result) stdout(result)
} }
}
exitIfLineProcessingError(ctx)
return nil return nil
}, },
} }

17
mcp.go
View File

@@ -158,15 +158,24 @@ var mcpServer = &cli.Command{
name := required[string](r, "name") name := required[string](r, "name")
limit, _ := optional[float64](r, "limit") limit, _ := optional[float64](r, "limit")
res := strings.Builder{}
res.Grow(500)
res.WriteString("search results: ")
// check if input is already a valid pubkey
if pubkey, err := nostr.PubKeyFromHex(name); err == nil {
pm := sys.FetchProfileMetadata(ctx, pubkey)
res.WriteString(fmt.Sprintf("\n\nResult 1\nUser name: \"%s\"\nPublic key: \"%s\"\nDescription: \"%s\"\n",
pm.ShortName(), pm.PubKey.Hex(), pm.About))
} else {
// otherwise try to search
filter := nostr.Filter{Search: name, Kinds: []nostr.Kind{0}} filter := nostr.Filter{Search: name, Kinds: []nostr.Kind{0}}
if limit > 0 { if limit > 0 {
filter.Limit = int(limit) filter.Limit = int(limit)
} }
res := strings.Builder{}
res.WriteString("search results: ")
l := 0 l := 0
for result := range sys.Pool.FetchMany(ctx, []string{"relay.nostr.band", "nostr.wine"}, filter, nostr.SubscriptionOptions{ for result := range sys.Pool.FetchMany(ctx, []string{"relay.nostr.band", "nostr.wine", "search.nos.social"}, filter, nostr.SubscriptionOptions{
Label: "nak-mcp-search", Label: "nak-mcp-search",
}) { }) {
l++ l++
@@ -181,6 +190,8 @@ var mcpServer = &cli.Command{
if l == 0 { if l == 0 {
return mcp.NewToolResultError("couldn't find anyone with that name."), nil return mcp.NewToolResultError("couldn't find anyone with that name."), nil
} }
}
return mcp.NewToolResultText(res.String()), nil return mcp.NewToolResultText(res.String()), nil
}) })