lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README | LICENSE

commit 67a3c74e2e307c596fba7848830317dc849e560f
parent 8647874eec7e34409e7bc38a54bc1fcd83bd48d1
Author: triesap <tyson@radroots.org>
Date:   Mon,  6 Oct 2025 16:01:12 +0100

events: add `kind` to event metadata and adapter decoding

Diffstat:
Mcrates/events-codec/src/job/feedback/decode.rs | 5+++--
Mcrates/events-codec/src/job/request/decode.rs | 3++-
Mcrates/events-codec/src/job/result/decode.rs | 5+++--
Mcrates/events/src/comment/models.rs | 1+
Mcrates/events/src/follow/models.rs | 1+
Mcrates/events/src/job/feedback/models.rs | 3++-
Mcrates/events/src/job/request/models.rs | 4++--
Mcrates/events/src/job/result/models.rs | 3++-
Mcrates/events/src/listing/models.rs | 1+
Mcrates/events/src/post/models.rs | 1+
Mcrates/events/src/profile/models.rs | 1+
Mcrates/events/src/reaction/models.rs | 1+
Mcrates/events/src/relay_document/models.rs | 3+--
Mcrates/net-core/src/nostr_client/inner.rs | 8++++----
Mcrates/net-core/src/nostr_client/manager.rs | 16++++++++--------
Mcrates/net-core/src/nostr_client/posts.rs | 24++++++++++++------------
Mcrates/net-core/src/nostr_client/profile.rs | 8++++----
Mcrates/net-core/src/nostr_client/status.rs | 28++++++++++++++++++++++------
Mcrates/nostr/src/event_adapters.rs | 3+++
Mcrates/nostr/src/events/mod.rs | 2+-
Dcrates/nostr/src/events/notes.rs | 59-----------------------------------------------------------
Acrates/nostr/src/events/post.rs | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcrates/nostr/src/lib.rs | 4++--
23 files changed, 136 insertions(+), 107 deletions(-)

