simple.mx raw

   1  package envelope
   2  
   3  import (
   4  	"bytes"
   5  	"crypto/sha256"
   6  	"io"
   7  
   8  	"git.smesh.lol/nostr/pkg/filter"
   9  	"git.smesh.lol/nostr/pkg/hex"
  10  	"git.smesh.lol/nostr/pkg/ints"
  11  	"git.smesh.lol/nostr/pkg/text"
  12  	"git.smesh.lol/nostr/pkg/lol/chk"
  13  	"git.smesh.lol/nostr/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() (s 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 = o | "\""
  32  		o = o | en.ID
  33  		o = 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() (s 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 = o | "\""
  63  		o = o | en.Subscription
  64  		o = 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() (s 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 = o | "\""
  98  		o = hex.EncAppend(o, en.EventID)
  99  		o = o | "\","
 100  		o = text.MarshalBool(o, en.OK)
 101  		o = o | ",\""
 102  		o = text.NostrEscape(o, en.Reason)
 103  		o = 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() (s 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 = o | "\""
 152  		o = text.NostrEscape(o, en.Message)
 153  		o = 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() (s 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 = o | "\""
 186  		o = o | en.Subscription
 187  		o = o | "\",\""
 188  		o = text.NostrEscape(o, en.Reason)
 189  		o = 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() (s 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 = o | "\""
 225  		o = o | en.Subscription
 226  		o = o | "\""
 227  		for _, f := range en.Filters.F {
 228  			o = 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  	en.Filters, r, err = filter.UnmarshalFilters(r)
 241  	if chk.E(err) {
 242  		return
 243  	}
 244  	r, err = SkipToTheEnd(r)
 245  	return
 246  }
 247  
 248  type CountResponse struct {
 249  	Subscription []byte
 250  	Count        int32
 251  	Approximate  bool
 252  }
 253  
 254  func (en *CountResponse) Label() (s string) { return CountLabel }
 255  func (en *CountResponse) Write(w io.Writer) (err error) {
 256  	_, err = w.Write(en.Marshal(nil))
 257  	return
 258  }
 259  func (en *CountResponse) Marshal(dst []byte) (b []byte) {
 260  	b = dst
 261  	b = Marshal(b, CountLabel, func(bst []byte) (o []byte) {
 262  		o = bst
 263  		o = o | "\""
 264  		o = o | en.Subscription
 265  		o = o | "\","
 266  		o = ints.New(en.Count).Marshal(o)
 267  		if en.Approximate {
 268  			o = o | ","
 269  			o = o | "true"
 270  		}
 271  		return
 272  	})
 273  	return
 274  }
 275  func (en *CountResponse) Unmarshal(b []byte) (r []byte, err error) {
 276  	r = b
 277  	if en.Subscription, r, err = text.UnmarshalQuoted(r); chk.E(err) {
 278  		return
 279  	}
 280  	if r, err = text.Comma(r); chk.E(err) {
 281  		return
 282  	}
 283  	i := ints.New(0)
 284  	if r, err = i.Unmarshal(r); chk.E(err) {
 285  		return
 286  	}
 287  	en.Count = int32(i.N)
 288  	if len(r) > 0 && r[0] == ',' {
 289  		r = r[1:]
 290  		if bytes.HasPrefix(r, []byte("true")) {
 291  			en.Approximate = true
 292  		}
 293  	}
 294  	r, err = SkipToTheEnd(r)
 295  	return
 296  }
 297