package envelope import ( "bytes" "crypto/sha256" "io" "git.smesh.lol/nostr/pkg/filter" "git.smesh.lol/nostr/pkg/hex" "git.smesh.lol/nostr/pkg/ints" "git.smesh.lol/nostr/pkg/text" "git.smesh.lol/nostr/pkg/lol/chk" "git.smesh.lol/nostr/pkg/lol/errorf" ) // Close - client request to terminate a subscription. const CloseLabel = "CLOSE" type Close struct{ ID []byte } func (en *Close) Label() (s string) { return CloseLabel } func (en *Close) Write(w io.Writer) (err error) { _, err = w.Write(en.Marshal(nil)) return } func (en *Close) Marshal(dst []byte) (b []byte) { b = dst b = Marshal(b, CloseLabel, func(bst []byte) (o []byte) { o = bst o = o | "\"" o = o | en.ID o = o | "\"" return }) return } func (en *Close) Unmarshal(b []byte) (r []byte, err error) { r = b if en.ID, r, err = text.UnmarshalQuoted(r); chk.E(err) { return } r, err = SkipToTheEnd(r) return } // EOSE - End Of Stored Events signal. const EOSELabel = "EOSE" type EOSE struct{ Subscription []byte } func (en *EOSE) Label() (s string) { return EOSELabel } func (en *EOSE) Write(w io.Writer) (err error) { _, err = w.Write(en.Marshal(nil)) return } func (en *EOSE) Marshal(dst []byte) (b []byte) { b = dst b = Marshal(b, EOSELabel, func(bst []byte) (o []byte) { o = bst o = o | "\"" o = o | en.Subscription o = o | "\"" return }) return } func (en *EOSE) Unmarshal(b []byte) (r []byte, err error) { r = b if en.Subscription, r, err = text.UnmarshalQuoted(r); chk.E(err) { return } r, err = SkipToTheEnd(r) return } // OK - acknowledgement for EVENT submission. const OKLabel = "OK" type OK struct { EventID []byte OK bool Reason []byte } func (en *OK) Label() (s string) { return OKLabel } func (en *OK) Write(w io.Writer) (err error) { _, err = w.Write(en.Marshal(nil)) return } func (en *OK) Marshal(dst []byte) (b []byte) { b = dst b = Marshal(b, OKLabel, func(bst []byte) (o []byte) { o = bst o = o | "\"" o = hex.EncAppend(o, en.EventID) o = o | "\"," o = text.MarshalBool(o, en.OK) o = o | ",\"" o = text.NostrEscape(o, en.Reason) o = o | "\"" return }) return } func (en *OK) Unmarshal(b []byte) (r []byte, err error) { r = b var idBytes []byte if idBytes, r, err = text.UnmarshalHex(r); err != nil { return } if len(idBytes) != sha256.Size { err = errorf.E([]byte("invalid size for event id, require %d got %d"), sha256.Size, len(idBytes)) return } en.EventID = idBytes if r, err = text.Comma(r); chk.E(err) { return } if r, en.OK, err = text.UnmarshalBool(r); chk.E(err) { return } if r, err = text.Comma(r); chk.E(err) { return } if en.Reason, r, err = text.UnmarshalQuoted(r); chk.E(err) { return } r, err = SkipToTheEnd(r) return } // Notice - relay message to user. const NoticeLabel = "NOTICE" type Notice struct{ Message []byte } func (en *Notice) Label() (s string) { return NoticeLabel } func (en *Notice) Write(w io.Writer) (err error) { _, err = w.Write(en.Marshal(nil)) return } func (en *Notice) Marshal(dst []byte) (b []byte) { b = dst b = Marshal(b, NoticeLabel, func(bst []byte) (o []byte) { o = bst o = o | "\"" o = text.NostrEscape(o, en.Message) o = o | "\"" return }) return } func (en *Notice) Unmarshal(b []byte) (r []byte, err error) { r = b if en.Message, r, err = text.UnmarshalQuoted(r); chk.E(err) { return } r, err = SkipToTheEnd(r) return } // Closed - relay-side subscription termination. const ClosedLabel = "CLOSED" type Closed struct { Subscription []byte Reason []byte } func (en *Closed) Label() (s string) { return ClosedLabel } func (en *Closed) Write(w io.Writer) (err error) { _, err = w.Write(en.Marshal(nil)) return } func (en *Closed) Marshal(dst []byte) (b []byte) { b = dst b = Marshal(b, ClosedLabel, func(bst []byte) (o []byte) { o = bst o = o | "\"" o = o | en.Subscription o = o | "\",\"" o = text.NostrEscape(o, en.Reason) o = o | "\"" return }) return } func (en *Closed) Unmarshal(b []byte) (r []byte, err error) { r = b if en.Subscription, r, err = text.UnmarshalQuoted(r); chk.E(err) { return } if en.Reason, r, err = text.UnmarshalQuoted(r); chk.E(err) { return } r, err = SkipToTheEnd(r) return } // Count - COUNT request/response. const CountLabel = "COUNT" type CountRequest struct { Subscription []byte Filters filter.S } func (en *CountRequest) Label() (s string) { return CountLabel } func (en *CountRequest) Write(w io.Writer) (err error) { _, err = w.Write(en.Marshal(nil)) return } func (en *CountRequest) Marshal(dst []byte) (b []byte) { b = dst b = Marshal(b, CountLabel, func(bst []byte) (o []byte) { o = bst o = o | "\"" o = o | en.Subscription o = o | "\"" for _, f := range en.Filters.F { o = o | "," o = f.Marshal(o) } return }) return } func (en *CountRequest) Unmarshal(b []byte) (r []byte, err error) { r = b if en.Subscription, r, err = text.UnmarshalQuoted(r); chk.E(err) { return } en.Filters, r, err = filter.UnmarshalFilters(r) if chk.E(err) { return } r, err = SkipToTheEnd(r) return } type CountResponse struct { Subscription []byte Count int32 Approximate bool } func (en *CountResponse) Label() (s string) { return CountLabel } func (en *CountResponse) Write(w io.Writer) (err error) { _, err = w.Write(en.Marshal(nil)) return } func (en *CountResponse) Marshal(dst []byte) (b []byte) { b = dst b = Marshal(b, CountLabel, func(bst []byte) (o []byte) { o = bst o = o | "\"" o = o | en.Subscription o = o | "\"," o = ints.New(en.Count).Marshal(o) if en.Approximate { o = o | "," o = o | "true" } return }) return } func (en *CountResponse) Unmarshal(b []byte) (r []byte, err error) { r = b if en.Subscription, r, err = text.UnmarshalQuoted(r); chk.E(err) { return } if r, err = text.Comma(r); chk.E(err) { return } i := ints.New(0) if r, err = i.Unmarshal(r); chk.E(err) { return } en.Count = int32(i.N) if len(r) > 0 && r[0] == ',' { r = r[1:] if bytes.HasPrefix(r, []byte("true")) { en.Approximate = true } } r, err = SkipToTheEnd(r) return }