Skip to content

Commit cae0393

Browse files
committed
filter: add content filter
Signed-off-by: William Casarin <[email protected]>
1 parent 8e1bb03 commit cae0393

File tree

5 files changed

+77
-20
lines changed

5 files changed

+77
-20
lines changed

noteguard.toml

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11

2-
pipeline = ["protected_events", "kinds", "whitelist", "ratelimit"]
2+
pipeline = ["protected_events", "kinds", "content", "whitelist", "ratelimit"]
33

44
[filters.ratelimit]
55
posts_per_minute = 8
66
whitelist = ["127.0.0.1"]
77

8+
[filters.content]
9+
filters = ["https://cdn.nostr.build/i/some-spammy-or-abusive-image-image.png"]
10+
811
[filters.whitelist]
912
pubkeys = ["16c21558762108afc34e4ff19e4ed51d9a48f79e0c34531efc423d21ab435e93"]
1013
ips = ["127.0.0.1"]

src/filters/content.rs

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
use crate::{Action, InputMessage, NoteFilter, OutputMessage};
2+
use serde::Deserialize;
3+
4+
#[derive(Deserialize, Default)]
5+
pub struct Content {
6+
filters: Vec<String>,
7+
}
8+
9+
impl NoteFilter for Content {
10+
fn filter_note(&mut self, msg: &InputMessage) -> OutputMessage {
11+
for filter in &self.filters {
12+
if msg.event.content.contains(filter) {
13+
return OutputMessage::new(msg.event.id.clone(), Action::ShadowReject, None);
14+
}
15+
}
16+
17+
OutputMessage::new(msg.event.id.clone(), Action::Accept, None)
18+
}
19+
20+
fn name(&self) -> &'static str {
21+
"content"
22+
}
23+
}

src/filters/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
mod content;
12
mod kinds;
23
mod protected_events;
34
mod ratelimit;
@@ -6,6 +7,7 @@ mod whitelist;
67
#[cfg(feature = "forwarder")]
78
mod forwarder;
89

10+
pub use content::Content;
911
pub use kinds::Kinds;
1012
pub use protected_events::ProtectedEvents;
1113
pub use ratelimit::RateLimit;

src/main.rs

+47-19
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
use noteguard::filters::{Kinds, ProtectedEvents, RateLimit, Whitelist};
1+
use noteguard::filters::{Content, Kinds, ProtectedEvents, RateLimit, Whitelist};
22

33
#[cfg(feature = "forwarder")]
44
use noteguard::filters::Forwarder;
55

6+
use log::info;
67
use noteguard::{Action, InputMessage, NoteFilter, OutputMessage};
78
use serde::de::DeserializeOwned;
89
use serde::Deserialize;
910
use std::collections::HashMap;
1011
use std::io::{self, Read};
11-
use log::info;
1212

