nostralink 0.1.9

Linked data library for nostr
Documentation
use super::prelude::*;
use nostr_sdk::prelude::*;
use std::time::Duration;

pub trait EventStateOperations {
    fn mark_seen(
        &self,
        agent_pubk: &PublicKey,
        event_id: &EventId,
        seen: bool,
    ) -> Result<bool, RdfStoreError>;

    fn mark_row_seen<T: Clone>(
        &self,
        agent_pubk: &PublicKey,
        row: &TRdfResultRow<T>,
        seen: bool,
    ) -> Result<bool, RdfStoreError>;
}

fn pred_user_seen_at() -> NamedNode {
    nostr_vocab_nn("user_seen_at")
}

fn pred_user_seen_by() -> NamedNode {
    nostr_vocab_nn("user_seen_by")
}

impl EventStateOperations for RdfEventsStore {
    fn mark_seen(
        &self,
        agent_pubk: &PublicKey,
        event_id: &EventId,
        _seen: bool,
    ) -> Result<bool, RdfStoreError> {
        let agent_nn = agent_pubk.named_node()?;
        let event_nn = event_id.named_node()?;

        let Ok(_) = self.store.insert(QuadRef::new(
            &event_nn,
            &pred_user_seen_at(),
            &now_literal(),
            &GraphName::DefaultGraph,
        )) else {
            return Err(RdfStoreError::QuadInsertError);
        };

        let Ok(_) = self.store.insert(QuadRef::new(
            &event_nn,
            &pred_user_seen_by(),
            &agent_nn,
            &GraphName::DefaultGraph,
        )) else {
            return Err(RdfStoreError::QuadInsertError);
        };

        Ok(true)
    }

    fn mark_row_seen<T: Clone>(
        &self,
        agent_pubk: &PublicKey,
        row: &TRdfResultRow<T>,
        _seen: bool,
    ) -> Result<bool, RdfStoreError> {
        let agent_nn = agent_pubk.named_node()?;

        let Some(event) = row.get(SPVars::EVENT) else {
            return Err(RdfStoreError::BadEventError);
        };

        let event_nn = event.value.to_named_node()?;

        if self.store.contains(
            // Already added
            QuadRef::new(
                &event_nn,
                &pred_user_seen_by(),
                &agent_nn,
                &GraphName::DefaultGraph,
            ),
        )? {
            return Ok(false);
        }

        let Ok(_) = self.store.insert(QuadRef::new(
            &event_nn,
            &pred_user_seen_at(),
            &now_literal(),
            &GraphName::DefaultGraph,
        )) else {
            return Err(RdfStoreError::QuadInsertError);
        };

        let Ok(_) = self.store.insert(QuadRef::new(
            &event_nn,
            &pred_user_seen_by(),
            &agent_nn,
            &GraphName::DefaultGraph,
        )) else {
            return Err(RdfStoreError::QuadInsertError);
        };

        Ok(true)
    }
}

pub trait EventStoreOperations {
    fn sweep(&self, duration: Duration) -> Result<(), RdfStoreError>;

    fn delete_events_older_than(
        &self,
        ts: Timestamp,
        ekinds: Option<Vec<Kind>>,
        pubkeys: Option<Vec<PublicKey>>,
    ) -> Result<(), RdfStoreError>;
}

impl EventStoreOperations for RdfEventsStore {
    /// Delete events older than the given timestamp
    fn delete_events_older_than(
        &self,
        ts: Timestamp,
        ekinds: Option<Vec<Kind>>,
        pubkeys: Option<Vec<PublicKey>>,
    ) -> Result<(), RdfStoreError> {
        let mut kf = String::new();
        let mut pubkf = String::new();

        if let Some(kinds) = ekinds {
            // Build a FILTER for these requested event kinds
            let values = kinds
                .iter()
                .map(|kind| format!("(?kind = {})", kind.to_string()))
                .collect::<Vec<_>>()
                .join(" || ");

            kf.push_str(&format!("FILTER({})", values));
        }

        if let Some(pubkl) = pubkeys {
            // Build a FILTER for these pubkeys
            let values = pubkl
                .iter()
                .map(|pk| format!("(?pubk = {})", pk.to_hex()))
                .collect::<Vec<_>>()
                .join(" || ");

            pubkf.push_str(&format!("FILTER({})", values));
        }

        let q = self
            .prepare_query(&nrq_get("delete_events_before")?)
            .replace("@KIND_FILTER@", &kf)
            .replace("@PUBK_FILTER@", &kf)
            .replace("@TS@", &format!("{}", ts.as_u64()));

        match self.store.update(
            Update::parse(&q, None).map_err(|_| RdfStoreError::QueryError)?,
        ) {
            Ok(_r) => Ok(()),
            Err(_e) => Err(RdfStoreError::QueryError),
        }
    }

    /// Sweep the store, purging old events to maintain an optimal store size
    fn sweep(&self, duration_before: Duration) -> Result<(), RdfStoreError> {
        let kinds = vec![
            Kind::TextNote,
            Kind::LongFormTextNote,
            Kind::Repost,
            Kind::GenericRepost,
            Kind::Reaction,
        ];

        self.delete_events_older_than(
            Timestamp::now() - duration_before,
            Some(kinds),
            None,
        )
    }
}