From 7b5642c881b414c8ef43db35ce7b0be95d9e154b Mon Sep 17 00:00:00 2001 From: Naomi Carrigan Date: Wed, 13 Aug 2025 18:27:46 -0700 Subject: [PATCH] feat: initial prototype --- README.md | 18 ++------- prod.env | 2 + src/config/reminders.ts | 49 +++++++++++++++++++++++ src/index.ts | 24 ++++++++++- src/modules/checkAltText.ts | 34 ++++++++++++++++ src/server/serve.ts | 79 +++++++++++++++++++++++++++++++++++++ src/utils/errorHandler.ts | 29 ++++++++++++++ src/utils/getRandomValue.ts | 17 ++++++++ src/utils/logger.ts | 12 ++++++ 9 files changed, 249 insertions(+), 15 deletions(-) create mode 100644 prod.env create mode 100644 src/config/reminders.ts create mode 100644 src/modules/checkAltText.ts create mode 100644 src/server/serve.ts create mode 100644 src/utils/errorHandler.ts create mode 100644 src/utils/getRandomValue.ts create mode 100644 src/utils/logger.ts diff --git a/README.md b/README.md index 47a840e..02b4ee8 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,10 @@ -# New Repository Template +# Altaria -This template contains all of our basic files for a new GitHub repository. There is also a handy workflow that will create an issue on a new repository made from this template, with a checklist for the steps we usually take in setting up a new repository. - -If you're starting a Node.JS project with TypeScript, we have a [specific template](https://github.com/naomi-lgbt/nodejs-typescript-template) for that purpose. - -## Readme - -Delete all of the above text (including this line), and uncomment the below text to use our standard readme template. - - +We may be contacted through our [Chat Server](http://chat.nhcarrigan.com) or via email at `contact@nhcarrigan.com`. diff --git a/prod.env b/prod.env new file mode 100644 index 0000000..64be8c7 --- /dev/null +++ b/prod.env @@ -0,0 +1,2 @@ +LOG_TOKEN="op://Environment Variables - Naomi/Alert Server/api_auth" +BOT_TOKEN="op://Environment Variables - Naomi/Altaria/bot token" \ No newline at end of file diff --git a/src/config/reminders.ts b/src/config/reminders.ts new file mode 100644 index 0000000..f8f276a --- /dev/null +++ b/src/config/reminders.ts @@ -0,0 +1,49 @@ +/** + * @copyright NHCarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ + +/* eslint-disable stylistic/max-len -- there will be some long strings in here. */ + +export const reminders = [ + "Psst~ I noticed your image doesn’t have alt-text! Could you please add one so everyone can enjoy it? 🌸", + "UwU~ don’t forget to add alt-text so our visually impaired friends can experience your post too! ✨", + "Heehee~ your image is lovely, but it would be even better with alt-text so everyone can appreciate it~ 💖", + "Altaria-chan here! Please add a short description for your image so nobody feels left out~ 🌈", + "Hmm… I think you forgot your alt-text! Could you add one so the whole server can enjoy your upload? 💕", + "Eep~ your post is missing alt-text! Let’s make it extra accessible together, okay? 🌟", + "Nyah~ alt-text makes sure *everyone* can experience your image! Could you add some? 🐾", + "Just a gentle reminder, sweetie~ images shine brighter when they have alt-text~ ✨", + "Altaria-chan believes in inclusive posting! Could you add a description to your image, pwease? 💌", + "Hehe~ let’s make your post purrfect by adding some alt-text so everyone can enjoy it~ 🐱", + "Ahem~ Altaria reporting in! Your image could use a sprinkle of alt-text magic~ 🪄", + "Oh! I think your picture is missing its story~ could you add some alt-text so everyone can see it? 📜", + "Mmm~ adding alt-text is like adding seasoning to a meal—it makes it better for everyone! 🍲", + "Kyaa~ your post is cute, but with alt-text it can be *inclusive* too! 💗", + "Tehee~ accessibility is love~ could you add some alt-text so all can share in it? 🌷", + "Poyo~ alt-text makes sure no one feels left out of the fun! Can you add some? 🥺", + "O-oh! You forgot your alt-text… w-wanna fix that together? 💞", + "Mofu mofu~ adding alt-text makes your post extra snuggly for everyone~ 🐑", + "Your art/photo deserves to be enjoyed by everyone~ let’s give it some alt-text magic! ✨", + "Yatta~ we can make your post perfect with just one thing… alt-text! 💎", + "Altaria thinks alt-text is the secret ingredient for kindness online~ 🍪", + "Could you help me out by adding alt-text to that image, sweetie? 💖", + "Ooh~ I can’t read images without alt-text… could you describe it for me? 📚", + "Nyaa~ alt-text helps make our community pawsitively inclusive! 🐾", + "Adding alt-text is like giving your image a warm hug~ please add some~ 🤗", + "Mmm! With alt-text, your post will be as beautiful in words as it is in sight~ 🌺", + "Altaria’s tip of the day~ alt-text helps everyone experience the joy in your post! 🌞", + "Could you add alt-text for me? I promise it’ll make your post sparkle~ ✨", + "Don’t forget, cutie~ alt-text is how we share the beauty of our images with *everyone*~ 💌", + "Adding alt-text shows you care~ could you add some to your upload? 🌹", + "Altaria’s little birdy heart sings when people use alt-text~ 🐦", + "Let’s make sure no one misses out! Add some alt-text, please? 🥰", + "Huggles~ alt-text makes the internet cozier for everyone~ 🧸", + "Oopsie~ your post is missing alt-text! Wanna fix it together? 💕", + "Mmm~ words paint pictures too~ let’s add some to your image~ 🎨", + "Pwease add alt-text so everyone can feel included, nya~ 🐱", + "Altaria believes accessibility is beautiful~ can you add some alt-text to help? 💖", + "Don’t forget to give your image its voice with alt-text~ 🎤", + "The best posts are the ones *everyone* can enjoy~ could you add alt-text? 🌼", +]; diff --git a/src/index.ts b/src/index.ts index 9e9295c..da3a880 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,4 +4,26 @@ * @author Naomi Carrigan */ -process.exit(0); +import { Client, GatewayIntentBits, Events } from "discord.js"; +import { checkAltText } from "./modules/checkAltText.js"; +import { instantiateServer } from "./server/serve.js"; +import { logger } from "./utils/logger.js"; + +const client = new Client({ + intents: [ + GatewayIntentBits.Guilds, + GatewayIntentBits.GuildMessages, + GatewayIntentBits.MessageContent, + ], +}); + +client.once(Events.ClientReady, () => { + void logger.log("info", `Logged in as ${client.user?.username ?? "unknown user"}`); +}); + +client.on(Events.MessageCreate, (message) => { + void checkAltText(message); +}); + +await client.login(process.env.BOT_TOKEN); +instantiateServer(); diff --git a/src/modules/checkAltText.ts b/src/modules/checkAltText.ts new file mode 100644 index 0000000..25537d7 --- /dev/null +++ b/src/modules/checkAltText.ts @@ -0,0 +1,34 @@ +/** + * @copyright NHCarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ + +import { reminders } from "../config/reminders.js"; +import { getRandomValue } from "../utils/getRandomValue.js"; +import type { Message } from "discord.js"; + +/** + * Checks if a message has image attachments, and + * if those attachments have alt text. + * @param message - The message payload from Discord. + */ +export const checkAltText = async(message: Message): Promise => { + if (message.attachments.size <= 0) { + return; + } + + const images = message.attachments.filter((attachment) => { + return attachment.contentType?.startsWith("image/"); + }); + const noDescription = images.filter((image) => { + return image.description === null; + }); + + if (noDescription.size > 0) { + const reminder = getRandomValue(reminders); + await message.reply({ + content: reminder, + }); + } +}; diff --git a/src/server/serve.ts b/src/server/serve.ts new file mode 100644 index 0000000..5e8dc36 --- /dev/null +++ b/src/server/serve.ts @@ -0,0 +1,79 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ + +import fastify from "fastify"; +import { logger } from "../utils/logger.js"; + +const html = ` + + + Altaria + + + + + + +
+

