import pino from "pino"; import { lambdaRequestTracker, pinoLambdaDestination } from "pino-lambda"; import { MicroBlogBackend } from "./pocketbase"; // custom destination formatter const destination = pinoLambdaDestination(); const logger = pino({}, destination); const withRequest = lambdaRequestTracker(); const baseURL = process.env.PIXELFED_BASE_URL; const accountID = process.env.PIXELFED_ACCOUNT_ID; const pb = new MicroBlogBackend(logger); type PixelFedPost = { media_attachments: { type: string; //'image', url: string; // 'https://gram.social/storage/m/_v2/703621281309160235/530d83cd3-f15549/FR41GdSiUQY0/bNHMrzuQkuhXKKfR1zG4HHcjFTe6G2YF02SOr2zi.jpg', description: string; // 'Blurry gate', }[]; id: string; content: string; account: { id: string; }; created_at: string; tags: { name: string }[]; }; const getPostUntilId = async ({ lastSavedId, maxId, carryPosts = [], }: { lastSavedId?: string; maxId?: string; carryPosts?: PixelFedPost[]; }): Promise => { const params = new URLSearchParams(); params.append("limit", "5"); params.append("only_media", "true"); if (maxId) { params.append("max_id", maxId); } const urlWithParams = new URL(`${baseURL}${accountID}/statuses`); urlWithParams.search = params.toString(); const res = await fetch(urlWithParams.toString()); const posts = (await res.json()) as PixelFedPost[]; const containsId = posts.some((post) => post.id === lastSavedId); if (!containsId && posts.length >= 5) { return getPostUntilId({ lastSavedId, carryPosts: carryPosts?.concat(posts), maxId: posts[posts.length - 1]?.id, }); } const allPosts = carryPosts?.concat(posts).reverse(); if (lastSavedId) { const index = allPosts.findIndex((post) => post.id === lastSavedId); return allPosts.slice(index + 1); } return allPosts; }; const savePost = async (post: PixelFedPost) => { const postData = { remoteId: post.id, authorId: post.account.id, posted: post.created_at, source: "pixelfed" as const, fullPost: post, }; return await pb.savePost(postData); }; const saveTags = async (post: PixelFedPost, postId: string) => { logger.info({ tags: post.tags }, "saving tags"); for (const tag of post.tags) { await pb.setTag(tag.name, postId); } }; const saveImages = async (post: PixelFedPost, postId: string) => { logger.info({ images: post.media_attachments }, "saving images"); for (const image of post.media_attachments) { await pb.saveAndSetImage( { remoteURL: image.url, alt: image.description }, postId ); } }; exports.run = async (event: any, context: any) => { withRequest(event, context); const lastSavedPostId = await pb.getLatestPostRemoteIDBySource("pixelfed"); const posts = await getPostUntilId({ lastSavedId: lastSavedPostId }); for (const post of posts) { logger.info({ post }, "saving post"); const savedNewPost = await savePost(post); await saveTags(post, savedNewPost.id); await saveImages(post, savedNewPost.id); } };