caracal 0.2.0

Nostr client for Gemini
use crate::user::CaracalUser;
use nostr_sdk::prelude::*;
use std::sync::Arc;
use std::time::Duration;

pub async fn user_subscribe(user: &CaracalUser) {
    let Ok(contacts) =
        user.client.get_contact_list(Duration::from_secs(5)).await
    else {
        eprintln!("Failed to retrieve contact list");
        return;
    };

    let now = Timestamp::now();
    let sub_id = SubscriptionId::new("user");

    user.client.connect().await;
    user.client.unsubscribe(&sub_id).await;

    let subscription = Filter::new()
        .kind(Kind::RelayList)
        .kind(Kind::InboxRelays)
        .kind(Kind::Metadata)
        .kind(Kind::ContactList)
        .kind(Kind::TextNote)
        .kind(Kind::LongFormTextNote)
        .kind(Kind::Reaction)
        .since(now - (86400 * 120))
        .authors(contacts.into_iter().map(|c| c.public_key));

    if let Err(e) = user
        .client
        .subscribe_with_id(sub_id, subscription, None)
        .await
    {
        eprintln!("Error subscribing: {e}")
    }
}

pub async fn handle_nostr_notifications(
    client: Arc<Client>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    client
        .handle_notifications(|notification| async {
            if let RelayPoolNotification::Event {
                relay_url: _,
                subscription_id: _,
                event,
            } = notification
            {
                match event.kind {
                    Kind::TextNote => {
                        if let Err(err) = client
                            .subscribe(
                                Filter::new()
                                    .kind(Kind::Metadata)
                                    .author(event.pubkey)
                                    .limit(1),
                                None,
                            )
                            .await
                        {
                            eprintln!("Metadata sub failed: {err}");
                        }
                    }
                    Kind::RelayList => {
                        for (url, _metadata) in
                            nip65::extract_relay_list(&event)
                        {
                            let Ok(_) = client.add_discovery_relay(url).await
                            else {
                                continue;
                            };

                            let Ok(_) = client.add_read_relay(url).await else {
                                continue;
                            };
                        }
                    }
                    _ => {}
                }
            } else if let RelayPoolNotification::Message {
                relay_url: _,
                message,
            } = notification
                && let RelayMessage::Event {
                    subscription_id: _,
                    ref event,
                } = message
                {
                    if let Kind::RelayList = event.kind {
                        for (url, _metadata) in
                            nip65::extract_relay_list(event)
                        {
                            let Ok(_) =
                                client.add_discovery_relay(url).await
                            else {
                                continue;
                            };

                            let Ok(_) = client.add_read_relay(url).await
                            else {
                                continue;
                            };
                        }
                    }
                }

            Ok(false)
        })
        .await?;

    Ok(())
}