package envelope import ( "bytes" "crypto/sha256" "io" "smesh.lol/pkg/nostr/filter" "smesh.lol/pkg/nostr/hex" "smesh.lol/pkg/nostr/ints" "smesh.lol/pkg/nostr/text" "smesh.lol/pkg/lol/chk" "smesh.lol/pkg/lol/errorf" ) // Close — client request to terminate a subscription. const CloseLabel = "CLOSE" type Close struct{ ID []byte } func (en *Close) Label() 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 = append(o, '"') o = append(o, en.ID...) o = append(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() 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 = append(o, '"') o = append(o, en.Subscription...) o = append(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() 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 = append(o, '"') o = hex.EncAppend(o, en.EventID) o = append(o, '"', ',') o = text.MarshalBool(o, en.OK) o = append(o, ',', '"') o = text.NostrEscape(o, en.Reason) o = append(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() 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 = append(o, '"') o = text.NostrEscape(o, en.Message) o = append(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() 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 = append(o, '"') o = append(o, en.Subscription...) o = append(o, '"', ',', '"') o = text.NostrEscape(o, en.Reason) o = append(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() 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 = append(o, '"') o = append(o, en.Subscription...) o = append(o, '"') for _, f := range en.Filters { o = append(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 } if r, err = en.Filters.Unmarshal(r); chk.E(err) { return } r, err = SkipToTheEnd(r) return } type CountResponse struct { Subscription []byte Count int Approximate bool } func (en *CountResponse) Label() 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 = append(o, '"') o = append(o, en.Subscription...) o = append(o, '"', ',') o = ints.New(en.Count).Marshal(o) if en.Approximate { o = append(o, ',') o = append(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 = int(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 }