aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorIRONM00N <64110067+IRONM00N@users.noreply.github.com>2022-02-23 21:38:40 -0500
committerIRONM00N <64110067+IRONM00N@users.noreply.github.com>2022-02-23 21:38:40 -0500
commit084a815f3799764d2dd697e8c693c7f80e0c7ab7 (patch)
tree7c62c41824d8ab25a67fe35016b6514744d33402 /src
parentd64563e89034b8e016d1b0a24e2b44280900b14c (diff)
downloadtanzanite-084a815f3799764d2dd697e8c693c7f80e0c7ab7.tar.gz
tanzanite-084a815f3799764d2dd697e8c693c7f80e0c7ab7.tar.bz2
tanzanite-084a815f3799764d2dd697e8c693c7f80e0c7ab7.zip
feat(hl): make functional, no cool downs yet, restrcited to su
Diffstat (limited to 'src')
-rw-r--r--src/commands/utilities/highlight-!.ts6
-rw-r--r--src/commands/utilities/highlight-add.ts61
-rw-r--r--src/commands/utilities/highlight-block.ts11
-rw-r--r--src/commands/utilities/highlight-clear.ts13
-rw-r--r--src/commands/utilities/highlight-matches.ts56
-rw-r--r--src/commands/utilities/highlight-remove.ts46
-rw-r--r--src/commands/utilities/highlight-show.ts33
-rw-r--r--src/lib/common/HighlightManager.ts192
-rw-r--r--src/lib/extensions/discord-akairo/BushClient.ts1
-rw-r--r--src/lib/models/instance/Guild.ts5
-rw-r--r--src/listeners/message/highlight.ts9
11 files changed, 340 insertions, 93 deletions
diff --git a/src/commands/utilities/highlight-!.ts b/src/commands/utilities/highlight-!.ts
index 687990f..dd6da8d 100644
--- a/src/commands/utilities/highlight-!.ts
+++ b/src/commands/utilities/highlight-!.ts
@@ -40,7 +40,7 @@ export const highlightCommandArgs: {
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,
+ type: ApplicationCommandOptionType.String,
required: true
}
],
@@ -49,7 +49,7 @@ export const highlightCommandArgs: {
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,
+ type: ApplicationCommandOptionType.String,
required: true
}
],
@@ -105,7 +105,7 @@ export default class HighlightCommand extends BushCommand {
channel: 'guild',
clientPermissions: (m) => util.clientSendAndPermCheck(m),
userPermissions: [],
- ownerOnly: true
+ superUserOnly: true
});
}
diff --git a/src/commands/utilities/highlight-add.ts b/src/commands/utilities/highlight-add.ts
index 316a931..9c7506e 100644
--- a/src/commands/utilities/highlight-add.ts
+++ b/src/commands/utilities/highlight-add.ts
@@ -1,6 +1,5 @@
-import { AllowedMentions, BushCommand, Highlight, type ArgType, type BushMessage, type BushSlashMessage } from '#lib';
+import { AllowedMentions, BushCommand, type ArgType, type BushMessage, type BushSlashMessage } from '#lib';
import assert from 'assert';
-import { ArgumentGeneratorReturn } from 'discord-akairo';
import { highlightCommandArgs, highlightSubcommands } from './highlight-!.js';
export default class HighlightAddCommand extends BushCommand {
@@ -9,6 +8,28 @@ export default class HighlightAddCommand extends BushCommand {
aliases: [],
category: 'utilities',
description: highlightSubcommands.add,
+ args: [
+ {
+ id: 'word',
+ description: highlightCommandArgs.add[0].description,
+ type: 'string',
+ match: 'rest',
+ prompt: highlightCommandArgs.add[0].description,
+ retry: highlightCommandArgs.add[0].retry,
+ optional: !highlightCommandArgs.add[0].required,
+ only: 'text',
+ slashType: false
+ },
+ {
+ id: 'regex',
+ description: highlightCommandArgs.add[1].description,
+ match: 'flag',
+ flag: '--regex',
+ prompt: highlightCommandArgs.add[1].description,
+ only: 'text',
+ slashType: false
+ }
+ ],
usage: [],
examples: [],
clientPermissions: [],
@@ -16,25 +37,6 @@ export default class HighlightAddCommand extends BushCommand {
});
}
- 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'> }
@@ -58,22 +60,19 @@ export default class HighlightAddCommand extends BushCommand {
}
}
- const [highlight] = await Highlight.findOrCreate({
- where: {
- guild: message.guild.id,
- user: message.author.id
- }
+ const res = await client.highlightManager.addHighlight(message.guild.id, message.author.id, {
+ word: args.word,
+ regex: args.regex
});
- if (highlight.words.some((w) => w.word === args.word))
+ if (typeof res === 'string')
+ return await message.util.reply({ content: `${util.emojis.error} ${res}`, allowedMentions: AllowedMentions.none() });
+ else if (!res)
return await message.util.reply({
- content: `${util.emojis.error} You have already highlighted "${args.word}".`,
+ content: `${util.emojis.error} There was an error highlighting "${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
index f546958..efc5437 100644
--- a/src/commands/utilities/highlight-block.ts
+++ b/src/commands/utilities/highlight-block.ts
@@ -33,11 +33,16 @@ export default class HighlightBlockCommand extends BushCommand {
public override async exec(
message: BushMessage | BushSlashMessage,
- args: { target: ArgType<'user'> | ArgType<'role'> | ArgType<'member'> }
+ args: { target: string | ArgType<'member'> | ArgType<'channel'> }
) {
assert(message.inGuild());
- if (!(args.target instanceof GuildMember || args.target instanceof Channel))
+ args.target =
+ typeof args.target === 'string'
+ ? (await util.arg.cast(util.arg.union('member', 'channel'), message, args.target))!
+ : args.target;
+
+ if (!args.target || !(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())
@@ -54,6 +59,7 @@ export default class HighlightBlockCommand extends BushCommand {
if (highlight[key].includes(args.target.id))
return await message.util.reply({
+ // eslint-disable-next-line @typescript-eslint/no-base-to-string
content: `${util.emojis.error} You have already blocked ${args.target}.`,
allowedMentions: AllowedMentions.none()
});
@@ -62,6 +68,7 @@ export default class HighlightBlockCommand extends BushCommand {
await highlight.save();
return await message.util.reply({
+ // eslint-disable-next-line @typescript-eslint/no-base-to-string
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
index e1cce2d..33893b9 100644
--- a/src/commands/utilities/highlight-clear.ts
+++ b/src/commands/utilities/highlight-clear.ts
@@ -1,4 +1,4 @@
-import { AllowedMentions, BushCommand, ConfirmationPrompt, Highlight, type BushMessage, type BushSlashMessage } from '#lib';
+import { AllowedMentions, BushCommand, ConfirmationPrompt, type BushMessage, type BushSlashMessage } from '#lib';
import assert from 'assert';
import { highlightSubcommands } from './highlight-!.js';
@@ -18,18 +18,13 @@ export default class HighlightClearCommand extends BushCommand {
public override async exec(message: BushMessage | BushSlashMessage) {
assert(message.inGuild());
- const [highlight] = await Highlight.findOrCreate({
- where: {
- guild: message.guild.id,
- user: message.author.id
- }
- });
+ if (message.util.isSlashMessage(message)) await message.interaction.deferReply();
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();
+ const success = await client.highlightManager.removeAllHighlights(message.author.id, message.guild.id);
+ if (!success) return await message.util.reply(`${util.emojis.error} There was an error clearing your highlight list.`);
return await message.util.reply({
content: `${util.emojis.success} Successfully cleared your highlight list.`,
diff --git a/src/commands/utilities/highlight-matches.ts b/src/commands/utilities/highlight-matches.ts
index e69de29..aa8308c 100644
--- a/src/commands/utilities/highlight-matches.ts
+++ b/src/commands/utilities/highlight-matches.ts
@@ -0,0 +1,56 @@
+import { BushCommand, ButtonPaginator, type ArgType, type BushMessage, type BushSlashMessage } from '#lib';
+import assert from 'assert';
+import { ArgumentGeneratorReturn } from 'discord-akairo';
+import { APIEmbed } from 'discord-api-types/v9';
+import { highlightCommandArgs, highlightSubcommands } from './highlight-!.js';
+
+export default class HighlightMatchesCommand extends BushCommand {
+ public constructor() {
+ super('highlight-matches', {
+ aliases: [],
+ category: 'utilities',
+ description: highlightSubcommands.matches,
+ usage: [],
+ examples: [],
+ clientPermissions: [],
+ userPermissions: []
+ });
+ }
+
+ public override *args(): ArgumentGeneratorReturn {
+ const phrase: ArgType<'string'> = yield {
+ type: 'string',
+ match: 'rest',
+ prompt: {
+ start: highlightCommandArgs.matches[0].description,
+ retry: highlightCommandArgs.matches[0].retry,
+ optional: !highlightCommandArgs.matches[0].required
+ }
+ };
+
+ return { phrase };
+ }
+
+ public override async exec(message: BushMessage | BushSlashMessage, args: { phrase: ArgType<'string'> }) {
+ assert(message.inGuild());
+
+ const res = await client.highlightManager.checkPhrase(message.guild.id, message.author.id, args.phrase);
+
+ if (!res.size) return await message.util.reply(`${util.emojis.error} You are not highlighting any words`);
+
+ const lines = res.map(
+ (passed, hl) => `${passed ? util.emojis.check : util.emojis.cross} ${hl.regex ? `/${hl.word}/gi` : hl.word}`
+ );
+ const chunked = util.chunk(lines, 10);
+
+ const pages = chunked.map(
+ (chunk): APIEmbed => ({
+ title: `Matches`,
+ description: chunk.join('\n'),
+ color: util.colors.default
+ })
+ );
+
+ return pages.length > 1 ? await ButtonPaginator.send(message, pages) : message.util.send({ embeds: pages });
+ }
+}
diff --git a/src/commands/utilities/highlight-remove.ts b/src/commands/utilities/highlight-remove.ts
index 1f7c4c0..7e8c416 100644
--- a/src/commands/utilities/highlight-remove.ts
+++ b/src/commands/utilities/highlight-remove.ts
@@ -1,6 +1,5 @@
-import { AllowedMentions, BushCommand, Highlight, type ArgType, type BushMessage, type BushSlashMessage } from '#lib';
+import { AllowedMentions, BushCommand, type ArgType, type BushMessage, type BushSlashMessage } from '#lib';
import assert from 'assert';
-import { ArgumentGeneratorReturn } from 'discord-akairo';
import { highlightCommandArgs, highlightSubcommands } from './highlight-!.js';
export default class HighlightRemoveCommand extends BushCommand {
@@ -9,6 +8,19 @@ export default class HighlightRemoveCommand extends BushCommand {
aliases: [],
category: 'utilities',
description: highlightSubcommands.remove,
+ args: [
+ {
+ id: 'word',
+ description: highlightCommandArgs.remove[0].description,
+ type: 'string',
+ match: 'rest',
+ prompt: highlightCommandArgs.remove[0].description,
+ retry: highlightCommandArgs.remove[0].retry,
+ optional: !highlightCommandArgs.remove[0].required,
+ only: 'text',
+ slashType: false
+ }
+ ],
usage: [],
examples: [],
clientPermissions: [],
@@ -16,39 +28,19 @@ export default class HighlightRemoveCommand extends BushCommand {
});
}
- 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
- }
- });
+ const res = await client.highlightManager.removeHighlight(message.guild.id, message.author.id, args.word);
- if (!highlight.words.some((w) => w.word === args.word))
+ if (typeof res === 'string')
+ return await message.util.reply({ content: `${util.emojis.error} ${res}`, allowedMentions: AllowedMentions.none() });
+ else if (!res)
return await message.util.reply({
- content: `${util.emojis.error} You have not highlighted "${args.word}".`,
+ content: `${util.emojis.error} There was an error unhighlighting "${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
index b9f414f..c3c6723 100644
--- a/src/commands/utilities/highlight-show.ts
+++ b/src/commands/utilities/highlight-show.ts
@@ -26,8 +26,39 @@ export default class HighlightShowCommand extends BushCommand {
}
});
+ if (!highlight.words.length) return message.util.reply(`${util.emojis.error} You are not highlighting any words.`);
+
+ const embed = new Embed()
+ .setTitle('Highlight List')
+ .setDescription(
+ highlight.words
+ .map((hl) => (hl.regex ? `/${hl.word}/gi` : hl.word))
+ .join('\n')
+ .substring(0, 4096)
+ )
+ .setColor(util.colors.default);
+
+ if (highlight.blacklistedChannels.length)
+ embed.addField({
+ name: 'Ignored Channels',
+ value: highlight.blacklistedChannels
+ .map((c) => `<#${c}>`)
+ .join('\n')
+ .substring(0, 1024),
+ inline: true
+ });
+ if (highlight.blacklistedUsers.length)
+ embed.addField({
+ name: 'Ignored Users',
+ value: highlight.blacklistedUsers
+ .map((u) => `<@!${u}>`)
+ .join('\n')
+ .substring(0, 1024),
+ inline: true
+ });
+
return await message.util.reply({
- embeds: [new Embed().setTitle('Highlight List').setDescription(highlight.words.join('\n')).setColor(util.colors.default)],
+ embeds: [embed],
allowedMentions: AllowedMentions.none()
});
}
diff --git a/src/lib/common/HighlightManager.ts b/src/lib/common/HighlightManager.ts
index a74ce9e..6194255 100644
--- a/src/lib/common/HighlightManager.ts
+++ b/src/lib/common/HighlightManager.ts
@@ -1,19 +1,48 @@
import { Highlight, type BushMessage, type HighlightWord } from '#lib';
-import type { Snowflake } from 'discord.js';
+import assert from 'assert';
+import { Collection, 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>();
+ /**
+ * Cached highlights: guildId -> word -> userId
+ */
+ public readonly cachedHighlights = new Collection<
+ /* guild */ Snowflake,
+ Collection</* word */ HighlightWord, /* users */ Set<Snowflake>>
+ >();
- public async syncCache() {
+ /**
+ * A collection of cooldowns of when a user last sent a message in a particular guild.
+ */
+ public readonly userLastTalkedCooldown = new Collection<
+ /* guild */ Snowflake,
+ Collection</* user */ Snowflake, /* last message */ Date>
+ >();
+
+ /**
+ * Users that users have blocked
+ */
+ public readonly userBlocks = new Collection<
+ /* guild */ Snowflake,
+ Collection</* word */ Snowflake, /* users */ Set<Snowflake>>
+ >();
+
+ /**
+ * A collection of cooldowns of when the bot last sent each user a highlight message.
+ */
+ public readonly lastedDMedUserCooldown = new Collection</* user */ Snowflake, /* last dm */ Date>();
+
+ /**
+ * Sync the cache with the database.
+ */
+ public async syncCache(): Promise<void> {
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());
+ if (!this.cachedHighlights.has(highlight.guild)) this.cachedHighlights.set(highlight.guild, new Collection());
const guildCache = this.cachedHighlights.get(highlight.guild)!;
if (!guildCache.get(word)) guildCache.set(word, new Set());
guildCache.get(word)!.add(highlight.user);
@@ -21,9 +50,14 @@ export class HighlightManager {
}
}
- public checkMessage(message: BushMessage): Map<Snowflake, string> {
+ /**
+ * Checks a message for highlights.
+ * @param message The message to check.
+ * @returns A collection users mapped to the highlight matched
+ */
+ public checkMessage(message: BushMessage): Collection<Snowflake, HighlightWord> {
// even if there are multiple matches, only the first one is returned
- const ret = new Map<Snowflake, string>();
+ const ret = new Collection<Snowflake, HighlightWord>();
if (!message.content || !message.inGuild()) return ret;
if (!this.cachedHighlights.has(message.guildId)) return ret;
@@ -32,7 +66,7 @@ export class HighlightManager {
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);
+ if (!ret.has(user)) ret.set(user, word);
}
}
}
@@ -40,32 +74,150 @@ export class HighlightManager {
return ret;
}
- public async checkPhraseForUser(guild: Snowflake, user: Snowflake, phrase: string): Promise<Map<string, boolean>> {
+ /**
+ * Checks a user provided phrase for their highlights.
+ * @param guild The guild to check in.
+ * @param user The user to get the highlights for.
+ * @param phrase The phrase for highlights in.
+ * @returns A collection of the user's highlights mapped to weather or not it was matched.
+ */
+ public async checkPhrase(guild: Snowflake, user: Snowflake, phrase: string): Promise<Collection<HighlightWord, boolean>> {
const highlights = await Highlight.findAll({ where: { guild, user } });
- const results = new Map<string, boolean>();
+ const results = new Collection<HighlightWord, boolean>();
for (const highlight of highlights) {
for (const word of highlight.words) {
- if (this.isMatch(phrase, word)) {
- results.set(word.word, true);
- }
+ results.set(word, this.isMatch(phrase, word));
}
}
return results;
}
- private isMatch(phrase: string, word: HighlightWord) {
- if (word.regex) {
- return new RegExp(word.word, 'gi').test(phrase);
+ /**
+ * Checks a particular highlight for a match within a phrase.
+ * @param phrase The phrase to check for the word in.
+ * @param hl The highlight to check for.
+ * @returns Whether or not the highlight was matched.
+ */
+ private isMatch(phrase: string, hl: HighlightWord): boolean {
+ if (hl.regex) {
+ return new RegExp(hl.word, 'gi').test(phrase);
} else {
- if (word.word.includes(' ')) {
- return phrase.includes(word.word);
+ if (hl.word.includes(' ')) {
+ return phrase.toLocaleLowerCase().includes(hl.word.toLocaleLowerCase());
} else {
const words = phrase.split(/\s*\b\s/);
- return words.includes(word.word);
+ return words.includes(hl.word);
}
}
}
+
+ /**
+ * Adds a new highlight to a user in a particular guild.
+ * @param guild The guild to add the highlight to.
+ * @param user The user to add the highlight to.
+ * @param hl The highlight to add.
+ * @returns A string representing a user error or a boolean indicating the database success.
+ */
+ public async addHighlight(guild: Snowflake, user: Snowflake, hl: HighlightWord): Promise<string | boolean> {
+ if (!this.cachedHighlights.has(guild)) this.cachedHighlights.set(guild, new Collection());
+ const guildCache = this.cachedHighlights.get(guild)!;
+
+ if (!guildCache.has(hl)) guildCache.set(hl, new Set());
+ guildCache.get(hl)!.add(user);
+
+ const [highlight] = await Highlight.findOrCreate({ where: { guild, user } });
+
+ if (highlight.words.some((w) => w.word === hl.word)) return `You have already highlighted "${hl.word}".`;
+
+ highlight.words = util.addToArray(highlight.words, hl);
+
+ return !!(await highlight.save().catch(() => false));
+ }
+
+ /**
+ * Removes a highlighted word for a user in a particular guild.
+ * @param guild The guild to remove the highlight from.
+ * @param user The user to remove the highlight from.
+ * @param hl The word to remove.
+ * @returns A string representing a user error or a boolean indicating the database success.
+ */
+ public async removeHighlight(guild: Snowflake, user: Snowflake, hl: string): Promise<string | boolean> {
+ if (!this.cachedHighlights.has(guild)) this.cachedHighlights.set(guild, new Collection());
+ const guildCache = this.cachedHighlights.get(guild)!;
+
+ const wordCache = guildCache.find((_, key) => key.word === hl);
+
+ if (!wordCache?.has(user)) return `You have not highlighted "${hl}".`;
+
+ wordCache!.delete(user);
+
+ const [highlight] = await Highlight.findOrCreate({ where: { guild, user } });
+
+ const toRemove = highlight.words.find((w) => w.word === hl);
+ if (!toRemove) return `Uhhhhh... This shouldn't happen.`;
+
+ highlight.words = util.removeFromArray(highlight.words, toRemove);
+
+ return !!(await highlight.save().catch(() => false));
+ }
+
+ /**
+ * Remove all highlight words for a user in a particular guild.
+ * @param guild The guild to remove the highlights from.
+ * @param user The user to remove the highlights from.
+ * @returns A boolean indicating the database success.
+ */
+ public async removeAllHighlights(guild: Snowflake, user: Snowflake): Promise<boolean> {
+ if (!this.cachedHighlights.has(guild)) this.cachedHighlights.set(guild, new Collection());
+ const guildCache = this.cachedHighlights.get(guild)!;
+
+ for (const [word, users] of guildCache.entries()) {
+ if (users.has(user)) users.delete(user);
+ if (!users.size) guildCache.delete(word);
+ }
+
+ const [highlight] = await Highlight.findOrCreate({ where: { guild, user } });
+
+ highlight.words = [];
+
+ return !!(await highlight.save().catch(() => false));
+ }
+
+ public async notify(message: BushMessage, user: Snowflake, hl: HighlightWord): Promise<boolean> {
+ assert(message.inGuild());
+ const recentMessages = message.channel.messages.cache
+ .filter((m) => m.createdTimestamp <= message.createdTimestamp && m.id !== message.id)
+ .filter((m) => m.cleanContent?.trim().length > 0)
+ .sort((a, b) => b.createdTimestamp - a.createdTimestamp)
+ .first(4)
+ .reverse();
+
+ return client.users
+ .send(user, {
+ // eslint-disable-next-line @typescript-eslint/no-base-to-string
+ content: `In ${util.format.input(message.guild.name)} ${message.channel}, your highlight "${hl.word}" was matched:`,
+ embeds: [
+ {
+ description: [...recentMessages, message]
+ .map(
+ (m) =>
+ `${util.timestamp(m.createdAt, 't')} ${util.format.input(`${m.author.tag}:`)} ${m.cleanContent
+ .trim()
+ .substring(0, 512)}`
+ )
+ .join('\n'),
+ author: { name: hl.regex ? `/${hl.word}/gi` : hl.word },
+ fields: [{ name: 'Source message', value: `[Jump to message](${message.url})` }],
+ color: util.colors.default,
+ footer: { text: 'Triggered' },
+ timestamp: message.createdAt.toISOString()
+ }
+ ]
+ })
+ .then(() => true)
+ .catch(() => false);
+ }
}
diff --git a/src/lib/extensions/discord-akairo/BushClient.ts b/src/lib/extensions/discord-akairo/BushClient.ts
index e46e701..266680a 100644
--- a/src/lib/extensions/discord-akairo/BushClient.ts
+++ b/src/lib/extensions/discord-akairo/BushClient.ts
@@ -467,6 +467,7 @@ export class BushClient<Ready extends boolean = boolean> extends AkairoClient<Re
});
try {
+ await this.highlightManager.syncCache();
await UpdateCacheTask.init(this);
void this.console.success('startup', `Successfully created <<cache>>.`, false);
this.stats.commandsUsed = await UpdateStatsTask.init();
diff --git a/src/lib/models/instance/Guild.ts b/src/lib/models/instance/Guild.ts
index cdf3552..7fe7ac1 100644
--- a/src/lib/models/instance/Guild.ts
+++ b/src/lib/models/instance/Guild.ts
@@ -399,6 +399,11 @@ export const guildFeaturesObj = asGuildFeature({
description: 'Use the Perspective API to detect toxicity.',
default: false,
notConfigurable: true
+ },
+ highlight: {
+ name: 'Highlight',
+ description: 'Allows the highlight command to be used.',
+ default: true
}
});
diff --git a/src/listeners/message/highlight.ts b/src/listeners/message/highlight.ts
index 25c8364..d3d7bfb 100644
--- a/src/listeners/message/highlight.ts
+++ b/src/listeners/message/highlight.ts
@@ -11,5 +11,14 @@ export default class HighlightListener extends BushListener {
public override async exec(...[message]: BushClientEvents['messageCreate']) {
if (!message.inGuild()) return;
+ if (message.author.bot) return;
+ if (!(await message.guild.hasFeature('highlight'))) return; // allows highlighting to be disabled on a guild-by-guild basis
+
+ const res = client.highlightManager.checkMessage(message);
+
+ for (const [user, hl] of res.entries()) {
+ if (message.author.id === user) continue;
+ void client.highlightManager.notify(message, user, hl);
+ }
}
}