simple.mx raw
1 package envelope
2
3 import (
4 "bytes"
5 "crypto/sha256"
6 "io"
7
8 "smesh.lol/pkg/nostr/filter"
9 "smesh.lol/pkg/nostr/hex"
10 "smesh.lol/pkg/nostr/ints"
11 "smesh.lol/pkg/nostr/text"
12 "smesh.lol/pkg/lol/chk"
13 "smesh.lol/pkg/lol/errorf"
14 )
15
16 // Close — client request to terminate a subscription.
17
18 const CloseLabel = "CLOSE"
19
20 type Close struct{ ID []byte }
21
22 func (en *Close) Label() string { return CloseLabel }
23 func (en *Close) Write(w io.Writer) (err error) {
24 _, err = w.Write(en.Marshal(nil))
25 return
26 }
27 func (en *Close) Marshal(dst []byte) (b []byte) {
28 b = dst
29 b = Marshal(b, CloseLabel, func(bst []byte) (o []byte) {
30 o = bst
31 o = append(o, '"')
32 o = append(o, en.ID...)
33 o = append(o, '"')
34 return
35 })
36 return
37 }
38 func (en *Close) Unmarshal(b []byte) (r []byte, err error) {
39 r = b
40 if en.ID, r, err = text.UnmarshalQuoted(r); chk.E(err) {
41 return
42 }
43 r, err = SkipToTheEnd(r)
44 return
45 }
46
47 // EOSE — End Of Stored Events signal.
48
49 const EOSELabel = "EOSE"
50
51 type EOSE struct{ Subscription []byte }
52
53 func (en *EOSE) Label() string { return EOSELabel }
54 func (en *EOSE) Write(w io.Writer) (err error) {
55 _, err = w.Write(en.Marshal(nil))
56 return
57 }
58 func (en *EOSE) Marshal(dst []byte) (b []byte) {
59 b = dst
60 b = Marshal(b, EOSELabel, func(bst []byte) (o []byte) {
61 o = bst
62 o = append(o, '"')
63 o = append(o, en.Subscription...)
64 o = append(o, '"')
65 return
66 })
67 return
68 }
69 func (en *EOSE) Unmarshal(b []byte) (r []byte, err error) {
70 r = b
71 if en.Subscription, r, err = text.UnmarshalQuoted(r); chk.E(err) {
72 return
73 }
74 r, err = SkipToTheEnd(r)
75 return
76 }
77
78 // OK — acknowledgement for EVENT submission.
79
80 const OKLabel = "OK"
81
82 type OK struct {
83 EventID []byte
84 OK bool
85 Reason []byte
86 }
87
88 func (en *OK) Label() string { return OKLabel }
89 func (en *OK) Write(w io.Writer) (err error) {
90 _, err = w.Write(en.Marshal(nil))
91 return
92 }
93 func (en *OK) Marshal(dst []byte) (b []byte) {
94 b = dst
95 b = Marshal(b, OKLabel, func(bst []byte) (o []byte) {
96 o = bst
97 o = append(o, '"')
98 o = hex.EncAppend(o, en.EventID)
99 o = append(o, '"', ',')
100 o = text.MarshalBool(o, en.OK)
101 o = append(o, ',', '"')
102 o = text.NostrEscape(o, en.Reason)
103 o = append(o, '"')
104 return
105 })
106 return
107 }
108 func (en *OK) Unmarshal(b []byte) (r []byte, err error) {
109 r = b
110 var idBytes []byte
111 if idBytes, r, err = text.UnmarshalHex(r); err != nil {
112 return
113 }
114 if len(idBytes) != sha256.Size {
115 err = errorf.E([]byte("invalid size for event id, require %d got %d"),
116 sha256.Size, len(idBytes))
117 return
118 }
119 en.EventID = idBytes
120 if r, err = text.Comma(r); chk.E(err) {
121 return
122 }
123 if r, en.OK, err = text.UnmarshalBool(r); chk.E(err) {
124 return
125 }
126 if r, err = text.Comma(r); chk.E(err) {
127 return
128 }
129 if en.Reason, r, err = text.UnmarshalQuoted(r); chk.E(err) {
130 return
131 }
132 r, err = SkipToTheEnd(r)
133 return
134 }
135
136 // Notice — relay message to user.
137
138 const NoticeLabel = "NOTICE"
139
140 type Notice struct{ Message []byte }
141
142 func (en *Notice) Label() string { return NoticeLabel }
143 func (en *Notice) Write(w io.Writer) (err error) {
144 _, err = w.Write(en.Marshal(nil))
145 return
146 }
147 func (en *Notice) Marshal(dst []byte) (b []byte) {
148 b = dst
149 b = Marshal(b, NoticeLabel, func(bst []byte) (o []byte) {
150 o = bst
151 o = append(o, '"')
152 o = text.NostrEscape(o, en.Message)
153 o = append(o, '"')
154 return
155 })
156 return
157 }
158 func (en *Notice) Unmarshal(b []byte) (r []byte, err error) {
159 r = b
160 if en.Message, r, err = text.UnmarshalQuoted(r); chk.E(err) {
161 return
162 }
163 r, err = SkipToTheEnd(r)
164 return
165 }
166
167 // Closed — relay-side subscription termination.
168
169 const ClosedLabel = "CLOSED"
170
171 type Closed struct {
172 Subscription []byte
173 Reason []byte
174 }
175
176 func (en *Closed) Label() string { return ClosedLabel }
177 func (en *Closed) Write(w io.Writer) (err error) {
178 _, err = w.Write(en.Marshal(nil))
179 return
180 }
181 func (en *Closed) Marshal(dst []byte) (b []byte) {
182 b = dst
183 b = Marshal(b, ClosedLabel, func(bst []byte) (o []byte) {
184 o = bst
185 o = append(o, '"')
186 o = append(o, en.Subscription...)
187 o = append(o, '"', ',', '"')
188 o = text.NostrEscape(o, en.Reason)
189 o = append(o, '"')
190 return
191 })
192 return
193 }
194 func (en *Closed) Unmarshal(b []byte) (r []byte, err error) {
195 r = b
196 if en.Subscription, r, err = text.UnmarshalQuoted(r); chk.E(err) {
197 return
198 }
199 if en.Reason, r, err = text.UnmarshalQuoted(r); chk.E(err) {
200 return
201 }
202 r, err = SkipToTheEnd(r)
203 return
204 }
205
206 // Count — COUNT request/response.
207
208 const CountLabel = "COUNT"
209
210 type CountRequest struct {
211 Subscription []byte
212 Filters filter.S
213 }
214
215 func (en *CountRequest) Label() string { return CountLabel }
216 func (en *CountRequest) Write(w io.Writer) (err error) {
217 _, err = w.Write(en.Marshal(nil))
218 return
219 }
220 func (en *CountRequest) Marshal(dst []byte) (b []byte) {
221 b = dst
222 b = Marshal(b, CountLabel, func(bst []byte) (o []byte) {
223 o = bst
224 o = append(o, '"')
225 o = append(o, en.Subscription...)
226 o = append(o, '"')
227 for _, f := range en.Filters {
228 o = append(o, ',')
229 o = f.Marshal(o)
230 }
231 return
232 })
233 return
234 }
235 func (en *CountRequest) Unmarshal(b []byte) (r []byte, err error) {
236 r = b
237 if en.Subscription, r, err = text.UnmarshalQuoted(r); chk.E(err) {
238 return
239 }
240 if r, err = en.Filters.Unmarshal(r); chk.E(err) {
241 return
242 }
243 r, err = SkipToTheEnd(r)
244 return
245 }
246
247 type CountResponse struct {
248 Subscription []byte
249 Count int
250 Approximate bool
251 }
252
253 func (en *CountResponse) Label() string { return CountLabel }
254 func (en *CountResponse) Write(w io.Writer) (err error) {
255 _, err = w.Write(en.Marshal(nil))
256 return
257 }
258 func (en *CountResponse) Marshal(dst []byte) (b []byte) {
259 b = dst
260 b = Marshal(b, CountLabel, func(bst []byte) (o []byte) {
261 o = bst
262 o = append(o, '"')
263 o = append(o, en.Subscription...)
264 o = append(o, '"', ',')
265 o = ints.New(en.Count).Marshal(o)
266 if en.Approximate {
267 o = append(o, ',')
268 o = append(o, "true"...)
269 }
270 return
271 })
272 return
273 }
274 func (en *CountResponse) Unmarshal(b []byte) (r []byte, err error) {
275 r = b
276 if en.Subscription, r, err = text.UnmarshalQuoted(r); chk.E(err) {
277 return
278 }
279 if r, err = text.Comma(r); chk.E(err) {
280 return
281 }
282 i := ints.New(0)
283 if r, err = i.Unmarshal(r); chk.E(err) {
284 return
285 }
286 en.Count = int(i.N)
287 if len(r) > 0 && r[0] == ',' {
288 r = r[1:]
289 if bytes.HasPrefix(r, []byte("true")) {
290 en.Approximate = true
291 }
292 }
293 r, err = SkipToTheEnd(r)
294 return
295 }
296