mirror of
https://github.com/misenhower/splatoon3.ink.git
synced 2026-03-21 17:54:13 -05:00
Update Threads client
This commit is contained in:
parent
811cd7fcd3
commit
1b8a866548
|
|
@ -49,6 +49,6 @@ BLUESKY_SERVICE=https://bsky.social
|
||||||
BLUESKY_IDENTIFIER=splatoon3.ink # Handle or email address
|
BLUESKY_IDENTIFIER=splatoon3.ink # Handle or email address
|
||||||
BLUESKY_PASSWORD=
|
BLUESKY_PASSWORD=
|
||||||
|
|
||||||
# Threads API parameters
|
# Threads API parameters (https://developers.facebook.com/docs/threads)
|
||||||
THREADS_USERNAME=
|
THREADS_USER_ID=
|
||||||
THREADS_PASSWORD=
|
THREADS_ACCESS_TOKEN=
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,63 @@
|
||||||
import sharp from 'sharp';
|
import sharp from 'sharp';
|
||||||
import threads from 'threads-api';
|
import { PutObjectCommand, S3Client } from '@aws-sdk/client-s3';
|
||||||
import Client from './Client.mjs';
|
import Client from './Client.mjs';
|
||||||
|
import ValueCache from '../../common/ValueCache.mjs';
|
||||||
|
|
||||||
export default class ThreadsClient extends Client {
|
export default class ThreadsClient extends Client {
|
||||||
key = 'threads';
|
key = 'threads';
|
||||||
name = 'Threads';
|
name = 'Threads';
|
||||||
|
|
||||||
#api;
|
#baseUrl = 'https://graph.threads.net/v1.0';
|
||||||
|
#tokenCache = new ValueCache('threads.token');
|
||||||
constructor() {
|
#accessToken;
|
||||||
super();
|
|
||||||
|
|
||||||
this.#api = new threads.ThreadsAPI({
|
|
||||||
username: process.env.THREADS_USERNAME,
|
|
||||||
// password: process.env.THREADS_PASSWORD,
|
|
||||||
token: process.env.THREADS_TOKEN,
|
|
||||||
deviceID: process.env.THREADS_DEVICE_ID,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async canSend() {
|
async canSend() {
|
||||||
// return process.env.THREADS_USERNAME
|
return process.env.THREADS_USER_ID
|
||||||
// && process.env.THREADS_PASSWORD;
|
&& process.env.THREADS_ACCESS_TOKEN
|
||||||
|
&& process.env.AWS_S3_BUCKET
|
||||||
|
&& process.env.AWS_S3_ENDPOINT;
|
||||||
|
}
|
||||||
|
|
||||||
return process.env.THREADS_USERNAME
|
async #getAccessToken() {
|
||||||
&& process.env.THREADS_TOKEN
|
if (this.#accessToken) {
|
||||||
&& process.env.THREADS_DEVICE_ID;
|
return this.#accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to use a previously refreshed token from cache
|
||||||
|
let cached = await this.#tokenCache.getData();
|
||||||
|
if (cached) {
|
||||||
|
this.#accessToken = cached;
|
||||||
|
return this.#accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to the .env token
|
||||||
|
this.#accessToken = process.env.THREADS_ACCESS_TOKEN;
|
||||||
|
return this.#accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
async #refreshToken() {
|
||||||
|
let currentToken = await this.#getAccessToken();
|
||||||
|
|
||||||
|
let params = new URLSearchParams({
|
||||||
|
grant_type: 'th_refresh_token',
|
||||||
|
access_token: currentToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = await fetch(`${this.#baseUrl}/refresh_access_token?${params}`);
|
||||||
|
let data = await response.json();
|
||||||
|
|
||||||
|
if (data.error) {
|
||||||
|
console.error(`[${this.name}] Failed to refresh token:`, data.error.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#accessToken = data.access_token;
|
||||||
|
|
||||||
|
// Cache the token with its expiry
|
||||||
|
let expires = new Date(Date.now() + data.expires_in * 1000);
|
||||||
|
await this.#tokenCache.setData(data.access_token, expires);
|
||||||
|
|
||||||
|
console.log(`[${this.name}] Token refreshed, expires ${expires.toISOString()}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async send(status, generator) {
|
async send(status, generator) {
|
||||||
|
|
@ -35,15 +67,130 @@ export default class ThreadsClient extends Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Refresh the token if it hasn't been refreshed in the last 24 hours
|
||||||
|
let lastRefresh = await this.#tokenCache.getCachedAt();
|
||||||
|
let oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000);
|
||||||
|
if (!lastRefresh || lastRefresh < oneDayAgo) {
|
||||||
|
await this.#refreshToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
let accessToken = await this.#getAccessToken();
|
||||||
let jpeg = await sharp(status.media[0].file).jpeg().toBuffer();
|
let jpeg = await sharp(status.media[0].file).jpeg().toBuffer();
|
||||||
|
|
||||||
await this.#api.publish({
|
// Upload image to S3 so it's publicly accessible
|
||||||
text: status.status,
|
let imageUrl = await this.#uploadImage(jpeg, generator.key);
|
||||||
image: { type: 'image/jpeg', data: jpeg },
|
|
||||||
});
|
// Create a media container
|
||||||
|
let containerId = await this.#createContainer(status.status, imageUrl, accessToken);
|
||||||
|
|
||||||
|
// Wait for the container to finish processing
|
||||||
|
await this.#waitForContainer(containerId, accessToken);
|
||||||
|
|
||||||
|
// Publish the container
|
||||||
|
await this.#publish(containerId, accessToken);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[${this.name}] Failed to post ${generator.key}:`, error.message);
|
console.error(`[${this.name}] Failed to post ${generator.key}:`, error.message);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async #uploadImage(buffer, key) {
|
||||||
|
let s3 = new S3Client({
|
||||||
|
endpoint: process.env.AWS_S3_ENDPOINT,
|
||||||
|
region: process.env.AWS_REGION,
|
||||||
|
credentials: {
|
||||||
|
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
||||||
|
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let s3Key = `status-screenshots/${key}.jpg`;
|
||||||
|
|
||||||
|
await s3.send(new PutObjectCommand({
|
||||||
|
Bucket: process.env.AWS_S3_BUCKET,
|
||||||
|
Key: s3Key,
|
||||||
|
Body: buffer,
|
||||||
|
ContentType: 'image/jpeg',
|
||||||
|
ACL: 'public-read',
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Construct the public URL (path-style to avoid SSL issues with dotted bucket names)
|
||||||
|
return `${process.env.AWS_S3_ENDPOINT}/${process.env.AWS_S3_BUCKET}/${s3Key}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async #createContainer(text, imageUrl, accessToken) {
|
||||||
|
let userId = process.env.THREADS_USER_ID;
|
||||||
|
let params = new URLSearchParams({
|
||||||
|
media_type: 'IMAGE',
|
||||||
|
image_url: imageUrl,
|
||||||
|
text,
|
||||||
|
access_token: accessToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = await fetch(`${this.#baseUrl}/${userId}/threads`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: params,
|
||||||
|
});
|
||||||
|
|
||||||
|
let data = await response.json();
|
||||||
|
|
||||||
|
if (data.error) {
|
||||||
|
throw new Error(`${data.error.message} (type: ${data.error.type}, code: ${data.error.code}, fbtrace_id: ${data.error.fbtrace_id})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data.id) {
|
||||||
|
throw new Error(`Unexpected response: ${JSON.stringify(data)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
async #waitForContainer(containerId, accessToken) {
|
||||||
|
// Poll container status until it's ready (or timeout after 60s)
|
||||||
|
let maxAttempts = 12;
|
||||||
|
|
||||||
|
for (let i = 0; i < maxAttempts; i++) {
|
||||||
|
let params = new URLSearchParams({
|
||||||
|
fields: 'status',
|
||||||
|
access_token: accessToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = await fetch(`${this.#baseUrl}/${containerId}?${params}`);
|
||||||
|
let data = await response.json();
|
||||||
|
|
||||||
|
if (data.status === 'FINISHED') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.status === 'ERROR') {
|
||||||
|
throw new Error(`Container processing failed: ${JSON.stringify(data)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait 5 seconds before checking again
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Container processing timed out');
|
||||||
|
}
|
||||||
|
|
||||||
|
async #publish(containerId, accessToken) {
|
||||||
|
let userId = process.env.THREADS_USER_ID;
|
||||||
|
let params = new URLSearchParams({
|
||||||
|
creation_id: containerId,
|
||||||
|
access_token: accessToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = await fetch(`${this.#baseUrl}/${userId}/threads_publish`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: params,
|
||||||
|
});
|
||||||
|
|
||||||
|
let data = await response.json();
|
||||||
|
|
||||||
|
if (data.error) {
|
||||||
|
throw new Error(data.error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ import EggstraWorkStatus from './generators/EggstraWorkStatus.mjs';
|
||||||
import EggstraWorkUpcomingStatus from './generators/EggstraWorkUpcomingStatus.mjs';
|
import EggstraWorkUpcomingStatus from './generators/EggstraWorkUpcomingStatus.mjs';
|
||||||
import BlueskyClient from './clients/BlueskyClient.mjs';
|
import BlueskyClient from './clients/BlueskyClient.mjs';
|
||||||
import ChallengeStatus from './generators/ChallengeStatus.mjs';
|
import ChallengeStatus from './generators/ChallengeStatus.mjs';
|
||||||
// import ThreadsClient from './clients/ThreadsClient.mjs';
|
import ThreadsClient from './clients/ThreadsClient.mjs';
|
||||||
|
|
||||||
function defaultStatusGenerators() {
|
function defaultStatusGenerators() {
|
||||||
return [
|
return [
|
||||||
|
|
@ -46,7 +46,7 @@ function defaultClients() {
|
||||||
// new TwitterClient,
|
// new TwitterClient,
|
||||||
new MastodonClient,
|
new MastodonClient,
|
||||||
new BlueskyClient,
|
new BlueskyClient,
|
||||||
// new ThreadsClient,
|
new ThreadsClient,
|
||||||
new ImageWriter,
|
new ImageWriter,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
135
package-lock.json
generated
135
package-lock.json
generated
|
|
@ -30,7 +30,6 @@
|
||||||
"s3-sync-client": "^4.3.1",
|
"s3-sync-client": "^4.3.1",
|
||||||
"sharp": "^0.34.5",
|
"sharp": "^0.34.5",
|
||||||
"sirv": "^3.0.2",
|
"sirv": "^3.0.2",
|
||||||
"threads-api": "^1.4.0",
|
|
||||||
"twitter-api-v2": "^1.29.0",
|
"twitter-api-v2": "^1.29.0",
|
||||||
"vue": "^3.5.28",
|
"vue": "^3.5.28",
|
||||||
"vue-i18n": "^11.2.8",
|
"vue-i18n": "^11.2.8",
|
||||||
|
|
@ -5819,11 +5818,6 @@
|
||||||
"url": "https://github.com/sponsors/sxzz"
|
"url": "https://github.com/sponsors/sxzz"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/asynckit": {
|
|
||||||
"version": "0.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
|
||||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
|
||||||
},
|
|
||||||
"node_modules/await-lock": {
|
"node_modules/await-lock": {
|
||||||
"version": "2.2.2",
|
"version": "2.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/await-lock/-/await-lock-2.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/await-lock/-/await-lock-2.2.2.tgz",
|
||||||
|
|
@ -6349,17 +6343,6 @@
|
||||||
"node": ">=0.1.90"
|
"node": ">=0.1.90"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/combined-stream": {
|
|
||||||
"version": "1.0.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
|
||||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
|
||||||
"dependencies": {
|
|
||||||
"delayed-stream": "~1.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/comment-parser": {
|
"node_modules/comment-parser": {
|
||||||
"version": "1.4.5",
|
"version": "1.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.5.tgz",
|
||||||
|
|
@ -6553,14 +6536,6 @@
|
||||||
"node": ">= 14"
|
"node": ">= 14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/delayed-stream": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/depd": {
|
"node_modules/depd": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||||
|
|
@ -6819,21 +6794,6 @@
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/es-set-tostringtag": {
|
|
||||||
"version": "2.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
|
||||||
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"es-errors": "^1.3.0",
|
|
||||||
"get-intrinsic": "^1.2.6",
|
|
||||||
"has-tostringtag": "^1.0.2",
|
|
||||||
"hasown": "^2.0.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/esbuild": {
|
"node_modules/esbuild": {
|
||||||
"version": "0.25.12",
|
"version": "0.25.12",
|
||||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
|
||||||
|
|
@ -7551,41 +7511,6 @@
|
||||||
"integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
|
"integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/follow-redirects": {
|
|
||||||
"version": "1.15.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
|
|
||||||
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "individual",
|
|
||||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"debug": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/form-data": {
|
|
||||||
"version": "4.0.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
|
|
||||||
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"asynckit": "^0.4.0",
|
|
||||||
"combined-stream": "^1.0.8",
|
|
||||||
"es-set-tostringtag": "^2.1.0",
|
|
||||||
"hasown": "^2.0.2",
|
|
||||||
"mime-types": "^2.1.12"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/forwarded": {
|
"node_modules/forwarded": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||||
|
|
@ -7847,20 +7772,6 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/has-tostringtag": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
|
||||||
"dependencies": {
|
|
||||||
"has-symbols": "^1.0.3"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/hasown": {
|
"node_modules/hasown": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||||
|
|
@ -8861,14 +8772,6 @@
|
||||||
"integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==",
|
"integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/mrmime": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
|
@ -11030,44 +10933,6 @@
|
||||||
"b4a": "^1.6.4"
|
"b4a": "^1.6.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/threads-api": {
|
|
||||||
"version": "1.6.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/threads-api/-/threads-api-1.6.3.tgz",
|
|
||||||
"integrity": "sha512-5ytREfUM4IVBn2vgxnaet/iyF8ux8Wc6+liV+WaJ/nHICoSdPjk9Iecpj7Yes7OpE0dhRtfokZy1z4h1d3Bfqw==",
|
|
||||||
"dependencies": {
|
|
||||||
"axios": "^1.4.0",
|
|
||||||
"mrmime": "^1.0.1",
|
|
||||||
"uuid": "^9.0.0"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"threads-api": "bin/cli.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/threads-api/node_modules/axios": {
|
|
||||||
"version": "1.6.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz",
|
|
||||||
"integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"follow-redirects": "^1.15.6",
|
|
||||||
"form-data": "^4.0.0",
|
|
||||||
"proxy-from-env": "^1.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/threads-api/node_modules/uuid": {
|
|
||||||
"version": "9.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
|
|
||||||
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
|
|
||||||
"funding": [
|
|
||||||
"https://github.com/sponsors/broofa",
|
|
||||||
"https://github.com/sponsors/ctavan"
|
|
||||||
],
|
|
||||||
"bin": {
|
|
||||||
"uuid": "dist/bin/uuid"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/tinybench": {
|
"node_modules/tinybench": {
|
||||||
"version": "2.9.0",
|
"version": "2.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,6 @@
|
||||||
"s3-sync-client": "^4.3.1",
|
"s3-sync-client": "^4.3.1",
|
||||||
"sharp": "^0.34.5",
|
"sharp": "^0.34.5",
|
||||||
"sirv": "^3.0.2",
|
"sirv": "^3.0.2",
|
||||||
"threads-api": "^1.4.0",
|
|
||||||
"twitter-api-v2": "^1.29.0",
|
"twitter-api-v2": "^1.29.0",
|
||||||
"vue": "^3.5.28",
|
"vue": "^3.5.28",
|
||||||
"vue-i18n": "^11.2.8",
|
"vue-i18n": "^11.2.8",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user