generated from nhcarrigan/template
feat: add analytics and logging, log interactions
Node.js CI / Lint and Test (push) Failing after 40s
Node.js CI / Lint and Test (push) Failing after 40s
This commit is contained in:
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
},
|
||||
"eslint.validate": ["typescript"]
|
||||
}
|
||||
+2
-1
@@ -22,7 +22,8 @@
|
||||
"typescript": "5.9.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nhcarrigan/logger": "1.0.0",
|
||||
"@nhcarrigan/discord-analytics": "0.0.3",
|
||||
"@nhcarrigan/logger": "1.1.1",
|
||||
"alex": "11.0.1",
|
||||
"discord.js": "14.22.1",
|
||||
"extra-markdown-text": "0.1.6",
|
||||
|
||||
Generated
+54
-5
@@ -8,9 +8,12 @@ importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
'@nhcarrigan/discord-analytics':
|
||||
specifier: 0.0.3
|
||||
version: 0.0.3(@nhcarrigan/logger@1.1.1)(discord.js@14.22.1)
|
||||
'@nhcarrigan/logger':
|
||||
specifier: 1.0.0
|
||||
version: 1.0.0
|
||||
specifier: 1.1.1
|
||||
version: 1.1.1
|
||||
alex:
|
||||
specifier: 11.0.1
|
||||
version: 11.0.1
|
||||
@@ -344,6 +347,12 @@ packages:
|
||||
'@jridgewell/sourcemap-codec@1.5.5':
|
||||
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
|
||||
|
||||
'@nhcarrigan/discord-analytics@0.0.3':
|
||||
resolution: {integrity: sha512-XUHcvS3ZbuwVpztHH7pC0T/C8nOJt/T5qz7pDKp3/4Kvlbg0UdeVSZio27QkLqRtB/CvNUS78xM3k9odwN1M7A==}
|
||||
peerDependencies:
|
||||
'@nhcarrigan/logger': '>=1.1.0-hotfix'
|
||||
discord.js: ^14.0.0
|
||||
|
||||
'@nhcarrigan/eslint-config@5.2.0':
|
||||
resolution: {integrity: sha512-YpTTqhviKMlRwKF+RC/GYiA5i2jTCmg8uftuiufldneNV5HMbGpTfBbV7tpa8++5mpYJc4+eZaf40QbDiz84dQ==}
|
||||
engines: {node: '>=22', pnpm: '>=9'}
|
||||
@@ -354,8 +363,8 @@ packages:
|
||||
typescript: '>=5'
|
||||
vitest: '>=2'
|
||||
|
||||
'@nhcarrigan/logger@1.0.0':
|
||||
resolution: {integrity: sha512-2e19Bie+ZKb6yKPKjhawqsENkhHatYkvBAmFZx9eToOXdOca+CYi51tldRMtejg6e0+4hOOf2bo5zdBQKmH0dw==}
|
||||
'@nhcarrigan/logger@1.1.1':
|
||||
resolution: {integrity: sha512-P6OEQFHDtf6psybYGljuCxkSW6DLQCsx1aZZ3w4YKBXHBFjDbhuvpM9K1kPhVN48hakitx2WPLEoIFr6YZELYw==}
|
||||
|
||||
'@nhcarrigan/typescript-config@4.0.0':
|
||||
resolution: {integrity: sha512-969HVha7A/Sg77fuMwOm6p14a+7C5iE6g55OD71srqwKIgksQl+Ex/hAI/pyzTQFDQ/FBJbpnHlR4Ov25QV/rw==}
|
||||
@@ -1054,6 +1063,10 @@ packages:
|
||||
core-util-is@1.0.3:
|
||||
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
|
||||
|
||||
cron-parser@4.9.0:
|
||||
resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
cross-spawn@7.0.6:
|
||||
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
||||
engines: {node: '>= 8'}
|
||||
@@ -2021,6 +2034,9 @@ packages:
|
||||
lodash@4.17.21:
|
||||
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
||||
|
||||
long-timeout@0.1.1:
|
||||
resolution: {integrity: sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==}
|
||||
|
||||
longest-streak@3.1.0:
|
||||
resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
|
||||
|
||||
@@ -2046,6 +2062,10 @@ packages:
|
||||
resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
luxon@3.7.2:
|
||||
resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
magic-bytes.js@1.12.1:
|
||||
resolution: {integrity: sha512-ThQLOhN86ZkJ7qemtVRGYM+gRgR8GEXNli9H/PMvpnZsE44Xfh3wx9kGJaldg314v85m+bFW6WBMaVHJc/c3zA==}
|
||||
|
||||
@@ -2311,6 +2331,10 @@ packages:
|
||||
node-releases@2.0.19:
|
||||
resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
|
||||
|
||||
node-schedule@2.1.1:
|
||||
resolution: {integrity: sha512-OXdegQq03OmXEjt2hZP33W2YPs/E5BcFQks46+G2gAxs4gHOIVD1u7EqlYLYSKsaIpyKCK9Gbk0ta1/gjRSMRQ==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
nopt@7.2.1:
|
||||
resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==}
|
||||
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
||||
@@ -2846,6 +2870,9 @@ packages:
|
||||
sonic-boom@4.2.0:
|
||||
resolution: {integrity: sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==}
|
||||
|
||||
sorted-array-functions@1.3.0:
|
||||
resolution: {integrity: sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==}
|
||||
|
||||
source-map-js@1.2.1:
|
||||
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -3658,6 +3685,12 @@ snapshots:
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.5': {}
|
||||
|
||||
'@nhcarrigan/discord-analytics@0.0.3(@nhcarrigan/logger@1.1.1)(discord.js@14.22.1)':
|
||||
dependencies:
|
||||
'@nhcarrigan/logger': 1.1.1
|
||||
discord.js: 14.22.1
|
||||
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.55.0)(react@19.1.1)(typescript@5.9.2)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.3.0)(yaml@2.8.1))':
|
||||
dependencies:
|
||||
'@eslint-community/eslint-plugin-eslint-comments': 4.4.1(eslint@9.33.0)
|
||||
@@ -3687,7 +3720,7 @@ snapshots:
|
||||
- eslint-import-resolver-webpack
|
||||
- supports-color
|
||||
|
||||
'@nhcarrigan/logger@1.0.0': {}
|
||||
'@nhcarrigan/logger@1.1.1': {}
|
||||
|
||||
'@nhcarrigan/typescript-config@4.0.0(typescript@5.9.2)':
|
||||
dependencies:
|
||||
@@ -4451,6 +4484,10 @@ snapshots:
|
||||
|
||||
core-util-is@1.0.3: {}
|
||||
|
||||
cron-parser@4.9.0:
|
||||
dependencies:
|
||||
luxon: 3.7.2
|
||||
|
||||
cross-spawn@7.0.6:
|
||||
dependencies:
|
||||
path-key: 3.1.1
|
||||
@@ -5627,6 +5664,8 @@ snapshots:
|
||||
|
||||
lodash@4.17.21: {}
|
||||
|
||||
long-timeout@0.1.1: {}
|
||||
|
||||
longest-streak@3.1.0: {}
|
||||
|
||||
loose-envify@1.4.0:
|
||||
@@ -5645,6 +5684,8 @@ snapshots:
|
||||
|
||||
lru-cache@7.18.3: {}
|
||||
|
||||
luxon@3.7.2: {}
|
||||
|
||||
magic-bytes.js@1.12.1: {}
|
||||
|
||||
magic-string@0.30.18:
|
||||
@@ -6175,6 +6216,12 @@ snapshots:
|
||||
|
||||
node-releases@2.0.19: {}
|
||||
|
||||
node-schedule@2.1.1:
|
||||
dependencies:
|
||||
cron-parser: 4.9.0
|
||||
long-timeout: 0.1.1
|
||||
sorted-array-functions: 1.3.0
|
||||
|
||||
nopt@7.2.1:
|
||||
dependencies:
|
||||
abbrev: 2.0.0
|
||||
@@ -6843,6 +6890,8 @@ snapshots:
|
||||
dependencies:
|
||||
atomic-sleep: 1.0.0
|
||||
|
||||
sorted-array-functions@1.3.0: {}
|
||||
|
||||
source-map-js@1.2.1: {}
|
||||
|
||||
space-separated-tokens@2.0.2: {}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* @author Naomi Carrigan
|
||||
*/
|
||||
|
||||
import { DiscordAnalytics } from "@nhcarrigan/discord-analytics";
|
||||
import {
|
||||
Client,
|
||||
GatewayIntentBits,
|
||||
@@ -22,8 +23,11 @@ const caelia = new Client({
|
||||
],
|
||||
});
|
||||
|
||||
const analytics = new DiscordAnalytics(caelia, logger);
|
||||
|
||||
caelia.once(Events.ClientReady, () => {
|
||||
void logger.log("debug", `Logged in as ${caelia.user?.username ?? "unknown user"}.`);
|
||||
analytics.startCron();
|
||||
});
|
||||
|
||||
caelia.on(Events.MessageCreate, (message) => {
|
||||
@@ -31,6 +35,7 @@ caelia.on(Events.MessageCreate, (message) => {
|
||||
});
|
||||
|
||||
caelia.on(Events.InteractionCreate, (interaction) => {
|
||||
void analytics.logGatewayEvent(Events.InteractionCreate, { ...interaction });
|
||||
if (!interaction.isChatInputCommand()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
/**
|
||||
* @copyright NHCarrigan
|
||||
* @license Naomi's Public License
|
||||
* @author Naomi Carrigan
|
||||
*/
|
||||
|
||||
import { markdown } from "alex";
|
||||
import { MessageFlags, type Message } from "discord.js";
|
||||
import { replaceCodeBlocks, replaceLinks } from "extra-markdown-text";
|
||||
import { alexConfig } from "../config/alex.js";
|
||||
import { logger } from "../utils/logger.js";
|
||||
|
||||
/**
|
||||
* Checks if a message contains non-inclusive language. If so,
|
||||
* provides a suggestion to improve vocabulary.
|
||||
* @param message -- The message payload from Discord.
|
||||
*/
|
||||
export const processMessage = async(message: Message): Promise<void> => {
|
||||
try {
|
||||
const { content, author, system } = message;
|
||||
if (author.bot || system || content.length <= 0) {
|
||||
return;
|
||||
}
|
||||
const noCodeBlocks = replaceCodeBlocks(content, () => {
|
||||
return "";
|
||||
});
|
||||
const noLinks = replaceLinks(noCodeBlocks, () => {
|
||||
return "";
|
||||
});
|
||||
const matches = markdown(noLinks, alexConfig).messages;
|
||||
if (matches.length === 0) {
|
||||
return;
|
||||
}
|
||||
const responses = matches.map((match) => {
|
||||
return `- You used the word \`${match.actual ?? "unknown word"}\`. This may not be inclusive language, because: ${match.reason.length > 0
|
||||
? match.reason
|
||||
: "I said so."}${[ null, "", undefined, "undefined", "null" ].includes(match.note)
|
||||
? ""
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Fuck off
|
||||
: ` -- ${match.note!}`}`;
|
||||
});
|
||||
|
||||
await message.reply({ components: [
|
||||
{ content: `Hi darling~! It looks like you may have used some language that is not the most inclusive...\n\n${responses.join("\n")}`,
|
||||
type: 10 },
|
||||
{
|
||||
components: [
|
||||
{
|
||||
label: "Is this inaccurate? Let us know!",
|
||||
style: 5,
|
||||
type: 2,
|
||||
url: "https://chat.nhcarrigan.com",
|
||||
},
|
||||
],
|
||||
type: 1,
|
||||
},
|
||||
],
|
||||
flags: [ MessageFlags.IsComponentsV2 ] }).catch(() => {
|
||||
return null;
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
await logger.error("process message module", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* @copyright NHCarrigan
|
||||
* @license Naomi's Public License
|
||||
* @author Naomi Carrigan
|
||||
*/
|
||||
|
||||
import { markdown } from "alex";
|
||||
import { MessageFlags, type Message } from "discord.js";
|
||||
import { replaceCodeBlocks, replaceLinks } from "extra-markdown-text";
|
||||
import { alexConfig } from "../config/alex.js";
|
||||
import { logger } from "../utils/logger.js";
|
||||
|
||||
/**
|
||||
* Checks if a message contains non-inclusive language. If so,
|
||||
* provides a suggestion to improve vocabulary.
|
||||
* @param message -- The message payload from Discord.
|
||||
*/
|
||||
// eslint-disable-next-line max-lines-per-function -- This function is long but it's not complex.
|
||||
export const processMessage = async(message: Message): Promise<void> => {
|
||||
try {
|
||||
const { content, author, system, guild } = message;
|
||||
if (author.bot || system || content.length <= 0) {
|
||||
return;
|
||||
}
|
||||
const noCodeBlocks = replaceCodeBlocks(content, () => {
|
||||
return "";
|
||||
});
|
||||
const noLinks = replaceLinks(noCodeBlocks, () => {
|
||||
return "";
|
||||
});
|
||||
const matches = markdown(noLinks, alexConfig).messages;
|
||||
if (matches.length === 0) {
|
||||
return;
|
||||
}
|
||||
await logger.metric("messages_flagged", 1, {
|
||||
guild: guild?.id ?? "DM",
|
||||
user: author.id,
|
||||
});
|
||||
const responses = matches.map((match) => {
|
||||
return `- You used the word \`${
|
||||
match.actual ?? "unknown word"
|
||||
}\`. This may not be inclusive language, because: ${
|
||||
match.reason.length > 0
|
||||
? match.reason
|
||||
: "I said so."
|
||||
}${
|
||||
[ null, "", undefined, "undefined", "null" ].includes(match.note)
|
||||
? ""
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Fuck off
|
||||
: ` -- ${match.note!}`
|
||||
}`;
|
||||
});
|
||||
|
||||
await message.
|
||||
reply({
|
||||
components: [
|
||||
{
|
||||
content: `Hi darling~! It looks like you may have used some language that is not the most inclusive...\n\n${responses.join(
|
||||
"\n",
|
||||
)}`,
|
||||
type: 10,
|
||||
},
|
||||
{
|
||||
components: [
|
||||
{
|
||||
label: "Is this inaccurate? Let us know!",
|
||||
style: 5,
|
||||
type: 2,
|
||||
url: "https://chat.nhcarrigan.com",
|
||||
},
|
||||
],
|
||||
type: 1,
|
||||
},
|
||||
],
|
||||
flags: [ MessageFlags.IsComponentsV2 ],
|
||||
}).
|
||||
catch(() => {
|
||||
return null;
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
await logger.error("process message module", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user