From f54327a55eb61ec404a3437d2ff13cbe0eb441c6 Mon Sep 17 00:00:00 2001 From: Mia Date: Sun, 3 Aug 2025 15:45:31 +0100 Subject: [PATCH] feat(parakeet): getBlocks and getListBlocks --- parakeet/src/xrpc/app_bsky/graph/lists.rs | 42 ++++++++++++++++ parakeet/src/xrpc/app_bsky/graph/relations.rs | 48 ++++++++++++++++++- parakeet/src/xrpc/app_bsky/mod.rs | 4 +- 3 files changed, 91 insertions(+), 3 deletions(-) diff --git a/parakeet/src/xrpc/app_bsky/graph/lists.rs b/parakeet/src/xrpc/app_bsky/graph/lists.rs index c1904c5e..7bb0b9ae 100644 --- a/parakeet/src/xrpc/app_bsky/graph/lists.rs +++ b/parakeet/src/xrpc/app_bsky/graph/lists.rs @@ -181,3 +181,45 @@ pub async fn get_list_mutes( lists: mutes, })) } + +pub async fn get_list_blocks( + State(state): State, + AtpAcceptLabelers(labelers): AtpAcceptLabelers, + auth: AtpAuth, + Query(query): Query, +) -> XrpcResult> { + let mut conn = state.pool.get().await?; + let did = auth.0.clone(); + let hyd = StatefulHydrator::new(&state.dataloaders, &state.cdn, &labelers, Some(auth)); + + let limit = query.limit.unwrap_or(50).clamp(1, 100); + + let mut blocks_query = schema::list_blocks::table + .select((schema::list_blocks::created_at, schema::list_blocks::list_uri)) + .filter(schema::list_blocks::did.eq(did)) + .into_boxed(); + + if let Some(cursor) = datetime_cursor(query.cursor.as_ref()) { + blocks_query = blocks_query.filter(schema::list_blocks::created_at.lt(cursor)); + } + + let results = blocks_query + .order(schema::list_blocks::created_at.desc()) + .limit(limit as i64) + .load::<(chrono::DateTime, String)>(&mut conn) + .await?; + + let cursor = results + .last() + .map(|(last, _)| last.timestamp_millis().to_string()); + + let uris = results.iter().map(|(_, uri)| uri.clone()).collect(); + + let lists = hyd.hydrate_lists(uris).await; + let lists = lists.into_values().collect::>(); + + Ok(Json(GetListMutesRes { + cursor, + lists, + })) +} diff --git a/parakeet/src/xrpc/app_bsky/graph/relations.rs b/parakeet/src/xrpc/app_bsky/graph/relations.rs index eef45c4d..804686a3 100644 --- a/parakeet/src/xrpc/app_bsky/graph/relations.rs +++ b/parakeet/src/xrpc/app_bsky/graph/relations.rs @@ -1,7 +1,7 @@ use crate::hydration::StatefulHydrator; use crate::xrpc::error::{Error, XrpcResult}; use crate::xrpc::extract::{AtpAcceptLabelers, AtpAuth}; -use crate::xrpc::{datetime_cursor, get_actor_did, ActorWithCursorQuery}; +use crate::xrpc::{datetime_cursor, get_actor_did, ActorWithCursorQuery, CursorQuery}; use crate::GlobalState; use axum::extract::{Query, State}; use axum::Json; @@ -11,6 +11,52 @@ use lexica::app_bsky::actor::ProfileView; use parakeet_db::schema; use serde::Serialize; +#[derive(Debug, Serialize)] +pub struct GetBlocksRes { + #[serde(skip_serializing_if = "Option::is_none")] + cursor: Option, + blocks: Vec, +} + +pub async fn get_blocks( + State(state): State, + AtpAcceptLabelers(labelers): AtpAcceptLabelers, + auth: AtpAuth, + Query(query): Query, +) -> XrpcResult> { + let mut conn = state.pool.get().await?; + let did = auth.0.clone(); + let hyd = StatefulHydrator::new(&state.dataloaders, &state.cdn, &labelers, Some(auth)); + + let limit = query.limit.unwrap_or(50).clamp(1, 100); + + let mut blocked_query = schema::blocks::table + .select((schema::blocks::created_at, schema::blocks::subject)) + .filter(schema::blocks::did.eq(did)) + .into_boxed(); + + if let Some(cursor) = datetime_cursor(query.cursor.as_ref()) { + blocked_query = blocked_query.filter(schema::blocks::created_at.lt(cursor)); + } + + let results = blocked_query + .order(schema::blocks::created_at.desc()) + .limit(limit as i64) + .load::<(chrono::DateTime, String)>(&mut conn) + .await?; + + let cursor = results + .last() + .map(|(last, _)| last.timestamp_millis().to_string()); + + let dids = results.iter().map(|(_, did)| did.clone()).collect(); + + let profiles = hyd.hydrate_profiles(dids).await; + let blocks = profiles.into_values().collect::>(); + + Ok(Json(GetBlocksRes { cursor, blocks })) +} + #[derive(Debug, Serialize)] pub struct AppBskyGraphGetFollowersRes { #[serde(skip_serializing_if = "Option::is_none")] diff --git a/parakeet/src/xrpc/app_bsky/mod.rs b/parakeet/src/xrpc/app_bsky/mod.rs index 2a48fa83..44a33eb3 100644 --- a/parakeet/src/xrpc/app_bsky/mod.rs +++ b/parakeet/src/xrpc/app_bsky/mod.rs @@ -30,12 +30,12 @@ pub fn routes() -> Router { // TODO: app.bsky.feed.getTimeline (complicated) // TODO: app.bsky.feed.searchPosts (search) .route("/app.bsky.graph.getActorStarterPacks", get(graph::starter_packs::get_actor_starter_packs)) - // TODO: app.bsky.graph.getBlocks + .route("/app.bsky.graph.getBlocks", get(graph::relations::get_blocks)) .route("/app.bsky.graph.getFollowers", get(graph::relations::get_followers)) .route("/app.bsky.graph.getFollows", get(graph::relations::get_follows)) // TODO: app.bsky.graph.getKnownFollowers .route("/app.bsky.graph.getList", get(graph::lists::get_list)) - // TODO: app.bsky.graph.getListBlocks + .route("/app.bsky.graph.getListBlocks", get(graph::lists::get_list_blocks)) .route("/app.bsky.graph.getListMutes", get(graph::lists::get_list_mutes)) .route("/app.bsky.graph.getLists", get(graph::lists::get_lists)) .route("/app.bsky.graph.getMutes", get(graph::mutes::get_mutes)) -- 2.51.2