/** * @copyright NHCarrigan * @license Naomi's Public License * @author Naomi Carrigan */ import { logger } from "../utils/logger.js"; import type { Amari } from "../interfaces/amari.js"; import type { IssueCreated, PullRequestCreated, GithubPayload, } from "../interfaces/github.js"; import type { FastifyRequest, FastifyReply } from "fastify"; const isIssue = (body: GithubPayload): body is IssueCreated => { return "issue" in body; }; 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. */ export const processGithubEvent = async( amari: Amari, request: FastifyRequest<{ // eslint-disable-next-line @typescript-eslint/naming-convention -- Fastify standard. Body: GithubPayload; // eslint-disable-next-line @typescript-eslint/naming-convention -- Fastify standard. Querystring: { secret: string }; }>, response: FastifyReply, ): Promise => { const { secret } = request.query; if (secret !== process.env.GH_WEBHOOK_SECRET) { await response.status(403).send({ message: "Invalid secret provided!", }); } const event = request.headers["x-github-event"]; if (typeof event !== "string") { await response. status(400). send({ message: "Invalid GitHub event header." }); return; } if (event === "ping") { await response.status(200).send({ message: "Pong!" }); return; } const { action } = request.body; await response.status(200).send({ message: "Payload received!" }); if (action === "opened" && event === "issues" && isIssue(request.body)) { await handleIssueOpened(amari, request.body); return; } if (action === "opened" && event === "pull_request" && isPull(request.body)) { await handlePrOpened(amari, request.body); } };