use crate::db::MirrorDb; use crate::discovery::DiscoveredRepo; use crate::health::GraspServer; use anyhow::Result; use nostr::Kind; use nostr_sdk::prelude::*; pub struct NostrMirror { client: nostr_sdk::Client, } impl NostrMirror { pub fn new(client: nostr_sdk::Client) -> Self { Self { client } } pub async fn forward_events_to_servers( &self, db: &MirrorDb, events: &[Event], servers: &[GraspServer], ) -> Result<()> { for event in events { if db.have_seen_event(&event.id.to_hex()).await? { continue; } for server in servers { if !server.is_grasp_server() { continue; } tracing::debug!( event_id = %event.id.to_hex(), kind = event.kind.as_u16(), server = %server.domain, "forwarding event" ); let url: RelayUrl = RelayUrl::parse(&server.relay_url)?; let urls = vec![url]; match self.client.send_event_to(urls, event.clone()).await { Ok(_) => { tracing::debug!( event_id = %event.id.to_hex(), server = %server.domain, "event forwarded" ); } Err(e) => { tracing::warn!( event_id = %event.id.to_hex(), server = %server.domain, error = %e, "failed to forward event" ); } } } let _ = db.record_event(&event.id.to_hex()).await; } Ok(()) } pub async fn forward_repo_events( &self, db: &MirrorDb, repo: &DiscoveredRepo, servers: &[GraspServer], ) -> Result<()> { let filters = vec![ Filter::new() .kind(Kind::Custom(30617)) .author(repo.pubkey) .identifier(&repo.identifier), Filter::new() .kind(Kind::Custom(30618)) .author(repo.pubkey) .identifier(&repo.identifier), ]; let mut all_events = Vec::new(); for filter in filters { let events = self .client .fetch_events(filter, std::time::Duration::from_secs(15)) .await?; all_events.extend(events); } if all_events.is_empty() { tracing::debug!(identifier = %repo.identifier, "no events to forward"); return Ok(()); } tracing::info!( identifier = %repo.identifier, count = all_events.len(), "forwarding repo events" ); self.forward_events_to_servers(db, &all_events, servers).await } pub async fn sync_all_events( &self, db: &MirrorDb, npubs: &[PublicKey], servers: &[GraspServer], ) -> Result<()> { let git_kinds = [ Kind::Custom(30617), Kind::Custom(30618), Kind::Custom(1631), Kind::Custom(1642), Kind::EventDeletion, ]; let mut all_events = Vec::new(); for pk in npubs { for kind in &git_kinds { let filter = Filter::new().kind(*kind).author(*pk).limit(100); let events = self .client .fetch_events(filter, std::time::Duration::from_secs(30)) .await?; all_events.extend(events); } } tracing::info!(count = all_events.len(), "fetched events for forwarding"); self.forward_events_to_servers(db, &all_events, servers).await } }