aidaemon 0.11.4

A personal AI agent that runs as a background daemon, accessible via Telegram, Slack, or Discord, with tool use, MCP integration, and persistent memory
Documentation
use super::*;

#[async_trait]
impl crate::traits::OAuthStore for SqliteStateStore {
    async fn save_oauth_connection(
        &self,
        conn: &crate::traits::OAuthConnection,
    ) -> anyhow::Result<i64> {
        let now = chrono::Utc::now().to_rfc3339();
        let result = sqlx::query(
            "INSERT INTO oauth_connections (service, auth_type, username, scopes, token_expires_at, created_at, updated_at) \
             VALUES (?, ?, ?, ?, ?, ?, ?) \
             ON CONFLICT(service) DO UPDATE SET \
             auth_type = excluded.auth_type, username = excluded.username, scopes = excluded.scopes, \
             token_expires_at = excluded.token_expires_at, updated_at = excluded.updated_at",
        )
        .bind(&conn.service)
        .bind(&conn.auth_type)
        .bind(&conn.username)
        .bind(&conn.scopes)
        .bind(&conn.token_expires_at)
        .bind(&now)
        .bind(&now)
        .execute(&self.pool)
        .await?;
        Ok(result.last_insert_rowid())
    }

    async fn save_pending_oauth_flow(
        &self,
        flow: &crate::traits::PendingOAuthFlow,
    ) -> anyhow::Result<()> {
        sqlx::query(
            "INSERT INTO pending_oauth_flows (state, service, code_verifier, session_id, created_at) \
             VALUES (?, ?, ?, ?, ?) \
             ON CONFLICT(state) DO UPDATE SET \
             service = excluded.service, code_verifier = excluded.code_verifier, \
             session_id = excluded.session_id, created_at = excluded.created_at",
        )
        .bind(&flow.state)
        .bind(&flow.service)
        .bind(&flow.code_verifier)
        .bind(&flow.session_id)
        .bind(&flow.created_at)
        .execute(&self.pool)
        .await?;
        Ok(())
    }

    async fn get_oauth_connection(
        &self,
        service: &str,
    ) -> anyhow::Result<Option<crate::traits::OAuthConnection>> {
        let row = sqlx::query(
            "SELECT id, service, auth_type, username, scopes, token_expires_at, created_at, updated_at \
             FROM oauth_connections WHERE service = ?",
        )
        .bind(service)
        .fetch_optional(&self.pool)
        .await?;

        Ok(row.map(|r| crate::traits::OAuthConnection {
            id: r.get("id"),
            service: r.get("service"),
            auth_type: r.get("auth_type"),
            username: r.try_get("username").unwrap_or(None),
            scopes: r.get("scopes"),
            token_expires_at: r.try_get("token_expires_at").unwrap_or(None),
            created_at: r.get("created_at"),
            updated_at: r.get("updated_at"),
        }))
    }

    async fn get_pending_oauth_flow(
        &self,
        state: &str,
    ) -> anyhow::Result<Option<crate::traits::PendingOAuthFlow>> {
        let row = sqlx::query(
            "SELECT state, service, code_verifier, session_id, created_at \
             FROM pending_oauth_flows WHERE state = ?",
        )
        .bind(state)
        .fetch_optional(&self.pool)
        .await?;

        Ok(row.map(|r| crate::traits::PendingOAuthFlow {
            state: r.get("state"),
            service: r.get("service"),
            code_verifier: r.try_get("code_verifier").unwrap_or(None),
            session_id: r.get("session_id"),
            created_at: r.get("created_at"),
        }))
    }

    async fn list_oauth_connections(&self) -> anyhow::Result<Vec<crate::traits::OAuthConnection>> {
        let rows = sqlx::query(
            "SELECT id, service, auth_type, username, scopes, token_expires_at, created_at, updated_at \
             FROM oauth_connections ORDER BY service ASC",
        )
        .fetch_all(&self.pool)
        .await?;

        Ok(rows
            .iter()
            .map(|r| crate::traits::OAuthConnection {
                id: r.get("id"),
                service: r.get("service"),
                auth_type: r.get("auth_type"),
                username: r.try_get("username").unwrap_or(None),
                scopes: r.get("scopes"),
                token_expires_at: r.try_get("token_expires_at").unwrap_or(None),
                created_at: r.get("created_at"),
                updated_at: r.get("updated_at"),
            })
            .collect())
    }

    async fn list_pending_oauth_flows(
        &self,
    ) -> anyhow::Result<Vec<crate::traits::PendingOAuthFlow>> {
        let rows = sqlx::query(
            "SELECT state, service, code_verifier, session_id, created_at \
             FROM pending_oauth_flows ORDER BY created_at ASC",
        )
        .fetch_all(&self.pool)
        .await?;

        Ok(rows
            .iter()
            .map(|r| crate::traits::PendingOAuthFlow {
                state: r.get("state"),
                service: r.get("service"),
                code_verifier: r.try_get("code_verifier").unwrap_or(None),
                session_id: r.get("session_id"),
                created_at: r.get("created_at"),
            })
            .collect())
    }

    async fn delete_oauth_connection(&self, service: &str) -> anyhow::Result<()> {
        sqlx::query("DELETE FROM oauth_connections WHERE service = ?")
            .bind(service)
            .execute(&self.pool)
            .await?;
        Ok(())
    }

    async fn delete_pending_oauth_flow(&self, state: &str) -> anyhow::Result<()> {
        sqlx::query("DELETE FROM pending_oauth_flows WHERE state = ?")
            .bind(state)
            .execute(&self.pool)
            .await?;
        Ok(())
    }

    async fn update_oauth_token_expiry(
        &self,
        service: &str,
        expires_at: Option<&str>,
    ) -> anyhow::Result<()> {
        let now = chrono::Utc::now().to_rfc3339();
        sqlx::query(
            "UPDATE oauth_connections SET token_expires_at = ?, updated_at = ? WHERE service = ?",
        )
        .bind(expires_at)
        .bind(&now)
        .bind(service)
        .execute(&self.pool)
        .await?;
        Ok(())
    }
}