mirror of
https://github.com/nostrlabs-io/notepush.git
synced 2025-06-21 21:32:51 +00:00
Better mute handling
This commit improves the robustness of mute list handling: 1. It listens to new mute lists from the relay interface, and saves them whenever there is a new one 2. It uses the user's own relay lists to fetch events (such as mute lists), helping to ensure we get their mute lists even when they are not stored in the Damus relay 3. It saves events it sees when fetching them from the network, if applicable Changelog-Changed: Improved robustness of mute handling Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
use nostr::{self, key::PublicKey, nips::nip51::MuteList, Alphabet, SingleLetterTag, TagKind::SingleLetter};
|
||||
use nostr_sdk::{Kind, TagKind};
|
||||
use nostr::{self, key::PublicKey, nips::{nip51::MuteList, nip65}, Alphabet, SingleLetterTag, TagKind::SingleLetter};
|
||||
use nostr_sdk::{EventId, Kind, TagKind};
|
||||
|
||||
/// Temporary scaffolding of old methods that have not been ported to use native Event methods
|
||||
pub trait ExtendedEvent {
|
||||
@ -14,7 +14,7 @@ pub trait ExtendedEvent {
|
||||
|
||||
/// Retrieves a set of event IDs referenced by the note
|
||||
fn referenced_event_ids(&self) -> std::collections::HashSet<nostr::EventId>;
|
||||
|
||||
|
||||
/// Retrieves a set of hashtags (t tags) referenced by the note
|
||||
fn referenced_hashtags(&self) -> std::collections::HashSet<String>;
|
||||
}
|
||||
@ -48,7 +48,7 @@ impl ExtendedEvent for nostr::Event {
|
||||
.filter_map(|tag| nostr::EventId::from_hex(tag).ok())
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
||||
/// Retrieves a set of hashtags (t tags) referenced by the note
|
||||
fn referenced_hashtags(&self) -> std::collections::HashSet<String> {
|
||||
self.get_tags_content(SingleLetter(SingleLetterTag::lowercase(Alphabet::T)))
|
||||
@ -102,12 +102,16 @@ pub trait MaybeConvertibleToMuteList {
|
||||
fn to_mute_list(&self) -> Option<MuteList>;
|
||||
}
|
||||
|
||||
pub trait MaybeConvertibleToTimestampedMuteList {
|
||||
fn to_timestamped_mute_list(&self) -> Option<TimestampedMuteList>;
|
||||
}
|
||||
|
||||
impl MaybeConvertibleToMuteList for nostr::Event {
|
||||
fn to_mute_list(&self) -> Option<MuteList> {
|
||||
if self.kind != Kind::MuteList {
|
||||
return None;
|
||||
}
|
||||
Some(MuteList {
|
||||
Some(MuteList {
|
||||
public_keys: self.referenced_pubkeys().iter().map(|pk| pk.clone()).collect(),
|
||||
hashtags: self.referenced_hashtags().iter().map(|tag| tag.clone()).collect(),
|
||||
event_ids: self.referenced_event_ids().iter().map(|id| id.clone()).collect(),
|
||||
@ -115,3 +119,104 @@ impl MaybeConvertibleToMuteList for nostr::Event {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl MaybeConvertibleToTimestampedMuteList for nostr::Event {
|
||||
fn to_timestamped_mute_list(&self) -> Option<TimestampedMuteList> {
|
||||
if self.kind != Kind::MuteList {
|
||||
return None;
|
||||
}
|
||||
let mute_list = self.to_mute_list()?;
|
||||
Some(TimestampedMuteList {
|
||||
mute_list,
|
||||
timestamp: self.created_at.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub type RelayList = Vec<(nostr::Url, Option<nostr::nips::nip65::RelayMetadata>)>;
|
||||
|
||||
pub trait MaybeConvertibleToRelayList {
|
||||
fn to_relay_list(&self) -> Option<RelayList>;
|
||||
}
|
||||
|
||||
impl MaybeConvertibleToRelayList for nostr::Event {
|
||||
fn to_relay_list(&self) -> Option<RelayList> {
|
||||
if self.kind != Kind::RelayList {
|
||||
return None;
|
||||
}
|
||||
let extracted_relay_list = nip65::extract_relay_list(&self);
|
||||
// Convert the extracted relay list data fully into owned data that can be returned
|
||||
let extracted_relay_list_owned = extracted_relay_list.into_iter()
|
||||
.map(|(url, metadata)| (url.clone(), metadata.as_ref().map(|m| m.clone())))
|
||||
.collect();
|
||||
Some(extracted_relay_list_owned)
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for types that can be encoded to and decoded from JSON, specific to this crate.
|
||||
/// This is defined to overcome the rust compiler's limitation of implementing a trait for a type that is not defined in the same crate.
|
||||
pub trait Codable {
|
||||
fn to_json(&self) -> Result<serde_json::Value, Box<dyn std::error::Error>>;
|
||||
fn from_json(json: serde_json::Value) -> Result<Self, Box<dyn std::error::Error>>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
impl Codable for MuteList {
|
||||
fn to_json(&self) -> Result<serde_json::Value, Box<dyn std::error::Error>> {
|
||||
Ok(serde_json::json!({
|
||||
"public_keys": self.public_keys.iter().map(|pk| pk.to_hex()).collect::<Vec<String>>(),
|
||||
"hashtags": self.hashtags.clone(),
|
||||
"event_ids": self.event_ids.iter().map(|id| id.to_hex()).collect::<Vec<String>>(),
|
||||
"words": self.words.clone()
|
||||
}))
|
||||
}
|
||||
|
||||
fn from_json(json: serde_json::Value) -> Result<Self, Box<dyn std::error::Error>>
|
||||
where
|
||||
Self: Sized {
|
||||
let public_keys = json.get("public_keys")
|
||||
.ok_or_else(|| "Missing 'public_keys' field".to_string())?
|
||||
.as_array()
|
||||
.ok_or_else(|| "'public_keys' must be an array".to_string())?
|
||||
.iter()
|
||||
.map(|pk| PublicKey::from_hex(pk.as_str().unwrap_or_default()).map_err(|e| e.to_string()))
|
||||
.collect::<Result<Vec<PublicKey>, String>>()?;
|
||||
|
||||
let hashtags = json.get("hashtags")
|
||||
.ok_or_else(|| "Missing 'hashtags' field".to_string())?
|
||||
.as_array()
|
||||
.ok_or_else(|| "'hashtags' must be an array".to_string())?
|
||||
.iter()
|
||||
.map(|tag| tag.as_str().map(|s| s.to_string()).ok_or_else(|| "Invalid hashtag".to_string()))
|
||||
.collect::<Result<Vec<String>, String>>()?;
|
||||
|
||||
let event_ids = json.get("event_ids")
|
||||
.ok_or_else(|| "Missing 'event_ids' field".to_string())?
|
||||
.as_array()
|
||||
.ok_or_else(|| "'event_ids' must be an array".to_string())?
|
||||
.iter()
|
||||
.map(|id| EventId::from_hex(id.as_str().unwrap_or_default()).map_err(|e| e.to_string()))
|
||||
.collect::<Result<Vec<EventId>, String>>()?;
|
||||
|
||||
let words = json.get("words")
|
||||
.ok_or_else(|| "Missing 'words' field".to_string())?
|
||||
.as_array()
|
||||
.ok_or_else(|| "'words' must be an array".to_string())?
|
||||
.iter()
|
||||
.map(|word| word.as_str().map(|s| s.to_string()).ok_or_else(|| "Invalid word".to_string()))
|
||||
.collect::<Result<Vec<String>, String>>()?;
|
||||
|
||||
Ok(MuteList {
|
||||
public_keys,
|
||||
hashtags,
|
||||
event_ids,
|
||||
words,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TimestampedMuteList {
|
||||
pub mute_list: MuteList,
|
||||
pub timestamp: nostr::Timestamp,
|
||||
}
|
||||
|
Reference in New Issue
Block a user