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