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