-
Notifications
You must be signed in to change notification settings - Fork 0
/
filter_event.go
201 lines (180 loc) · 6.88 KB
/
filter_event.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
package sifters
import (
"fmt"
"time"
"github.com/jiftechnify/strfrui"
"github.com/jiftechnify/strfrui/sifters/internal/utils"
"github.com/nbd-wtf/go-nostr"
)
// MatchesFilters makes an event-sifter that matches a Nostr event against the given Nostr filters.
func MatchesFilters(filters []nostr.Filter, mode Mode) *SifterUnit {
matchInput := func(input *strfrui.Input) (inputMatchResult, error) {
return matchResultFromBool(nostr.Filters(filters).Match(input.Event), nil)
}
defaultRejFn := rejectWithMsgPerMode(
mode,
"blocked: event must match filters to be accepted",
"blocked: event is denied by filters",
)
return newSifterUnit(matchInput, mode, defaultRejFn)
}
// AuthorMatcher makes an event-sifter that matches the author (pubkey) of a Nostr event with the given matcher function.
//
// If the matcher returns non-nil error, this sifter always rejects the input.
func AuthorMatcher(matcher func(string) (bool, error), mode Mode) *SifterUnit {
matchInput := func(input *strfrui.Input) (inputMatchResult, error) {
return matchResultFromBool(matcher(input.Event.PubKey))
}
defaultRejFn := rejectWithMsgPerMode(
mode,
"blocked: event author is not in the whitelist",
"blocked: event author is in the blacklist",
)
return newSifterUnit(matchInput, mode, defaultRejFn)
}
// AuthorList makes an event-sifter that checks if the author (pubkey) of a Nostr event is in the given list.
func AuthorList(authors []string, mode Mode) *SifterUnit {
authorSet := utils.SliceToSet(authors)
matchInput := func(input *strfrui.Input) (inputMatchResult, error) {
_, ok := authorSet[input.Event.PubKey]
return matchResultFromBool(ok, nil)
}
defaultRejFn := rejectWithMsgPerMode(
mode,
"blocked: event author is not in the whitelist",
"blocked: event author is in the blacklist",
)
return newSifterUnit(matchInput, mode, defaultRejFn)
}
var (
// Non-Parametarized Replaceable events: kind 0, 3, 41 and 10000 <= kind < 20000
KindsAllNonParamReplaceable = func(k int) bool {
return k == 0 || k == 3 || k == 41 || (10000 <= k && k < 20000)
}
// Parameterized replaceable events: kind 30000 <= kind < 40000
KindsAllParamReplaceable = func(k int) bool {
return 30000 <= k && k < 40000
}
// General replaceable events (including both parametarized and non-parameterized)
KindsAllReplaceable = func(k int) bool {
return KindsAllNonParamReplaceable(k) || KindsAllParamReplaceable(k)
}
// Ephemeral events: kind 20000 <= kind < 30000
KindsAllEphemeral = func(k int) bool {
return 20000 <= k && k < 30000
}
// Regular events
KindsAllRegular = func(k int) bool {
return !(KindsAllReplaceable(k) || KindsAllEphemeral(k))
}
)
// KindMatcherFallible makes an event-sifter that matches the kind of a Nostr event with the given matcher function that is fallible (i.e. can return error).
//
// If the matcher returns non-nil error, this sifter always rejects the input.
func KindMatcherFallible(matcher func(int) (bool, error), mode Mode) *SifterUnit {
matchInput := func(input *strfrui.Input) (inputMatchResult, error) {
return matchResultFromBool(matcher(input.Event.Kind))
}
defaultRejFn := rejectWithMsgPerMode(
mode,
"blocked: the kind of the event is not in the whitelist",
"blocked: the kind of the event is in the blacklist",
)
return newSifterUnit(matchInput, mode, defaultRejFn)
}
// KindMatcher makes an event-sifter that matches the kind of a Nostr event with the given matcher function.
//
// Note that the matcher function can't return any error unlike other XxxMatcher sifters.
// To use a fallible matcher, you may want to [KindMatcherFallible] instead.
func KindMatcher(matcher func(int) bool, mode Mode) *SifterUnit {
matcherf := func(k int) (bool, error) {
return matcher(k), nil
}
return KindMatcherFallible(matcherf, mode)
}
// KindList makes an event-sifter that checks if the kind of a Nostr event is in the given list.
func KindList(kinds []int, mode Mode) *SifterUnit {
kindSet := utils.SliceToSet(kinds)
matchInput := func(input *strfrui.Input) (inputMatchResult, error) {
_, ok := kindSet[input.Event.Kind]
return matchResultFromBool(ok, nil)
}
defaultRejFn := rejectWithMsgPerMode(
mode,
"blocked: the kind of the event is not in the whitelist",
"blocked: the kind of the event is in the blacklist",
)
return newSifterUnit(matchInput, mode, defaultRejFn)
}
// TagsMatcher makes an event-sifter that matches the tag list of a Nostr event with the given matcher function.
// You can utilize various matching methods on [github.com/nbd-wtf/go-nostr.Tags] in the matcher.
//
// If the matcher returns non-nil error, this sifter always rejects the input.
func TagsMatcher(matcher func(nostr.Tags) (bool, error), mode Mode) *SifterUnit {
matchInput := func(input *strfrui.Input) (inputMatchResult, error) {
return matchResultFromBool(matcher(input.Event.Tags))
}
defaultRejFn := rejectWithMsgPerMode(
mode,
"blocked: event tags don't match required patterns",
"blocked: event tags match forbidden patterns",
)
return newSifterUnit(matchInput, mode, defaultRejFn)
}
type fakeableClock struct {
fakeNow time.Time
}
var (
clock fakeableClock
)
func (c fakeableClock) now() time.Time {
if c.fakeNow.IsZero() {
return time.Now()
}
return c.fakeNow
}
func (c *fakeableClock) setFake(t time.Time) {
c.fakeNow = t
}
func (c *fakeableClock) reset() {
c.fakeNow = time.Time{}
}
// RelativeTimeRange represents a time range defined as a pair of maximum allowed duration in the past and future.
//
// Either of the durations can be zero (or left unspecified), means the corresponding side of the range is unbounded.
type RelativeTimeRange struct {
MaxPastDelta time.Duration
MaxFutureDelta time.Duration
}
// Contains checks if the given time is in the time range.
func (r RelativeTimeRange) Contains(t time.Time) bool {
now := clock.now()
okPast := r.MaxPastDelta == 0 || !t.Before(now.Add(-r.MaxPastDelta))
okFuture := r.MaxFutureDelta == 0 || !t.After(now.Add(r.MaxFutureDelta))
return okPast && okFuture
}
// String returns a string representation of the time range.
func (r RelativeTimeRange) String() string {
left := "-∞"
if r.MaxPastDelta != 0 {
left = r.MaxFutureDelta.String() + " ago"
}
right := "+∞"
if r.MaxFutureDelta != 0 {
right = r.MaxFutureDelta.String() + " after"
}
return fmt.Sprintf("[%s, %s]", left, right)
}
// CreatedAtRange makes an event-sifter that checks if the creation timestamp (created_at) of a Nostr event is in the given time range.
func CreatedAtRange(timeRange RelativeTimeRange, mode Mode) *SifterUnit {
matchInput := func(input *strfrui.Input) (inputMatchResult, error) {
createdAt := input.Event.CreatedAt.Time()
return matchResultFromBool(timeRange.Contains(createdAt), nil)
}
defaultRejFn := rejectWithMsgPerMode(
mode,
fmt.Sprintf("invalid: event timestamp is out of the range: %v", timeRange),
fmt.Sprintf("blocked: event timestamp must be out of the range: %v", timeRange),
)
return newSifterUnit(matchInput, mode, defaultRejFn)
}