lib

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

commit bfcaeadefd0c8837784a60d2676a2c33164e3802
parent 1250d06a8267974ee9ec98a3728aacce6f3f0c94
Author: triesap <tyson@radroots.org>
Date:   Wed,  7 Jan 2026 17:58:22 +0000

nostr: preserve nip89 identifier


- fetch prior handler event by author and kind
- keep existing d tag when republishing
- fall back to first kind string when missing
- keep relay and nostrconnect_url tags intact

Diffstat:
Mnostr/src/events/application_handler.rs | 44+++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 43 insertions(+), 1 deletion(-)

diff --git a/nostr/src/events/application_handler.rs b/nostr/src/events/application_handler.rs @@ -6,8 +6,16 @@ use alloc::{string::String, vec::Vec}; use crate::error::RadrootsNostrError; use crate::events::radroots_nostr_build_event; +#[cfg(feature = "client")] +use crate::filter::radroots_nostr_filter_tag; +#[cfg(feature = "client")] +use crate::tags::radroots_nostr_tag_first_value; +#[cfg(feature = "client")] +use crate::types::{RadrootsNostrEvent, RadrootsNostrFilter, RadrootsNostrKind}; use crate::types::{RadrootsNostrEventBuilder, RadrootsNostrMetadata}; use radroots_events::kinds::KIND_APPLICATION_HANDLER; +#[cfg(feature = "client")] +use core::time::Duration; #[derive(Debug, Clone)] pub struct RadrootsNostrApplicationHandlerSpec { @@ -100,6 +108,40 @@ pub async fn radroots_nostr_publish_application_handler( spec: &RadrootsNostrApplicationHandlerSpec, ) -> Result<crate::types::RadrootsNostrOutput<crate::types::RadrootsNostrEventId>, RadrootsNostrError> { - let builder = radroots_nostr_build_application_handler_event(spec)?; + let mut spec = spec.clone(); + if spec.identifier.is_none() { + if let Some(existing) = fetch_existing_identifier(client, &spec).await? { + spec.identifier = Some(existing); + } + } + let builder = radroots_nostr_build_application_handler_event(&spec)?; crate::client::radroots_nostr_send_event(client, builder).await } + +#[cfg(feature = "client")] +async fn fetch_existing_identifier( + client: &crate::client::RadrootsNostrClient, + spec: &RadrootsNostrApplicationHandlerSpec, +) -> Result<Option<String>, RadrootsNostrError> { + let first_kind = spec + .kinds + .first() + .ok_or_else(|| RadrootsNostrError::FilterTagError("kinds are empty".to_string()))?; + let author = client.public_key().await?; + let filter = RadrootsNostrFilter::new() + .author(author) + .kind(RadrootsNostrKind::Custom(KIND_APPLICATION_HANDLER as u16)); + let filter = radroots_nostr_filter_tag(filter, "k", vec![first_kind.to_string()])?; + let mut events = client.fetch_events(filter, Duration::from_secs(5)).await?; + events.sort_by_key(|event| event.created_at.as_secs()); + let event = events.pop(); + Ok(event.and_then(|event| tag_value(&event, "d"))) +} + +#[cfg(feature = "client")] +fn tag_value(event: &RadrootsNostrEvent, key: &str) -> Option<String> { + event + .tags + .iter() + .find_map(|tag| radroots_nostr_tag_first_value(tag, key)) +}