diff --git a/README.md b/README.md index 2640876..070cb52 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ NIPs with a relay-specific implementation are listed here. - [x] NIP-02: Hide old contact list events - [ ] NIP-03: OpenTimestamps - [x] NIP-05: Mapping Nostr keys to DNS identifiers -- [ ] NIP-09: Event deletion +- [x] NIP-09: Event deletion - [x] NIP-11: Relay information document - [x] NIP-12: Generic tag search (_experimental_) diff --git a/src/db.rs b/src/db.rs index 58e1861..b831cce 100644 --- a/src/db.rs +++ b/src/db.rs @@ -332,6 +332,29 @@ pub fn write_event(conn: &mut PooledConnection, e: &Event) -> Result { ); } } + // if this event is a deletion, hide the referenced events from the same author. + if e.kind == 5 { + let event_candidates = e.tag_values_by_name("e"); + let mut params: Vec> = vec![]; + // first parameter will be author + params.push(Box::new(hex::decode(&e.pubkey)?)); + event_candidates + .iter() + .filter(|x| is_hex(x) && x.len() == 64) + .filter_map(|x| hex::decode(x).ok()) + .for_each(|x| params.push(Box::new(x))); + let query = format!( + "UPDATE event SET hidden=TRUE WHERE author=?, event_hash IN ({})", + repeat_vars(params.len() - 1) + ); + let mut stmt = tx.prepare(&query)?; + let update_count = stmt.execute(rusqlite::params_from_iter(params))?; + info!( + "hid {} deleted events for author {:?}", + update_count, + e.get_author_prefix() + ); + } tx.commit()?; Ok(ins_count) } diff --git a/src/error.rs b/src/error.rs index 42d96e4..5a0c992 100644 --- a/src/error.rs +++ b/src/error.rs @@ -48,6 +48,8 @@ pub enum Error { JoinError, #[error("Hyper Client error")] HyperError(hyper::Error), + #[error("Hex encoding error")] + HexError(hex::FromHexError), #[error("Unknown/Undocumented")] UnknownError, } @@ -58,6 +60,12 @@ pub enum Error { // } //} +impl From for Error { + fn from(h: hex::FromHexError) -> Self { + Error::HexError(h) + } +} + impl From for Error { fn from(h: hyper::Error) -> Self { Error::HyperError(h) diff --git a/src/event.rs b/src/event.rs index 1a4da87..45a3b83 100644 --- a/src/event.rs +++ b/src/event.rs @@ -124,6 +124,16 @@ impl Event { self.pubkey.chars().take(8).collect() } + /// Retrieve tag values + pub fn tag_values_by_name(&self, tag_name: &str) -> Vec { + self.tags + .iter() + .filter(|x| x.len() > 1) + .filter(|x| x.get(0).unwrap() == tag_name) + .map(|x| x.get(1).unwrap().to_owned()) + .collect() + } + /// Check if this event has a valid signature. fn is_valid(&self) -> bool { // TODO: return a Result with a reason for invalid events @@ -335,6 +345,32 @@ mod tests { assert_eq!(c, expected); } + #[test] + fn event_tag_select() { + let e = Event { + id: "999".to_owned(), + pubkey: "012345".to_owned(), + created_at: 501234, + kind: 1, + tags: vec![ + vec!["j".to_owned(), "abc".to_owned()], + vec!["e".to_owned(), "foo".to_owned()], + vec!["e".to_owned(), "bar".to_owned()], + vec!["e".to_owned(), "baz".to_owned()], + vec![ + "p".to_owned(), + "aaaa".to_owned(), + "ws://example.com".to_owned(), + ], + ], + content: "this is a test".to_owned(), + sig: "abcde".to_owned(), + tagidx: None, + }; + let v = e.tag_values_by_name("e"); + assert_eq!(v, vec!["foo", "bar", "baz"]); + } + #[test] fn event_canonical_with_tags() { let e = Event {