hashtree_cli/
root_events.rs1use git_remote_htree::nostr_client::hashtree_root_kinds;
4use nostr::{nips::nip19::FromBech32, Alphabet, Event, Filter, PublicKey, SingleLetterTag};
5
6#[derive(Debug, Clone)]
7pub struct PeerRootEvent {
8 pub hash: String,
9 pub key: Option<String>,
10 pub encrypted_key: Option<String>,
11 pub self_encrypted_key: Option<String>,
12 pub event_id: String,
13 pub created_at: u64,
14 pub peer_id: String,
15}
16
17pub fn build_root_filter(owner_pubkey: &str, tree_name: &str) -> Option<Filter> {
18 let author = PublicKey::from_hex(owner_pubkey)
19 .or_else(|_| PublicKey::from_bech32(owner_pubkey))
20 .ok()?;
21 Some(
22 Filter::new()
23 .kinds(hashtree_root_kinds())
24 .author(author)
25 .custom_tag(
26 SingleLetterTag::lowercase(Alphabet::D),
27 tree_name.to_string(),
28 )
29 .custom_tag(SingleLetterTag::lowercase(Alphabet::L), "hashtree")
30 .limit(50),
31 )
32}
33
34pub fn pick_latest_event<'a, I>(events: I) -> Option<&'a Event>
35where
36 I: IntoIterator<Item = &'a Event>,
37{
38 events.into_iter().max_by(|a, b| {
39 let ordering = a.created_at.cmp(&b.created_at);
40 if ordering == std::cmp::Ordering::Equal {
41 b.id.cmp(&a.id)
42 } else {
43 ordering
44 }
45 })
46}
47
48pub fn root_event_from_peer(
49 event: &Event,
50 peer_id: &str,
51 tree_name: &str,
52) -> Option<PeerRootEvent> {
53 let mut tree_match = false;
54 let mut labeled = false;
55 let mut key = None;
56 let mut encrypted_key = None;
57 let mut self_encrypted_key = None;
58 let mut hash_tag = None;
59
60 for tag in event.tags.iter() {
61 let slice = tag.as_slice();
62 if slice.len() < 2 {
63 continue;
64 }
65 match slice[0].as_str() {
66 "d" => tree_match = slice[1].as_str() == tree_name,
67 "l" => labeled |= slice[1].as_str() == "hashtree",
68 "hash" => hash_tag = Some(slice[1].to_string()),
69 "key" => key = Some(slice[1].to_string()),
70 "encryptedKey" => encrypted_key = Some(slice[1].to_string()),
71 "selfEncryptedKey" => self_encrypted_key = Some(slice[1].to_string()),
72 _ => {}
73 }
74 }
75
76 if !tree_match || !labeled {
77 return None;
78 }
79
80 let hash = hash_tag.or_else(|| {
81 if event.content.is_empty() {
82 None
83 } else {
84 Some(event.content.clone())
85 }
86 })?;
87
88 Some(PeerRootEvent {
89 hash,
90 key,
91 encrypted_key,
92 self_encrypted_key,
93 event_id: event.id.to_hex(),
94 created_at: event.created_at.as_secs(),
95 peer_id: peer_id.to_string(),
96 })
97}