mirror of
https://github.com/mikedilger/gossip.git
synced 2024-09-29 16:31:18 +00:00
Filter basics
This commit is contained in:
parent
a3631cd33e
commit
fe891fe2af
85
src/filter.rs
Normal file
85
src/filter.rs
Normal file
@ -0,0 +1,85 @@
|
||||
use crate::globals::GLOBALS;
|
||||
use crate::people::Person;
|
||||
use crate::profile::Profile;
|
||||
use nostr_types::{Event, EventKind};
|
||||
use rhai::{AST, Engine, Scope};
|
||||
use std::fs;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum EventFilterAction {
|
||||
Deny,
|
||||
Allow,
|
||||
MuteAuthor,
|
||||
}
|
||||
|
||||
pub fn load_script(engine: &Engine) -> Option<AST> {
|
||||
let profile = match Profile::current() {
|
||||
Ok(profile) => profile,
|
||||
Err(e) => {
|
||||
tracing::error!("Profile failed: {}", e);
|
||||
return None;
|
||||
},
|
||||
};
|
||||
|
||||
let mut path = profile.profile_dir.clone();
|
||||
path.push("filter.rhai");
|
||||
|
||||
let script = match fs::read_to_string(&path) {
|
||||
Ok(script) => script,
|
||||
Err(e) => {
|
||||
tracing::info!("No spam filter: {}", e);
|
||||
return None;
|
||||
},
|
||||
};
|
||||
|
||||
let ast = match engine.compile(script) {
|
||||
Ok(ast) => ast,
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to compile spam filter: {}", e);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
tracing::info!("Spam filter loaded.");
|
||||
|
||||
Some(ast)
|
||||
}
|
||||
|
||||
pub fn filter(event: Event, author: Option<Person>) -> EventFilterAction {
|
||||
let ast = match &GLOBALS.filter {
|
||||
Some(ast) => ast,
|
||||
None => return EventFilterAction::Allow
|
||||
};
|
||||
|
||||
let mut scope = Scope::new();
|
||||
scope.push("id", event.id.as_hex_string());
|
||||
scope.push("pubkey", event.pubkey.as_hex_string());
|
||||
scope.push("kind", <EventKind as Into<u32>>::into(event.kind));
|
||||
// FIXME tags
|
||||
scope.push("content", event.content.clone());
|
||||
scope.push(
|
||||
"nip05valid",
|
||||
match author {
|
||||
Some(a) => a.nip05_valid,
|
||||
None => false,
|
||||
},
|
||||
);
|
||||
|
||||
match GLOBALS.filter_engine.call_fn::<i64>(&mut scope, &ast, "filter", ()) {
|
||||
Ok(action) => match action {
|
||||
0 => {
|
||||
tracing::info!("SPAM FILTER BLOCKING EVENT {}", event.id.as_hex_string());
|
||||
EventFilterAction::Deny
|
||||
}
|
||||
1 => EventFilterAction::Allow,
|
||||
2 => EventFilterAction::MuteAuthor,
|
||||
_ => EventFilterAction::Allow,
|
||||
},
|
||||
Err(ear) => {
|
||||
tracing::error!("{}", ear);
|
||||
EventFilterAction::Allow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only call the filter if the author isn't followed
|
@ -14,6 +14,7 @@ use gossip_relay_picker::RelayPicker;
|
||||
use nostr_types::{Event, Id, PayRequestData, Profile, PublicKey, RelayUrl, UncheckedUrl};
|
||||
use parking_lot::RwLock as PRwLock;
|
||||
use regex::Regex;
|
||||
use rhai::{AST, Engine};
|
||||
use std::collections::HashSet;
|
||||
use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize};
|
||||
use tokio::sync::{broadcast, mpsc, Mutex, RwLock};
|
||||
@ -115,6 +116,10 @@ pub struct Globals {
|
||||
|
||||
/// Events Processed
|
||||
pub events_processed: AtomicU32,
|
||||
|
||||
/// Filter
|
||||
pub filter_engine: Engine,
|
||||
pub filter: Option<AST>,
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
@ -131,6 +136,9 @@ lazy_static! {
|
||||
Err(e) => panic!("{e}")
|
||||
};
|
||||
|
||||
let filter_engine = Engine::new();
|
||||
let filter = crate::filter::load_script(&filter_engine);
|
||||
|
||||
Globals {
|
||||
first_run: AtomicBool::new(false),
|
||||
to_minions,
|
||||
@ -161,6 +169,8 @@ lazy_static! {
|
||||
hashtag_regex: Regex::new(r"(?:^|\W)(#[\w\p{Extended_Pictographic}]+)(?:$|\W)").unwrap(),
|
||||
storage,
|
||||
events_processed: AtomicU32::new(0),
|
||||
filter_engine,
|
||||
filter,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ mod delegation;
|
||||
mod error;
|
||||
mod feed;
|
||||
mod fetcher;
|
||||
mod filter;
|
||||
mod globals;
|
||||
mod media;
|
||||
mod nip05;
|
||||
|
@ -215,6 +215,7 @@ impl People {
|
||||
});
|
||||
}
|
||||
|
||||
// FIXME this is expensive
|
||||
pub fn get_followed_pubkeys(&self) -> Vec<PublicKey> {
|
||||
if let Ok(vec) = GLOBALS.storage.filter_people(|p| p.followed) {
|
||||
vec.iter().map(|p| p.pubkey).collect()
|
||||
@ -223,6 +224,11 @@ impl People {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME this is expensive
|
||||
pub fn is_followed(&self, pubkey: &PublicKey) -> bool {
|
||||
self.get_followed_pubkeys().contains(pubkey)
|
||||
}
|
||||
|
||||
pub fn get_followed_pubkeys_needing_relay_lists(
|
||||
&self,
|
||||
among_these: &[PublicKey],
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::comms::ToOverlordMessage;
|
||||
use crate::error::Error;
|
||||
use crate::filter::EventFilterAction;
|
||||
use crate::globals::GLOBALS;
|
||||
use crate::person_relay::PersonRelay;
|
||||
use nostr_types::{
|
||||
@ -27,6 +28,22 @@ pub async fn process_new_event(
|
||||
|
||||
GLOBALS.events_processed.fetch_add(1, Ordering::SeqCst);
|
||||
|
||||
// Spam filter (displayable and author is not followed)
|
||||
if event.kind.is_feed_displayable() && !GLOBALS.people.is_followed(&event.pubkey) {
|
||||
let author = GLOBALS.storage.read_person(&event.pubkey)?;
|
||||
match crate::filter::filter(event.clone(), author) {
|
||||
EventFilterAction::Allow => {}
|
||||
EventFilterAction::Deny => {
|
||||
tracing::info!("SPAM FILTER: Filtered out event {}", event.id.as_hex_string());
|
||||
return Ok(());
|
||||
},
|
||||
EventFilterAction::MuteAuthor => {
|
||||
GLOBALS.people.mute(&event.pubkey, true)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if we already had this event
|
||||
let duplicate = GLOBALS.storage.has_event(event.id)?;
|
||||
if duplicate {
|
||||
@ -144,14 +161,6 @@ pub async fn process_new_event(
|
||||
}
|
||||
}
|
||||
|
||||
// Save event relationships (whether from a relay or not)
|
||||
let invalid_ids = GLOBALS
|
||||
.storage
|
||||
.process_relationships_of_event(event, None)?;
|
||||
|
||||
// Invalidate UI events indicated by those relationships
|
||||
GLOBALS.ui_notes_to_invalidate.write().extend(&invalid_ids);
|
||||
|
||||
// Save event_hashtags
|
||||
if seen_on.is_some() {
|
||||
let hashtags = event.hashtags();
|
||||
@ -160,6 +169,14 @@ pub async fn process_new_event(
|
||||
}
|
||||
}
|
||||
|
||||
// Save event relationships (whether from a relay or not)
|
||||
let invalid_ids = GLOBALS
|
||||
.storage
|
||||
.process_relationships_of_event(event, None)?;
|
||||
|
||||
// Invalidate UI events indicated by those relationships
|
||||
GLOBALS.ui_notes_to_invalidate.write().extend(&invalid_ids);
|
||||
|
||||
// If metadata, update person
|
||||
if event.kind == EventKind::Metadata {
|
||||
let metadata: Metadata = serde_json::from_str(&event.content)?;
|
||||
|
Loading…
Reference in New Issue
Block a user