mirror of
https://github.com/fiatjaf/nak.git
synced 2026-07-30 19:36:15 +00:00
102 lines
2.3 KiB
Go
102 lines
2.3 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"fiatjaf.com/nostr"
|
|
"github.com/itchyny/gojq"
|
|
)
|
|
|
|
const eventJQPrelude = `
|
|
def tags(tagName): .tags | map(select(.[0] == tagName));
|
|
def tag(tagName): tags(tagName) | .[0];
|
|
def value(tagName): tag(tagName)[1];
|
|
def has(tagName): (tags(tagName) | length) > 0;
|
|
def hasnt(tagName): (tags(tagName) | length) == 0;
|
|
def has_value(tagName; tagValue): tags(tagName) | map(select(.[1] == tagValue)) | length > 0;
|
|
def date: .created_at | gmtime | strftime("%Y-%m-%d");
|
|
def time: .created_at | gmtime | strftime("%H:%M:%S");
|
|
def datetime: .created_at | gmtime | strftime("%Y-%m-%dT%H:%M:%SZ");
|
|
`
|
|
|
|
// jqProcessor runs the compiled jq expression against an event and returns the
|
|
// rendered output string. When matches is false the event should be skipped.
|
|
type jqProcessor func(nostr.Event) (out string, matches bool, err error)
|
|
|
|
func jqPrepare(expr string, raw bool) (jqProcessor, error) {
|
|
if expr == "" {
|
|
return nil, nil
|
|
}
|
|
|
|
query, err := gojq.Parse(eventJQPrelude + expr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid jq expression: %w", err)
|
|
}
|
|
|
|
code, err := gojq.Compile(query)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to compile jq expression: %w", err)
|
|
}
|
|
|
|
return func(evt nostr.Event) (string, bool, error) {
|
|
input, err := toJQInput(evt)
|
|
if err != nil {
|
|
return "", false, err
|
|
}
|
|
|
|
iter := code.Run(input)
|
|
for {
|
|
v, ok := iter.Next()
|
|
if !ok {
|
|
return "", false, nil
|
|
}
|
|
|
|
if err, ok := v.(error); ok {
|
|
return "", false, err
|
|
}
|
|
|
|
if jqTruthy(v) {
|
|
return jqStringify(v, raw), true, nil
|
|
}
|
|
}
|
|
}, nil
|
|
}
|
|
|
|
// jqStringify renders a jq result value. In raw mode string results are printed
|
|
// without JSON quoting (like `jq -r`); non-string results are always
|
|
// JSON-encoded.
|
|
func jqStringify(v any, raw bool) string {
|
|
if raw {
|
|
if s, ok := v.(string); ok {
|
|
return s
|
|
}
|
|
}
|
|
out, _ := json.MarshalToString(v)
|
|
return out
|
|
}
|
|
|
|
func toJQInput(v any) (any, error) {
|
|
data, err := json.Marshal(v)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to marshal jq input: %w", err)
|
|
}
|
|
|
|
var input any
|
|
if err := json.Unmarshal(data, &input); err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal jq input: %w", err)
|
|
}
|
|
|
|
return input, nil
|
|
}
|
|
|
|
func jqTruthy(v any) bool {
|
|
switch v := v.(type) {
|
|
case nil:
|
|
return false
|
|
case bool:
|
|
return v
|
|
default:
|
|
return true
|
|
}
|
|
}
|