use super::*;
pub(super) async fn publish_event_with_retry(
client: &Client,
relay_urls: &[RelayUrl],
event: Event,
label: &str,
) -> anyhow::Result<()> {
let mut last_error = "no relays configured".to_string();
for attempt in 0..5 {
ensure_publish_connection(client, relay_urls).await;
match publish_event_once(client, relay_urls, &event).await {
Ok(()) => return Ok(()),
Err(error) => last_error = error.to_string(),
}
if attempt < 4 {
sleep(Duration::from_millis(750 * (attempt + 1) as u64)).await;
}
}
Err(anyhow::anyhow!("{label}: {last_error}"))
}
pub(super) async fn publish_event_fire_and_forget(
client: &Client,
relay_urls: &[RelayUrl],
event: &Event,
label: &str,
) -> anyhow::Result<Vec<String>> {
if relay_urls.is_empty() {
return Err(anyhow::anyhow!("{label}: no relays configured"));
}
client.connect().await;
let output = client
.send_msg_to(
relay_urls.to_vec(),
ClientMessage::Event(Cow::Borrowed(event)),
)
.await
.map_err(|error| anyhow::anyhow!("{label}: {error}"))?;
if output.success.is_empty() {
let reasons = output.failed.values().cloned().collect::<Vec<_>>();
return Err(anyhow::anyhow!(
"{label}: {}",
if reasons.is_empty() {
"no relay accepted event for send".to_string()
} else {
reasons.join("; ")
}
));
}
let mut relays = output
.success
.into_iter()
.map(|relay| relay.to_string())
.collect::<Vec<_>>();
relays.sort();
Ok(relays)
}
async fn ensure_publish_connection(client: &Client, relay_urls: &[RelayUrl]) {
client.connect().await;
let deadline = tokio::time::Instant::now() + Duration::from_millis(500);
loop {
let connected = client
.relays()
.await
.iter()
.filter(|(relay_url, relay)| {
relay_urls.iter().any(|configured| configured == *relay_url)
&& relay.status() == RelayStatus::Connected
})
.count();
if connected > 0 || tokio::time::Instant::now() >= deadline {
return;
}
sleep(Duration::from_millis(25)).await;
}
}
pub(super) async fn publish_event_once(
client: &Client,
relay_urls: &[RelayUrl],
event: &Event,
) -> anyhow::Result<()> {
if relay_urls.is_empty() {
return Err(anyhow::anyhow!("no relays configured"));
}
let output = client
.send_event_to(relay_urls.to_vec(), event)
.await
.map_err(|error| anyhow::anyhow!(error.to_string()))?;
if output.success.is_empty() {
let reasons = output.failed.values().cloned().collect::<Vec<_>>();
Err(anyhow::anyhow!(if reasons.is_empty() {
"no relay accepted event".to_string()
} else {
reasons.join("; ")
}))
} else {
Ok(())
}
}