Liora

+ Altaria +
+

Discord bot that gives you a gentle reminder when you forget to attach alt-text to an image.

+ +
+
+

Links

+

+ + Source Code + +

+

+ + Documentation + +

+

+ + Support + +

+
+
+ +`; + +/** + * Starts up a web server for health monitoring. + */ +export const instantiateServer = (): void => { + try { + const server = fastify({ + logger: false, + }); + + server.get("/", (_request, response) => { + response.header("Content-Type", "text/html"); + response.send(html); + }); + + server.listen({ port: 6022 }, (error) => { + if (error) { + void logger.error("instantiate server", error); + return; + } + void logger.log("debug", "Server listening on port 6022."); + }); + } catch (error) { + if (error instanceof Error) { + void logger.error("instantiate server", error); + return; + } + void logger.error("instantiate server", new Error(String(error))); + } +}; diff --git a/src/utils/errorHandler.ts b/src/utils/errorHandler.ts new file mode 100644 index 0000000..7828e0c --- /dev/null +++ b/src/utils/errorHandler.ts @@ -0,0 +1,29 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ + +import crypto from "node:crypto"; +import { logger } from "./logger.js"; + +/** + * Generates a UUID for an error, sends the error to the logger, + * and returns the UUID to be shared with the user. + * @param error - The error to log. + * @param context - The context in which the error occurred. + * @returns A UUID string assigned to the error. + */ +export const errorHandler = async( + error: unknown, + context: string, +): Promise => { + const id = crypto.randomUUID(); + await logger.error( + `${context} - Error ID: ${id}`, + error instanceof Error + ? error + : new Error(String(error)), + ); + return id; +}; diff --git a/src/utils/getRandomValue.ts b/src/utils/getRandomValue.ts new file mode 100644 index 0000000..bd9574c --- /dev/null +++ b/src/utils/getRandomValue.ts @@ -0,0 +1,17 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ + +/** + * Returns a random value from the provided array. + * @template T - The type of the elements in the array. + * @param array - The array to select a random value from. + * @returns A random value from the array. + */ +export const getRandomValue = (array: Array): T => { + const randomIndex = Math.floor(Math.random() * array.length); + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- We know the array is not empty. + return array[randomIndex] as T; +}; diff --git a/src/utils/logger.ts b/src/utils/logger.ts new file mode 100644 index 0000000..7e5ba03 --- /dev/null +++ b/src/utils/logger.ts @@ -0,0 +1,12 @@ +/** + * @copyright NHCarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ + +import { Logger } from "@nhcarrigan/logger"; + +export const logger = new Logger( + "Altaria", + process.env.LOG_TOKEN ?? "", +);