diff --git a/crates/events-codec/src/job/feedback/decode.rs b/crates/events-codec/src/job/feedback/decode.rs @@ -1,11 +1,11 @@ use radroots_events::{ + RadrootsNostrEvent, RadrootsNostrEventPtr, job::{ + JobPaymentRequest, feedback::models::{ RadrootsJobFeedback, RadrootsJobFeedbackEventIndex, RadrootsJobFeedbackEventMetadata, }, - JobPaymentRequest, }, - RadrootsNostrEvent, RadrootsNostrEventPtr, }; use crate::job::{ @@ -92,6 +92,7 @@ pub fn metadata_from_event( id, author, published_at, + kind, job_feedback, }) } diff --git a/crates/events-codec/src/job/request/decode.rs b/crates/events-codec/src/job/request/decode.rs @@ -1,9 +1,9 @@ use radroots_events::{ + RadrootsNostrEvent, job::request::models::{ RadrootsJobInput, RadrootsJobParam, RadrootsJobRequest, RadrootsJobRequestEventIndex, RadrootsJobRequestEventMetadata, }, - RadrootsNostrEvent, }; use crate::job::{ @@ -82,6 +82,7 @@ pub fn metadata_from_event( id, author, published_at, + kind, job_request, }) } diff --git a/crates/events-codec/src/job/result/decode.rs b/crates/events-codec/src/job/result/decode.rs @@ -1,12 +1,12 @@ use radroots_events::{ + RadrootsNostrEvent, RadrootsNostrEventPtr, job::{ + JobPaymentRequest, request::models::RadrootsJobInput, result::models::{ RadrootsJobResult, RadrootsJobResultEventIndex, RadrootsJobResultEventMetadata, }, - JobPaymentRequest, }, - RadrootsNostrEvent, RadrootsNostrEventPtr, }; use crate::job::{ @@ -89,6 +89,7 @@ pub fn metadata_from_event( id, author, published_at, + kind, job_result, }) } diff --git a/crates/events/src/comment/models.rs b/crates/events/src/comment/models.rs @@ -15,6 +15,7 @@ pub struct RadrootsCommentEventMetadata { pub id: String, pub author: String, pub published_at: u32, + pub kind: u32, pub comment: RadrootsComment, } diff --git a/crates/events/src/follow/models.rs b/crates/events/src/follow/models.rs @@ -14,6 +14,7 @@ pub struct RadrootsFollowEventMetadata { pub id: String, pub author: String, pub published_at: u32, + pub kind: u32, pub follow: RadrootsFollow, } diff --git a/crates/events/src/job/feedback/models.rs b/crates/events/src/job/feedback/models.rs @@ -1,8 +1,8 @@ use serde::{Deserialize, Serialize}; use crate::{ - job::{JobFeedbackStatus, JobPaymentRequest}, RadrootsNostrEvent, RadrootsNostrEventPtr, + job::{JobFeedbackStatus, JobPaymentRequest}, }; #[typeshare::typeshare] @@ -18,6 +18,7 @@ pub struct RadrootsJobFeedbackEventMetadata { pub id: String, pub author: String, pub published_at: u32, + pub kind: u32, pub job_feedback: RadrootsJobFeedback, } diff --git a/crates/events/src/job/request/models.rs b/crates/events/src/job/request/models.rs @@ -1,7 +1,6 @@ use serde::{Deserialize, Serialize}; - -use crate::{job::JobInputType, RadrootsNostrEvent}; +use crate::{RadrootsNostrEvent, job::JobInputType}; #[typeshare::typeshare] #[derive(Clone, Debug, Serialize, Deserialize)] @@ -16,6 +15,7 @@ pub struct RadrootsJobRequestEventMetadata { pub id: String, pub author: String, pub published_at: u32, + pub kind: u32, pub job_request: RadrootsJobRequest, } diff --git a/crates/events/src/job/result/models.rs b/crates/events/src/job/result/models.rs @@ -1,8 +1,8 @@ use serde::{Deserialize, Serialize}; use crate::{ - job::{request::models::RadrootsJobInput, JobPaymentRequest}, RadrootsNostrEvent, RadrootsNostrEventPtr, + job::{JobPaymentRequest, request::models::RadrootsJobInput}, }; #[typeshare::typeshare] @@ -18,6 +18,7 @@ pub struct RadrootsJobResultEventMetadata { pub id: String, pub author: String, pub published_at: u32, + pub kind: u32, pub job_result: RadrootsJobResult, } diff --git a/crates/events/src/listing/models.rs b/crates/events/src/listing/models.rs @@ -19,6 +19,7 @@ pub struct RadrootsListingEventMetadata { pub id: String, pub author: String, pub published_at: u32, + pub kind: u32, pub listing: RadrootsListing, } diff --git a/crates/events/src/post/models.rs b/crates/events/src/post/models.rs @@ -14,6 +14,7 @@ pub struct RadrootsPostEventMetadata { pub id: String, pub author: String, pub published_at: u32, + pub kind: u32, pub post: RadrootsPost, } diff --git a/crates/events/src/profile/models.rs b/crates/events/src/profile/models.rs @@ -14,6 +14,7 @@ pub struct RadrootsProfileEventMetadata { pub id: String, pub author: String, pub published_at: u32, + pub kind: u32, pub profile: RadrootsProfile, } diff --git a/crates/events/src/reaction/models.rs b/crates/events/src/reaction/models.rs @@ -14,6 +14,7 @@ pub struct RadrootsReactionEventMetadata { pub id: String, pub author: String, pub published_at: u32, + pub kind: u32, pub reaction: RadrootsReaction, } diff --git a/crates/events/src/relay_document/models.rs b/crates/events/src/relay_document/models.rs @@ -10,4 +10,4 @@ pub struct RadrootsRelayDocument { supported_nips: Option<Vec<u16>>, software: Option<String>, version: Option<String>, -} -\ No newline at end of file +} diff --git a/crates/net-core/src/nostr_client/inner.rs b/crates/net-core/src/nostr_client/inner.rs @@ -13,8 +13,8 @@ pub(super) struct Inner { pub statuses: Arc<Mutex<HashMap<RelayUrl, RelayStatus>>>, pub last_error: Arc<Mutex<Option<String>>>, pub rt: Handle, - pub posts_tx: broadcast::Sender<RadrootsPostEventMetadata>, - pub posts_stream: Arc<Mutex<Option<JoinHandle<()>>>>, + pub post_events_tx: broadcast::Sender<RadrootsPostEventMetadata>, + pub post_events_stream: Arc<Mutex<Option<JoinHandle<()>>>>, } impl Inner { @@ -29,8 +29,8 @@ impl Inner { statuses: Arc::new(Mutex::new(HashMap::new())), last_error: Arc::new(Mutex::new(None)), rt, - posts_tx: tx, - posts_stream: Arc::new(Mutex::new(None)), + post_events_tx: tx, + post_events_stream: Arc::new(Mutex::new(None)), }) } } diff --git a/crates/net-core/src/nostr_client/manager.rs b/crates/net-core/src/nostr_client/manager.rs @@ -20,10 +20,10 @@ impl NostrClientManager { } impl NostrClientManager { - pub fn start_textnote_stream(&self, since_unix: Option<u64>) { + pub fn start_post_event_stream(&self, since_unix: Option<u64>) { if self .inner - .posts_stream + .post_events_stream .lock() .ok() .is_some_and(|h| h.is_some()) @@ -61,29 +61,29 @@ impl NostrClientManager { let meta = radroots_nostr::event_adapters::to_post_event_metadata(&event); let ts = event.created_at.as_u64(); since = ts.saturating_add(1); - let _ = inner.posts_tx.send(meta); + let _ = inner.post_events_tx.send(meta); } } } }); - if let Ok(mut g) = self.inner.posts_stream.lock() { + if let Ok(mut g) = self.inner.post_events_stream.lock() { *g = Some(handle); } } - pub fn stop_textnote_stream(&self) { - if let Ok(mut g) = self.inner.posts_stream.lock() { + pub fn stop_post_event_stream(&self) { + if let Ok(mut g) = self.inner.post_events_stream.lock() { if let Some(h) = g.take() { h.abort(); } } } - pub fn subscribe_text_notes( + pub fn subscribe_post_events( &self, ) -> tokio::sync::broadcast::Receiver<radroots_events::post::models::RadrootsPostEventMetadata> { - self.inner.posts_tx.subscribe() + self.inner.post_events_tx.subscribe() } } diff --git a/crates/net-core/src/nostr_client/posts.rs b/crates/net-core/src/nostr_client/posts.rs @@ -4,8 +4,8 @@ use radroots_events::post::models::RadrootsPostEventMetadata; use super::manager::NostrClientManager; impl NostrClientManager { - pub async fn publish_text_note(&self, content: String) -> Result<String> { - let builder = radroots_nostr::events::notes::build_text_note(content); + pub async fn publish_post_event(&self, content: String) -> Result<String> { + let builder = radroots_nostr::events::post::build_post_event(content); let out = self .inner .client @@ -15,20 +15,20 @@ impl NostrClientManager { Ok(out.val.to_string()) } - pub fn publish_text_note_blocking(&self, content: String) -> Result<String> { + pub fn publish_post_event_blocking(&self, content: String) -> Result<String> { let rt = self.inner.rt.clone(); let this = self.clone(); - rt.block_on(async move { this.publish_text_note(content).await }) + rt.block_on(async move { this.publish_post_event(content).await }) } - pub async fn publish_reply_to_event( + pub async fn publish_post_reply_event( &self, parent_event_id_hex: String, parent_author_hex: String, content: String, root_event_id_hex: Option<String>, ) -> Result<String> { - let builder = radroots_nostr::events::notes::build_reply( + let builder = radroots_nostr::events::post::build_post_reply_event( &parent_event_id_hex, &parent_author_hex, content, @@ -46,7 +46,7 @@ impl NostrClientManager { Ok(out.val.to_string()) } - pub fn publish_reply_to_event_blocking( + pub fn publish_post_reply_event_blocking( &self, parent_event_id_hex: String, parent_author_hex: String, @@ -56,7 +56,7 @@ impl NostrClientManager { let rt = self.inner.rt.clone(); let this = self.clone(); rt.block_on(async move { - this.publish_reply_to_event( + this.publish_post_reply_event( parent_event_id_hex, parent_author_hex, content, @@ -66,25 +66,25 @@ impl NostrClientManager { }) } - pub async fn fetch_text_notes( + pub async fn fetch_post_events( &self, limit: u16, since_unix: Option<u64>, ) -> Result<Vec<RadrootsPostEventMetadata>> { let items = - radroots_nostr::events::notes::fetch_text_notes(&self.inner.client, limit, since_unix) + radroots_nostr::events::post::fetch_post_events(&self.inner.client, limit, since_unix) .await .map_err(|e| NetError::Msg(e.to_string()))?; Ok(items) } - pub fn fetch_text_notes_blocking( + pub fn fetch_post_events_blocking( &self, limit: u16, since_unix: Option<u64>, ) -> Result<Vec<RadrootsPostEventMetadata>> { let rt = self.inner.rt.clone(); let this = self.clone(); - rt.block_on(async move { this.fetch_text_notes(limit, since_unix).await }) + rt.block_on(async move { this.fetch_post_events(limit, since_unix).await }) } } diff --git a/crates/net-core/src/nostr_client/profile.rs b/crates/net-core/src/nostr_client/profile.rs @@ -4,7 +4,7 @@ use radroots_events::profile::models::RadrootsProfileEventMetadata; use super::manager::NostrClientManager; impl NostrClientManager { - pub async fn fetch_profile_kind0( + pub async fn fetch_profile_event( &self, author: nostr::PublicKey, ) -> Result<Option<RadrootsProfileEventMetadata>> { @@ -26,16 +26,16 @@ impl NostrClientManager { Ok(None) } - pub fn fetch_profile_kind0_blocking( + pub fn fetch_profile_event_blocking( &self, author: nostr::PublicKey, ) -> Result<Option<RadrootsProfileEventMetadata>> { let rt = self.inner.rt.clone(); let this = self.clone(); - rt.block_on(async move { this.fetch_profile_kind0(author).await }) + rt.block_on(async move { this.fetch_profile_event(author).await }) } - pub fn set_profile_kind0_blocking( + pub fn publish_profile_event_blocking( &self, name: Option<String>, display_name: Option<String>, diff --git a/crates/net-core/src/nostr_client/status.rs b/crates/net-core/src/nostr_client/status.rs @@ -1,5 +1,5 @@ use nostr_sdk::prelude::MonitorNotification; -use tracing::info; +use tracing::{info, warn}; use super::manager::NostrClientManager; use super::types::{Light, NostrConnectionSnapshot}; @@ -9,15 +9,29 @@ impl NostrClientManager { let inner = self.inner.clone(); let rt = inner.rt.clone(); let inner_for_task = inner.clone(); + rt.spawn(async move { - if let Some(m) = inner_for_task.client.monitor() { - let mut rx = m.subscribe(); + if let Some(monitor) = inner_for_task.client.monitor() { + let mut rx = monitor.subscribe(); while let Ok(notification) = rx.recv().await { - let MonitorNotification::StatusChanged { relay_url, status } = notification; - { - let mut map = inner_for_task.statuses.lock().unwrap(); + let MonitorNotification::StatusChanged { relay_url, status } = notification + else { + continue; + }; + + if let Ok(mut map) = inner_for_task.statuses.lock() { map.insert(relay_url.clone(), status); + } else { + if let Ok(mut last) = inner_for_task.last_error.lock() { + *last = Some("status watcher: statuses mutex poisoned".to_string()); + } + warn!( + "status watcher: statuses mutex poisoned; dropping update for {}", + relay_url + ); + continue; } + info!("relay status changed {} -> {:?}", relay_url, status); } } @@ -35,6 +49,7 @@ impl NostrClientManager { let mut connected = 0usize; let mut connecting = 0usize; + for (_url, st) in map.iter() { match st { nostr_sdk::prelude::RelayStatus::Connected => connected += 1, @@ -52,6 +67,7 @@ impl NostrClientManager { }; let last_error = self.inner.last_error.lock().ok().and_then(|e| e.clone()); + NostrConnectionSnapshot { light, connected, diff --git a/crates/nostr/src/event_adapters.rs b/crates/nostr/src/event_adapters.rs @@ -12,6 +12,7 @@ pub fn to_post_event_metadata(e: &Event) -> RadrootsPostEventMetadata { id: e.id.to_string(), author: e.pubkey.to_string(), published_at: e.created_at.as_u64() as u32, + kind: e.kind.as_u16() as u32, post: RadrootsPost { content: e.content.clone(), }, @@ -25,6 +26,7 @@ pub fn to_profile_event_metadata(e: &Event) -> Option<RadrootsProfileEventMetada id: e.id.to_string(), author: e.pubkey.to_string(), published_at: e.created_at.as_u64() as u32, + kind: e.kind.as_u16() as u32, profile: p, }); } @@ -46,6 +48,7 @@ pub fn to_profile_event_metadata(e: &Event) -> Option<RadrootsProfileEventMetada id: e.id.to_string(), author: e.pubkey.to_string(), published_at: e.created_at.as_u64() as u32, + kind: e.kind.as_u16() as u32, profile: p, }); } diff --git a/crates/nostr/src/events/mod.rs b/crates/nostr/src/events/mod.rs @@ -1,6 +1,6 @@ pub mod jobs; pub mod metadata; -pub mod notes; +pub mod post; extern crate alloc; use alloc::{string::String, vec::Vec}; diff --git a/crates/nostr/src/events/notes.rs b/crates/nostr/src/events/notes.rs @@ -1,59 +0,0 @@ -use crate::error::NostrUtilsError; - -#[cfg(all(feature = "sdk", feature = "events"))] -use core::time::Duration; -use nostr::{ - event::{EventBuilder, EventId, Tag}, - key::PublicKey, -}; -#[cfg(all(feature = "sdk", feature = "events"))] -use nostr_sdk::prelude::{Client, Filter, Kind, Timestamp}; - -pub fn build_text_note(content: impl Into<String>) -> EventBuilder { - EventBuilder::text_note(content) -} - -pub fn build_reply( - parent_event_id_hex: &str, - parent_author_hex: &str, - content: impl Into<String>, - root_event_id_hex: Option<&str>, -) -> Result<EventBuilder, NostrUtilsError> { - let parent_id = EventId::from_hex(parent_event_id_hex)?; - let parent_pubkey = PublicKey::from_hex(parent_author_hex)?; - let mut tags: Vec<Tag> = Vec::new(); - - if let Some(root_hex) = root_event_id_hex { - if !root_hex.is_empty() { - if let Ok(root_id) = EventId::from_hex(root_hex) { - tags.push(Tag::event(root_id)); - } - } - } - - tags.push(Tag::event(parent_id)); - tags.push(Tag::public_key(parent_pubkey)); - - Ok(EventBuilder::text_note(content).tags(tags)) -} - -#[cfg(all(feature = "sdk", feature = "events"))] -pub async fn fetch_text_notes( - client: &Client, - limit: u16, - since_unix: Option<u64>, -) -> Result<Vec<radroots_events::post::models::RadrootsPostEventMetadata>, NostrUtilsError> { - let mut filter = Filter::new().kind(Kind::TextNote).limit(limit.into()); - - if let Some(s) = since_unix { - filter = filter.since(Timestamp::from(s)); - } - - let events = client.fetch_events(filter, Duration::from_secs(10)).await?; - let out = events - .into_iter() - .map(|ev| crate::event_adapters::to_post_event_metadata(&ev)) - .collect(); - - Ok(out) -} diff --git a/crates/nostr/src/events/post.rs b/crates/nostr/src/events/post.rs @@ -0,0 +1,59 @@ +use crate::error::NostrUtilsError; + +#[cfg(all(feature = "sdk", feature = "events"))] +use core::time::Duration; +use nostr::{ + event::{EventBuilder, EventId, Tag}, + key::PublicKey, +}; +#[cfg(all(feature = "sdk", feature = "events"))] +use nostr_sdk::prelude::{Client, Filter, Kind, Timestamp}; + +pub fn build_post_event(content: impl Into<String>) -> EventBuilder { + EventBuilder::text_note(content) +} + +pub fn build_post_reply_event( + parent_event_id_hex: &str, + parent_author_hex: &str, + content: impl Into<String>, + root_event_id_hex: Option<&str>, +) -> Result<EventBuilder, NostrUtilsError> { + let parent_id = EventId::from_hex(parent_event_id_hex)?; + let parent_pubkey = PublicKey::from_hex(parent_author_hex)?; + let mut tags: Vec<Tag> = Vec::new(); + + if let Some(root_hex) = root_event_id_hex { + if !root_hex.is_empty() { + if let Ok(root_id) = EventId::from_hex(root_hex) { + tags.push(Tag::event(root_id)); + } + } + } + + tags.push(Tag::event(parent_id)); + tags.push(Tag::public_key(parent_pubkey)); + + Ok(EventBuilder::text_note(content).tags(tags)) +} + +#[cfg(all(feature = "sdk", feature = "events"))] +pub async fn fetch_post_events( + client: &Client, + limit: u16, + since_unix: Option<u64>, +) -> Result<Vec<radroots_events::post::models::RadrootsPostEventMetadata>, NostrUtilsError> { + let mut filter = Filter::new().kind(Kind::TextNote).limit(limit.into()); + + if let Some(s) = since_unix { + filter = filter.since(Timestamp::from(s)); + } + + let events = client.fetch_events(filter, Duration::from_secs(10)).await?; + let out = events + .into_iter() + .map(|ev| crate::event_adapters::to_post_event_metadata(&ev)) + .collect(); + + Ok(out) +} diff --git a/crates/nostr/src/lib.rs b/crates/nostr/src/lib.rs @@ -34,11 +34,11 @@ pub mod prelude { pub use crate::events::{ jobs::{nostr_build_event_job_feedback, nostr_build_event_job_result}, metadata::{build_metadata_event, fetch_metadata_for_author, post_metadata_event}, - notes::{build_reply as build_text_reply, build_text_note}, + post::{build_post_event, build_post_reply_event}, }; #[cfg(all(feature = "sdk", feature = "events"))] - pub use crate::events::notes::fetch_text_notes; + pub use crate::events::post::fetch_post_events; pub use crate::parse::{parse_pubkey, parse_pubkeys}; pub use crate::relays::{add_relay, connect, remove_relay};