From 3f89b7eb4fbdf4f8eb075b0fec883296d38bd076 Mon Sep 17 00:00:00 2001 From: Hikari Date: Tue, 3 Mar 2026 12:04:20 -0800 Subject: [PATCH] fix: replace deprecated ephemeral flag and add error handling across modules - Replace `ephemeral: true` with `flags: [ MessageFlags.Ephemeral ]` in all command files - Add try/catch with logger.error to logMenteeJoin, checkAchievements, processMentorshipRole - Extract handleIssueOpened and handlePrOpened helpers in processGitHubEvent to reduce complexity - Add logger.error calls in all newly introduced catch blocks --- src/commands/createIssue.ts | 2 +- src/commands/createTask.ts | 2 +- src/commands/forwardToOwner.ts | 2 +- src/commands/onboardMentee.ts | 2 +- src/modules/checkAchievements.ts | 51 ++++++++------ src/modules/logMenteeJoin.ts | 44 +++++++----- src/modules/processGitHubEvent.ts | 101 ++++++++++++++++++--------- src/modules/processMentorshipRole.ts | 40 ++++++----- 8 files changed, 149 insertions(+), 95 deletions(-) diff --git a/src/commands/createIssue.ts b/src/commands/createIssue.ts index be258ff..07d5c28 100644 --- a/src/commands/createIssue.ts +++ b/src/commands/createIssue.ts @@ -95,7 +95,7 @@ export const createIssue = async( const title = interaction.options.getString("title", true); const description = interaction.options.getString("description") ?? ""; - await interaction.deferReply({ ephemeral: true }); + await interaction.deferReply({ flags: [ MessageFlags.Ephemeral ] }); try { const augmentedBody = await generateIssueBody(description, title); diff --git a/src/commands/createTask.ts b/src/commands/createTask.ts index 3f5c53e..f4d4fe5 100644 --- a/src/commands/createTask.ts +++ b/src/commands/createTask.ts @@ -98,7 +98,7 @@ export const createTask = async( const description = interaction.options.getString("description") ?? ""; const priority = interaction.options.getInteger("priority") ?? 3; - await interaction.deferReply({ ephemeral: true }); + await interaction.deferReply({ flags: [ MessageFlags.Ephemeral ] }); try { const augmentedDesc = await generateTaskDescription(description, title); diff --git a/src/commands/forwardToOwner.ts b/src/commands/forwardToOwner.ts index b15683e..df9a635 100644 --- a/src/commands/forwardToOwner.ts +++ b/src/commands/forwardToOwner.ts @@ -20,7 +20,7 @@ import { logger } from "../utils/logger.js"; const forwardToOwner = async( interaction: MessageContextMenuCommandInteraction, ): Promise => { - await interaction.deferReply({ ephemeral: true }); + await interaction.deferReply({ flags: [ MessageFlags.Ephemeral ] }); if (interaction.user.id !== ids.users.naomi) { await interaction.editReply("❌ Only Naomi can use this command."); diff --git a/src/commands/onboardMentee.ts b/src/commands/onboardMentee.ts index bedd8ed..19a5ab5 100644 --- a/src/commands/onboardMentee.ts +++ b/src/commands/onboardMentee.ts @@ -67,7 +67,7 @@ export const onboardMentee = async( const githubUsername = interaction.options.getString("github_username", true); const menteeUser = interaction.options.getUser("mentee", true); - await interaction.deferReply({ ephemeral: true }); + await interaction.deferReply({ flags: [ MessageFlags.Ephemeral ] }); try { const repoUrl = await setupMenteeRepository(amari, githubUsername); diff --git a/src/modules/checkAchievements.ts b/src/modules/checkAchievements.ts index 2819cda..2a20225 100644 --- a/src/modules/checkAchievements.ts +++ b/src/modules/checkAchievements.ts @@ -23,6 +23,7 @@ import { type MessageActionRowComponentBuilder, } from "discord.js"; import { ids } from "../config/ids.js"; +import { logger } from "../utils/logger.js"; import type { Amari } from "../interfaces/amari.js"; const username = "naomilgbt"; @@ -91,28 +92,34 @@ export const checkRetroAchievements = async( return; } - const auth = buildAuthorization({ username, webApiKey }); + try { + const auth = buildAuthorization({ username, webApiKey }); - const recentAchievements = await getUserRecentAchievements(auth, { - recentMinutes: 10, - username: username, - }); - - if (recentAchievements.length <= 0) { - return; - } - - const channel = amari.discord.channels.cache.get(ids.channels.gaming) - ?? await amari.discord.channels.fetch(ids.channels.gaming); - - if (channel?.isSendable() !== true) { - return; - } - - await Promise.all(recentAchievements.map(async(achievement) => { - await channel.send({ - components: constructComponents(achievement), - flags: [ MessageFlags.IsComponentsV2 ], + const recentAchievements = await getUserRecentAchievements(auth, { + recentMinutes: 10, + username: username, }); - })); + + if (recentAchievements.length <= 0) { + return; + } + + const channel = amari.discord.channels.cache.get(ids.channels.gaming) + ?? await amari.discord.channels.fetch(ids.channels.gaming); + + if (channel?.isSendable() !== true) { + return; + } + + await Promise.all(recentAchievements.map(async(achievement) => { + await channel.send({ + components: constructComponents(achievement), + flags: [ MessageFlags.IsComponentsV2 ], + }); + })); + } catch (error) { + if (error instanceof Error) { + await logger.error("checkRetroAchievements module", error); + } + } }; diff --git a/src/modules/logMenteeJoin.ts b/src/modules/logMenteeJoin.ts index 597f285..2a245de 100644 --- a/src/modules/logMenteeJoin.ts +++ b/src/modules/logMenteeJoin.ts @@ -20,25 +20,31 @@ export const logMenteeJoin = async( amari: Amari, member: GuildMember, ): Promise => { - const request = await fetch(`https://forms.nhcarrigan.com/api/database/rows/table/756/?user_field_names=true&search=${member.id}`, { headers: { - authorization: `Token ${process.env.BASEROW_TOKEN ?? "huh"}`, - } }); - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Fetch accepts no generic here. - const response = await request.json() as MentorshipRow; + try { + const request = await fetch(`https://forms.nhcarrigan.com/api/database/rows/table/756/?user_field_names=true&search=${member.id}`, { headers: { + authorization: `Token ${process.env.BASEROW_TOKEN ?? "huh"}`, + } }); + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Fetch accepts no generic here. + const response = await request.json() as MentorshipRow; - if (response.count <= 0) { - return; + if (response.count <= 0) { + return; + } + + const channel = amari.discord.channels.cache.get(ids.channels.general) + ?? await amari.discord.channels.fetch(ids.channels.general); + + if (channel?.isSendable() !== true) { + await logger.log( + "warn", + "General channel does not exist or is not sendable.", + ); + return; + } + await logger.metric("processed_mentee_join", 1, { user: member.id }); + } catch (error) { + if (error instanceof Error) { + await logger.error("logMenteeJoin module", error); + } } - - const channel = amari.discord.channels.cache.get(ids.channels.general) - ?? await amari.discord.channels.fetch(ids.channels.general); - - if (channel?.isSendable() !== true) { - await logger.log( - "warn", - "General channel does not exist or is not sendable.", - ); - return; - } - await logger.metric("processed_mentee_join", 1, { user: member.id }); }; diff --git a/src/modules/processGitHubEvent.ts b/src/modules/processGitHubEvent.ts index 90ffd0a..f5c7528 100644 --- a/src/modules/processGitHubEvent.ts +++ b/src/modules/processGitHubEvent.ts @@ -21,13 +21,78 @@ const isPull = (body: GithubPayload): body is PullRequestCreated => { return "pull_request" in body; }; +/** + * Handles a newly opened GitHub issue by auto-assigning Naomi. + * @param amari - Amari's instance. + * @param body - The parsed issue webhook payload. + */ +const handleIssueOpened = async( + amari: Amari, + body: IssueCreated, +): Promise => { + await logger.log("info", "Processing new issue"); + const { issue, repository } = body; + const { number, user } = issue; + const { owner, name } = repository; + try { + await amari.github.rest.issues.addAssignees({ + assignees: [ "naomi-lgbt" ], + // eslint-disable-next-line @typescript-eslint/naming-convention -- Github SDK requirement. + issue_number: number, + owner: owner.login, + repo: name, + }); + await logger.metric("processed_github_event", 1, { + action: "opened", + event: "issue", + user: user.login, + }); + } catch (error) { + if (error instanceof Error) { + await logger.error("processGitHubEvent module", error); + } + } +}; + +/** + * Handles a newly opened GitHub pull request by requesting Naomi's review. + * @param amari - Amari's instance. + * @param body - The parsed pull request webhook payload. + */ +const handlePrOpened = async( + amari: Amari, + body: PullRequestCreated, +): Promise => { + const { pull_request: pr, repository } = body; + const { number, user } = pr; + await logger.log("info", "Processing new PR"); + const { owner, name } = repository; + try { + await amari.github.rest.pulls.requestReviewers({ + owner: owner.login, + // eslint-disable-next-line @typescript-eslint/naming-convention -- Github SDK requirement. + pull_number: number, + repo: name, + reviewers: [ "naomi-lgbt" ], + }); + await logger.metric("processed_github_event", 1, { + action: "opened", + event: "pull_request", + user: user.login, + }); + } catch (error) { + if (error instanceof Error) { + await logger.error("processGitHubEvent module", error); + } + } +}; + /** * Handles a payload from a GitHub webhook. * @param amari - Amari's instance. * @param request - The Fastify request payload. * @param response - The Fastify reply class. */ -// eslint-disable-next-line max-statements, max-lines-per-function -- STFU. export const processGithubEvent = async( amari: Amari, request: FastifyRequest<{ @@ -58,40 +123,10 @@ export const processGithubEvent = async( const { action } = request.body; await response.status(200).send({ message: "Payload received!" }); if (action === "opened" && event === "issues" && isIssue(request.body)) { - await logger.log("info", "Processing new issue"); - const { issue, repository } = request.body; - const { number, user } = issue; - const { owner, name } = repository; - await amari.github.rest.issues.addAssignees({ - assignees: [ "naomi-lgbt" ], - // eslint-disable-next-line @typescript-eslint/naming-convention -- Github SDK requirement. - issue_number: number, - owner: owner.login, - repo: name, - }); - await logger.metric("processed_github_event", 1, { - action: "opened", - event: "issue", - user: user.login, - }); + await handleIssueOpened(amari, request.body); return; } if (action === "opened" && event === "pull_request" && isPull(request.body)) { - const { pull_request: pr, repository } = request.body; - const { number, user } = pr; - await logger.log("info", "Processing new PR"); - await logger.metric("processed_github_event", 1, { - action: "opened", - event: "pull_request", - user: user.login, - }); - const { owner, name } = repository; - await amari.github.rest.pulls.requestReviewers({ - owner: owner.login, - // eslint-disable-next-line @typescript-eslint/naming-convention -- Github SDK requirement. - pull_number: number, - repo: name, - reviewers: [ "naomi-lgbt" ], - }); + await handlePrOpened(amari, request.body); } }; diff --git a/src/modules/processMentorshipRole.ts b/src/modules/processMentorshipRole.ts index d65277c..f9f43e0 100644 --- a/src/modules/processMentorshipRole.ts +++ b/src/modules/processMentorshipRole.ts @@ -34,20 +34,21 @@ export const processMentorshipRole = async( return; } - const channel - = amari.discord.channels.cache.get(ids.channels.menteeChat) - ?? await amari.discord.channels.fetch(ids.channels.menteeChat); + try { + const channel + = amari.discord.channels.cache.get(ids.channels.menteeChat) + ?? await amari.discord.channels.fetch(ids.channels.menteeChat); - if (channel?.isSendable() !== true) { - await logger.log( - "warn", - "Mentee Chat channel does not exist or is not sendable.", - ); - return; - } + if (channel?.isSendable() !== true) { + await logger.log( + "warn", + "Mentee Chat channel does not exist or is not sendable.", + ); + return; + } - await channel.send({ - content: `Hey <@${updatedMember.id}>~! Welcome to the mentorship programme! + await channel.send({ + content: `Hey <@${updatedMember.id}>~! Welcome to the mentorship programme! Please ping (mention, tag) Naomi in this channel with the following template to get started: \`\`\` @@ -56,9 +57,14 @@ First name: Last name: \`\`\` Then read our [mentorship wiki]() for the next steps!`, - }); - addWelcomedMentee(updatedMember.id); - await logger.metric("processed_mentorship_role", 1, { - user: updatedMember.id, - }); + }); + addWelcomedMentee(updatedMember.id); + await logger.metric("processed_mentorship_role", 1, { + user: updatedMember.id, + }); + } catch (error) { + if (error instanceof Error) { + await logger.error("processMentorshipRole module", error); + } + } };