nostr_gossip/
lib.rs

1// Copyright (c) 2022-2023 Yuki Kishimoto
2// Copyright (c) 2023-2025 Rust Nostr Developers
3// Distributed under the MIT software license
4
5//! Nostr Gossip
6
7#![forbid(unsafe_code)]
8#![warn(missing_docs)]
9#![warn(rustdoc::bare_urls)]
10#![warn(clippy::large_futures)]
11
12use std::any::Any;
13use std::collections::HashSet;
14use std::fmt::Debug;
15use std::sync::Arc;
16
17use nostr::prelude::*;
18
19pub mod error;
20pub mod flags;
21pub mod prelude;
22
23use self::error::GossipError;
24
25/// Gossip list kind
26#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
27pub enum GossipListKind {
28    /// NIP-17
29    Nip17,
30    /// NIP-65
31    Nip65,
32}
33
34impl GossipListKind {
35    /// Convert to event [`Kind`].
36    pub fn to_event_kind(&self) -> Kind {
37        match self {
38            Self::Nip17 => Kind::InboxRelays,
39            Self::Nip65 => Kind::RelayList,
40        }
41    }
42}
43
44/// Public key status
45#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
46pub enum GossipPublicKeyStatus {
47    /// The public key data is updated
48    Updated,
49    /// The public key data is outdated
50    Outdated {
51        /// The timestamp of the relay list event that is currently stored
52        created_at: Option<Timestamp>,
53    },
54}
55
56/// Best relay selection.
57#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
58pub enum BestRelaySelection {
59    /// Get all the best relays for **reading** and **writing** events (NIP-65)
60    All {
61        /// Limit for read relays
62        read: usize,
63        /// Limit for write relays
64        write: usize,
65        /// Limit for hints
66        hints: usize,
67        /// Limit for most received relays
68        most_received: usize,
69    },
70    /// Get the best relays for **reading** events (NIP-65)
71    Read {
72        /// Limit
73        limit: usize,
74    },
75    /// Get the best relays for **writing** events (NIP-65)
76    Write {
77        /// Limit
78        limit: usize,
79    },
80    /// Get the best relays for **reading** and **writing** private messages (NIP-17)
81    PrivateMessage {
82        /// Limit
83        limit: usize,
84    },
85    /// Relays found in hints
86    Hints {
87        /// Limit
88        limit: usize,
89    },
90    /// Relays that received most events
91    MostReceived {
92        /// Limit
93        limit: usize,
94    },
95}
96
97/// Nostr gossip trait.
98pub trait NostrGossip: Any + Debug + Send + Sync {
99    /// Process an [`Event`]
100    ///
101    /// Optionally takes the [`RelayUrl`] from where the [`Event`] comes from.
102    fn process<'a>(
103        &'a self,
104        event: &'a Event,
105        relay_url: Option<&'a RelayUrl>,
106    ) -> BoxedFuture<'a, Result<(), GossipError>>;
107
108    /// Check the [`PublicKey`] status
109    fn status<'a>(
110        &'a self,
111        public_key: &'a PublicKey,
112        list: GossipListKind,
113    ) -> BoxedFuture<'a, Result<GossipPublicKeyStatus, GossipError>>;
114
115    /// Update the last check timestamp for an [`PublicKey`].
116    fn update_fetch_attempt<'a>(
117        &'a self,
118        public_key: &'a PublicKey,
119        list: GossipListKind,
120    ) -> BoxedFuture<'a, Result<(), GossipError>>;
121
122    /// Get the best relays for a [`PublicKey`].
123    fn get_best_relays<'a>(
124        &'a self,
125        public_key: &'a PublicKey,
126        selection: BestRelaySelection,
127    ) -> BoxedFuture<'a, Result<HashSet<RelayUrl>, GossipError>>;
128}
129
130#[doc(hidden)]
131pub trait IntoNostrGossip {
132    fn into_nostr_gossip(self) -> Arc<dyn NostrGossip>;
133}
134
135impl IntoNostrGossip for Arc<dyn NostrGossip> {
136    fn into_nostr_gossip(self) -> Arc<dyn NostrGossip> {
137        self
138    }
139}
140
141impl<T> IntoNostrGossip for T
142where
143    T: NostrGossip + Sized + 'static,
144{
145    fn into_nostr_gossip(self) -> Arc<dyn NostrGossip> {
146        Arc::new(self)
147    }
148}
149
150impl<T> IntoNostrGossip for Arc<T>
151where
152    T: NostrGossip + 'static,
153{
154    fn into_nostr_gossip(self) -> Arc<dyn NostrGossip> {
155        self
156    }
157}