1313
#[derive(Deserialize)]
1414
struct Config {
@@ -49,6 +49,7 @@ impl Noteguard {
4949
self.register_filter::<Whitelist>();
5050
self.register_filter::<ProtectedEvents>();
5151
self.register_filter::<Kinds>();
52+
self.register_filter::<Content>();
5253

5354
#[cfg(feature = "forwarder")]
5455
self.register_filter::<Forwarder>();
@@ -117,7 +118,6 @@ fn serialize_output_message(msg: &OutputMessage) -> String {
117118
serde_json::to_string(msg).expect("OutputMessage should always serialize correctly")
118119
}
119120

120-
121121
fn noteguard() {
122122
env_logger::init();
123123
info!("running noteguard");
@@ -157,7 +157,11 @@ fn noteguard() {
157157
};
158158

159159
if input_message.message_type != "new" {
160-
let out = OutputMessage::new(input_message.event.id.clone(), Action::Reject, Some("invalid strfry write policy input".to_string()));
160+
let out = OutputMessage::new(
161+
input_message.event.id.clone(),
162+
Action::Reject,
163+
Some("invalid strfry write policy input".to_string()),
164+
);
161165
println!("{}", serialize_output_message(&out));
162166
continue;
163167
}
@@ -169,7 +173,6 @@ fn noteguard() {
169173
}
170174
}
171175

172-
173176
#[cfg(test)]
174177
mod tests {
175178
use super::*;
@@ -197,7 +200,11 @@ mod tests {
197200
}
198201

199202
// Helper function to create a mock OutputMessage
200-
fn create_mock_output_message(event_id: &str, action: Action, msg: Option<&str>) -> OutputMessage {
203+
fn create_mock_output_message(
204+
event_id: &str,
205+
action: Action,
206+
msg: Option<&str>,
207+
) -> OutputMessage {
201208
OutputMessage {
202209
id: event_id.to_string(),
203210
action,
@@ -210,7 +217,9 @@ mod tests {
210217
let noteguard = Noteguard::new();
211218
assert!(noteguard.registered_filters.contains_key("ratelimit"));
212219
assert!(noteguard.registered_filters.contains_key("whitelist"));
213-
assert!(noteguard.registered_filters.contains_key("protected_events"));
220+
assert!(noteguard
221+
.registered_filters
222+
.contains_key("protected_events"));
214223
assert!(noteguard.registered_filters.contains_key("kinds"));
215224
}
216225

@@ -219,12 +228,15 @@ mod tests {
219228
let mut noteguard = Noteguard::new();
220229

221230
// Create a mock config with one filter (RateLimit)
222-
let config: Config = toml::from_str(r#"
231+
let config: Config = toml::from_str(
232+
r#"
223233
pipeline = ["ratelimit"]
224234
225235
[filters.ratelimit]
226236
posts_per_minute = 3
227-
"#).expect("Failed to parse config");
237+
"#,
238+
)
239+
.expect("Failed to parse config");
228240

229241
assert!(noteguard.load_config(&config).is_ok());
230242
assert_eq!(noteguard.loaded_filters.len(), 1);
@@ -235,14 +247,19 @@ mod tests {
235247
let mut noteguard = Noteguard::new();
236248

237249
// Create a mock config with one filter (RateLimit)
238-
let config: Config = toml::from_str(r#"
250+
let config: Config = toml::from_str(
251+
r#"
239252
pipeline = ["ratelimit"]
240253
241254
[filters.ratelimit]
242255
posts_per_minute = 3
243-
"#).expect("Failed to parse config");
256+
"#,
257+
)
258+
.expect("Failed to parse config");
244259

245-
noteguard.load_config(&config).expect("Failed to load config");
260+
noteguard
261+
.load_config(&config)
262+
.expect("Failed to load config");
246263

247264
let input_message = create_mock_input_message("test_event_1", "new");
248265
let output_message = noteguard.run(input_message);
@@ -255,13 +272,18 @@ mod tests {
255272
let mut noteguard = Noteguard::new();
256273

257274
// Create a mock config with one filter (ProtectedEvents) which will shadow reject the input
258-
let config: Config = toml::from_str(r#"
275+
let config: Config = toml::from_str(
276+
r#"
259277
pipeline = ["protected_events"]
260278
261279
[filters.protected_events]
262-
"#).expect("Failed to parse config");
280+
"#,
281+
)
282+
.expect("Failed to parse config");
263283

264-
noteguard.load_config(&config).expect("Failed to load config");
284+
noteguard
285+
.load_config(&config)
286+
.expect("Failed to load config");
265287

266288
let input_message = create_mock_input_message("test_event_3", "new");
267289
let output_message = noteguard.run(input_message);
@@ -274,13 +296,18 @@ mod tests {
274296
let mut noteguard = Noteguard::new();
275297

276298
// Create a mock config with one filter (Whitelist) which will reject the input
277-
let config: Config = toml::from_str(r#"
299+
let config: Config = toml::from_str(
300+
r#"
278301
pipeline = ["whitelist"]
279302
[filters.whitelist]
280303
pubkeys = ["something"]
281-
"#).expect("Failed to parse config");
304+
"#,
305+
)
306+
.expect("Failed to parse config");
282307

283-
noteguard.load_config(&config).expect("Failed to load config");
308+
noteguard
309+
.load_config(&config)
310+
.expect("Failed to load config");
284311

285312
let input_message = create_mock_input_message("test_event_2", "new");
286313
let output_message = noteguard.run(input_message);
@@ -308,7 +335,8 @@ mod tests {
308335
}
309336
"#;
310337

311-
let input_message: InputMessage = serde_json::from_str(input_json).expect("Failed to deserialize input message");
338+
let input_message: InputMessage =
339+
serde_json::from_str(input_json).expect("Failed to deserialize input message");
312340
assert_eq!(input_message.event.id, "test_event_5");
313341
assert_eq!(input_message.message_type, "new");
314342
}

test/inputs

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{"type": "new","receivedAt":12345,"sourceType":"IP4","sourceInfo": "127.0.0.3","event":{"id":"eabb9491951fc64d85b6cba28a7d53c333c3f1d59779d63abccf9a8f61a0bdef","pubkey":"5f54041530509de28550475bfe73db709609a4ee1e59281527ba81692923418f","created_at":1722305142,"kind":1,"tags":[["t","lightning"],["t","zapping"],["t","introductions"],["t","freefrom"],["t","help"],["t","nostr"],["t","plebchain"],["t","zapathon"],["t","damus"],["t","bitcoin"],["t","anime"],["t","grownostr"],["t","music"],["t","programming"],["t","cat"],["t","dog"],["t","nsfw"],["t","asknostr"],["t","foodstr"],["t","sports"],["t","acg"],["t","game"],["t","movie"],["t","book"],["t","art"],["t","tech"],["t","science"]],"content":"Freefrom is running out of funds and is about to close down. Please scan the QR code to donate.\n Freefrom 资金不足即将倒闭,请扫描二维码捐款\n https://ice.frostsky.com/2024/07/29/9e447d397cac0c27993f0cbcb6efb235.png\n https://cdn.nostr.build/i/270de92fa4e5118a28352720b7f26c360aa2dbfdb1c12517f3e3117a2f9de47a.png\n ","sig":"6fa13bedffb2250e2437af6898ab3b11444eb3aef5fd4b484b25658632d283774186f98a024c225581cd9bbaccb0966fa1360d13fd5671f97c7f7bf515f94166"}}
12
{"type": "new","receivedAt":12345,"sourceType":"IP4","sourceInfo": "127.0.0.3","event":{"id": "70651d96a2b6b3431cc06b7543249ccd22ab5c203c6aa590b7688f916f252f8f","pubkey": "879d67486027539073d6531d271e3791b15c3e48becbfe4c3727e93355330cc8","created_at": 1720545068,"kind": 1,"tags": [["-"]],"content": "hello there","sig": "21a901e3663bac846493df588ad2185751a5a2826a64c26afb9edce8f9d9344cf00c1ea43016e7faca69da661eadd2731b457a0c31b207ab6ed509a047bf7845"}}
23
{"type": "new","receivedAt":12345,"sourceType":"IP4","sourceInfo": "127.0.0.3","event":{"id": "68421a122cef086512b2c5bd29ca6285ced8bd8e302e347e3c5d90466c860a76","pubkey": "16c21558762108afc34e4ff19e4ed51d9a48f79e0c34531efc423d21ab435e93","created_at": 1720408658,"kind": 1,"tags": [],"content": "hi","sig": "7b76471744ded2b720ca832cdc89e670f6093ce38aeef55a5c6a4e077883d7d80dda1e9051032fb1faa1c3c212c517e93ee42b3ceac8e8e9b04bad46a361de90"}}
34
{"type": "new","receivedAt":12345,"sourceType":"IP4","sourceInfo": "127.0.0.3","event":{"id": "68421a122cef086512b2c5bd29ca6285ced8bd8e302e347e3c5d90466c860a76","pubkey": "16c21558762108afc34e4ff19e4ed51d9a48f79e0c34531efc423d21ab435e93","created_at": 1720408658,"kind": 1,"tags": [],"content": "hi","sig": "7b76471744ded2b720ca832cdc89e670f6093ce38aeef55a5c6a4e077883d7d80dda1e9051032fb1faa1c3c212c517e93ee42b3ceac8e8e9b04bad46a361de90"}}

0 commit comments

Comments
 (0)