1 // Package closedenvelope defines the nostr message type CLOSED which is sent
2 // from a relay to indicate the relay-side termination of a subscription or the
3 // demand for authentication associated with a subscription.
4 package closedenvelope
5 6 import (
7 "io"
8 9 "next.orly.dev/pkg/nostr/encoders/envelopes"
10 "next.orly.dev/pkg/nostr/encoders/text"
11 "next.orly.dev/pkg/nostr/interfaces/codec"
12 "next.orly.dev/pkg/lol/chk"
13 )
14 15 // L is the label associated with this type of codec.Envelope.
16 const L = "CLOSED"
17 18 // T is a CLOSED envelope, which is a signal that a subscription has been
19 // stopped on the relay side for some reason. Primarily this is for auth and can
20 // be for other things like rate limiting.
21 type T struct {
22 Subscription []byte
23 Reason []byte
24 }
25 26 var _ codec.Envelope = (*T)(nil)
27 28 // New creates an empty new T.
29 func New() *T {
30 return new(T)
31 }
32 33 // NewFrom creates a new closedenvelope.T populated with subscription ID and Reason.
34 func NewFrom(id, msg []byte) *T {
35 return &T{
36 Subscription: id, Reason: msg,
37 }
38 }
39 40 // Label returns the label of a closedenvelope.T.
41 func (en *T) Label() string { return L }
42 43 // ReasonString returns the Reason in the form of a string.
44 func (en *T) ReasonString() string { return string(en.Reason) }
45 46 // Write the closedenvelope.T to a provided io.Writer.
47 func (en *T) Write(w io.Writer) (err error) {
48 var b []byte
49 b = en.Marshal(b)
50 _, err = w.Write(b)
51 return
52 }
53 54 // Marshal a closedenvelope.T envelope in minified JSON, appending to a provided
55 // destination slice. Note that this ensures correct string escaping on the
56 // Reason field.
57 func (en *T) Marshal(dst []byte) (b []byte) {
58 b = dst
59 b = envelopes.Marshal(
60 b, L,
61 func(bst []byte) (o []byte) {
62 o = bst
63 o = append(o, '"')
64 o = append(o, en.Subscription...)
65 o = append(o, '"')
66 o = append(o, ',')
67 o = append(o, '"')
68 o = text.NostrEscape(o, en.Reason)
69 o = append(o, '"')
70 return
71 },
72 )
73 return
74 }
75 76 // Unmarshal a closedenvelope.T from minified JSON, returning the remainder after the end
77 // of the envelope. Note that this ensures the Reason string is correctly
78 // unescaped by NIP-01 escaping rules.
79 func (en *T) Unmarshal(b []byte) (r []byte, err error) {
80 r = b
81 if en.Subscription, r, err = text.UnmarshalQuoted(r); chk.E(err) {
82 return
83 }
84 if en.Reason, r, err = text.UnmarshalQuoted(r); chk.E(err) {
85 return
86 }
87 if r, err = envelopes.SkipToTheEnd(r); chk.E(err) {
88 return
89 }
90 return
91 }
92 93 // Parse reads a closedenvelope.T from minified JSON into a newly allocated closedenvelope.T.
94 func Parse(b []byte) (t *T, rem []byte, err error) {
95 t = New()
96 if rem, err = t.Unmarshal(b); chk.E(err) {
97 return
98 }
99 return
100 }
101