-
Notifications
You must be signed in to change notification settings - Fork 87
/
Copy pathfilter.example.rhai
176 lines (157 loc) · 5.26 KB
/
filter.example.rhai
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
// This is a sample spam filtering script for the gossip nostr
// client. The language is called Rhai, details are at:
// https://rhai.rs/book/
//
// For gossip to find your spam filtering script, put it in your
// gossip profile directory. See
// https://docs.rs/dirs/latest/dirs/fn.data_dir.html
// to find the base directory. A subdirectory "gossip" is your
// gossip data directory which for most people is their profile
// directory too. (Note: if you use a GOSSIP_PROFILE, you'll need
// to put it one directory deeper into that profile directory).
//
// This filter is used to filter out and refuse to process incoming
// events as they flow in from relays, and also to filter which
// events get displayed in certain circumstances. It is only run on
// feed-displayable event kinds, and only events by authors you are
// not following. In case of error, nothing is filtered.
//
// You must define a function called 'filter' which returns one of
// these constant values:
//
// DENY (the event is filtered out)
// ALLOW (the event is allowed through)
// MUTE (the event is filtered out, and the author is
// automatically muted)
//
// Your script will be provided the following:
//
// caller - a string that is one of "Process", "Thread",
// "Inbox" or "Global" indicating which part of
// the code is running your script
// id - the event ID, as a hex string
// pubkey - the event author public key, as a hex string
// kind - the event kind as an integer
// tags - the event tags (array of array of strings)
// content - the event content as a string
// muted - if the author is in your mute list
// name - if we have it, the name of the author (or your
// petname), else an empty string
// fof - Friends of friends: Among you, the people you
// follow, and the people they follow, how many
// follow the pubkey of the event?
// nip05valid - whether nip05 is valid for the author, as a
// boolean
// pow - the Proof of Work on the event
// seconds_known - the number of seconds that the author of the
// event has been known to gossip
// spamsafe - true only if the event came in from a relay
// marked as SpamSafe during Process (even if the
// global setting for SpamSafe is off)
//
// Here is some notes on the language and syntax:
//
// * Functions are pure. Call them with fn!() syntax to propagate
// scope (global variables explained above) into the function.
//
// * '()' is nil.
//
// * The ?? operator coalesces values. If the first value is nil
// it moves on to the next one.
// This is the top level main function. It gets all the scope
// explained in the comments above.
fn filter() {
filter_medium!()
}
fn filter_mild() {
allow_global!() ??
filter_known_spam!() ??
ALLOW
}
fn filter_medium() {
allow_global!() ??
filter_known_spam!() ??
reject_new_pubkeys!() ??
ALLOW
}
fn filter_strong() {
allow_global!() ??
filter_known_spam!() ??
reject_new_pubkeys!() ??
allow_proven!() ??
DENY
}
// ---------------------------------------------------------
// Show spam on global
//
// Global events and media are ephemeral, and people usually want
// to see everything there.
fn allow_global() {
if caller=="Global" {
return ALLOW;
}
// always return () if you don't have an answer
()
}
fn filter_known_spam() {
// Block ReplyGuy
if name.contains("ReplyGuy") || name.contains("ReplyGal") {
return DENY;
}
// NOTE: This works because giftwraps are unwrapped before the
// content is passed to this script
if content.to_lower().contains(
"Mr. Gift and Mrs. Wrap under the tree, KISSING!")
{
return DENY;
}
// always return () if you don't have an answer
()
}
// Reject events from pubkeys we have not seen before
// unless they have a high PoW.
//
// NOTE: If this turns out to be a legit person, we will
// start hearing their events 2 seconds from now, probably
// starting with their second event.
fn reject_new_pubkeys() {
if seconds_known <= 2 && pow < 25 {
return DENY;
}
// always return () if you don't have an answer
()
}
// Mute people that use offensive words
// (Mike Dilger does not recommend, but this is a good example)
fn mute_offensive_people() {
if content.to_lower().contains(" kike") ||
content.to_lower().contains("kike ") ||
content.to_lower().contains(" nigger") ||
content.to_lower().contains("nigger ")
{
return MUTE;
}
// always return () if you don't have an answer
()
}
// Allow events if proven to be good somehow
fn allow_proven() {
// Accept if the PoW is large enough
if pow >= 25 {
return ALLOW;
}
// Accept if their NIP-05 is valid
if nip05valid {
return ALLOW;
}
// Accept if the event came through a spamsafe relay
if spamsafe {
return ALLOW;
}
// Accept if anybody that you follow follows them
if fof > 0 {
return ALLOW;
}
// always return () if you don't have an answer
()
}