Skip to main content

contextvm_sdk/relay/
mod.rs

1//! Nostr relay pool management.
2//!
3//! Wraps nostr-sdk's Client for relay connection, event publishing, and subscription.
4
5pub mod mock;
6pub use mock::MockRelayPool;
7
8use async_trait::async_trait;
9
10use crate::core::error::{Error, Result};
11use nostr_sdk::prelude::*;
12use std::sync::Arc;
13
14/// Trait abstracting relay pool operations, enabling dependency injection and testing.
15#[async_trait]
16pub trait RelayPoolTrait: Send + Sync {
17    /// Connect to the given relay URLs.
18    async fn connect(&self, relay_urls: &[String]) -> Result<()>;
19    /// Disconnect from all relays.
20    async fn disconnect(&self) -> Result<()>;
21    /// Publish a pre-built event to relays.
22    async fn publish_event(&self, event: &Event) -> Result<EventId>;
23    /// Build, sign, and publish an event from a builder.
24    async fn publish(&self, builder: EventBuilder) -> Result<EventId>;
25    /// Sign an event builder without publishing.
26    async fn sign(&self, builder: EventBuilder) -> Result<Event>;
27    /// Get the signer associated with this relay pool.
28    async fn signer(&self) -> Result<Arc<dyn NostrSigner>>;
29    /// Get notifications receiver for event streaming.
30    fn notifications(&self) -> tokio::sync::broadcast::Receiver<RelayPoolNotification>;
31    /// Get the public key of the signer.
32    async fn public_key(&self) -> Result<PublicKey>;
33    /// Subscribe to events matching filters.
34    async fn subscribe(&self, filters: Vec<Filter>) -> Result<()>;
35}
36
37/// Relay pool wrapper for managing Nostr relay connections.
38pub struct RelayPool {
39    client: Arc<Client>,
40}
41
42impl RelayPool {
43    /// Create a new relay pool with the given signer.
44    pub async fn new<T>(signer: T) -> Result<Self>
45    where
46        T: IntoNostrSigner,
47    {
48        let client = Client::builder().signer(signer).build();
49
50        Ok(Self {
51            client: Arc::new(client),
52        })
53    }
54
55    /// Connect to the given relay URLs.
56    pub async fn connect(&self, relay_urls: &[String]) -> Result<()> {
57        for url in relay_urls {
58            self.client
59                .add_relay(url)
60                .await
61                .map_err(|e| Error::Transport(e.to_string()))?;
62        }
63
64        self.client.connect().await;
65
66        Ok(())
67    }
68
69    /// Disconnect from all relays.
70    pub async fn disconnect(&self) -> Result<()> {
71        self.client.disconnect().await;
72        Ok(())
73    }
74
75    /// Publish a pre-built event to relays.
76    pub async fn publish_event(&self, event: &Event) -> Result<EventId> {
77        let output = self
78            .client
79            .send_event(event)
80            .await
81            .map_err(|e| Error::Transport(e.to_string()))?;
82        Ok(output.val)
83    }
84
85    /// Build, sign, and publish an event from a builder.
86    pub async fn publish(&self, builder: EventBuilder) -> Result<EventId> {
87        let output = self
88            .client
89            .send_event_builder(builder)
90            .await
91            .map_err(|e| Error::Transport(e.to_string()))?;
92        Ok(output.val)
93    }
94
95    /// Sign an event builder without publishing.
96    pub async fn sign(&self, builder: EventBuilder) -> Result<Event> {
97        self.client
98            .sign_event_builder(builder)
99            .await
100            .map_err(|e| Error::Transport(e.to_string()))
101    }
102
103    /// Get the underlying nostr-sdk Client.
104    pub fn client(&self) -> &Arc<Client> {
105        &self.client
106    }
107
108    /// Get notifications receiver for event streaming.
109    pub fn notifications(&self) -> tokio::sync::broadcast::Receiver<RelayPoolNotification> {
110        self.client.notifications()
111    }
112
113    /// Get the public key of the signer.
114    pub async fn public_key(&self) -> Result<PublicKey> {
115        let signer = self
116            .client
117            .signer()
118            .await
119            .map_err(|e| Error::Other(e.to_string()))?;
120        signer
121            .get_public_key()
122            .await
123            .map_err(|e| Error::Other(e.to_string()))
124    }
125
126    /// Subscribe to events matching filters.
127    pub async fn subscribe(&self, filters: Vec<Filter>) -> Result<()> {
128        for filter in filters {
129            self.client
130                .subscribe(filter, None)
131                .await
132                .map_err(|e| Error::Transport(e.to_string()))?;
133        }
134        Ok(())
135    }
136}
137
138#[async_trait]
139impl RelayPoolTrait for RelayPool {
140    async fn connect(&self, relay_urls: &[String]) -> Result<()> {
141        RelayPool::connect(self, relay_urls).await
142    }
143
144    async fn disconnect(&self) -> Result<()> {
145        RelayPool::disconnect(self).await
146    }
147
148    async fn publish_event(&self, event: &Event) -> Result<EventId> {
149        RelayPool::publish_event(self, event).await
150    }
151
152    async fn publish(&self, builder: EventBuilder) -> Result<EventId> {
153        RelayPool::publish(self, builder).await
154    }
155
156    async fn sign(&self, builder: EventBuilder) -> Result<Event> {
157        RelayPool::sign(self, builder).await
158    }
159
160    async fn signer(&self) -> Result<Arc<dyn NostrSigner>> {
161        self.client
162            .signer()
163            .await
164            .map_err(|e| Error::Other(e.to_string()))
165    }
166
167    fn notifications(&self) -> tokio::sync::broadcast::Receiver<RelayPoolNotification> {
168        RelayPool::notifications(self)
169    }
170
171    async fn public_key(&self) -> Result<PublicKey> {
172        RelayPool::public_key(self).await
173    }
174
175    async fn subscribe(&self, filters: Vec<Filter>) -> Result<()> {
176        RelayPool::subscribe(self, filters).await
177    }
178}