diff options
109 files changed, 2408 insertions, 2231 deletions
diff --git a/.yarnrc.yml b/.yarnrc.yml index bd5ccfd..33ad4a3 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -2,7 +2,7 @@ enableGlobalCache: true enableTelemetry: false -nodeLinker: pnpm +nodeLinker: node-modules plugins: - path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs diff --git a/lib/automod/AutomodShared.ts b/lib/automod/AutomodShared.ts index 29b0536..48217dd 100644 --- a/lib/automod/AutomodShared.ts +++ b/lib/automod/AutomodShared.ts @@ -162,7 +162,7 @@ export async function handleAutomodInteraction(interaction: ButtonInteraction) { ephemeral: true }); - const check = victim ? await Moderation.permissionCheck(moderator, victim, 'ban', true) : true; + const check = victim ? await Moderation.permissionCheck(moderator, victim, Moderation.Action.Ban, true) : true; if (check !== true) return interaction.reply({ content: check, ephemeral: true }); const result = await interaction.guild?.customBan({ @@ -203,7 +203,7 @@ export async function handleAutomodInteraction(interaction: ButtonInteraction) { ephemeral: true }); - const check = await Moderation.permissionCheck(moderator, victim, 'unmute', true); + const check = await Moderation.permissionCheck(moderator, victim, Moderation.Action.Unmute, true); if (check !== true) return interaction.reply({ content: check, ephemeral: true }); const check2 = await Moderation.checkMutePermissions(interaction.guild); diff --git a/lib/common/Appeals.ts b/lib/common/Appeals.ts new file mode 100644 index 0000000..43c56fd --- /dev/null +++ b/lib/common/Appeals.ts @@ -0,0 +1,273 @@ +import { AppealStatus, ModLog } from '#lib/models/instance/ModLog.js'; +import { colors, emojis } from '#lib/utils/Constants.js'; +import { input } from '#lib/utils/Format.js'; +import { capitalize, ModalInput } from '#lib/utils/Utils.js'; +import { + ActionRowBuilder, + ButtonBuilder, + ButtonStyle, + EmbedBuilder, + TextInputStyle, + type ButtonInteraction, + type ModalSubmitInteraction, + type Snowflake +} from 'discord.js'; +import assert from 'node:assert/strict'; +import { Action, punishments } from './Moderation.js'; + +type AppealBase = 'appeal_attempt' | 'appeal_submit' | 'appeal_accept' | 'appeal_deny'; + +type RawAppealInfo = [baseId: AppealBase, punishment: `${Action}`, guildId: Snowflake, userId: Snowflake, modlogId: string]; + +type AppealInfo = [baseId: AppealBase, punishment: Action, guildId: Snowflake, userId: Snowflake, modlogId: string]; + +export type AppealIdString = + `${RawAppealInfo[0]};${RawAppealInfo[1]};${RawAppealInfo[2]};${RawAppealInfo[3]};${RawAppealInfo[4]}`; + +function parseAppeal(customId: AppealIdString | string): AppealInfo { + const [baseId, _punishment, guildId, userId, modlogId] = customId.split(';') as RawAppealInfo; + + const punishment = Action[Action[_punishment] as keyof typeof Action]; + + return [baseId, punishment, guildId, userId, modlogId]; +} + +/** + * Handles when a user clicks the "Appeal Punishment" button on a punishment dm. + * @param interaction A button interaction with a custom id thar starts with "appeal_attempt;". + */ +export async function handleAppealAttempt(interaction: ButtonInteraction) { + const [baseId, punishment, guildId, userId, modlogId] = parseAppeal(interaction.customId); + + const { base, past, appealCustom } = punishments[punishment]; + const appealName = appealCustom ?? capitalize(base); + + const guild = interaction.client.guilds.resolve(guildId); + if (!guild) { + return await interaction.reply(`${emojis.error} I am no longer in that server.`); + } + + const modlog = await ModLog.findByPk(modlogId); + if (!modlog) { + return await interaction.reply(`:skull: I cannot find the modlog ${input(modlogId)}. Please report this to my developers.`); + } + + switch (modlog.appeal) { + case AppealStatus.Accepted: + return await interaction.reply( + `${emojis.error} Your punishment (${input(modlogId)}) has already been appealed and accepted.` + ); + case AppealStatus.Denied: + return await interaction.reply( + `${emojis.error} Your punishment (${input(modlogId)}) has already been appealed and denied.` + ); + case AppealStatus.Submitted: + return await interaction.reply( + `${emojis.error} Your punishment (${input( + modlogId + )}) has already been appealed, please be patient for a moderator to review your appeal.` + ); + default: { + const _exhaustiveCheck: AppealStatus.None = modlog.appeal; + } + } + + const baseInput = { + style: TextInputStyle.Paragraph, + required: true, + maxLength: 1024 + }; + + return await interaction.showModal({ + customId: `appeal_submit;${punishment};${guildId};${userId};${modlogId}`, + title: `${appealName} Appeal`, + components: [ + ModalInput({ + ...baseInput, + label: `Why were you ${past}?`, + placeholder: `Why do you think you received a ${base}?`, + customId: 'appeal_reason' + }), + ModalInput({ + ...baseInput, + label: 'Do you believe it was fair?', + placeholder: `Do you think that your ${base} is fair?`, + customId: 'appeal_fair' + }), + ModalInput({ + ...baseInput, + label: `Why should your ${base} be removed?`, + placeholder: `Why do you think your ${base} be removed?`, + customId: 'appeal_why' + }) + ] + }); +} + +/** + * Handles when a user submits the modal for appealing a punishment. + * @param interaction A modal interaction with a custom id that starts with "appeal_submit;". + */ +export async function handleAppealSubmit(interaction: ModalSubmitInteraction) { + const [baseId, punishment, guildId, userId, modlogId] = parseAppeal(interaction.customId); + + const { base, past, appealCustom } = punishments[punishment]; + const appealName = appealCustom ?? capitalize(base); + + const guild = interaction.client.guilds.resolve(guildId); + if (!guild) { + return await interaction.reply(`${emojis.error} I am no longer in that server.`); + } + + const modlog = await ModLog.findByPk(modlogId); + if (!modlog) { + return await interaction.reply(`:skull: I cannot find the modlog ${input(modlogId)}. Please report this to my developers.`); + } + + if (modlog.appeal !== AppealStatus.None) { + return await interaction.reply(`Invalid appeal status: ${modlog.appeal}`); + } + + modlog.appeal = AppealStatus.Submitted; + await modlog.save(); + + const appealChannel = await guild.getLogChannel('appeals'); + if (!appealChannel) { + return await interaction.reply(`${emojis.error} I could not find an appeals channel in this server.`); + } + + const user = await interaction.client.users.fetch(userId); + + const reason = interaction.fields.getTextInputValue('appeal_reason'); + const fair = interaction.fields.getTextInputValue('appeal_fair'); + const why = interaction.fields.getTextInputValue('appeal_why'); + + const embed = new EmbedBuilder() + .setTitle(`${appealName} Appeal`) + .setColor(colors.newBlurple) + .setTimestamp() + .setFooter({ text: `CaseID: ${modlogId}` }) + .setAuthor({ name: user.tag, iconURL: user.displayAvatarURL() }) + .addFields( + { name: `Why were you ${past}?`, value: reason }, + { name: 'Do you believe it was fair?', value: fair }, + { name: `Why should your ${base} be removed?`, value: why } + ); + return await appealChannel.send({ + content: `Appeal submitted by ${user.tag} (${user.id})`, + embeds: [embed], + components: [ + new ActionRowBuilder<ButtonBuilder>().addComponents( + new ButtonBuilder({ + customId: `appeal_accept;${punishment};${guildId};${userId};${modlogId}`, + label: 'Accept Appeal', + style: ButtonStyle.Success + }), + new ButtonBuilder({ + customId: `appeal_deny;${punishment};${guildId};${userId};${modlogId}`, + label: 'Deny Appeal', + style: ButtonStyle.Danger + }) + ) + ] + }); +} + +/** + * Handles interactions when a moderator clicks the "Accept" or "Deny" button on a punishment appeal. + * @param interaction A button interaction with a custom id that starts with "appeal_accept;" or "appeal_deny;". + */ +export async function handleAppealDecision(interaction: ButtonInteraction) { + const [baseId, punishment, guildId, userId, modlogId] = parseAppeal(interaction.customId); + + const { base, past, appealCustom } = punishments[punishment]; + const appealName = (appealCustom ?? base).toLowerCase(); + + const modlog = await ModLog.findByPk(modlogId); + + if (!modlog) { + return await interaction.reply(`:skull: I cannot find the modlog ${input(modlogId)}. Please report this to my developers.`); + } + + if (modlog.appeal !== AppealStatus.Submitted) { + return await interaction.reply( + `:skull: Case ${input(modlogId)} has an invalid state of ${input(modlog.appeal)}. Please report this to my developers.` + ); + } + + if (baseId === 'appeal_deny') { + modlog.appeal = AppealStatus.Denied; + await modlog.save(); + + await interaction.client.users + .send(userId, `Your ${appealName} appeal has been denied in ${interaction.client.guilds.resolve(guildId)!}.`) + .catch(() => {}); + + return await interaction.update({ + content: `${emojis.cross} Appeal denied.`, + embeds: interaction.message.embeds, + components: [ + new ActionRowBuilder<ButtonBuilder>().addComponents( + new ButtonBuilder({ + disabled: true, + style: ButtonStyle.Danger, + label: 'Appeal Denied', + custom_id: 'noop' + }) + ) + ] + }); + } else if (baseId === 'appeal_accept') { + modlog.appeal = AppealStatus.Accepted; + await modlog.save(); + + await interaction.client.users + .send(userId, `Your ${appealName} appeal has been accepted in ${interaction.client.guilds.resolve(guildId)!}.`) + .catch(() => {}); + + switch (punishment) { + case Action.Warn: + case Action.Unmute: + case Action.Kick: + case Action.Unban: + case Action.Untimeout: + case Action.Unblock: + case Action.RemovePunishRole: + assert.fail(`Cannot appeal ${appealName} (Action.${Action[punishment]})`); + return; + case Action.Mute: { + throw new Error('Not implemented'); + } + case Action.Ban: { + throw new Error('Not implemented'); + } + case Action.Timeout: { + throw new Error('Not implemented'); + } + case Action.Block: { + throw new Error('Not implemented'); + } + case Action.AddPunishRole: { + throw new Error('Not implemented'); + } + default: { + const _exhaustiveCheck: never = punishment; + } + } + + return await interaction.update({ + content: `${emojis.check} Appeal accepted.`, + embeds: interaction.message.embeds, + components: [ + new ActionRowBuilder<ButtonBuilder>().addComponents( + new ButtonBuilder({ + disabled: true, + style: ButtonStyle.Success, + label: 'Appeal Accepted', + custom_id: 'noop' + }) + ) + ] + }); + } +} diff --git a/lib/common/ButtonPaginator.ts b/lib/common/ButtonPaginator.ts index 02c78ea..c8c9229 100644 --- a/lib/common/ButtonPaginator.ts +++ b/lib/common/ButtonPaginator.ts @@ -1,5 +1,5 @@ import { DeleteButton, type CommandMessage, type SlashMessage } from '#lib'; -import { CommandUtil } from 'discord-akairo'; +import { CommandUtil } from '@notenoughupdates/discord-akairo'; import { ActionRowBuilder, ButtonBuilder, @@ -34,7 +34,7 @@ export class ButtonPaginator { protected constructor( protected message: CommandMessage | SlashMessage, protected embeds: EmbedBuilder[] | APIEmbed[], - protected text: string | null, + protected text: string, protected deleteOnExit: boolean, startOn: number ) { @@ -199,7 +199,7 @@ export class ButtonPaginator { public static async send( message: CommandMessage | SlashMessage, embeds: EmbedBuilder[] | APIEmbed[], - text: string | null = null, + text: string = '', deleteOnExit = true, startOn = 1 ) { diff --git a/lib/common/ConfirmationPrompt.ts b/lib/common/ConfirmationPrompt.ts index b87d9ef..631b8de 100644 --- a/lib/common/ConfirmationPrompt.ts +++ b/lib/common/ConfirmationPrompt.ts @@ -1,5 +1,11 @@ import { type CommandMessage, type SlashMessage } from '#lib'; -import { ActionRowBuilder, ButtonBuilder, ButtonStyle, type MessageComponentInteraction, type MessageOptions } from 'discord.js'; +import { + ActionRowBuilder, + ButtonBuilder, + ButtonStyle, + type MessageComponentInteraction, + type MessageCreateOptions +} from 'discord.js'; /** * Sends a message with buttons for the user to confirm or cancel the action. @@ -9,7 +15,7 @@ export class ConfirmationPrompt { * @param message The message that triggered the command * @param messageOptions Options for sending the message */ - protected constructor(protected message: CommandMessage | SlashMessage, protected messageOptions: MessageOptions) {} + protected constructor(protected message: CommandMessage | SlashMessage, protected messageOptions: MessageCreateOptions) {} /** * Sends a message with buttons for the user to confirm or cancel the action. @@ -58,7 +64,7 @@ export class ConfirmationPrompt { * @param message The message that triggered the command * @param sendOptions Options for sending the message */ - public static async send(message: CommandMessage | SlashMessage, sendOptions: MessageOptions): Promise<boolean> { + public static async send(message: CommandMessage | SlashMessage, sendOptions: MessageCreateOptions): Promise<boolean> { return new ConfirmationPrompt(message, sendOptions).send(); } } diff --git a/lib/common/DeleteButton.ts b/lib/common/DeleteButton.ts index 340d07f..2d053cd 100644 --- a/lib/common/DeleteButton.ts +++ b/lib/common/DeleteButton.ts @@ -1,5 +1,5 @@ import { PaginateEmojis, type CommandMessage, type SlashMessage } from '#lib'; -import { CommandUtil } from 'discord-akairo'; +import { CommandUtil } from '@notenoughupdates/discord-akairo'; import { ActionRowBuilder, ButtonBuilder, @@ -7,7 +7,7 @@ import { MessageComponentInteraction, MessageEditOptions, MessagePayload, - type MessageOptions + type MessageCreateOptions } from 'discord.js'; /** @@ -18,7 +18,7 @@ export class DeleteButton { * @param message The message to respond to * @param messageOptions The send message options */ - protected constructor(protected message: CommandMessage | SlashMessage, protected messageOptions: MessageOptions) {} + protected constructor(protected message: CommandMessage | SlashMessage, protected messageOptions: MessageCreateOptions) {} /** * Sends a message with a button for the user to delete it. @@ -72,7 +72,7 @@ export class DeleteButton { * @param message The message to respond to * @param options The send message options */ - public static async send(message: CommandMessage | SlashMessage, options: Omit<MessageOptions, 'components'>) { + public static async send(message: CommandMessage | SlashMessage, options: Omit<MessageCreateOptions, 'components'>) { return new DeleteButton(message, options).send(); } } diff --git a/lib/common/HighlightManager.ts b/lib/common/HighlightManager.ts index ca71a83..3a76a5b 100644 --- a/lib/common/HighlightManager.ts +++ b/lib/common/HighlightManager.ts @@ -435,6 +435,9 @@ export class HighlightManager { } private generateDmEmbed(message: Message, hl: HighlightWord) { + // janky MessageManager typings + 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) diff --git a/lib/common/Moderation.ts b/lib/common/Moderation.ts index 60e32c0..7697b2f 100644 --- a/lib/common/Moderation.ts +++ b/lib/common/Moderation.ts @@ -1,17 +1,7 @@ -import { - ActivePunishment, - ActivePunishmentType, - baseMuteResponse, - colors, - emojis, - format, - Guild as GuildDB, - humanizeDuration, - ModLog, - permissionsResponse, - type ModLogType, - type ValueOf -} from '#lib'; +import { baseMuteResponse, permissionsResponse } from '#lib/extensions/discord.js/ExtendedGuildMember.js'; +import { ActivePunishment, ActivePunishmentType, Guild as GuildDB, ModLog, type ModLogType } from '#lib/models/index.js'; +import { colors, emojis } from '#lib/utils/Constants.js'; +import { format, humanizeDuration, ValueOf } from '#lib/utils/Utils.js'; import assert from 'assert/strict'; import { ActionRowBuilder, @@ -28,29 +18,178 @@ import { type UserResolvable } from 'discord.js'; -enum punishMap { - 'warned' = 'warn', - 'muted' = 'mute', - 'unmuted' = 'unmute', - 'kicked' = 'kick', - 'banned' = 'ban', - 'unbanned' = 'unban', - 'timedout' = 'timeout', - 'untimedout' = 'untimeout', - 'blocked' = 'block', - 'unblocked' = 'unblock' +export enum Action { + Warn, + Mute, + Unmute, + Kick, + Ban, + Unban, + Timeout, + Untimeout, + Block, + Unblock, + AddPunishRole, + RemovePunishRole +} + +interface ActionInfo { + /** + * The base verb of the action + */ + base: string; + + /** + * The past tense form of the action + */ + past: string; + + /** + * Whether or not a user can appeal this action + */ + appealable: boolean; + + /** + * Whether a moderator can perform this action on themself. + */ + selfInflictable: boolean; + + /** + * Whether the action requires the target to be in the guild. + */ + membershipRequired: boolean; + + /** + * Custom appeal title, otherwise {@link ActionInfo.base} is used. + */ + appealCustom?: string; } -enum reversedPunishMap { - 'warn' = 'warned', - 'mute' = 'muted', - 'unmute' = 'unmuted', - 'kick' = 'kicked', - 'ban' = 'banned', - 'unban' = 'unbanned', - 'timeout' = 'timedout', - 'untimeout' = 'untimedout', - 'block' = 'blocked', - 'unblock' = 'unblocked' + +export const punishments: Record<Action, ActionInfo> = { + [Action.Warn]: { + base: 'warn', + past: 'warned', + appealable: false, + selfInflictable: false, + membershipRequired: true + }, + [Action.Mute]: { + base: 'mute', + past: 'muted', + appealable: true, + selfInflictable: false, + membershipRequired: true + }, + [Action.Unmute]: { + base: 'unmute', + past: 'unmuted', + appealable: false, + selfInflictable: true, + membershipRequired: true + }, + [Action.Kick]: { + base: 'kick', + past: 'kicked', + appealable: false, + selfInflictable: false, + membershipRequired: true + }, + [Action.Ban]: { + base: 'ban', + past: 'banned', + appealable: true, + selfInflictable: false, + membershipRequired: false + }, + [Action.Unban]: { + base: 'unban', + past: 'unbanned', + appealable: false, + selfInflictable: true, + membershipRequired: false + }, + [Action.Timeout]: { + base: 'timeout', + past: 'timed out', + appealable: true, + selfInflictable: false, + membershipRequired: true + }, + [Action.Untimeout]: { + base: 'untimeout', + past: 'untimed out', + appealable: false, + selfInflictable: true, + membershipRequired: true + }, + [Action.Block]: { + base: 'block', + past: 'blocked', + appealable: true, + selfInflictable: false, + membershipRequired: true + }, + [Action.Unblock]: { + base: 'unblock', + past: 'unblocked', + appealable: false, + selfInflictable: true, + membershipRequired: true + }, + [Action.AddPunishRole]: { + base: 'add a punishment role to', + past: 'added punishment role', + appealable: true, + appealCustom: 'Punishment Role', + selfInflictable: false, + membershipRequired: true + }, + [Action.RemovePunishRole]: { + base: 'remove a punishment role from', + past: 'removed punishment role', + appealable: false, + selfInflictable: true, + membershipRequired: true + } +}; + +interface BaseOptions { + /** + * The client. + */ + client: Client; +} + +interface BaseCreateModLogEntryOptions extends BaseOptions { + /** + * The type of modlog entry. + */ + type: ModLogType; + + /** + * The reason for the punishment. + */ + reason: string | undefined | null; + + /** + * The duration of the punishment. + */ + duration?: number; + + /** + * Whether the punishment is a pseudo punishment. + */ + pseudo?: boolean; + + /** + * The evidence for the punishment. + */ + evidence?: string; + + /** + * Makes the modlog entry hidden. + */ + hidden?: boolean; } /** @@ -65,57 +204,52 @@ enum reversedPunishMap { export async function permissionCheck( moderator: GuildMember, victim: GuildMember, - type: - | 'mute' - | 'unmute' - | 'warn' - | 'kick' - | 'ban' - | 'unban' - | 'add a punishment role to' - | 'remove a punishment role from' - | 'block' - | 'unblock' - | 'timeout' - | 'untimeout', + type: Action, checkModerator = true, force = false ): Promise<true | string> { if (force) return true; + const action = punishments[type]; + // If the victim is not in the guild anymore it will be undefined - if ((!victim || !victim.guild) && !['ban', 'unban'].includes(type)) return true; + if (!victim?.guild && action.membershipRequired) return true; - if (moderator.guild.id !== victim.guild.id) { - throw new Error('moderator and victim not in same guild'); - } + assert(moderator.guild.id === victim.guild.id, 'moderator and victim should be from the same guild'); const isOwner = moderator.guild.ownerId === moderator.id; - if (moderator.id === victim.id && !type.startsWith('un')) { - return `${emojis.error} You cannot ${type} yourself.`; + + const selfInflicted = moderator.id === victim.id; + + if (selfInflicted && !action.selfInflictable) { + return `${emojis.error} You cannot ${action.base} yourself.`; } if ( moderator.roles.highest.position <= victim.roles.highest.position && !isOwner && - !(type.startsWith('un') && moderator.id === victim.id) + !(action.selfInflictable && selfInflicted) ) { - return `${emojis.error} You cannot ${type} **${victim.user.tag}** because they have higher or equal role hierarchy as you do.`; + return `${emojis.error} You cannot ${action.base} ${format.input( + victim.user.tag + )} because they have higher or equal role hierarchy as you do.`; } if ( victim.roles.highest.position >= victim.guild.members.me!.roles.highest.position && - !(type.startsWith('un') && moderator.id === victim.id) + !(action.selfInflictable && selfInflicted) ) { - return `${emojis.error} You cannot ${type} **${victim.user.tag}** because they have higher or equal role hierarchy as I do.`; + return `${emojis.error} You cannot ${action.base} ${format.input( + victim.user.tag + )} because they have higher or equal role hierarchy as I do.`; } if ( checkModerator && victim.permissions.has(PermissionFlagsBits.ManageMessages) && - !(type.startsWith('un') && moderator.id === victim.id) + !(action.selfInflictable && selfInflicted) ) { if (await moderator.guild.hasFeature('modsCanPunishMods')) { return true; } else { - return `${emojis.error} You cannot ${type} **${victim.user.tag}** because they are a moderator.`; + return `${emojis.error} You cannot ${action.base} ${format.input(victim.user.tag)} because they are a moderator.`; } } return true; @@ -129,18 +263,49 @@ export async function permissionCheck( export async function checkMutePermissions( guild: Guild ): Promise<ValueOf<typeof baseMuteResponse> | ValueOf<typeof permissionsResponse> | true> { - if (!guild.members.me!.permissions.has('ManageRoles')) return permissionsResponse.MISSING_PERMISSIONS; + if (!guild.members.me!.permissions.has('ManageRoles')) { + return permissionsResponse.MISSING_PERMISSIONS; + } + const muteRoleID = await guild.getSetting('muteRole'); if (!muteRoleID) return baseMuteResponse.NO_MUTE_ROLE; + const muteRole = guild.roles.cache.get(muteRoleID); if (!muteRole) return baseMuteResponse.MUTE_ROLE_INVALID; - if (muteRole.position >= guild.members.me!.roles.highest.position || muteRole.managed) + + if (muteRole.position >= guild.members.me!.roles.highest.position || muteRole.managed) { return baseMuteResponse.MUTE_ROLE_NOT_MANAGEABLE; + } return true; } /** + * Options for creating a modlog entry. + */ +export interface CreateModLogEntryOptions extends BaseCreateModLogEntryOptions { + /** + * The client. + */ + client: Client; + + /** + * The user that a modlog entry is created for. + */ + user: GuildMemberResolvable; + + /** + * The moderator that created the modlog entry. + */ + moderator: GuildMemberResolvable; + + /** + * The guild that the punishment is created for. + */ + guild: GuildResolvable; +} + +/** * Creates a modlog entry for a punishment. * @param options Options for creating a modlog entry. * @param getCaseNumber Whether or not to get the case number of the entry. @@ -166,6 +331,26 @@ export async function createModLogEntry( } /** + * Simple options for creating a modlog entry. + */ +export interface SimpleCreateModLogEntryOptions extends BaseCreateModLogEntryOptions { + /** + * The user that a modlog entry is created for. + */ + user: Snowflake; + + /** + * The moderator that created the modlog entry. + */ + moderator: Snowflake; + + /** + * The guild that the punishment is created for. + */ + guild: Snowflake; +} + +/** * Creates a modlog entry with already resolved ids. * @param options Options for creating a modlog entry. * @param getCaseNumber Whether or not to get the case number of the entry. @@ -192,6 +377,7 @@ export async function createModLogEntrySimple( evidence: options.evidence, hidden: options.hidden ?? false }); + const saveResult: ModLog | null = await modLogEntry.save().catch(async (e) => { await options.client.utils.handleError('createModLogEntry', e); return null; @@ -206,6 +392,41 @@ export async function createModLogEntrySimple( } /** + * Options for creating a punishment entry. + */ +export interface CreatePunishmentEntryOptions extends BaseOptions { + /** + * The type of punishment. + */ + type: 'mute' | 'ban' | 'role' | 'block'; + + /** + * The user that the punishment is created for. + */ + user: GuildMemberResolvable; + + /** + * The length of time the punishment lasts for. + */ + duration: number | undefined; + + /** + * The guild that the punishment is created for. + */ + guild: GuildResolvable; + + /** + * The id of the modlog that is linked to the punishment entry. + */ + modlog: string; + + /** + * Extra information for the punishment. The role for role punishments and the channel for blocks. + */ + extraInfo?: Snowflake; +} + +/** * Creates a punishment entry. * @param options Options for creating the punishment entry. * @returns The database entry, or null if no entry is created. @@ -221,6 +442,7 @@ export async function createPunishmentEntry(options: CreatePunishmentEntryOption ? { user, type, guild, expires, modlog: options.modlog, extraInfo: options.extraInfo } : { user, type, guild, expires, modlog: options.modlog } ); + return await entry.save().catch(async (e) => { await options.client.utils.handleError('createPunishmentEntry', e); return null; @@ -228,6 +450,31 @@ export async function createPunishmentEntry(options: CreatePunishmentEntryOption } /** + * Options for removing a punishment entry. + */ +export interface RemovePunishmentEntryOptions extends BaseOptions { + /** + * The type of punishment. + */ + type: 'mute' | 'ban' | 'role' | 'block'; + + /** + * The user that the punishment is destroyed for. + */ + user: GuildMemberResolvable; + + /** + * The guild that the punishment was in. + */ + guild: GuildResolvable; + + /** + * Extra information for the punishment. The role for role punishments and the channel for blocks. + */ + extraInfo?: Snowflake; +} + +/** * Destroys a punishment entry. * @param options Options for destroying the punishment entry. * @returns Whether or not the entry was destroyed. @@ -250,6 +497,7 @@ export async function removePunishmentEntry(options: RemovePunishmentEntryOption await options.client.utils.handleError('removePunishmentEntry', e); success = false; }); + if (entries) { const promises = entries.map(async (entry) => entry.destroy().catch(async (e) => { @@ -270,212 +518,14 @@ export async function removePunishmentEntry(options: RemovePunishmentEntryOption */ function findTypeEnum(type: 'mute' | 'ban' | 'role' | 'block') { const typeMap = { - ['mute']: ActivePunishmentType.MUTE, - ['ban']: ActivePunishmentType.BAN, - ['role']: ActivePunishmentType.ROLE, - ['block']: ActivePunishmentType.BLOCK + mute: ActivePunishmentType.Mute, + ban: ActivePunishmentType.Ban, + role: ActivePunishmentType.Role, + block: ActivePunishmentType.Block }; return typeMap[type]; } -export function punishmentToPresentTense(punishment: PunishmentTypeDM): PunishmentTypePresent { - return punishMap[punishment]; -} - -export function punishmentToPastTense(punishment: PunishmentTypePresent): PunishmentTypeDM { - return reversedPunishMap[punishment]; -} - -/** - * Notifies the specified user of their punishment. - * @param options Options for notifying the user. - * @returns Whether or not the dm was successfully sent. - */ -export async function punishDM(options: PunishDMOptions): Promise<boolean> { - const ending = await options.guild.getSetting('punishmentEnding'); - const dmEmbed = - ending && ending.length && options.sendFooter - ? new EmbedBuilder().setDescription(ending).setColor(colors.newBlurple) - : undefined; - - const appealsEnabled = !!( - (await options.guild.hasFeature('punishmentAppeals')) && (await options.guild.getLogChannel('appeals')) - ); - - let content = `You have been ${options.punishment} `; - if (options.punishment.includes('blocked')) { - assert(options.channel); - content += `from <#${options.channel}> `; - } - content += `in ${format.input(options.guild.name)} `; - if (options.duration !== null && options.duration !== undefined) - content += options.duration ? `for ${humanizeDuration(options.duration)} ` : 'permanently '; - const reason = options.reason?.trim() ? options.reason?.trim() : 'No reason provided'; - content += `for ${format.input(reason)}.`; - - let components; - if (appealsEnabled && options.modlog) - components = [ - new ActionRowBuilder<ButtonBuilder>({ - components: [ - new ButtonBuilder({ - customId: `appeal;${punishmentToPresentTense(options.punishment)};${ - options.guild.id - };${options.client.users.resolveId(options.user)};${options.modlog}`, - style: ButtonStyle.Primary, - label: 'Appeal' - }).toJSON() - ] - }) - ]; - - const dmSuccess = await options.client.users - .send(options.user, { - content, - embeds: dmEmbed ? [dmEmbed] : undefined, - components - }) - .catch(() => false); - return !!dmSuccess; -} - -interface BaseCreateModLogEntryOptions extends BaseOptions { - /** - * The type of modlog entry. - */ - type: ModLogType; - - /** - * The reason for the punishment. - */ - reason: string | undefined | null; - - /** - * The duration of the punishment. - */ - duration?: number; - - /** - * Whether the punishment is a pseudo punishment. - */ - pseudo?: boolean; - - /** - * The evidence for the punishment. - */ - evidence?: string; - - /** - * Makes the modlog entry hidden. - */ - hidden?: boolean; -} - -/** - * Options for creating a modlog entry. - */ -export interface CreateModLogEntryOptions extends BaseCreateModLogEntryOptions { - /** - * The client. - */ - client: Client; - - /** - * The user that a modlog entry is created for. - */ - user: GuildMemberResolvable; - - /** - * The moderator that created the modlog entry. - */ - moderator: GuildMemberResolvable; - - /** - * The guild that the punishment is created for. - */ - guild: GuildResolvable; -} - -/** - * Simple options for creating a modlog entry. - */ -export interface SimpleCreateModLogEntryOptions extends BaseCreateModLogEntryOptions { - /** - * The user that a modlog entry is created for. - */ - user: Snowflake; - - /** - * The moderator that created the modlog entry. - */ - moderator: Snowflake; - - /** - * The guild that the punishment is created for. - */ - guild: Snowflake; -} - -/** - * Options for creating a punishment entry. - */ -export interface CreatePunishmentEntryOptions extends BaseOptions { - /** - * The type of punishment. - */ - type: 'mute' | 'ban' | 'role' | 'block'; - - /** - * The user that the punishment is created for. - */ - user: GuildMemberResolvable; - - /** - * The length of time the punishment lasts for. - */ - duration: number | undefined; - - /** - * The guild that the punishment is created for. - */ - guild: GuildResolvable; - - /** - * The id of the modlog that is linked to the punishment entry. - */ - modlog: string; - - /** - * Extra information for the punishment. The role for role punishments and the channel for blocks. - */ - extraInfo?: Snowflake; -} - -/** - * Options for removing a punishment entry. - */ -export interface RemovePunishmentEntryOptions extends BaseOptions { - /** - * The type of punishment. - */ - type: 'mute' | 'ban' | 'role' | 'block'; - - /** - * The user that the punishment is destroyed for. - */ - user: GuildMemberResolvable; - - /** - * The guild that the punishment was in. - */ - guild: GuildResolvable; - - /** - * Extra information for the punishment. The role for role punishments and the channel for blocks. - */ - extraInfo?: Snowflake; -} - /** * Options for sending a user a punishment dm. */ @@ -498,7 +548,7 @@ export interface PunishDMOptions extends BaseOptions { /** * The punishment that the user has received. */ - punishment: PunishmentTypeDM; + punishment: Action; /** * The reason the user's punishment. @@ -522,35 +572,59 @@ export interface PunishDMOptions extends BaseOptions { channel?: Snowflake; } -interface BaseOptions { - /** - * The client. - */ - client: Client; -} +/** + * Notifies the specified user of their punishment. + * @param options Options for notifying the user. + * @returns Whether or not the dm was successfully sent. + */ +export async function punishDM(options: PunishDMOptions): Promise<boolean> { + const ending = await options.guild.getSetting('punishmentEnding'); + const dmEmbed = + ending && ending.length && options.sendFooter + ? new EmbedBuilder().setDescription(ending).setColor(colors.newBlurple) + : undefined; + + const appealsEnabled = + (await options.guild.hasFeature('punishmentAppeals')) && Boolean(await options.guild.getLogChannel('appeals')); -export type PunishmentTypeDM = - | 'warned' - | 'muted' - | 'unmuted' - | 'kicked' - | 'banned' - | 'unbanned' - | 'timedout' - | 'untimedout' - | 'blocked' - | 'unblocked'; - -export type PunishmentTypePresent = - | 'warn' - | 'mute' - | 'unmute' - | 'kick' - | 'ban' - | 'unban' - | 'timeout' - | 'untimeout' - | 'block' - | 'unblock'; - -export type AppealButtonId = `appeal;${PunishmentTypePresent};${Snowflake};${Snowflake};${string}`; + let content = `You have been ${options.punishment} `; + if ([Action.Block, Action.Unblock].includes(options.punishment)) { + assert(options.channel); + content += `from <#${options.channel}> `; + } + content += `in ${format.input(options.guild.name)} `; + if (options.duration !== null && options.duration !== undefined) { + content += options.duration ? `for ${humanizeDuration(options.duration)} ` : 'permanently '; + } + const reason = options.reason?.trim() ? options.reason?.trim() : 'No reason provided'; + content += `for ${format.input(reason)}.`; + + let components; + if (appealsEnabled && options.modlog) { + const punishment = options.punishment; + const guildId = options.guild.id; + const userId = options.client.users.resolveId(options.user); + const modlogCase = options.modlog; + + components = [ + new ActionRowBuilder<ButtonBuilder>({ + components: [ + new ButtonBuilder({ + customId: `appeal_attempt;${Action[punishment]};${guildId};${userId};${modlogCase}`, + style: ButtonStyle.Primary, + label: 'Appeal Punishment' + }) + ] + }) + ]; + } + + const dmSuccess = await options.client.users + .send(options.user, { + content, + embeds: dmEmbed ? [dmEmbed] : undefined, + components + }) + .catch(() => false); + return !!dmSuccess; +} diff --git a/lib/common/tags.ts b/lib/common/tags.ts index 4af8783..826b820 100644 --- a/lib/common/tags.ts +++ b/lib/common/tags.ts @@ -1,5 +1,5 @@ -/* these functions are adapted from the common-tags npm package which is licensed under the MIT license */ -/* the JSDOCs are adapted from the @types/common-tags npm package which is licensed under the MIT license */ +/* The stripIndent, stripIndents, and format functions are adapted from the common-tags npm package which is licensed under the MIT license */ +/* The JSDOCs for said functions are adapted from the @types/common-tags npm package which is licensed under the MIT license */ /** * Strips the **initial** indentation from the beginning of each line in a multiline string. @@ -32,3 +32,131 @@ function format(strings: TemplateStringsArray, ...expressions: any[]) { .replace(/^\n/, ''); return str; } + +export function commas(strings: TemplateStringsArray, ...expressions: any[]) { + const str = strings + .reduce((result, string, index) => ''.concat(result, localString(expressions[index - 1]), string)) + .replace(/[^\S\n]+$/gm, '') + .replace(/^\n/, ''); + + return str; +} + +function localString(val: any) { + return typeof val === 'number' ? val.toLocaleString() : val; +} + +export function commasStripIndents(strings: TemplateStringsArray, ...expressions: any[]) { + return stripIndents`${commas(strings, ...expressions)}`; +} + +function splitByNewline(strings: TemplateStringsArray, ...expressions: any[]): any[][] { + const ret: any[][] = []; + + let current: any[] = []; + + for (let i = 0; i < strings.length; i++) { + const string = strings[i]; + if (string.includes('\n')) { + // divide the string by newlines + const [first, ...rest] = string.split('\n'); + + // no point add an empty string + if (first !== '') { + // complete the current line + current.push(first); + } + + // ignore empty first line + if (i !== 0 && current.length !== 1 && current[1] !== '') { + // add the current line to the list of lines + ret.push(current); + } + + // handle multiple newlines + if (rest.length > 1) { + // loop though everything but the final element + for (const line of rest.slice(0, -1)) { + ret.push([line]); + } + } + + // since there are no more empty newlines, add to the current line so that expressions can be added + const last = rest[rest.length - 1]; + current = [last]; + } else { + // if there are no newlines, just add to the current line + current.push(string); + } + + // now add the expression + if (i < expressions.length) current.push(expressions[i]); + } + + // add the final line + ret.push(current); + + return ret; +} + +/** + * Creates information fields for embeds. Commas are added to numbers. + * Lines are ignored if the expression is `null`, `undefined`, or `false`. + * Additionally, leading whitespace is removed. If the first line is empty, + * it is ignored. + * @example + * ```ts + * const value = 'value'; + * const condition = false; + * + * embedField` + * Header ${value} + * Another Header ${condition && 50} + * A Third Header ${50000}` + * + * // **Header:** value + * // **A Third Header:** 50,000 + * ``` + */ +export function embedField(strings: TemplateStringsArray, ...expressions: any[]) { + const lines: any[][] = splitByNewline(strings, ...expressions); + + // loop through each line and remove any leading whitespace + for (let i = 0; i < lines.length; i++) { + lines[i][0] = lines[i][0].replace(/^[^\S\n]+/gm, ''); + } + + const result: string[] = []; + + out: for (let i = 0; i < lines.length; i++) { + // eslint-disable-next-line prefer-const + let [header, ...rest] = lines[i]; + + header = `**${header.trim()}:**`; + + const lineContent: string[] = []; + + for (let i = 0; i < rest.length; i++) { + const value = rest[i]; + if (typeof value === 'string') { + lineContent.push(value); + } else if (typeof value === 'number') { + // add commas to numbers + lineContent.push(value.toLocaleString()); + } else if (value === null || value === undefined || value === false) { + if (i === 0) { + // ignore this line + continue out; + } else { + throw new Error('Null or false values can only be used as the first expression in a line.'); + } + } else { + lineContent.push(value.toString()); + } + } + + result.push(`${header} ${lineContent.join('')}`); + } + + return result.join('\n'); +} diff --git a/lib/extensions/discord-akairo/BotCommand.ts b/lib/extensions/discord-akairo/BotCommand.ts index 11a8bad..a975667 100644 --- a/lib/extensions/discord-akairo/BotCommand.ts +++ b/lib/extensions/discord-akairo/BotCommand.ts @@ -11,14 +11,6 @@ import type { import { Command, CommandArguments, - type AkairoApplicationCommandAutocompleteOption, - type AkairoApplicationCommandChannelOptionData, - type AkairoApplicationCommandChoicesData, - type AkairoApplicationCommandNonOptionsData, - type AkairoApplicationCommandNumericOptionData, - type AkairoApplicationCommandOptionData, - type AkairoApplicationCommandSubCommandData, - type AkairoApplicationCommandSubGroupData, type ArgumentMatch, type ArgumentOptions, type ArgumentType, @@ -29,8 +21,9 @@ import { type ContextMenuCommand, type SlashOption, type SlashResolveType -} from 'discord-akairo'; +} from '@notenoughupdates/discord-akairo'; import { + ApplicationCommandChannelOption, PermissionsBitField, type ApplicationCommandOptionChoiceData, type ApplicationCommandOptionType, @@ -39,7 +32,7 @@ import { type Snowflake, type User } from 'discord.js'; -import _ from 'lodash'; +import { camelCase } from 'lodash-es'; import { SlashMessage } from './SlashMessage.js'; export interface OverriddenBaseArgumentType extends BaseArgumentType { @@ -89,7 +82,7 @@ interface BaseBotArgumentOptions extends Omit<ArgumentOptions, 'type' | 'prompt' /** * The type used for slash commands. Set to false to disable this argument for slash commands. */ - slashType: AkairoApplicationCommandOptionData['type'] | false; + slashType: SlashOption['type'] | false; /** * Allows you to get a discord resolved object @@ -111,7 +104,7 @@ interface BaseBotArgumentOptions extends Omit<ArgumentOptions, 'type' | 'prompt' /** * When the option type is channel, the allowed types of channels that can be selected */ - channelTypes?: AkairoApplicationCommandChannelOptionData['channelTypes']; + channelTypes?: ApplicationCommandChannelOption['channelTypes']; /** * The minimum value for an {@link ApplicationCommandOptionType.Integer Integer} or {@link ApplicationCommandOptionType.Number Number} option @@ -508,8 +501,11 @@ export abstract class BotCommand extends Command { (options_.slash || options_.slashOnly) && arg.slashType !== false ) { + // credit to https://dev.to/lucianbc/union-type-merging-in-typescript-9al + type AllKeys<T> = T extends any ? keyof T : never; + const newArg: { - [key in SlashOptionKeys]?: any; + [key in AllKeys<SlashOption>]?: any; } = { name: arg.id, // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing @@ -560,7 +556,7 @@ export abstract class BotCommand extends Command { }); for (const arg of combined) { - const name = _.camelCase('id' in arg ? arg.id : arg.name), + const name = camelCase('id' in arg ? arg.id : arg.name), description = arg.description || '*No description provided.*', optional = arg.optional ?? false, autocomplete = arg.autocomplete ?? false, @@ -607,15 +603,6 @@ export abstract class BotCommand extends Command { public abstract override exec(message: CommandMessage | SlashMessage, args: CommandArguments): any; } -type SlashOptionKeys = - | keyof AkairoApplicationCommandSubGroupData - | keyof AkairoApplicationCommandNonOptionsData - | keyof AkairoApplicationCommandChannelOptionData - | keyof AkairoApplicationCommandChoicesData - | keyof AkairoApplicationCommandAutocompleteOption - | keyof AkairoApplicationCommandNumericOptionData - | keyof AkairoApplicationCommandSubCommandData; - interface PseudoArguments extends BaseBotArgumentType { boolean: boolean; flag: boolean; diff --git a/lib/extensions/discord-akairo/BotCommandHandler.ts b/lib/extensions/discord-akairo/BotCommandHandler.ts index e9b509f..c1415e3 100644 --- a/lib/extensions/discord-akairo/BotCommandHandler.ts +++ b/lib/extensions/discord-akairo/BotCommandHandler.ts @@ -1,5 +1,10 @@ -import type { BotCommand, CommandMessage, SlashMessage } from '#lib'; -import { CommandHandler, CommandHandlerEvents, type Category, type CommandHandlerOptions } from 'discord-akairo'; +import type { BotCommand, CommandMessage, SlashMessage, TanzaniteClient } from '#lib'; +import { + CommandHandler, + CommandHandlerEvents, + type Category, + type CommandHandlerOptions +} from '@notenoughupdates/discord-akairo'; import { GuildMember, PermissionResolvable, type Collection, type Message, type PermissionsString } from 'discord.js'; import { CommandHandlerEvent } from '../../utils/Constants.js'; @@ -41,6 +46,8 @@ export interface BotCommandHandlerEvents extends CommandHandlerEvents { } export class BotCommandHandler extends CommandHandler { + public declare readonly client: TanzaniteClient; + public declare modules: Collection<string, BotCommand>; public declare categories: Collection<string, Category<string, BotCommand>>; diff --git a/lib/extensions/discord-akairo/BotInhibitor.ts b/lib/extensions/discord-akairo/BotInhibitor.ts index 8892b8b..8a53e0d 100644 --- a/lib/extensions/discord-akairo/BotInhibitor.ts +++ b/lib/extensions/discord-akairo/BotInhibitor.ts @@ -1,8 +1,10 @@ -import type { BotCommand, CommandMessage, InhibitorReason, InhibitorType, SlashMessage } from '#lib'; -import { Inhibitor, InhibitorOptions } from 'discord-akairo'; +import type { BotCommand, CommandMessage, InhibitorReason, InhibitorType, SlashMessage, TanzaniteClient } from '#lib'; +import { Inhibitor, InhibitorOptions } from '@notenoughupdates/discord-akairo'; import { Message } from 'discord.js'; export abstract class BotInhibitor extends Inhibitor { + public declare readonly client: TanzaniteClient; + public constructor(id: InhibitorReason, options?: BotInhibitorOptions) { super(id, options); } diff --git a/lib/extensions/discord-akairo/BotInhibitorHandler.ts b/lib/extensions/discord-akairo/BotInhibitorHandler.ts index c6f318d..05caca6 100644 --- a/lib/extensions/discord-akairo/BotInhibitorHandler.ts +++ b/lib/extensions/discord-akairo/BotInhibitorHandler.ts @@ -1,3 +1,6 @@ -import { InhibitorHandler } from 'discord-akairo'; +import { InhibitorHandler } from '@notenoughupdates/discord-akairo'; +import { TanzaniteClient } from './TanzaniteClient.js'; -export class BotInhibitorHandler extends InhibitorHandler {} +export class BotInhibitorHandler extends InhibitorHandler { + public declare readonly client: TanzaniteClient; +} diff --git a/lib/extensions/discord-akairo/BotListener.ts b/lib/extensions/discord-akairo/BotListener.ts index 4f760e2..85acce3 100644 --- a/lib/extensions/discord-akairo/BotListener.ts +++ b/lib/extensions/discord-akairo/BotListener.ts @@ -1,6 +1,9 @@ -import { Listener, type ListenerOptions } from 'discord-akairo'; +import { Listener, type ListenerOptions } from '@notenoughupdates/discord-akairo'; +import { TanzaniteClient } from './TanzaniteClient.js'; export abstract class BotListener extends Listener { + public declare readonly client: TanzaniteClient<boolean>; + public constructor(id: string, options: BotListenerOptions) { super(id, options); } diff --git a/lib/extensions/discord-akairo/BotListenerHandler.ts b/lib/extensions/discord-akairo/BotListenerHandler.ts index bc14a53..6a1ad4c 100644 --- a/lib/extensions/discord-akairo/BotListenerHandler.ts +++ b/lib/extensions/discord-akairo/BotListenerHandler.ts @@ -1,4 +1,4 @@ -import { ListenerHandler } from 'discord-akairo'; +import { ListenerHandler } from '@notenoughupdates/discord-akairo'; import type readline from 'readline'; import { TanzaniteClient } from './TanzaniteClient.js'; diff --git a/lib/extensions/discord-akairo/BotTask.ts b/lib/extensions/discord-akairo/BotTask.ts index 09b30ed..fd0dc2e 100644 --- a/lib/extensions/discord-akairo/BotTask.ts +++ b/lib/extensions/discord-akairo/BotTask.ts @@ -1,3 +1,3 @@ -import { Task } from 'discord-akairo'; +import { Task } from '@notenoughupdates/discord-akairo'; export abstract class BotTask extends Task {} diff --git a/lib/extensions/discord-akairo/BotTaskHandler.ts b/lib/extensions/discord-akairo/BotTaskHandler.ts index b522f2c..1b4b5bd 100644 --- a/lib/extensions/discord-akairo/BotTaskHandler.ts +++ b/lib/extensions/discord-akairo/BotTaskHandler.ts @@ -1,3 +1,3 @@ -import { TaskHandler } from 'discord-akairo'; +import { TaskHandler } from '@notenoughupdates/discord-akairo'; export class BotTaskHandler extends TaskHandler {} diff --git a/lib/extensions/discord-akairo/SlashMessage.ts b/lib/extensions/discord-akairo/SlashMessage.ts index 0a6669b..b93f25f 100644 --- a/lib/extensions/discord-akairo/SlashMessage.ts +++ b/lib/extensions/discord-akairo/SlashMessage.ts @@ -1,3 +1,3 @@ -import { AkairoMessage } from 'discord-akairo'; +import { AkairoMessage } from '@notenoughupdates/discord-akairo'; export class SlashMessage extends AkairoMessage {} diff --git a/lib/extensions/discord-akairo/TanzaniteClient.ts b/lib/extensions/discord-akairo/TanzaniteClient.ts index ac09aea..a8346ba 100644 --- a/lib/extensions/discord-akairo/TanzaniteClient.ts +++ b/lib/extensions/discord-akairo/TanzaniteClient.ts @@ -11,8 +11,20 @@ import { snowflake } from '#args'; import type { Config } from '#config'; -import { patch, type PatchedElements } from '@notenoughupdates/events-intercept'; -import * as Sentry from '@sentry/node'; +import { + ActivePunishment, + Global, + Guild as GuildModel, + GuildCount, + Highlight, + Level, + MemberCount, + ModLog, + Reminder, + Shared, + Stat, + StickyRole +} from '#lib/models/index.js'; import { AkairoClient, ArgumentTypeCaster, @@ -20,7 +32,9 @@ import { version as akairoVersion, type ArgumentPromptData, type OtherwiseContentSupplier -} from 'discord-akairo'; +} from '@notenoughupdates/discord-akairo'; +import * as Sentry from '@sentry/node'; +import { patch, type PatchedElements } from '@tanzanite/events-intercept'; import { ActivityType, GatewayIntentBits, @@ -32,33 +46,19 @@ import { type Awaitable, type If, type Message, - type MessageOptions, + type MessageCreateOptions, type Snowflake, type UserResolvable } from 'discord.js'; -import type EventEmitter from 'events'; import { google } from 'googleapis'; -import path from 'path'; -import readline from 'readline'; +import { type EventEmitter } from 'node:events'; +import path from 'node:path'; +import readline from 'node:readline'; +import { fileURLToPath } from 'node:url'; import { Options as SequelizeOptions, Sequelize, Sequelize as SequelizeType } from 'sequelize'; -import { fileURLToPath } from 'url'; import { tinyColor } from '../../arguments/tinyColor.js'; import { BotCache } from '../../common/BotCache.js'; import { HighlightManager } from '../../common/HighlightManager.js'; -import { - ActivePunishment, - Global, - Guild as GuildModel, - GuildCount, - Highlight, - Level, - MemberCount, - ModLog, - Reminder, - Shared, - Stat, - StickyRole -} from '../../models/index.js'; import { AllowedMentions } from '../../utils/AllowedMentions.js'; import { BotClientUtils } from '../../utils/BotClientUtils.js'; import { emojis } from '../../utils/Constants.js'; @@ -279,7 +279,7 @@ export class TanzaniteClient<Ready extends boolean = boolean> extends AkairoClie const modify = async ( message: Message, - text: string | MessagePayload | MessageOptions | OtherwiseContentSupplier, + text: string | MessagePayload | MessageCreateOptions | OtherwiseContentSupplier, data: ArgumentPromptData, replaceError: boolean ) => { @@ -387,7 +387,7 @@ export class TanzaniteClient<Ready extends boolean = boolean> extends AkairoClie */ public async init() { if (parseInt(process.versions.node.split('.')[0]) < 18) { - void (await this.console.error('version', `Please use node <<v18.x.x>>, not <<${process.version}>>.`, false)); + void (await this.console.error('version', `Please use node <<v18.x.x>> or greater, not <<${process.version}>>.`, false)); process.exit(2); } diff --git a/lib/extensions/discord.js/BotClientEvents.ts b/lib/extensions/discord.js/BotClientEvents.ts index 941a6d8..88f67e9 100644 --- a/lib/extensions/discord.js/BotClientEvents.ts +++ b/lib/extensions/discord.js/BotClientEvents.ts @@ -1,4 +1,4 @@ -import type { AkairoClientEvents } from 'discord-akairo'; +import type { AkairoClientEvents } from '@notenoughupdates/discord-akairo'; import type { ButtonInteraction, Collection, diff --git a/lib/extensions/discord.js/ExtendedGuild.ts b/lib/extensions/discord.js/ExtendedGuild.ts index 6bf81ee..84db7d0 100644 --- a/lib/extensions/discord.js/ExtendedGuild.ts +++ b/lib/extensions/discord.js/ExtendedGuild.ts @@ -1,4 +1,5 @@ import { + Action, createModLogEntry, createModLogEntrySimple, createPunishmentEntry, @@ -17,15 +18,16 @@ import { Guild, JSONEncodable, Message, + MessageCreateOptions, MessageType, PermissionFlagsBits, SnowflakeUtil, ThreadChannel, + WebhookCreateMessageOptions, type APIMessage, type GuildMember, type GuildMemberResolvable, type GuildTextBasedChannel, - type MessageOptions, type MessagePayload, type NewsChannel, type Snowflake, @@ -33,10 +35,9 @@ import { type User, type UserResolvable, type VoiceChannel, - type Webhook, - type WebhookMessageOptions + type Webhook } from 'discord.js'; -import _ from 'lodash'; +import { camelCase } from 'lodash-es'; import { TanzaniteClient } from '../discord-akairo/TanzaniteClient.js'; import { banResponse, BanResponse, dmResponse, permissionsResponse, punishmentEntryRemove } from './ExtendedGuildMember.js'; @@ -91,7 +92,10 @@ interface Extension { * @param logType The corresponding channel that the message will be sent to * @param message The parameters for {@link TextChannel.send} */ - sendLogChannel(logType: GuildLogType, message: string | MessagePayload | MessageOptions): Promise<Message | null | undefined>; + sendLogChannel( + logType: GuildLogType, + message: string | MessagePayload | MessageCreateOptions + ): Promise<Message | null | undefined>; /** * Sends a formatted error message in a guild's error log channel * @param title The title of the error embed @@ -135,7 +139,7 @@ interface Extension { declare module 'discord.js' { export interface BaseGuild { - client: TanzaniteClient; + client: TanzaniteClient<true>; } export interface Guild extends AnonymousGuild, Extension {} @@ -202,7 +206,7 @@ export class ExtendedGuild extends Guild implements Extension { public override async sendLogChannel( logType: GuildLogType, - message: string | MessagePayload | MessageOptions + message: string | MessagePayload | MessageCreateOptions ): Promise<Message | null | undefined> { const logChannel = await this.getLogChannel(logType); if (!logChannel || !logChannel.isTextBased()) { @@ -220,7 +224,7 @@ export class ExtendedGuild extends Guild implements Extension { } public override async error(title: string, message: string): Promise<void> { - void this.client.console.info(_.camelCase(title), message.replace(/\*\*(.*?)\*\*/g, '<<$1>>')); + void this.client.console.info(camelCase(title), message.replace(/\*\*(.*?)\*\*/g, '<<$1>>')); void this.sendLogChannel('error', { embeds: [{ title: title, description: message, color: colors.error }] }); } @@ -240,7 +244,7 @@ export class ExtendedGuild extends Guild implements Extension { // add modlog entry const { log: modlog } = await createModLogEntry({ client: this.client, - type: options.duration ? ModLogType.TEMP_BAN : ModLogType.PERM_BAN, + type: options.duration ? ModLogType.TempBan : ModLogType.PermBan, user: user, moderator: moderator.id, reason: options.reason, @@ -257,7 +261,7 @@ export class ExtendedGuild extends Guild implements Extension { modlog: modlog.id, guild: this, user: user, - punishment: 'banned', + punishment: Action.Ban, duration: options.duration ?? 0, reason: options.reason ?? undefined, sendFooter: true @@ -309,7 +313,7 @@ export class ExtendedGuild extends Guild implements Extension { // add modlog entry const { log: modlog } = await createModLogEntrySimple({ client: this.client, - type: ModLogType.PERM_BAN, + type: ModLogType.PermBan, user: options.user, moderator: options.moderator, reason: options.reason, @@ -326,7 +330,7 @@ export class ExtendedGuild extends Guild implements Extension { modlog: modlog.id, guild: this, user: options.user, - punishment: 'banned', + punishment: Action.Ban, duration: 0, reason: options.reason ?? undefined, sendFooter: true @@ -390,7 +394,7 @@ export class ExtendedGuild extends Guild implements Extension { // add modlog entry const { log: modlog } = await createModLogEntry({ client: this.client, - type: ModLogType.UNBAN, + type: ModLogType.Unban, user: user.id, moderator: moderator.id, reason: options.reason, @@ -414,7 +418,7 @@ export class ExtendedGuild extends Guild implements Extension { client: this.client, guild: this, user: user, - punishment: 'unbanned', + punishment: Action.Unban, reason: options.reason ?? undefined, sendFooter: false }); @@ -546,7 +550,7 @@ export class ExtendedGuild extends Guild implements Extension { if (!webhook) return null; - const sendOptions: Omit<WebhookMessageOptions, 'flags'> = {}; + const sendOptions: Omit<WebhookCreateMessageOptions, 'flags'> = {}; const displayName = quote.member?.displayName ?? quote.author.username; @@ -559,7 +563,7 @@ export class ExtendedGuild extends Guild implements Extension { sendOptions.content = quote.content || undefined; sendOptions.threadId = channel instanceof ThreadChannel ? channel.id : undefined; sendOptions.embeds = quote.embeds.length ? quote.embeds : undefined; - //@ts-expect-error: jank + // @ts-expect-error: jank sendOptions.attachments = quote.attachments.size ? [...quote.attachments.values()].map((a) => AttachmentBuilder.from(a as JSONEncodable<AttachmentPayload>)) : undefined; @@ -720,7 +724,9 @@ export class ExtendedGuild extends Guild implements Extension { } sendOptions.allowedMentions = AllowedMentions.none(); - sendOptions.username ??= quote.member?.displayName ?? quote.author.username; + sendOptions.username ??= (quote.member?.displayName ?? quote.author.username) + .replaceAll(/discord/gi, '[REDACTED]') + .replaceAll(/clyde/gi, '[REDACTED]'); sendOptions.avatarURL = quote.member?.displayAvatarURL({ size: 2048 }) ?? quote.author.displayAvatarURL({ size: 2048 }); return await webhook.send(sendOptions); /* .catch((e: any) => e); */ diff --git a/lib/extensions/discord.js/ExtendedGuildMember.ts b/lib/extensions/discord.js/ExtendedGuildMember.ts index 9ef45f1..b11e9e3 100644 --- a/lib/extensions/discord.js/ExtendedGuildMember.ts +++ b/lib/extensions/discord.js/ExtendedGuildMember.ts @@ -8,11 +8,11 @@ import { type Role } from 'discord.js'; import { + Action, checkMutePermissions, createModLogEntry, createPunishmentEntry, punishDM, - PunishmentTypeDM, removePunishmentEntry } from '../../common/Moderation.js'; import { ModLogType } from '../../models/index.js'; @@ -32,7 +32,7 @@ interface Extension { * @returns Whether or not the dm was sent successfully. */ customPunishDM( - punishment: PunishmentTypeDM, + punishment: Action, reason?: string | null, duration?: number, modlog?: string, @@ -119,13 +119,13 @@ interface Extension { declare module 'discord.js' { export interface GuildMember extends Extension { - readonly client: TanzaniteClient; + readonly client: TanzaniteClient<true>; } } export class ExtendedGuildMember extends GuildMember implements Extension { public override async customPunishDM( - punishment: PunishmentTypeDM, + punishment: Action, reason?: string | null, duration?: number, modlog?: string, @@ -154,7 +154,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension { const result = await createModLogEntry( { client: this.client, - type: ModLogType.WARN, + type: ModLogType.Warn, user: this, moderator: moderator.id, reason: options.reason, @@ -169,7 +169,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension { if (!options.silent) { // dm user - const dmSuccess = await this.customPunishDM('warned', options.reason); + const dmSuccess = await this.customPunishDM(Action.Warn, options.reason); dmSuccessEvent = dmSuccess; if (!dmSuccess) return { result: warnResponse.DM_ERROR, caseNum: result.caseNum }; } @@ -195,7 +195,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension { if (options.addToModlog || options.duration) { const { log: modlog } = await createModLogEntry({ client: this.client, - type: options.duration ? ModLogType.TEMP_PUNISHMENT_ROLE : ModLogType.PERM_PUNISHMENT_ROLE, + type: options.duration ? ModLogType.TempPunishmentRole : ModLogType.PermPunishmentRole, guild: this.guild, moderator: moderator.id, user: this, @@ -262,7 +262,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension { if (options.addToModlog) { const { log: modlog } = await createModLogEntry({ client: this.client, - type: ModLogType.REMOVE_PUNISHMENT_ROLE, + type: ModLogType.RemovePunishmentRole, guild: this.guild, moderator: moderator.id, user: this, @@ -362,7 +362,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension { // add modlog entry const { log: modlog } = await createModLogEntry({ client: this.client, - type: options.duration ? ModLogType.TEMP_MUTE : ModLogType.PERM_MUTE, + type: options.duration ? ModLogType.TempMute : ModLogType.PermMute, user: this, moderator: moderator.id, reason: options.reason, @@ -389,7 +389,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension { if (!options.silent) { // dm user - const dmSuccess = await this.customPunishDM('muted', options.reason, options.duration ?? 0, modlog.id); + const dmSuccess = await this.customPunishDM(Action.Mute, options.reason, options.duration ?? 0, modlog.id); dmSuccessEvent = dmSuccess; if (!dmSuccess) return muteResponse.DM_ERROR; } @@ -441,7 +441,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension { // add modlog entry const { log: modlog } = await createModLogEntry({ client: this.client, - type: ModLogType.UNMUTE, + type: ModLogType.Unmute, user: this, moderator: moderator.id, reason: options.reason, @@ -465,7 +465,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension { if (!options.silent) { // dm user - const dmSuccess = await this.customPunishDM('unmuted', options.reason, undefined, '', false); + const dmSuccess = await this.customPunishDM(Action.Unmute, options.reason, undefined, '', false); dmSuccessEvent = dmSuccess; if (!dmSuccess) return unmuteResponse.DM_ERROR; } @@ -505,7 +505,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension { // add modlog entry const { log: modlog } = await createModLogEntry({ client: this.client, - type: ModLogType.KICK, + type: ModLogType.Kick, user: this, moderator: moderator.id, reason: options.reason, @@ -517,7 +517,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension { caseID = modlog.id; // dm user - const dmSuccess = options.silent ? null : await this.customPunishDM('kicked', options.reason, undefined, modlog.id); + const dmSuccess = options.silent ? null : await this.customPunishDM(Action.Kick, options.reason, undefined, modlog.id); dmSuccessEvent = dmSuccess ?? undefined; // kick @@ -564,7 +564,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension { // add modlog entry const { log: modlog } = await createModLogEntry({ client: this.client, - type: options.duration ? ModLogType.TEMP_BAN : ModLogType.PERM_BAN, + type: options.duration ? ModLogType.TempBan : ModLogType.PermBan, user: this, moderator: moderator.id, reason: options.reason, @@ -579,7 +579,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension { // dm user const dmSuccess = options.silent ? null - : await this.customPunishDM('banned', options.reason, options.duration ?? 0, modlog.id); + : await this.customPunishDM(Action.Ban, options.reason, options.duration ?? 0, modlog.id); dmSuccessEvent = dmSuccess ?? undefined; // ban @@ -646,7 +646,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension { // add modlog entry const { log: modlog } = await createModLogEntry({ client: this.client, - type: options.duration ? ModLogType.TEMP_CHANNEL_BLOCK : ModLogType.PERM_CHANNEL_BLOCK, + type: options.duration ? ModLogType.TempChannelBlock : ModLogType.PermChannelBlock, user: this, moderator: moderator.id, reason: options.reason, @@ -674,7 +674,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension { ? null : await punishDM({ client: this.client, - punishment: 'blocked', + punishment: Action.Block, reason: options.reason ?? undefined, duration: options.duration ?? 0, modlog: modlog.id, @@ -736,7 +736,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension { // add modlog entry const { log: modlog } = await createModLogEntry({ client: this.client, - type: ModLogType.CHANNEL_UNBLOCK, + type: ModLogType.ChannelUnblock, user: this, moderator: moderator.id, reason: options.reason, @@ -762,7 +762,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension { ? null : await punishDM({ client: this.client, - punishment: 'unblocked', + punishment: Action.Unblock, reason: options.reason ?? undefined, guild: this.guild, user: this, @@ -819,7 +819,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension { // add modlog entry const { log: modlog } = await createModLogEntry({ client: this.client, - type: ModLogType.TIMEOUT, + type: ModLogType.Timeout, user: this, moderator: moderator.id, reason: options.reason, @@ -834,7 +834,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension { if (!options.silent) { // dm user - const dmSuccess = await this.customPunishDM('timedout', options.reason, options.duration, modlog.id); + const dmSuccess = await this.customPunishDM(Action.Timeout, options.reason, options.duration, modlog.id); dmSuccessEvent = dmSuccess; if (!dmSuccess) return timeoutResponse.DM_ERROR; } @@ -877,7 +877,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension { // add modlog entry const { log: modlog } = await createModLogEntry({ client: this.client, - type: ModLogType.REMOVE_TIMEOUT, + type: ModLogType.RemoveTimeout, user: this, moderator: moderator.id, reason: options.reason, @@ -891,7 +891,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension { if (!options.silent) { // dm user - const dmSuccess = await this.customPunishDM('untimedout', options.reason, undefined, '', false); + const dmSuccess = await this.customPunishDM(Action.Untimeout, options.reason, undefined, '', false); dmSuccessEvent = dmSuccess; if (!dmSuccess) return removeTimeoutResponse.DM_ERROR; } diff --git a/lib/extensions/discord.js/ExtendedMessage.ts b/lib/extensions/discord.js/ExtendedMessage.ts index 1bb0904..07cba3d 100644 --- a/lib/extensions/discord.js/ExtendedMessage.ts +++ b/lib/extensions/discord.js/ExtendedMessage.ts @@ -1,11 +1,11 @@ -import { CommandUtil } from 'discord-akairo'; +import { CommandUtil } from '@notenoughupdates/discord-akairo'; import { Message, type Client } from 'discord.js'; import type { RawMessageData } from 'discord.js/typings/rawDataTypes.js'; -export class ExtendedMessage<Cached extends boolean = boolean> extends Message<Cached> { - public declare util: CommandUtil<Message>; +export class ExtendedMessage<InGuild extends boolean = boolean> extends Message<InGuild> { + public declare util: CommandUtil<Message<InGuild>>; - public constructor(client: Client, data: RawMessageData) { + public constructor(client: Client<true>, data: RawMessageData) { super(client, data); this.util = new CommandUtil(client.commandHandler, this); } diff --git a/lib/extensions/discord.js/ExtendedUser.ts b/lib/extensions/discord.js/ExtendedUser.ts index 7846a70..8f9d27b 100644 --- a/lib/extensions/discord.js/ExtendedUser.ts +++ b/lib/extensions/discord.js/ExtendedUser.ts @@ -14,7 +14,7 @@ interface Extension { declare module 'discord.js' { export interface User extends Extension { - readonly client: TanzaniteClient; + readonly client: TanzaniteClient<true>; } } diff --git a/lib/global.ts b/lib/global.ts index 0a0bcca..d3419c7 100644 --- a/lib/global.ts +++ b/lib/global.ts @@ -1,5 +1,7 @@ /* eslint-disable */ +declare const nodeFetch: typeof import('node-fetch').default; + declare global { interface ReadonlyArray<T> { includes<S, R extends `${Extract<S, string>}`>( @@ -8,6 +10,8 @@ declare global { fromIndex?: number ): searchElement is R & S; } + + var fetch: typeof nodeFetch; } export {}; diff --git a/lib/index.ts b/lib/index.ts index fc7bb4c..d486d68 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -3,6 +3,7 @@ export * from './automod/AutomodShared.js'; export * from './automod/MemberAutomod.js'; export * from './automod/MessageAutomod.js'; export * from './automod/PresenceAutomod.js'; +export * from './common/Appeals.js'; export * from './common/BotCache.js'; export * from './common/ButtonPaginator.js'; export * from './common/CanvasProgressBar.js'; @@ -10,12 +11,9 @@ export * from './common/ConfirmationPrompt.js'; export * from './common/DeleteButton.js'; export * as Moderation from './common/Moderation.js'; export type { - AppealButtonId, CreateModLogEntryOptions, CreatePunishmentEntryOptions, PunishDMOptions, - PunishmentTypeDM, - PunishmentTypePresent, RemovePunishmentEntryOptions, SimpleCreateModLogEntryOptions } from './common/Moderation.js'; diff --git a/lib/models/instance/ActivePunishment.ts b/lib/models/instance/ActivePunishment.ts index 9bd9d01..1b57f47 100644 --- a/lib/models/instance/ActivePunishment.ts +++ b/lib/models/instance/ActivePunishment.ts @@ -4,10 +4,10 @@ import { DataTypes, type Sequelize } from 'sequelize'; import { BaseModel } from '../BaseModel.js'; export enum ActivePunishmentType { - BAN = 'BAN', - MUTE = 'MUTE', - ROLE = 'ROLE', - BLOCK = 'BLOCK' + Ban = 'BAN', + Mute = 'MUTE', + Role = 'ROLE', + Block = 'BLOCK' } export interface ActivePunishmentModel { diff --git a/lib/models/instance/Guild.ts b/lib/models/instance/Guild.ts index 72091ca..462beee 100644 --- a/lib/models/instance/Guild.ts +++ b/lib/models/instance/Guild.ts @@ -210,7 +210,7 @@ export const guildSettingsObj = asGuildSetting({ name: 'Auto Publish Channels', description: 'Channels were every message is automatically published.', type: 'channel-array', - subType: [ChannelType.GuildNews] + subType: [ChannelType.GuildAnnouncement] }, welcomeChannel: { name: 'Welcome Channel', @@ -218,10 +218,10 @@ export const guildSettingsObj = asGuildSetting({ type: 'channel', subType: [ ChannelType.GuildText, - ChannelType.GuildNews, - ChannelType.GuildNewsThread, - ChannelType.GuildPublicThread, - ChannelType.GuildPrivateThread + ChannelType.GuildAnnouncement, + ChannelType.AnnouncementThread, + ChannelType.PublicThread, + ChannelType.PrivateThread ] }, muteRole: { diff --git a/lib/models/instance/Level.ts b/lib/models/instance/Level.ts index e22d63b..27def46 100644 --- a/lib/models/instance/Level.ts +++ b/lib/models/instance/Level.ts @@ -18,6 +18,9 @@ export interface LevelModelCreationAttributes { * Leveling information for a user in a guild. */ export class Level extends BaseModel<LevelModel, LevelModelCreationAttributes> implements LevelModel { + public static MAX_XP = 2147483647; + public static MAX_LEVEL = Level.convertXpToLevel(Level.MAX_XP); + /** * The user's id. */ diff --git a/lib/models/instance/ModLog.ts b/lib/models/instance/ModLog.ts index 324ad83..7a1a60a 100644 --- a/lib/models/instance/ModLog.ts +++ b/lib/models/instance/ModLog.ts @@ -4,104 +4,98 @@ import { DataTypes, type Sequelize } from 'sequelize'; import { BaseModel } from '../BaseModel.js'; export enum ModLogType { - PERM_BAN = 'PERM_BAN', - TEMP_BAN = 'TEMP_BAN', - UNBAN = 'UNBAN', - KICK = 'KICK', - PERM_MUTE = 'PERM_MUTE', - TEMP_MUTE = 'TEMP_MUTE', - UNMUTE = 'UNMUTE', - WARN = 'WARN', - PERM_PUNISHMENT_ROLE = 'PERM_PUNISHMENT_ROLE', - TEMP_PUNISHMENT_ROLE = 'TEMP_PUNISHMENT_ROLE', - REMOVE_PUNISHMENT_ROLE = 'REMOVE_PUNISHMENT_ROLE', - PERM_CHANNEL_BLOCK = 'PERM_CHANNEL_BLOCK', - TEMP_CHANNEL_BLOCK = 'TEMP_CHANNEL_BLOCK', - CHANNEL_UNBLOCK = 'CHANNEL_UNBLOCK', - TIMEOUT = 'TIMEOUT', - REMOVE_TIMEOUT = 'REMOVE_TIMEOUT' + PermBan = 'PERM_BAN', + TempBan = 'TEMP_BAN', + Unban = 'UNBAN', + Kick = 'KICK', + PermMute = 'PERM_MUTE', + TempMute = 'TEMP_MUTE', + Unmute = 'UNMUTE', + Warn = 'WARN', + PermPunishmentRole = 'PERM_PUNISHMENT_ROLE', + TempPunishmentRole = 'TEMP_PUNISHMENT_ROLE', + RemovePunishmentRole = 'REMOVE_PUNISHMENT_ROLE', + PermChannelBlock = 'PERM_CHANNEL_BLOCK', + TempChannelBlock = 'TEMP_CHANNEL_BLOCK', + ChannelUnblock = 'CHANNEL_UNBLOCK', + Timeout = 'TIMEOUT', + RemoveTimeout = 'REMOVE_TIMEOUT' } -export interface ModLogModel { - id: string; - type: ModLogType; - user: Snowflake; - moderator: Snowflake; - reason: string | null; - duration: number | null; - guild: Snowflake; - evidence: string; - pseudo: boolean; - hidden: boolean; -} - -export interface ModLogModelCreationAttributes { - id?: string; - type: ModLogType; - user: Snowflake; - moderator: Snowflake; - reason?: string | null; - duration?: number; - guild: Snowflake; - evidence?: string; - pseudo?: boolean; - hidden?: boolean; +export enum AppealStatus { + None = 'NONE', + Submitted = 'SUBMITTED', + Accepted = 'ACCEPTED', + Denied = 'DENIED' } -/** - * A mod log case. - */ -export class ModLog extends BaseModel<ModLogModel, ModLogModelCreationAttributes> implements ModLogModel { +export interface ModLogModel { /** * The primary key of the modlog entry. */ - public declare id: string; - + id: string; /** * The type of punishment. */ - public declare type: ModLogType; - + type: ModLogType; /** * The user being punished. */ - public declare user: Snowflake; - + user: Snowflake; /** * The user carrying out the punishment. */ - public declare moderator: Snowflake; - + moderator: Snowflake; /** * The reason the user is getting punished. */ - public declare reason: string | null; - + reason: string | null; /** * The amount of time the user is getting punished for. */ - public declare duration: number | null; - + duration: number | null; /** * The guild the user is getting punished in. */ - public declare guild: Snowflake; - + guild: Snowflake; /** * Evidence of what the user is getting punished for. */ - public declare evidence: string; - + evidence: string; /** * Not an actual modlog just used so a punishment entry can be made. */ - public declare pseudo: boolean; - + pseudo: boolean; /** * Hides from the modlog command unless show hidden is specified. */ - public declare hidden: boolean; + hidden: boolean; + /** + * The status of an appeal for this punishment + */ + appeal: AppealStatus; +} +export interface ModLogModelCreationAttributes { + id?: string; + type: ModLogType; + user: Snowflake; + moderator: Snowflake; + reason?: string | null; + duration?: number; + guild: Snowflake; + evidence?: string; + pseudo?: boolean; + hidden?: boolean; + appeal?: AppealStatus; +} + +export interface ModLog extends ModLogModel {} + +/** + * A mod log case. + */ +export class ModLog extends BaseModel<ModLogModel, ModLogModelCreationAttributes> { /** * Initializes the model. * @param sequelize The sequelize instance. @@ -118,7 +112,8 @@ export class ModLog extends BaseModel<ModLogModel, ModLogModelCreationAttributes guild: { type: DataTypes.STRING, references: { model: 'Guilds', key: 'id' } }, evidence: { type: DataTypes.TEXT, allowNull: true }, pseudo: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: false }, - hidden: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: false } + hidden: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: false }, + appeal: { type: DataTypes.STRING, allowNull: false, defaultValue: AppealStatus.None } }, { sequelize } ); diff --git a/lib/types/misc.ts b/lib/types/misc.ts index 5bf760c..5f28dae 100644 --- a/lib/types/misc.ts +++ b/lib/types/misc.ts @@ -1,14 +1,14 @@ import type { InteractionReplyOptions, + MessageCreateOptions, MessageEditOptions, - MessageOptions, MessagePayload, - ReplyMessageOptions, + MessageReplyOptions, WebhookEditMessageOptions } from 'discord.js'; -export type ReplyMessageType = string | MessagePayload | ReplyMessageOptions; +export type ReplyMessageType = string | MessagePayload | MessageReplyOptions; export type EditMessageType = string | MessageEditOptions | MessagePayload; export type SlashSendMessageType = string | MessagePayload | InteractionReplyOptions; export type SlashEditMessageType = string | MessagePayload | WebhookEditMessageOptions; -export type SendMessageType = string | MessagePayload | MessageOptions; +export type SendMessageType = string | MessagePayload | MessageCreateOptions; diff --git a/lib/utils/Arg.ts b/lib/utils/Arg.ts index 80ca878..803230b 100644 --- a/lib/utils/Arg.ts +++ b/lib/utils/Arg.ts @@ -1,5 +1,5 @@ import type { BaseBotArgumentType, BotArgumentType, BotArgumentTypeCaster, CommandMessage, SlashMessage } from '#lib'; -import { Argument, type Command, type Flag, type ParsedValuePredicate } from 'discord-akairo'; +import { Argument, type Command, type Flag, type ParsedValuePredicate } from '@notenoughupdates/discord-akairo'; import { type Message } from 'discord.js'; /** diff --git a/lib/utils/BotClientUtils.ts b/lib/utils/BotClientUtils.ts index 4b2c99b..6837237 100644 --- a/lib/utils/BotClientUtils.ts +++ b/lib/utils/BotClientUtils.ts @@ -21,7 +21,7 @@ import { type Snowflake, type UserResolvable } from 'discord.js'; -import _ from 'lodash'; +import { camelCase } from 'lodash-es'; import { emojis, Pronoun, PronounCode, pronounMapping, regex } from './Constants.js'; import { generateErrorEmbed } from './ErrorHandler.js'; import { addOrRemoveFromArray, formatError, inspect } from './Utils.js'; @@ -329,7 +329,7 @@ export class BotClientUtils { * @param error */ public async handleError(context: string, error: Error) { - await this.client.console.error(_.camelCase(context), `An error occurred:\n${formatError(error, false)}`, false); + await this.client.console.error(camelCase(context), `An error occurred:\n${formatError(error, false)}`, false); await this.client.console.channelError({ embeds: await generateErrorEmbed(this.client, { type: 'unhandledRejection', error: error, context }) }); @@ -382,6 +382,7 @@ export class BotClientUtils { public async uploadImageToImgur(image: string) { const clientId = this.client.config.credentials.imgurClientId; + // @ts-expect-error: missing global types const formData = new FormData(); formData.append('type', 'base64'); formData.append('image', image); diff --git a/lib/utils/Constants.ts b/lib/utils/Constants.ts index dd65e28..5ecbce2 100644 --- a/lib/utils/Constants.ts +++ b/lib/utils/Constants.ts @@ -1,4 +1,4 @@ -import { default as deepLock } from 'deep-lock'; +import deepLock from '@tanzanite/deep-lock'; import { Colors, GuildFeature, Snowflake } from 'discord.js'; const rawCapeUrl = 'https://raw.githubusercontent.com/NotEnoughUpdates/capes/master/'; @@ -294,6 +294,7 @@ export const mappings = deepLock({ AUTO_MODERATION: { name: 'Auto Moderation', important: false, emoji: '<:autoModeration:1010579417942200321>', weight: 33 }, MEMBER_PROFILES: { name: 'Member Profiles', important: false, emoji: '<:memberProfiles:1010580480409747547>', weight: 34 }, NEW_THREAD_PERMISSIONS: { name: 'New Thread Permissions', important: false, emoji: '<:newThreadPermissions:1010580968442171492>', weight: 35 }, + [GuildFeature.InvitesDisabled]: { name: 'Invites Disabled', important: false, emoji: null, weight: 36 }, }, regions: { diff --git a/lib/utils/ErrorHandler.ts b/lib/utils/ErrorHandler.ts index 3f8be89..ea4a026 100644 --- a/lib/utils/ErrorHandler.ts +++ b/lib/utils/ErrorHandler.ts @@ -1,4 +1,4 @@ -import { AkairoMessage, Command } from 'discord-akairo'; +import { AkairoMessage, Command } from '@notenoughupdates/discord-akairo'; import { ChannelType, Client, EmbedBuilder, escapeInlineCode, GuildTextBasedChannel, Message } from 'discord.js'; import { BotCommandHandlerEvents } from '../extensions/discord-akairo/BotCommandHandler.js'; import { SlashMessage } from '../extensions/discord-akairo/SlashMessage.js'; diff --git a/lib/utils/Utils.ts b/lib/utils/Utils.ts index 13806ec..ea70abf 100644 --- a/lib/utils/Utils.ts +++ b/lib/utils/Utils.ts @@ -1,9 +1,11 @@ +import { Util as AkairoUtil } from '@notenoughupdates/discord-akairo'; import { humanizeDuration as humanizeDurationMod } from '@notenoughupdates/humanize-duration'; +import deepLock from '@tanzanite/deep-lock'; import assert from 'assert/strict'; import cp from 'child_process'; -import deepLock from 'deep-lock'; -import { Util as AkairoUtil } from 'discord-akairo'; import { + ActionRowBuilder, + APITextInputComponent, Constants as DiscordConstants, EmbedBuilder, Message, @@ -11,6 +13,8 @@ import { PermissionFlagsBits, PermissionsBitField, PermissionsString, + TextInputBuilder, + TextInputComponentData, type APIEmbed, type APIMessage, type CommandInteraction, @@ -159,7 +163,7 @@ export async function slashRespond( delete (newResponseOptions as InteractionReplyOptions).ephemeral; // Cannot change a preexisting message to be ephemeral return (await interaction.editReply(newResponseOptions)) as Message | APIMessage; } else { - await interaction.reply(newResponseOptions); + await interaction.reply(newResponseOptions as SlashSendMessageType); return await interaction.fetchReply().catch(() => undefined); } } @@ -547,3 +551,9 @@ export function deepWriteable<T>(obj: T): DeepWritable<T> { export function formatPerms(permissions: PermissionsString[]) { return permissions.map((p) => `\`${mappings.permissions[p]?.name ?? p}\``).join(', '); } + +export function ModalInput(options: Partial<TextInputComponentData | APITextInputComponent>): ActionRowBuilder<TextInputBuilder> { + return new ActionRowBuilder<TextInputBuilder>({ + components: [new TextInputBuilder(options)] + }); +} diff --git a/neu-item-repo b/neu-item-repo -Subproject 17617a4517877c3e514bee9445668f3b4d83459 +Subproject f18a467e17e845bb79404ed329e14a73e389134 diff --git a/neu-item-repo-dangerous b/neu-item-repo-dangerous -Subproject 17617a4517877c3e514bee9445668f3b4d83459 +Subproject f18a467e17e845bb79404ed329e14a73e389134 diff --git a/package.json b/package.json index 8db194c..3f5250b 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ ], "license": "CC-BY-NC-SA-4.0", "scripts": { - "build": "yarn rimraf dist && yarn tsc -b", + "build": "rm -rf dist && yarn tsc -b", "build:no-emit": "yarn tsc --noEmit", "build:keep": "yarn tsc -b", "start": "yarn build && yarn start:raw", @@ -33,7 +33,7 @@ "format": "yarn prettier . --write", "format:check": "yarn prettier . --check", "lint": "yarn eslint src lib config tests", - "upgrade": "yarn rimraf yarn.lock && yarn cache clean && yarn install && yarn set version latest && git submodule update --recursive --remote && yarn upgrade-interactive", + "upgrade": "rm -f yarn.lock && yarn cache clean && yarn install && yarn set version latest && git submodule update --recursive --remote && yarn upgrade-interactive", "upgrade:sdk": "yarn dlx @yarnpkg/sdks vscode", "beta": "git push && git checkout beta && git merge master && git push && git checkout master", "deploy:beta": "pm2 deploy ecosystem.config.cjs beta", @@ -63,67 +63,66 @@ "default": "./config/index.js" } }, + "disabled-dependencies": { + "gif-to-apng": "^0.1.2" + }, "dependencies": { - "@discordjs/builders": "^1.1.0", - "@discordjs/rest": "^1.0.1", + "@discordjs/builders": "^1.2.0", + "@discordjs/rest": "^1.2.0", "@ironm00n/nbt-ts": "^1.4.0", - "@napi-rs/canvas": "^0.1.29", - "@notenoughupdates/discord.js-minesweeper": "^1.0.10", - "@notenoughupdates/events-intercept": "^3.0.1", + "@napi-rs/canvas": "^0.1.30", + "@notenoughupdates/discord-akairo": "^10.0.0-dev.1664158089.48d6368", "@notenoughupdates/humanize-duration": "^4.0.1", - "@notenoughupdates/simplify-number": "^1.0.1", - "@notenoughupdates/wolfram-alpha-api": "^1.0.2", - "@sentry/integrations": "^7.11.1", - "@sentry/node": "^7.11.1", - "@sentry/tracing": "^7.11.1", + "@sentry/integrations": "^7.13.0", + "@sentry/node": "^7.13.0", + "@sentry/tracing": "^7.13.0", + "@tanzanite/deep-lock": "^1.1.1", + "@tanzanite/discord.js-minesweeper": "^1.2.0", + "@tanzanite/events-intercept": "^3.1.0", + "@tanzanite/simplify-number": "^2.0.1", + "@tanzanite/wolfram-alpha": "^1.1.0", "chalk": "^5.0.1", - "deep-lock": "^1.0.0", - "discord-akairo": "npm:@notenoughupdates/discord-akairo@dev", - "discord-api-types": "0.37.1", - "discord.js": "npm:@notenoughupdates/discord.js@forum", + "discord-api-types": "0.37.10", + "discord.js": "^14.5.0", "fuse.js": "^6.6.2", - "gif-to-apng": "^0.1.2", - "googleapis": "^107.0.0", - "lodash": "^4.17.21", - "mathjs": "^11.1.0", + "googleapis": "^108.0.0", + "lodash-es": "^4.17.21", + "mathjs": "^11.2.1", "nanoid": "^4.0.0", "numeral": "^2.0.6", "pg": "^8.8.0", "pg-hstore": "^2.3.4", "prettier": "^2.7.1", "pretty-bytes": "^6.0.0", - "rimraf": "^3.0.2", - "sequelize": "6.21.4", + "sequelize": "6.23.1", "tinycolor2": "^1.4.2", - "typescript": "^4.8.2", - "vm2": "^3.9.10" + "typescript": "^4.8.3", + "vm2": "^3.9.11" }, "devDependencies": { "@sapphire/snowflake": "^3.2.2", - "@sentry/types": "^7.11.1", + "@sentry/types": "^7.13.0", "@types/eslint": "^8.4.6", - "@types/express": "^4.17.13", - "@types/lodash": "^4.14.184", - "@types/node": "^18.7.13", + "@types/express": "^4.17.14", + "@types/lodash-es": "^4", + "@types/node": "^18.7.21", + "@types/node-fetch": "^2.6.2", "@types/numeral": "^2.0.2", "@types/pg": "^8.6.5", - "@types/prettier": "^2.7.0", - "@types/rimraf": "^3.0.2", + "@types/prettier": "^2.7.1", "@types/tinycolor2": "^1.4.3", - "@types/validator": "^13.7.5", - "@typescript-eslint/eslint-plugin": "^5.35.1", - "@typescript-eslint/parser": "^5.35.1", - "eslint": "^8.23.0", + "@types/validator": "^13.7.7", + "@typescript-eslint/eslint-plugin": "^5.38.0", + "@typescript-eslint/parser": "^5.38.0", + "eslint": "^8.24.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-deprecation": "^1.3.2", "ts-essentials": "^9.3.0", - "vitest": "^0.22.1" + "vitest": "^0.23.4" }, "packageManager": "yarn@3.2.3", "resolutions": { - "@discordjs/builders@workspace:^": "^1.1.0", - "@discordjs/collection@workspace:^": "^1.0.1", - "@discordjs/rest@workspace:^": "^1.0.1", - "tslib": "^2.4.0" + "tslib": "^2.4.0", + "discord.js": "npm:@notenoughupdates/discord.js@dev" } } @@ -1,26 +1,38 @@ +import { performance } from 'node:perf_hooks'; +performance.mark('processStart'); + console.log('Tanzanite is Starting'); -import { init } from '../lib/utils/Logger.js'; +import { init } from '#lib/utils/Logger.js'; // creates proxies on console.log and console.warn // also starts a REPL session init(); import { config } from '#config'; -import { dirname } from 'path'; -import { fileURLToPath } from 'url'; -import { Sentry } from '../lib/common/Sentry.js'; -import { TanzaniteClient } from '../lib/extensions/discord-akairo/TanzaniteClient.js'; +import { Sentry } from '#lib/common/Sentry.js'; +import { TanzaniteClient } from '#lib/extensions/discord-akairo/TanzaniteClient.js'; +import { dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; const isDry = process.argv.includes('dry'); -if (!isDry && config.credentials.sentryDsn !== null) new Sentry(dirname(fileURLToPath(import.meta.url)) || process.cwd(), config); + +if (!isDry && config.credentials.sentryDsn !== null) { + new Sentry(dirname(fileURLToPath(import.meta.url)) || process.cwd(), config); +} + TanzaniteClient.extendStructures(); + const client = new TanzaniteClient(config); -// @ts-ignore: for debugging purposes +// @ts-ignore: I don't want to add this to the global typings, this is only for debugging purposes global.client = client; -if (!isDry) await client.dbPreInit(); +if (!isDry) { + await client.dbPreInit(); +} + await client.init(); + if (isDry) { process.exit(0); } else { diff --git a/src/commands/config/config.ts b/src/commands/config/config.ts index adc41d8..4d38c54 100644 --- a/src/commands/config/config.ts +++ b/src/commands/config/config.ts @@ -13,8 +13,8 @@ import { type GuildSettingType, type SlashMessage } from '#lib'; +import { ExtSub, type ArgumentGeneratorReturn, type SlashOption } from '@notenoughupdates/discord-akairo'; import assert from 'assert/strict'; -import { type ArgumentGeneratorReturn, type SlashOption } from 'discord-akairo'; import { ActionRowBuilder, ApplicationCommandOptionType, @@ -32,10 +32,9 @@ import { User, type Message, type MessageComponentInteraction, - type MessageOptions + type MessageCreateOptions } from 'discord.js'; -import _ from 'lodash'; -const { camelCase, snakeCase } = _; +import { camelCase, snakeCase } from 'lodash-es'; export const arrayActions = ['view' as const, 'add' as const, 'remove' as const, 'clear' as const]; export type ArrayActions = typeof arrayActions[number]; @@ -79,7 +78,7 @@ export default class ConfigCommand extends BotCommand { description: `Manage the server's ${loweredName}`, type: ApplicationCommandOptionType.SubcommandGroup, options: isArray - ? [ + ? ([ { name: 'view', description: `View the server's ${loweredName}.`, @@ -118,8 +117,8 @@ export default class ConfigCommand extends BotCommand { description: `Remove all values from a server's ${loweredName}.`, type: ApplicationCommandOptionType.Subcommand } - ] - : [ + ] as ExtSub[]) + : ([ { name: 'view', description: `View the server's ${loweredName}.`, @@ -144,7 +143,7 @@ export default class ConfigCommand extends BotCommand { description: `Delete the server's ${loweredName}.`, type: ApplicationCommandOptionType.Subcommand } - ] + ] as ExtSub[]) }; }), channel: 'guild', @@ -309,7 +308,7 @@ export default class ConfigCommand extends BotCommand { public async generateMessageOptions( message: CommandMessage | SlashMessage, setting?: undefined | keyof typeof guildSettingsObj - ): Promise<MessageOptions & InteractionUpdateOptions> { + ): Promise<MessageCreateOptions & InteractionUpdateOptions> { assert(message.inGuild()); const settingsEmbed = new EmbedBuilder().setColor(colors.default); diff --git a/src/commands/config/disable.ts b/src/commands/config/disable.ts index 6dd94a6..776ecf0 100644 --- a/src/commands/config/disable.ts +++ b/src/commands/config/disable.ts @@ -10,9 +10,9 @@ import { } from '#lib'; import assert from 'assert/strict'; import { ApplicationCommandOptionType, AutocompleteInteraction } from 'discord.js'; -import { default as Fuse } from 'fuse.js'; -assert(Fuse); +// todo: remove this bullshit once typescript gets its shit together +const Fuse = (await import('fuse.js')).default as unknown as typeof import('fuse.js').default; export default class DisableCommand extends BotCommand { private static blacklistedCommands = ['eval', 'disable']; diff --git a/src/commands/config/log.ts b/src/commands/config/log.ts index 0c74ce7..6d0f594 100644 --- a/src/commands/config/log.ts +++ b/src/commands/config/log.ts @@ -8,8 +8,8 @@ import { type GuildLogType, type SlashMessage } from '#lib'; +import { ArgumentGeneratorReturn } from '@notenoughupdates/discord-akairo'; import assert from 'assert/strict'; -import { ArgumentGeneratorReturn } from 'discord-akairo'; import { ApplicationCommandOptionType, ChannelType } from 'discord.js'; export default class LogCommand extends BotCommand { @@ -38,10 +38,10 @@ export default class LogCommand extends BotCommand { slashType: ApplicationCommandOptionType.Channel, channelTypes: [ ChannelType.GuildText, - ChannelType.GuildNews, - ChannelType.GuildNewsThread, - ChannelType.GuildPublicThread, - ChannelType.GuildPrivateThread + ChannelType.GuildAnnouncement, + ChannelType.AnnouncementThread, + ChannelType.PublicThread, + ChannelType.PrivateThread ], only: 'slash' } diff --git a/src/commands/dev/eval.ts b/src/commands/dev/eval.ts index 83168e0..5fe21c0 100644 --- a/src/commands/dev/eval.ts +++ b/src/commands/dev/eval.ts @@ -283,7 +283,7 @@ export default class EvalCommand extends BotCommand { if (!err && proto) embed.addFields({ name: ':gear: Proto', value: proto }); if (!silent || message.util.isSlashMessage(message)) { - await message.util.reply({ content: null, embeds: [embed] }); + await message.util.reply({ content: '', embeds: [embed] }); } else { const success = await message.author.send({ embeds: [embed] }).catch(() => false); if (!deleteMsg) await message.react(success ? emojis.successFull : emojis.errorFull).catch(() => {}); diff --git a/src/commands/dev/superUser.ts b/src/commands/dev/superUser.ts index fc7fcbf..54332d6 100644 --- a/src/commands/dev/superUser.ts +++ b/src/commands/dev/superUser.ts @@ -1,5 +1,5 @@ import { BotCommand, emojis, format, type ArgType, type CommandMessage } from '#lib'; -import { type ArgumentGeneratorReturn, type ArgumentTypeCasterReturn } from 'discord-akairo'; +import { type ArgumentGeneratorReturn, type ArgumentTypeCasterReturn } from '@notenoughupdates/discord-akairo'; export default class SuperUserCommand extends BotCommand { public constructor() { diff --git a/src/commands/dev/test.ts b/src/commands/dev/test.ts index e1f3b73..994b76f 100644 --- a/src/commands/dev/test.ts +++ b/src/commands/dev/test.ts @@ -1,13 +1,14 @@ -import { BotCommand, ButtonPaginator, colors, emojis, OptArgType, Shared, type CommandMessage } from '#lib'; +import { BotCommand, ButtonPaginator, chunk, colors, emojis, OptArgType, Shared, type CommandMessage } from '#lib'; import { ActionRowBuilder, + APIEmbed, ButtonBuilder, ButtonStyle, + Collection, EmbedBuilder, - GatewayDispatchEvents, + Message, Routes, - type ApplicationCommand, - type Collection + type ApplicationCommand } from 'discord.js'; import badLinksSecretArray from '../../../lib/badlinks-secret.js'; import badLinksArray from '../../../lib/badlinks.js'; @@ -52,15 +53,38 @@ export default class TestCommand extends BotCommand { return await message.util.reply(responses[Math.floor(Math.random() * responses.length)]); } + console.dir(args); + if (args.feature) { if (['button', 'buttons'].includes(args.feature?.toLowerCase())) { const buttonRow = new ActionRowBuilder<ButtonBuilder>().addComponents( - new ButtonBuilder({ style: ButtonStyle.Primary, customId: 'primaryButton', label: 'Primary' }), - new ButtonBuilder({ style: ButtonStyle.Secondary, customId: 'secondaryButton', label: 'Secondary' }), - new ButtonBuilder({ style: ButtonStyle.Success, customId: 'successButton', label: 'Success' }), - new ButtonBuilder({ style: ButtonStyle.Danger, customId: 'dangerButton', label: 'Danger' }), - new ButtonBuilder({ style: ButtonStyle.Link, label: 'Link', url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ' }) + new ButtonBuilder({ + style: ButtonStyle.Primary, + customId: 'test;button;primary', + label: 'Primary' + }), + new ButtonBuilder({ + style: ButtonStyle.Secondary, + customId: 'test;button;secondary', + label: 'Secondary' + }), + new ButtonBuilder({ + style: ButtonStyle.Success, + customId: 'test;button;success', + label: 'Success' + }), + new ButtonBuilder({ + style: ButtonStyle.Danger, + customId: 'test;button;danger', + label: 'Danger' + }), + new ButtonBuilder({ + style: ButtonStyle.Link, + label: 'Link', + url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ' + }) ); + return await message.util.reply({ content: 'buttons', components: [buttonRow] }); } else if (['embed', 'button embed'].includes(args.feature?.toLowerCase())) { const embed = new EmbedBuilder() @@ -80,18 +104,23 @@ export default class TestCommand extends BotCommand { const buttonRow = new ActionRowBuilder<ButtonBuilder>().addComponents( new ButtonBuilder({ style: ButtonStyle.Link, label: 'Link', url: 'https://google.com/' }) ); + return await message.util.reply({ content: 'Test', embeds: [embed], components: [buttonRow] }); } else if (['lots of buttons'].includes(args.feature?.toLowerCase())) { const buttonRows: ActionRowBuilder<ButtonBuilder>[] = []; + for (let a = 1; a <= 5; a++) { const row = new ActionRowBuilder<ButtonBuilder>(); + for (let b = 1; b <= 5; b++) { - const id = (a + 5 * (b - 1)).toString(); + const id = `test;lots;${a + 5 * (b - 1)}`; const button = new ButtonBuilder({ style: ButtonStyle.Primary, customId: id, label: id }); row.addComponents(button); } + buttonRows.push(row); } + return await message.util.reply({ content: 'buttons', components: buttonRows }); } else if (['paginate'].includes(args.feature?.toLowerCase())) { const embeds = []; @@ -142,13 +171,16 @@ export default class TestCommand extends BotCommand { return message.util.reply(`${emojis.error} no`); } else if (['sync automod'].includes(args.feature?.toLowerCase())) { const row = (await Shared.findByPk(0))!; + row.badLinks = badLinksArray; row.badLinksSecret = badLinksSecretArray; row.badWords = badWords; + await row.save(); + return await message.util.reply(`${emojis.success} Synced automod.`); } else if (['modal'].includes(args.feature?.toLowerCase())) { - const m = await message.util.reply({ + return await message.util.reply({ content: 'Click for modal', components: [ new ActionRowBuilder<ButtonBuilder>().addComponents( @@ -156,63 +188,57 @@ export default class TestCommand extends BotCommand { ) ] }); + } else if (args.feature.includes('backlog experiments')) { + this.client.logger.debug('backlog experiments'); + + if (message.channelId !== '1019830755658055691') { + return await message.util.reply(`${emojis.error} This only works in <#1019830755658055691>.`); + } - // eslint-disable-next-line @typescript-eslint/no-misused-promises - this.client.ws.on(GatewayDispatchEvents.InteractionCreate, async (i: any) => { - if (i?.data?.custom_id !== 'test;modal' || i?.data?.component_type !== 2) return; - if (i?.message?.id !== m.id) return; - - const text = { type: 4, style: 1, min_length: 1, max_length: 4000, required: true }; - - await this.client.rest.post(Routes.interactionCallback(i.id, i.token), { - body: { - type: 9, - data: { - custom_id: 'test;login', - title: 'Login (real)', - components: [ - { - type: 1, - components: [ - { - ...text, - custom_id: 'test;login;email', - label: 'Email', - placeholder: 'Email' - } - ] - }, - { - type: 1, - components: [ - { - ...text, - custom_id: 'test;login;password', - label: 'Password', - placeholder: 'Password' - } - ] - }, - { - type: 1, - components: [ - { - ...text, - custom_id: 'test;login;2fa', - label: 'Enter Discord Auth Code', - min_length: 6, - max_length: 6, - placeholder: '6-digit authentication code' - } - ] - } - ] - } - } + let messages = new Collection<string, Message>(); + let lastID: string | undefined; + + // eslint-disable-next-line no-constant-condition + while (true) { + const fetchedMessages = await message.channel.messages.fetch({ + limit: 100, + ...(lastID && { before: lastID }) }); - }); - return; + if (fetchedMessages.size === 0) { + break; + } + + messages = messages.concat(fetchedMessages); + lastID = fetchedMessages.lastKey(); + + this.client.logger.debug(messages.size); + this.client.logger.debug(lastID); + } + + const embeds = messages + .sort((a, b) => a.createdTimestamp - b.createdTimestamp) + .filter((m) => m.embeds.length > 0 && (m.embeds[0].title?.includes('Guild Experiment') ?? false)) + .map( + (m): APIEmbed => ({ + ...m.embeds[0]!.toJSON(), + timestamp: new Date(m.createdTimestamp).toISOString() + }) + ); + + const chunked = chunk(embeds, 10); + + let i = 0; + for (const chunk of chunked) { + this.client.logger.debug(i); + this.client.logger.debug(chunk, 1); + await this.client.rest.post(Routes.channelMessages('795356494261911553'), { + body: { embeds: chunk } + }); + i++; + } + + return await message.util.reply(`${emojis.success} Done.`); } } return await message.util.reply(responses[Math.floor(Math.random() * responses.length)]); diff --git a/src/commands/fun/minesweeper.ts b/src/commands/fun/minesweeper.ts index 85945c7..b0528ac 100644 --- a/src/commands/fun/minesweeper.ts +++ b/src/commands/fun/minesweeper.ts @@ -1,8 +1,6 @@ import { BotCommand, emojis, OptArgType, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; -import { Minesweeper } from '@notenoughupdates/discord.js-minesweeper'; -import assert from 'assert/strict'; +import { Minesweeper } from '@tanzanite/discord.js-minesweeper'; import { ApplicationCommandOptionType } from 'discord.js'; -assert(Minesweeper); export default class MinesweeperCommand extends BotCommand { public constructor() { diff --git a/src/commands/info/guildInfo.ts b/src/commands/info/guildInfo.ts index e364a89..acd5b86 100644 --- a/src/commands/info/guildInfo.ts +++ b/src/commands/info/guildInfo.ts @@ -11,6 +11,7 @@ import { type OptArgType, type SlashMessage } from '#lib'; +import { embedField } from '#lib/common/tags.js'; import assert from 'assert/strict'; import { ApplicationCommandOptionType, @@ -26,7 +27,6 @@ import { PermissionFlagsBits, type BaseGuildVoiceChannel, type GuildPreview, - type Snowflake, type Vanity } from 'discord.js'; @@ -66,9 +66,13 @@ export default class GuildInfoCommand extends BotCommand { let guild: ArgType<'guild' | 'snowflake'> | GuildPreview = args.guild ?? message.guild!; if (typeof guild === 'string') { - const preview = await this.client.fetchGuildPreview(`${args.guild}` as Snowflake).catch(() => undefined); - if (preview) guild = preview; - else return await message.util.reply(`${emojis.error} That guild is not discoverable or does not exist.`); + const preview = await this.client.fetchGuildPreview(`${args.guild}`).catch(() => {}); + + if (preview) { + guild = preview; + } else { + return await message.util.reply(`${emojis.error} That guild is not discoverable or does not exist.`); + } } assert(guild); @@ -96,10 +100,13 @@ export default class GuildInfoCommand extends BotCommand { const otherEmojis = mappings.otherEmojis; const verifiedGuilds = Object.values(mappings.guilds); - if (verifiedGuilds.includes(guild.id as typeof verifiedGuilds[number])) description.push(otherEmojis.BushVerified); - if (guild instanceof Guild) { - if (guild.premiumTier !== GuildPremiumTier.None) description.push(otherEmojis[`BoostTier${guild.premiumTier}`]); + if (verifiedGuilds.includes(guild.id as typeof verifiedGuilds[number])) { + description.push(otherEmojis.BushVerified); + } + + if (guild instanceof Guild && guild.premiumTier !== GuildPremiumTier.None) { + description.push(otherEmojis[`BoostTier${guild.premiumTier}`]); } const features = mappings.features; @@ -138,52 +145,65 @@ export default class GuildInfoCommand extends BotCommand { ) ] as RTCRegion[]; + const members = guild.memberCount; + const online = guild.approximatePresenceCount ?? 0; + const offline = members - online; + guildAbout.push( - `**Owner:** ${escapeMarkdown(guild.members.cache.get(guild.ownerId)?.user.tag ?? '¯\\_(ツ)_/¯')}`, - `**Created** ${timestampAndDelta(guild.createdAt, 'd')}`, - `**Members:** ${guild.memberCount.toLocaleString() ?? 0} (${emojis.onlineCircle} ${ - guild.approximatePresenceCount?.toLocaleString() ?? 0 - }, ${emojis.offlineCircle} ${(guild.memberCount - (guild.approximatePresenceCount ?? 0)).toLocaleString() ?? 0})`, - `**Regions:** ${guildRegions.map((region) => mappings.regions[region] || region).join(', ')}` + embedField` + Owner ${escapeMarkdown(guild.members.cache.get(guild.ownerId)?.user.tag ?? '¯\\_(ツ)_/¯')} + Created ${timestampAndDelta(guild.createdAt, 'd')} + Members ${members} (${emojis.onlineCircle} ${online}, ${emojis.offlineCircle} ${offline}) + Regions ${guildRegions.map((region) => mappings.regions[region] || region).join(', ')} + Boosts ${guild.premiumSubscriptionCount && `Level ${guild.premiumTier} with ${guild.premiumSubscriptionCount} boosts`}` ); - if (guild.premiumSubscriptionCount) - guildAbout.push(`**Boosts:** Level ${guild.premiumTier} with ${guild.premiumSubscriptionCount ?? 0} boosts`); + if (guild.members.me?.permissions.has(PermissionFlagsBits.ManageGuild) && guild.vanityURLCode) { const vanityInfo: Vanity = await guild.fetchVanityData(); - guildAbout.push(`**Vanity URL:** discord.gg/${vanityInfo.code}`, `**Vanity Uses:** ${vanityInfo.uses?.toLocaleString()}`); + guildAbout.push( + embedField` + Vanity URL ${`discord.gg/${vanityInfo.code}`} + Vanity Uses ${vanityInfo.uses}` + ); } - if (guild.icon) guildAbout.push(`**Icon:** [link](${guild.iconURL({ size: 4096, extension: 'png' })})`); - if (guild.banner) guildAbout.push(`**Banner:** [link](${guild.bannerURL({ size: 4096, extension: 'png' })})`); - if (guild.splash) guildAbout.push(`**Splash:** [link](${guild.splashURL({ size: 4096, extension: 'png' })})`); + guildAbout.push( + embedField` + Icon ${guild.icon && `[link](${guild.iconURL({ size: 4096, extension: 'png' })})`} + Banner ${guild.banner && `[link](${guild.bannerURL({ size: 4096, extension: 'png' })})`} + Splash ${guild.splash && `[link](${guild.splashURL({ size: 4096, extension: 'png' })})`}` + ); } else { + const members = guild.approximateMemberCount; + const online = guild.approximatePresenceCount; + const offline = members - online; + guildAbout.push( - `**Members:** ${guild.approximateMemberCount?.toLocaleString() ?? 0} (${emojis.onlineCircle} ${ - guild.approximatePresenceCount?.toLocaleString() ?? 0 - }, ${emojis.offlineCircle} ${( - (guild.approximateMemberCount ?? 0) - (guild.approximatePresenceCount ?? 0) - ).toLocaleString()})`, - `**Emojis:** ${(guild as GuildPreview).emojis.size?.toLocaleString() ?? 0}`, - `**Stickers:** ${(guild as GuildPreview).stickers.size}` + embedField` + Members ${members} (${emojis.onlineCircle} ${online}, ${emojis.offlineCircle} ${offline}) + Emojis ${guild.emojis.size} + Stickers ${guild.stickers.size}` ); } - embed.addFields({ name: '» About', value: guildAbout.join('\n') }); + embed.addFields({ + name: '» About', + // filter out anything that is undefined + value: guildAbout.filter((v) => v !== undefined).join('\n') + }); } private generateStatsField(embed: EmbedBuilder, guild: Guild | GuildPreview) { if (!(guild instanceof Guild)) return; - const guildStats: string[] = []; - const channelTypes = ( [ ['Text', [ChannelType.GuildText]], ['Voice', [ChannelType.GuildVoice]], - ['News', [ChannelType.GuildNews]], + ['News', [ChannelType.GuildAnnouncement]], ['Stage', [ChannelType.GuildStageVoice]], ['Category', [ChannelType.GuildCategory]], - ['Thread', [ChannelType.GuildNewsThread, ChannelType.GuildPrivateThread, ChannelType.GuildPublicThread]] + ['Thread', [ChannelType.AnnouncementThread, ChannelType.PrivateThread, ChannelType.PublicThread]] ] as const ).map( (type) => @@ -205,30 +225,25 @@ export default class GuildInfoCommand extends BotCommand { [GuildPremiumTier.None]: 0 } as const; - guildStats.push( - `**Channels:** ${guild.channels.cache.size.toLocaleString()} / 500 (${channelTypes.join(', ')})`, - // subtract 1 for @everyone role - `**Roles:** ${((guild.roles.cache.size ?? 0) - 1).toLocaleString()} / 250`, - `**Emojis:** ${guild.emojis.cache.size?.toLocaleString() ?? 0} / ${EmojiTierMap[guild.premiumTier]}`, - `**Stickers:** ${guild.stickers.cache.size?.toLocaleString() ?? 0} / ${StickerTierMap[guild.premiumTier]}` - ); + const guildStats = embedField` + Channels ${guild.channels.cache.size} / 500 (${channelTypes.join(', ')}) + Roles ${guild.roles.cache.size - 1 /* account for @everyone role */} / 250 + Emojis ${guild.emojis.cache.size} / ${EmojiTierMap[guild.premiumTier]} + Stickers ${guild.stickers.cache.size} / ${StickerTierMap[guild.premiumTier]}`; - embed.addFields({ name: '» Stats', value: guildStats.join('\n') }); + embed.addFields({ name: '» Stats', value: guildStats }); } private generateSecurityField(embed: EmbedBuilder, guild: Guild | GuildPreview) { if (!(guild instanceof Guild)) return; - const guildSecurity: string[] = []; - - guildSecurity.push( - `**Verification Level:** ${MappedGuildVerificationLevel[guild.verificationLevel]}`, - `**Explicit Content Filter:** ${MappedGuildExplicitContentFilter[guild.explicitContentFilter]}`, - `**Default Message Notifications:** ${MappedGuildDefaultMessageNotifications[guild.defaultMessageNotifications]}`, - `**2FA Required:** ${guild.mfaLevel === GuildMFALevel.Elevated ? 'True' : 'False'}` - ); + const guildSecurity = embedField` + Verification Level ${MappedGuildVerificationLevel[guild.verificationLevel]} + Explicit Content Filter ${MappedGuildExplicitContentFilter[guild.explicitContentFilter]} + Default Message Notifications ${MappedGuildDefaultMessageNotifications[guild.defaultMessageNotifications]} + 2FA Required ${guild.mfaLevel === GuildMFALevel.Elevated ? 'True' : 'False'}`; - embed.addFields({ name: '» Security', value: guildSecurity.join('\n') }); + embed.addFields({ name: '» Security', value: guildSecurity }); } } diff --git a/src/commands/info/help.ts b/src/commands/info/help.ts index 1680b75..565fc25 100644 --- a/src/commands/info/help.ts +++ b/src/commands/info/help.ts @@ -20,9 +20,11 @@ import { ButtonStyle, EmbedBuilder } from 'discord.js'; -import { default as Fuse } from 'fuse.js'; import packageDotJSON from '../../../package.json' assert { type: 'json' }; +// todo: remove this bullshit once typescript gets its shit together +const Fuse = (await import('fuse.js')).default as unknown as typeof import('fuse.js').default; + assert(Fuse); assert(packageDotJSON); diff --git a/src/commands/info/inviteInfo.ts b/src/commands/info/inviteInfo.ts index bf66a4c..123063d 100644 --- a/src/commands/info/inviteInfo.ts +++ b/src/commands/info/inviteInfo.ts @@ -1,4 +1,5 @@ import { Arg, ArgType, BotCommand, colors, type CommandMessage, type SlashMessage } from '#lib'; +import { embedField } from '#lib/common/tags.js'; import { ApplicationCommandOptionType, EmbedBuilder, Invite } from 'discord.js'; export default class InviteInfoCommand extends BotCommand { @@ -38,8 +39,10 @@ export default class InviteInfoCommand extends BotCommand { } private generateAboutField(embed: EmbedBuilder, invite: Invite) { - const about = [`**code:** ${invite.code}`, `**channel:** ${invite.channel!.name}`]; + const about = embedField` + Code ${invite.code} + Channel ${invite.channel!.name}`; - embed.addFields({ name: '» About', value: about.join('\n') }); + embed.addFields({ name: '» About', value: about }); } } diff --git a/src/commands/info/ping.ts b/src/commands/info/ping.ts index ad58cc0..66cdf01 100644 --- a/src/commands/info/ping.ts +++ b/src/commands/info/ping.ts @@ -43,7 +43,7 @@ export default class PingCommand extends BotCommand { .setColor(colors.default) .setTimestamp(); return message.util.reply({ - content: null, + content: '', embeds: [embed] }); } diff --git a/src/commands/info/snowflake.ts b/src/commands/info/snowflake.ts index ba93611..1d3533e 100644 --- a/src/commands/info/snowflake.ts +++ b/src/commands/info/snowflake.ts @@ -1,5 +1,5 @@ import { BotCommand, colors, timestamp, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; -import { stripIndent } from '#tags'; +import { embedField } from '#tags'; import { ApplicationCommandOptionType, ChannelType, @@ -50,7 +50,7 @@ export default class SnowflakeCommand extends BotCommand { snowflakeEmbed.setTitle(`:snowflake: DM with ${escapeMarkdown(channel.recipient?.tag ?? '¯\\_(ツ)_/¯')} \`[Channel]\``); } else if ( channel.type === ChannelType.GuildCategory || - channel.type === ChannelType.GuildNews || + channel.type === ChannelType.GuildAnnouncement || channel.type === ChannelType.GuildText || channel.type === ChannelType.GuildVoice || channel.type === ChannelType.GuildStageVoice || @@ -68,10 +68,10 @@ export default class SnowflakeCommand extends BotCommand { // Guild if (this.client.guilds.cache.has(snowflake)) { const guild = this.client.guilds.cache.get(snowflake)!; - const guildInfo = stripIndent` - **Name:** ${escapeMarkdown(guild.name)} - **Owner:** ${escapeMarkdown(this.client.users.cache.get(guild.ownerId)?.tag ?? '¯\\_(ツ)_/¯')} (${guild.ownerId}) - **Members:** ${guild.memberCount?.toLocaleString()}`; + const guildInfo = embedField` + Name ${escapeMarkdown(guild.name)} + Owner ${escapeMarkdown(this.client.users.cache.get(guild.ownerId)?.tag ?? '¯\\_(ツ)_/¯')} (${guild.ownerId}) + Members ${guild.memberCount?.toLocaleString()}`; if (guild.icon) snowflakeEmbed.setThumbnail(guild.iconURL({ size: 2048 })!); snowflakeEmbed.addFields({ name: '» Server Info', value: guildInfo }); snowflakeEmbed.setTitle(`:snowflake: ${escapeMarkdown(guild.name)} \`[Server]\``); @@ -81,8 +81,8 @@ export default class SnowflakeCommand extends BotCommand { const fetchedUser = await this.client.users.fetch(`${snowflake}`).catch(() => undefined); if (this.client.users.cache.has(snowflake) || fetchedUser) { const user = (this.client.users.cache.get(snowflake) ?? fetchedUser)!; - const userInfo = stripIndent` - **Name:** <@${user.id}> (${escapeMarkdown(user.tag)})`; + const userInfo = embedField` + Name ${`<@${user.id}> (${escapeMarkdown(user.tag)})`}`; if (user.avatar) snowflakeEmbed.setThumbnail(user.avatarURL({ size: 2048 })!); snowflakeEmbed.addFields({ name: '» User Info', value: userInfo }); snowflakeEmbed.setTitle(`:snowflake: ${escapeMarkdown(user.tag)} \`[User]\``); @@ -91,9 +91,9 @@ export default class SnowflakeCommand extends BotCommand { // Emoji if (this.client.emojis.cache.has(snowflake)) { const emoji = this.client.emojis.cache.get(snowflake)!; - const emojiInfo = stripIndent` - **Name:** ${escapeMarkdown(emoji.name ?? '¯\\_(ツ)_/¯')} - **Animated:** ${emoji.animated}`; + const emojiInfo = embedField` + Name ${escapeMarkdown(emoji.name ?? '¯\\_(ツ)_/¯')} + Animated ${emoji.animated}`; if (emoji.url) snowflakeEmbed.setThumbnail(emoji.url); snowflakeEmbed.addFields({ name: '» Emoji Info', value: emojiInfo }); snowflakeEmbed.setTitle(`:snowflake: ${escapeMarkdown(emoji.name ?? '¯\\_(ツ)_/¯')} \`[Emoji]\``); @@ -102,13 +102,13 @@ export default class SnowflakeCommand extends BotCommand { // Role if (message.guild && message.guild.roles.cache.has(snowflake)) { const role = message.guild.roles.cache.get(snowflake)!; - const roleInfo = stripIndent` - **Name:** <@&${role.id}> (${escapeMarkdown(role.name)}) - **Members:** ${role.members.size} - **Hoisted:** ${role.hoist} - **Managed:** ${role.managed} - **Position:** ${role.position} - **Hex Color:** ${role.hexColor}`; + const roleInfo = embedField` + Name ${`<@&${role.id}> (${escapeMarkdown(role.name)})`} + Members ${role.members.size} + Hoisted ${role.hoist} + Managed ${role.managed} + Position ${role.position} + Hex Color ${role.hexColor}`; if (role.color) snowflakeEmbed.setColor(role.color); snowflakeEmbed.addFields({ name: '» Role Info', value: roleInfo }); snowflakeEmbed.setTitle(`:snowflake: ${escapeMarkdown(role.name)} \`[Role]\``); @@ -116,12 +116,12 @@ export default class SnowflakeCommand extends BotCommand { // SnowflakeInfo const deconstructedSnowflake: DeconstructedSnowflake = SnowflakeUtil.deconstruct(snowflake); - const snowflakeInfo = stripIndent` - **Timestamp:** ${deconstructedSnowflake.timestamp} - **Created:** ${timestamp(new Date(Number(deconstructedSnowflake.timestamp)))} - **Worker ID:** ${deconstructedSnowflake.workerId} - **Process ID:** ${deconstructedSnowflake.processId} - **Increment:** ${deconstructedSnowflake.increment}`; + const snowflakeInfo = embedField` + Timestamp ${deconstructedSnowflake.timestamp} + Created ${timestamp(new Date(Number(deconstructedSnowflake.timestamp)))} + Worker ID ${deconstructedSnowflake.workerId} + Process ID ${deconstructedSnowflake.processId} + Increment ${deconstructedSnowflake.increment}`; snowflakeEmbed.addFields({ name: '» Snowflake Info', value: snowflakeInfo }); return await message.util.reply({ embeds: [snowflakeEmbed] }); diff --git a/src/commands/info/userInfo.ts b/src/commands/info/userInfo.ts index 25621fa..22088fe 100644 --- a/src/commands/info/userInfo.ts +++ b/src/commands/info/userInfo.ts @@ -13,6 +13,7 @@ import { type OptArgType, type SlashMessage } from '#lib'; +import { embedField } from '#lib/common/tags.js'; import { ActivityType, ApplicationCommandOptionType, @@ -20,7 +21,6 @@ import { EmbedBuilder, escapeMarkdown, PermissionFlagsBits, - TeamMemberMembershipState, UserFlags, type APIApplication, type ApplicationFlagsString, @@ -128,62 +128,69 @@ export default class UserInfoCommand extends BotCommand { await this.generateBotField(userEmbed, user); - if (emojis) + if (emojis) { userEmbed.setDescription( `\u200B${emojis.filter((e) => e).join(' ')}${ userEmbed.data.description?.length ? `\n\n${userEmbed.data.description}` : '' }` ); // zero width space + } + return userEmbed; } public static async generateGeneralInfoField(embed: EmbedBuilder, user: User, title = '» General Information') { - // General Info - const generalInfo = [ - `**Mention:** <@${user.id}>`, - `**ID:** ${user.id}`, - `**Created:** ${timestampAndDelta(user.createdAt, 'd')}` - ]; - if (user.accentColor !== null) generalInfo.push(`**Accent Color:** ${user.hexAccentColor}`); - if (user.banner) generalInfo.push(`**Banner:** [link](${user.bannerURL({ extension: 'png', size: 4096 })})`); - - const pronouns = await Promise.race([user.client.utils.getPronounsOf(user), sleep(2 * Time.Second)]); // cut off request after 2 seconds - - if (pronouns && typeof pronouns === 'string' && pronouns !== 'Unspecified') generalInfo.push(`**Pronouns:** ${pronouns}`); - - embed.addFields({ name: title, value: generalInfo.join('\n') }); + const pronouns = await Promise.race([ + user.client.utils.getPronounsOf(user), + // cut off request after 2 seconds + sleep(2 * Time.Second) + ]); + + const generalInfo = embedField` + Mention ${`<@${user.id}>`} + ID ${user.id} + Created ${timestampAndDelta(user.createdAt, 'd')} + Accent Color ${user.hexAccentColor} + Banner ${user.banner && `[link](${user.bannerURL({ extension: 'png', size: 4096 })})`} + Pronouns ${typeof pronouns === 'string' && pronouns !== 'Unspecified' && pronouns}`; + + embed.addFields({ name: title, value: generalInfo }); } public static generateServerInfoField(embed: EmbedBuilder, member?: GuildMember | undefined, title = '» Server Information') { if (!member) return; - // Server User Info - const serverUserInfo = []; - if (member.joinedTimestamp) - serverUserInfo.push( - `**${member.guild!.ownerId == member.user.id ? 'Created Server' : 'Joined'}:** ${timestampAndDelta( - member.joinedAt!, - 'd' - )}` - ); - if (member.premiumSince) serverUserInfo.push(`**Booster Since:** ${timestampAndDelta(member.premiumSince, 'd')}`); - if (member.displayHexColor) serverUserInfo.push(`**Display Color:** ${member.displayHexColor}`); - if (member.user.id == mappings.users['IRONM00N'] && member.guild?.id == mappings.guilds["Moulberry's Bush"]) - serverUserInfo.push(`**General Deletions:** 1⅓`); - if ( - ([mappings.users['nopo'], mappings.users['Bestower']] as const).includes(member.user.id) && - member.guild.id == mappings.guilds["Moulberry's Bush"] - ) - serverUserInfo.push(`**General Deletions:** ⅓`); - if (member?.nickname) serverUserInfo.push(`**Nickname:** ${escapeMarkdown(member?.nickname)}`); - if (serverUserInfo.length) embed.addFields({ name: title, value: serverUserInfo.join('\n') }); + const isGuildOwner = member.guild.ownerId === member.id; + + const deletions = (() => { + if (member.guild.id !== mappings.guilds["Moulberry's Bush"]) return null; + + switch (member.id) { + case mappings.users['IRONM00N']: + return '1⅓'; + case mappings.users['nopo']: + case mappings.users['Bestower']: + return '⅓'; + default: + return null; + } + })(); + + const serverUserInfo = embedField` + Created Server ${member.joinedAt && isGuildOwner && timestampAndDelta(member.joinedAt!, 'd')} + Joined ${member.joinedAt && !isGuildOwner && timestampAndDelta(member.joinedAt!, 'd')} + Booster Since ${member.premiumSince && timestampAndDelta(member.premiumSince, 'd')} + Display Color ${member.displayHexColor} + #general Deletions ${deletions} + Nickname ${member.nickname && escapeMarkdown(member.nickname)}`; + + if (serverUserInfo.length) embed.addFields({ name: title, value: serverUserInfo }); } public static generatePresenceField(embed: EmbedBuilder, member?: GuildMember | undefined, title = '» Presence') { if (!member || !member.presence) return; if (!member.presence.status && !member.presence.clientStatus && !member.presence.activities) return; - // User Presence Info let customStatus = ''; const activitiesNames: string[] = []; if (member.presence.activities) { @@ -231,7 +238,7 @@ export default class UserInfoCommand extends BotCommand { const joined = roles.join(', '); embed.addFields({ name: `» Role${roles.length - 1 ? 's' : ''} [${roles.length}]`, - value: joined.length > 1024 ? 'Too Many Roles to Display' + '...' : joined + value: joined.length > 1024 ? 'Too Many Roles to Display...' : joined }); } @@ -242,7 +249,6 @@ export default class UserInfoCommand extends BotCommand { ) { if (!member) return; - // Important Perms const perms = this.getImportantPermissions(member); if (perms.length) embed.addFields({ name: title, value: perms.join(' ') }); @@ -282,27 +288,13 @@ export default class UserInfoCommand extends BotCommand { return emojis.cross; }; - const botInfo = [ - `**Publicity:** ${applicationInfo.bot_public ? 'Public' : 'Private'}`, - `**Requires Code Grant:** ${applicationInfo.bot_require_code_grant ? emojis.check : emojis.cross}`, - `**Server Members Intent:** ${intent('GatewayGuildMembers', 'GatewayGuildMembersLimited')}`, - `**Presence Intent:** ${intent('GatewayPresence', 'GatewayPresenceLimited')}`, - `**Message Content Intent:** ${intent('GatewayMessageContent', 'GatewayMessageContentLimited')}` - ]; - - if (applicationInfo.owner || applicationInfo.team) { - const teamMembers = applicationInfo.owner - ? [applicationInfo.owner] - : applicationInfo - .team!.members.filter((tm) => tm.membership_state === TeamMemberMembershipState.Accepted) - .map((tm) => tm.user); - botInfo.push( - `**Developer${teamMembers.length > 1 ? 's' : ''}:** ${teamMembers - .map((m) => `${m.username}#${m.discriminator}`) - .join(', ')}` - ); - } + const botInfo = embedField` + Publicity ${applicationInfo.bot_public ? 'Public' : 'Private'} + Code Grant ${applicationInfo.bot_require_code_grant ? 'Required' : 'Not Required'} + Server Members Intent ${intent('GatewayGuildMembers', 'GatewayGuildMembersLimited')} + Presence Intent ${intent('GatewayPresence', 'GatewayPresenceLimited')} + Message Content Intent ${intent('GatewayMessageContent', 'GatewayMessageContentLimited')}`; - if (botInfo.length) embed.addFields({ name: title, value: botInfo.join('\n') }); + embed.addFields({ name: title, value: botInfo }); } } diff --git a/src/commands/leveling/level.ts b/src/commands/leveling/level.ts index 869140d..bf4ca9b 100644 --- a/src/commands/leveling/level.ts +++ b/src/commands/leveling/level.ts @@ -9,11 +9,11 @@ import { type SlashMessage } from '#lib'; import canvas from '@napi-rs/canvas'; -import { SimplifyNumber } from '@notenoughupdates/simplify-number'; +import { simplifyNumber } from '@tanzanite/simplify-number'; import assert from 'assert/strict'; import { ApplicationCommandOptionType, AttachmentBuilder, Guild, PermissionFlagsBits, User } from 'discord.js'; + assert(canvas); -assert(SimplifyNumber); export default class LevelCommand extends BotCommand { public constructor() { @@ -119,9 +119,9 @@ export default class LevelCommand extends BotCommand { // Draw level data text ctx.fillStyle = white; - const xpTxt = `${SimplifyNumber(currentLevelXpProgress)}/${SimplifyNumber(xpForNextLevel)}`; + const xpTxt = `${simplifyNumber(currentLevelXpProgress)}/${simplifyNumber(xpForNextLevel)}`; - const rankTxt = SimplifyNumber(rank.indexOf(rank.find((x) => x.user === user.id)!) + 1); + const rankTxt = simplifyNumber(rank.indexOf(rank.find((x) => x.user === user.id)!) + 1); ctx.fillText(`Level: ${userLevel} XP: ${xpTxt} Rank: ${rankTxt}`, AVATAR_SIZE + 70, AVATAR_SIZE - 20); // Return image in buffer form diff --git a/src/commands/leveling/setLevel.ts b/src/commands/leveling/setLevel.ts index 6f6f69e..3a995a2 100644 --- a/src/commands/leveling/setLevel.ts +++ b/src/commands/leveling/setLevel.ts @@ -1,4 +1,5 @@ -import { AllowedMentions, BotCommand, emojis, format, Level, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { AllowedMentions, BotCommand, emojis, Level, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { commas } from '#lib/common/tags.js'; import assert from 'assert/strict'; import { ApplicationCommandOptionType } from 'discord.js'; @@ -42,20 +43,27 @@ export default class SetLevelCommand extends BotCommand { assert(message.inGuild()); assert(user.id); - if (isNaN(level) || !Number.isInteger(level)) + if (isNaN(level) || !Number.isInteger(level)) { return await message.util.reply(`${emojis.error} Provide a valid number to set the user's level to.`); - if (level > 6553 || level < 0) - return await message.util.reply(`${emojis.error} You cannot set a level higher than **6,553**.`); + } + + if (level > Level.MAX_LEVEL || level < 0) { + return await message.util.reply(commas`${emojis.error} You cannot set a level higher than **${Level.MAX_LEVEL}**.`); + } const [levelEntry] = await Level.findOrBuild({ - where: { user: user.id, guild: message.guild.id }, - defaults: { user: user.id, guild: message.guild.id, xp: 0 } + where: { + user: user.id, + guild: message.guild.id + } }); - await levelEntry.update({ xp: Level.convertLevelToXp(level), user: user.id, guild: message.guild.id }); + + const xp = Level.convertLevelToXp(level); + + await levelEntry.update({ xp, user: user.id, guild: message.guild.id }); + return await message.util.send({ - content: `Successfully set level of <@${user.id}> to ${format.input(level.toLocaleString())} (${format.input( - levelEntry.xp.toLocaleString() - )} XP)`, + content: commas`Successfully set level of <@${user.id}> to **${level}** (**${xp}** xp)`, allowedMentions: AllowedMentions.none() }); } diff --git a/src/commands/leveling/setXp.ts b/src/commands/leveling/setXp.ts index 8c3b86f..270ad68 100644 --- a/src/commands/leveling/setXp.ts +++ b/src/commands/leveling/setXp.ts @@ -1,4 +1,6 @@ -import { AllowedMentions, BotCommand, emojis, format, Level, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { AllowedMentions, BotCommand, emojis, Level, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { commas } from '#lib/common/tags.js'; +import { input } from '#lib/utils/Format.js'; import assert from 'assert/strict'; import { ApplicationCommandOptionType } from 'discord.js'; @@ -44,22 +46,36 @@ export default class SetXpCommand extends BotCommand { assert(user.id); if (isNaN(xp)) return await message.util.reply(`${emojis.error} Provide a valid number.`); - if (xp > 2147483647 || xp < 0) + + if (xp > Level.MAX_XP || xp < 0) { return await message.util.reply( - `${emojis.error} Provide an positive integer under **2,147,483,647** to set the user's xp to.` + commas`${emojis.error} Provide an positive integer under **${Level.MAX_XP}** to set the user's xp to.` ); + } const [levelEntry] = await Level.findOrBuild({ - where: { user: user.id, guild: message.guild.id }, - defaults: { user: user.id, guild: message.guild.id } + where: { + user: user.id, + guild: message.guild.id + } }); - await levelEntry.update({ xp: xp, user: user.id, guild: message.guild.id }); + const res = await levelEntry + .update({ xp: xp, user: user.id, guild: message.guild.id }) + .catch((e) => (e instanceof Error ? e : null)); + + xp = levelEntry.xp; + const level = Level.convertXpToLevel(xp); + + if (res instanceof Error || res == null) { + return await message.util.reply({ + content: commas`Unable to set <@${user.id}>'s xp to **${xp}** with error ${input(res?.message ?? '¯\\_(ツ)_/¯')}.`, + allowedMentions: AllowedMentions.none() + }); + } return await message.util.send({ - content: `Successfully set <@${user.id}>'s xp to ${format.input(levelEntry.xp.toLocaleString())} (level ${format.input( - Level.convertXpToLevel(levelEntry.xp).toLocaleString() - )}).`, + content: commas`${emojis.success} Successfully set <@${user.id}>'s xp to **${xp}** (level **${level}**).`, allowedMentions: AllowedMentions.none() }); } diff --git a/src/commands/moderation/ban.ts b/src/commands/moderation/ban.ts index aee8805..ae77cde 100644 --- a/src/commands/moderation/ban.ts +++ b/src/commands/moderation/ban.ts @@ -96,7 +96,9 @@ export default class BanCommand extends BotCommand { if (!user) return message.util.reply(`${emojis.error} Invalid user.`); const useForce = args.force && message.author.isOwner(); - const canModerateResponse = member ? await Moderation.permissionCheck(message.member, member, 'ban', true, useForce) : true; + const canModerateResponse = member + ? await Moderation.permissionCheck(message.member, member, Moderation.Action.Ban, true, useForce) + : true; if (canModerateResponse !== true) { return await message.util.reply(canModerateResponse); diff --git a/src/commands/moderation/block.ts b/src/commands/moderation/block.ts index a5ad31d..da1dec8 100644 --- a/src/commands/moderation/block.ts +++ b/src/commands/moderation/block.ts @@ -83,7 +83,7 @@ export default class BlockCommand extends BotCommand { return await message.util.reply(`${emojis.error} The user you selected is not in the server or is not a valid user.`); const useForce = args.force && message.author.isOwner(); - const canModerateResponse = await Moderation.permissionCheck(message.member, member, 'block', true, useForce); + const canModerateResponse = await Moderation.permissionCheck(message.member, member, Moderation.Action.Block, true, useForce); if (canModerateResponse !== true) { return message.util.reply(canModerateResponse); diff --git a/src/commands/moderation/evidence.ts b/src/commands/moderation/evidence.ts index 9a5e70f..b7c020a 100644 --- a/src/commands/moderation/evidence.ts +++ b/src/commands/moderation/evidence.ts @@ -10,8 +10,8 @@ import { type CommandMessage, type SlashMessage } from '#lib'; +import { Argument, ArgumentGeneratorReturn } from '@notenoughupdates/discord-akairo'; import assert from 'assert/strict'; -import { Argument, ArgumentGeneratorReturn } from 'discord-akairo'; import { ApplicationCommandOptionType, type Message } from 'discord.js'; export default class EvidenceCommand extends BotCommand { diff --git a/src/commands/moderation/kick.ts b/src/commands/moderation/kick.ts index 82ddce4..d757e91 100644 --- a/src/commands/moderation/kick.ts +++ b/src/commands/moderation/kick.ts @@ -70,7 +70,7 @@ export default class KickCommand extends BotCommand { if (!member) return await message.util.reply(`${emojis.error} The user you selected is not in the server or is not a valid user.`); const useForce = force && message.author.isOwner(); - const canModerateResponse = await Moderation.permissionCheck(message.member, member, 'kick', true, useForce); + const canModerateResponse = await Moderation.permissionCheck(message.member, member, Moderation.Action.Kick, true, useForce); if (canModerateResponse !== true) { return message.util.reply(canModerateResponse); diff --git a/src/commands/moderation/modlog.ts b/src/commands/moderation/modlog.ts index dcab9ef..649e44f 100644 --- a/src/commands/moderation/modlog.ts +++ b/src/commands/moderation/modlog.ts @@ -12,12 +12,11 @@ import { type CommandMessage, type SlashMessage } from '#lib'; +import { embedField } from '#lib/common/tags.js'; import assert from 'assert/strict'; import { ApplicationCommandOptionType, escapeMarkdown, User } from 'discord.js'; export default class ModlogCommand extends BotCommand { - public static separator = '\n━━━━━━━━━━━━━━━\n'; - public constructor() { super('modlog', { aliases: ['modlog', 'modlogs'], @@ -62,44 +61,63 @@ export default class ModlogCommand extends BotCommand { const logs = await ModLog.findAll({ where: { guild: message.guild.id, - user: foundUser.id + user: foundUser.id, + pseudo: false }, order: [['createdAt', 'ASC']] }); - const niceLogs = logs - .filter((log) => !log.pseudo && !(!hidden && log.hidden)) - .map((log) => ModlogCommand.generateModlogInfo(log, false, false)); - if (niceLogs.length < 1) return message.util.reply(`${emojis.error} **${foundUser.tag}** does not have any modlogs.`); + const niceLogs = logs.filter((log) => !log.hidden || hidden).map((log) => generateModlogInfo(log, false, false)); + + if (niceLogs.length < 1) { + return message.util.reply(`${emojis.error} **${foundUser.tag}** does not have any modlogs.`); + } + const chunked: string[][] = chunk(niceLogs, 4); const embedPages = chunked.map((chunk) => ({ title: `${foundUser.tag}'s Modlogs`, - description: chunk.join(ModlogCommand.separator), + description: chunk.join(modlogSeparator), color: colors.default })); return await ButtonPaginator.send(message, embedPages, undefined, true); } else if (search) { const entry = await ModLog.findByPk(search as string); - if (!entry || entry.pseudo || (entry.hidden && !hidden)) + + if (!entry || entry.pseudo || (entry.hidden && !hidden)) { return message.util.send(`${emojis.error} That modlog does not exist.`); - if (entry.guild !== message.guild.id) return message.util.reply(`${emojis.error} This modlog is from another server.`); + } + + if (entry.guild !== message.guild.id) { + return message.util.reply(`${emojis.error} This modlog is from another server.`); + } + const embed = { title: `Case ${entry.id}`, - description: ModlogCommand.generateModlogInfo(entry, true, false), + description: generateModlogInfo(entry, true, false), color: colors.default }; return await ButtonPaginator.send(message, [embed]); } } +} - public static generateModlogInfo(log: ModLog, showUser: boolean, userFacing: boolean): string { - const trim = (str: string): string => (str.endsWith('\n') ? str.substring(0, str.length - 1).trim() : str.trim()); - const modLog = [`**Case ID:** ${escapeMarkdown(log.id)}`, `**Type:** ${log.type.toLowerCase()}`]; - if (showUser) modLog.push(`**User:** <@!${log.user}>`); - if (!userFacing) modLog.push(`**Moderator:** <@!${log.moderator}>`); - if (log.duration) modLog.push(`**Duration:** ${humanizeDuration(log.duration)}`); - modLog.push(`**Reason:** ${trim(log.reason ?? 'No Reason Specified.')}`); - modLog.push(`**Date:** ${timestamp(log.createdAt)}`); - if (log.evidence && !userFacing) modLog.push(`**Evidence:** ${trim(log.evidence)}`); - return modLog.join(`\n`); +export const modlogSeparator = '\n━━━━━━━━━━━━━━━\n'; + +const trim = (str: string): string => { + if (str.endsWith('\n')) { + return str.substring(0, str.length - 1).trim(); + } else { + return str.trim(); } +}; + +export function generateModlogInfo(log: ModLog, showUser: boolean, userFacing: boolean): string { + return embedField` + Case ID ${escapeMarkdown(log.id)} + Type ${log.type.toLowerCase()} + User ${showUser && `<@!${log.user}>`} + Moderator ${!userFacing && `<@!${log.moderator}>`} + Duration ${log.duration && humanizeDuration(log.duration)} + Reason ${trim(log.reason ?? 'No Reason Specified.')} + Date ${timestamp(log.createdAt)} + Evidence ${log.evidence && !userFacing && trim(log.evidence)}`; } diff --git a/src/commands/moderation/mute.ts b/src/commands/moderation/mute.ts index 9ffaf8d..a64dc99 100644 --- a/src/commands/moderation/mute.ts +++ b/src/commands/moderation/mute.ts @@ -78,7 +78,7 @@ export default class MuteCommand extends BotCommand { return await message.util.reply(`${emojis.error} The user you selected is not in the server or is not a valid user.`); const useForce = args.force && message.author.isOwner(); - const canModerateResponse = await Moderation.permissionCheck(message.member, member, 'mute', true, useForce); + const canModerateResponse = await Moderation.permissionCheck(message.member, member, Moderation.Action.Mute, true, useForce); if (canModerateResponse !== true) { return message.util.reply(canModerateResponse); diff --git a/src/commands/moderation/myLogs.ts b/src/commands/moderation/myLogs.ts index 8faca8c..e3f5f10 100644 --- a/src/commands/moderation/myLogs.ts +++ b/src/commands/moderation/myLogs.ts @@ -12,7 +12,7 @@ import { import { ApplicationCommandOptionType } from 'discord.js'; import { input, sanitizeInputForDiscord } from '../../../lib/utils/Format.js'; -import ModlogCommand from './modlog.js'; +import { generateModlogInfo, modlogSeparator } from './modlog.js'; export default class MyLogsCommand extends BotCommand { public constructor() { super('myLogs', { @@ -50,14 +50,14 @@ export default class MyLogsCommand extends BotCommand { const logs = await ModLog.findAll({ where: { guild: guild.id, - user: message.author.id + user: message.author.id, + pseudo: false, + hidden: false }, order: [['createdAt', 'ASC']] }); - const niceLogs = logs - .filter((log) => !log.pseudo && !log.hidden) - .map((log) => ModlogCommand.generateModlogInfo(log, false, true)); + const niceLogs = logs.map((log) => generateModlogInfo(log, false, true)); if (niceLogs.length < 1) return message.util.reply(`${emojis.error} You don't have any modlogs in ${input(guild.name)}.`); @@ -65,7 +65,7 @@ export default class MyLogsCommand extends BotCommand { const embedPages = chunked.map((chunk) => ({ title: `Your Modlogs in ${sanitizeInputForDiscord(guild.name)}`, - description: chunk.join(ModlogCommand.separator), + description: chunk.join(modlogSeparator), color: colors.default })); diff --git a/src/commands/moderation/role.ts b/src/commands/moderation/role.ts index 565f214..a664aa4 100644 --- a/src/commands/moderation/role.ts +++ b/src/commands/moderation/role.ts @@ -12,8 +12,8 @@ import { type OptArgType, type SlashMessage } from '#lib'; +import { type ArgumentGeneratorReturn } from '@notenoughupdates/discord-akairo'; import assert from 'assert/strict'; -import { type ArgumentGeneratorReturn } from 'discord-akairo'; import { ApplicationCommandOptionType, PermissionFlagsBits, type Snowflake } from 'discord.js'; export default class RoleCommand extends BotCommand { diff --git a/src/commands/moderation/slowmode.ts b/src/commands/moderation/slowmode.ts index 82d0264..1256d1f 100644 --- a/src/commands/moderation/slowmode.ts +++ b/src/commands/moderation/slowmode.ts @@ -1,6 +1,6 @@ import { Arg, BotCommand, emojis, format, humanizeDuration, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; +import { Argument } from '@notenoughupdates/discord-akairo'; import assert from 'assert/strict'; -import { Argument } from 'discord-akairo'; import { ApplicationCommandOptionType, ChannelType } from 'discord.js'; export default class SlowmodeCommand extends BotCommand { @@ -30,7 +30,7 @@ export default class SlowmodeCommand extends BotCommand { retry: '{error} Choose a valid channel.', optional: true, slashType: ApplicationCommandOptionType.Channel, - channelTypes: [ChannelType.GuildText, ChannelType.GuildPrivateThread, ChannelType.GuildPublicThread] + channelTypes: [ChannelType.GuildText, ChannelType.PrivateThread, ChannelType.PublicThread] } ], slash: true, @@ -55,7 +55,7 @@ export default class SlowmodeCommand extends BotCommand { if ( args.channel.type !== ChannelType.GuildText && - args.channel.type !== ChannelType.GuildNews && + args.channel.type !== ChannelType.GuildAnnouncement && args.channel.type !== ChannelType.GuildVoice && !args.channel.isThread() ) diff --git a/src/commands/moderation/timeout.ts b/src/commands/moderation/timeout.ts index 7bb02f7..db6ab56 100644 --- a/src/commands/moderation/timeout.ts +++ b/src/commands/moderation/timeout.ts @@ -73,7 +73,13 @@ export default class TimeoutCommand extends BotCommand { return await message.util.reply(`${emojis.error} The user you selected is not in the server or is not a valid user.`); const useForce = args.force && message.author.isOwner(); - const canModerateResponse = await Moderation.permissionCheck(message.member, member, 'timeout', true, useForce); + const canModerateResponse = await Moderation.permissionCheck( + message.member, + member, + Moderation.Action.Timeout, + true, + useForce + ); if (canModerateResponse !== true) { return message.util.reply(canModerateResponse); diff --git a/src/commands/moderation/unblock.ts b/src/commands/moderation/unblock.ts index 4838392..4fdfc28 100644 --- a/src/commands/moderation/unblock.ts +++ b/src/commands/moderation/unblock.ts @@ -75,7 +75,13 @@ export default class UnblockCommand extends BotCommand { return await message.util.reply(`${emojis.error} The user you selected is not in the server or is not a valid user.`); const useForce = args.force && message.author.isOwner(); - const canModerateResponse = await Moderation.permissionCheck(message.member, member, 'unblock', true, useForce); + const canModerateResponse = await Moderation.permissionCheck( + message.member, + member, + Moderation.Action.Unblock, + true, + useForce + ); if (canModerateResponse !== true) { return message.util.reply(canModerateResponse); diff --git a/src/commands/moderation/unmute.ts b/src/commands/moderation/unmute.ts index a4d348d..c8fccc8 100644 --- a/src/commands/moderation/unmute.ts +++ b/src/commands/moderation/unmute.ts @@ -66,7 +66,13 @@ export default class UnmuteCommand extends BotCommand { const member = message.guild.members.cache.get(user.id)!; const useForce = force && message.author.isOwner(); - const canModerateResponse = await Moderation.permissionCheck(message.member, member, 'unmute', true, useForce); + const canModerateResponse = await Moderation.permissionCheck( + message.member, + member, + Moderation.Action.Unmute, + true, + useForce + ); if (canModerateResponse !== true) { return message.util.reply(canModerateResponse); diff --git a/src/commands/moderation/untimeout.ts b/src/commands/moderation/untimeout.ts index 3775c65..64364e5 100644 --- a/src/commands/moderation/untimeout.ts +++ b/src/commands/moderation/untimeout.ts @@ -73,7 +73,13 @@ export default class UntimeoutCommand extends BotCommand { if (!member.isCommunicationDisabled()) return message.util.reply(`${emojis.error} That user is not timed out.`); const useForce = args.force && message.author.isOwner(); - const canModerateResponse = await Moderation.permissionCheck(message.member, member, 'timeout', true, useForce); + const canModerateResponse = await Moderation.permissionCheck( + message.member, + member, + Moderation.Action.Untimeout, + true, + useForce + ); if (canModerateResponse !== true) { return message.util.reply(canModerateResponse); diff --git a/src/commands/moderation/warn.ts b/src/commands/moderation/warn.ts index 4bc7f13..a7ed814 100644 --- a/src/commands/moderation/warn.ts +++ b/src/commands/moderation/warn.ts @@ -69,7 +69,7 @@ export default class WarnCommand extends BotCommand { const member = message.guild.members.cache.get(user.id); if (!member) return message.util.reply(`${emojis.error} I cannot warn users that are not in the server.`); const useForce = force && message.author.isOwner(); - const canModerateResponse = await Moderation.permissionCheck(message.member, member, 'warn', true, useForce); + const canModerateResponse = await Moderation.permissionCheck(message.member, member, Moderation.Action.Warn, true, useForce); if (canModerateResponse !== true) { return message.util.reply(canModerateResponse); diff --git a/src/commands/moulberry-bush/capes.ts b/src/commands/moulberry-bush/capes.ts index b292f24..a67cf46 100644 --- a/src/commands/moulberry-bush/capes.ts +++ b/src/commands/moulberry-bush/capes.ts @@ -13,7 +13,9 @@ import { } from '#lib'; import assert from 'assert/strict'; import { ApplicationCommandOptionType, type APIEmbed, type AutocompleteInteraction } from 'discord.js'; -import { default as Fuse } from 'fuse.js'; + +// todo: remove this bullshit once typescript gets its shit together +const Fuse = (await import('fuse.js')).default as unknown as typeof import('fuse.js').default; assert(Fuse); @@ -85,7 +87,7 @@ export default class CapesCommand extends BotCommand { } } else { const embeds: APIEmbed[] = capes.map(this.makeEmbed); - await ButtonPaginator.send(message, embeds, null); + await ButtonPaginator.send(message, embeds, ''); } } diff --git a/src/commands/tickets/ticket-!.ts b/src/commands/tickets/ticket-!.ts index c5c59f2..b98ec1f 100644 --- a/src/commands/tickets/ticket-!.ts +++ b/src/commands/tickets/ticket-!.ts @@ -1,5 +1,5 @@ import { BotCommand, deepWriteable, type SlashMessage } from '#lib'; -import { Flag, type ArgumentGeneratorReturn, type SlashOption } from 'discord-akairo'; +import { Flag, type ArgumentGeneratorReturn, type SlashOption } from '@notenoughupdates/discord-akairo'; import { ApplicationCommandOptionType } from 'discord.js'; export const ticketSubcommands = deepWriteable({ diff --git a/src/commands/utilities/activity.ts b/src/commands/utilities/activity.ts index 89ca53e..3f17d0a 100644 --- a/src/commands/utilities/activity.ts +++ b/src/commands/utilities/activity.ts @@ -7,7 +7,7 @@ import { type CommandMessage, type SlashMessage } from '#lib'; -import { type ArgumentGeneratorReturn, type ArgumentTypeCaster } from 'discord-akairo'; +import { type ArgumentGeneratorReturn, type ArgumentTypeCaster } from '@notenoughupdates/discord-akairo'; import { ApplicationCommandOptionType, ChannelType, type DiscordAPIError, type Snowflake } from 'discord.js'; const activityMap = { diff --git a/src/commands/utilities/highlight-!.ts b/src/commands/utilities/highlight-!.ts index 7716887..0e2db33 100644 --- a/src/commands/utilities/highlight-!.ts +++ b/src/commands/utilities/highlight-!.ts @@ -1,5 +1,5 @@ import { BotCommand, deepWriteable, Highlight, HighlightWord, type SlashMessage } from '#lib'; -import { Flag, type ArgumentGeneratorReturn, type SlashOption } from 'discord-akairo'; +import { Flag, type ArgumentGeneratorReturn, type SlashOption } from '@notenoughupdates/discord-akairo'; import { ApplicationCommandOptionType, Constants, type AutocompleteInteraction, type CacheType } from 'discord.js'; export const highlightSubcommands = deepWriteable({ diff --git a/src/commands/utilities/highlight-block.ts b/src/commands/utilities/highlight-block.ts index b16852e..d1dec1e 100644 --- a/src/commands/utilities/highlight-block.ts +++ b/src/commands/utilities/highlight-block.ts @@ -1,6 +1,6 @@ import { AllowedMentions, BotCommand, emojis, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { Argument, ArgumentGeneratorReturn } from '@notenoughupdates/discord-akairo'; import assert from 'assert/strict'; -import { Argument, ArgumentGeneratorReturn } from 'discord-akairo'; import { BaseChannel, GuildMember, User } from 'discord.js'; import { HighlightBlockResult } from '../../../lib/common/HighlightManager.js'; import { highlightSubcommands } from './highlight-!.js'; diff --git a/src/commands/utilities/highlight-matches.ts b/src/commands/utilities/highlight-matches.ts index d54fd4a..0665b37 100644 --- a/src/commands/utilities/highlight-matches.ts +++ b/src/commands/utilities/highlight-matches.ts @@ -1,6 +1,6 @@ import { BotCommand, ButtonPaginator, chunk, colors, emojis, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { type ArgumentGeneratorReturn } from '@notenoughupdates/discord-akairo'; import assert from 'assert/strict'; -import { type ArgumentGeneratorReturn } from 'discord-akairo'; import { type APIEmbed } from 'discord.js'; import { highlightSubcommands } from './highlight-!.js'; diff --git a/src/commands/utilities/highlight-unblock.ts b/src/commands/utilities/highlight-unblock.ts index 0f2dd78..f9cc806 100644 --- a/src/commands/utilities/highlight-unblock.ts +++ b/src/commands/utilities/highlight-unblock.ts @@ -1,6 +1,6 @@ import { AllowedMentions, BotCommand, emojis, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { Argument, ArgumentGeneratorReturn } from '@notenoughupdates/discord-akairo'; import assert from 'assert'; -import { Argument, ArgumentGeneratorReturn } from 'discord-akairo'; import { BaseChannel, GuildMember, User } from 'discord.js'; import { HighlightUnblockResult } from '../../../lib/common/HighlightManager.js'; import { highlightSubcommands } from './highlight-!.js'; diff --git a/src/commands/utilities/price.ts b/src/commands/utilities/price.ts index 6f08aaa..0d5d4b3 100644 --- a/src/commands/utilities/price.ts +++ b/src/commands/utilities/price.ts @@ -1,7 +1,9 @@ import { ArgType, BotCommand, colors, emojis, format, formatList, type CommandMessage } from '#lib'; import assert from 'assert/strict'; import { ApplicationCommandOptionType, AutocompleteInteraction, EmbedBuilder } from 'discord.js'; -import { default as Fuse } from 'fuse.js'; + +// todo: remove this bullshit once typescript gets its shit together +const Fuse = (await import('fuse.js')).default as unknown as typeof import('fuse.js').default; assert(Fuse); diff --git a/src/commands/utilities/steal.ts b/src/commands/utilities/steal.ts index a208920..bd2976a 100644 --- a/src/commands/utilities/steal.ts +++ b/src/commands/utilities/steal.ts @@ -1,13 +1,11 @@ import { Arg, BotCommand, emojis, format, OptArgType, regex, type CommandMessage, type SlashMessage } from '#lib'; +import { type ArgumentGeneratorReturn, type ArgumentType, type ArgumentTypeCaster } from '@notenoughupdates/discord-akairo'; import assert from 'assert/strict'; -import { type ArgumentGeneratorReturn, type ArgumentType, type ArgumentTypeCaster } from 'discord-akairo'; import { ApplicationCommandOptionType, Attachment } from 'discord.js'; -import _ from 'lodash'; +import { snakeCase } from 'lodash-es'; import { Stream } from 'stream'; import { URL } from 'url'; -assert(_); - // so I don't have to retype things const enum lang { emojiStart = 'What emoji would you like to steal?', @@ -53,7 +51,7 @@ export default class StealCommand extends BotCommand { const name = yield { prompt: { start: lang.nameStart, retry: lang.nameRetry, optional: true }, - default: hasImage && message.attachments.first()!.name ? _.snakeCase(message.attachments.first()!.name!) : 'unnamed_emoji' + default: hasImage && message.attachments.first()!.name ? snakeCase(message.attachments.first()!.name!) : 'unnamed_emoji' }; return { emoji, name }; diff --git a/src/commands/utilities/whoHasRole.ts b/src/commands/utilities/whoHasRole.ts index c01a0c3..31e413d 100644 --- a/src/commands/utilities/whoHasRole.ts +++ b/src/commands/utilities/whoHasRole.ts @@ -77,7 +77,7 @@ export default class WhoHasRoleCommand extends BotCommand { return await message.util.reply(`${emojis.error} No members found matching the given roles.`); } - return await ButtonPaginator.send(message, embedPages, null, true); + return await ButtonPaginator.send(message, embedPages, '', true); } } diff --git a/src/commands/utilities/wolframAlpha.ts b/src/commands/utilities/wolframAlpha.ts index 503af87..48739cf 100644 --- a/src/commands/utilities/wolframAlpha.ts +++ b/src/commands/utilities/wolframAlpha.ts @@ -1,7 +1,7 @@ import { AllowedMentions, BotCommand, colors, emojis, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; -import { initializeClass as WolframAlphaAPI } from '@notenoughupdates/wolfram-alpha-api'; +import { initializeClass as WolframAlphaAPI } from '@tanzanite/wolfram-alpha'; import assert from 'assert/strict'; -import { ApplicationCommandOptionType, EmbedBuilder, type MessageOptions } from 'discord.js'; +import { ApplicationCommandOptionType, EmbedBuilder, type MessageCreateOptions } from 'discord.js'; assert(WolframAlphaAPI); @@ -62,7 +62,7 @@ export default class WolframAlphaCommand extends BotCommand { name: '📥 Input', value: await this.client.utils.inspectCleanRedactCodeblock(args.expression) }); - const sendOptions: MessageOptions = { content: null, allowedMentions: AllowedMentions.none() }; + const sendOptions: MessageCreateOptions = { content: '', allowedMentions: AllowedMentions.none() }; try { const calculated = await (args.image ? waApi.getSimple({ i: args.expression, timeout: 1, background: '2C2F33', foreground: 'white' }) diff --git a/src/context-menu-commands/message/viewRaw.ts b/src/context-menu-commands/message/viewRaw.ts index 0a8fcfc..08a421d 100644 --- a/src/context-menu-commands/message/viewRaw.ts +++ b/src/context-menu-commands/message/viewRaw.ts @@ -1,4 +1,4 @@ -import { ContextMenuCommand } from 'discord-akairo'; +import { ContextMenuCommand } from '@notenoughupdates/discord-akairo'; import { ApplicationCommandType, type ContextMenuCommandInteraction, type Message } from 'discord.js'; import { getRawData } from '../../commands/utilities/viewRaw.js'; diff --git a/src/context-menu-commands/user/modlog.ts b/src/context-menu-commands/user/modlog.ts index c78396e..b68a7e9 100644 --- a/src/context-menu-commands/user/modlog.ts +++ b/src/context-menu-commands/user/modlog.ts @@ -1,6 +1,6 @@ import { ModlogCommand } from '#commands'; import { emojis, SlashMessage } from '#lib'; -import { CommandUtil, ContextMenuCommand } from 'discord-akairo'; +import { CommandUtil, ContextMenuCommand } from '@notenoughupdates/discord-akairo'; import { ApplicationCommandType, type ContextMenuCommandInteraction } from 'discord.js'; export default class ModlogContextMenuCommand extends ContextMenuCommand { @@ -8,7 +8,8 @@ export default class ModlogContextMenuCommand extends ContextMenuCommand { super('modlog', { name: "Users's Modlogs", type: ApplicationCommandType.User, - category: 'user' + category: 'user', + dmPermission: false }); } @@ -28,6 +29,8 @@ export default class ModlogContextMenuCommand extends ContextMenuCommand { const pseudoMessage = new SlashMessage(this.client, interaction as any); pseudoMessage.util = new CommandUtil(this.client.commandHandler, pseudoMessage); - void new ModlogCommand().exec(pseudoMessage, { search: interaction.targetId, hidden: false }); + const command = this.client.commandHandler.modules.get('modlog') as ModlogCommand; + + void command.exec(pseudoMessage, { search: interaction.targetId, hidden: false }); } } diff --git a/src/context-menu-commands/user/userInfo.ts b/src/context-menu-commands/user/userInfo.ts index 6d7f3b6..283e4a0 100644 --- a/src/context-menu-commands/user/userInfo.ts +++ b/src/context-menu-commands/user/userInfo.ts @@ -1,27 +1,35 @@ import { UserInfoCommand } from '#commands'; -import { format } from '#lib'; -import { ContextMenuCommand } from 'discord-akairo'; -import { ApplicationCommandType, type ContextMenuCommandInteraction, type Guild } from 'discord.js'; +import { emojis } from '#lib'; +import { ContextMenuCommand } from '@notenoughupdates/discord-akairo'; +import assert from 'assert'; +import { ApplicationCommandType, GuildMember, UserContextMenuCommandInteraction } from 'discord.js'; export default class UserInfoContextMenuCommand extends ContextMenuCommand { public constructor() { super('userInfo', { name: 'User Info', type: ApplicationCommandType.User, - category: 'user' + category: 'user', + dmPermission: false }); } - public override async exec(interaction: ContextMenuCommandInteraction) { + public override async exec(interaction: UserContextMenuCommandInteraction) { + if (!interaction.inCachedGuild()) + return interaction.reply({ + content: `${emojis.error} You can't use this command outside of a server.`, + ephemeral: true + }); + await interaction.deferReply({ ephemeral: true }); - const user = await this.client.users.fetch(interaction.targetId).catch(() => null); - if (!user) return interaction.reply(`⁉ I couldn't find that user`); + const user = interaction.targetUser; + + const guild = interaction.guild ?? undefined; - const guild = interaction.guild as Guild; + const member = interaction.targetMember ?? undefined; - const member = await guild.members.fetch(interaction.targetId).catch(() => null); - if (!member) return interaction.reply(`${format.input(user.tag)} doesn't appear to be a member of this server anymore.`); + assert(member instanceof GuildMember || member === undefined); const userEmbed = await UserInfoCommand.makeUserInfoEmbed(user, member, guild); diff --git a/src/listeners/bush/appealListener.ts b/src/listeners/bush/appealListener.ts index 46859d1..99f1505 100644 --- a/src/listeners/bush/appealListener.ts +++ b/src/listeners/bush/appealListener.ts @@ -2,7 +2,7 @@ import { BotListener, colors, Emitter, mappings, ModLog, type BotClientEvents } import assert from 'assert/strict'; import { EmbedBuilder, Events } from 'discord.js'; import UserInfoCommand from '../../commands/info/userInfo.js'; -import ModlogCommand from '../../commands/moderation/modlog.js'; +import { generateModlogInfo, modlogSeparator } from '../../commands/moderation/modlog.js'; export default class AppealListener extends BotListener { public constructor() { @@ -42,7 +42,9 @@ export default class AppealListener extends BotListener { await ModLog.findAll({ where: { user: user.id, - guild: message.guildId + guild: message.guildId, + pseudo: false, + hidden: false }, order: [['createdAt', 'DESC']] }) @@ -60,15 +62,19 @@ export default class AppealListener extends BotListener { member: { if (!message.guild.members.cache.has(user.id)) break member; + const member = message.guild.members.cache.get(user.id)!; + UserInfoCommand.generateServerInfoField(embed, member); - if (member.roles.cache.size > 1) UserInfoCommand.generateRolesField(embed, member); + if (member.roles.cache.size > 1) { + UserInfoCommand.generateRolesField(embed, member); + } } embed.addFields({ name: '» Latest Modlogs', value: latestModlogs.length - ? latestModlogs.map((ml) => ModlogCommand.generateModlogInfo(ml, false, false)).join(ModlogCommand.separator) + ? latestModlogs.map((ml) => generateModlogInfo(ml, false, false)).join(modlogSeparator) : 'No Modlogs Found' }); diff --git a/src/listeners/bush/experimentYoink.ts b/src/listeners/bush/experimentYoink.ts new file mode 100644 index 0000000..5b7e526 --- /dev/null +++ b/src/listeners/bush/experimentYoink.ts @@ -0,0 +1,28 @@ +import { BotClientEvents, BotListener, Emitter, mappings } from '#lib'; +import { Events, Routes } from 'discord.js'; + +export default class ExperimentYoink extends BotListener { + public constructor() { + super('experimentYoink', { + emitter: Emitter.Client, + event: Events.MessageCreate + }); + } + + public async exec(...[message]: BotClientEvents[Events.MessageCreate]): Promise<any> { + if (message.channelId !== '1019830755658055691') return; + if (message.embeds.length < 1) return; + if (!message.embeds[0].title?.includes('Guild Experiment')) return; + + const guild = this.client.guilds.cache.get(mappings.guilds["Moulberry's Bush"]); + + if (guild == null) return; + + return await this.client.rest.post(Routes.channelMessages('795356494261911553'), { + body: { + content: message.content, + embeds: message.embeds.map((embed) => embed.toJSON()) + } + }); + } +} diff --git a/src/listeners/client/ready.ts b/src/listeners/client/ready.ts index b74c132..e19c4eb 100644 --- a/src/listeners/client/ready.ts +++ b/src/listeners/client/ready.ts @@ -1,6 +1,9 @@ import { BotClientEvents, BotListener, Emitter, Guild } from '#lib'; +import { commas } from '#lib/common/tags.js'; +import { humanizeDuration } from '@notenoughupdates/humanize-duration'; import chalk from 'chalk'; import { Events } from 'discord.js'; +import { performance } from 'perf_hooks'; export default class ReadyListener extends BotListener { public constructor() { @@ -12,19 +15,28 @@ export default class ReadyListener extends BotListener { // eslint-disable-next-line no-empty-pattern public async exec(...[]: BotClientEvents[Events.ClientReady]) { + performance.mark('clientReady'); + process.emit('ready' as any); const tag = `<<${this.client.user?.tag}>>`, - guildCount = `<<${this.client.guilds.cache.size.toLocaleString()}>>`, - userCount = `<<${this.client.users.cache.size.toLocaleString()}>>`; + guildCount = commas`<<${this.client.guilds.cache.size}>>`, + userCount = commas`<<${this.client.users.cache.size}>>`; void this.client.logger.success('ready', `Logged in to ${tag} serving ${guildCount} guilds and ${userCount} users.`); - console.log( - chalk.blue( - `------------------------------------------------------------------------------${ - this.client.config.isDevelopment ? '---' : this.client.config.isBeta ? '----' : '' - }` - ) + + console.log(chalk.blue('-'.repeat(84 + (this.client.config.isDevelopment ? 3 : this.client.config.isBeta ? 4 : 0)))); + + const measure = performance.measure('start', 'processStart', 'clientReady'); + + void this.client.logger.info( + 'ready', + `Took <<${humanizeDuration(measure.duration, { + language: 'en', + largest: 3, + round: false, + maxDecimalPoints: 3 + })}>> to start.` ); const guilds = await Guild.findAll(); diff --git a/src/listeners/commands/commandBlocked.ts b/src/listeners/commands/commandBlocked.ts index c81857c..7795241 100644 --- a/src/listeners/commands/commandBlocked.ts +++ b/src/listeners/commands/commandBlocked.ts @@ -9,7 +9,7 @@ import { type BotCommandHandlerEvents, type CommandMessage } from '#lib'; -import { type Client, type InteractionReplyOptions, type ReplyMessageOptions } from 'discord.js'; +import { type Client, type InteractionReplyOptions, type MessageReplyOptions } from 'discord.js'; export default class CommandBlockedListener extends BotListener { public constructor() { @@ -124,7 +124,7 @@ export default class CommandBlockedListener extends BotListener { } // some inhibitors do not have message.util yet - function respond(content: string | (ReplyMessageOptions & InteractionReplyOptions)) { + function respond(content: string | (MessageReplyOptions & InteractionReplyOptions)) { return message.util ? message.util.reply(content) : message.reply(content); } } diff --git a/src/listeners/contextCommands/contextCommandBlocked.ts b/src/listeners/contextCommands/contextCommandBlocked.ts index 93b53c7..80c7a34 100644 --- a/src/listeners/contextCommands/contextCommandBlocked.ts +++ b/src/listeners/contextCommands/contextCommandBlocked.ts @@ -1,6 +1,6 @@ import { BotListener, ContextCommandHandlerEvent, Emitter, emojis, format } from '#lib'; -import { type ContextMenuCommandHandlerEvents } from 'discord-akairo'; -import { BuiltInReasons } from 'discord-akairo/dist/src/util/Constants.js'; +import { type ContextMenuCommandHandlerEvents } from '@notenoughupdates/discord-akairo'; +import { BuiltInReasons } from '@notenoughupdates/discord-akairo/dist/src/util/Constants.js'; export default class ContextCommandBlockedListener extends BotListener { public constructor() { diff --git a/src/listeners/contextCommands/contextCommandError.ts b/src/listeners/contextCommands/contextCommandError.ts index 24e5cef..5043bae 100644 --- a/src/listeners/contextCommands/contextCommandError.ts +++ b/src/listeners/contextCommands/contextCommandError.ts @@ -9,7 +9,7 @@ import { getErrorStack, IFuckedUpError } from '#lib'; -import { type ContextMenuCommand, type ContextMenuCommandHandlerEvents } from 'discord-akairo'; +import { type ContextMenuCommand, type ContextMenuCommandHandlerEvents } from '@notenoughupdates/discord-akairo'; import { ChannelType, Client, ContextMenuCommandInteraction, EmbedBuilder, GuildTextBasedChannel } from 'discord.js'; export default class ContextCommandErrorListener extends BotListener { diff --git a/src/listeners/contextCommands/contextCommandNotFound.ts b/src/listeners/contextCommands/contextCommandNotFound.ts index da364ed..e7538ee 100644 --- a/src/listeners/contextCommands/contextCommandNotFound.ts +++ b/src/listeners/contextCommands/contextCommandNotFound.ts @@ -1,5 +1,5 @@ import { BotListener, ContextCommandHandlerEvent, Emitter } from '#lib'; -import { type ContextMenuCommandHandlerEvents } from 'discord-akairo'; +import { type ContextMenuCommandHandlerEvents } from '@notenoughupdates/discord-akairo'; export default class ContextCommandNotFoundListener extends BotListener { public constructor() { diff --git a/src/listeners/contextCommands/contextCommandStarted.ts b/src/listeners/contextCommands/contextCommandStarted.ts index bf7cc58..a820d3e 100644 --- a/src/listeners/contextCommands/contextCommandStarted.ts +++ b/src/listeners/contextCommands/contextCommandStarted.ts @@ -1,5 +1,5 @@ import { BotListener, ContextCommandHandlerEvent, Emitter } from '#lib'; -import { ContextMenuCommandHandlerEvents } from 'discord-akairo'; +import { ContextMenuCommandHandlerEvents } from '@notenoughupdates/discord-akairo'; import { ApplicationCommandType, ChannelType } from 'discord.js'; export default class ContextCommandStartedListener extends BotListener { diff --git a/src/listeners/guild/syncUnbanPunishmentModel.ts b/src/listeners/guild/syncUnbanPunishmentModel.ts index eac3aa2..c6de768 100644 --- a/src/listeners/guild/syncUnbanPunishmentModel.ts +++ b/src/listeners/guild/syncUnbanPunishmentModel.ts @@ -13,7 +13,7 @@ export default class SyncUnbanListener extends BotListener { where: { user: ban.user.id, guild: ban.guild.id, - type: ActivePunishmentType.BAN + type: ActivePunishmentType.Ban } }); for (const dbBan of bans) { diff --git a/src/listeners/interaction/$interactionCreate.ts b/src/listeners/interaction/$interactionCreate.ts new file mode 100644 index 0000000..86aa5e2 --- /dev/null +++ b/src/listeners/interaction/$interactionCreate.ts @@ -0,0 +1,31 @@ +import { BotListener, Emitter, TanzaniteEvent, type BotClientEvents } from '#lib'; +import { Events, InteractionType } from 'discord.js'; + +export default class InteractionCreateListener extends BotListener { + public constructor() { + super('interactionCreate', { + emitter: Emitter.Client, + event: Events.InteractionCreate + }); + } + + public async exec(...[interaction]: BotClientEvents[Events.InteractionCreate]) { + if (!interaction) return; + + void this.client.console.verbose( + 'interactionVerbose', + `An interaction of type <<${InteractionType[interaction.type]}>> was received from <<${interaction.user.tag}>>.` + ); + + if (interaction.isCommand() || interaction.isAutocomplete()) { + // handled by the command handler + return; + } else if (interaction.isButton()) { + this.client.emit(TanzaniteEvent.Button, interaction); + } else if (interaction.isModalSubmit()) { + this.client.emit(TanzaniteEvent.ModalSubmit, interaction); + } else if (interaction.isSelectMenu()) { + this.client.emit(TanzaniteEvent.SelectMenu, interaction); + } + } +} diff --git a/src/listeners/interaction/button.ts b/src/listeners/interaction/button.ts index e69de29..d0730d5 100644 --- a/src/listeners/interaction/button.ts +++ b/src/listeners/interaction/button.ts @@ -0,0 +1,129 @@ +import { + BotClientEvents, + BotListener, + Emitter, + emojis, + handleAppealAttempt, + handleAppealDecision, + handleAutomodInteraction, + TanzaniteEvent +} from '#lib'; +import { + ActionRowData, + ButtonInteraction, + ComponentType, + ModalActionRowComponentData, + TextInputComponentData, + TextInputStyle +} from 'discord.js'; + +export default class ButtonListener extends BotListener { + public constructor() { + super(TanzaniteEvent.Button, { + emitter: Emitter.Client, + event: TanzaniteEvent.Button + }); + } + + public async exec(...[interaction]: BotClientEvents[TanzaniteEvent.Button]) { + const { customId } = interaction; + + if (customId.startsWith('automod;')) { + return void handleAutomodInteraction(interaction); + } else if (customId.startsWith('button-role;')) { + return void this.handleButtonRoles(interaction); + } else if (customId === 'test;modal') { + return this.handleTestModal(interaction); + } else if (customId.startsWith('test;lots;') || customId.startsWith('test;button;')) { + return await interaction.reply({ + content: 'Buttons go brrr', + ephemeral: true + }); + } else if (customId.startsWith('appeal_attempt;')) { + return handleAppealAttempt(interaction); + } else if (customId.startsWith('appeal_accept;') || customId.startsWith('appeal_deny;')) { + return handleAppealDecision(interaction); + } + } + + private async handleButtonRoles(interaction: ButtonInteraction) { + if (!interaction.inCachedGuild()) return; + + const [, roleId] = interaction.customId.split(';'); + const role = interaction.guild.roles.cache.get(roleId); + if (!role) { + return interaction.reply({ + content: `${emojis.error} That role does not exist.`, + ephemeral: true + }); + } + const has = interaction.member.roles.cache.has(roleId); + await interaction.deferReply({ ephemeral: true }); + if (has) { + const success = await interaction.member.roles.remove(roleId).catch(() => false); + if (success) { + return interaction.editReply({ + content: `${emojis.success} Removed the ${role} role from you.`, + allowedMentions: {} + }); + } else { + return interaction.editReply({ + content: `${emojis.error} Failed to remove ${role} from you.`, + allowedMentions: {} + }); + } + } else { + const success = await interaction.member.roles.add(roleId).catch(() => false); + if (success) { + return interaction.editReply({ + content: `${emojis.success} Added the ${role} role to you.`, + allowedMentions: {} + }); + } else { + return interaction.editReply({ + content: `${emojis.error} Failed to add ${role} to you.`, + allowedMentions: {} + }); + } + } + } + + private async handleTestModal(interaction: ButtonInteraction) { + const shortText = ( + options: Pick<TextInputComponentData, 'customId' | 'label' | 'placeholder'> & Partial<TextInputComponentData> + ): ActionRowData<ModalActionRowComponentData> => ({ + type: ComponentType.ActionRow as const, + components: [ + { + type: ComponentType.TextInput as const, + style: TextInputStyle.Short as const, + ...options + } + ] + }); + + return interaction.showModal({ + customId: 'test;login', + title: 'Login (real)', + components: [ + shortText({ + customId: 'test;login;email', + label: 'Email', + placeholder: 'Email' + }), + shortText({ + customId: 'test;login;password', + label: 'Password', + placeholder: 'Password' + }), + shortText({ + customId: 'test;login;2fa', + label: 'Enter Discord Auth Code', + minLength: 6, + maxLength: 6, + placeholder: '6-digit authentication code' + }) + ] + }); + } +} diff --git a/src/listeners/interaction/interactionCreate.ts b/src/listeners/interaction/interactionCreate.ts deleted file mode 100644 index ced359c..0000000 --- a/src/listeners/interaction/interactionCreate.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { - BotListener, - Emitter, - emojis, - format, - formatList, - handleAutomodInteraction, - surroundEach, - type BotClientEvents -} from '#lib'; -import { Events, InteractionType } from 'discord.js'; - -export default class InteractionCreateListener extends BotListener { - public constructor() { - super('interactionCreate', { - emitter: Emitter.Client, - event: Events.InteractionCreate - }); - } - - public async exec(...[interaction]: BotClientEvents[Events.InteractionCreate]) { - if (!interaction) return; - if ('customId' in interaction && (interaction as any)['customId'].startsWith('test')) return; - void this.client.console.verbose( - 'interactionVerbose', - `An interaction of type <<${InteractionType[interaction.type]}>> was received from <<${interaction.user.tag}>>.` - ); - if (interaction.type === InteractionType.ApplicationCommand) { - return; - } else if (interaction.isButton()) { - const id = interaction.customId; - if (['paginate_', 'command_', 'confirmationPrompt_', 'appeal'].some((s) => id.startsWith(s))) return; - else if (id.startsWith('automod;')) void handleAutomodInteraction(interaction); - else if (id.startsWith('button-role;') && interaction.inCachedGuild()) { - const [, roleId] = id.split(';'); - const role = interaction.guild.roles.cache.get(roleId); - if (!role) return interaction.reply({ content: `${emojis.error} That role does not exist.`, ephemeral: true }); - const has = interaction.member.roles.cache.has(roleId); - await interaction.deferReply({ ephemeral: true }); - if (has) { - const success = await interaction.member.roles.remove(roleId).catch(() => false); - if (success) - return interaction.editReply({ - content: `${emojis.success} Removed the ${role} role from you.`, - allowedMentions: {} - }); - else - return interaction.editReply({ - content: `${emojis.error} Failed to remove ${role} from you.`, - allowedMentions: {} - }); - } else { - const success = await interaction.member.roles.add(roleId).catch(() => false); - if (success) - return interaction.editReply({ - content: `${emojis.success} Added the ${role} role to you.`, - allowedMentions: {} - }); - else - return interaction.editReply({ - content: `${emojis.error} Failed to add ${role} to you.`, - allowedMentions: {} - }); - } - } else return await interaction.reply({ content: 'Buttons go brrr', ephemeral: true }); - } else if (interaction.isSelectMenu()) { - if (interaction.customId.startsWith('command_')) return; - return await interaction.reply({ - content: `You selected ${ - Array.isArray(interaction.values) - ? formatList(surroundEach(interaction.values, '`'), 'and') - : format.input(interaction.values) - }.`, - ephemeral: true - }); - } - } -} diff --git a/src/listeners/interaction/modalSubmit.ts b/src/listeners/interaction/modalSubmit.ts new file mode 100644 index 0000000..8cf93f4 --- /dev/null +++ b/src/listeners/interaction/modalSubmit.ts @@ -0,0 +1,20 @@ +import { BotClientEvents, BotListener, Emitter, emojis, handleAppealSubmit, TanzaniteEvent } from '#lib'; + +export default class ModalSubmitListener extends BotListener { + public constructor() { + super(TanzaniteEvent.ModalSubmit, { + emitter: Emitter.Client, + event: TanzaniteEvent.ModalSubmit + }); + } + + public async exec(...[interaction]: BotClientEvents[TanzaniteEvent.ModalSubmit]) { + const { customId } = interaction; + + if (customId === 'test;login') { + return interaction.reply({ content: `${emojis.loading} Selling your account information to Facebook...`, ephemeral: true }); + } else if (customId.startsWith('appeal_submit;')) { + return handleAppealSubmit(interaction); + } + } +} diff --git a/src/listeners/interaction/selectMenu.ts b/src/listeners/interaction/selectMenu.ts new file mode 100644 index 0000000..112f303 --- /dev/null +++ b/src/listeners/interaction/selectMenu.ts @@ -0,0 +1,23 @@ +import { BotClientEvents, BotListener, Emitter, format, formatList, surroundEach, TanzaniteEvent } from '#lib'; + +export default class SelectMenuListener extends BotListener { + public constructor() { + super(TanzaniteEvent.SelectMenu, { + emitter: Emitter.Client, + event: TanzaniteEvent.SelectMenu + }); + } + + public async exec(...[interaction]: BotClientEvents[TanzaniteEvent.SelectMenu]) { + if (interaction.customId.startsWith('command_')) return; + + return await interaction.reply({ + content: `You selected ${ + Array.isArray(interaction.values) + ? formatList(surroundEach(interaction.values, '`'), 'and') + : format.input(interaction.values) + }.`, + ephemeral: true + }); + } +} diff --git a/src/listeners/member-custom/levelUpdate.ts b/src/listeners/member-custom/levelUpdate.ts index 53734fd..7d3cee7 100644 --- a/src/listeners/member-custom/levelUpdate.ts +++ b/src/listeners/member-custom/levelUpdate.ts @@ -29,11 +29,12 @@ export default class LevelUpdateListener extends BotListener { .send(`${format.input(member.user.tag)} leveled up to level ${format.input(`${newLevel}`)}.`) .catch(() => null); - if (!success) + if (!success) { await message.guild.error( 'LevelUpdate', `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]) { diff --git a/src/listeners/message/autoPublisher.ts b/src/listeners/message/autoPublisher.ts index 36c448a..46577e4 100644 --- a/src/listeners/message/autoPublisher.ts +++ b/src/listeners/message/autoPublisher.ts @@ -13,7 +13,10 @@ export default class autoPublisherListener extends BotListener { if (!message.guild || !(await message.guild.hasFeature('autoPublish'))) return; const autoPublishChannels = await message.guild.getSetting('autoPublishChannels'); if (autoPublishChannels) { - if (message.channel.type === ChannelType.GuildNews && autoPublishChannels.some((x) => message.channel.id.includes(x))) { + if ( + message.channel.type === ChannelType.GuildAnnouncement && + autoPublishChannels.some((x) => message.channel.id.includes(x)) + ) { await message .crosspost() .then( diff --git a/src/listeners/message/level.ts b/src/listeners/message/level.ts index a445d1e..60742ab 100644 --- a/src/listeners/message/level.ts +++ b/src/listeners/message/level.ts @@ -3,6 +3,7 @@ import { MessageType } from 'discord.js'; export default class LevelListener extends BotListener { #levelCooldowns: Set<string> = new Set(); + public constructor() { super('level', { emitter: Emitter.CommandHandler, @@ -14,34 +15,52 @@ export default class LevelListener extends BotListener { public async exec(...[message]: BotCommandHandlerEvents[CommandHandlerEvent.MessageInvalid]) { if (message.author.bot || !message.author || !message.inGuild()) return; if (!(await message.guild.hasFeature('leveling'))) return; - if (this.#levelCooldowns.has(`${message.guildId}-${message.author.id}`)) return; + + const lock = `${message.guildId}-${message.author.id}`; + if (this.#levelCooldowns.has(lock)) return; if ((await message.guild.getSetting('noXpChannels')).includes(message.channel.id)) return; - if (message.type !== MessageType.Default && message.type !== MessageType.Reply) return; //checks for join messages, slash commands, booster messages etc - const [user] = await Level.findOrBuild({ + + // checks for join messages, slash commands, booster messages etc + if (![MessageType.Default, MessageType.Reply, MessageType.ThreadStarterMessage].includes(message.type)) { + return; + } + + const [levelEntry] = await Level.findOrBuild({ where: { user: message.author.id, guild: message.guildId - }, - defaults: { - user: message.author.id, - guild: message.guildId, - xp: 0 } }); - const previousLevel = Level.convertXpToLevel(user.xp); + + const previousLevel = Level.convertXpToLevel(levelEntry.xp); const xpToGive = Level.genRandomizedXp(); - user.xp = user.xp + xpToGive; - const success = await user.save().catch((e) => { - void this.client.utils.handleError('level', e); + + let xp = levelEntry.xp + xpToGive; + + if (xp > Level.MAX_XP) { + xp = Level.MAX_XP; + } + + const success = await levelEntry.update({ xp, user: message.author.id, guild: message.guild.id }).catch((e) => { + void this.client.utils.handleError('LevelListener', e); + return false; }); - const newLevel = Level.convertXpToLevel(user.xp); - if (previousLevel !== newLevel) - this.client.emit(TanzaniteEvent.LevelUpdate, message.member!, previousLevel, newLevel, user.xp, message); - if (success) - void this.client.logger.verbose(`level`, `Gave <<${xpToGive}>> XP to <<${message.author.tag}>> in <<${message.guild}>>.`); - this.#levelCooldowns.add(`${message.guildId}-${message.author.id}`); - setTimeout(() => this.#levelCooldowns.delete(`${message.guildId}-${message.author.id}`), 60_000); + + const newLevel = Level.convertXpToLevel(levelEntry.xp); + + if (success) { + if (previousLevel !== newLevel) { + // level up messages and level roles + this.client.emit(TanzaniteEvent.LevelUpdate, message.member!, previousLevel, newLevel, levelEntry.xp, message); + } + + void this.client.logger.verbose(`level`, `Gave <<${xpToGive}>> xp to <<${message.author.tag}>> in <<${message.guild}>>.`); + } + + this.#levelCooldowns.add(lock); + + setTimeout(() => this.#levelCooldowns.delete(lock), 60_000); } } diff --git a/src/listeners/message/quoteCreate.ts b/src/listeners/message/quoteCreate.ts index 1c3130c..fca6c9f 100644 --- a/src/listeners/message/quoteCreate.ts +++ b/src/listeners/message/quoteCreate.ts @@ -10,7 +10,7 @@ export default class QuoteCreateListener extends BotListener { } public async exec(...[message]: BotClientEvents[Events.MessageCreate]) { - if (message.author.id !== mappings.users['IRONM00N'] || !this.client.config.isProduction) return; + if (message.author.id !== mappings.users['IRONM00N'] /* || !this.client.config.isProduction */) return; if (!message.inGuild()) return; const messages = await this.client.utils.resolveMessagesFromLinks(message.content); diff --git a/src/listeners/track-manual-punishments/modlogSyncBan.ts b/src/listeners/track-manual-punishments/modlogSyncBan.ts index 83f6dd4..5a4d768 100644 --- a/src/listeners/track-manual-punishments/modlogSyncBan.ts +++ b/src/listeners/track-manual-punishments/modlogSyncBan.ts @@ -37,7 +37,7 @@ export default class ModlogSyncBanListener extends BotListener { const { log } = await Moderation.createModLogEntry({ client: this.client, - type: ModLogType.PERM_BAN, + type: ModLogType.PermBan, user: ban.user, moderator: first.executor, reason: `[Manual] ${first.reason ? first.reason : 'No reason given'}`, diff --git a/src/listeners/track-manual-punishments/modlogSyncKick.ts b/src/listeners/track-manual-punishments/modlogSyncKick.ts index e8b2433..a05e666 100644 --- a/src/listeners/track-manual-punishments/modlogSyncKick.ts +++ b/src/listeners/track-manual-punishments/modlogSyncKick.ts @@ -37,7 +37,7 @@ export default class ModlogSyncKickListener extends BotListener { const { log } = await Moderation.createModLogEntry({ client: this.client, - type: ModLogType.KICK, + type: ModLogType.Kick, user: member.user, moderator: first.executor, reason: `[Manual] ${first.reason ? first.reason : 'No reason given'}`, diff --git a/src/listeners/track-manual-punishments/modlogSyncTimeout.ts b/src/listeners/track-manual-punishments/modlogSyncTimeout.ts index 2a4e0bb..f6abbab 100644 --- a/src/listeners/track-manual-punishments/modlogSyncTimeout.ts +++ b/src/listeners/track-manual-punishments/modlogSyncTimeout.ts @@ -41,7 +41,7 @@ export default class ModlogSyncTimeoutListener extends BotListener { const { log } = await Moderation.createModLogEntry({ client: this.client, - type: newTime ? ModLogType.TIMEOUT : ModLogType.REMOVE_TIMEOUT, + type: newTime ? ModLogType.Timeout : ModLogType.RemoveTimeout, user: newMember.user, moderator: first.executor, reason: `[Manual] ${first.reason ? first.reason : 'No reason given'}`, diff --git a/src/listeners/track-manual-punishments/modlogSyncUnban.ts b/src/listeners/track-manual-punishments/modlogSyncUnban.ts index 4738066..35ee3d2 100644 --- a/src/listeners/track-manual-punishments/modlogSyncUnban.ts +++ b/src/listeners/track-manual-punishments/modlogSyncUnban.ts @@ -36,7 +36,7 @@ export default class ModlogSyncUnbanListener extends BotListener { const { log } = await Moderation.createModLogEntry({ client: this.client, - type: ModLogType.UNBAN, + type: ModLogType.Unban, user: ban.user, moderator: first.executor, reason: `[Manual] ${first.reason ? first.reason : 'No reason given'}`, diff --git a/src/listeners/ws/INTERACTION_CREATE.ts b/src/listeners/ws/INTERACTION_CREATE.ts index d0327df..49f8bdf 100644 --- a/src/listeners/ws/INTERACTION_CREATE.ts +++ b/src/listeners/ws/INTERACTION_CREATE.ts @@ -1,25 +1,5 @@ -import { BotListener, capitalize, colors, Emitter, emojis, Moderation, PunishmentTypePresent } from '#lib'; -import assert from 'assert/strict'; -import { - ActionRowBuilder, - ButtonBuilder, - ButtonStyle, - ComponentType, - EmbedBuilder, - GatewayDispatchEvents, - InteractionResponseType, - InteractionType, - Routes, - Snowflake, - TextInputStyle, - User, - type APIEmbed, - type APIInteraction, - type APIInteractionResponseChannelMessageWithSource, - type APIInteractionResponseDeferredMessageUpdate, - type APIInteractionResponseUpdateMessage, - type APIModalInteractionResponse -} from 'discord.js'; +import { BotListener, Emitter } from '#lib'; +import { GatewayDispatchEvents, type APIInteraction } from 'discord.js'; export default class WsInteractionCreateListener extends BotListener { public constructor() { @@ -29,215 +9,5 @@ export default class WsInteractionCreateListener extends BotListener { }); } - public async exec(interaction: APIInteraction) { - // console.dir(interaction); - - const respond = ( - options: - | APIModalInteractionResponse - | APIInteractionResponseDeferredMessageUpdate - | APIInteractionResponseChannelMessageWithSource - | APIInteractionResponseUpdateMessage - ) => { - return this.client.rest.post( - Routes.interactionCallback(interaction.id, interaction.token), - options ? { body: options } : undefined - ); - }; - - const deferredMessageUpdate = () => { - return respond({ - type: InteractionResponseType.DeferredMessageUpdate - }); - }; - - if (interaction.type === InteractionType.MessageComponent) { - if (interaction.data.custom_id.startsWith('appeal;')) { - const [, punishment, guildId, userId, modlogCase] = interaction.data.custom_id.split(';') as [ - 'appeal', - PunishmentTypePresent, - Snowflake, - Snowflake, - string - ]; - - const guild = this.client.guilds.resolve(guildId); - if (!guild) - return respond({ - type: InteractionResponseType.ChannelMessageWithSource, - data: { - content: `${emojis.error} I am no longer in that server.` - } - }); - - const modal: APIModalInteractionResponse = { - type: InteractionResponseType.Modal, - data: { - custom_id: `appeal_submit;${punishment};${guildId};${userId};${modlogCase}`, - title: `${capitalize(punishment)} Appeal`, - components: [ - { - type: ComponentType.ActionRow, - components: [ - { - type: ComponentType.TextInput, - style: TextInputStyle.Paragraph, - max_length: 1024, - required: true, - label: `Why were you ${Moderation.punishmentToPastTense(punishment)}?`, - placeholder: `Why do you think you received a ${punishment}?`, - custom_id: 'appeal_reason' - } - ] - }, - { - type: ComponentType.ActionRow, - components: [ - { - type: ComponentType.TextInput, - style: TextInputStyle.Paragraph, - max_length: 1024, - required: true, - label: 'Do you believe it was fair?', - placeholder: `Why do you think you received a ${punishment}?`, - custom_id: 'appeal_fair' - } - ] - }, - { - type: ComponentType.ActionRow, - components: [ - { - type: ComponentType.TextInput, - style: TextInputStyle.Paragraph, - max_length: 1024, - required: true, - label: `Why should your ${punishment} be removed?`, - placeholder: `Why should your ${punishment} be removed?`, - custom_id: 'appeal_why' - } - ] - } - ] - } - }; - - return respond(modal); - } else if ( - interaction.data.custom_id.startsWith('appeal_accept;') || - interaction.data.custom_id.startsWith('appeal_deny;') - ) { - const [action, punishment, guildId, userId /* modlogCase */] = interaction.data.custom_id.split(';') as [ - 'appeal_accept' | 'appeal_deny', - PunishmentTypePresent, - Snowflake, - Snowflake, - string - ]; - - if (action === 'appeal_deny') { - await this.client.users - .send(userId, `Your ${punishment} appeal has been denied in ${this.client.guilds.resolve(guildId)!}.`) - .catch(() => {}); - - void respond({ - type: InteractionResponseType.ChannelMessageWithSource, - data: { - components: [ - { - type: 1, - components: [ - { - type: ComponentType.Button, - style: ButtonStyle.Danger, - label: 'Close', - custom_id: 'appeal_denied' - } - ] - } - ] - } - }); - } - } - } else if (interaction.type === InteractionType.ModalSubmit) { - if (interaction.data.custom_id.startsWith('appeal_submit;')) { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [, punishment, guildId, userId, modlogCase] = interaction.data.custom_id.split(';') as [ - 'appeal_submit', - PunishmentTypePresent, - Snowflake, - Snowflake, - string - ]; - - const guild = this.client.guilds.resolve(guildId); - if (!guild) - return respond({ - type: InteractionResponseType.ChannelMessageWithSource, - data: { - content: `${emojis.error} I am no longer in that server.` - } - }); - - const channel = await guild.getLogChannel('appeals'); - if (!channel) - return respond({ - type: InteractionResponseType.ChannelMessageWithSource, - data: { - content: `${emojis.error} ${guild.name} has misconfigured their appeals channel.` - } - }); - - assert(interaction.user); - const user = new User(this.client, interaction.user); - assert(user); - - // const caseId = await ModLog.findOne({ where: { user: userId, guild: guildId, id: modlogCase } }); - - const embed = new EmbedBuilder() - .setTitle(`${capitalize(punishment)} Appeal`) - .setColor(colors.newBlurple) - .setTimestamp() - .setFooter({ text: `CaseID: ${modlogCase}` }) - .setAuthor({ name: user.tag, iconURL: user.displayAvatarURL() }) - .addFields( - { - name: `Why were you ${Moderation.punishmentToPastTense(punishment)}?`, - value: interaction.data.components![0].components[0]!.value.substring(0, 1024) - }, - { - name: 'Do you believe it was fair?', - value: interaction.data.components![1].components[0]!.value.substring(0, 1024) - }, - { - name: `Why should your ${punishment} be removed?`, - value: interaction.data.components![2].components[0]!.value.substring(0, 1024) - } - ) - .toJSON() as APIEmbed; - - const components = [ - new ActionRowBuilder<ButtonBuilder>({ - components: [ - new ButtonBuilder({ - customId: `appeal_accept;${punishment};${guildId};${userId};${modlogCase}`, - label: 'Accept', - style: ButtonStyle.Success - }).toJSON(), - new ButtonBuilder({ - customId: `appeal_deny;${punishment};${guildId};${userId};${modlogCase}`, - label: 'Deny', - style: ButtonStyle.Danger - }).toJSON() - ] - }) - ]; - - await channel.send({ embeds: [embed], components }); - } else { - return deferredMessageUpdate(); - } - } - } + public async exec(interaction: APIInteraction) {} } diff --git a/src/tasks/feature/removeExpiredPunishements.ts b/src/tasks/feature/removeExpiredPunishements.ts index eac325a..5d5d5ab 100644 --- a/src/tasks/feature/removeExpiredPunishements.ts +++ b/src/tasks/feature/removeExpiredPunishements.ts @@ -35,7 +35,7 @@ export default class RemoveExpiredPunishmentsTask extends BotTask { assert(guild); switch (entry.type) { - case ActivePunishmentType.BAN: { + case ActivePunishmentType.Ban: { assert(user); const result = await guild.customUnban({ user: user, reason: 'Punishment expired' }); if (['success', 'user not banned', 'cannot resolve user'].includes(result)) await entry.destroy(); @@ -43,7 +43,7 @@ export default class RemoveExpiredPunishmentsTask extends BotTask { void this.client.logger.verbose(`removeExpiredPunishments`, `Unbanned ${entry.user}.`); break; } - case ActivePunishmentType.BLOCK: { + case ActivePunishmentType.Block: { if (!member) { await entry.destroy(); // channel overrides are removed when the member leaves the guild return; @@ -54,7 +54,7 @@ export default class RemoveExpiredPunishmentsTask extends BotTask { void this.client.logger.verbose(`removeExpiredPunishments`, `Unblocked ${entry.user}.`); break; } - case ActivePunishmentType.MUTE: { + case ActivePunishmentType.Mute: { if (!member) return; const result = await member.customUnmute({ reason: 'Punishment expired' }); if (['success', 'failed to dm'].includes(result)) await entry.destroy(); @@ -62,7 +62,7 @@ export default class RemoveExpiredPunishmentsTask extends BotTask { void this.client.logger.verbose(`removeExpiredPunishments`, `Unmuted ${entry.user}.`); break; } - case ActivePunishmentType.ROLE: { + case ActivePunishmentType.Role: { if (!member) return; const role = guild?.roles?.cache?.get(entry.extraInfo); if (!role) throw new Error(`Cannot unmute ${member.user.tag} because I cannot find the mute role.`); @@ -5,16 +5,16 @@ __metadata: version: 6 cacheKey: 8 -"@babel/runtime@npm:^7.18.9": - version: 7.18.9 - resolution: "@babel/runtime@npm:7.18.9" +"@babel/runtime@npm:^7.19.0": + version: 7.19.0 + resolution: "@babel/runtime@npm:7.19.0" dependencies: regenerator-runtime: ^0.13.4 - checksum: 36dd736baba7164e82b3cc9d43e081f0cb2d05ff867ad39cac515d99546cee75b7f782018b02a3dcf5f2ef3d27f319faa68965fdfec49d4912c60c6002353a2e + checksum: fa69c351bb05e1db3ceb9a02fdcf620c234180af68cdda02152d3561015f6d55277265d3109815992f96d910f3db709458cae4f8df1c3def66f32e0867d82294 languageName: node linkType: hard -"@discordjs/builders@npm:^1.1.0": +"@discordjs/builders@npm:^1.2.0": version: 1.2.0 resolution: "@discordjs/builders@npm:1.2.0" dependencies: @@ -27,38 +27,45 @@ __metadata: languageName: node linkType: hard -"@discordjs/collection@npm:^1.0.1": +"@discordjs/collection@npm:^1.1.0": version: 1.1.0 resolution: "@discordjs/collection@npm:1.1.0" checksum: 9a78763a181130d91b51d0d93553fd75d09d0aabd6556890a35404bbefe9c5112cb74c3b1e486a213607f6577f9d2d8ee94ee3177652116bac80516e7d3083d6 languageName: node linkType: hard -"@discordjs/rest@npm:^1.0.1": - version: 1.1.0 - resolution: "@discordjs/rest@npm:1.1.0" +"@discordjs/rest@npm:^1.2.0": + version: 1.2.0 + resolution: "@discordjs/rest@npm:1.2.0" dependencies: - "@discordjs/collection": ^1.0.1 + "@discordjs/collection": ^1.1.0 "@sapphire/async-queue": ^1.5.0 "@sapphire/snowflake": ^3.2.2 - discord-api-types: ^0.37.3 - file-type: ^17.1.6 + discord-api-types: ^0.37.10 + file-type: ^18.0.0 tslib: ^2.4.0 - undici: ^5.9.1 - checksum: b216d5b217bf70a713480738851a0e607cbed267a0ad60942268f9761c605aac6552fd9cdda56bcabe332f6e5ccb5b7ce9911645e24d3d9342ed6f18dccc6b94 + undici: ^5.10.0 + checksum: fa414f2a92e94ad587c0a793e5a378cc0597ed062fe1509314b7c29f925b7a9cc013efad8f572f589d520a9dca4f19a4cc2403f14b89d38e6c61aa2292d96cfe + languageName: node + linkType: hard + +"@esbuild/android-arm@npm:0.15.9": + version: 0.15.9 + resolution: "@esbuild/android-arm@npm:0.15.9" + conditions: os=android & cpu=arm languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.14.54": - version: 0.14.54 - resolution: "@esbuild/linux-loong64@npm:0.14.54" +"@esbuild/linux-loong64@npm:0.15.9": + version: 0.15.9 + resolution: "@esbuild/linux-loong64@npm:0.15.9" conditions: os=linux & cpu=loong64 languageName: node linkType: hard -"@eslint/eslintrc@npm:^1.3.1": - version: 1.3.1 - resolution: "@eslint/eslintrc@npm:1.3.1" +"@eslint/eslintrc@npm:^1.3.2": + version: 1.3.2 + resolution: "@eslint/eslintrc@npm:1.3.2" dependencies: ajv: ^6.12.4 debug: ^4.3.2 @@ -69,7 +76,7 @@ __metadata: js-yaml: ^4.1.0 minimatch: ^3.1.2 strip-json-comments: ^3.1.1 - checksum: 9844dcc58a44399649926d5a17a2d53d529b80d3e8c3e9d0964ae198bac77ee6bb1cf44940f30cd9c2e300f7568ec82500be42ace6cacefb08aebf9905fe208e + checksum: 2074dca47d7e1c5c6323ff353f690f4b25d3ab53fe7d27337e2592d37a894cf60ca0e85ca66b50ff2db0bc7e630cc1e9c7347d65bb185b61416565584c38999c languageName: node linkType: hard @@ -80,14 +87,14 @@ __metadata: languageName: node linkType: hard -"@humanwhocodes/config-array@npm:^0.10.4": - version: 0.10.4 - resolution: "@humanwhocodes/config-array@npm:0.10.4" +"@humanwhocodes/config-array@npm:^0.10.5": + version: 0.10.5 + resolution: "@humanwhocodes/config-array@npm:0.10.5" dependencies: "@humanwhocodes/object-schema": ^1.2.1 debug: ^4.1.1 minimatch: ^3.0.4 - checksum: d480e5d57e6d787565b6cff78e27c3d1b380692d4ffb0ada7d7f5957a56c9032f034da05a3e443065dbd0671ebf4d859036ced34e96b325bbc1badbae3c05300 + checksum: af4fa2633c57414be22ddba0a072cc611ef9a07104542fa24bde918a0153b89b6e08ca6a20ccc9079de6079e219e2406e38414d1b662db8bb59a3ba9d6eee6e3 languageName: node linkType: hard @@ -119,82 +126,82 @@ __metadata: languageName: node linkType: hard -"@napi-rs/canvas-android-arm64@npm:0.1.29": - version: 0.1.29 - resolution: "@napi-rs/canvas-android-arm64@npm:0.1.29" +"@napi-rs/canvas-android-arm64@npm:0.1.30": + version: 0.1.30 + resolution: "@napi-rs/canvas-android-arm64@npm:0.1.30" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@napi-rs/canvas-darwin-arm64@npm:0.1.29": - version: 0.1.29 - resolution: "@napi-rs/canvas-darwin-arm64@npm:0.1.29" +"@napi-rs/canvas-darwin-arm64@npm:0.1.30": + version: 0.1.30 + resolution: "@napi-rs/canvas-darwin-arm64@npm:0.1.30" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@napi-rs/canvas-darwin-x64@npm:0.1.29": - version: 0.1.29 - resolution: "@napi-rs/canvas-darwin-x64@npm:0.1.29" +"@napi-rs/canvas-darwin-x64@npm:0.1.30": + version: 0.1.30 + resolution: "@napi-rs/canvas-darwin-x64@npm:0.1.30" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@napi-rs/canvas-linux-arm-gnueabihf@npm:0.1.29": - version: 0.1.29 - resolution: "@napi-rs/canvas-linux-arm-gnueabihf@npm:0.1.29" +"@napi-rs/canvas-linux-arm-gnueabihf@npm:0.1.30": + version: 0.1.30 + resolution: "@napi-rs/canvas-linux-arm-gnueabihf@npm:0.1.30" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@napi-rs/canvas-linux-arm64-gnu@npm:0.1.29": - version: 0.1.29 - resolution: "@napi-rs/canvas-linux-arm64-gnu@npm:0.1.29" +"@napi-rs/canvas-linux-arm64-gnu@npm:0.1.30": + version: 0.1.30 + resolution: "@napi-rs/canvas-linux-arm64-gnu@npm:0.1.30" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@napi-rs/canvas-linux-arm64-musl@npm:0.1.29": - version: 0.1.29 - resolution: "@napi-rs/canvas-linux-arm64-musl@npm:0.1.29" +"@napi-rs/canvas-linux-arm64-musl@npm:0.1.30": + version: 0.1.30 + resolution: "@napi-rs/canvas-linux-arm64-musl@npm:0.1.30" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@napi-rs/canvas-linux-x64-gnu@npm:0.1.29": - version: 0.1.29 - resolution: "@napi-rs/canvas-linux-x64-gnu@npm:0.1.29" +"@napi-rs/canvas-linux-x64-gnu@npm:0.1.30": + version: 0.1.30 + resolution: "@napi-rs/canvas-linux-x64-gnu@npm:0.1.30" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@napi-rs/canvas-linux-x64-musl@npm:0.1.29": - version: 0.1.29 - resolution: "@napi-rs/canvas-linux-x64-musl@npm:0.1.29" +"@napi-rs/canvas-linux-x64-musl@npm:0.1.30": + version: 0.1.30 + resolution: "@napi-rs/canvas-linux-x64-musl@npm:0.1.30" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@napi-rs/canvas-win32-x64-msvc@npm:0.1.29": - version: 0.1.29 - resolution: "@napi-rs/canvas-win32-x64-msvc@npm:0.1.29" +"@napi-rs/canvas-win32-x64-msvc@npm:0.1.30": + version: 0.1.30 + resolution: "@napi-rs/canvas-win32-x64-msvc@npm:0.1.30" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"@napi-rs/canvas@npm:^0.1.29": - version: 0.1.29 - resolution: "@napi-rs/canvas@npm:0.1.29" +"@napi-rs/canvas@npm:^0.1.30": + version: 0.1.30 + resolution: "@napi-rs/canvas@npm:0.1.30" dependencies: - "@napi-rs/canvas-android-arm64": 0.1.29 - "@napi-rs/canvas-darwin-arm64": 0.1.29 - "@napi-rs/canvas-darwin-x64": 0.1.29 - "@napi-rs/canvas-linux-arm-gnueabihf": 0.1.29 - "@napi-rs/canvas-linux-arm64-gnu": 0.1.29 - "@napi-rs/canvas-linux-arm64-musl": 0.1.29 - "@napi-rs/canvas-linux-x64-gnu": 0.1.29 - "@napi-rs/canvas-linux-x64-musl": 0.1.29 - "@napi-rs/canvas-win32-x64-msvc": 0.1.29 + "@napi-rs/canvas-android-arm64": 0.1.30 + "@napi-rs/canvas-darwin-arm64": 0.1.30 + "@napi-rs/canvas-darwin-x64": 0.1.30 + "@napi-rs/canvas-linux-arm-gnueabihf": 0.1.30 + "@napi-rs/canvas-linux-arm64-gnu": 0.1.30 + "@napi-rs/canvas-linux-arm64-musl": 0.1.30 + "@napi-rs/canvas-linux-x64-gnu": 0.1.30 + "@napi-rs/canvas-linux-x64-musl": 0.1.30 + "@napi-rs/canvas-win32-x64-msvc": 0.1.30 dependenciesMeta: "@napi-rs/canvas-android-arm64": optional: true @@ -214,7 +221,7 @@ __metadata: optional: true "@napi-rs/canvas-win32-x64-msvc": optional: true - checksum: 1a8a61429f2be2c975e5e543f6a79c2d1762acfe85a87d308e156b5cbcf4d0d60649fb374128888f465b09563bc96ef91c14c2c9b6c4dd21e1c1acf22c000737 + checksum: ee5e6f308a530daf1df85d4fe03877f8d12e06ed00c36ca128166832dd7eefd2c0481c8031a4150688e32cf96838719d93a55c2e1f0a17feb012ce6981c0ab08 languageName: node linkType: hard @@ -245,17 +252,12 @@ __metadata: languageName: node linkType: hard -"@notenoughupdates/discord.js-minesweeper@npm:^1.0.10": - version: 1.0.10 - resolution: "@notenoughupdates/discord.js-minesweeper@npm:1.0.10" - checksum: f9467e8f78c825c058098c32b4f7e1e9fca2a3ce90536363d716de2849a1402d32ea8a17c0e188af0fb9f1851174661deacf82d64705dc92306d634dad5c57c5 - languageName: node - linkType: hard - -"@notenoughupdates/events-intercept@npm:^3.0.1": - version: 3.0.1 - resolution: "@notenoughupdates/events-intercept@npm:3.0.1" - checksum: cd1f8d608160276c35d0cfe5116181bc34c5017b1e2f785e868d16b8983ed434abbc12f1d090bc5e5ced26c9460aea457bac92a919f4efda14db246a5392c62e +"@notenoughupdates/discord-akairo@npm:^10.0.0-dev.1664158089.48d6368": + version: 10.0.0-dev.1664158089.48d6368 + resolution: "@notenoughupdates/discord-akairo@npm:10.0.0-dev.1664158089.48d6368" + dependencies: + zod: ^3.19.1 + checksum: d07d3d554f4bd45663339275d93b795506da65be592cdf50ee75bd1fd239d2dc574d36107f1406fffaf0ecb2649d38839b3ac9c9684c9966ae06ff83058a6557 languageName: node linkType: hard @@ -266,20 +268,6 @@ __metadata: languageName: node linkType: hard -"@notenoughupdates/simplify-number@npm:^1.0.1": - version: 1.0.1 - resolution: "@notenoughupdates/simplify-number@npm:1.0.1" - checksum: 91b73abf024888fdf68954dfc46464f6e22f0f83291bae7f75f223b49d115f23be0aad809a72a0dba10012418e1562a9703795c2ec084edd37f52dca16649141 - languageName: node - linkType: hard - -"@notenoughupdates/wolfram-alpha-api@npm:^1.0.2": - version: 1.0.2 - resolution: "@notenoughupdates/wolfram-alpha-api@npm:1.0.2" - checksum: 6aee1d5c2fc13393fb7d8d9e6ffa0c688f9a1f9c44f012a3042778384afbb5c777df7c9ec699f6e45b24326c3d12919302453085c17ce28dbc89717c3353f86d - languageName: node - linkType: hard - "@npmcli/fs@npm:^2.1.0": version: 2.1.2 resolution: "@npmcli/fs@npm:2.1.2" @@ -308,12 +296,12 @@ __metadata: linkType: hard "@sapphire/shapeshift@npm:^3.5.1": - version: 3.5.1 - resolution: "@sapphire/shapeshift@npm:3.5.1" + version: 3.6.0 + resolution: "@sapphire/shapeshift@npm:3.6.0" dependencies: fast-deep-equal: ^3.1.3 lodash.uniqwith: ^4.5.0 - checksum: caecfef844c9e43e921a5051da888fae7da8980bfd9f9bb4f7fee85931d40929ffb9b6dfae464c0dccee61e56f7698f998e4d9a54d25f35fad39a51ba1a4f391 + checksum: 31b426424d064c516144c6eda07dfa0e44d7cbb8309dde919b923aa6ae939faac6384fa4d08db391a8da11efa3a83c18c9be1ebd053ba29403e61f5e2450b788 languageName: node linkType: hard @@ -324,83 +312,118 @@ __metadata: languageName: node linkType: hard -"@sentry/core@npm:7.11.1": - version: 7.11.1 - resolution: "@sentry/core@npm:7.11.1" +"@sentry/core@npm:7.13.0": + version: 7.13.0 + resolution: "@sentry/core@npm:7.13.0" dependencies: - "@sentry/hub": 7.11.1 - "@sentry/types": 7.11.1 - "@sentry/utils": 7.11.1 + "@sentry/hub": 7.13.0 + "@sentry/types": 7.13.0 + "@sentry/utils": 7.13.0 tslib: ^1.9.3 - checksum: 4e3fa11d248182a6cdce87ff60111cd29fe028fb84abed7f362f3d7b1a6db9b97bf6bf07317f2338fce01b331a1e867a34e3ef477134b859270ef0d0f48bdc8e + checksum: 8fd5b0eab630a54ea1beec1b3fe7deb388e95ca3312c1f0eaffa32090882ca7792287b5e7d157fc1c408a0ec1bfb769ac45a77a5385068dcd8fc6f3e6949f8a1 languageName: node linkType: hard -"@sentry/hub@npm:7.11.1": - version: 7.11.1 - resolution: "@sentry/hub@npm:7.11.1" +"@sentry/hub@npm:7.13.0": + version: 7.13.0 + resolution: "@sentry/hub@npm:7.13.0" dependencies: - "@sentry/types": 7.11.1 - "@sentry/utils": 7.11.1 + "@sentry/types": 7.13.0 + "@sentry/utils": 7.13.0 tslib: ^1.9.3 - checksum: 1df55e6e3a494167e6b1592f1aa6ea185feeed3a68d9060340877b3db5943fe45437c5d508dfabc1d4275ce7f3c3910f3bb4ad1880a3c9d01a7d07200f3feb91 + checksum: 690f1eecd4d9f0a87ca4344b551b990df234ec7197b21175293fb713f917c6171ed44f35042af5b83a0bae5e0963a520490bfd905d48bd040a2f2532247e04d6 languageName: node linkType: hard -"@sentry/integrations@npm:^7.11.1": - version: 7.11.1 - resolution: "@sentry/integrations@npm:7.11.1" +"@sentry/integrations@npm:^7.13.0": + version: 7.13.0 + resolution: "@sentry/integrations@npm:7.13.0" dependencies: - "@sentry/types": 7.11.1 - "@sentry/utils": 7.11.1 + "@sentry/types": 7.13.0 + "@sentry/utils": 7.13.0 localforage: ^1.8.1 tslib: ^1.9.3 - checksum: e89ab412462e4d93a6689360d3afe2eb5e64a45f3d2bf9e8c3cdb8f71bfe66b71b62ac1d867d9c726c4d2e1ab9309893bb7f856f74acbc382debe60db33008fa + checksum: b952043a97c60ee157fe734de6a44dcdd1d2175d5810122b0703f9a8e0999948f048042f5a8bb35efdd7a29ee1c177cfd99f1102e0797a0c2e5b62ee790c1c89 languageName: node linkType: hard -"@sentry/node@npm:^7.11.1": - version: 7.11.1 - resolution: "@sentry/node@npm:7.11.1" +"@sentry/node@npm:^7.13.0": + version: 7.13.0 + resolution: "@sentry/node@npm:7.13.0" dependencies: - "@sentry/core": 7.11.1 - "@sentry/hub": 7.11.1 - "@sentry/types": 7.11.1 - "@sentry/utils": 7.11.1 + "@sentry/core": 7.13.0 + "@sentry/hub": 7.13.0 + "@sentry/types": 7.13.0 + "@sentry/utils": 7.13.0 cookie: ^0.4.1 https-proxy-agent: ^5.0.0 lru_map: ^0.3.3 tslib: ^1.9.3 - checksum: 824780c2d4fecbfaabfacd27b7108a21c6e37b98a61aeb2dda394dae6d67584ef46a749df80fb7dfa3e43bd4e6dcb28b7bcf8a83c77ebef621d05f231651724a + checksum: 36de86c7bb8e62fe93c0cc72dfbd0d77925ef18b6abd98a034b676853c9cca705be276fee617ddcedbf89659454055cd79ab82f3f7b77d465617810787d8775d languageName: node linkType: hard -"@sentry/tracing@npm:^7.11.1": - version: 7.11.1 - resolution: "@sentry/tracing@npm:7.11.1" +"@sentry/tracing@npm:^7.13.0": + version: 7.13.0 + resolution: "@sentry/tracing@npm:7.13.0" dependencies: - "@sentry/hub": 7.11.1 - "@sentry/types": 7.11.1 - "@sentry/utils": 7.11.1 + "@sentry/hub": 7.13.0 + "@sentry/types": 7.13.0 + "@sentry/utils": 7.13.0 tslib: ^1.9.3 - checksum: c0c4b540eff30deb94b939d7792f765410bd0abb3c495a49cd89c1ed113f0ca46fb7b12bdc777ddb42f64018a5647afe69bb444d77a2e0cb8a8ae9ed813a7f7b + checksum: 4d51a166b74799a85bfef9e5027147bec2ef977bd50e820773a8d9a8f6f2658c97b40ce67501f7a3f1406d30da64f38454d903fdf98e81d4da30b70fc63aca27 languageName: node linkType: hard -"@sentry/types@npm:7.11.1, @sentry/types@npm:^7.11.1": - version: 7.11.1 - resolution: "@sentry/types@npm:7.11.1" - checksum: 935dbd83d8c43bca202979fef12fe2af0508e44c61a0b3b2e10971006b59938fac36bfde530c7b7db14601dcbb17a2a7b35f479de8bbfe961df3eff7fd9327ce +"@sentry/types@npm:7.13.0, @sentry/types@npm:^7.13.0": + version: 7.13.0 + resolution: "@sentry/types@npm:7.13.0" + checksum: 8bd94d1cf735af61a3cea77e7e925e487dc84b925fa1e1a0aa9a566096e5d99d3bb72c6ea2f03c20d7894824f35edbb8b25352b3a1c78e62729a77a93a1abf71 languageName: node linkType: hard -"@sentry/utils@npm:7.11.1": - version: 7.11.1 - resolution: "@sentry/utils@npm:7.11.1" +"@sentry/utils@npm:7.13.0": + version: 7.13.0 + resolution: "@sentry/utils@npm:7.13.0" dependencies: - "@sentry/types": 7.11.1 + "@sentry/types": 7.13.0 tslib: ^1.9.3 - checksum: e0a1d8528e9f99a35c88786cc230a5466966a59554796bbf13a33aa8956911c8bce7c556bb7a90ba20c9af9fa64ef6b87b956d1d8bae6ff42950ce4125c3b44e + checksum: e655e2bd444c9f8aedac4bc52e9a75bf74ef0086ffb1b9efc9cf6670aab9b0deca45e9d7c808e7d19e51ff4b40bcd178b6c5b2ea55e8cfb81da630db6b75bfe2 + languageName: node + linkType: hard + +"@tanzanite/deep-lock@npm:^1.1.1": + version: 1.1.1 + resolution: "@tanzanite/deep-lock@npm:1.1.1" + checksum: cde199ae80726ebe560c46e15fec852da8a00257affe4fc575048261b6f6db165f49dde308bbfadd91d0bc088e9efa93845ed92dc88a67dc35b1353797e14e1b + languageName: node + linkType: hard + +"@tanzanite/discord.js-minesweeper@npm:^1.2.0": + version: 1.2.0 + resolution: "@tanzanite/discord.js-minesweeper@npm:1.2.0" + checksum: 6612b41bd4b46f581e5ab00dc102c96d96eac731089708e5b3080b2d9fc14993ca4dd3ddb3e8b0f51339e72159d9fe27f39d0b4fe62adb014e7b909ee4d39a29 + languageName: node + linkType: hard + +"@tanzanite/events-intercept@npm:^3.1.0": + version: 3.1.0 + resolution: "@tanzanite/events-intercept@npm:3.1.0" + checksum: f76a6ac1b91348278a0b526ee7467b33596fd1a4a0bdfbb29e0a15407106dba1b5ccd1dd4bf24c2032f93548a31df1551d76b694410c76dbe8bec45f9435c580 + languageName: node + linkType: hard + +"@tanzanite/simplify-number@npm:^2.0.1": + version: 2.0.1 + resolution: "@tanzanite/simplify-number@npm:2.0.1" + checksum: 8061f944340e2599386dea30254fc363e5aed6dd17279508fec42d6c49e70858a56e1106b30b784589fa92616126a3932c51b79d37ac86f6d917e93389f0ddce + languageName: node + linkType: hard + +"@tanzanite/wolfram-alpha@npm:^1.1.0": + version: 1.1.0 + resolution: "@tanzanite/wolfram-alpha@npm:1.1.0" + checksum: 35bf790a2fa0e0964766eae3da643b8af254de7f94ee1ae3d52d2ac0138132895598a0a34abaacf769cac615155ea78cb403ebd16f7680c6086f7cdbf8936678 languageName: node linkType: hard @@ -480,35 +503,25 @@ __metadata: linkType: hard "@types/express-serve-static-core@npm:^4.17.18": - version: 4.17.30 - resolution: "@types/express-serve-static-core@npm:4.17.30" + version: 4.17.31 + resolution: "@types/express-serve-static-core@npm:4.17.31" dependencies: "@types/node": "*" "@types/qs": "*" "@types/range-parser": "*" - checksum: c40d9027884ab9e97fa29d9d41d1b75a5966109312e26594cf03c61b278b5bf8e095f53589e47899b34a2e224291a44043617695c3e8bd22284f988e48582ee6 + checksum: 009bfbe1070837454a1056aa710d0390ee5fb8c05dfe5a1691cc3e2ca88dc256f80e1ca27cb51a978681631d2f6431bfc9ec352ea46dd0c6eb183d0170bde5df languageName: node linkType: hard -"@types/express@npm:^4.17.13": - version: 4.17.13 - resolution: "@types/express@npm:4.17.13" +"@types/express@npm:^4.17.14": + version: 4.17.14 + resolution: "@types/express@npm:4.17.14" dependencies: "@types/body-parser": "*" "@types/express-serve-static-core": ^4.17.18 "@types/qs": "*" "@types/serve-static": "*" - checksum: 12a2a0e6c4b993fc0854bec665906788aea0d8ee4392389d7a98a5de1eefdd33c9e1e40a91f3afd274011119c506f7b4126acb97fae62ae20b654974d44cba12 - languageName: node - linkType: hard - -"@types/glob@npm:*": - version: 7.2.0 - resolution: "@types/glob@npm:7.2.0" - dependencies: - "@types/minimatch": "*" - "@types/node": "*" - checksum: 6ae717fedfdfdad25f3d5a568323926c64f52ef35897bcac8aca8e19bc50c0bd84630bbd063e5d52078b2137d8e7d3c26eabebd1a2f03ff350fff8a91e79fc19 + checksum: 15c1af46d02de834e4a225eccaa9d85c0370fdbb3ed4e1bc2d323d24872309961542b993ae236335aeb3e278630224a6ea002078d39e651d78a3b0356b1eaa79 languageName: node linkType: hard @@ -519,10 +532,19 @@ __metadata: languageName: node linkType: hard -"@types/lodash@npm:^4.14.184": - version: 4.14.184 - resolution: "@types/lodash@npm:4.14.184" - checksum: 6d9a4d67f7f9d0ec3fd21174f3dd3d00629dc1227eb469450eace53adbc1f7e2330699c28d0fe093e5f0fef0f0e763098be1f779268857213224af082b62be21 +"@types/lodash-es@npm:^4": + version: 4.17.6 + resolution: "@types/lodash-es@npm:4.17.6" + dependencies: + "@types/lodash": "*" + checksum: 9bd239dd525086e278821949ce12fbdd4f100a060fed9323fc7ad5661113e1641f28a7ebab617230ed3474680d8f4de705c1928b48252bb684be6ec9eed715db + languageName: node + linkType: hard + +"@types/lodash@npm:*": + version: 4.14.185 + resolution: "@types/lodash@npm:4.14.185" + checksum: f81d13da5ecab110ca9c5c7cc2bedc3c9802a6acf668576aecd1b8f4b134ed81d06c15f1e600fb08f05975098280a0d97d30cddfc2cb39ec1c6b56e971ca53b3 languageName: node linkType: hard @@ -533,13 +555,6 @@ __metadata: languageName: node linkType: hard -"@types/minimatch@npm:*": - version: 5.1.0 - resolution: "@types/minimatch@npm:5.1.0" - checksum: 041c1bf29a9c3d29ac401380cf94fa79613c8fccaa5b9e763996159e504bda4cb11e80a638f7842a987590f232864ba84a19f9b803d433cd92bac2759696dd24 - languageName: node - linkType: hard - "@types/ms@npm:*": version: 0.7.31 resolution: "@types/ms@npm:0.7.31" @@ -547,10 +562,20 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:*, @types/node@npm:^18.7.13": - version: 18.7.13 - resolution: "@types/node@npm:18.7.13" - checksum: 45431e7e89ecaf85c7d2c180d801c132a7c59e2f8ad578726b6d71cc74e3267c18f9ccdcad738bc0479790c078f0c79efb0e58da2c6be535c15995dbb19050c9 +"@types/node-fetch@npm:^2.6.2": + version: 2.6.2 + resolution: "@types/node-fetch@npm:2.6.2" + dependencies: + "@types/node": "*" + form-data: ^3.0.0 + checksum: 6f73b1470000d303d25a6fb92875ea837a216656cb7474f66cdd67bb014aa81a5a11e7ac9c21fe19bee9ecb2ef87c1962bceeaec31386119d1ac86e4c30ad7a6 + languageName: node + linkType: hard + +"@types/node@npm:*, @types/node@npm:^18.7.21": + version: 18.7.21 + resolution: "@types/node@npm:18.7.21" + checksum: 9e0558dd44766fb3b1b26815e9877849d8f1188115e0d2bee540cee2c5a11f90e4c00ee059edae8ea018e7f66217e4bf53271536c9a6fd05e33f713b8daa4c66 languageName: node linkType: hard @@ -572,10 +597,10 @@ __metadata: languageName: node linkType: hard -"@types/prettier@npm:^2.7.0": - version: 2.7.0 - resolution: "@types/prettier@npm:2.7.0" - checksum: bf5d0c7c1270909b39399539ac106d20ddaa85fe92eb1d59922dc99159604b4f8d5e41b0045fb29c8011585cf5bca2350b7441ef3d9816c08bd0e10ebd4b31d4 +"@types/prettier@npm:^2.7.1": + version: 2.7.1 + resolution: "@types/prettier@npm:2.7.1" + checksum: 5e3f58e229d6c73b5f5cae2e8f96c1c4a5b5805f83459e17a045ba8e96152b1d38e86b63e3172fb159dac923388699660862b75b2d37e54220805f0e691e26f1 languageName: node linkType: hard @@ -593,16 +618,6 @@ __metadata: languageName: node linkType: hard -"@types/rimraf@npm:^3.0.2": - version: 3.0.2 - resolution: "@types/rimraf@npm:3.0.2" - dependencies: - "@types/glob": "*" - "@types/node": "*" - checksum: b47fa302f46434cba704d20465861ad250df79467d3d289f9d6490d3aeeb41e8cb32dd80bd1a8fd833d1e185ac719fbf9be12e05ad9ce9be094d8ee8f1405347 - languageName: node - linkType: hard - "@types/serve-static@npm:*": version: 1.15.0 resolution: "@types/serve-static@npm:1.15.0" @@ -620,10 +635,10 @@ __metadata: languageName: node linkType: hard -"@types/validator@npm:^13.7.1, @types/validator@npm:^13.7.5": - version: 13.7.6 - resolution: "@types/validator@npm:13.7.6" - checksum: f860dd87bc5f90cc33d2802cf2a4da307ddac63c86d86b858a85dd62d457c8fa42fc8756e05b5d24e741376debc13d46ed14bc700006bf7d0cb910118022492b +"@types/validator@npm:^13.7.1, @types/validator@npm:^13.7.7": + version: 13.7.7 + resolution: "@types/validator@npm:13.7.7" + checksum: e32d2bc3b86317da2dff7565e371a2bd0108cdbd7c0b426070c9b9fab30cf07572eea4fb193cb7420261b2eb99b2fc112bf021d48f98eee181844a47533f817a languageName: node linkType: hard @@ -636,15 +651,14 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:^5.35.1": - version: 5.35.1 - resolution: "@typescript-eslint/eslint-plugin@npm:5.35.1" +"@typescript-eslint/eslint-plugin@npm:^5.38.0": + version: 5.38.0 + resolution: "@typescript-eslint/eslint-plugin@npm:5.38.0" dependencies: - "@typescript-eslint/scope-manager": 5.35.1 - "@typescript-eslint/type-utils": 5.35.1 - "@typescript-eslint/utils": 5.35.1 + "@typescript-eslint/scope-manager": 5.38.0 + "@typescript-eslint/type-utils": 5.38.0 + "@typescript-eslint/utils": 5.38.0 debug: ^4.3.4 - functional-red-black-tree: ^1.0.1 ignore: ^5.2.0 regexpp: ^3.2.0 semver: ^7.3.7 @@ -655,53 +669,54 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 073f4dffd863881f1c87e1c217ac13bda44aaa2db12ef260032b5e8eb6ffd6b9cf6f62c85132dbf84152f353c435c66dd4f75c3bcb86eb23e926737aa4fb66fa + checksum: e9cd1970c7c8a438aee912cf00aa27bdcde0a0fb57bbfe70eccda93eefa5b4fb4c7ebf5ba7a51744c1ec2b4df3a72b8dcd19dc17a9c3e4e3435f631ac6b10a6a languageName: node linkType: hard "@typescript-eslint/experimental-utils@npm:^5.0.0": - version: 5.35.1 - resolution: "@typescript-eslint/experimental-utils@npm:5.35.1" + version: 5.38.0 + resolution: "@typescript-eslint/experimental-utils@npm:5.38.0" dependencies: - "@typescript-eslint/utils": 5.35.1 + "@typescript-eslint/utils": 5.38.0 peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: b67f347e45e719080ac6460c7624db2d1eb1c02abd1d031cfb96f2b0cd8a0cfff3cec71a3913fd0da20c045ec9e0b888e3ef1e50bcb606e2644b8e4f91cce1c5 + checksum: c1cf9c74550ff2e9f988c15ca343625037c522583017e1b97f56a19d2c539b617934f34ce7aaa323ea4d26408410a761eea4e4e34c9036a270070c49e44d2571 languageName: node linkType: hard -"@typescript-eslint/parser@npm:^5.35.1": - version: 5.35.1 - resolution: "@typescript-eslint/parser@npm:5.35.1" +"@typescript-eslint/parser@npm:^5.38.0": + version: 5.38.0 + resolution: "@typescript-eslint/parser@npm:5.38.0" dependencies: - "@typescript-eslint/scope-manager": 5.35.1 - "@typescript-eslint/types": 5.35.1 - "@typescript-eslint/typescript-estree": 5.35.1 + "@typescript-eslint/scope-manager": 5.38.0 + "@typescript-eslint/types": 5.38.0 + "@typescript-eslint/typescript-estree": 5.38.0 debug: ^4.3.4 peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: typescript: optional: true - checksum: 57ea1a1da60b370f8d5c11c86155f7339359a90f2c59e34c89f626f1a79cb440248f07bd307a27ebbbcc997d2731cb9754cdbc37639770940521a938dd89870c + checksum: d5fb2d8f3a25cd6ff31326c665db4617f2d428247cad690f0404de440abbcfc7261528f54d642d2b121aae34aadecb55a24b72c8ef341cafdc7b2bbcbf7dae8d languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:5.35.1": - version: 5.35.1 - resolution: "@typescript-eslint/scope-manager@npm:5.35.1" +"@typescript-eslint/scope-manager@npm:5.38.0": + version: 5.38.0 + resolution: "@typescript-eslint/scope-manager@npm:5.38.0" dependencies: - "@typescript-eslint/types": 5.35.1 - "@typescript-eslint/visitor-keys": 5.35.1 - checksum: 5a969a081309bac5962f99ee6dfdfd9c68ea677bc79d9796592dce82a36217f67aa55c7bf421b2c97b46c5149d6a9401bb4c57829595e8c19f47cfa9e8c2dd86 + "@typescript-eslint/types": 5.38.0 + "@typescript-eslint/visitor-keys": 5.38.0 + checksum: a34d2976e9c755b853b6524e0b9fb1da237340ddff9f6839a51ba37998527c02d0f2f16ffc3d4baa47898f2bb7eb85a6749d6ca588c0461dbd654d8f9925dd0f languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:5.35.1": - version: 5.35.1 - resolution: "@typescript-eslint/type-utils@npm:5.35.1" +"@typescript-eslint/type-utils@npm:5.38.0": + version: 5.38.0 + resolution: "@typescript-eslint/type-utils@npm:5.38.0" dependencies: - "@typescript-eslint/utils": 5.35.1 + "@typescript-eslint/typescript-estree": 5.38.0 + "@typescript-eslint/utils": 5.38.0 debug: ^4.3.4 tsutils: ^3.21.0 peerDependencies: @@ -709,23 +724,23 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: af317ba156f2767f76a7f97193873a00468370e157fdcc6ac19f664bc6c4c0a6836bd25028d17fdd54d339b6842fda68b82f1ce4142a222de6953625ea6c0a9c + checksum: 43f2f55329b2357bedf158a93a469d058a11c69f8f88ff891080b8cb5977bffe8d679923bce7048cbc076c083e0f5741c83b761355309d606cc4e217e1da4208 languageName: node linkType: hard -"@typescript-eslint/types@npm:5.35.1": - version: 5.35.1 - resolution: "@typescript-eslint/types@npm:5.35.1" - checksum: a4e1001867f43f3364b109fc5a07b91ae7a34b78ab191c6c5c4695dac9bb2b80b0a602651c0b807c1c7c1fc3656d2bbd47c637afa08a09e7b1c39eae3c489e00 +"@typescript-eslint/types@npm:5.38.0": + version: 5.38.0 + resolution: "@typescript-eslint/types@npm:5.38.0" + checksum: 03aec1de64417e60830c6d33bb4f1bf4402411080371013513f55c7a2fadb6f8745a89a7604cde03d89aa53307f94bc913060c5897ed93285247e4c39af43a00 languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:5.35.1": - version: 5.35.1 - resolution: "@typescript-eslint/typescript-estree@npm:5.35.1" +"@typescript-eslint/typescript-estree@npm:5.38.0": + version: 5.38.0 + resolution: "@typescript-eslint/typescript-estree@npm:5.38.0" dependencies: - "@typescript-eslint/types": 5.35.1 - "@typescript-eslint/visitor-keys": 5.35.1 + "@typescript-eslint/types": 5.38.0 + "@typescript-eslint/visitor-keys": 5.38.0 debug: ^4.3.4 globby: ^11.1.0 is-glob: ^4.0.3 @@ -734,33 +749,33 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: a917ca4753a3f92c8d8555c96f5414383a9742761625476fa36a019401543aa74996159afa0f7fc7fae05fe0f904e3c6f4153a55412070c8a94e8171e81084c7 + checksum: 174461c91e49a0340945da2d31e38ec175cd90b2b5068f3c925518cc9182100fe1435d3225908a52f62257e97bc2b995cbc6b6bd1b7143ff0a0e4b483bd70834 languageName: node linkType: hard -"@typescript-eslint/utils@npm:5.35.1": - version: 5.35.1 - resolution: "@typescript-eslint/utils@npm:5.35.1" +"@typescript-eslint/utils@npm:5.38.0": + version: 5.38.0 + resolution: "@typescript-eslint/utils@npm:5.38.0" dependencies: "@types/json-schema": ^7.0.9 - "@typescript-eslint/scope-manager": 5.35.1 - "@typescript-eslint/types": 5.35.1 - "@typescript-eslint/typescript-estree": 5.35.1 + "@typescript-eslint/scope-manager": 5.38.0 + "@typescript-eslint/types": 5.38.0 + "@typescript-eslint/typescript-estree": 5.38.0 eslint-scope: ^5.1.1 eslint-utils: ^3.0.0 peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: 2b04092583c3139dd090727c24fb9d7fdb1fb9f20f2e3f0141cab5b98b6a1934b0fc8cab948f7faae55588385b0f1fb7bbf91f52c705ce4528036a527c3119c6 + checksum: c927a68d4ff5029ed3dbc7e6e87702f7cdfba26452ccf401b37cc68f6e5cca72eb884831dbc7957512998d59950b1852b2ecea19f174a20fe659d851b4afd4fd languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:5.35.1": - version: 5.35.1 - resolution: "@typescript-eslint/visitor-keys@npm:5.35.1" +"@typescript-eslint/visitor-keys@npm:5.38.0": + version: 5.38.0 + resolution: "@typescript-eslint/visitor-keys@npm:5.38.0" dependencies: - "@typescript-eslint/types": 5.35.1 + "@typescript-eslint/types": 5.38.0 eslint-visitor-keys: ^3.3.0 - checksum: ef3c8377aac89935b5cc2fcf37bb3e42aa5f98848e7c22bdcbe5bb06c0fe8a1373a6897fd21109be8929b4708ad06c8874d2ef7bba17ff64911964203457330d + checksum: cc3d0c6eb0c9a20a25d66b640d759cb1b52f8df485f16d948218d63d798b5c0672ef298f5dae5e5327ec021c0f8369d1da5d26b9c16a245a20fa44a9365956bc languageName: node linkType: hard @@ -899,6 +914,13 @@ __metadata: languageName: node linkType: hard +"asynckit@npm:^0.4.0": + version: 0.4.0 + resolution: "asynckit@npm:0.4.0" + checksum: 7b78c451df768adba04e2d02e63e2d0bf3b07adcd6e42b4cf665cb7ce899bedd344c69a1dcbce355b5f972d597b25aaa1c1742b52cffd9caccb22f348114f6be + languageName: node + linkType: hard + "balanced-match@npm:^1.0.0": version: 1.0.2 resolution: "balanced-match@npm:1.0.2" @@ -906,7 +928,7 @@ __metadata: languageName: node linkType: hard -"base64-js@npm:^1.3.0, base64-js@npm:^1.3.1": +"base64-js@npm:^1.3.0": version: 1.5.1 resolution: "base64-js@npm:1.5.1" checksum: 669632eb3745404c2f822a18fc3a0122d2f9a7a13f7fb8b5823ee19d1d2ff9ee5b52c53367176ea4ad093c332fd5ab4bd0ebae5a8e27917a4105a4cfc86b1005 @@ -920,16 +942,6 @@ __metadata: languageName: node linkType: hard -"bl@npm:^1.0.0": - version: 1.2.3 - resolution: "bl@npm:1.2.3" - dependencies: - readable-stream: ^2.3.5 - safe-buffer: ^5.1.1 - checksum: 123f097989ce2fa9087ce761cd41176aaaec864e28f7dfe5c7dab8ae16d66d9844f849c3ad688eb357e3c5e4f49b573e3c0780bb8bc937206735a3b6f8569a5f - languageName: node - linkType: hard - "brace-expansion@npm:^1.1.7": version: 1.1.11 resolution: "brace-expansion@npm:1.1.11" @@ -958,30 +970,6 @@ __metadata: languageName: node linkType: hard -"buffer-alloc-unsafe@npm:^1.1.0": - version: 1.1.0 - resolution: "buffer-alloc-unsafe@npm:1.1.0" - checksum: c5e18bf51f67754ec843c9af3d4c005051aac5008a3992938dda1344e5cfec77c4b02b4ca303644d1e9a6e281765155ce6356d85c6f5ccc5cd21afc868def396 - languageName: node - linkType: hard - -"buffer-alloc@npm:^1.2.0": - version: 1.2.0 - resolution: "buffer-alloc@npm:1.2.0" - dependencies: - buffer-alloc-unsafe: ^1.1.0 - buffer-fill: ^1.0.0 - checksum: 560cd27f3cbe73c614867da373407d4506309c62fe18de45a1ce191f3785ec6ca2488d802ff82065798542422980ca25f903db078c57822218182c37c3576df5 - languageName: node - linkType: hard - -"buffer-crc32@npm:~0.2.3": - version: 0.2.13 - resolution: "buffer-crc32@npm:0.2.13" - checksum: 06252347ae6daca3453b94e4b2f1d3754a3b146a111d81c68924c22d91889a40623264e95e67955b1cb4a68cbedf317abeabb5140a9766ed248973096db5ce1c - languageName: node - linkType: hard - "buffer-equal-constant-time@npm:1.0.1": version: 1.0.1 resolution: "buffer-equal-constant-time@npm:1.0.1" @@ -989,13 +977,6 @@ __metadata: languageName: node linkType: hard -"buffer-fill@npm:^1.0.0": - version: 1.0.0 - resolution: "buffer-fill@npm:1.0.0" - checksum: c29b4723ddeab01e74b5d3b982a0c6828f2ded49cef049ddca3dac661c874ecdbcecb5dd8380cf0f4adbeb8cff90a7de724126750a1f1e5ebd4eb6c59a1315b1 - languageName: node - linkType: hard - "buffer-writer@npm:2.0.0": version: 2.0.0 resolution: "buffer-writer@npm:2.0.0" @@ -1003,16 +984,6 @@ __metadata: languageName: node linkType: hard -"buffer@npm:^5.2.1": - version: 5.7.1 - resolution: "buffer@npm:5.7.1" - dependencies: - base64-js: ^1.3.1 - ieee754: ^1.1.13 - checksum: e2cf8429e1c4c7b8cbd30834ac09bd61da46ce35f5c22a78e6c2f04497d6d25541b16881e30a019c6fd3154150650ccee27a308eff3e26229d788bbdeb08ab84 - languageName: node - linkType: hard - "cacache@npm:^16.1.0": version: 16.1.3 resolution: "cacache@npm:16.1.3" @@ -1134,10 +1105,12 @@ __metadata: languageName: node linkType: hard -"commander@npm:^2.8.1": - version: 2.20.3 - resolution: "commander@npm:2.20.3" - checksum: ab8c07884e42c3a8dbc5dd9592c606176c7eb5c1ca5ff274bcf907039b2c41de3626f684ea75ccf4d361ba004bbaff1f577d5384c155f3871e456bdf27becf9e +"combined-stream@npm:^1.0.8": + version: 1.0.8 + resolution: "combined-stream@npm:1.0.8" + dependencies: + delayed-stream: ~1.0.0 + checksum: 49fa4aeb4916567e33ea81d088f6584749fc90c7abec76fd516bf1c5aa5c79f3584b5ba3de6b86d26ddd64bae5329c4c7479343250cfe71c75bb366eae53bb7c languageName: node linkType: hard @@ -1169,24 +1142,6 @@ __metadata: languageName: node linkType: hard -"core-util-is@npm:~1.0.0": - version: 1.0.3 - resolution: "core-util-is@npm:1.0.3" - checksum: 9de8597363a8e9b9952491ebe18167e3b36e7707569eed0ebf14f8bba773611376466ae34575bca8cfe3c767890c859c74056084738f09d4e4a6f902b2ad7d99 - languageName: node - linkType: hard - -"cross-spawn@npm:^5.0.1": - version: 5.1.0 - resolution: "cross-spawn@npm:5.1.0" - dependencies: - lru-cache: ^4.0.1 - shebang-command: ^1.2.0 - which: ^1.2.9 - checksum: 726939c9954fc70c20e538923feaaa33bebc253247d13021737c3c7f68cdc3e0a57f720c0fe75057c0387995349f3f12e20e9bfdbf12274db28019c7ea4ec166 - languageName: node - linkType: hard - "cross-spawn@npm:^7.0.2": version: 7.0.3 resolution: "cross-spawn@npm:7.0.3" @@ -1211,72 +1166,9 @@ __metadata: linkType: hard "decimal.js@npm:^10.4.0": - version: 10.4.0 - resolution: "decimal.js@npm:10.4.0" - checksum: 98702d9d817a9e5b3767ea6580e7f3b35544b9454e463a5dd5d3232131470f39067d02864c45cab009eb1200bc162cd26a33d34c622cd79e4657a3e25e95fb4e - languageName: node - linkType: hard - -"decompress-tar@npm:^4.0.0, decompress-tar@npm:^4.1.0, decompress-tar@npm:^4.1.1": - version: 4.1.1 - resolution: "decompress-tar@npm:4.1.1" - dependencies: - file-type: ^5.2.0 - is-stream: ^1.1.0 - tar-stream: ^1.5.2 - checksum: 42d5360b558a28dd884e1bf809e3fea92b9910fda5151add004d4a64cc76ac124e8b3e9117e805f2349af9e49c331d873e6fc5ad86a00e575703fee632b0a225 - languageName: node - linkType: hard - -"decompress-tarbz2@npm:^4.0.0": - version: 4.1.1 - resolution: "decompress-tarbz2@npm:4.1.1" - dependencies: - decompress-tar: ^4.1.0 - file-type: ^6.1.0 - is-stream: ^1.1.0 - seek-bzip: ^1.0.5 - unbzip2-stream: ^1.0.9 - checksum: 519c81337730159a1f2d7072a6ee8523ffd76df48d34f14c27cb0a27f89b4e2acf75dad2f761838e5bc63230cea1ac154b092ecb7504be4e93f7d0e32ddd6aff - languageName: node - linkType: hard - -"decompress-targz@npm:^4.0.0": - version: 4.1.1 - resolution: "decompress-targz@npm:4.1.1" - dependencies: - decompress-tar: ^4.1.1 - file-type: ^5.2.0 - is-stream: ^1.1.0 - checksum: 22738f58eb034568dc50d370c03b346c428bfe8292fe56165847376b5af17d3c028fefca82db642d79cb094df4c0a599d40a8f294b02aad1d3ddec82f3fd45d4 - languageName: node - linkType: hard - -"decompress-unzip@npm:^4.0.1": - version: 4.0.1 - resolution: "decompress-unzip@npm:4.0.1" - dependencies: - file-type: ^3.8.0 - get-stream: ^2.2.0 - pify: ^2.3.0 - yauzl: ^2.4.2 - checksum: ba9f3204ab2415bedb18d796244928a18148ef40dbb15174d0d01e5991b39536b03d02800a8a389515a1523f8fb13efc7cd44697df758cd06c674879caefd62b - languageName: node - linkType: hard - -"decompress@npm:^4.1.0": - version: 4.2.1 - resolution: "decompress@npm:4.2.1" - dependencies: - decompress-tar: ^4.0.0 - decompress-tarbz2: ^4.0.0 - decompress-targz: ^4.0.0 - decompress-unzip: ^4.0.1 - graceful-fs: ^4.1.10 - make-dir: ^1.0.0 - pify: ^2.3.0 - strip-dirs: ^2.0.0 - checksum: 8247a31c6db7178413715fdfb35a482f019c81dfcd6e8e623d9f0382c9889ce797ce0144de016b256ed03298907a620ce81387cca0e69067a933470081436cb8 + version: 10.4.1 + resolution: "decimal.js@npm:10.4.1" + checksum: 5da6dc74af5b73d954741b24d404ef6da07841794d9e51412a2708ec384dd7b4bced3365fb178f4cd119b7ef45f0b34344014a4dc0494c8374c5e746df3cb410 languageName: node linkType: hard @@ -1296,10 +1188,10 @@ __metadata: languageName: node linkType: hard -"deep-lock@npm:^1.0.0": +"delayed-stream@npm:~1.0.0": version: 1.0.0 - resolution: "deep-lock@npm:1.0.0" - checksum: e099a64c8c65c142285cea08b122385ba3a88ee37d246ef50e67028ef8da8f4d5b6a42b6129a9d043d2c65ba217b58e955053b16af3d8243c8adcb3721113a0b + resolution: "delayed-stream@npm:1.0.0" + checksum: 46fe6e83e2cb1d85ba50bd52803c68be9bd953282fa7096f51fc29edd5d67ff84ff753c51966061e5ba7cb5e47ef6d36a91924eddb7f3f3483b1c560f77a0020 languageName: node linkType: hard @@ -1326,52 +1218,29 @@ __metadata: languageName: node linkType: hard -"discord-akairo@npm:@notenoughupdates/discord-akairo@dev": - version: 10.0.0-dev.1661396495.d2761d1 - resolution: "@notenoughupdates/discord-akairo@npm:10.0.0-dev.1661396495.d2761d1" - dependencies: - zod: ^3.17.10 - checksum: 69e3dd4d5d0e548f6f1c8075251fe3d386e69f1ef5089e80893057e314bf4d3513855baaa4e17e94d6f862973b19cef171f873b17cdee02ff45dd7a699d7685d +"discord-api-types@npm:0.37.10, discord-api-types@npm:^0.37.10, discord-api-types@npm:^0.37.3": + version: 0.37.10 + resolution: "discord-api-types@npm:0.37.10" + checksum: a09460953a02adf3c40a486e3946c6ca0dd6c76d279444a4595abecfe8b5b88ad49d48b5eeb85e2cb95256a0824ee075cb7550d371c13e64e37ec703206c092b languageName: node linkType: hard -"discord-api-types@npm:0.37.1": - version: 0.37.1 - resolution: "discord-api-types@npm:0.37.1" - checksum: 4adfd605dae426299b537105f657b4649535f446202051aaef0ccbbaf024175bdbe868487119480ff094c8e479c2e424ce393f2595b16e1840cc0f3a555e510f - languageName: node - linkType: hard - -"discord-api-types@npm:^0.36.3": - version: 0.36.3 - resolution: "discord-api-types@npm:0.36.3" - checksum: 3089c0fb37425dc5df03c76d82988d43fcc272699b06a02fc830d0a3bef550009aaebdf6d646529e8a7ccea76ae3f43b099d736ea5ef37a0be143142ab49871d - languageName: node - linkType: hard - -"discord-api-types@npm:^0.37.3": - version: 0.37.5 - resolution: "discord-api-types@npm:0.37.5" - checksum: 10a23b813d9a30d836aa27169f768883eeed971ab67e58325ff272bfe6b63b169d727ae983d7c1b69c7e0b02ed9072e66e79319ce48174b36cd4a2a5fafe2e9f - languageName: node - linkType: hard - -"discord.js@npm:@notenoughupdates/discord.js@forum": - version: 14.1.2-forum - resolution: "@notenoughupdates/discord.js@npm:14.1.2-forum" +"discord.js@npm:@notenoughupdates/discord.js@dev": + version: 14.5.1-dev.1664157090-edb72ba.0 + resolution: "@notenoughupdates/discord.js@npm:14.5.1-dev.1664157090-edb72ba.0" dependencies: - "@discordjs/builders": "workspace:^" - "@discordjs/collection": "workspace:^" - "@discordjs/rest": "workspace:^" + "@discordjs/builders": ^1.2.0 + "@discordjs/collection": ^1.1.0 + "@discordjs/rest": ^1.2.0 "@sapphire/snowflake": ^3.2.2 "@types/ws": ^8.5.3 - discord-api-types: ^0.36.3 + discord-api-types: ^0.37.10 fast-deep-equal: ^3.1.3 lodash.snakecase: ^4.1.1 tslib: ^2.4.0 - undici: ^5.8.0 - ws: ^8.8.1 - checksum: abcc01e4b72d89875db497cf8bd75d2f0b5b09946671f8ffad5f2c9e7cfbec82912bfe354e561a6b709fcf6a7261bf79d622122487c04acd76d681a74bda1d3f + undici: ^5.10.0 + ws: ^8.9.0 + checksum: a5fb3a8b269ad9c279e3839a1dda4d6ce9e54e6eef4cc9a4a08996cddb49c045faa347e3696d1ed58432e7707b886298ada1b2c4ed2522edbd9d0bfc71d53abc languageName: node linkType: hard @@ -1416,15 +1285,6 @@ __metadata: languageName: node linkType: hard -"end-of-stream@npm:^1.0.0": - version: 1.4.4 - resolution: "end-of-stream@npm:1.4.4" - dependencies: - once: ^1.4.0 - checksum: 530a5a5a1e517e962854a31693dbb5c0b2fc40b46dad2a56a2deec656ca040631124f4795823acc68238147805f8b021abbe221f4afed5ef3c8e8efc2024908b - languageName: node - linkType: hard - "env-paths@npm:^2.2.0": version: 2.2.1 resolution: "env-paths@npm:2.2.1" @@ -1439,172 +1299,175 @@ __metadata: languageName: node linkType: hard -"esbuild-android-64@npm:0.14.54": - version: 0.14.54 - resolution: "esbuild-android-64@npm:0.14.54" +"esbuild-android-64@npm:0.15.9": + version: 0.15.9 + resolution: "esbuild-android-64@npm:0.15.9" conditions: os=android & cpu=x64 languageName: node linkType: hard -"esbuild-android-arm64@npm:0.14.54": - version: 0.14.54 - resolution: "esbuild-android-arm64@npm:0.14.54" +"esbuild-android-arm64@npm:0.15.9": + version: 0.15.9 + resolution: "esbuild-android-arm64@npm:0.15.9" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"esbuild-darwin-64@npm:0.14.54": - version: 0.14.54 - resolution: "esbuild-darwin-64@npm:0.14.54" +"esbuild-darwin-64@npm:0.15.9": + version: 0.15.9 + resolution: "esbuild-darwin-64@npm:0.15.9" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"esbuild-darwin-arm64@npm:0.14.54": - version: 0.14.54 - resolution: "esbuild-darwin-arm64@npm:0.14.54" +"esbuild-darwin-arm64@npm:0.15.9": + version: 0.15.9 + resolution: "esbuild-darwin-arm64@npm:0.15.9" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"esbuild-freebsd-64@npm:0.14.54": - version: 0.14.54 - resolution: "esbuild-freebsd-64@npm:0.14.54" +"esbuild-freebsd-64@npm:0.15.9": + version: 0.15.9 + resolution: "esbuild-freebsd-64@npm:0.15.9" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"esbuild-freebsd-arm64@npm:0.14.54": - version: 0.14.54 - resolution: "esbuild-freebsd-arm64@npm:0.14.54" +"esbuild-freebsd-arm64@npm:0.15.9": + version: 0.15.9 + resolution: "esbuild-freebsd-arm64@npm:0.15.9" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"esbuild-linux-32@npm:0.14.54": - version: 0.14.54 - resolution: "esbuild-linux-32@npm:0.14.54" +"esbuild-linux-32@npm:0.15.9": + version: 0.15.9 + resolution: "esbuild-linux-32@npm:0.15.9" conditions: os=linux & cpu=ia32 languageName: node linkType: hard -"esbuild-linux-64@npm:0.14.54": - version: 0.14.54 - resolution: "esbuild-linux-64@npm:0.14.54" +"esbuild-linux-64@npm:0.15.9": + version: 0.15.9 + resolution: "esbuild-linux-64@npm:0.15.9" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"esbuild-linux-arm64@npm:0.14.54": - version: 0.14.54 - resolution: "esbuild-linux-arm64@npm:0.14.54" +"esbuild-linux-arm64@npm:0.15.9": + version: 0.15.9 + resolution: "esbuild-linux-arm64@npm:0.15.9" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"esbuild-linux-arm@npm:0.14.54": - version: 0.14.54 - resolution: "esbuild-linux-arm@npm:0.14.54" +"esbuild-linux-arm@npm:0.15.9": + version: 0.15.9 + resolution: "esbuild-linux-arm@npm:0.15.9" conditions: os=linux & cpu=arm languageName: node linkType: hard -"esbuild-linux-mips64le@npm:0.14.54": - version: 0.14.54 - resolution: "esbuild-linux-mips64le@npm:0.14.54" +"esbuild-linux-mips64le@npm:0.15.9": + version: 0.15.9 + resolution: "esbuild-linux-mips64le@npm:0.15.9" conditions: os=linux & cpu=mips64el languageName: node linkType: hard -"esbuild-linux-ppc64le@npm:0.14.54": - version: 0.14.54 - resolution: "esbuild-linux-ppc64le@npm:0.14.54" +"esbuild-linux-ppc64le@npm:0.15.9": + version: 0.15.9 + resolution: "esbuild-linux-ppc64le@npm:0.15.9" conditions: os=linux & cpu=ppc64 languageName: node linkType: hard -"esbuild-linux-riscv64@npm:0.14.54": - version: 0.14.54 - resolution: "esbuild-linux-riscv64@npm:0.14.54" +"esbuild-linux-riscv64@npm:0.15.9": + version: 0.15.9 + resolution: "esbuild-linux-riscv64@npm:0.15.9" conditions: os=linux & cpu=riscv64 languageName: node linkType: hard -"esbuild-linux-s390x@npm:0.14.54": - version: 0.14.54 - resolution: "esbuild-linux-s390x@npm:0.14.54" +"esbuild-linux-s390x@npm:0.15.9": + version: 0.15.9 + resolution: "esbuild-linux-s390x@npm:0.15.9" conditions: os=linux & cpu=s390x languageName: node linkType: hard -"esbuild-netbsd-64@npm:0.14.54": - version: 0.14.54 - resolution: "esbuild-netbsd-64@npm:0.14.54" +"esbuild-netbsd-64@npm:0.15.9": + version: 0.15.9 + resolution: "esbuild-netbsd-64@npm:0.15.9" conditions: os=netbsd & cpu=x64 languageName: node linkType: hard -"esbuild-openbsd-64@npm:0.14.54": - version: 0.14.54 - resolution: "esbuild-openbsd-64@npm:0.14.54" +"esbuild-openbsd-64@npm:0.15.9": + version: 0.15.9 + resolution: "esbuild-openbsd-64@npm:0.15.9" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard -"esbuild-sunos-64@npm:0.14.54": - version: 0.14.54 - resolution: "esbuild-sunos-64@npm:0.14.54" +"esbuild-sunos-64@npm:0.15.9": + version: 0.15.9 + resolution: "esbuild-sunos-64@npm:0.15.9" conditions: os=sunos & cpu=x64 languageName: node linkType: hard -"esbuild-windows-32@npm:0.14.54": - version: 0.14.54 - resolution: "esbuild-windows-32@npm:0.14.54" +"esbuild-windows-32@npm:0.15.9": + version: 0.15.9 + resolution: "esbuild-windows-32@npm:0.15.9" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"esbuild-windows-64@npm:0.14.54": - version: 0.14.54 - resolution: "esbuild-windows-64@npm:0.14.54" +"esbuild-windows-64@npm:0.15.9": + version: 0.15.9 + resolution: "esbuild-windows-64@npm:0.15.9" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"esbuild-windows-arm64@npm:0.14.54": - version: 0.14.54 - resolution: "esbuild-windows-arm64@npm:0.14.54" +"esbuild-windows-arm64@npm:0.15.9": + version: 0.15.9 + resolution: "esbuild-windows-arm64@npm:0.15.9" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"esbuild@npm:^0.14.47": - version: 0.14.54 - resolution: "esbuild@npm:0.14.54" - dependencies: - "@esbuild/linux-loong64": 0.14.54 - esbuild-android-64: 0.14.54 - esbuild-android-arm64: 0.14.54 - esbuild-darwin-64: 0.14.54 - esbuild-darwin-arm64: 0.14.54 - esbuild-freebsd-64: 0.14.54 - esbuild-freebsd-arm64: 0.14.54 - esbuild-linux-32: 0.14.54 - esbuild-linux-64: 0.14.54 - esbuild-linux-arm: 0.14.54 - esbuild-linux-arm64: 0.14.54 - esbuild-linux-mips64le: 0.14.54 - esbuild-linux-ppc64le: 0.14.54 - esbuild-linux-riscv64: 0.14.54 - esbuild-linux-s390x: 0.14.54 - esbuild-netbsd-64: 0.14.54 - esbuild-openbsd-64: 0.14.54 - esbuild-sunos-64: 0.14.54 - esbuild-windows-32: 0.14.54 - esbuild-windows-64: 0.14.54 - esbuild-windows-arm64: 0.14.54 +"esbuild@npm:^0.15.6": + version: 0.15.9 + resolution: "esbuild@npm:0.15.9" + dependencies: + "@esbuild/android-arm": 0.15.9 + "@esbuild/linux-loong64": 0.15.9 + esbuild-android-64: 0.15.9 + esbuild-android-arm64: 0.15.9 + esbuild-darwin-64: 0.15.9 + esbuild-darwin-arm64: 0.15.9 + esbuild-freebsd-64: 0.15.9 + esbuild-freebsd-arm64: 0.15.9 + esbuild-linux-32: 0.15.9 + esbuild-linux-64: 0.15.9 + esbuild-linux-arm: 0.15.9 + esbuild-linux-arm64: 0.15.9 + esbuild-linux-mips64le: 0.15.9 + esbuild-linux-ppc64le: 0.15.9 + esbuild-linux-riscv64: 0.15.9 + esbuild-linux-s390x: 0.15.9 + esbuild-netbsd-64: 0.15.9 + esbuild-openbsd-64: 0.15.9 + esbuild-sunos-64: 0.15.9 + esbuild-windows-32: 0.15.9 + esbuild-windows-64: 0.15.9 + esbuild-windows-arm64: 0.15.9 dependenciesMeta: + "@esbuild/android-arm": + optional: true "@esbuild/linux-loong64": optional: true esbuild-android-64: @@ -1649,7 +1512,7 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: 49e360b1185c797f5ca3a7f5f0a75121494d97ddf691f65ed1796e6257d318f928342a97f559bb8eced6a90cf604dd22db4a30e0dbbf15edd9dbf22459b639af + checksum: 33da8cc0c7bd03f1acfafd51812638fd75ae01f95db3737dbc4d94a9f9e9c234df2ffc76c0fa7147f2466c342b7085f835edc93b3a3dac84aaa1cf46ed80b980 languageName: node linkType: hard @@ -1737,12 +1600,12 @@ __metadata: languageName: node linkType: hard -"eslint@npm:^8.23.0": - version: 8.23.0 - resolution: "eslint@npm:8.23.0" +"eslint@npm:^8.24.0": + version: 8.24.0 + resolution: "eslint@npm:8.24.0" dependencies: - "@eslint/eslintrc": ^1.3.1 - "@humanwhocodes/config-array": ^0.10.4 + "@eslint/eslintrc": ^1.3.2 + "@humanwhocodes/config-array": ^0.10.5 "@humanwhocodes/gitignore-to-minimatch": ^1.0.2 "@humanwhocodes/module-importer": ^1.0.1 ajv: ^6.10.0 @@ -1760,7 +1623,6 @@ __metadata: fast-deep-equal: ^3.1.3 file-entry-cache: ^6.0.1 find-up: ^5.0.0 - functional-red-black-tree: ^1.0.1 glob-parent: ^6.0.1 globals: ^13.15.0 globby: ^11.1.0 @@ -1769,6 +1631,7 @@ __metadata: import-fresh: ^3.0.0 imurmurhash: ^0.1.4 is-glob: ^4.0.0 + js-sdsl: ^4.1.4 js-yaml: ^4.1.0 json-stable-stringify-without-jsonify: ^1.0.1 levn: ^0.4.1 @@ -1782,7 +1645,7 @@ __metadata: text-table: ^0.2.0 bin: eslint: bin/eslint.js - checksum: ff6075daa28d817a7ac4508f31bc108a04d9ab5056608c8651b5bf9cfea5d708ca16dea6cdab2c3c0ae99b0bf0e726af8504eaa8e17c8e12e242cb68237ead64 + checksum: ca293ce7116599b742d7ab4d43db469beec22f40dd272092d809498be3cff3a7c567769f9763bdf6799aac13dd53447b93a99629b7b54092783046eb57eaced6 languageName: node linkType: hard @@ -1836,21 +1699,6 @@ __metadata: languageName: node linkType: hard -"execa@npm:^0.6.3": - version: 0.6.3 - resolution: "execa@npm:0.6.3" - dependencies: - cross-spawn: ^5.0.1 - get-stream: ^3.0.0 - is-stream: ^1.1.0 - npm-run-path: ^2.0.0 - p-finally: ^1.0.0 - signal-exit: ^3.0.0 - strip-eof: ^1.0.0 - checksum: 2c66177731273a7c0a4c031af81b486b67ec1eeeb8f353ebc68e0cfe7f63aca9ebc1e6fe03ba10f130f2bd179c0ac69b35668fe2bfc1ceb68fbf5291d0783457 - languageName: node - linkType: hard - "extend@npm:^3.0.2": version: 3.0.2 resolution: "extend@npm:3.0.2" @@ -1866,15 +1714,15 @@ __metadata: linkType: hard "fast-glob@npm:^3.2.9": - version: 3.2.11 - resolution: "fast-glob@npm:3.2.11" + version: 3.2.12 + resolution: "fast-glob@npm:3.2.12" dependencies: "@nodelib/fs.stat": ^2.0.2 "@nodelib/fs.walk": ^1.2.3 glob-parent: ^5.1.2 merge2: ^1.3.0 micromatch: ^4.0.4 - checksum: f473105324a7780a20c06de842e15ddbb41d3cb7e71d1e4fe6e8373204f22245d54f5ab9e2061e6a1c613047345954d29b022e0e76f5c28b1df9858179a0e6d7 + checksum: 0b1990f6ce831c7e28c4d505edcdaad8e27e88ab9fa65eedadb730438cfc7cde4910d6c975d6b7b8dc8a73da4773702ebcfcd6e3518e73938bb1383badfe01c2 languageName: node linkType: hard @@ -1893,9 +1741,9 @@ __metadata: linkType: hard "fast-text-encoding@npm:^1.0.0": - version: 1.0.4 - resolution: "fast-text-encoding@npm:1.0.4" - checksum: aa92d27451ad972ac8391b40f8680dc6722513b0b9c123bc6991fb789e46302a89d76a4e23a3071ca2071a63e5e116a0c75d790703a5568b22019350196e405d + version: 1.0.6 + resolution: "fast-text-encoding@npm:1.0.6" + checksum: 9d58f694314b3283e785bf61954902536da228607ad246905e30256f9ab8331f780ac987e7222c9f5eafd04168d07e12b8054c85cedb76a2c05af0e82387a903 languageName: node linkType: hard @@ -1908,15 +1756,6 @@ __metadata: languageName: node linkType: hard -"fd-slicer@npm:~1.1.0": - version: 1.1.0 - resolution: "fd-slicer@npm:1.1.0" - dependencies: - pend: ~1.2.0 - checksum: c8585fd5713f4476eb8261150900d2cb7f6ff2d87f8feb306ccc8a1122efd152f1783bdb2b8dc891395744583436bfd8081d8e63ece0ec8687eeefea394d4ff2 - languageName: node - linkType: hard - "file-entry-cache@npm:^6.0.1": version: 6.0.1 resolution: "file-entry-cache@npm:6.0.1" @@ -1926,35 +1765,14 @@ __metadata: languageName: node linkType: hard -"file-type@npm:^17.1.6": - version: 17.1.6 - resolution: "file-type@npm:17.1.6" +"file-type@npm:^18.0.0": + version: 18.0.0 + resolution: "file-type@npm:18.0.0" dependencies: readable-web-to-node-stream: ^3.0.2 - strtok3: ^7.0.0-alpha.9 - token-types: ^5.0.0-alpha.2 - checksum: 797e0d155ecaf4b575d4569a0188bfed85af19d18cf3d93ec8bb66d797172a1fde9f13d56135c6a0b471cacd7ecc1adb0c9a45c6e3a19436f682a275d0be16cc - languageName: node - linkType: hard - -"file-type@npm:^3.8.0": - version: 3.9.0 - resolution: "file-type@npm:3.9.0" - checksum: 1db70b2485ac77c4edb4b8753c1874ee6194123533f43c2651820f96b518f505fa570b093fedd6672eb105ba9fb89c62f84b6492e46788e39c3447aed37afa2d - languageName: node - linkType: hard - -"file-type@npm:^5.2.0": - version: 5.2.0 - resolution: "file-type@npm:5.2.0" - checksum: b2b21c7fc3cfb3c6a3a18b0d5d7233b74d8c17d82757655766573951daf42962a5c809e5fc3637675b237c558ebc67e4958fb2cc5a4ad407bc545aaa40001c74 - languageName: node - linkType: hard - -"file-type@npm:^6.1.0": - version: 6.2.0 - resolution: "file-type@npm:6.2.0" - checksum: 749540cefcd4959121eb83e373ed84e49b2e5a510aa5d598b725bd772dd306ae41fd00d3162ae3f6563b4db5cfafbbd0df321de3f20c17e20a8c56431ae55e58 + strtok3: ^7.0.0 + token-types: ^5.0.1 + checksum: 67f5a927b8030e35a4faf9dd9dea9e17bcb042fb61b9851b7dd1b1b3bb3ecfdd9f83bc3bc72686316ea2bac70df652c61e10affa9b5957b1a3d731df4925e3cb languageName: node linkType: hard @@ -1994,6 +1812,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 + "fraction.js@npm:^4.2.0": version: 4.2.0 resolution: "fraction.js@npm:4.2.0" @@ -2001,13 +1830,6 @@ __metadata: languageName: node linkType: hard -"fs-constants@npm:^1.0.0": - version: 1.0.0 - resolution: "fs-constants@npm:1.0.0" - checksum: 18f5b718371816155849475ac36c7d0b24d39a11d91348cfcb308b4494824413e03572c403c86d3a260e049465518c4f0d5bd00f0371cdfcad6d4f30a85b350d - languageName: node - linkType: hard - "fs-minipass@npm:^2.0.0, fs-minipass@npm:^2.1.0": version: 2.1.0 resolution: "fs-minipass@npm:2.1.0" @@ -2050,13 +1872,6 @@ __metadata: languageName: node linkType: hard -"functional-red-black-tree@npm:^1.0.1": - version: 1.0.1 - resolution: "functional-red-black-tree@npm:1.0.1" - checksum: ca6c170f37640e2d94297da8bb4bf27a1d12bea3e00e6a3e007fd7aa32e37e000f5772acf941b4e4f3cf1c95c3752033d0c509af157ad8f526e7f00723b9eb9f - languageName: node - linkType: hard - "fuse.js@npm:^6.6.2": version: 6.6.2 resolution: "fuse.js@npm:6.6.2" @@ -2110,40 +1925,13 @@ __metadata: linkType: hard "get-intrinsic@npm:^1.0.2": - version: 1.1.2 - resolution: "get-intrinsic@npm:1.1.2" + version: 1.1.3 + resolution: "get-intrinsic@npm:1.1.3" dependencies: function-bind: ^1.1.1 has: ^1.0.3 has-symbols: ^1.0.3 - checksum: 252f45491f2ba88ebf5b38018020c7cc3279de54b1d67ffb70c0cdf1dfa8ab31cd56467b5d117a8b4275b7a4dde91f86766b163a17a850f036528a7b2faafb2b - languageName: node - linkType: hard - -"get-stream@npm:^2.2.0": - version: 2.3.1 - resolution: "get-stream@npm:2.3.1" - dependencies: - object-assign: ^4.0.1 - pinkie-promise: ^2.0.0 - checksum: d82c86556e131ba7bef00233aa0aa7a51230e6deac11a971ce0f47cd43e2a5e968a3e3914cd082f07cd0d69425653b2f96735b0a7d5c5c03fef3ab857a531367 - languageName: node - linkType: hard - -"get-stream@npm:^3.0.0": - version: 3.0.0 - resolution: "get-stream@npm:3.0.0" - checksum: 36142f46005ed74ce3a45c55545ec4e7da8e243554179e345a786baf144e5c4a35fb7bdc49fadfa9f18bd08000589b6fe364abdadfc4e1eb0e1b9914a6bb9c56 - languageName: node - linkType: hard - -"gif-to-apng@npm:^0.1.2": - version: 0.1.2 - resolution: "gif-to-apng@npm:0.1.2" - dependencies: - decompress: ^4.1.0 - execa: ^0.6.3 - checksum: 2f545858151960d9eb8b18d2b6c6f882280a9e99aba8c11a036444461013fe32a1291946207d1a6b31a74cec3cfcca3780d1e3d398cf0a6f2d203c12642e42ac + checksum: 152d79e87251d536cf880ba75cfc3d6c6c50e12b3a64e1ea960e73a3752b47c69f46034456eae1b0894359ce3bc64c55c186f2811f8a788b75b638b06fab228a languageName: node linkType: hard @@ -2216,8 +2004,8 @@ __metadata: linkType: hard "google-auth-library@npm:^8.0.2": - version: 8.4.0 - resolution: "google-auth-library@npm:8.4.0" + version: 8.5.2 + resolution: "google-auth-library@npm:8.5.2" dependencies: arrify: ^2.0.0 base64-js: ^1.3.0 @@ -2228,7 +2016,7 @@ __metadata: gtoken: ^6.1.0 jws: ^4.0.0 lru-cache: ^6.0.0 - checksum: 4f3f52399c851dff3be797f888e675dbb6099613fcd934aece0d1527b9e9a77e5aadc9be12d137981d2d820a59050b0baf29ea126d2722e20a3e1adf425c93e6 + checksum: 5ab2904f5da3c119a7c241a1d5a11640468a7da58dfcec8a9cad181cc2723e6662b3c65997906069852d9fa066ba3e4b7f14e11cbb80d541e320b66ead6777dc languageName: node linkType: hard @@ -2244,30 +2032,30 @@ __metadata: linkType: hard "googleapis-common@npm:^6.0.0": - version: 6.0.1 - resolution: "googleapis-common@npm:6.0.1" + version: 6.0.3 + resolution: "googleapis-common@npm:6.0.3" dependencies: extend: ^3.0.2 gaxios: ^5.0.1 google-auth-library: ^8.0.2 qs: ^6.7.0 url-template: ^2.0.8 - uuid: ^8.0.0 - checksum: 46bfdd4673006075e3349500e312f2267f1d2830434d76b7b20d8de3ebfb9c5a3f525cc88b6e89f5d8f5f8f3ff9fcee90e4667dac45306cddb05d0f70b97b999 + uuid: ^9.0.0 + checksum: 72b3f2f4018d96d6e5bcc08096c693e393ba1ae59be243ac409e8876d941f918aad556a62d828709956552a5940295360674b9681b7871a1b3be54ea8a588f3f languageName: node linkType: hard -"googleapis@npm:^107.0.0": - version: 107.0.0 - resolution: "googleapis@npm:107.0.0" +"googleapis@npm:^108.0.0": + version: 108.0.0 + resolution: "googleapis@npm:108.0.0" dependencies: google-auth-library: ^8.0.2 googleapis-common: ^6.0.0 - checksum: 6cf90ad0039d61d65aacdfca9eb98af55ae380ec1b4cea43b58b49d765b924e8b5307e4c4e3b0f0128f53cab813fbdeaaf36522b60bb0a114c218c62a95f8f97 + checksum: 30f22202d174bcf7b2f02645785341953aaaa6280a40991ff6b77fae293945354a92e78f59e565529aee65c85e70e57147f8d991c7ae1f4593a5a9588613cc90 languageName: node linkType: hard -"graceful-fs@npm:^4.1.10, graceful-fs@npm:^4.2.6": +"graceful-fs@npm:^4.2.6": version: 4.2.10 resolution: "graceful-fs@npm:4.2.10" checksum: 3f109d70ae123951905d85032ebeae3c2a5a7a997430df00ea30df0e3a6c60cf6689b109654d6fdacd28810a053348c4d14642da1d075049e6be1ba5216218da @@ -2282,13 +2070,13 @@ __metadata: linkType: hard "gtoken@npm:^6.1.0": - version: 6.1.1 - resolution: "gtoken@npm:6.1.1" + version: 6.1.2 + resolution: "gtoken@npm:6.1.2" dependencies: gaxios: ^5.0.1 google-p12-pem: ^4.0.0 jws: ^4.0.0 - checksum: f063ed3f418f5a9c33fbe599b09f56a55b18f6c8d2913f11cc2fc5025d53b96fd6ab1b4deb2c7631167d6b89d0939e4ea77f94767fcf338862ffda8911250980 + checksum: cf3210afe2ccee8feaa06f0c7eb942e217244a8563a1d0a71aa3095eea545015896741c1d48654d8de35b7b07579f93e25e5dfe817f06b7e753646b67f7a4ecf languageName: node linkType: hard @@ -2368,7 +2156,7 @@ __metadata: languageName: node linkType: hard -"ieee754@npm:^1.1.13, ieee754@npm:^1.2.1": +"ieee754@npm:^1.2.1": version: 1.2.1 resolution: "ieee754@npm:1.2.1" checksum: 5144c0c9815e54ada181d80a0b810221a253562422e7c6c3a60b1901154184f49326ec239d618c416c1c5945a2e197107aee8d986a3dd836b53dffefd99b5e7e @@ -2421,9 +2209,9 @@ __metadata: linkType: hard "inflection@npm:^1.13.2": - version: 1.13.2 - resolution: "inflection@npm:1.13.2" - checksum: e7ad0559384ed7c526813404bde843f8f17941d47625ad60fc3b09e46efde873dd9840818007c6bd4dbe388e6248fa033d5a8c405c5fc62738c51b118a0e940f + version: 1.13.4 + resolution: "inflection@npm:1.13.4" + checksum: 6744feede9998ad8abd2b1db4af79f494a166e656a0aa949d90c8f4a945c1d07038a3756bf7af78c8f6fce368ba213a7ebf35da3edeffd39f1da0ff465eed6eb languageName: node linkType: hard @@ -2437,7 +2225,7 @@ __metadata: languageName: node linkType: hard -"inherits@npm:2, inherits@npm:^2.0.3, inherits@npm:~2.0.3": +"inherits@npm:2, inherits@npm:^2.0.3": version: 2.0.4 resolution: "inherits@npm:2.0.4" checksum: 4a48a733847879d6cf6691860a6b1e3f0f4754176e4d71494c41f3475553768b10f84b5ce1d40fbd0e34e6bfbb864ee35858ad4dd2cf31e02fc4a154b724d7f1 @@ -2490,13 +2278,6 @@ __metadata: languageName: node linkType: hard -"is-natural-number@npm:^4.0.1": - version: 4.0.1 - resolution: "is-natural-number@npm:4.0.1" - checksum: 3e5e3d52e0dfa4fea923b5d2b8a5cdbd9bf110c4598d30304b98528b02f40c9058a2abf1bae10bcbaf2bac18ace41cff7bc9673aff339f8c8297fae74ae0e75d - languageName: node - linkType: hard - "is-number@npm:^7.0.0": version: 7.0.0 resolution: "is-number@npm:7.0.0" @@ -2504,13 +2285,6 @@ __metadata: languageName: node linkType: hard -"is-stream@npm:^1.1.0": - version: 1.1.0 - resolution: "is-stream@npm:1.1.0" - checksum: 063c6bec9d5647aa6d42108d4c59723d2bd4ae42135a2d4db6eadbd49b7ea05b750fd69d279e5c7c45cf9da753ad2c00d8978be354d65aa9f6bb434969c6a2ae - languageName: node - linkType: hard - "is-stream@npm:^2.0.0": version: 2.0.1 resolution: "is-stream@npm:2.0.1" @@ -2518,13 +2292,6 @@ __metadata: languageName: node linkType: hard -"isarray@npm:~1.0.0": - version: 1.0.0 - resolution: "isarray@npm:1.0.0" - checksum: f032df8e02dce8ec565cf2eb605ea939bdccea528dbcf565cdf92bfa2da9110461159d86a537388ef1acef8815a330642d7885b29010e8f7eac967c9993b65ab - languageName: node - linkType: hard - "isexe@npm:^2.0.0": version: 2.0.0 resolution: "isexe@npm:2.0.0" @@ -2539,6 +2306,13 @@ __metadata: languageName: node linkType: hard +"js-sdsl@npm:^4.1.4": + version: 4.1.4 + resolution: "js-sdsl@npm:4.1.4" + checksum: 1977cea4ab18e0e03e28bdf0371d8b443fad65ca0988e0faa216406faf6bb943714fe8f7cc7a5bfe5f35ba3d94ddae399f4d10200f547f2c3320688b0670d726 + languageName: node + linkType: hard + "js-yaml@npm:^4.1.0": version: 4.1.0 resolution: "js-yaml@npm:4.1.0" @@ -2638,6 +2412,13 @@ __metadata: languageName: node linkType: hard +"lodash-es@npm:^4.17.21": + version: 4.17.21 + resolution: "lodash-es@npm:4.17.21" + checksum: 05cbffad6e2adbb331a4e16fbd826e7faee403a1a04873b82b42c0f22090f280839f85b95393f487c1303c8a3d2a010048bf06151a6cbe03eee4d388fb0a12d2 + languageName: node + linkType: hard + "lodash.merge@npm:^4.6.2": version: 4.6.2 resolution: "lodash.merge@npm:4.6.2" @@ -2675,16 +2456,6 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^4.0.1": - version: 4.1.5 - resolution: "lru-cache@npm:4.1.5" - dependencies: - pseudomap: ^1.0.2 - yallist: ^2.1.2 - checksum: 4bb4b58a36cd7dc4dcec74cbe6a8f766a38b7426f1ff59d4cf7d82a2aa9b9565cd1cb98f6ff60ce5cd174524868d7bc9b7b1c294371851356066ca9ac4cf135a - languageName: node - linkType: hard - "lru-cache@npm:^6.0.0": version: 6.0.0 resolution: "lru-cache@npm:6.0.0" @@ -2708,15 +2479,6 @@ __metadata: languageName: node linkType: hard -"make-dir@npm:^1.0.0": - version: 1.3.0 - resolution: "make-dir@npm:1.3.0" - dependencies: - pify: ^3.0.0 - checksum: c564f6e7bb5ace1c02ad56b3a5f5e07d074af0c0b693c55c7b2c2b148882827c8c2afc7b57e43338a9f90c125b58d604e8cf3e6990a48bf949dfea8c79668c0b - languageName: node - linkType: hard - "make-fetch-happen@npm:^10.0.3": version: 10.2.1 resolution: "make-fetch-happen@npm:10.2.1" @@ -2741,11 +2503,11 @@ __metadata: languageName: node linkType: hard -"mathjs@npm:^11.1.0": - version: 11.1.0 - resolution: "mathjs@npm:11.1.0" +"mathjs@npm:^11.2.1": + version: 11.2.1 + resolution: "mathjs@npm:11.2.1" dependencies: - "@babel/runtime": ^7.18.9 + "@babel/runtime": ^7.19.0 complex.js: ^2.1.1 decimal.js: ^10.4.0 escape-latex: ^1.2.0 @@ -2756,7 +2518,7 @@ __metadata: typed-function: ^4.1.0 bin: mathjs: bin/cli.js - checksum: 66d1c285e18478d8d5e07b8e67268a58bf296f21c5a72cddfcefc46fe81c5876eee77b4a630f9191a6cbd789d3ac1d5d2380adb4e83fd0569949c9f70ffa48df + checksum: 83ff278bc7a633757ca0b81f2c7c60f8ea70fb077266f60ea6b25a09325ba71f27096d08b3eeb58e8c438b9252e5b70e4a770842830cc249aaa85d523ed19616 languageName: node linkType: hard @@ -2777,6 +2539,22 @@ __metadata: languageName: node linkType: hard +"mime-db@npm:1.52.0": + version: 1.52.0 + resolution: "mime-db@npm:1.52.0" + checksum: 0d99a03585f8b39d68182803b12ac601d9c01abfa28ec56204fa330bc9f3d1c5e14beb049bafadb3dbdf646dfb94b87e24d4ec7b31b7279ef906a8ea9b6a513f + languageName: node + linkType: hard + +"mime-types@npm:^2.1.12": + version: 2.1.35 + resolution: "mime-types@npm:2.1.35" + dependencies: + mime-db: 1.52.0 + checksum: 89a5b7f1def9f3af5dad6496c5ed50191ae4331cc5389d7c521c8ad28d5fdad2d06fd81baf38fed813dc4e46bb55c8145bb0ff406330818c9cf712fb2e9b3836 + languageName: node + linkType: hard + "minimatch@npm:^3.0.4, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" @@ -2988,15 +2766,6 @@ __metadata: languageName: node linkType: hard -"npm-run-path@npm:^2.0.0": - version: 2.0.2 - resolution: "npm-run-path@npm:2.0.2" - dependencies: - path-key: ^2.0.0 - checksum: acd5ad81648ba4588ba5a8effb1d98d2b339d31be16826a118d50f182a134ac523172101b82eab1d01cb4c2ba358e857d54cfafd8163a1ffe7bd52100b741125 - languageName: node - linkType: hard - "npmlog@npm:^6.0.0": version: 6.0.2 resolution: "npmlog@npm:6.0.2" @@ -3016,13 +2785,6 @@ __metadata: languageName: node linkType: hard -"object-assign@npm:^4.0.1": - version: 4.1.1 - resolution: "object-assign@npm:4.1.1" - checksum: fcc6e4ea8c7fe48abfbb552578b1c53e0d194086e2e6bbbf59e0a536381a292f39943c6e9628af05b5528aa5e3318bb30d6b2e53cadaf5b8fe9e12c4b69af23f - languageName: node - linkType: hard - "object-inspect@npm:^1.9.0": version: 1.12.2 resolution: "object-inspect@npm:1.12.2" @@ -3030,7 +2792,7 @@ __metadata: languageName: node linkType: hard -"once@npm:^1.3.0, once@npm:^1.4.0": +"once@npm:^1.3.0": version: 1.4.0 resolution: "once@npm:1.4.0" dependencies: @@ -3053,13 +2815,6 @@ __metadata: languageName: node linkType: hard -"p-finally@npm:^1.0.0": - version: 1.0.0 - resolution: "p-finally@npm:1.0.0" - checksum: 93a654c53dc805dd5b5891bab16eb0ea46db8f66c4bfd99336ae929323b1af2b70a8b0654f8f1eae924b2b73d037031366d645f1fd18b3d30cbd15950cc4b1d4 - languageName: node - linkType: hard - "p-limit@npm:^3.0.2": version: 3.1.0 resolution: "p-limit@npm:3.1.0" @@ -3117,13 +2872,6 @@ __metadata: languageName: node linkType: hard -"path-key@npm:^2.0.0": - version: 2.0.1 - resolution: "path-key@npm:2.0.1" - checksum: f7ab0ad42fe3fb8c7f11d0c4f849871e28fbd8e1add65c370e422512fc5887097b9cf34d09c1747d45c942a8c1e26468d6356e2df3f740bf177ab8ca7301ebfd - languageName: node - linkType: hard - "path-key@npm:^3.1.0": version: 3.1.1 resolution: "path-key@npm:3.1.1" @@ -3159,13 +2907,6 @@ __metadata: languageName: node linkType: hard -"pend@npm:~1.2.0": - version: 1.2.0 - resolution: "pend@npm:1.2.0" - checksum: 6c72f5243303d9c60bd98e6446ba7d30ae29e3d56fdb6fae8767e8ba6386f33ee284c97efe3230a0d0217e2b1723b8ab490b1bbf34fcbb2180dbc8a9de47850d - languageName: node - linkType: hard - "pg-connection-string@npm:^2.5.0": version: 2.5.0 resolution: "pg-connection-string@npm:2.5.0" @@ -3261,36 +3002,6 @@ __metadata: languageName: node linkType: hard -"pify@npm:^2.3.0": - version: 2.3.0 - resolution: "pify@npm:2.3.0" - checksum: 9503aaeaf4577acc58642ad1d25c45c6d90288596238fb68f82811c08104c800e5a7870398e9f015d82b44ecbcbef3dc3d4251a1cbb582f6e5959fe09884b2ba - languageName: node - linkType: hard - -"pify@npm:^3.0.0": - version: 3.0.0 - resolution: "pify@npm:3.0.0" - checksum: 6cdcbc3567d5c412450c53261a3f10991665d660961e06605decf4544a61a97a54fefe70a68d5c37080ff9d6f4cf51444c90198d1ba9f9309a6c0d6e9f5c4fde - languageName: node - linkType: hard - -"pinkie-promise@npm:^2.0.0": - version: 2.0.1 - resolution: "pinkie-promise@npm:2.0.1" - dependencies: - pinkie: ^2.0.0 - checksum: b53a4a2e73bf56b6f421eef711e7bdcb693d6abb474d57c5c413b809f654ba5ee750c6a96dd7225052d4b96c4d053cdcb34b708a86fceed4663303abee52fcca - languageName: node - linkType: hard - -"pinkie@npm:^2.0.0": - version: 2.0.4 - resolution: "pinkie@npm:2.0.4" - checksum: b12b10afea1177595aab036fc220785488f67b4b0fc49e7a27979472592e971614fa1c728e63ad3e7eb748b4ec3c3dbd780819331dad6f7d635c77c10537b9db - languageName: node - linkType: hard - "postcss@npm:^8.4.16": version: 8.4.16 resolution: "postcss@npm:8.4.16" @@ -3355,13 +3066,6 @@ __metadata: languageName: node linkType: hard -"process-nextick-args@npm:~2.0.0": - version: 2.0.1 - resolution: "process-nextick-args@npm:2.0.1" - checksum: 1d38588e520dab7cea67cbbe2efdd86a10cc7a074c09657635e34f035277b59fbb57d09d8638346bf7090f8e8ebc070c96fa5fd183b777fff4f5edff5e9466cf - languageName: node - linkType: hard - "promise-inflight@npm:^1.0.1": version: 1.0.1 resolution: "promise-inflight@npm:1.0.1" @@ -3379,13 +3083,6 @@ __metadata: languageName: node linkType: hard -"pseudomap@npm:^1.0.2": - version: 1.0.2 - resolution: "pseudomap@npm:1.0.2" - checksum: 856c0aae0ff2ad60881168334448e898ad7a0e45fe7386d114b150084254c01e200c957cf378378025df4e052c7890c5bd933939b0e0d2ecfcc1dc2f0b2991f5 - languageName: node - linkType: hard - "punycode@npm:^2.1.0": version: 2.1.1 resolution: "punycode@npm:2.1.1" @@ -3409,21 +3106,6 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:^2.3.0, readable-stream@npm:^2.3.5": - version: 2.3.7 - resolution: "readable-stream@npm:2.3.7" - dependencies: - core-util-is: ~1.0.0 - inherits: ~2.0.3 - isarray: ~1.0.0 - process-nextick-args: ~2.0.0 - safe-buffer: ~5.1.1 - string_decoder: ~1.1.1 - util-deprecate: ~1.0.1 - checksum: e4920cf7549a60f8aaf694d483a0e61b2a878b969d224f89b3bc788b8d920075132c4b55a7494ee944c7b6a9a0eada28a7f6220d80b0312ece70bbf08eeca755 - languageName: node - linkType: hard - "readable-stream@npm:^3.6.0": version: 3.6.0 resolution: "readable-stream@npm:3.6.0" @@ -3491,10 +3173,10 @@ __metadata: languageName: node linkType: hard -"retry-as-promised@npm:^5.0.0": - version: 5.0.0 - resolution: "retry-as-promised@npm:5.0.0" - checksum: 4d17e0597f967db0516714f0f2b085e2d1e8ea592450b013a1e0cd41dad74a9c3d1eb3c45351887de4bca1c3ac5f74dd07ef9942a3fb0e4db996a83bb28cb46e +"retry-as-promised@npm:^6.1.0": + version: 6.1.0 + resolution: "retry-as-promised@npm:6.1.0" + checksum: cde3ad5c827d3f51b9161b9453dfd5eb8b8b6c22b6b405f7294eaa1939ef1966f8bd4c8a228ce2d66a24959d1dcc48b113c4c810ecdaddec2968b6d20576329d languageName: node linkType: hard @@ -3523,9 +3205,9 @@ __metadata: languageName: node linkType: hard -"rollup@npm:>=2.75.6 <2.77.0 || ~2.77.0": - version: 2.77.3 - resolution: "rollup@npm:2.77.3" +"rollup@npm:~2.78.0": + version: 2.78.1 + resolution: "rollup@npm:2.78.1" dependencies: fsevents: ~2.3.2 dependenciesMeta: @@ -3533,7 +3215,7 @@ __metadata: optional: true bin: rollup: dist/bin/rollup - checksum: b179c68249584565ddb5664a241e8e48c293b2207718d885b08ee25797d98857a383f06b544bb89819407da5a71557f4713309a278f61c4778bb32b1d3321a1c + checksum: 9034814383ca5bdb4bea6d499270aeb31cdb0bb884f81b0c6a1d19c63cc973f040e6ee09b7af8a7169dd231c090f4b44ef8b99c4bfdf884aceeb3dcefb8cfa14 languageName: node linkType: hard @@ -3546,20 +3228,13 @@ __metadata: languageName: node linkType: hard -"safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.1, safe-buffer@npm:~5.2.0": +"safe-buffer@npm:^5.0.1, safe-buffer@npm:~5.2.0": version: 5.2.1 resolution: "safe-buffer@npm:5.2.1" checksum: b99c4b41fdd67a6aaf280fcd05e9ffb0813654894223afb78a31f14a19ad220bba8aba1cb14eddce1fcfb037155fe6de4e861784eb434f7d11ed58d1e70dd491 languageName: node linkType: hard -"safe-buffer@npm:~5.1.0, safe-buffer@npm:~5.1.1": - version: 5.1.2 - resolution: "safe-buffer@npm:5.1.2" - checksum: f2f1f7943ca44a594893a852894055cf619c1fbcb611237fc39e461ae751187e7baf4dc391a72125e0ac4fb2d8c5c0b3c71529622e6a58f46b960211e704903c - languageName: node - linkType: hard - "safer-buffer@npm:>= 2.1.2 < 3.0.0": version: 2.1.2 resolution: "safer-buffer@npm:2.1.2" @@ -3574,18 +3249,6 @@ __metadata: languageName: node linkType: hard -"seek-bzip@npm:^1.0.5": - version: 1.0.6 - resolution: "seek-bzip@npm:1.0.6" - dependencies: - commander: ^2.8.1 - bin: - seek-bunzip: bin/seek-bunzip - seek-table: bin/seek-bzip-table - checksum: c2ab3291e7085558499efd4e99d1466ee6782f6c4a4e4c417aa859e1cd2f5117fb3b5444f3d27c38ec5908c0f0312e2a0bc69dff087751f97b3921b5bde4f9ed - languageName: node - linkType: hard - "semver@npm:^7.3.5, semver@npm:^7.3.7": version: 7.3.7 resolution: "semver@npm:7.3.7" @@ -3604,9 +3267,9 @@ __metadata: languageName: node linkType: hard -"sequelize@npm:6.21.4": - version: 6.21.4 - resolution: "sequelize@npm:6.21.4" +"sequelize@npm:6.23.1": + version: 6.23.1 + resolution: "sequelize@npm:6.23.1" dependencies: "@types/debug": ^4.1.7 "@types/validator": ^13.7.1 @@ -3617,7 +3280,7 @@ __metadata: moment: ^2.29.1 moment-timezone: ^0.5.34 pg-connection-string: ^2.5.0 - retry-as-promised: ^5.0.0 + retry-as-promised: ^6.1.0 semver: ^7.3.5 sequelize-pool: ^7.1.0 toposort-class: ^1.0.1 @@ -3631,6 +3294,8 @@ __metadata: optional: true mysql2: optional: true + oracledb: + optional: true pg: optional: true pg-hstore: @@ -3641,7 +3306,7 @@ __metadata: optional: true tedious: optional: true - checksum: 6d066083125aa8a90286c82379964b5e54620daa7eac7f0d8b25c52cad93f8cae70cc4606a58097e815f57bad405969353d35321c1c9edfff27d953cec9ff064 + checksum: cb34d8b6cd4c3aba19bb1d6c6af4904d6526053fab611e1ae39cc5ba8f949b5e0c581806bf02aef07bd4a87782ec7096d6f0df77bcb3c46cc1361ac04818141e languageName: node linkType: hard @@ -3652,15 +3317,6 @@ __metadata: languageName: node linkType: hard -"shebang-command@npm:^1.2.0": - version: 1.2.0 - resolution: "shebang-command@npm:1.2.0" - dependencies: - shebang-regex: ^1.0.0 - checksum: 9eed1750301e622961ba5d588af2212505e96770ec376a37ab678f965795e995ade7ed44910f5d3d3cb5e10165a1847f52d3348c64e146b8be922f7707958908 - languageName: node - linkType: hard - "shebang-command@npm:^2.0.0": version: 2.0.0 resolution: "shebang-command@npm:2.0.0" @@ -3670,13 +3326,6 @@ __metadata: languageName: node linkType: hard -"shebang-regex@npm:^1.0.0": - version: 1.0.0 - resolution: "shebang-regex@npm:1.0.0" - checksum: 404c5a752cd40f94591dfd9346da40a735a05139dac890ffc229afba610854d8799aaa52f87f7e0c94c5007f2c6af55bdcaeb584b56691926c5eaf41dc8f1372 - languageName: node - linkType: hard - "shebang-regex@npm:^3.0.0": version: 3.0.0 resolution: "shebang-regex@npm:3.0.0" @@ -3695,7 +3344,7 @@ __metadata: languageName: node linkType: hard -"signal-exit@npm:^3.0.0, signal-exit@npm:^3.0.7": +"signal-exit@npm:^3.0.7": version: 3.0.7 resolution: "signal-exit@npm:3.0.7" checksum: a2f098f247adc367dffc27845853e9959b9e88b01cb301658cfe4194352d8d2bb32e18467c786a7fe15f1d44b233ea35633d076d5e737870b7139949d1ab6318 @@ -3780,15 +3429,6 @@ __metadata: languageName: node linkType: hard -"string_decoder@npm:~1.1.1": - version: 1.1.1 - resolution: "string_decoder@npm:1.1.1" - dependencies: - safe-buffer: ~5.1.0 - checksum: 9ab7e56f9d60a28f2be697419917c50cac19f3e8e6c28ef26ed5f4852289fe0de5d6997d29becf59028556f2c62983790c1d9ba1e2a3cc401768ca12d5183a5b - languageName: node - linkType: hard - "strip-ansi@npm:^6.0.1": version: 6.0.1 resolution: "strip-ansi@npm:6.0.1" @@ -3798,22 +3438,6 @@ __metadata: languageName: node linkType: hard -"strip-dirs@npm:^2.0.0": - version: 2.1.0 - resolution: "strip-dirs@npm:2.1.0" - dependencies: - is-natural-number: ^4.0.1 - checksum: 9465547d71d8819daa7a5c9d4d783289ed8eac72eb06bd687bed382ce62af8ab8e6ffbda229805f5d2e71acce2ca4915e781c94190d284994cbc0b7cdc8303cc - languageName: node - linkType: hard - -"strip-eof@npm:^1.0.0": - version: 1.0.0 - resolution: "strip-eof@npm:1.0.0" - checksum: 40bc8ddd7e072f8ba0c2d6d05267b4e0a4800898c3435b5fb5f5a21e6e47dfaff18467e7aa0d1844bb5d6274c3097246595841fbfeb317e541974ee992cac506 - languageName: node - linkType: hard - "strip-json-comments@npm:^3.1.0, strip-json-comments@npm:^3.1.1": version: 3.1.1 resolution: "strip-json-comments@npm:3.1.1" @@ -3821,7 +3445,16 @@ __metadata: languageName: node linkType: hard -"strtok3@npm:^7.0.0-alpha.9": +"strip-literal@npm:^0.4.1": + version: 0.4.2 + resolution: "strip-literal@npm:0.4.2" + dependencies: + acorn: ^8.8.0 + checksum: 831cdcaba61bc82c14ef5ca423a64bb8044b3b128abd15dff454d3fd05b0dbc7b4403760a7a636923d3c2e71a8e65174cef28ee9aef61f9a66819f865da4fdda + languageName: node + linkType: hard + +"strtok3@npm:^7.0.0": version: 7.0.0 resolution: "strtok3@npm:7.0.0" dependencies: @@ -3851,76 +3484,59 @@ __metadata: version: 0.0.0-use.local resolution: "tanzanite@workspace:." dependencies: - "@discordjs/builders": ^1.1.0 - "@discordjs/rest": ^1.0.1 + "@discordjs/builders": ^1.2.0 + "@discordjs/rest": ^1.2.0 "@ironm00n/nbt-ts": ^1.4.0 - "@napi-rs/canvas": ^0.1.29 - "@notenoughupdates/discord.js-minesweeper": ^1.0.10 - "@notenoughupdates/events-intercept": ^3.0.1 + "@napi-rs/canvas": ^0.1.30 + "@notenoughupdates/discord-akairo": ^10.0.0-dev.1664158089.48d6368 "@notenoughupdates/humanize-duration": ^4.0.1 - "@notenoughupdates/simplify-number": ^1.0.1 - "@notenoughupdates/wolfram-alpha-api": ^1.0.2 "@sapphire/snowflake": ^3.2.2 - "@sentry/integrations": ^7.11.1 - "@sentry/node": ^7.11.1 - "@sentry/tracing": ^7.11.1 - "@sentry/types": ^7.11.1 + "@sentry/integrations": ^7.13.0 + "@sentry/node": ^7.13.0 + "@sentry/tracing": ^7.13.0 + "@sentry/types": ^7.13.0 + "@tanzanite/deep-lock": ^1.1.1 + "@tanzanite/discord.js-minesweeper": ^1.2.0 + "@tanzanite/events-intercept": ^3.1.0 + "@tanzanite/simplify-number": ^2.0.1 + "@tanzanite/wolfram-alpha": ^1.1.0 "@types/eslint": ^8.4.6 - "@types/express": ^4.17.13 - "@types/lodash": ^4.14.184 - "@types/node": ^18.7.13 + "@types/express": ^4.17.14 + "@types/lodash-es": ^4 + "@types/node": ^18.7.21 + "@types/node-fetch": ^2.6.2 "@types/numeral": ^2.0.2 "@types/pg": ^8.6.5 - "@types/prettier": ^2.7.0 - "@types/rimraf": ^3.0.2 + "@types/prettier": ^2.7.1 "@types/tinycolor2": ^1.4.3 - "@types/validator": ^13.7.5 - "@typescript-eslint/eslint-plugin": ^5.35.1 - "@typescript-eslint/parser": ^5.35.1 + "@types/validator": ^13.7.7 + "@typescript-eslint/eslint-plugin": ^5.38.0 + "@typescript-eslint/parser": ^5.38.0 chalk: ^5.0.1 - deep-lock: ^1.0.0 - discord-akairo: "npm:@notenoughupdates/discord-akairo@dev" - discord-api-types: 0.37.1 - discord.js: "npm:@notenoughupdates/discord.js@forum" - eslint: ^8.23.0 + discord-api-types: 0.37.10 + discord.js: ^14.5.0 + eslint: ^8.24.0 eslint-config-prettier: ^8.5.0 eslint-plugin-deprecation: ^1.3.2 fuse.js: ^6.6.2 - gif-to-apng: ^0.1.2 - googleapis: ^107.0.0 - lodash: ^4.17.21 - mathjs: ^11.1.0 + googleapis: ^108.0.0 + lodash-es: ^4.17.21 + mathjs: ^11.2.1 nanoid: ^4.0.0 numeral: ^2.0.6 pg: ^8.8.0 pg-hstore: ^2.3.4 prettier: ^2.7.1 pretty-bytes: ^6.0.0 - rimraf: ^3.0.2 - sequelize: 6.21.4 + sequelize: 6.23.1 tinycolor2: ^1.4.2 ts-essentials: ^9.3.0 - typescript: ^4.8.2 - vitest: ^0.22.1 - vm2: ^3.9.10 + typescript: ^4.8.3 + vitest: ^0.23.4 + vm2: ^3.9.11 languageName: unknown linkType: soft -"tar-stream@npm:^1.5.2": - version: 1.6.2 - resolution: "tar-stream@npm:1.6.2" - dependencies: - bl: ^1.0.0 - buffer-alloc: ^1.2.0 - end-of-stream: ^1.0.0 - fs-constants: ^1.0.0 - readable-stream: ^2.3.0 - to-buffer: ^1.1.1 - xtend: ^4.0.0 - checksum: a5d49e232d3e33321bbd150381b6a4e5046bf12b1c2618acb95435b7871efde4d98bd1891eb2200478a7142ef7e304e033eb29bbcbc90451a2cdfa1890e05245 - languageName: node - linkType: hard - "tar@npm:^6.1.11, tar@npm:^6.1.2": version: 6.1.11 resolution: "tar@npm:6.1.11" @@ -3942,13 +3558,6 @@ __metadata: languageName: node linkType: hard -"through@npm:^2.3.8": - version: 2.3.8 - resolution: "through@npm:2.3.8" - checksum: a38c3e059853c494af95d50c072b83f8b676a9ba2818dcc5b108ef252230735c54e0185437618596c790bbba8fcdaef5b290405981ffa09dce67b1f1bf190cbd - languageName: node - linkType: hard - "tiny-emitter@npm:^2.1.0": version: 2.1.0 resolution: "tiny-emitter@npm:2.1.0" @@ -3956,6 +3565,13 @@ __metadata: languageName: node linkType: hard +"tinybench@npm:^2.1.5": + version: 2.1.5 + resolution: "tinybench@npm:2.1.5" + checksum: 5a6d378271a00ace6fa6970404eb1efd539d3e9b9908a9cb6d66ad78acfd873762734f3fc69fe10d86217318871367997d2a8b9566039fd224c518493466aa96 + languageName: node + linkType: hard + "tinycolor2@npm:^1.4.2": version: 1.4.2 resolution: "tinycolor2@npm:1.4.2" @@ -3963,10 +3579,10 @@ __metadata: languageName: node linkType: hard -"tinypool@npm:^0.2.4": - version: 0.2.4 - resolution: "tinypool@npm:0.2.4" - checksum: f050bd36c89529a2a0d3f9c1fdbba3f317114e3ee6eb5d5ba72c51e887d45ef3ef8d8533fb2ca2eba7189d19d2231712b81b3a75e099248532f5563369929c33 +"tinypool@npm:^0.3.0": + version: 0.3.0 + resolution: "tinypool@npm:0.3.0" + checksum: 92291c309ed8d004c1ee1ef7f610cd90352383f12c52b0ec16abd9ebc665c03626e7afbc9993df97f63e67fea002b5cc18ba5e8f90260643867cbcac278a183c languageName: node linkType: hard @@ -3977,13 +3593,6 @@ __metadata: languageName: node linkType: hard -"to-buffer@npm:^1.1.1": - version: 1.1.1 - resolution: "to-buffer@npm:1.1.1" - checksum: 6c897f58c2bdd8b8b1645ea515297732fec6dafb089bf36d12370c102ff5d64abf2be9410e0b1b7cfc707bada22d9a4084558010bfc78dd7023748dc5dd9a1ce - languageName: node - linkType: hard - "to-regex-range@npm:^5.0.1": version: 5.0.1 resolution: "to-regex-range@npm:5.0.1" @@ -3993,7 +3602,7 @@ __metadata: languageName: node linkType: hard -"token-types@npm:^5.0.0-alpha.2": +"token-types@npm:^5.0.1": version: 5.0.1 resolution: "token-types@npm:5.0.1" dependencies: @@ -4081,44 +3690,34 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^4.8.2": - version: 4.8.2 - resolution: "typescript@npm:4.8.2" +"typescript@npm:^4.8.3": + version: 4.8.3 + resolution: "typescript@npm:4.8.3" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 7f5b81d0d558c9067f952c7af52ab7f19c2e70a916817929e4a5b256c93990bf3178eccb1ac8a850bc75df35f6781b6f4cb3370ce20d8b1ded92ed462348f628 + checksum: 8286a5edcaf3d68e65c451aa1e7150ad1cf53ee0813c07ec35b7abdfdb10f355ecaa13c6a226a694ae7a67785fd7eeebf89f845da0b4f7e4a35561ddc459aba0 languageName: node linkType: hard -"typescript@patch:typescript@^4.8.2#~builtin<compat/typescript>": - version: 4.8.2 - resolution: "typescript@patch:typescript@npm%3A4.8.2#~builtin<compat/typescript>::version=4.8.2&hash=a1c5e5" +"typescript@patch:typescript@^4.8.3#~builtin<compat/typescript>": + version: 4.8.3 + resolution: "typescript@patch:typescript@npm%3A4.8.3#~builtin<compat/typescript>::version=4.8.3&hash=a1c5e5" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 5cb0f02f414f5405f4b0e7ee1fd7fa9177b6a8783c9017b6cad85f56ce4c4f93e0e6f2ce37e863cb597d44227cd009474c9fbd85bf7a50004e5557426cb58079 - languageName: node - linkType: hard - -"unbzip2-stream@npm:^1.0.9": - version: 1.4.3 - resolution: "unbzip2-stream@npm:1.4.3" - dependencies: - buffer: ^5.2.1 - through: ^2.3.8 - checksum: 0e67c4a91f4fa0fc7b4045f8b914d3498c2fc2e8c39c359977708ec85ac6d6029840e97f508675fdbdf21fcb8d276ca502043406f3682b70f075e69aae626d1d + checksum: 2222d2382fb3146089b1d27ce2b55e9d1f99cc64118f1aba75809b693b856c5d3c324f052f60c75b577947fc538bc1c27bad0eb76cbdba9a63a253489504ba7e languageName: node linkType: hard "underscore@npm:^1.13.1": - version: 1.13.4 - resolution: "underscore@npm:1.13.4" - checksum: 6b04f66cd454e8793a552dc49c71e24e5208a29b9d9c0af988a96948af79103399c36fb15db43f3629bfed152f8b1fe94f44e1249e9d196069c0fc7edfadb636 + version: 1.13.6 + resolution: "underscore@npm:1.13.6" + checksum: d5cedd14a9d0d91dd38c1ce6169e4455bb931f0aaf354108e47bd46d3f2da7464d49b2171a5cf786d61963204a42d01ea1332a903b7342ad428deaafaf70ec36 languageName: node linkType: hard -"undici@npm:^5.8.0, undici@npm:^5.9.1": +"undici@npm:^5.10.0": version: 5.10.0 resolution: "undici@npm:5.10.0" checksum: 7ba2b71dccc74cd2bdf645b83e9aaef374ae04855943d0a2f42a3d0b9e5556f37cc9b5156fb5288277a2fa95fd46a56f3ae0d5cf73db3f008d75ec41104b136c @@ -4159,14 +3758,14 @@ __metadata: languageName: node linkType: hard -"util-deprecate@npm:^1.0.1, util-deprecate@npm:~1.0.1": +"util-deprecate@npm:^1.0.1": version: 1.0.2 resolution: "util-deprecate@npm:1.0.2" checksum: 474acf1146cb2701fe3b074892217553dfcf9a031280919ba1b8d651a068c9b15d863b7303cb15bd00a862b498e6cf4ad7b4a08fb134edd5a6f7641681cb54a2 languageName: node linkType: hard -"uuid@npm:^8.0.0, uuid@npm:^8.3.2": +"uuid@npm:^8.3.2": version: 8.3.2 resolution: "uuid@npm:8.3.2" bin: @@ -4175,6 +3774,15 @@ __metadata: languageName: node linkType: hard +"uuid@npm:^9.0.0": + version: 9.0.0 + resolution: "uuid@npm:9.0.0" + bin: + uuid: dist/bin/uuid + checksum: 8dd2c83c43ddc7e1c71e36b60aea40030a6505139af6bee0f382ebcd1a56f6cd3028f7f06ffb07f8cf6ced320b76aea275284b224b002b289f89fe89c389b028 + languageName: node + linkType: hard + "validator@npm:^13.7.0": version: 13.7.0 resolution: "validator@npm:13.7.0" @@ -4183,14 +3791,14 @@ __metadata: linkType: hard "vite@npm:^2.9.12 || ^3.0.0-0": - version: 3.0.9 - resolution: "vite@npm:3.0.9" + version: 3.1.3 + resolution: "vite@npm:3.1.3" dependencies: - esbuild: ^0.14.47 + esbuild: ^0.15.6 fsevents: ~2.3.2 postcss: ^8.4.16 resolve: ^1.22.1 - rollup: ">=2.75.6 <2.77.0 || ~2.77.0" + rollup: ~2.78.0 peerDependencies: less: "*" sass: "*" @@ -4210,13 +3818,13 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: 6341aa43579ae45f8a383bdc0c5041dea3dff98f14e0a546d6d884a864134b00082246a28d1de8adff0ce0dd92b468c7ade8f972ffe1ed97258671d63e0f16f7 + checksum: af13c9820c292792f02d0a25fd46d8557e627b93f95bc05b5f7f1261e9565e9e69fda2df0c0898f248edb811ebcac5400a85a81625ef29749f50b18273439d91 languageName: node linkType: hard -"vitest@npm:^0.22.1": - version: 0.22.1 - resolution: "vitest@npm:0.22.1" +"vitest@npm:^0.23.4": + version: 0.23.4 + resolution: "vitest@npm:0.23.4" dependencies: "@types/chai": ^4.3.3 "@types/chai-subset": ^1.3.3 @@ -4224,7 +3832,9 @@ __metadata: chai: ^4.3.6 debug: ^4.3.4 local-pkg: ^0.4.2 - tinypool: ^0.2.4 + strip-literal: ^0.4.1 + tinybench: ^2.1.5 + tinypool: ^0.3.0 tinyspy: ^1.0.2 vite: ^2.9.12 || ^3.0.0-0 peerDependencies: @@ -4246,19 +3856,19 @@ __metadata: optional: true bin: vitest: vitest.mjs - checksum: 7abe50ceb51181e77cd62eb3a07c2da17f13078f09be34cc2e98f1f94a77eba33a56c644d48ae16bb474945ffc1cfc8664b1f4976c3de495c5e474057420c4ca + checksum: c0356ff585d6f85678abe4e4f61362ea55af87b15f9b1fc478f5fed511627efa5f849a1530709fa2ca09a3f5e93dd16dfaf6570c9cd84ca6fd6ef387aa8ee512 languageName: node linkType: hard -"vm2@npm:^3.9.10": - version: 3.9.10 - resolution: "vm2@npm:3.9.10" +"vm2@npm:^3.9.11": + version: 3.9.11 + resolution: "vm2@npm:3.9.11" dependencies: acorn: ^8.7.0 acorn-walk: ^8.2.0 bin: vm2: bin/vm2 - checksum: 5534df3d3f3aa9060ab23fd22a2f5e756c19bec7b9af50028aa05c6f7c21c3da303101f1982004964d5320f9484d02a2d299e841994dce03af7fe1061871b518 + checksum: aab39e6e4b59146d24abacd79f490e854a6e058a8b23d93d2be5aca7720778e2605d2cc028ccc4a5f50d3d91b0c38be9a6247a80d2da1a6de09425cc437770b4 languageName: node linkType: hard @@ -4279,17 +3889,6 @@ __metadata: languageName: node linkType: hard -"which@npm:^1.2.9": - version: 1.3.1 - resolution: "which@npm:1.3.1" - dependencies: - isexe: ^2.0.0 - bin: - which: ./bin/which - checksum: f2e185c6242244b8426c9df1510e86629192d93c1a986a7d2a591f2c24869e7ffd03d6dac07ca863b2e4c06f59a4cc9916c585b72ee9fa1aa609d0124df15e04 - languageName: node - linkType: hard - "which@npm:^2.0.1, which@npm:^2.0.2": version: 2.0.2 resolution: "which@npm:2.0.2" @@ -4333,9 +3932,9 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.8.1": - version: 8.8.1 - resolution: "ws@npm:8.8.1" +"ws@npm:^8.9.0": + version: 8.9.0 + resolution: "ws@npm:8.9.0" peerDependencies: bufferutil: ^4.0.1 utf-8-validate: ^5.0.2 @@ -4344,7 +3943,7 @@ __metadata: optional: true utf-8-validate: optional: true - checksum: 2152cf862cae0693f3775bc688a6afb2e989d19d626d215e70f5fcd8eb55b1c3b0d3a6a4052905ec320e2d7734e20aeedbf9744496d62f15a26ad79cf4cf7dae + checksum: 23aa0f021b2eb65c108ec4c3e08c0d81ba01f82b500432dfe327fd6be36079c1d81fdb0eac6464d2a0eb49904d34a9ab8c59619d673fa07b8346f83aeb0cbf12 languageName: node linkType: hard @@ -4355,13 +3954,6 @@ __metadata: languageName: node linkType: hard -"yallist@npm:^2.1.2": - version: 2.1.2 - resolution: "yallist@npm:2.1.2" - checksum: 9ba99409209f485b6fcb970330908a6d41fa1c933f75e08250316cce19383179a6b70a7e0721b89672ebb6199cc377bf3e432f55100da6a7d6e11902b0a642cb - languageName: node - linkType: hard - "yallist@npm:^4.0.0": version: 4.0.0 resolution: "yallist@npm:4.0.0" @@ -4369,16 +3961,6 @@ __metadata: languageName: node linkType: hard -"yauzl@npm:^2.4.2": - version: 2.10.0 - resolution: "yauzl@npm:2.10.0" - dependencies: - buffer-crc32: ~0.2.3 - fd-slicer: ~1.1.0 - checksum: 7f21fe0bbad6e2cb130044a5d1d0d5a0e5bf3d8d4f8c4e6ee12163ce798fee3de7388d22a7a0907f563ac5f9d40f8699a223d3d5c1718da90b0156da6904022b - languageName: node - linkType: hard - "yocto-queue@npm:^0.1.0": version: 0.1.0 resolution: "yocto-queue@npm:0.1.0" @@ -4386,9 +3968,9 @@ __metadata: languageName: node linkType: hard -"zod@npm:^3.17.10": - version: 3.18.0 - resolution: "zod@npm:3.18.0" - checksum: 86a9a9928f4b40a07020d4b9832842fa4a70050c2d7bd1b2866bc1acfd734aa53a40f87a99a4312a637341a608b0105770c7a1f83b56df78e97691b4f5badcd8 +"zod@npm:^3.19.1": + version: 3.19.1 + resolution: "zod@npm:3.19.1" + checksum: 56e420ea5845912324a8fc61833714a2aec84954e418b52660d76502183c6e62fef9447cbfa64349640c5ce190cf2c24267e006bb80f066183e2f3fa9fe11864 languageName: node linkType: hard |