diff --git a/packages/api/package.json b/packages/api/package.json index 9499375..dd48487 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -4,6 +4,7 @@ "version": "3.8.0", "dependencies": { "@ambito-dolar/core": "^3.4.0", + "@atproto/api": "^0.6.20", "@aws-sdk/client-dynamodb": "^3.370.0", "@aws-sdk/client-s3": "^3.370.0", "@aws-sdk/client-sns": "^3.370.0", diff --git a/packages/api/src/libs/shared.js b/packages/api/src/libs/shared.js index 7f3fad4..2c8ef44 100644 --- a/packages/api/src/libs/shared.js +++ b/packages/api/src/libs/shared.js @@ -17,6 +17,7 @@ import semverGte from 'semver/functions/gte'; import semverLt from 'semver/functions/lt'; import zlib from 'zlib'; +import { publish as publishToBsky } from './social/bsky'; import { publish as publishToInstagram } from './social/instagram'; import { publish as publishToMastodon } from './social/mastodon'; import { publish as publishToReddit } from './social/reddit'; @@ -579,7 +580,14 @@ const triggerSendSocialNotificationsEvent = async (caption, image_url) => const triggerSocials = async (targets, caption, url, file, story_file) => { const promises = _.chain( - targets ?? ['ifttt', 'instagram', 'mastodon', 'reddit' /*, 'twitter' */] + targets ?? [ + 'ifttt', + 'instagram', + 'mastodon', + 'reddit', + // 'twitter', + 'bsky', + ], ) .map((target) => { let promise; @@ -601,6 +609,9 @@ const triggerSocials = async (targets, caption, url, file, story_file) => { /* case 'twitter': promise = publishToTwitter(caption, file); break; */ + case 'bsky': + promise = publishToBsky(caption, file); + break; } if (promise) { return [target, promise]; diff --git a/packages/api/src/libs/social/bsky.js b/packages/api/src/libs/social/bsky.js new file mode 100644 index 0000000..5fc5649 --- /dev/null +++ b/packages/api/src/libs/social/bsky.js @@ -0,0 +1,50 @@ +import { BskyAgent, RichText } from '@atproto/api'; + +export const publish = async (caption, file) => { + try { + const start_time = Date.now(); + const agent = new BskyAgent({ service: 'https://bsky.social' }); + await agent.login({ + identifier: process.env.BSKY_USERNAME, + password: process.env.BSKY_PASSWORD, + }); + const attachment = + file && + (await agent.uploadBlob(file, { + encoding: 'image/jpeg', + })); + const richText = new RichText({ text: caption }); + // automatically detects mentions and links + await richText.detectFacets(agent); + const postRecord = { + $type: 'app.bsky.feed.post', + text: richText.text, + facets: richText.facets, + createdAt: new Date().toISOString(), + ...(attachment && { + embed: { + $type: 'app.bsky.embed.images', + images: [ + { + image: attachment.data.blob, + alt: '', + }, + ], + }, + }), + }; + const result = await agent.post(postRecord); + const duration = (Date.now() - start_time) / 1000; + return { + ...result, + duration, + }; + } catch (error) { + /* console.error( + 'Unable to publish to bsky', + JSON.stringify({ error: error.message }) + ); */ + // unhandled error + throw error; + } +};