generated from nhcarrigan/template
+2
-1
@@ -23,7 +23,8 @@
|
|||||||
"typescript": "5.9.2"
|
"typescript": "5.9.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nhcarrigan/logger": "1.0.0",
|
"@nhcarrigan/discord-analytics": "0.0.5",
|
||||||
|
"@nhcarrigan/logger": "1.1.1",
|
||||||
"@retroachievements/api": "2.6.0",
|
"@retroachievements/api": "2.6.0",
|
||||||
"discord.js": "14.22.0",
|
"discord.js": "14.22.0",
|
||||||
"fastify": "5.5.0",
|
"fastify": "5.5.0",
|
||||||
|
|||||||
Generated
+20
-5
@@ -8,9 +8,12 @@ importers:
|
|||||||
|
|
||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@nhcarrigan/discord-analytics':
|
||||||
|
specifier: 0.0.5
|
||||||
|
version: 0.0.5(@nhcarrigan/logger@1.1.1)(discord.js@14.22.0)
|
||||||
'@nhcarrigan/logger':
|
'@nhcarrigan/logger':
|
||||||
specifier: 1.0.0
|
specifier: 1.1.1
|
||||||
version: 1.0.0
|
version: 1.1.1
|
||||||
'@retroachievements/api':
|
'@retroachievements/api':
|
||||||
specifier: 2.6.0
|
specifier: 2.6.0
|
||||||
version: 2.6.0
|
version: 2.6.0
|
||||||
@@ -349,6 +352,12 @@ packages:
|
|||||||
'@jridgewell/sourcemap-codec@1.5.5':
|
'@jridgewell/sourcemap-codec@1.5.5':
|
||||||
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
|
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
|
||||||
|
|
||||||
|
'@nhcarrigan/discord-analytics@0.0.5':
|
||||||
|
resolution: {integrity: sha512-dxkXFB/o12AEPWGmv2/d+rQYZ+rzm1tkW9gjeVs/8JMadk+gDIDlSdOlWZrov/VhQGWLpHJFfZD2Y9qcnPf8kg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@nhcarrigan/logger': '>=1.1.0-hotfix'
|
||||||
|
discord.js: ^14.0.0
|
||||||
|
|
||||||
'@nhcarrigan/eslint-config@5.2.0':
|
'@nhcarrigan/eslint-config@5.2.0':
|
||||||
resolution: {integrity: sha512-YpTTqhviKMlRwKF+RC/GYiA5i2jTCmg8uftuiufldneNV5HMbGpTfBbV7tpa8++5mpYJc4+eZaf40QbDiz84dQ==}
|
resolution: {integrity: sha512-YpTTqhviKMlRwKF+RC/GYiA5i2jTCmg8uftuiufldneNV5HMbGpTfBbV7tpa8++5mpYJc4+eZaf40QbDiz84dQ==}
|
||||||
engines: {node: '>=22', pnpm: '>=9'}
|
engines: {node: '>=22', pnpm: '>=9'}
|
||||||
@@ -359,8 +368,8 @@ packages:
|
|||||||
typescript: '>=5'
|
typescript: '>=5'
|
||||||
vitest: '>=2'
|
vitest: '>=2'
|
||||||
|
|
||||||
'@nhcarrigan/logger@1.0.0':
|
'@nhcarrigan/logger@1.1.1':
|
||||||
resolution: {integrity: sha512-2e19Bie+ZKb6yKPKjhawqsENkhHatYkvBAmFZx9eToOXdOca+CYi51tldRMtejg6e0+4hOOf2bo5zdBQKmH0dw==}
|
resolution: {integrity: sha512-P6OEQFHDtf6psybYGljuCxkSW6DLQCsx1aZZ3w4YKBXHBFjDbhuvpM9K1kPhVN48hakitx2WPLEoIFr6YZELYw==}
|
||||||
|
|
||||||
'@nhcarrigan/typescript-config@4.0.0':
|
'@nhcarrigan/typescript-config@4.0.0':
|
||||||
resolution: {integrity: sha512-969HVha7A/Sg77fuMwOm6p14a+7C5iE6g55OD71srqwKIgksQl+Ex/hAI/pyzTQFDQ/FBJbpnHlR4Ov25QV/rw==}
|
resolution: {integrity: sha512-969HVha7A/Sg77fuMwOm6p14a+7C5iE6g55OD71srqwKIgksQl+Ex/hAI/pyzTQFDQ/FBJbpnHlR4Ov25QV/rw==}
|
||||||
@@ -2630,6 +2639,12 @@ snapshots:
|
|||||||
|
|
||||||
'@jridgewell/sourcemap-codec@1.5.5': {}
|
'@jridgewell/sourcemap-codec@1.5.5': {}
|
||||||
|
|
||||||
|
'@nhcarrigan/discord-analytics@0.0.5(@nhcarrigan/logger@1.1.1)(discord.js@14.22.0)':
|
||||||
|
dependencies:
|
||||||
|
'@nhcarrigan/logger': 1.1.1
|
||||||
|
discord.js: 14.22.0
|
||||||
|
node-schedule: 2.1.1
|
||||||
|
|
||||||
'@nhcarrigan/eslint-config@5.2.0(@typescript-eslint/utils@8.40.0(eslint@9.33.0)(typescript@5.9.2))(eslint@9.33.0)(playwright@1.54.2)(react@19.1.1)(typescript@5.9.2)(vitest@3.2.4(@types/node@24.3.0))':
|
'@nhcarrigan/eslint-config@5.2.0(@typescript-eslint/utils@8.40.0(eslint@9.33.0)(typescript@5.9.2))(eslint@9.33.0)(playwright@1.54.2)(react@19.1.1)(typescript@5.9.2)(vitest@3.2.4(@types/node@24.3.0))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint-community/eslint-plugin-eslint-comments': 4.4.1(eslint@9.33.0)
|
'@eslint-community/eslint-plugin-eslint-comments': 4.4.1(eslint@9.33.0)
|
||||||
@@ -2659,7 +2674,7 @@ snapshots:
|
|||||||
- eslint-import-resolver-webpack
|
- eslint-import-resolver-webpack
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@nhcarrigan/logger@1.0.0': {}
|
'@nhcarrigan/logger@1.1.1': {}
|
||||||
|
|
||||||
'@nhcarrigan/typescript-config@4.0.0(typescript@5.9.2)':
|
'@nhcarrigan/typescript-config@4.0.0(typescript@5.9.2)':
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const freeCodeCampSprintChannels: Array<Channel> = [
|
|||||||
channelId: "1424801426160488520",
|
channelId: "1424801426160488520",
|
||||||
createThread: false,
|
createThread: false,
|
||||||
name: "fsd sprints",
|
name: "fsd sprints",
|
||||||
roleId: "1417979684754428054",
|
roleId: "1425506225453273224",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
* @author Naomi Carrigan
|
* @author Naomi Carrigan
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { DiscordAnalytics } from "@nhcarrigan/discord-analytics";
|
||||||
import { Client,
|
import { Client,
|
||||||
GatewayIntentBits,
|
GatewayIntentBits,
|
||||||
Events,
|
Events,
|
||||||
@@ -59,10 +60,13 @@ const amari: Amari = {
|
|||||||
recentlyActiveChannels: new Set<string>(),
|
recentlyActiveChannels: new Set<string>(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const analytics = new DiscordAnalytics(amari.discord, logger);
|
||||||
|
|
||||||
amari.discord.once(Events.ClientReady, () => {
|
amari.discord.once(Events.ClientReady, () => {
|
||||||
void logger.log("debug",
|
void logger.log("debug",
|
||||||
`Authenticated to Discord as ${amari.discord.user?.username ?? "unknown"}`);
|
`Authenticated to Discord as ${amari.discord.user?.username ?? "unknown"}`);
|
||||||
void cacheData(amari);
|
void cacheData(amari);
|
||||||
|
analytics.startCron();
|
||||||
scheduleJob("post news", "0 * * * *", async() => {
|
scheduleJob("post news", "0 * * * *", async() => {
|
||||||
await postFreeCodeCampNews(amari);
|
await postFreeCodeCampNews(amari);
|
||||||
await postHackerNews(amari);
|
await postHackerNews(amari);
|
||||||
@@ -91,6 +95,7 @@ amari.discord.on(Events.MessageCreate, (message) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
amari.discord.on(Events.InteractionCreate, (interaction) => {
|
amari.discord.on(Events.InteractionCreate, (interaction) => {
|
||||||
|
void analytics.logGatewayEvent(Events.InteractionCreate, { ...interaction });
|
||||||
if (interaction.isButton() && interaction.customId === "resolve") {
|
if (interaction.isButton() && interaction.customId === "resolve") {
|
||||||
if (interaction.user.id !== ids.users.naomi) {
|
if (interaction.user.id !== ids.users.naomi) {
|
||||||
return void interaction.reply(
|
return void interaction.reply(
|
||||||
|
|||||||
@@ -48,4 +48,5 @@ Welcome to our community! It looks like you may have applied for our mentorship
|
|||||||
|
|
||||||
If that is correct, you should ping Naomi to grant your role and begin onboarding! <a:love:1364089736557494353>`,
|
If that is correct, you should ping Naomi to grant your role and begin onboarding! <a:love:1364089736557494353>`,
|
||||||
});
|
});
|
||||||
|
await logger.metric("processed_mentee_join", 1, { user: member.id });
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -41,4 +41,5 @@ export const logMenteeLeave = async(
|
|||||||
|
|
||||||
It seems they were part of the mentorship programme, so you may need to offboard them.`,
|
It seems they were part of the mentorship programme, so you may need to offboard them.`,
|
||||||
});
|
});
|
||||||
|
await logger.metric("processed_mentee_leave", 1, { user: member.id });
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import type { FastifyRequest, FastifyReply } from "fastify";
|
|||||||
* @param request - The fastify request payload.
|
* @param request - The fastify request payload.
|
||||||
* @param response - The fastify reply class.
|
* @param response - The fastify reply class.
|
||||||
*/
|
*/
|
||||||
|
// eslint-disable-next-line max-lines-per-function -- only long because of analytics.
|
||||||
export const processFormSubmission = async(
|
export const processFormSubmission = async(
|
||||||
amari: Amari,
|
amari: Amari,
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention -- Fastify standard.
|
// eslint-disable-next-line @typescript-eslint/naming-convention -- Fastify standard.
|
||||||
@@ -67,4 +68,5 @@ export const processFormSubmission = async(
|
|||||||
],
|
],
|
||||||
flags: [ MessageFlags.IsComponentsV2 ],
|
flags: [ MessageFlags.IsComponentsV2 ],
|
||||||
});
|
});
|
||||||
|
await logger.metric("processed_form_submission", 1, { table: String(table) });
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,9 +6,11 @@
|
|||||||
|
|
||||||
import { logger } from "../utils/logger.js";
|
import { logger } from "../utils/logger.js";
|
||||||
import type { Amari } from "../interfaces/amari.js";
|
import type { Amari } from "../interfaces/amari.js";
|
||||||
import type { IssueCreated,
|
import type {
|
||||||
|
IssueCreated,
|
||||||
PullRequestCreated,
|
PullRequestCreated,
|
||||||
GithubPayload } from "../interfaces/github.js";
|
GithubPayload,
|
||||||
|
} from "../interfaces/github.js";
|
||||||
import type { FastifyRequest, FastifyReply } from "fastify";
|
import type { FastifyRequest, FastifyReply } from "fastify";
|
||||||
|
|
||||||
const isIssue = (body: GithubPayload): body is IssueCreated => {
|
const isIssue = (body: GithubPayload): body is IssueCreated => {
|
||||||
@@ -44,7 +46,8 @@ export const processGithubEvent = async(
|
|||||||
}
|
}
|
||||||
const event = request.headers["x-github-event"];
|
const event = request.headers["x-github-event"];
|
||||||
if (typeof event !== "string") {
|
if (typeof event !== "string") {
|
||||||
await response.status(400).
|
await response.
|
||||||
|
status(400).
|
||||||
send({ message: "Invalid GitHub event header." });
|
send({ message: "Invalid GitHub event header." });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -57,7 +60,7 @@ export const processGithubEvent = async(
|
|||||||
if (action === "opened" && event === "issues" && isIssue(request.body)) {
|
if (action === "opened" && event === "issues" && isIssue(request.body)) {
|
||||||
await logger.log("info", "Processing new issue");
|
await logger.log("info", "Processing new issue");
|
||||||
const { issue, repository } = request.body;
|
const { issue, repository } = request.body;
|
||||||
const { number } = issue;
|
const { number, user } = issue;
|
||||||
const { owner, name } = repository;
|
const { owner, name } = repository;
|
||||||
await amari.github.rest.issues.addAssignees({
|
await amari.github.rest.issues.addAssignees({
|
||||||
assignees: [ "naomi-lgbt" ],
|
assignees: [ "naomi-lgbt" ],
|
||||||
@@ -66,12 +69,22 @@ export const processGithubEvent = async(
|
|||||||
owner: owner.login,
|
owner: owner.login,
|
||||||
repo: name,
|
repo: name,
|
||||||
});
|
});
|
||||||
|
await logger.metric("processed_github_event", 1, {
|
||||||
|
action: "opened",
|
||||||
|
event: "issue",
|
||||||
|
user: user.login,
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (action === "opened" && event === "pull_request" && isPull(request.body)) {
|
if (action === "opened" && event === "pull_request" && isPull(request.body)) {
|
||||||
await logger.log("info", "Processing new PR");
|
|
||||||
const { pull_request: pr, repository } = request.body;
|
const { pull_request: pr, repository } = request.body;
|
||||||
const { number } = pr;
|
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;
|
const { owner, name } = repository;
|
||||||
await amari.github.rest.pulls.requestReviewers({
|
await amari.github.rest.pulls.requestReviewers({
|
||||||
owner: owner.login,
|
owner: owner.login,
|
||||||
|
|||||||
@@ -22,12 +22,15 @@ export const processMentorshipRole = async(
|
|||||||
oldMember: GuildMember | PartialGuildMember,
|
oldMember: GuildMember | PartialGuildMember,
|
||||||
updatedMember: GuildMember,
|
updatedMember: GuildMember,
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
if (oldMember.roles.cache.has(ids.roles.mentorship)
|
if (
|
||||||
|| !updatedMember.roles.cache.has(ids.roles.mentorship)) {
|
oldMember.roles.cache.has(ids.roles.mentorship)
|
||||||
|
|| !updatedMember.roles.cache.has(ids.roles.mentorship)
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const channel = amari.discord.channels.cache.get(ids.channels.menteeChat)
|
const channel
|
||||||
|
= amari.discord.channels.cache.get(ids.channels.menteeChat)
|
||||||
?? await amari.discord.channels.fetch(ids.channels.menteeChat);
|
?? await amari.discord.channels.fetch(ids.channels.menteeChat);
|
||||||
|
|
||||||
if (channel?.isSendable() !== true) {
|
if (channel?.isSendable() !== true) {
|
||||||
@@ -49,4 +52,7 @@ Once you have done this, your next step is to read our [wiki](<https://nhcarriga
|
|||||||
|
|
||||||
Naomi will follow up with you from there! Best of luck on your journey~! <a:love:1364089736557494353>`,
|
Naomi will follow up with you from there! Best of luck on your journey~! <a:love:1364089736557494353>`,
|
||||||
});
|
});
|
||||||
|
await logger.metric("processed_mentorship_role", 1, {
|
||||||
|
user: updatedMember.id,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ export const processUserGuildTag = async(
|
|||||||
&& member.roles.cache.has(ids.roles.representing)) {
|
&& member.roles.cache.has(ids.roles.representing)) {
|
||||||
await member.roles.remove(ids.roles.representing);
|
await member.roles.remove(ids.roles.representing);
|
||||||
}
|
}
|
||||||
|
await logger.metric("processed_guild_tag", 1, { user: user.id });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
await logger.error("process user guild tag module", error);
|
await logger.error("process user guild tag module", error);
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ export const respondToDm = async(
|
|||||||
await message.reply({
|
await message.reply({
|
||||||
content: responses.dm,
|
content: responses.dm,
|
||||||
});
|
});
|
||||||
|
await logger.metric("processed_dm", 1, { user: author.id });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
await logger.error("respond to DM module", error);
|
await logger.error("respond to DM module", error);
|
||||||
|
|||||||
@@ -31,34 +31,31 @@ export const respondToMention = async(
|
|||||||
if (amari.recentlyActiveChannels.has(channel.id)) {
|
if (amari.recentlyActiveChannels.has(channel.id)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (mentions.has(ids.users.naomi, {
|
const mentionsNaomi = mentions.has(ids.users.naomi, {
|
||||||
ignoreEveryone: true,
|
ignoreEveryone: true,
|
||||||
ignoreRepliedUser: true,
|
ignoreRepliedUser: true,
|
||||||
ignoreRoles: true,
|
ignoreRoles: true,
|
||||||
}) || /naomi/i.test(content)) {
|
}) || /naomi/i.test(content);
|
||||||
await naomi.send(
|
const mentionsNHCarrigan = mentions.has(ids.roles.nhcarrigan, {
|
||||||
{
|
|
||||||
components: getComponentsForNaomi(author, content, url),
|
|
||||||
flags: [ MessageFlags.IsComponentsV2 ],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (mentions.has(ids.roles.nhcarrigan, {
|
|
||||||
ignoreEveryone: true,
|
ignoreEveryone: true,
|
||||||
ignoreRepliedUser: true,
|
ignoreRepliedUser: true,
|
||||||
}) || mentions.has(ids.users.nhcarrigan, {
|
}) || mentions.has(ids.users.nhcarrigan, {
|
||||||
ignoreEveryone: true,
|
ignoreEveryone: true,
|
||||||
ignoreRepliedUser: true,
|
ignoreRepliedUser: true,
|
||||||
ignoreRoles: true,
|
ignoreRoles: true,
|
||||||
}) || /nhcarrigan/i.test(content)) {
|
}) || /nhcarrigan/i.test(content);
|
||||||
|
if (!mentionsNaomi && !mentionsNHCarrigan) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
await naomi.send(
|
await naomi.send(
|
||||||
{
|
{
|
||||||
components: getComponentsForNaomi(author, content, url),
|
components: getComponentsForNaomi(author, content, url),
|
||||||
flags: [ MessageFlags.IsComponentsV2 ],
|
flags: [ MessageFlags.IsComponentsV2 ],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
await logger.metric("processed_mention", 1, { pingType: mentionsNaomi
|
||||||
|
? "naomi"
|
||||||
|
: "nhcarrigan", user: author.id });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
await logger.error("respond to mention module", error);
|
await logger.error("respond to mention module", error);
|
||||||
|
|||||||
Reference in New Issue
Block a user