diff --git a/src/commands/forwardToOwner.ts b/src/commands/forwardToOwner.ts index 2166985..5c8e73d 100644 --- a/src/commands/forwardToOwner.ts +++ b/src/commands/forwardToOwner.ts @@ -1,3 +1,9 @@ +/** + * @copyright NHCarrigan + * @license Naomi's Public License + * @author Teklu Abayneh + */ + import { ContextMenuCommandBuilder, ApplicationCommandType, @@ -9,13 +15,14 @@ import { ButtonStyle, } from "discord.js"; import { ids } from "../config/ids.js"; +import { logger } from "../utils/logger.js"; export const forwardOwnerDM = { - data: new ContextMenuCommandBuilder() - .setName("Forward to Naomi") - .setType(ApplicationCommandType.Message), + data: new ContextMenuCommandBuilder().setName("Forward to Naomi"). + setType(ApplicationCommandType.Message), - async execute(interaction: MessageContextMenuCommandInteraction) { + async execute(interaction: MessageContextMenuCommandInteraction): + Promise { await interaction.deferReply({ ephemeral: true }); if (interaction.user.id !== ids.users.naomi) { @@ -26,47 +33,58 @@ export const forwardOwnerDM = { const message = interaction.targetMessage; if (message.author.id === ids.users.naomi) { - await interaction.editReply("No need to forward your own message to yourself 😄"); + await interaction.editReply( + "No need to forward your own message to yourself 😄", + ); return; } try { const naomi = await interaction.client.users.fetch(ids.users.naomi); + const forwardedEmbed = new EmbedBuilder(). + setColor(0x58_65_F2). + setTitle(`Message from ${String(message.author.tag)}!`). + setDescription( + `${(message.attachments.size > 0 + ? `**Attachments:** ${String(message.attachments.size)} + file(s)\n\n` + : "\n") + + (message.embeds.length > 0 + ? `**Embeds:** ${String(message.embeds.length)}\n\n` + : "")} + \n${message.content !== "" || message.content !== null + ? message.content + : "*[No text content]*"}\n\n`, + ); - const forwardedEmbed = new EmbedBuilder() - .setColor(0x5865F2) - .setTitle(`Message from ${message.author.tag}!`) - .setDescription( - (message.attachments.size > 0 ? `**Attachments:** ${message.attachments.size} file(s)\n\n` : "\n") + - (message.embeds.length > 0 ? `**Embeds:** ${message.embeds.length}\n\n` : "") + "\n" + - (message.content || "*[No text content]*") + "\n\n"); + const viewButton = new ButtonBuilder(). + setLabel("View Message"). + setURL(message.url). + setStyle(ButtonStyle.Link); -const viewButton = new ButtonBuilder() - .setLabel("View Message") - .setURL(message.url) - .setStyle(ButtonStyle.Link); - -const row = new ActionRowBuilder() - .addComponents(viewButton); - await naomi.send({ - embeds: [forwardedEmbed], - files: message.attachments.map((att) => att.url), - components: [row], + const row = new ActionRowBuilder().addComponents( + viewButton, + ); + await naomi.send({ + components: [ row ], + embeds: [ forwardedEmbed ], + files: message.attachments.map((att) => { + return att.url; + }), }); await interaction.editReply({ content: "✅ Forwarded to your DMs!", }); } catch (error) { - console.error("Failed to forward:", error); - let replyText = "❌ Failed to forward message."; - - if (error instanceof DiscordAPIError && error.code === 50007) { - replyText += " (Naomi's DMs might be closed)"; + if (error instanceof DiscordAPIError && error.code === 50_007) { + replyText = `${replyText} (Naomi's DMs might be closed)`; } - await interaction.editReply(replyText); + if (error instanceof Error) { + await logger.error("operation", error); + } } }, -}; \ No newline at end of file +}; diff --git a/src/config/ids.ts b/src/config/ids.ts index 35fca11..174ae5f 100644 --- a/src/config/ids.ts +++ b/src/config/ids.ts @@ -70,6 +70,6 @@ export const ids = { amari: "1406431359345496255", naomi: "465650873650118659", nhcarrigan: "1382837581649150104", - teklu:"1381735115163570198", + teklu: "1381735115163570198", }, -}; \ No newline at end of file +}; diff --git a/src/index.ts b/src/index.ts index 84cac00..03243d9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,11 +6,11 @@ import { DiscordAnalytics } from "@nhcarrigan/discord-analytics"; import { - Client, - GatewayIntentBits, - Events, - Partials, - MessageFlags, + Client, + GatewayIntentBits, + Events, + Partials, + MessageFlags, } from "discord.js"; import { scheduleJob } from "node-schedule"; import { App } from "octokit"; @@ -32,148 +32,150 @@ import { logger } from "./utils/logger.js"; import type { Amari } from "./interfaces/amari.js"; if ( - process.env.GH_CLIENT_ID === undefined - || process.env.GH_PRIVATE_KEY === undefined + process.env.GH_CLIENT_ID === undefined + || process.env.GH_PRIVATE_KEY === undefined ) { - throw new Error("Cannot initialise GitHub!"); + throw new Error("Cannot initialise GitHub!"); } const githubApp = new App({ - appId: process.env.GH_CLIENT_ID, - privateKey: process.env.GH_PRIVATE_KEY.replaceAll("\\n", "\n"), + appId: process.env.GH_CLIENT_ID, + privateKey: process.env.GH_PRIVATE_KEY.replaceAll("\\n", "\n"), }); const octokit = await githubApp.getInstallationOctokit(83_119_105); const { data } = await octokit.rest.apps.getAuthenticated(); await logger.log( - "debug", - `Authenticated to GitHub as ${data?.name ?? "unknown"}`, + "debug", + `Authenticated to GitHub as ${data?.name ?? "unknown"}`, ); const amari: Amari = { - discord: new Client({ - intents: [ - GatewayIntentBits.Guilds, - GatewayIntentBits.GuildMessages, - GatewayIntentBits.MessageContent, - GatewayIntentBits.GuildMembers, - GatewayIntentBits.DirectMessages, - ], - partials: [Partials.Channel], - }), - github: octokit, - lastRssItems: { - freeCodeCamp: null, - hackerNews: null, - }, - recentlyActiveChannels: new Set(), + discord: new Client({ + intents: [ + GatewayIntentBits.Guilds, + GatewayIntentBits.GuildMessages, + GatewayIntentBits.MessageContent, + GatewayIntentBits.GuildMembers, + GatewayIntentBits.DirectMessages, + ], + partials: [ Partials.Channel ], + }), + github: octokit, + lastRssItems: { + freeCodeCamp: null, + hackerNews: null, + }, + recentlyActiveChannels: new Set(), }; const analytics = new DiscordAnalytics(amari.discord, logger); amari.discord.once(Events.ClientReady, () => { - void logger.log( - "debug", - `Authenticated to Discord as ${amari.discord.user?.username ?? "unknown"}`, - ); - void cacheData(amari); - analytics.startCron(); - scheduleJob("post news", "0 * * * *", async () => { - await postFreeCodeCampNews(amari); - await postHackerNews(amari); - }); - scheduleJob("check guild tags", "0 0 * * *", async () => { - await logger.log("debug", "Auditing guild tags."); - await cacheData(amari); - }); - scheduleJob("post progress reminders", "0 9 * * 1-5", async () => { - await postProgressReminders(amari); - }); - setInterval(() => { - amari.recentlyActiveChannels = new Set(); - }, 10 * 60 * 1000); - setInterval(() => { - void checkRetroAchievements(amari); - }, 10 * 60 * 1000); + void logger.log( + "debug", + `Authenticated to Discord as ${amari.discord.user?.username ?? "unknown"}`, + ); + void cacheData(amari); + analytics.startCron(); + scheduleJob("post news", "0 * * * *", async() => { + await postFreeCodeCampNews(amari); + await postHackerNews(amari); + }); + scheduleJob("check guild tags", "0 0 * * *", async() => { + await logger.log("debug", "Auditing guild tags."); + await cacheData(amari); + }); + scheduleJob("post progress reminders", "0 9 * * 1-5", async() => { + await postProgressReminders(amari); + }); + setInterval( + () => { + amari.recentlyActiveChannels = new Set(); + }, + 10 * 60 * 1000, + ); + setInterval( + () => { + void checkRetroAchievements(amari); + }, + 10 * 60 * 1000, + ); }); amari.discord.on(Events.MessageCreate, (message) => { - if (!message.inGuild()) { - void respondToDm(amari, message); - return; - } - void handleMessageCreate(amari, message); + if (!message.inGuild()) { + void respondToDm(amari, message); + return; + } + void handleMessageCreate(amari, message); }); amari.discord.on(Events.InteractionCreate, (interaction) => { - void analytics.logGatewayEvent(Events.InteractionCreate, { ...interaction }); + void analytics.logGatewayEvent(Events.InteractionCreate, { ...interaction }); - if (interaction.isMessageContextMenuCommand() && interaction.commandName === "Forward to Naomi") { - void forwardOwnerDM.execute(interaction); - return + if ( + interaction.isMessageContextMenuCommand() + && interaction.commandName === "Forward to Naomi" + ) { + void forwardOwnerDM.execute(interaction); + return; + } + + if (interaction.isButton() && interaction.customId === "resolve") { + if (interaction.user.id !== ids.users.naomi) { + return void interaction.reply({ + content: "Who are you????", + flags: [ MessageFlags.Ephemeral ], + }); } - if (interaction.isButton() && interaction.customId === "resolve") { - if (interaction.user.id !== ids.users.naomi) { - return void interaction.reply({ - content: "Who are you????", - flags: [MessageFlags.Ephemeral], - }); - } - - return void interaction.message.delete(); - } - if (interaction.isAutocomplete()) { - return void interaction; - } - return void interaction.reply({ - content: "What?", - flags: [MessageFlags.Ephemeral], - }); + return void interaction.message.delete(); + } + if (interaction.isAutocomplete()) { + return void interaction; + } + return void interaction.reply({ + content: "What?", + flags: [ MessageFlags.Ephemeral ], + }); }); amari.discord.on(Events.ThreadCreate, (thread) => { - if (thread.parent?.isThreadOnly() !== true) { - return; - } - const { bugReports, communityFeedback, featureRequests, policyIdeation } - = ids.channels; - if ( - ![bugReports, - communityFeedback, - featureRequests, - policyIdeation].includes( - thread.parent.id, - ) - ) { - return; - } - const tagId = getForumTagId(thread.parent.id); - if (tagId === null) { - return; - } - void thread.setAppliedTags([tagId]); + if (thread.parent?.isThreadOnly() !== true) { + return; + } + const { bugReports, communityFeedback, featureRequests, policyIdeation } + = ids.channels; + if ( + ![ bugReports, communityFeedback, featureRequests, policyIdeation ].includes( + thread.parent.id, + ) + ) { + return; + } + const tagId = getForumTagId(thread.parent.id); + if (tagId === null) { + return; + } + void thread.setAppliedTags([ tagId ]); }); amari.discord.on(Events.UserUpdate, (_oldUser, updatedUser) => { - void processUserGuildTag(amari, updatedUser); + void processUserGuildTag(amari, updatedUser); }); amari.discord.on(Events.GuildMemberUpdate, (oldMember, updatedMember) => { - void processMentorshipRole(amari, oldMember, updatedMember); + void processMentorshipRole(amari, oldMember, updatedMember); }); amari.discord.on(Events.GuildMemberAdd, (member) => { - void logMenteeJoin(amari, member); + void logMenteeJoin(amari, member); }); amari.discord.on(Events.GuildMemberRemove, (member) => { - void logMenteeLeave(amari, member); + void logMenteeLeave(amari, member); }); await amari.discord.login(process.env.BOT_TOKEN); -amari.discord.on(Events.MessageCreate, (message) => { - const channelType = message.channel.type; - console.log(`📩 [${channelType}] ${message.author.tag}: ${message.content}`); -}); instantiateServer(amari); diff --git a/src/scripts/deploy-global.ts b/src/scripts/deployGlobal.ts similarity index 53% rename from src/scripts/deploy-global.ts rename to src/scripts/deployGlobal.ts index 0a4de4e..4445cfb 100644 --- a/src/scripts/deploy-global.ts +++ b/src/scripts/deployGlobal.ts @@ -1,10 +1,14 @@ +/** + * @copyright NHCarrigan + * @license Naomi's Public License + * @author Teklu Abayneh + */ + import { REST, Routes } from "discord.js"; import { forwardOwnerDM } from "../commands/forwardToOwner.js"; +import { logger } from "../utils/logger.js"; -const commands = [ - forwardOwnerDM.data.toJSON(), -]; - +const commands = [ forwardOwnerDM.data.toJSON() ]; const token = process.env.BOT_TOKEN; const clientId = process.env.GH_CLIENT_ID; @@ -16,17 +20,13 @@ if (clientId === undefined) { } const rest = new REST({ version: "10" }).setToken(token); -(async() => { +const requestCommand = async(): Promise => { try { - console.log("Registering GLOBAL context menu command... wait till appear everywhere."); - - await rest.put( - Routes.applicationCommands(clientId), - { body: commands }, - ); - - console.log("Global registration sent! then check right-click on a message → Apps."); + await rest.put(Routes.applicationCommands(clientId), { body: commands }); } catch (error) { - console.error("Registration failed:", error); + if (error instanceof Error) { + await logger.error("operation", error); + } } -})(); +}; +requestCommand();