generated from nhcarrigan/template
This commit is contained in:
Generated
+8
@@ -133,6 +133,9 @@ importers:
|
|||||||
gray-matter:
|
gray-matter:
|
||||||
specifier: 4.0.3
|
specifier: 4.0.3
|
||||||
version: 4.0.3
|
version: 4.0.3
|
||||||
|
twitter-api-v2:
|
||||||
|
specifier: 1.24.0
|
||||||
|
version: 1.24.0
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: 24.0.10
|
specifier: 24.0.10
|
||||||
@@ -4589,6 +4592,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-kc8ZibdRcuWUG1pbYSBFWqmIjynlD8Lp7IB6U3vIzvOv9VG+6Sp8bzyeBWE3Oi8XV5KsQrznyRTBPvrf99E4mA==}
|
resolution: {integrity: sha512-kc8ZibdRcuWUG1pbYSBFWqmIjynlD8Lp7IB6U3vIzvOv9VG+6Sp8bzyeBWE3Oi8XV5KsQrznyRTBPvrf99E4mA==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
twitter-api-v2@1.24.0:
|
||||||
|
resolution: {integrity: sha512-RDEiuNwnFirvf4c5f1sysgg0rfMQgekXgKt+/UdbNu+Bs5bJ1VbXkqKzdd2a2lPMlDVDbdGUoe2pOd4n25fFVQ==}
|
||||||
|
|
||||||
type-check@0.4.0:
|
type-check@0.4.0:
|
||||||
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
|
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
|
||||||
engines: {node: '>= 0.8.0'}
|
engines: {node: '>= 0.8.0'}
|
||||||
@@ -10097,6 +10103,8 @@ snapshots:
|
|||||||
turbo-windows-64: 2.5.4
|
turbo-windows-64: 2.5.4
|
||||||
turbo-windows-arm64: 2.5.4
|
turbo-windows-arm64: 2.5.4
|
||||||
|
|
||||||
|
twitter-api-v2@1.24.0: {}
|
||||||
|
|
||||||
type-check@0.4.0:
|
type-check@0.4.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
prelude-ls: 1.2.1
|
prelude-ls: 1.2.1
|
||||||
|
|||||||
+2
-1
@@ -22,7 +22,8 @@
|
|||||||
"@nhcarrigan/logger": "1.0.0",
|
"@nhcarrigan/logger": "1.0.0",
|
||||||
"@prisma/client": "6.11.1",
|
"@prisma/client": "6.11.1",
|
||||||
"fastify": "5.4.0",
|
"fastify": "5.4.0",
|
||||||
"gray-matter": "4.0.3"
|
"gray-matter": "4.0.3",
|
||||||
|
"twitter-api-v2": "1.24.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "24.0.10",
|
"@types/node": "24.0.10",
|
||||||
|
|||||||
+6
-1
@@ -8,4 +8,9 @@ REDDIT_CLIENT_SECRET="op://Environment Variables - Naomi/Hikari/reddit_client_se
|
|||||||
REDDIT_PASSWORD="op://Environment Variables - Naomi/Hikari/reddit_password"
|
REDDIT_PASSWORD="op://Environment Variables - Naomi/Hikari/reddit_password"
|
||||||
REDDIT_USERNAME="op://Environment Variables - Naomi/Hikari/reddit_username"
|
REDDIT_USERNAME="op://Environment Variables - Naomi/Hikari/reddit_username"
|
||||||
BSKY_APP_PASSWORD="op://Environment Variables - Naomi/Hikari/bsky_password"
|
BSKY_APP_PASSWORD="op://Environment Variables - Naomi/Hikari/bsky_password"
|
||||||
ANTHROPIC_KEY="op://Environment Variables - Naomi/Hikari/anthropic_key"
|
ANTHROPIC_KEY="op://Environment Variables - Naomi/Hikari/anthropic_key"
|
||||||
|
TWITTER_TOKEN="op://Environment Variables - Naomi/Hikari/twitter_access_token"
|
||||||
|
TWITTER_SECRET="op://Environment Variables - Naomi/Hikari/twitter_access_secret"
|
||||||
|
TWITTER_CONSUMER_KEY="op://Environment Variables - Naomi/Hikari/twitter_consumer_key"
|
||||||
|
TWITTER_CONSUMER_SECRET="op://Environment Variables - Naomi/Hikari/twitter_consumer_secret"
|
||||||
|
TWITTER_BEARER_TOKEN="op://Environment Variables - Naomi/Hikari/twitter_bearer_token"
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { TwitterApi } from "twitter-api-v2";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forwards an announcement to our Twitter account.
|
||||||
|
* @param content - The main body of the announcement.
|
||||||
|
* @returns A message indicating the success or failure of the operation.
|
||||||
|
*/
|
||||||
|
export const announceOnTwitter = async(content: string): Promise<string> => {
|
||||||
|
if (
|
||||||
|
process.env.TWITTER_CONSUMER_KEY === undefined
|
||||||
|
|| process.env.TWITTER_CONSUMER_SECRET === undefined
|
||||||
|
|| process.env.TWITTER_TOKEN === undefined
|
||||||
|
|| process.env.TWITTER_SECRET === undefined
|
||||||
|
) {
|
||||||
|
return "Twitter credentials are not set.";
|
||||||
|
}
|
||||||
|
const twitterClient = new TwitterApi({
|
||||||
|
accessSecret: process.env.TWITTER_SECRET,
|
||||||
|
accessToken: process.env.TWITTER_TOKEN,
|
||||||
|
appKey: process.env.TWITTER_CONSUMER_KEY,
|
||||||
|
appSecret: process.env.TWITTER_CONSUMER_SECRET,
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await twitterClient.v2.
|
||||||
|
tweet(content).
|
||||||
|
catch((error: unknown) => {
|
||||||
|
return error instanceof Error
|
||||||
|
? error.message
|
||||||
|
: String(error);
|
||||||
|
});
|
||||||
|
if (typeof result === "string") {
|
||||||
|
return `Failed to send message to Twitter. ${result}`;
|
||||||
|
}
|
||||||
|
return "Successfully sent message to Twitter.";
|
||||||
|
};
|
||||||
@@ -4,11 +4,12 @@
|
|||||||
* @author Naomi Carrigan
|
* @author Naomi Carrigan
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention -- 'Tis a class.
|
||||||
import Anthropic from "@anthropic-ai/sdk";
|
import Anthropic from "@anthropic-ai/sdk";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Summarises an announcement using AI, to condense the content for platforms like Bluesky and Twitter.
|
* Summarises an announcement using AI, to condense the content for platforms like Bluesky and Twitter.
|
||||||
* @param title
|
* @param title - The title of the announcement.
|
||||||
* @param content - The main body of the announcement.
|
* @param content - The main body of the announcement.
|
||||||
* @returns A message indicating the success or failure of the operation.
|
* @returns A message indicating the success or failure of the operation.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { announceOnBluesky } from "../modules/announceOnBluesky.js";
|
|||||||
import { announceOnDiscord } from "../modules/announceOnDiscord.js";
|
import { announceOnDiscord } from "../modules/announceOnDiscord.js";
|
||||||
import { announceOnForum } from "../modules/announceOnForum.js";
|
import { announceOnForum } from "../modules/announceOnForum.js";
|
||||||
import { announceOnReddit } from "../modules/announceOnReddit.js";
|
import { announceOnReddit } from "../modules/announceOnReddit.js";
|
||||||
|
import { announceOnTwitter } from "../modules/announceOnTwitter.js";
|
||||||
import { getIpFromRequest } from "../modules/getIpFromRequest.js";
|
import { getIpFromRequest } from "../modules/getIpFromRequest.js";
|
||||||
import { summarisePost } from "../modules/summarisePost.js";
|
import { summarisePost } from "../modules/summarisePost.js";
|
||||||
import type { FastifyPluginAsync } from "fastify";
|
import type { FastifyPluginAsync } from "fastify";
|
||||||
@@ -109,18 +110,19 @@ export const announcementRoutes: FastifyPluginAsync = async(server) => {
|
|||||||
const summary = await summarisePost(title, content);
|
const summary = await summarisePost(title, content);
|
||||||
if (summary === null) {
|
if (summary === null) {
|
||||||
return await reply.status(201).send({
|
return await reply.status(201).send({
|
||||||
message: `Announcement processed. Discord: ${discord}, Forum: ${forum}, Reddit: ${reddit}, Bluesky: Skipped (AI summarisation failed).`,
|
message: `Announcement processed. Discord: ${discord}, Forum: ${forum}, Reddit: ${reddit}, Bluesky: Skipped (AI summarisation failed), Twitter: Skipped (AI summarisation failed).`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (summary.length > 280) {
|
if (summary.length > 280) {
|
||||||
return await reply.status(201).send({
|
return await reply.status(201).send({
|
||||||
message: `Announcement processed. Discord: ${discord}, Forum: ${forum}, Reddit: ${reddit}, Bluesky: Skipped (AI summary too long).`,
|
message: `Announcement processed. Discord: ${discord}, Forum: ${forum}, Reddit: ${reddit}, Bluesky: Skipped (AI summary too long), Twitter: Skipped (AI summary too long).`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const bluesky = await announceOnBluesky(summary);
|
const bluesky = await announceOnBluesky(summary);
|
||||||
|
const twitter = await announceOnTwitter(summary);
|
||||||
return await reply.status(201).send({
|
return await reply.status(201).send({
|
||||||
message: `Announcement processed. Discord: ${discord}, Forum: ${forum}, Reddit: ${reddit}, Bluesky: ${bluesky}`,
|
message: `Announcement processed. Discord: ${discord}, Forum: ${forum}, Reddit: ${reddit}, Bluesky: ${bluesky}, Twitter: ${twitter}`,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user