diff --git a/event_aux.go b/event_aux.go index b86f5bc..70e2ddf 100644 --- a/event_aux.go +++ b/event_aux.go @@ -2,128 +2,12 @@ package nostr import ( "encoding/json" - "errors" "fmt" - "strings" "time" "github.com/valyala/fastjson" - "golang.org/x/exp/slices" ) -type Tag []string - -// StartsWith checks if a tag contains a prefix. -// for example, -// ["p", "abcdef...", "wss://relay.com"] -// would match against -// ["p", "abcdef..."] -// or even -// ["p", "abcdef...", "wss://"] -func (tag Tag) StartsWith(prefix []string) bool { - prefixLen := len(prefix) - - if prefixLen > len(tag) { - return false - } - // check initial elements for equality - for i := 0; i < prefixLen-1; i++ { - if prefix[i] != tag[i] { - return false - } - } - // check last element just for a prefix - return strings.HasPrefix(tag[prefixLen-1], prefix[prefixLen-1]) -} - -type Tags []Tag - -// GetFirst gets the first tag in tags that matches the prefix, see [Tag.StartsWith] -func (tags Tags) GetFirst(tagPrefix []string) *Tag { - for _, v := range tags { - if v.StartsWith(tagPrefix) { - return &v - } - } - return nil -} - -// GetLast gets the last tag in tags that matches the prefix, see [Tag.StartsWith] -func (tags Tags) GetLast(tagPrefix []string) *Tag { - for i := len(tags) - 1; i >= 0; i-- { - v := tags[i] - if v.StartsWith(tagPrefix) { - return &v - } - } - return nil -} - -// GetLast gets all the tags that match the prefix, see [Tag.StartsWith] -func (tags Tags) GetAll(tagPrefix []string) Tags { - result := make(Tags, 0, len(tags)) - for _, v := range tags { - if v.StartsWith(tagPrefix) { - result = append(result, v) - } - } - return result -} - -// FilterOut removes all tags that match the prefix, see [Tag.StartsWith] -func (tags Tags) FilterOut(tagPrefix []string) Tags { - filtered := make(Tags, 0, len(tags)) - for _, v := range tags { - if !v.StartsWith(tagPrefix) { - filtered = append(filtered, v) - } - } - return filtered -} - -// AppendUnique appends a tag if it doesn't exist yet, otherwise does nothing -func (tags Tags) AppendUnique(tag Tag) Tags { - if tags.GetFirst(tag) == nil { - return append(tags, tag) - } else { - return tags - } -} - -func (t *Tags) Scan(src interface{}) error { - var jtags []byte = make([]byte, 0) - - switch v := src.(type) { - case []byte: - jtags = v - case string: - jtags = []byte(v) - default: - return errors.New("couldn't scan tags, it's not a json string") - } - - json.Unmarshal(jtags, &t) - return nil -} - -func (tags Tags) ContainsAny(tagName string, values []string) bool { - for _, tag := range tags { - if len(tag) < 2 { - continue - } - - if tag[0] != tagName { - continue - } - - if slices.Contains(values, tag[1]) { - return true - } - } - - return false -} - func (evt *Event) UnmarshalJSON(payload []byte) error { var fastjsonParser fastjson.Parser parsed, err := fastjsonParser.ParseBytes(payload) diff --git a/nip10/nip10.go b/nip10/nip10.go new file mode 100644 index 0000000..88f2bc0 --- /dev/null +++ b/nip10/nip10.go @@ -0,0 +1,24 @@ +package nip10 + +import "github.com/nbd-wtf/go-nostr" + +func GetThreadRoot(tags nostr.Tags) *nostr.Tag { + for _, tag := range tags { + if len(tag) >= 4 && tag[0] == "e" && tag[3] == "root" { + return &tag + } + } + + return tags.GetFirst([]string{"e", ""}) +} + +func GetImmediateReply(tags nostr.Tags) *nostr.Tag { + for i := len(tags) - 1; i >= 0; i-- { + tag := tags[i] + if len(tag) >= 4 && tag[0] == "e" && tag[3] == "reply" { + return &tag + } + } + + return tags.GetLast([]string{"e", ""}) +} diff --git a/tags.go b/tags.go new file mode 100644 index 0000000..d76df25 --- /dev/null +++ b/tags.go @@ -0,0 +1,143 @@ +package nostr + +import ( + "encoding/json" + "errors" + "strings" + + "golang.org/x/exp/slices" +) + +type Tag []string + +// StartsWith checks if a tag contains a prefix. +// for example, +// ["p", "abcdef...", "wss://relay.com"] +// would match against +// ["p", "abcdef..."] +// or even +// ["p", "abcdef...", "wss://"] +func (tag Tag) StartsWith(prefix []string) bool { + prefixLen := len(prefix) + + if prefixLen > len(tag) { + return false + } + // check initial elements for equality + for i := 0; i < prefixLen-1; i++ { + if prefix[i] != tag[i] { + return false + } + } + // check last element just for a prefix + return strings.HasPrefix(tag[prefixLen-1], prefix[prefixLen-1]) +} + +func (tag Tag) Key() string { + if len(tag) > 0 { + return tag[0] + } + return "" +} + +func (tag Tag) Value() string { + if len(tag) > 1 { + return tag[1] + } + return "" +} + +func (tag Tag) Relay() string { + if tag[0] == "e" || tag[0] == "p" && len(tag) > 2 { + return tag[2] + } + return "" +} + +type Tags []Tag + +// GetFirst gets the first tag in tags that matches the prefix, see [Tag.StartsWith] +func (tags Tags) GetFirst(tagPrefix []string) *Tag { + for _, v := range tags { + if v.StartsWith(tagPrefix) { + return &v + } + } + return nil +} + +// GetLast gets the last tag in tags that matches the prefix, see [Tag.StartsWith] +func (tags Tags) GetLast(tagPrefix []string) *Tag { + for i := len(tags) - 1; i >= 0; i-- { + v := tags[i] + if v.StartsWith(tagPrefix) { + return &v + } + } + return nil +} + +// GetLast gets all the tags that match the prefix, see [Tag.StartsWith] +func (tags Tags) GetAll(tagPrefix []string) Tags { + result := make(Tags, 0, len(tags)) + for _, v := range tags { + if v.StartsWith(tagPrefix) { + result = append(result, v) + } + } + return result +} + +// FilterOut removes all tags that match the prefix, see [Tag.StartsWith] +func (tags Tags) FilterOut(tagPrefix []string) Tags { + filtered := make(Tags, 0, len(tags)) + for _, v := range tags { + if !v.StartsWith(tagPrefix) { + filtered = append(filtered, v) + } + } + return filtered +} + +// AppendUnique appends a tag if it doesn't exist yet, otherwise does nothing +func (tags Tags) AppendUnique(tag Tag) Tags { + if tags.GetFirst(tag) == nil { + return append(tags, tag) + } else { + return tags + } +} + +func (t *Tags) Scan(src interface{}) error { + var jtags []byte = make([]byte, 0) + + switch v := src.(type) { + case []byte: + jtags = v + case string: + jtags = []byte(v) + default: + return errors.New("couldn't scan tags, it's not a json string") + } + + json.Unmarshal(jtags, &t) + return nil +} + +func (tags Tags) ContainsAny(tagName string, values []string) bool { + for _, tag := range tags { + if len(tag) < 2 { + continue + } + + if tag[0] != tagName { + continue + } + + if slices.Contains(values, tag[1]) { + return true + } + } + + return false +}