/** * @copyright NHCarrigan * @license Naomi's Public License * @author Naomi Carrigan */ import { DiscordAnalytics } from "@nhcarrigan/discord-analytics"; import { Client, GatewayIntentBits, Events, Partials, MessageFlags, } from "discord.js"; import { scheduleJob } from "node-schedule"; import { App } from "octokit"; import { ids } from "./config/ids.js"; import { handleMessageCreate } from "./events/handleMessageCreate.js"; import { cacheData } from "./modules/cacheData.js"; import { checkRetroAchievements } from "./modules/checkAchievements.js"; import { getForumTagId } from "./modules/getForumTagId.js"; import { logMenteeJoin } from "./modules/logMenteeJoin.js"; import { logMenteeLeave } from "./modules/logMenteeLeave.js"; import { postFreeCodeCampNews, postHackerNews } from "./modules/postNews.js"; import { postProgressReminders } from "./modules/postProgressReminders.js"; import { processMentorshipRole } from "./modules/processMentorshipRole.js"; import { processUserGuildTag } from "./modules/processUserGuildTag.js"; import { respondToDm } from "./modules/respondToDm.js"; import { instantiateServer } from "./server/serve.js"; 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 ) { 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"), }); 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"}`, ); 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(), }; 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); }); amari.discord.on(Events.MessageCreate, (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 }); 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 ], }); }); 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 ]); }); amari.discord.on(Events.UserUpdate, (_oldUser, updatedUser) => { void processUserGuildTag(amari, updatedUser); }); amari.discord.on(Events.GuildMemberUpdate, (oldMember, updatedMember) => { void processMentorshipRole(amari, oldMember, updatedMember); }); amari.discord.on(Events.GuildMemberAdd, (member) => { void logMenteeJoin(amari, member); }); amari.discord.on(Events.GuildMemberRemove, (member) => { void logMenteeLeave(amari, member); }); await amari.discord.login(process.env.BOT_TOKEN); instantiateServer(amari);