Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sdk: allow to send/get msgs and events to/from specific relays #273

Merged
merged 9 commits into from
Feb 1, 2024
35 changes: 29 additions & 6 deletions bindings/nostr-sdk-ffi/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,29 @@ impl Client {

// TODO: add get_events_of_with_opts

/// Get events of filters from specific relays
///
/// Get events both from **local database** and **relays**
///
/// If no relay is specified, will be queried only the database.
pub fn get_events_from(
&self,
urls: Vec<String>,
filters: Vec<Arc<Filter>>,
timeout: Option<Duration>,
) -> Result<Vec<Arc<Event>>> {
let filters = filters
.into_iter()
.map(|f| f.as_ref().deref().clone())
.collect();
Ok(self
.inner
.get_events_from(urls, filters, timeout)?
.into_iter()
.map(|e| Arc::new(e.into()))
.collect())
}

pub fn req_events_of(&self, filters: Vec<Arc<Filter>>, timeout: Option<Duration>) {
let filters = filters
.into_iter()
Expand All @@ -178,8 +201,8 @@ impl Client {
Ok(self.inner.send_msg(msg.into())?)
}

pub fn send_msg_to(&self, url: String, msg: ClientMessage) -> Result<()> {
Ok(self.inner.send_msg_to(url, msg.into())?)
pub fn send_msg_to(&self, urls: Vec<String>, msg: ClientMessage) -> Result<()> {
Ok(self.inner.send_msg_to(urls, msg.into())?)
}

pub fn send_event(&self, event: Arc<Event>) -> Result<Arc<EventId>> {
Expand All @@ -190,10 +213,10 @@ impl Client {
))
}

pub fn send_event_to(&self, url: String, event: Arc<Event>) -> Result<Arc<EventId>> {
pub fn send_event_to(&self, urls: Vec<String>, event: Arc<Event>) -> Result<Arc<EventId>> {
Ok(Arc::new(
self.inner
.send_event_to(url, event.as_ref().deref().clone())?
.send_event_to(urls, event.as_ref().deref().clone())?
.into(),
))
}
Expand Down Expand Up @@ -223,12 +246,12 @@ impl Client {
/// Rise an error if the [`ClientSigner`] is not set.
pub fn send_event_builder_to(
&self,
url: String,
urls: Vec<String>,
builder: Arc<EventBuilder>,
) -> Result<Arc<EventId>> {
Ok(Arc::new(
self.inner
.send_event_builder_to(url, builder.as_ref().deref().clone())?
.send_event_builder_to(urls, builder.as_ref().deref().clone())?
.into(),
))
}
Expand Down
13 changes: 7 additions & 6 deletions bindings/nostr-sdk-ffi/src/relay/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use uniffi::{Enum, Object};

pub mod options;

use self::options::RelaySendOptions;
use crate::error::Result;

#[derive(Object)]
Expand Down Expand Up @@ -182,11 +183,11 @@ impl Relay {
block_on(async move { Ok(self.inner.terminate().await?) })
}

pub fn send_msg(&self, msg: ClientMessage, wait: Option<Duration>) -> Result<()> {
block_on(async move { Ok(self.inner.send_msg(msg.into(), wait).await?) })
pub fn send_msg(&self, msg: ClientMessage, opts: Arc<RelaySendOptions>) -> Result<()> {
block_on(async move { Ok(self.inner.send_msg(msg.into(), **opts).await?) })
}

pub fn subscribe(&self, filters: Vec<Arc<Filter>>, wait: Option<Duration>) -> Result<()> {
pub fn subscribe(&self, filters: Vec<Arc<Filter>>, opts: Arc<RelaySendOptions>) -> Result<()> {
block_on(async move {
Ok(self
.inner
Expand All @@ -195,14 +196,14 @@ impl Relay {
.into_iter()
.map(|f| f.as_ref().deref().clone())
.collect(),
wait,
**opts,
)
.await?)
})
}

pub fn unsubscribe(&self, wait: Option<Duration>) -> Result<()> {
block_on(async move { Ok(self.inner.unsubscribe(wait).await?) })
pub fn unsubscribe(&self, opts: Arc<RelaySendOptions>) -> Result<()> {
block_on(async move { Ok(self.inner.unsubscribe(**opts).await?) })
}

pub fn get_events_of(
Expand Down
47 changes: 47 additions & 0 deletions bindings/nostr-sdk-ffi/src/relay/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,53 @@ use std::time::Duration;
use nostr_ffi::helper::unwrap_or_clone_arc;
use uniffi::{Enum, Object};

#[derive(Clone, Object)]
pub struct RelaySendOptions {
inner: nostr_sdk::RelaySendOptions,
}

impl Deref for RelaySendOptions {
type Target = nostr_sdk::RelaySendOptions;

fn deref(&self) -> &Self::Target {
&self.inner
}
}

#[uniffi::export]
impl RelaySendOptions {
/// New default `RelaySendOptions`
#[uniffi::constructor]
pub fn new() -> Self {
Self {
inner: nostr_sdk::RelaySendOptions::default(),
}
}

/// Skip wait for disconnected relay (default: true)
pub fn skip_disconnected(self: Arc<Self>, value: bool) -> Self {
let mut builder = unwrap_or_clone_arc(self);
builder.inner = builder.inner.skip_disconnected(value);
builder
}

/// Skip wait for confirmation that message is sent (default: false)
pub fn skip_send_confirmation(self: Arc<Self>, value: bool) -> Self {
let mut builder = unwrap_or_clone_arc(self);
builder.inner = builder.inner.skip_send_confirmation(value);
builder
}

/// Timeout for sending event (default: 10 secs)
///
/// If `None`, the default timeout will be used
pub fn timeout(self: Arc<Self>, timeout: Option<Duration>) -> Self {
let mut builder = unwrap_or_clone_arc(self);
builder.inner = builder.inner.timeout(timeout);
builder
}
}

#[derive(Enum)]
pub enum NegentropyDirection {
Up,
Expand Down
2 changes: 1 addition & 1 deletion bindings/nostr-sdk-js/examples/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ async function main() {
await client.sendEvent(event);

// Send custom event to a specific previously added relay
// await client.sendEventTo("wss://relay.damus.io", event);
// await client.sendEventTo(["wss://relay.damus.io"], event);

let builder = new EventBuilder(1111, "My custom event signer with the ClientSigner", []);
await client.sendEventBuilder(builder);
Expand Down
47 changes: 39 additions & 8 deletions bindings/nostr-sdk-js/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub use self::signer::JsClientSigner;
use self::zapper::{JsZapDetails, JsZapEntity};
use crate::abortable::JsAbortHandle;
use crate::database::JsNostrDatabase;
use crate::duration::JsDuration;
use crate::relay::options::JsNegentropyOptions;
use crate::relay::{JsRelay, JsRelayArray};

Expand Down Expand Up @@ -163,10 +164,10 @@ impl JsClient {
pub async fn get_events_of(
&self,
filters: Vec<JsFilter>,
timeout: Option<f64>,
timeout: Option<JsDuration>,
) -> Result<JsEventArray> {
let filters: Vec<Filter> = filters.into_iter().map(|f| f.into()).collect();
let timeout: Option<Duration> = timeout.map(Duration::from_secs_f64);
let timeout: Option<Duration> = timeout.map(|d| *d);
let events: Vec<Event> = self
.inner
.get_events_of(filters, timeout)
Expand All @@ -183,6 +184,36 @@ impl JsClient {
Ok(events)
}

/// Get events of filters from specific relays
///
/// Get events both from **local database** and **relays**
///
/// If no relay is specified, will be queried only the database.
#[wasm_bindgen(js_name = getEventsFrom)]
pub async fn get_events_from(
&self,
urls: Vec<String>,
filters: Vec<JsFilter>,
timeout: Option<JsDuration>,
) -> Result<JsEventArray> {
let filters: Vec<Filter> = filters.into_iter().map(|f| f.into()).collect();
let timeout: Option<Duration> = timeout.map(|d| *d);
let events: Vec<Event> = self
.inner
.get_events_from(urls, filters, timeout)
.await
.map_err(into_err)?;
let events: JsEventArray = events
.into_iter()
.map(|e| {
let e: JsEvent = e.into();
JsValue::from(e)
})
.collect::<Array>()
.unchecked_into();
Ok(events)
}

/// Request events of filters.
/// All events will be received on notification listener
/// until the EOSE "end of stored events" message is received from the relay.
Expand All @@ -204,9 +235,9 @@ impl JsClient {

/// Send client message to a specific relay
#[wasm_bindgen(js_name = sendMsgTo)]
pub async fn send_msg_to(&self, url: String, msg: &JsClientMessage) -> Result<()> {
pub async fn send_msg_to(&self, urls: Vec<String>, msg: &JsClientMessage) -> Result<()> {
self.inner
.send_msg_to(url, msg.deref().clone())
.send_msg_to(urls, msg.deref().clone())
.await
.map_err(into_err)
}
Expand All @@ -229,9 +260,9 @@ impl JsClient {
/// This method will wait for the `OK` message from the relay.
/// If you not want to wait for the `OK` message, use `sendMsgTo` method instead.
#[wasm_bindgen(js_name = sendEventTo)]
pub async fn send_event_to(&self, url: String, event: &JsEvent) -> Result<JsEventId> {
pub async fn send_event_to(&self, urls: Vec<String>, event: &JsEvent) -> Result<JsEventId> {
self.inner
.send_event_to(url, event.deref().clone())
.send_event_to(urls, event.deref().clone())
.await
.map_err(into_err)
.map(|id| id.into())
Expand Down Expand Up @@ -265,11 +296,11 @@ impl JsClient {
#[wasm_bindgen(js_name = sendEventBuilderTo)]
pub async fn send_event_builder_to(
&self,
url: String,
urls: Vec<String>,
builder: &JsEventBuilder,
) -> Result<JsEventId> {
self.inner
.send_event_builder_to(url, builder.deref().clone())
.send_event_builder_to(urls, builder.deref().clone())
.await
.map_err(into_err)
.map(|id| id.into())
Expand Down
2 changes: 1 addition & 1 deletion crates/nostr-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ async fn main() -> Result<()> {
// client.send_event(event).await?;

// Send custom event to a specific previously added relay
client.send_event_to("wss://relay.damus.io", event).await?;
client.send_event_to(["wss://relay.damus.io"], event).await?;

Ok(())
}
Expand Down
12 changes: 10 additions & 2 deletions crates/nostr-sdk/examples/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,32 @@ async fn main() -> Result<()> {
let client = Client::new(&my_keys);
client.add_relay("wss://relay.damus.io").await?;
client.add_relay("wss://nostr.wine").await?;
client.add_relay("wss://relay.rip").await?;

client.connect().await;

// Publish a text note
client.publish_text_note("Hello world", []).await?;

// Create a text note POW event
// Create a text note POW event and broadcast to all connected relays
let event: Event =
EventBuilder::text_note("POW text note from nostr-sdk", []).to_pow_event(&my_keys, 20)?;
client.send_event(event).await?;

// Send multiple events at once
// Send multiple events at once (to all relays)
let mut events: Vec<Event> = Vec::new();
for i in 0..10 {
events.push(EventBuilder::text_note(format!("Event #{i}"), []).to_event(&my_keys)?);
}
let opts = RelaySendOptions::default();
client.batch_event(events, opts).await?;

// Send event to specific relays
let event: Event = EventBuilder::text_note("POW text note from nostr-sdk 16", [])
.to_pow_event(&my_keys, 16)?;
client
.send_event_to(["wss://relay.damus.io", "wss://relay.rip"], event)
.await?;

Ok(())
}
16 changes: 16 additions & 0 deletions crates/nostr-sdk/examples/get-events-of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,31 @@ async fn main() -> Result<()> {
let client = Client::default();
client.add_relay("wss://relay.damus.io").await?;
client.add_relay("wss://nostr.wine").await?;
client.add_relay("wss://relay.rip").await?;
client.add_relay("wss://relay.nostr.info").await?;

client.connect().await;

// Get events from all connected relays
let filter = Filter::new().author(public_key).kind(Kind::Metadata);
let events = client
.get_events_of(vec![filter], Some(Duration::from_secs(10)))
.await;
println!("{events:#?}");

// Get events from specific relays
let filter = Filter::new()
.author(public_key)
.kind(Kind::TextNote)
.limit(3);
let events = client
.get_events_from(
["wss://relay.damus.io", "wss://relay.rip"],
vec![filter],
Some(Duration::from_secs(10)),
)
.await;
println!("{events:#?}");

Ok(())
}
6 changes: 3 additions & 3 deletions crates/nostr-sdk/examples/subscriptions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ async fn main() -> Result<()> {
.subscribe_with_internal_id(
InternalSubscriptionId::Custom(String::from("other-id")),
vec![other_filters],
None,
RelaySendOptions::default(),
)
.await?;

Expand All @@ -61,7 +61,7 @@ async fn main() -> Result<()> {
.subscribe_with_internal_id(
InternalSubscriptionId::Custom(String::from("other-id")),
vec![other_filters],
None,
RelaySendOptions::default(),
)
.await?;
} else {
Expand All @@ -73,7 +73,7 @@ async fn main() -> Result<()> {
relay
.unsubscribe_with_internal_id(
InternalSubscriptionId::Custom(String::from("other-id")),
None,
RelaySendOptions::default(),
)
.await?;
// OR
Expand Down
Loading
Loading