diff --git a/app/index.mjs b/app/index.mjs index 35894e8..59bee9c 100644 --- a/app/index.mjs +++ b/app/index.mjs @@ -7,6 +7,7 @@ import { warmCaches } from "./splatnet/index.mjs"; import MastodonClient from './social/clients/MastodonClient.mjs'; import ImageWriter from './social/clients/ImageWriter.mjs'; import BlueskyClient from './social/clients/BlueskyClient.mjs'; +import ThreadsClient from './social/clients/ThreadsClient.mjs'; import { archiveData } from './data/DataArchiver.mjs'; consoleStamp(console); @@ -19,6 +20,7 @@ const actions = { socialTestMastodon: () => testStatuses([new MastodonClient]), socialTestBluesky: () => testStatuses([new BlueskyClient]), socialTestImage: () => testStatuses([new ImageWriter]), + socialTestThreads: () => testStatuses([new ThreadsClient]), splatnet: updatePrimary, splatnetAll: updateAll, warmCaches, diff --git a/app/social/clients/ThreadsClient.mjs b/app/social/clients/ThreadsClient.mjs new file mode 100644 index 0000000..16403c7 --- /dev/null +++ b/app/social/clients/ThreadsClient.mjs @@ -0,0 +1,25 @@ +import threads from "threads-api"; +import Client from "./Client.mjs"; + +export default class ThreadsClient extends Client { + key = "threads"; + name = "Threads"; + + #api; + + constructor() { + super(); + + this.#api = new threads.ThreadsAPI({ + username: process.env.THREADS_USERNAME, + password: process.env.THREADS_PASSWORD, + }); + } + + async send(status, generator) { + await this.#api.publish({ + text: status.status, + image: status.media[0].file, + }); + } +} diff --git a/app/social/index.mjs b/app/social/index.mjs index 5e45942..4b99252 100644 --- a/app/social/index.mjs +++ b/app/social/index.mjs @@ -15,6 +15,7 @@ import EggstraWorkStatus from "./generators/EggstraWorkStatus.mjs"; import EggstraWorkUpcomingStatus from "./generators/EggstraWorkUpcomingStatus.mjs"; import BlueskyClient from "./clients/BlueskyClient.mjs"; import ChallengeStatus from "./generators/ChallengeStatus.mjs"; +import ThreadsClient from "./clients/ThreadsClient.mjs"; function defaultStatusGenerators() { return [ @@ -37,6 +38,7 @@ function defaultClients() { new TwitterClient, new MastodonClient, new BlueskyClient, + new ThreadsClient, new ImageWriter, ]; } diff --git a/package-lock.json b/package-lock.json index fb7a622..1264666 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,7 @@ "pinia": "^2.0.22", "puppeteer": "^18.0.3", "sharp": "^0.32.0", + "threads-api": "file:../threads-api/threads-api", "twitter-api-v2": "^1.12.7", "vue": "^3.2.39", "vue-i18n": "^9.2.2", @@ -41,6 +42,38 @@ "vite": "^3.1.3" } }, + "../threads-api/threads-api": { + "version": "1.1.1", + "license": "MIT", + "dependencies": { + "@swc/cli": "^0.1.62", + "@swc/core": "^1.3.68", + "axios": "^1.4.0", + "mime-types": "^2.1.35", + "uuid": "^9.0.0" + }, + "bin": { + "threads-api": "bin/cli.js" + }, + "devDependencies": { + "@babel/core": "^7.22.8", + "@babel/preset-env": "^7.22.7", + "@types/jest": "^29.5.2", + "@types/mime-types": "^2.1.1", + "@types/node": "^20.4.0", + "@types/uuid": "^9.0.2", + "babel-jest": "^29.6.1", + "commander": "^11.0.0", + "dotenv": "^16.3.1", + "jest": "^29.6.1", + "ts-jest": "^29.1.1", + "tslib": "^2.6.0", + "typescript": "^5.1.6" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/@atproto/api": { "version": "0.2.9", "resolved": "https://registry.npmjs.org/@atproto/api/-/api-0.2.9.tgz", @@ -5728,9 +5761,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -6143,6 +6176,10 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/threads-api": { + "resolved": "../threads-api/threads-api", + "link": true + }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -10785,9 +10822,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "requires": { "lru-cache": "^6.0.0" } @@ -11098,6 +11135,29 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "threads-api": { + "version": "file:../threads-api/threads-api", + "requires": { + "@babel/core": "^7.22.8", + "@babel/preset-env": "^7.22.7", + "@swc/cli": "^0.1.62", + "@swc/core": "^1.3.68", + "@types/jest": "^29.5.2", + "@types/mime-types": "^2.1.1", + "@types/node": "^20.4.0", + "@types/uuid": "^9.0.2", + "axios": "^1.4.0", + "babel-jest": "^29.6.1", + "commander": "^11.0.0", + "dotenv": "^16.3.1", + "jest": "^29.6.1", + "mime-types": "^2.1.35", + "ts-jest": "^29.1.1", + "tslib": "^2.6.0", + "typescript": "^5.1.6", + "uuid": "^9.0.0" + } + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", diff --git a/package.json b/package.json index fa57dd0..c94db78 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "pinia": "^2.0.22", "puppeteer": "^18.0.3", "sharp": "^0.32.0", + "threads-api": "file:../threads-api/threads-api", "twitter-api-v2": "^1.12.7", "vue": "^3.2.39", "vue-i18n": "^9.2.2",