aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore12
-rw-r--r--.vscode/extensions.json24
-rw-r--r--.vscode/settings.json6
-rw-r--r--.yarnrc.yml3
-rw-r--r--package.json6
-rw-r--r--src/commands/info/help.ts5
-rw-r--r--src/commands/moulberry-bush/capes.ts4
-rw-r--r--src/commands/utilities/highlight-!.ts150
-rw-r--r--src/commands/utilities/highlight-add.ts82
-rw-r--r--src/commands/utilities/highlight-block.ts69
-rw-r--r--src/commands/utilities/highlight-clear.ts39
-rw-r--r--src/commands/utilities/highlight-matches.ts0
-rw-r--r--src/commands/utilities/highlight-remove.ts57
-rw-r--r--src/commands/utilities/highlight-show.ts34
-rw-r--r--src/commands/utilities/highlight-unblock.ts69
-rw-r--r--src/lib/common/HighlightManager.ts71
-rw-r--r--src/lib/extensions/discord-akairo/BushClient.ts10
-rw-r--r--src/lib/extensions/discord-akairo/BushClientUtil.ts18
-rw-r--r--src/lib/extensions/discord-akairo/BushSlashMessage.ts8
-rw-r--r--src/lib/index.ts1
-rw-r--r--src/lib/models/instance/Highlight.ts81
-rw-r--r--src/listeners/member-custom/bushLevelUpdate.ts73
-rw-r--r--src/listeners/message/automodUpdate.ts5
-rw-r--r--src/listeners/message/highlight.ts15
-rw-r--r--yarn.lock99
25 files changed, 836 insertions, 105 deletions
diff --git a/.gitignore b/.gitignore
index 207c3c0..e3672cb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,22 +16,14 @@ dist
.idea
-# Cache file creation bug
-.yarn/cache/*
-.yarn/sdks/eslint/*
-.yarn/sdks/prettier/*
-.yarn/install-state.gz
-.yarn/build-state.url
-.yarn/releases.gz
-
# yarn
.pnp.*
.yarn/*
-!.yarn/releases
+!.yarn/patches
!.yarn/plugins
+!.yarn/releases
!.yarn/sdks
!.yarn/versions
-!.yarn/cache
# Options and credentials for the bot
src/config/options.ts
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
index 5722024..2b70e67 100644
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -1,14 +1,14 @@
{
- "recommendations": [
- "aaron-bond.better-comments",
- "arcanis.vscode-zipfs",
- "dbaeumer.vscode-eslint",
- "eamodio.gitlens",
- "esbenp.prettier-vscode",
- "streetsidesoftware.code-spell-checker",
- "github.vscode-pull-request-github",
- "ckolkman.vscode-postgres",
- "tobias-faller.vt100-syntax-highlighting",
- "pkief.material-icon-theme"
- ]
+ "recommendations": [
+ "aaron-bond.better-comments",
+ "arcanis.vscode-zipfs",
+ "dbaeumer.vscode-eslint",
+ "eamodio.gitlens",
+ "esbenp.prettier-vscode",
+ "streetsidesoftware.code-spell-checker",
+ "github.vscode-pull-request-github",
+ "ckolkman.vscode-postgres",
+ "tobias-faller.vt100-syntax-highlighting",
+ "pkief.material-icon-theme"
+ ]
}
diff --git a/.vscode/settings.json b/.vscode/settings.json
index b41dbcf..086fe81 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -92,5 +92,9 @@
"italic": false
}
],
- "javascript.preferences.importModuleSpecifierEnding": "js"
+ "javascript.preferences.importModuleSpecifierEnding": "js",
+ "discord.removeDetails": false,
+ "discord.removeLowerDetails": false,
+ "discord.removeRemoteRepository": false,
+ "discord.removeTimestamp": false
}
diff --git a/.yarnrc.yml b/.yarnrc.yml
index d5c5c56..ad81b11 100644
--- a/.yarnrc.yml
+++ b/.yarnrc.yml
@@ -1,4 +1,7 @@
nodeLinker: node-modules
+pnpEnableEsmLoader: true
+pnpMode: loose
+enableTelemetry: false
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs
diff --git a/package.json b/package.json
index 7af0e11..93e2231 100644
--- a/package.json
+++ b/package.json
@@ -50,6 +50,7 @@
"deploy:all": "yarn beta && pm2 deploy ecosystem.config.cjs production && pm2 deploy ecosystem.config.cjs beta"
},
"dependencies": {
+ "@discordjs/rest": "npm:@notenoughupdates/rest@dev",
"@notenoughupdates/discord.js-minesweeper": "^1.0.10",
"@notenoughupdates/events-intercept": "^3.0.1",
"@notenoughupdates/humanize-duration": "^4.0.1",
@@ -78,15 +79,16 @@
"rimraf": "^3.0.2",
"sequelize": "6.13.0",
"tinycolor2": "^1.4.2",
- "tslib": "^2.3.1",
"typescript": "^4.5.5",
"vm2": "^3.9.5"
},
"devDependencies": {
+ "@sapphire/snowflake": "^3.1.0",
+ "@sentry/types": "^6.17.4",
"@types/eslint": "^8.4.1",
"@types/express": "^4.17.13",
"@types/lodash": "^4.14.178",
- "@types/node": "^17.0.14",
+ "@types/node": "^17.0.15",
"@types/node-os-utils": "^1.2.0",
"@types/numeral": "^2.0.2",
"@types/pg": "^8.6.4",
diff --git a/src/commands/info/help.ts b/src/commands/info/help.ts
index e31153b..2383566 100644
--- a/src/commands/info/help.ts
+++ b/src/commands/info/help.ts
@@ -77,7 +77,10 @@ export default class HelpCommand extends BushCommand {
if (command.channel == 'guild' && !message.guild && !args.showHidden) return false;
if (command.ownerOnly && !isOwner) return false;
if (command.superUserOnly && !isSuperUser) return false;
- return !(command.restrictedGuilds?.includes(message.guild?.id ?? '') === false && !args.showHidden);
+ if (command.restrictedGuilds?.includes(message.guild?.id ?? '') === false && !args.showHidden) return false;
+ if (command.aliases.length === 0) return false;
+
+ return true;
});
const categoryNice = category.id
.replace(/(\b\w)/gi, (lc) => lc.toUpperCase())
diff --git a/src/commands/moulberry-bush/capes.ts b/src/commands/moulberry-bush/capes.ts
index 0b2fcad..032f62d 100644
--- a/src/commands/moulberry-bush/capes.ts
+++ b/src/commands/moulberry-bush/capes.ts
@@ -114,7 +114,9 @@ export default class CapesCommand extends BushCommand {
findAllMatches: true
}).search(interaction.options.getFocused().toString());
- const res = fuzzy.slice(0, fuzzy.length >= 25 ? 25 : undefined).map((v) => ({ name: v.item, value: v.item }));
+ const res = (fuzzy.length ? fuzzy : capes.map((c) => ({ item: c })))
+ .slice(0, fuzzy.length >= 25 ? 25 : undefined)
+ .map((v) => ({ name: v.item, value: v.item }));
void interaction.respond(res);
}
diff --git a/src/commands/utilities/highlight-!.ts b/src/commands/utilities/highlight-!.ts
new file mode 100644
index 0000000..332af03
--- /dev/null
+++ b/src/commands/utilities/highlight-!.ts
@@ -0,0 +1,150 @@
+import { BushCommand, Highlight, HighlightWord, type BushSlashMessage } from '#lib';
+import { Flag, type ArgumentGeneratorReturn, type SlashOption } from 'discord-akairo';
+import { ApplicationCommandOptionType } from 'discord-api-types';
+import { ApplicationCommandSubCommandData, AutocompleteInteraction, CacheType } from 'discord.js';
+
+type Unpacked<T> = T extends (infer U)[] ? U : T;
+
+export const highlightCommandArgs: {
+ [Command in keyof typeof highlightSubcommands]: (Unpacked<Required<ApplicationCommandSubCommandData['options']>> & {
+ retry?: string;
+ })[];
+} = {
+ add: [
+ {
+ name: 'word',
+ description: 'What word do you want to highlight?',
+ retry: '{error} Enter a valid word.',
+ type: ApplicationCommandOptionType.String,
+ required: true
+ },
+ {
+ name: 'regex',
+ description: 'Should the word be matched using regular expression?',
+ type: ApplicationCommandOptionType.Boolean,
+ required: false
+ }
+ ],
+ remove: [
+ {
+ name: 'word',
+ description: 'Which word do you want to stop highlighting?',
+ retry: '{error} Enter a valid word.',
+ type: ApplicationCommandOptionType.String,
+ required: true,
+ autocomplete: true
+ }
+ ],
+ block: [
+ {
+ name: 'target',
+ description: 'What user/channel would you like to prevent from triggering your highlights?',
+ retry: '{error} Enter a valid user or channel.',
+ type: ApplicationCommandOptionType.Mentionable,
+ required: true
+ }
+ ],
+ unblock: [
+ {
+ name: 'target',
+ description: 'What user/channel would you like to allow triggering your highlights again?',
+ retry: '{error} Enter a valid user or channel.',
+ type: ApplicationCommandOptionType.Mentionable,
+ required: true
+ }
+ ],
+ show: [],
+ clear: [],
+ matches: [
+ {
+ name: 'phrase',
+ description: 'What phrase would you like to test your highlighted words against?',
+ retry: '{error} Enter a valid phrase to test.',
+ type: ApplicationCommandOptionType.String,
+ required: true
+ }
+ ]
+};
+
+export const highlightSubcommands = {
+ add: 'Add a word to highlight.',
+ remove: 'Stop highting a word.',
+ block: 'Block a user or channel from triggering your highlights.',
+ unblock: 'Re-allow a user or channel to triggering your highlights.',
+ show: 'List all your current highlighted words.',
+ clear: 'Remove all of your highlighted words.',
+ matches: 'Test a phrase to see if it matches your current highlighted words.'
+} as const;
+
+export default class HighlightCommand extends BushCommand {
+ public constructor() {
+ super('highlight', {
+ aliases: ['highlight', 'hl'],
+ category: 'utilities',
+ description: 'Command description.',
+ usage: ['template <requiredArg> [optionalArg]'],
+ examples: ['template 1 2'],
+ slashOptions: Object.entries(highlightSubcommands).map((args) => {
+ // typescript being annoying
+ const [subcommand, description] = args as [keyof typeof highlightSubcommands, typeof args[1]];
+
+ return {
+ name: subcommand,
+ description,
+ type: ApplicationCommandOptionType.Subcommand,
+ options: highlightCommandArgs[subcommand].map((arg) => ({
+ name: arg.name,
+ description: arg.description,
+ type: arg.type,
+ required: arg.required,
+ autocomplete: arg.autocomplete
+ }))
+ } as SlashOption;
+ }),
+ slash: true,
+ channel: 'guild',
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: [],
+ ownerOnly: true
+ });
+ }
+
+ public override *args(): ArgumentGeneratorReturn {
+ const subcommand: keyof typeof highlightSubcommands = yield {
+ id: 'subcommand',
+ type: Object.keys(highlightSubcommands),
+ prompt: {
+ start: 'What sub command would you like to use?',
+ retry: `{error} Valid subcommands are: ${Object.keys(highlightSubcommands)
+ .map((s) => `\`${s}\``)
+ .join()}.`
+ }
+ };
+
+ return Flag.continue(`highlight-${subcommand}`);
+ }
+
+ public override async execSlash(message: BushSlashMessage, args: { subcommand: keyof typeof highlightSubcommands }) {
+ // manual `Flag.continue`
+ const subcommand = this.handler.modules.get(`highlight-${args.subcommand}`)!;
+ return subcommand.exec(message, args);
+ }
+
+ public override async autocomplete(interaction: AutocompleteInteraction<CacheType>) {
+ if (!interaction.inCachedGuild())
+ return interaction.respond([{ name: 'You must be in a server to use this command.', value: 'error' }]);
+
+ switch (interaction.options.getSubcommand(true)) {
+ case 'word': {
+ const { words } = (await Highlight.findOne({
+ where: {
+ guild: interaction.guild.id,
+ user: interaction.user.id
+ }
+ })) ?? { words: [] as HighlightWord[] };
+ if (!words.length) return interaction.respond([]);
+ return interaction.respond(words.map((w) => ({ name: w.word, value: w.word })));
+ }
+ }
+ }
+}
diff --git a/src/commands/utilities/highlight-add.ts b/src/commands/utilities/highlight-add.ts
new file mode 100644
index 0000000..ec5443c
--- /dev/null
+++ b/src/commands/utilities/highlight-add.ts
@@ -0,0 +1,82 @@
+import { AllowedMentions, BushCommand, Highlight, type ArgType, type BushMessage, type BushSlashMessage } from '#lib';
+import assert from 'assert';
+import { ArgumentGeneratorReturn } from 'discord-akairo';
+import { highlightCommandArgs, highlightSubcommands } from './highlight-!';
+
+export default class HighlightAddCommand extends BushCommand {
+ public constructor() {
+ super('highlight-add', {
+ aliases: [],
+ category: 'utilities',
+ description: highlightSubcommands.add,
+ usage: [],
+ examples: [],
+ clientPermissions: [],
+ userPermissions: []
+ });
+ }
+
+ public override *args(): ArgumentGeneratorReturn {
+ const word: ArgType<'string'> = yield {
+ type: 'string',
+ match: 'rest',
+ prompt: {
+ start: highlightCommandArgs.add[0].description,
+ retry: highlightCommandArgs.add[0].retry,
+ optional: !highlightCommandArgs.add[0].required
+ }
+ };
+
+ const regex: boolean = yield {
+ match: 'flag',
+ flag: 'regex'
+ };
+
+ return { word, regex };
+ }
+
+ public override async exec(
+ message: BushMessage | BushSlashMessage,
+ args: { word: ArgType<'string'>; regex: ArgType<'boolean'> }
+ ) {
+ assert(message.inGuild());
+
+ if (!args.regex) {
+ if (args.word.length < 2)
+ return message.util.send(`${util.emojis.error} You can only highlight words that are longer than 2 characters.`);
+ if (args.word.length > 50)
+ return await message.util.reply(`${util.emojis.error} You can only highlight words that are shorter than 50 characters.`);
+ } else {
+ try {
+ new RegExp(args.word);
+ } catch (e) {
+ assert(e instanceof SyntaxError);
+ return message.util.send({
+ content: `${util.emojis.error} Invalid regex ${util.format.inlineCode(e.message)}.`,
+ allowedMentions: AllowedMentions.none()
+ });
+ }
+ }
+
+ const [highlight] = await Highlight.findOrCreate({
+ where: {
+ guild: message.guild.id,
+ user: message.author.id
+ }
+ });
+
+ if (highlight.words.some((w) => w.word === args.word))
+ return await message.util.reply({
+ content: `${util.emojis.error} You have already highlighted "${args.word}".`,
+ allowedMentions: AllowedMentions.none()
+ });
+
+ highlight.words = util.addToArray(highlight.words, { word: args.word, regex: args.regex });
+ await highlight.save();
+
+ return await message.util.reply({
+ content: `${util.emojis.success} Successfully added "${args.word}" to your highlight list.`,
+ allowedMentions: AllowedMentions.none()
+ });
+ }
+}
diff --git a/src/commands/utilities/highlight-block.ts b/src/commands/utilities/highlight-block.ts
new file mode 100644
index 0000000..5a18b8a
--- /dev/null
+++ b/src/commands/utilities/highlight-block.ts
@@ -0,0 +1,69 @@
+import { AllowedMentions, BushCommand, Highlight, type ArgType, type BushMessage, type BushSlashMessage } from '#lib';
+import assert from 'assert';
+import { Argument, ArgumentGeneratorReturn } from 'discord-akairo';
+import { Channel, GuildMember } from 'discord.js';
+import { highlightCommandArgs, highlightSubcommands } from './highlight-!';
+
+export default class HighlightBlockCommand extends BushCommand {
+ public constructor() {
+ super('highlight-block', {
+ aliases: [],
+ category: 'utilities',
+ description: highlightSubcommands.block,
+ usage: [],
+ examples: [],
+ clientPermissions: [],
+ userPermissions: []
+ });
+ }
+
+ public override *args(): ArgumentGeneratorReturn {
+ const target: ArgType<'member'> | ArgType<'channel'> = yield {
+ type: Argument.union('member', 'channel'),
+ match: 'rest',
+ prompt: {
+ start: highlightCommandArgs.block[0].description,
+ retry: highlightCommandArgs.block[0].retry,
+ optional: !highlightCommandArgs.block[0].required
+ }
+ };
+
+ return { target };
+ }
+
+ public override async exec(
+ message: BushMessage | BushSlashMessage,
+ args: { target: ArgType<'user'> | ArgType<'role'> | ArgType<'member'> }
+ ) {
+ assert(message.inGuild());
+
+ if (!(args.target instanceof GuildMember || args.target instanceof Channel))
+ return await message.util.reply(`${util.emojis.error} You can only block users or channels.`);
+
+ if (args.target instanceof Channel && !args.target.isTextBased())
+ return await message.util.reply(`${util.emojis.error} You can only block text-based channels.`);
+
+ const [highlight] = await Highlight.findOrCreate({
+ where: {
+ guild: message.guild.id,
+ user: message.author.id
+ }
+ });
+
+ const key = `blacklisted${args.target instanceof Channel ? 'Channels' : 'Users'}` as const;
+
+ if (highlight[key].includes(args.target.id))
+ return await message.util.reply({
+ content: `${util.emojis.error} You have already blocked ${args.target}.`,
+ allowedMentions: AllowedMentions.none()
+ });
+
+ highlight[key] = util.addToArray(highlight[key], args.target.id);
+ await highlight.save();
+
+ return await message.util.reply({
+ content: `${util.emojis.success} Successfully blocked ${args.target} from triggering your highlights.`,
+ allowedMentions: AllowedMentions.none()
+ });
+ }
+}
diff --git a/src/commands/utilities/highlight-clear.ts b/src/commands/utilities/highlight-clear.ts
new file mode 100644
index 0000000..aded467
--- /dev/null
+++ b/src/commands/utilities/highlight-clear.ts
@@ -0,0 +1,39 @@
+import { AllowedMentions, BushCommand, ConfirmationPrompt, Highlight, type BushMessage, type BushSlashMessage } from '#lib';
+import assert from 'assert';
+import { highlightSubcommands } from './highlight-!';
+
+export default class HighlightClearCommand extends BushCommand {
+ public constructor() {
+ super('highlight-clear', {
+ aliases: [],
+ category: 'utilities',
+ description: highlightSubcommands.clear,
+ usage: [],
+ examples: [],
+ clientPermissions: [],
+ userPermissions: []
+ });
+ }
+
+ public override async exec(message: BushMessage | BushSlashMessage) {
+ assert(message.inGuild());
+
+ const [highlight] = await Highlight.findOrCreate({
+ where: {
+ guild: message.guild.id,
+ user: message.author.id
+ }
+ });
+
+ const confirm = await ConfirmationPrompt.send(message, { content: `Are you sure you want to clear your highlight list?` });
+ if (!confirm) return await message.util.reply(`${util.emojis.warn} You decided not to clear your highlight list.`);
+
+ highlight.words = [];
+ await highlight.save();
+
+ return await message.util.reply({
+ content: `${util.emojis.success} Successfully cleared your highlight list.`,
+ allowedMentions: AllowedMentions.none()
+ });
+ }
+}
diff --git a/src/commands/utilities/highlight-matches.ts b/src/commands/utilities/highlight-matches.ts
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/commands/utilities/highlight-matches.ts
diff --git a/src/commands/utilities/highlight-remove.ts b/src/commands/utilities/highlight-remove.ts
new file mode 100644
index 0000000..0432a16
--- /dev/null
+++ b/src/commands/utilities/highlight-remove.ts
@@ -0,0 +1,57 @@
+import { AllowedMentions, BushCommand, Highlight, type ArgType, type BushMessage, type BushSlashMessage } from '#lib';
+import assert from 'assert';
+import { ArgumentGeneratorReturn } from 'discord-akairo';
+import { highlightCommandArgs, highlightSubcommands } from './highlight-!';
+
+export default class HighlightRemoveCommand extends BushCommand {
+ public constructor() {
+ super('highlight-remove', {
+ aliases: [],
+ category: 'utilities',
+ description: highlightSubcommands.remove,
+ usage: [],
+ examples: [],
+ clientPermissions: [],
+ userPermissions: []
+ });
+ }
+
+ public override *args(): ArgumentGeneratorReturn {
+ const word: ArgType<'string'> = yield {
+ type: 'string',
+ match: 'rest',
+ prompt: {
+ start: highlightCommandArgs.remove[0].description,
+ retry: highlightCommandArgs.remove[0].retry,
+ optional: !highlightCommandArgs.remove[0].required
+ }
+ };
+
+ return { word };
+ }
+
+ public override async exec(message: BushMessage | BushSlashMessage, args: { word: ArgType<'string'> }) {
+ assert(message.inGuild());
+
+ const [highlight] = await Highlight.findOrCreate({
+ where: {
+ guild: message.guild.id,
+ user: message.author.id
+ }
+ });
+
+ if (!highlight.words.some((w) => w.word === args.word))
+ return await message.util.reply({
+ content: `${util.emojis.error} You have not highlighted "${args.word}".`,
+ allowedMentions: AllowedMentions.none()
+ });
+
+ highlight.words = util.removeFromArray(highlight.words, highlight.words.find((w) => w.word === args.word)!);
+ await highlight.save();
+
+ return await message.util.reply({
+ content: `${util.emojis.success} Successfully removed "${args.word}" from your highlight list.`,
+ allowedMentions: AllowedMentions.none()
+ });
+ }
+}
diff --git a/src/commands/utilities/highlight-show.ts b/src/commands/utilities/highlight-show.ts
new file mode 100644
index 0000000..ab7c0c5
--- /dev/null
+++ b/src/commands/utilities/highlight-show.ts
@@ -0,0 +1,34 @@
+import { AllowedMentions, BushCommand, Highlight, type BushMessage, type BushSlashMessage } from '#lib';
+import assert from 'assert';
+import { Embed } from 'discord.js';
+import { highlightSubcommands } from './highlight-!';
+
+export default class HighlightShowCommand extends BushCommand {
+ public constructor() {
+ super('highlight-show', {
+ aliases: [],
+ category: 'utilities',
+ description: highlightSubcommands.show,
+ usage: [],
+ examples: [],
+ clientPermissions: [],
+ userPermissions: []
+ });
+ }
+
+ public override async exec(message: BushMessage | BushSlashMessage) {
+ assert(message.inGuild());
+
+ const [highlight] = await Highlight.findOrCreate({
+ where: {
+ guild: message.guild.id,
+ user: message.author.id
+ }
+ });
+
+ return await message.util.reply({
+ embeds: [new Embed().setTitle('Highlight List').setDescription(highlight.words.join('\n')).setColor(util.colors.default)],
+ allowedMentions: AllowedMentions.none()
+ });
+ }
+}
diff --git a/src/commands/utilities/highlight-unblock.ts b/src/commands/utilities/highlight-unblock.ts
new file mode 100644
index 0000000..7e5c0fb
--- /dev/null
+++ b/src/commands/utilities/highlight-unblock.ts
@@ -0,0 +1,69 @@
+import { AllowedMentions, BushCommand, Highlight, type ArgType, type BushMessage, type BushSlashMessage } from '#lib';
+import assert from 'assert';
+import { Argument, ArgumentGeneratorReturn } from 'discord-akairo';
+import { Channel, GuildMember } from 'discord.js';
+import { highlightCommandArgs, highlightSubcommands } from './highlight-!';
+
+export default class HighlightUnblockCommand extends BushCommand {
+ public constructor() {
+ super('highlight-unblock', {
+ aliases: [],
+ category: 'utilities',
+ description: highlightSubcommands.unblock,
+ usage: [],
+ examples: [],
+ clientPermissions: [],
+ userPermissions: []
+ });
+ }
+
+ public override *args(): ArgumentGeneratorReturn {
+ const target: ArgType<'member'> | ArgType<'channel'> = yield {
+ type: Argument.union('member', 'channel'),
+ match: 'rest',
+ prompt: {
+ start: highlightCommandArgs.unblock[0].description,
+ retry: highlightCommandArgs.unblock[0].retry,
+ optional: !highlightCommandArgs.unblock[0].required
+ }
+ };
+
+ return { target };
+ }
+
+ public override async exec(
+ message: BushMessage | BushSlashMessage,
+ args: { target: ArgType<'user'> | ArgType<'role'> | ArgType<'member'> }
+ ) {
+ assert(message.inGuild());
+
+ if (!(args.target instanceof GuildMember || args.target instanceof Channel))
+ return await message.util.reply(`${util.emojis.error} You can only unblock users or channels.`);
+
+ if (args.target instanceof Channel && !args.target.isTextBased())
+ return await message.util.reply(`${util.emojis.error} You can only unblock text-based channels.`);
+
+ const [highlight] = await Highlight.findOrCreate({
+ where: {
+ guild: message.guild.id,
+ user: message.author.id
+ }
+ });
+
+ const key = `blacklisted${args.target instanceof Channel ? 'Channels' : 'Users'}` as const;
+
+ if (!highlight[key].includes(args.target.id))
+ return await message.util.reply({
+ content: `${util.emojis.error} ${args.target} is not blocked so cannot be unblock.`,
+ allowedMentions: AllowedMentions.none()
+ });
+
+ highlight[key] = util.removeFromArray(highlight[key], args.target.id);
+ await highlight.save();
+
+ return await message.util.reply({
+ content: `${util.emojis.success} Successfully blocked ${args.target} from triggering your highlights.`,
+ allowedMentions: AllowedMentions.none()
+ });
+ }
+}
diff --git a/src/lib/common/HighlightManager.ts b/src/lib/common/HighlightManager.ts
new file mode 100644
index 0000000..a74ce9e
--- /dev/null
+++ b/src/lib/common/HighlightManager.ts
@@ -0,0 +1,71 @@
+import { Highlight, type BushMessage, type HighlightWord } from '#lib';
+import type { Snowflake } from 'discord.js';
+
+export class HighlightManager {
+ public cachedHighlights: Map</* guild */ Snowflake, Map</* word */ HighlightWord, /* users */ Set<Snowflake>>> = new Map();
+ public userLastTalkedCooldown = new Map<Snowflake, Map<Snowflake, Date>>();
+ public lastedDMedUserCooldown = new Map</* user */ Snowflake, /* last dm */ Date>();
+
+ public async syncCache() {
+ const highlights = await Highlight.findAll();
+
+ this.cachedHighlights.clear();
+
+ for (const highlight of highlights) {
+ highlight.words.forEach((word) => {
+ if (!this.cachedHighlights.has(highlight.guild)) this.cachedHighlights.set(highlight.guild, new Map());
+ const guildCache = this.cachedHighlights.get(highlight.guild)!;
+ if (!guildCache.get(word)) guildCache.set(word, new Set());
+ guildCache.get(word)!.add(highlight.user);
+ });
+ }
+ }
+
+ public checkMessage(message: BushMessage): Map<Snowflake, string> {
+ // even if there are multiple matches, only the first one is returned
+ const ret = new Map<Snowflake, string>();
+ if (!message.content || !message.inGuild()) return ret;
+ if (!this.cachedHighlights.has(message.guildId)) return ret;
+
+ const guildCache = this.cachedHighlights.get(message.guildId)!;
+
+ for (const [word, users] of guildCache.entries()) {
+ if (this.isMatch(message.content, word)) {
+ for (const user of users) {
+ if (!ret.has(user)) ret.set(user, word.word);
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ public async checkPhraseForUser(guild: Snowflake, user: Snowflake, phrase: string): Promise<Map<string, boolean>> {
+ const highlights = await Highlight.findAll({ where: { guild, user } });
+
+ const results = new Map<string, boolean>();
+
+ for (const highlight of highlights) {
+ for (const word of highlight.words) {
+ if (this.isMatch(phrase, word)) {
+ results.set(word.word, true);
+ }
+ }
+ }
+
+ return results;
+ }
+
+ private isMatch(phrase: string, word: HighlightWord) {
+ if (word.regex) {
+ return new RegExp(word.word, 'gi').test(phrase);
+ } else {
+ if (word.word.includes(' ')) {
+ return phrase.includes(word.word);
+ } else {
+ const words = phrase.split(/\s*\b\s/);
+ return words.includes(word.word);
+ }
+ }
+ }
+}
diff --git a/src/lib/extensions/discord-akairo/BushClient.ts b/src/lib/extensions/discord-akairo/BushClient.ts
index eb1fe88..3f1c944 100644
--- a/src/lib/extensions/discord-akairo/BushClient.ts
+++ b/src/lib/extensions/discord-akairo/BushClient.ts
@@ -10,7 +10,7 @@ import {
roleWithDuration,
snowflake
} from '#args';
-import type {
+import {
BushBaseGuildEmojiManager,
BushChannelManager,
BushClientEvents,
@@ -47,8 +47,10 @@ import type { Options as SequelizeOptions, Sequelize as SequelizeType } from 'se
import { fileURLToPath } from 'url';
import UpdateCacheTask from '../../../tasks/updateCache.js';
import UpdateStatsTask from '../../../tasks/updateStats.js';
+import { HighlightManager } from '../../common/HighlightManager';
import { ActivePunishment } from '../../models/instance/ActivePunishment.js';
import { Guild as GuildModel } from '../../models/instance/Guild.js';
+import { Highlight } from '../../models/instance/Highlight.js';
import { Level } from '../../models/instance/Level.js';
import { ModLog } from '../../models/instance/ModLog.js';
import { Reminder } from '../../models/instance/Reminder.js';
@@ -184,6 +186,11 @@ export class BushClient<Ready extends boolean = boolean> extends AkairoClient<Re
public sentry!: typeof Sentry;
/**
+ * Manages most aspects of the highlight command
+ */
+ public highlightManager = new HighlightManager();
+
+ /**
* @param config The configuration for the bot.
*/
public constructor(config: Config) {
@@ -403,6 +410,7 @@ export class BushClient<Ready extends boolean = boolean> extends AkairoClient<Re
Level.initModel(this.instanceDB);
StickyRole.initModel(this.instanceDB);
Reminder.initModel(this.instanceDB);
+ Highlight.initModel(this.instanceDB);
await this.instanceDB.sync({ alter: true }); // Sync all tables to fix everything if updated
await this.console.success('startup', `Successfully connected to <<instance database>>.`, false);
} catch (e) {
diff --git a/src/lib/extensions/discord-akairo/BushClientUtil.ts b/src/lib/extensions/discord-akairo/BushClientUtil.ts
index c3739d6..a3ddfed 100644
--- a/src/lib/extensions/discord-akairo/BushClientUtil.ts
+++ b/src/lib/extensions/discord-akairo/BushClientUtil.ts
@@ -520,6 +520,24 @@ export class BushClientUtil extends ClientUtil {
}
/**
+ * Remove an item from an array. All duplicates will be removed.
+ * @param array The array to remove an element from.
+ * @param value The element to remove from the array.
+ */
+ public removeFromArray<T>(array: T[], value: T): T[] {
+ return this.addOrRemoveFromArray('remove', array, value);
+ }
+
+ /**
+ * Add an item from an array. All duplicates will be removed.
+ * @param array The array to add an element to.
+ * @param value The element to add to the array.
+ */
+ public addToArray<T>(array: T[], value: T): T[] {
+ return this.addOrRemoveFromArray('add', array, value);
+ }
+
+ /**
* Surrounds a string to the begging an end of each element in an array.
* @param array The array you want to surround.
* @param surroundChar1 The character placed in the beginning of the element.
diff --git a/src/lib/extensions/discord-akairo/BushSlashMessage.ts b/src/lib/extensions/discord-akairo/BushSlashMessage.ts
index d342ea6..0860964 100644
--- a/src/lib/extensions/discord-akairo/BushSlashMessage.ts
+++ b/src/lib/extensions/discord-akairo/BushSlashMessage.ts
@@ -1,4 +1,5 @@
import {
+ BushCommandHandler,
BushGuildTextBasedChannel,
type BushClient,
type BushCommandUtil,
@@ -8,14 +9,15 @@ import {
type BushUser
} from '#lib';
import { AkairoMessage } from 'discord-akairo';
-import { type ChatInputCommandInteraction, type ContextMenuCommandInteraction } from 'discord.js';
+import { type ChatInputCommandInteraction } from 'discord.js';
export class BushSlashMessage extends AkairoMessage {
public declare client: BushClient;
- public declare util: BushCommandUtil<BushSlashMessage>;
+ public declare util: BushCommandUtil<BushSlashMessage> & { handler: BushCommandHandler };
public declare author: BushUser;
public declare member: BushGuildMember | null;
- public constructor(client: BushClient, interaction: ChatInputCommandInteraction | ContextMenuCommandInteraction) {
+ public declare interaction: ChatInputCommandInteraction;
+ public constructor(client: BushClient, interaction: ChatInputCommandInteraction) {
super(client, interaction);
}
}
diff --git a/src/lib/index.ts b/src/lib/index.ts
index 0c73875..7a9ab5f 100644
--- a/src/lib/index.ts
+++ b/src/lib/index.ts
@@ -69,6 +69,7 @@ export * from './extensions/discord.js/other.js';
export * from './models/BaseModel.js';
export * from './models/instance/ActivePunishment.js';
export * from './models/instance/Guild.js';
+export * from './models/instance/Highlight.js';
export * from './models/instance/Level.js';
export * from './models/instance/ModLog.js';
export * from './models/instance/Reminder.js';
diff --git a/src/lib/models/instance/Highlight.ts b/src/lib/models/instance/Highlight.ts
new file mode 100644
index 0000000..5889fad
--- /dev/null
+++ b/src/lib/models/instance/Highlight.ts
@@ -0,0 +1,81 @@
+import { type Snowflake } from 'discord.js';
+import { nanoid } from 'nanoid';
+import { type Sequelize } from 'sequelize';
+import { BaseModel } from '../BaseModel.js';
+const { DataTypes } = (await import('sequelize')).default;
+
+export interface HighlightModel {
+ pk: string;
+ user: Snowflake;
+ guild: Snowflake;
+ words: HighlightWord[];
+ blacklistedChannels: Snowflake[];
+ blacklistedUsers: Snowflake[];
+}
+
+export interface HighLightCreationAttributes {
+ pk?: string;
+ user: Snowflake;
+ guild: Snowflake;
+ words?: HighlightWord[];
+ blacklistedChannels?: Snowflake[];
+ blacklistedUsers?: Snowflake[];
+}
+
+export interface HighlightWord {
+ word: string;
+ regex: boolean;
+}
+
+/**
+ * List of words that should cause the user to be notified for if found in the specified guild.
+ */
+export class Highlight extends BaseModel<HighlightModel, HighLightCreationAttributes> implements HighlightModel {
+ /**
+ * The primary key of the highlight.
+ */
+ public declare pk: string;
+
+ /**
+ * The user that the highlight is for.
+ */
+ public declare user: Snowflake;
+
+ /**
+ * The guild to look for highlights in.
+ */
+ public declare guild: Snowflake;
+
+ /**
+ * The words to look for.
+ */
+ public declare words: HighlightWord[];
+
+ /**
+ * Channels that the user choose to ignore highlights in.
+ */
+ public declare blacklistedChannels: Snowflake[];
+
+ /**
+ * Users that the user choose to ignore highlights from.
+ */
+ public declare blacklistedUsers: Snowflake[];
+
+ /**
+ * Initializes the model.
+ * @param sequelize The sequelize instance.
+ */
+ public static initModel(sequelize: Sequelize): void {
+ Highlight.init(
+ {
+ pk: { type: DataTypes.STRING, primaryKey: true, defaultValue: nanoid },
+ user: { type: DataTypes.STRING, allowNull: false },
+ guild: { type: DataTypes.STRING, allowNull: false },
+ words: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] },
+ blacklistedChannels: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] },
+ blacklistedUsers: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] }
+ },
+ { sequelize }
+ );
+ }
+}
diff --git a/src/listeners/member-custom/bushLevelUpdate.ts b/src/listeners/member-custom/bushLevelUpdate.ts
index d0dc4a5..c43e341 100644
--- a/src/listeners/member-custom/bushLevelUpdate.ts
+++ b/src/listeners/member-custom/bushLevelUpdate.ts
@@ -1,6 +1,8 @@
import { BushListener, type BushClientEvents } from '#lib';
import { type TextChannel } from 'discord.js';
+type Args = BushClientEvents['bushLevelUpdate'];
+
export default class BushLevelUpdateListener extends BushListener {
public constructor() {
super('bushLevelUpdate', {
@@ -10,39 +12,48 @@ export default class BushLevelUpdateListener extends BushListener {
});
}
- public override async exec(...[member, _oldLevel, newLevel, _currentXp, message]: BushClientEvents['bushLevelUpdate']) {
- if (await message.guild.hasFeature('sendLevelUpMessages')) {
- void (async () => {
- const channel = ((await message.guild.channels
- .fetch((await message.guild.getSetting('levelUpChannel')) ?? message.channelId)
- .catch(() => null)) ?? message.channel) as TextChannel;
+ public override async exec(...[member, _oldLevel, newLevel, _currentXp, message]: Args) {
+ void this.sendLevelUpMessages(member, newLevel, message);
+ void this.assignLevelRoles(member, newLevel, message);
+ }
- const success = await channel
- .send(`${util.format.input(member.user.tag)} leveled up to level ${util.format.input(`${newLevel}`)}.`)
- .catch(() => null);
+ private async sendLevelUpMessages(member: Args[0], newLevel: Args[2], message: Args[4]) {
+ if (!(await message.guild.hasFeature('sendLevelUpMessages'))) return;
- if (!success) await client.console.warn('bushLevelUpdate', `Could not send level up message in ${message.guild}`);
- })();
- }
- void (async () => {
- const levelRoles = await message.guild.getSetting('levelRoles');
- if (Object.keys(levelRoles).length) {
- const promises = [];
- for (let i = 1; i <= newLevel; i++) {
- if (levelRoles[i]) {
- if (member.roles.cache.has(levelRoles[i])) continue;
- else promises.push(member.roles.add(levelRoles[i], `[LevelRoles] Role given for reaching level ${i}`));
- }
- }
- try {
- if (promises.length) await Promise.all(promises);
- } catch (e) {
- await member.guild.error(
- 'bushLevelUpdate',
- `There was an error adding level roles to ${member.user.tag} upon reaching to level ${newLevel}.\n${e?.message ?? e}`
- );
- }
+ const channel = ((await message.guild.channels
+ .fetch((await message.guild.getSetting('levelUpChannel')) ?? message.channelId)
+ .catch(() => null)) ?? message.channel) as TextChannel;
+
+ const success = await channel
+ .send(`${util.format.input(member.user.tag)} leveled up to level ${util.format.input(`${newLevel}`)}.`)
+ .catch(() => null);
+
+ if (!success)
+ await message.guild.error(
+ 'bushLevelUpdate',
+ `Could not send level up message for ${member.user.tag} in <#${message.channel.id}>.`
+ );
+ }
+
+ private async assignLevelRoles(member: Args[0], newLevel: Args[2], message: Args[4]) {
+ const levelRoles = await message.guild.getSetting('levelRoles');
+
+ if (!Object.keys(levelRoles).length) return;
+
+ const promises = [];
+ for (let i = 1; i <= newLevel; i++) {
+ if (levelRoles[i]) {
+ if (member.roles.cache.has(levelRoles[i])) continue;
+ else promises.push(member.roles.add(levelRoles[i], `[LevelRoles] Role given for reaching level ${i}`));
}
- })();
+ }
+ try {
+ if (promises.length) await Promise.all(promises);
+ } catch (e) {
+ await member.guild.error(
+ 'bushLevelUpdate',
+ `There was an error adding level roles to ${member.user.tag} upon reaching to level ${newLevel}.\n${e?.message ?? e}`
+ );
+ }
}
}
diff --git a/src/listeners/message/automodUpdate.ts b/src/listeners/message/automodUpdate.ts
index c96a5a9..d2e6f40 100644
--- a/src/listeners/message/automodUpdate.ts
+++ b/src/listeners/message/automodUpdate.ts
@@ -1,4 +1,4 @@
-import { AutoMod, BushListener, type BushClientEvents, type BushMessage } from '#lib';
+import { AutoMod, BushListener, type BushClientEvents } from '#lib';
export default class AutomodMessageUpdateListener extends BushListener {
public constructor() {
@@ -10,7 +10,8 @@ export default class AutomodMessageUpdateListener extends BushListener {
}
public override async exec(...[_, newMessage]: BushClientEvents['messageUpdate']) {
- const fullMessage = newMessage.partial ? await newMessage.fetch() : (newMessage as BushMessage);
+ const fullMessage = newMessage.partial ? await newMessage.fetch().catch(() => null) : newMessage;
+ if (!fullMessage) return;
return new AutoMod(fullMessage);
}
}
diff --git a/src/listeners/message/highlight.ts b/src/listeners/message/highlight.ts
new file mode 100644
index 0000000..25c8364
--- /dev/null
+++ b/src/listeners/message/highlight.ts
@@ -0,0 +1,15 @@
+import { BushListener, type BushClientEvents } from '#lib';
+
+export default class HighlightListener extends BushListener {
+ public constructor() {
+ super('highlight', {
+ emitter: 'client',
+ event: 'messageCreate',
+ category: 'message'
+ });
+ }
+
+ public override async exec(...[message]: BushClientEvents['messageCreate']) {
+ if (!message.inGuild()) return;
+ }
+}
diff --git a/yarn.lock b/yarn.lock
index a5842be..20abb5e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -14,45 +14,39 @@ __metadata:
languageName: node
linkType: hard
-"@discordjs/builders@npm:^0.12.0":
- version: 0.12.0
- resolution: "@discordjs/builders@npm:0.12.0"
+"@discordjs/builders@npm:^0.13.0-dev":
+ version: 0.13.0-dev.1644067366.5f4b44d
+ resolution: "@discordjs/builders@npm:0.13.0-dev.1644067366.5f4b44d"
dependencies:
- "@sindresorhus/is": ^4.3.0
+ "@sindresorhus/is": ^4.4.0
discord-api-types: ^0.26.1
ts-mixer: ^6.0.0
tslib: ^2.3.1
zod: ^3.11.6
- checksum: 3c4ef256371121938d5d75571e19f0697b1575dd9fb31a7ca47337d00a1ec45b2c1ce19a1261650a5f6393ac4116f8ed9e82120ce5de534be562dde919a2a6d7
- languageName: node
- linkType: hard
-
-"@discordjs/collection@npm:^0.4.0":
- version: 0.4.0
- resolution: "@discordjs/collection@npm:0.4.0"
- checksum: fa8fc4246921f3230eb6c5d6d4dc0caf9dd659fcc903175944edf4fb0a9ed9913fdf164733d3f1e644ef469bc79b0d38a526ee620b92169cb40e79b40b0c716b
+ checksum: fc4dda961d4df92e3c85601c1cf0eb7ff0df7e33a858a2cf55393b4bbcde6434968a4625ef369fcfc835675543506bde035845181d2d6eb5d4127d9be55dc62c
languageName: node
linkType: hard
-"@discordjs/collection@npm:^0.5.0":
- version: 0.5.0
- resolution: "@discordjs/collection@npm:0.5.0"
- checksum: a228979bb5f955cf095ce47441c55d0196cd99ff8484b57d26a45e58506c6646d776a98a9e11b822aab4e15fb2ac1aecfdebc63cde8ab7d04d3d74b716e80144
+"@discordjs/collection@npm:^0.6.0-dev":
+ version: 0.6.0-dev.1644067357.5f4b44d
+ resolution: "@discordjs/collection@npm:0.6.0-dev.1644067357.5f4b44d"
+ checksum: 419c84a767cc3abaa30c9248c16c4725867bd5779df56bb389ec87728eb20dd74460cd1d491768ded43511c220c829ea987d9ec8774c6ed78e7ab4623b26f417
languageName: node
linkType: hard
-"@discordjs/rest@npm:^0.3.0":
- version: 0.3.0
- resolution: "@discordjs/rest@npm:0.3.0"
+"@discordjs/rest@npm:@notenoughupdates/rest@dev":
+ version: 0.3.0-dev.1644086576.90b011b
+ resolution: "@notenoughupdates/rest@npm:0.3.0-dev.1644086576.90b011b"
dependencies:
- "@discordjs/collection": ^0.4.0
- "@sapphire/async-queue": ^1.1.9
- "@sapphire/snowflake": ^3.0.1
+ "@discordjs/collection": ^0.6.0-dev
+ "@sapphire/async-queue": ^1.2.0
+ "@sapphire/snowflake": ^3.1.0
+ "@types/node-fetch": ^2.5.12
discord-api-types: ^0.26.1
form-data: ^4.0.0
- node-fetch: ^2.6.5
+ node-fetch: ^2.6.7
tslib: ^2.3.1
- checksum: 0e5724156e0375b2181036d25d8847c5b7d8ab46a3409a19dad57ec9b3301d9127917a52558d3daa7e2b513804d4de9fcd5f6d56e056cc48dd567ebf26548c6d
+ checksum: 011c317c7155205e1280c6d543730db5a5f19282103804212c31248e0e0e5e752fc73dd4d67c91b4040ddb5e2c95941ff67a552b0b5a1837146b845f78be4fab
languageName: node
linkType: hard
@@ -199,14 +193,14 @@ __metadata:
languageName: node
linkType: hard
-"@sapphire/async-queue@npm:^1.1.9":
+"@sapphire/async-queue@npm:^1.2.0":
version: 1.2.0
resolution: "@sapphire/async-queue@npm:1.2.0"
checksum: 9959c91fe031e9350134740b68e64798eff1f72f1417f312a4f7bebbd875035a406ba5ae1e71640c3819dec10d0f86a0588b494088f353f85701f2f1196e4560
languageName: node
linkType: hard
-"@sapphire/snowflake@npm:^3.0.1":
+"@sapphire/snowflake@npm:^3.1.0":
version: 3.1.0
resolution: "@sapphire/snowflake@npm:3.1.0"
checksum: 979d41f531983b992e65f79a75016e92bb4f3984148bd7e2164059b4e8e18df0206c36c5a1a02f32c39c425b268f2e7871d9eef1eb5f1690f8837e451cc00812
@@ -290,7 +284,7 @@ __metadata:
languageName: node
linkType: hard
-"@sentry/types@npm:6.17.4":
+"@sentry/types@npm:6.17.4, @sentry/types@npm:^6.17.4":
version: 6.17.4
resolution: "@sentry/types@npm:6.17.4"
checksum: e2c514b42cb27143150bcbea3438e65b96deebf5804ffbe6d889c5997cd448ec61ed486a4b903fd57d7297cfcc9cb33d2dd0b3a394830a66fe3b99c0fee05aab
@@ -307,7 +301,7 @@ __metadata:
languageName: node
linkType: hard
-"@sindresorhus/is@npm:^4.2.0, @sindresorhus/is@npm:^4.3.0":
+"@sindresorhus/is@npm:^4.2.0, @sindresorhus/is@npm:^4.4.0":
version: 4.4.0
resolution: "@sindresorhus/is@npm:4.4.0"
checksum: 1d2471a75e03ce2182c3a3d014d027addeaeae1a7a2adfdb03c91cce17900b207e493db012e35ffa21808c563ce3b8e2e7c24646b3d5c27467e08bef8b0e16f0
@@ -471,6 +465,16 @@ __metadata:
languageName: node
linkType: hard
+"@types/node-fetch@npm:^2.5.12":
+ version: 2.5.12
+ resolution: "@types/node-fetch@npm:2.5.12"
+ dependencies:
+ "@types/node": "*"
+ form-data: ^3.0.0
+ checksum: ad63c85ba6a9477b8e057ec8682257738130d98e8ece4e31141789bd99df9d9147985cc8bc0cb5c8983ed5aa6bb95d46df23d1e055f4ad5cf8b82fc69cf626c7
+ languageName: node
+ linkType: hard
+
"@types/node-os-utils@npm:^1.2.0":
version: 1.2.0
resolution: "@types/node-os-utils@npm:1.2.0"
@@ -478,10 +482,10 @@ __metadata:
languageName: node
linkType: hard
-"@types/node@npm:*, @types/node@npm:^17.0.14":
- version: 17.0.14
- resolution: "@types/node@npm:17.0.14"
- checksum: cc059ce29686bad5890685f45741826a1a7d1d27382464f6d5fa00b72ba239f6f5b8245a7fa5a56c23ce928030dc76b165a4ab0b86dc078f05b44597d8fe1a46
+"@types/node@npm:*, @types/node@npm:^17.0.15":
+ version: 17.0.15
+ resolution: "@types/node@npm:17.0.15"
+ checksum: aa64ecf4fbcf9888e794dcdc20e98c49cdcb102b17e57c44ca56943904732d6cc250e766f8448a3cd71d6a40a4b597bd83c565e5bd9b982733fa3f9813d5c291
languageName: node
linkType: hard
@@ -862,18 +866,21 @@ __metadata:
version: 0.0.0-use.local
resolution: "bush-bot@workspace:."
dependencies:
+ "@discordjs/rest": "npm:@notenoughupdates/rest@dev"
"@notenoughupdates/discord.js-minesweeper": ^1.0.10
"@notenoughupdates/events-intercept": ^3.0.1
"@notenoughupdates/humanize-duration": ^4.0.1
"@notenoughupdates/simplify-number": ^1.0.1
"@notenoughupdates/wolfram-alpha-api": ^1.0.1
+ "@sapphire/snowflake": ^3.1.0
"@sentry/integrations": ^6.17.4
"@sentry/node": ^6.17.4
"@sentry/tracing": ^6.17.4
+ "@sentry/types": ^6.17.4
"@types/eslint": ^8.4.1
"@types/express": ^4.17.13
"@types/lodash": ^4.14.178
- "@types/node": ^17.0.14
+ "@types/node": ^17.0.15
"@types/node-os-utils": ^1.2.0
"@types/numeral": ^2.0.2
"@types/pg": ^8.6.4
@@ -907,7 +914,6 @@ __metadata:
rimraf: ^3.0.2
sequelize: 6.13.0
tinycolor2: ^1.4.2
- tslib: ^2.3.1
typescript: ^4.5.5
vm2: ^3.9.5
languageName: unknown
@@ -1212,18 +1218,18 @@ __metadata:
linkType: hard
"discord.js@npm:@notenoughupdates/discord.js@dev":
- version: 14.0.0-dev.1643976443.d5ed9fc
- resolution: "@notenoughupdates/discord.js@npm:14.0.0-dev.1643976443.d5ed9fc"
+ version: 14.0.0-dev.1644086580.90b011b
+ resolution: "@notenoughupdates/discord.js@npm:14.0.0-dev.1644086580.90b011b"
dependencies:
- "@discordjs/builders": ^0.12.0
- "@discordjs/collection": ^0.5.0
- "@discordjs/rest": ^0.3.0
- "@sapphire/snowflake": ^3.0.1
+ "@discordjs/builders": ^0.13.0-dev
+ "@discordjs/collection": ^0.6.0-dev
+ "@discordjs/rest": "npm:@notenoughupdates/rest@dev"
+ "@sapphire/snowflake": ^3.1.0
"@types/ws": ^8.2.2
discord-api-types: ^0.26.1
node-fetch: ^2.6.7
ws: ^8.4.2
- checksum: 30287c7b181ab35ad9f2a3a54ad4ff76483f2638e5b4a190dd2ca883d9e1cf0e1e58fd9cd0b2eb092d8ca3dc5641c27cea11e56f733529e015f7230848f9d4ab
+ checksum: d0b0e5fa11d32a3ffc16d860fe5d400059619afeab33cce60ba873089453a11c90ca9285033abf936743d6bb6cab5de32c9c052bf95235a452919a12bb018110
languageName: node
linkType: hard
@@ -1556,6 +1562,17 @@ __metadata:
languageName: node
linkType: hard
+"form-data@npm:^3.0.0":
+ version: 3.0.1
+ resolution: "form-data@npm:3.0.1"
+ dependencies:
+ asynckit: ^0.4.0
+ combined-stream: ^1.0.8
+ mime-types: ^2.1.12
+ checksum: b019e8d35c8afc14a2bd8a7a92fa4f525a4726b6d5a9740e8d2623c30e308fbb58dc8469f90415a856698933c8479b01646a9dff33c87cc4e76d72aedbbf860d
+ languageName: node
+ linkType: hard
+
"form-data@npm:^4.0.0":
version: 4.0.0
resolution: "form-data@npm:4.0.0"