From 6e976975e752e6cad3784173ebb2f2dfa025e28d Mon Sep 17 00:00:00 2001 From: Jared Pereira Date: Mon, 9 Feb 2026 14:32:43 +0400 Subject: [PATCH] add inngest function to track bsky post likes --- app/api/inngest/client.ts | 6 +++ app/api/inngest/functions/sync_bsky_likes.ts | 41 +++++++++++++++++++ app/api/inngest/route.tsx | 2 + app/api/rpc/[command]/get_publication_data.ts | 1 + .../dashboard/PublishedPostsLists.tsx | 1 + appview/index.ts | 18 ++++++++ drizzle/schema.ts | 3 +- supabase/database.types.ts | 3 ++ .../20260209000000_add_bsky_like_count.sql | 1 + 9 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 app/api/inngest/functions/sync_bsky_likes.ts create mode 100644 supabase/migrations/20260209000000_add_bsky_like_count.sql diff --git a/app/api/inngest/client.ts b/app/api/inngest/client.ts index 4dceb80f..f648b39f 100644 --- a/app/api/inngest/client.ts +++ b/app/api/inngest/client.ts @@ -51,6 +51,12 @@ export type Events = { documentUris?: string[]; }; }; + "appview/sync-bsky-likes": { + data: { + document_uri: string; + bsky_post_uri: string; + }; + }; "user/write-records-to-pds": { data: { did: string; diff --git a/app/api/inngest/functions/sync_bsky_likes.ts b/app/api/inngest/functions/sync_bsky_likes.ts new file mode 100644 index 00000000..9b67aab8 --- /dev/null +++ b/app/api/inngest/functions/sync_bsky_likes.ts @@ -0,0 +1,41 @@ +import { inngest } from "../client"; +import { supabaseServerClient } from "supabase/serverClient"; +import { AtpAgent } from "@atproto/api"; + +const TOTAL_ITERATIONS = 144; // 36 hours at 15-minute intervals + +export const sync_bsky_likes = inngest.createFunction( + { + id: "sync_bsky_likes", + idempotency: "event.data.bsky_post_uri", + }, + { event: "appview/sync-bsky-likes" }, + async ({ event, step }) => { + const { document_uri, bsky_post_uri } = event.data; + + const agent = new AtpAgent({ service: "https://public.api.bsky.app" }); + + const fetchAndUpdate = async () => { + const res = await agent.app.bsky.feed.getPosts({ + uris: [bsky_post_uri], + }); + const post = res.data.posts[0]; + if (!post) return 0; + const likeCount = post.likeCount ?? 0; + await supabaseServerClient + .from("documents") + .update({ bsky_like_count: likeCount }) + .eq("uri", document_uri); + return likeCount; + }; + + let likeCount = await step.run("sync-0", fetchAndUpdate); + + for (let i = 1; i < TOTAL_ITERATIONS; i++) { + await step.sleep(`wait-${i}`, "15m"); + likeCount = await step.run(`sync-${i}`, fetchAndUpdate); + } + + return { likeCount }; + }, +); diff --git a/app/api/inngest/route.tsx b/app/api/inngest/route.tsx index ce86472d..ea0f2f9e 100644 --- a/app/api/inngest/route.tsx +++ b/app/api/inngest/route.tsx @@ -13,6 +13,7 @@ import { check_oauth_session, } from "./functions/cleanup_expired_oauth_sessions"; import { write_records_to_pds } from "./functions/write_records_to_pds"; +import { sync_bsky_likes } from "./functions/sync_bsky_likes"; export const { GET, POST, PUT } = serve({ client: inngest, @@ -28,5 +29,6 @@ export const { GET, POST, PUT } = serve({ cleanup_expired_oauth_sessions, check_oauth_session, write_records_to_pds, + sync_bsky_likes, ], }); diff --git a/app/api/rpc/[command]/get_publication_data.ts b/app/api/rpc/[command]/get_publication_data.ts index 09cc91f4..9fe23804 100644 --- a/app/api/rpc/[command]/get_publication_data.ts +++ b/app/api/rpc/[command]/get_publication_data.ts @@ -86,6 +86,7 @@ export const get_publication_data = makeRoute({ indexed_at: dip.documents.indexed_at, sort_date: dip.documents.sort_date, data: dip.documents.data, + bsky_like_count: dip.documents.bsky_like_count, commentsCount: dip.documents.comments_on_documents[0]?.count || 0, mentionsCount: dip.documents.document_mentions_in_bsky[0]?.count || 0, recommendsCount: diff --git a/app/lish/[did]/[publication]/dashboard/PublishedPostsLists.tsx b/app/lish/[did]/[publication]/dashboard/PublishedPostsLists.tsx index a7e4c630..29b4b8a8 100644 --- a/app/lish/[did]/[publication]/dashboard/PublishedPostsLists.tsx +++ b/app/lish/[did]/[publication]/dashboard/PublishedPostsLists.tsx @@ -112,6 +112,7 @@ function PublishedPostItem(props: { indexed_at: doc.indexed_at, sort_date: doc.sort_date, data: doc.data, + bsky_like_count: doc.bsky_like_count ?? 0, }, }, ], diff --git a/appview/index.ts b/appview/index.ts index 139ea9fd..7319f549 100644 --- a/appview/index.ts +++ b/appview/index.ts @@ -109,6 +109,15 @@ async function handleEvent(evt: Event) { data: record.value as Json, }); if (docResult.error) console.log(docResult.error); + if (record.value.postRef?.uri) { + await inngest.send({ + name: "appview/sync-bsky-likes", + data: { + document_uri: evt.uri.toString(), + bsky_post_uri: record.value.postRef.uri, + }, + }); + } if (record.value.publication) { let publicationURI = new AtUri(record.value.publication); @@ -269,6 +278,15 @@ async function handleEvent(evt: Event) { data: record.value as Json, }); if (docResult.error) console.log(docResult.error); + if (record.value.bskyPostRef?.uri) { + await inngest.send({ + name: "appview/sync-bsky-likes", + data: { + document_uri: evt.uri.toString(), + bsky_post_uri: record.value.bskyPostRef.uri, + }, + }); + } // site.standard.document uses "site" field to reference the publication // For documents in publications, site is an AT-URI (at://did:plc:xxx/site.standard.publication/rkey) diff --git a/drizzle/schema.ts b/drizzle/schema.ts index 65cd9add..05b5b5ad 100644 --- a/drizzle/schema.ts +++ b/drizzle/schema.ts @@ -1,4 +1,4 @@ -import { pgTable, pgEnum, text, jsonb, foreignKey, timestamp, boolean, uuid, index, bigint, unique, uniqueIndex, smallint, primaryKey } from "drizzle-orm/pg-core" +import { pgTable, pgEnum, text, jsonb, foreignKey, timestamp, boolean, uuid, index, bigint, unique, uniqueIndex, smallint, primaryKey, integer } from "drizzle-orm/pg-core" import { sql } from "drizzle-orm" export const aal_level = pgEnum("aal_level", ['aal1', 'aal2', 'aal3']) @@ -225,6 +225,7 @@ export const documents = pgTable("documents", { uri: text("uri").primaryKey().notNull(), data: jsonb("data").notNull(), indexed_at: timestamp("indexed_at", { withTimezone: true, mode: 'string' }).defaultNow().notNull(), + bsky_like_count: integer("bsky_like_count").default(0).notNull(), }); export const atp_poll_votes = pgTable("atp_poll_votes", { diff --git a/supabase/database.types.ts b/supabase/database.types.ts index 6153540b..1eb727c2 100644 --- a/supabase/database.types.ts +++ b/supabase/database.types.ts @@ -335,17 +335,20 @@ export type Database = { } documents: { Row: { + bsky_like_count: number data: Json indexed_at: string sort_date: string uri: string } Insert: { + bsky_like_count?: number data: Json indexed_at?: string uri: string } Update: { + bsky_like_count?: number data?: Json indexed_at?: string uri?: string diff --git a/supabase/migrations/20260209000000_add_bsky_like_count.sql b/supabase/migrations/20260209000000_add_bsky_like_count.sql new file mode 100644 index 00000000..676476b0 --- /dev/null +++ b/supabase/migrations/20260209000000_add_bsky_like_count.sql @@ -0,0 +1 @@ +ALTER TABLE documents ADD COLUMN bsky_like_count integer NOT NULL DEFAULT 0; -- 2.51.2