filter.mx raw

   1  package nostr
   2  
   3  // Filter is a Nostr subscription filter (NIP-01).
   4  type Filter struct {
   5  	IDs     []string
   6  	Authors []string
   7  	Kinds   []int
   8  	Tags    map[string][]string // "#e" -> ["abc"], "#p" -> ["def"]
   9  	Since   int64
  10  	Until   int64
  11  	Limit   int
  12  	Proxy   []string // _proxy extension: relay URLs to fetch from via orly relay
  13  }
  14  
  15  // Matches checks if an event passes the filter.
  16  func (f *Filter) Matches(e *Event) bool {
  17  	if len(f.IDs) > 0 && !containsStr(f.IDs, e.ID) {
  18  		return false
  19  	}
  20  	if len(f.Authors) > 0 && !containsStr(f.Authors, e.PubKey) {
  21  		return false
  22  	}
  23  	if len(f.Kinds) > 0 && !containsInt(f.Kinds, e.Kind) {
  24  		return false
  25  	}
  26  	if f.Since > 0 && e.CreatedAt < f.Since {
  27  		return false
  28  	}
  29  	if f.Until > 0 && e.CreatedAt > f.Until {
  30  		return false
  31  	}
  32  	if f.Tags != nil {
  33  		for key, values := range f.Tags {
  34  			if len(key) < 2 || key[0] != '#' {
  35  				continue
  36  			}
  37  			tagKey := string(key[1:])
  38  			if !eventHasTagValue(e, tagKey, values) {
  39  				return false
  40  			}
  41  		}
  42  	}
  43  	return true
  44  }
  45  
  46  // Serialize returns the filter as a JSON object string for REQ messages.
  47  func (f *Filter) Serialize() string {
  48  	buf := []byte{:0:128}
  49  	buf = append(buf, '{')
  50  	first := true
  51  
  52  	if len(f.IDs) > 0 {
  53  		buf = appendField(buf, &first)
  54  		buf = append(buf, "\"ids\":"...)
  55  		buf = appendStrArray(buf, f.IDs)
  56  	}
  57  	if len(f.Authors) > 0 {
  58  		buf = appendField(buf, &first)
  59  		buf = append(buf, "\"authors\":"...)
  60  		buf = appendStrArray(buf, f.Authors)
  61  	}
  62  	if len(f.Kinds) > 0 {
  63  		buf = appendField(buf, &first)
  64  		buf = append(buf, "\"kinds\":["...)
  65  		for i, k := range f.Kinds {
  66  			if i > 0 {
  67  				buf = append(buf, ',')
  68  			}
  69  			buf = append(buf, intToStr(k)...)
  70  		}
  71  		buf = append(buf, ']')
  72  	}
  73  	if f.Tags != nil {
  74  		for key, values := range f.Tags {
  75  			buf = appendField(buf, &first)
  76  			buf = append(buf, '"')
  77  			buf = append(buf, key...)
  78  			buf = append(buf, "\":"...)
  79  			buf = appendStrArray(buf, values)
  80  		}
  81  	}
  82  	if f.Since > 0 {
  83  		buf = appendField(buf, &first)
  84  		buf = append(buf, "\"since\":"...)
  85  		buf = append(buf, i64ToStr(f.Since)...)
  86  	}
  87  	if f.Until > 0 {
  88  		buf = appendField(buf, &first)
  89  		buf = append(buf, "\"until\":"...)
  90  		buf = append(buf, i64ToStr(f.Until)...)
  91  	}
  92  	if f.Limit > 0 {
  93  		buf = appendField(buf, &first)
  94  		buf = append(buf, "\"limit\":"...)
  95  		buf = append(buf, intToStr(f.Limit)...)
  96  	}
  97  	if len(f.Proxy) > 0 {
  98  		buf = appendField(buf, &first)
  99  		buf = append(buf, "\"_proxy\":"...)
 100  		buf = appendStrArray(buf, f.Proxy)
 101  	}
 102  
 103  	buf = append(buf, '}')
 104  	return string(buf)
 105  }
 106  
 107  func appendField(buf []byte, first *bool) []byte {
 108  	if !*first {
 109  		buf = append(buf, ',')
 110  	}
 111  	*first = false
 112  	return buf
 113  }
 114  
 115  func appendStrArray(buf []byte, ss []string) []byte {
 116  	buf = append(buf, '[')
 117  	for i, s := range ss {
 118  		if i > 0 {
 119  			buf = append(buf, ',')
 120  		}
 121  		buf = append(buf, '"')
 122  		buf = append(buf, s...)
 123  		buf = append(buf, '"')
 124  	}
 125  	buf = append(buf, ']')
 126  	return buf
 127  }
 128  
 129  func i64ToStr(n int64) string {
 130  	if n == 0 {
 131  		return "0"
 132  	}
 133  	neg := false
 134  	if n < 0 {
 135  		neg = true
 136  		n = -n
 137  	}
 138  	var b [20]byte
 139  	i := len(b)
 140  	for n > 0 {
 141  		i--
 142  		b[i] = byte('0' + n%10)
 143  		n /= 10
 144  	}
 145  	if neg {
 146  		i--
 147  		b[i] = '-'
 148  	}
 149  	return string(b[i:])
 150  }
 151  
 152  func intToStr(n int) string {
 153  	if n == 0 {
 154  		return "0"
 155  	}
 156  	neg := false
 157  	if n < 0 {
 158  		neg = true
 159  		n = -n
 160  	}
 161  	var b [20]byte
 162  	i := len(b)
 163  	for n > 0 {
 164  		i--
 165  		b[i] = byte('0' + n%10)
 166  		n /= 10
 167  	}
 168  	if neg {
 169  		i--
 170  		b[i] = '-'
 171  	}
 172  	return string(b[i:])
 173  }
 174  
 175  func containsStr(ss []string, s string) bool {
 176  	for _, v := range ss {
 177  		if v == s {
 178  			return true
 179  		}
 180  	}
 181  	return false
 182  }
 183  
 184  func containsInt(ns []int, n int) bool {
 185  	for _, v := range ns {
 186  		if v == n {
 187  			return true
 188  		}
 189  	}
 190  	return false
 191  }
 192  
 193  func eventHasTagValue(e *Event, tagKey string, values []string) bool {
 194  	for _, t := range e.Tags {
 195  		if len(t) > 1 && t[0] == tagKey {
 196  			for _, v := range values {
 197  				if t[1] == v {
 198  					return true
 199  				}
 200  			}
 201  		}
 202  	}
 203  	return false
 204  }
 205