From 661e4c9935aeb8760dafc7ced4bbec6cc356a033 Mon Sep 17 00:00:00 2001 From: IRONM00N <64110067+IRONM00N@users.noreply.github.com> Date: Tue, 14 Jun 2022 12:47:57 -0400 Subject: remove the war crimes that I previously committed - Remove custom typings and replace with declaration merging - Fix the typings for args - Replace all discord-api-types imports with discord.js imports - Fix discord.js breaking changes --- package.json | 12 +- src/arguments/abbreviatedNumber.ts | 2 +- src/arguments/contentWithDuration.ts | 2 +- src/arguments/discordEmoji.ts | 2 +- src/arguments/duration.ts | 2 +- src/arguments/durationSeconds.ts | 2 +- src/arguments/globalUser.ts | 5 +- src/arguments/messageLink.ts | 4 +- src/arguments/permission.ts | 2 +- src/arguments/roleWithDuration.ts | 4 +- src/arguments/snowflake.ts | 4 +- src/arguments/tinyColor.ts | 10 + src/commands/_fake-command/ironmoon.ts | 6 +- src/commands/admin/channelPermissions.ts | 10 +- src/commands/admin/roleAll.ts | 5 +- src/commands/config/_customAutomodPhrases.ts | 8 +- src/commands/config/blacklist.ts | 12 +- src/commands/config/config.ts | 15 +- src/commands/config/disable.ts | 6 +- src/commands/config/features.ts | 8 +- src/commands/config/log.ts | 4 +- src/commands/dev/__template.ts | 4 +- src/commands/dev/debug.ts | 4 +- src/commands/dev/dm.ts | 4 +- src/commands/dev/eval.ts | 30 +- src/commands/dev/javascript.ts | 9 +- src/commands/dev/reload.ts | 8 +- src/commands/dev/say.ts | 6 +- src/commands/dev/servers.ts | 7 +- src/commands/dev/sh.ts | 8 +- src/commands/dev/superUser.ts | 23 +- src/commands/dev/syncAutomod.ts | 4 +- src/commands/dev/test.ts | 300 ++--- src/commands/fun/coinFlip.ts | 4 +- src/commands/fun/dice.ts | 4 +- src/commands/fun/eightBall.ts | 4 +- src/commands/fun/minesweeper.ts | 24 +- src/commands/info/avatar.ts | 7 +- src/commands/info/botInfo.ts | 4 +- src/commands/info/color.ts | 33 +- src/commands/info/guildInfo.ts | 28 +- src/commands/info/help.ts | 14 +- src/commands/info/icon.ts | 4 +- src/commands/info/links.ts | 4 +- src/commands/info/ping.ts | 12 +- src/commands/info/pronouns.ts | 4 +- src/commands/info/snowflake.ts | 16 +- src/commands/info/userInfo.ts | 41 +- src/commands/leveling/leaderboard.ts | 4 +- src/commands/leveling/level.ts | 16 +- src/commands/leveling/levelRoles.ts | 4 +- src/commands/leveling/setLevel.ts | 4 +- src/commands/leveling/setXp.ts | 4 +- src/commands/moderation/_activePunishments.ts | 149 ++- src/commands/moderation/ban.ts | 12 +- src/commands/moderation/block.ts | 11 +- src/commands/moderation/evidence.ts | 47 +- src/commands/moderation/hideCase.ts | 4 +- src/commands/moderation/kick.ts | 9 +- src/commands/moderation/lockdown.ts | 18 +- src/commands/moderation/massBan.ts | 8 +- src/commands/moderation/massEvidence.ts | 4 +- src/commands/moderation/modlog.ts | 6 +- src/commands/moderation/mute.ts | 10 +- src/commands/moderation/purge.ts | 16 +- src/commands/moderation/removeReactionEmoji.ts | 6 +- src/commands/moderation/role.ts | 17 +- src/commands/moderation/slowmode.ts | 17 +- src/commands/moderation/timeout.ts | 8 +- src/commands/moderation/unban.ts | 10 +- src/commands/moderation/unblock.ts | 11 +- src/commands/moderation/unlockdown.ts | 13 +- src/commands/moderation/unmute.ts | 13 +- src/commands/moderation/untimeout.ts | 10 +- src/commands/moderation/warn.ts | 10 +- src/commands/moulberry-bush/capePermissions.ts | 4 +- src/commands/moulberry-bush/capes.ts | 15 +- src/commands/moulberry-bush/giveawayPing.ts | 4 +- src/commands/moulberry-bush/moulHammer.ts | 6 +- src/commands/moulberry-bush/report.ts | 4 +- src/commands/moulberry-bush/rule.ts | 4 +- src/commands/moulberry-bush/serverStatus.ts | 4 +- src/commands/utilities/_poll.ts | 149 ++- src/commands/utilities/activity.ts | 11 +- src/commands/utilities/calculator.ts | 4 +- src/commands/utilities/decode.ts | 5 +- src/commands/utilities/hash.ts | 4 +- src/commands/utilities/highlight-!.ts | 12 +- src/commands/utilities/highlight-add.ts | 7 +- src/commands/utilities/highlight-block.ts | 9 +- src/commands/utilities/highlight-clear.ts | 4 +- src/commands/utilities/highlight-matches.ts | 8 +- src/commands/utilities/highlight-remove.ts | 4 +- src/commands/utilities/highlight-show.ts | 4 +- src/commands/utilities/highlight-unblock.ts | 9 +- src/commands/utilities/price.ts | 8 +- src/commands/utilities/remind.ts | 6 +- src/commands/utilities/reminders.ts | 7 +- src/commands/utilities/steal.ts | 20 +- src/commands/utilities/suicide.ts | 4 +- src/commands/utilities/uuid.ts | 18 +- src/commands/utilities/viewRaw.ts | 19 +- src/commands/utilities/whoHasRole.ts | 8 +- src/commands/utilities/wolframAlpha.ts | 7 +- src/context-menu-commands/message/viewRaw.ts | 5 +- src/context-menu-commands/user/modlog.ts | 8 +- src/context-menu-commands/user/userInfo.ts | 5 +- src/inhibitors/blacklist/channelGlobalBlacklist.ts | 4 +- src/inhibitors/blacklist/channelGuildBlacklist.ts | 4 +- src/inhibitors/blacklist/guildBlacklist.ts | 4 +- src/inhibitors/blacklist/userGlobalBlacklist.ts | 4 +- src/inhibitors/blacklist/userGuildBlacklist.ts | 4 +- src/inhibitors/checks/fatal.ts | 5 +- src/inhibitors/checks/guildUnavailable.ts | 5 +- src/inhibitors/command/dm.ts | 4 +- src/inhibitors/command/globalDisabledCommand.ts | 4 +- src/inhibitors/command/guild.ts | 4 +- src/inhibitors/command/guildDisabledCommand.ts | 4 +- src/inhibitors/command/nsfw.ts | 4 +- src/inhibitors/command/owner.ts | 4 +- src/inhibitors/command/restrictedChannel.ts | 4 +- src/inhibitors/command/restrictedGuild.ts | 4 +- src/inhibitors/command/superUser.ts | 4 +- src/lib/common/AutoMod.ts | 12 +- src/lib/common/ButtonPaginator.ts | 25 +- src/lib/common/ConfirmationPrompt.ts | 8 +- src/lib/common/DeleteButton.ts | 10 +- src/lib/common/HighlightManager.ts | 10 +- src/lib/common/util/Arg.ts | 16 +- src/lib/common/util/Moderation.ts | 51 +- .../discord-akairo/BushArgumentTypeCaster.ts | 4 +- src/lib/extensions/discord-akairo/BushClient.ts | 146 +-- .../extensions/discord-akairo/BushClientUtil.ts | 49 +- src/lib/extensions/discord-akairo/BushCommand.ts | 87 +- .../discord-akairo/BushCommandHandler.ts | 45 +- .../extensions/discord-akairo/BushCommandUtil.ts | 22 - src/lib/extensions/discord-akairo/BushInhibitor.ts | 9 +- .../discord-akairo/BushInhibitorHandler.ts | 5 +- src/lib/extensions/discord-akairo/BushListener.ts | 3 - .../discord-akairo/BushListenerHandler.ts | 5 +- .../extensions/discord-akairo/BushSlashMessage.ts | 34 - src/lib/extensions/discord-akairo/BushTask.ts | 11 +- .../extensions/discord-akairo/BushTaskHandler.ts | 9 +- src/lib/extensions/discord-akairo/SlashMessage.ts | 3 + src/lib/extensions/discord.js/BushActivity.ts | 14 - .../discord.js/BushApplicationCommand.ts | 16 - .../discord.js/BushApplicationCommandManager.ts | 151 --- .../BushApplicationCommandPermissionsManager.ts | 184 --- .../discord.js/BushBaseGuildEmojiManager.ts | 19 - .../discord.js/BushBaseGuildTextChannel.ts | 28 - .../discord.js/BushBaseGuildVoiceChannel.ts | 13 - .../extensions/discord.js/BushButtonInteraction.ts | 30 - .../extensions/discord.js/BushCategoryChannel.ts | 41 - .../discord.js/BushCategoryChannelChildManager.ts | 44 - src/lib/extensions/discord.js/BushChannel.ts | 39 - .../extensions/discord.js/BushChannelManager.ts | 25 - .../discord.js/BushChatInputCommandInteraction.ts | 49 - src/lib/extensions/discord.js/BushClientEvents.ts | 291 ++--- src/lib/extensions/discord.js/BushClientUser.ts | 98 -- src/lib/extensions/discord.js/BushDMChannel.ts | 45 - src/lib/extensions/discord.js/BushEmoji.ts | 14 - src/lib/extensions/discord.js/BushGuild.ts | 782 ------------ .../BushGuildApplicationCommandManager.ts | 114 -- src/lib/extensions/discord.js/BushGuildBan.ts | 15 - src/lib/extensions/discord.js/BushGuildChannel.ts | 47 - .../discord.js/BushGuildChannelManager.ts | 183 --- src/lib/extensions/discord.js/BushGuildEmoji.ts | 20 - .../discord.js/BushGuildEmojiRoleManager.ts | 55 - src/lib/extensions/discord.js/BushGuildManager.ts | 35 - src/lib/extensions/discord.js/BushGuildMember.ts | 1159 ------------------ .../discord.js/BushGuildMemberManager.ts | 177 --- src/lib/extensions/discord.js/BushMessage.ts | 63 - .../extensions/discord.js/BushMessageManager.ts | 113 -- .../extensions/discord.js/BushMessageReaction.ts | 20 - .../discord.js/BushModalSubmitInteraction.ts | 96 -- src/lib/extensions/discord.js/BushNewsChannel.ts | 15 - src/lib/extensions/discord.js/BushPresence.ts | 19 - src/lib/extensions/discord.js/BushReactionEmoji.ts | 16 - src/lib/extensions/discord.js/BushRole.ts | 18 - .../discord.js/BushSelectMenuInteraction.ts | 30 - src/lib/extensions/discord.js/BushStageChannel.ts | 20 - src/lib/extensions/discord.js/BushStageInstance.ts | 17 - src/lib/extensions/discord.js/BushTextChannel.ts | 42 - src/lib/extensions/discord.js/BushThreadChannel.ts | 47 - src/lib/extensions/discord.js/BushThreadManager.ts | 84 -- src/lib/extensions/discord.js/BushThreadMember.ts | 19 - .../discord.js/BushThreadMemberManager.ts | 62 - src/lib/extensions/discord.js/BushUser.ts | 34 - src/lib/extensions/discord.js/BushUserManager.ts | 60 - src/lib/extensions/discord.js/BushVoiceChannel.ts | 40 - src/lib/extensions/discord.js/BushVoiceState.ts | 20 - src/lib/extensions/discord.js/ExtendedGuild.ts | 870 ++++++++++++++ .../extensions/discord.js/ExtendedGuildMember.ts | 1240 ++++++++++++++++++++ src/lib/extensions/discord.js/ExtendedMessage.ts | 12 + src/lib/extensions/discord.js/ExtendedUser.ts | 35 + src/lib/extensions/discord.js/other.ts | 188 --- src/lib/index.ts | 53 +- src/lib/utils/BushLogger.ts | 6 +- src/listeners/bush/joinAutoBan.ts | 5 +- src/listeners/bush/supportThread.ts | 8 +- src/listeners/bush/userUpdateAutoBan.ts | 7 +- src/listeners/commands/commandBlocked.ts | 16 +- src/listeners/commands/commandError.ts | 26 +- .../commands/commandMissingPermissions.ts | 2 +- .../contextCommands/contextCommandBlocked.ts | 2 +- .../contextCommands/contextCommandError.ts | 16 +- .../contextCommands/contextCommandNotFound.ts | 2 +- src/listeners/guild/guildMemberAdd.ts | 8 +- src/listeners/guild/guildMemberRemove.ts | 18 +- src/listeners/guild/joinRoles.ts | 8 +- src/listeners/interaction/interactionCreate.ts | 8 +- src/listeners/member-custom/bushLevelUpdate.ts | 3 + src/listeners/member-custom/bushPurge.ts | 2 +- src/listeners/message/autoPublisher.ts | 3 +- src/listeners/message/blacklistedFile.ts | 4 +- src/listeners/message/level.ts | 12 +- src/listeners/rest/rateLimit.ts | 2 +- .../track-manual-punishments/modlogSyncBan.ts | 8 +- .../track-manual-punishments/modlogSyncKick.ts | 7 +- .../track-manual-punishments/modlogSyncTimeout.ts | 7 +- .../track-manual-punishments/modlogSyncUnban.ts | 8 +- src/listeners/ws/INTERACTION_CREATE.ts | 27 +- src/tasks/removeExpiredPunishements.ts | 6 +- yarn.lock | 236 ++-- 224 files changed, 3510 insertions(+), 6060 deletions(-) create mode 100644 src/arguments/tinyColor.ts delete mode 100644 src/lib/extensions/discord-akairo/BushCommandUtil.ts delete mode 100644 src/lib/extensions/discord-akairo/BushSlashMessage.ts create mode 100644 src/lib/extensions/discord-akairo/SlashMessage.ts delete mode 100644 src/lib/extensions/discord.js/BushActivity.ts delete mode 100644 src/lib/extensions/discord.js/BushApplicationCommand.ts delete mode 100644 src/lib/extensions/discord.js/BushApplicationCommandManager.ts delete mode 100644 src/lib/extensions/discord.js/BushApplicationCommandPermissionsManager.ts delete mode 100644 src/lib/extensions/discord.js/BushBaseGuildEmojiManager.ts delete mode 100644 src/lib/extensions/discord.js/BushBaseGuildTextChannel.ts delete mode 100644 src/lib/extensions/discord.js/BushBaseGuildVoiceChannel.ts delete mode 100644 src/lib/extensions/discord.js/BushButtonInteraction.ts delete mode 100644 src/lib/extensions/discord.js/BushCategoryChannel.ts delete mode 100644 src/lib/extensions/discord.js/BushCategoryChannelChildManager.ts delete mode 100644 src/lib/extensions/discord.js/BushChannel.ts delete mode 100644 src/lib/extensions/discord.js/BushChannelManager.ts delete mode 100644 src/lib/extensions/discord.js/BushChatInputCommandInteraction.ts delete mode 100644 src/lib/extensions/discord.js/BushClientUser.ts delete mode 100644 src/lib/extensions/discord.js/BushDMChannel.ts delete mode 100644 src/lib/extensions/discord.js/BushEmoji.ts delete mode 100644 src/lib/extensions/discord.js/BushGuild.ts delete mode 100644 src/lib/extensions/discord.js/BushGuildApplicationCommandManager.ts delete mode 100644 src/lib/extensions/discord.js/BushGuildBan.ts delete mode 100644 src/lib/extensions/discord.js/BushGuildChannel.ts delete mode 100644 src/lib/extensions/discord.js/BushGuildChannelManager.ts delete mode 100644 src/lib/extensions/discord.js/BushGuildEmoji.ts delete mode 100644 src/lib/extensions/discord.js/BushGuildEmojiRoleManager.ts delete mode 100644 src/lib/extensions/discord.js/BushGuildManager.ts delete mode 100644 src/lib/extensions/discord.js/BushGuildMember.ts delete mode 100644 src/lib/extensions/discord.js/BushGuildMemberManager.ts delete mode 100644 src/lib/extensions/discord.js/BushMessage.ts delete mode 100644 src/lib/extensions/discord.js/BushMessageManager.ts delete mode 100644 src/lib/extensions/discord.js/BushMessageReaction.ts delete mode 100644 src/lib/extensions/discord.js/BushModalSubmitInteraction.ts delete mode 100644 src/lib/extensions/discord.js/BushNewsChannel.ts delete mode 100644 src/lib/extensions/discord.js/BushPresence.ts delete mode 100644 src/lib/extensions/discord.js/BushReactionEmoji.ts delete mode 100644 src/lib/extensions/discord.js/BushRole.ts delete mode 100644 src/lib/extensions/discord.js/BushSelectMenuInteraction.ts delete mode 100644 src/lib/extensions/discord.js/BushStageChannel.ts delete mode 100644 src/lib/extensions/discord.js/BushStageInstance.ts delete mode 100644 src/lib/extensions/discord.js/BushTextChannel.ts delete mode 100644 src/lib/extensions/discord.js/BushThreadChannel.ts delete mode 100644 src/lib/extensions/discord.js/BushThreadManager.ts delete mode 100644 src/lib/extensions/discord.js/BushThreadMember.ts delete mode 100644 src/lib/extensions/discord.js/BushThreadMemberManager.ts delete mode 100644 src/lib/extensions/discord.js/BushUser.ts delete mode 100644 src/lib/extensions/discord.js/BushUserManager.ts delete mode 100644 src/lib/extensions/discord.js/BushVoiceChannel.ts delete mode 100644 src/lib/extensions/discord.js/BushVoiceState.ts create mode 100644 src/lib/extensions/discord.js/ExtendedGuild.ts create mode 100644 src/lib/extensions/discord.js/ExtendedGuildMember.ts create mode 100644 src/lib/extensions/discord.js/ExtendedMessage.ts create mode 100644 src/lib/extensions/discord.js/ExtendedUser.ts delete mode 100644 src/lib/extensions/discord.js/other.ts diff --git a/package.json b/package.json index 61ecb5c..ea08576 100644 --- a/package.json +++ b/package.json @@ -72,20 +72,20 @@ "chalk": "^5.0.1", "deep-lock": "^1.0.0", "discord-akairo": "npm:@notenoughupdates/discord-akairo@dev", - "discord-api-types": "0.33.5", + "discord-api-types": "0.34.0", "discord.js": "npm:@notenoughupdates/discord.js@dev", "fuse.js": "^6.6.2", "gif-to-apng": "^0.1.2", - "googleapis": "^101.0.0", + "googleapis": "^102.0.0", "got": "^12.1.0", "lodash": "^4.17.21", "mathjs": "^10.6.1", "nanoid": "^4.0.0", - "node-os-utils": "^1.3.6", + "node-os-utils": "^1.3.7", "numeral": "^2.0.6", "pg": "^8.7.3", "pg-hstore": "^2.3.4", - "prettier": "^2.6.2", + "prettier": "^2.7.0", "pretty-bytes": "^6.0.0", "rimraf": "^3.0.2", "sequelize": "6.20.1", @@ -107,8 +107,8 @@ "@types/rimraf": "^3.0.2", "@types/tinycolor2": "^1.4.3", "@types/validator": "^13.7.3", - "@typescript-eslint/eslint-plugin": "^5.27.1", - "@typescript-eslint/parser": "^5.27.1", + "@typescript-eslint/eslint-plugin": "^5.28.0", + "@typescript-eslint/parser": "^5.28.0", "eslint": "^8.17.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-deprecation": "^1.3.2", diff --git a/src/arguments/abbreviatedNumber.ts b/src/arguments/abbreviatedNumber.ts index 43d2938..eba9214 100644 --- a/src/arguments/abbreviatedNumber.ts +++ b/src/arguments/abbreviatedNumber.ts @@ -1,4 +1,4 @@ -import { type BushArgumentTypeCaster } from '#lib'; +import type { BushArgumentTypeCaster } from '#lib'; import assert from 'assert'; import numeral from 'numeral'; assert(typeof numeral === 'function'); diff --git a/src/arguments/contentWithDuration.ts b/src/arguments/contentWithDuration.ts index cd8549b..e92997a 100644 --- a/src/arguments/contentWithDuration.ts +++ b/src/arguments/contentWithDuration.ts @@ -1,4 +1,4 @@ -import { type BushArgumentTypeCaster, type ParsedDuration } from '#lib'; +import type { BushArgumentTypeCaster, ParsedDuration } from '#lib'; export const contentWithDuration: BushArgumentTypeCaster> = async (_, phrase) => { return client.util.parseDuration(phrase); diff --git a/src/arguments/discordEmoji.ts b/src/arguments/discordEmoji.ts index 3edf123..57710e4 100644 --- a/src/arguments/discordEmoji.ts +++ b/src/arguments/discordEmoji.ts @@ -1,5 +1,5 @@ import type { BushArgumentTypeCaster } from '#lib'; -import type { Snowflake } from 'discord-api-types/v10'; +import type { Snowflake } from 'discord.js'; export const discordEmoji: BushArgumentTypeCaster = (_, phrase) => { if (!phrase) return null; diff --git a/src/arguments/duration.ts b/src/arguments/duration.ts index 58593ef..ffd9159 100644 --- a/src/arguments/duration.ts +++ b/src/arguments/duration.ts @@ -1,4 +1,4 @@ -import { type BushArgumentTypeCaster } from '#lib'; +import type { BushArgumentTypeCaster } from '#lib'; export const duration: BushArgumentTypeCaster = (_, phrase) => { return client.util.parseDuration(phrase).duration; diff --git a/src/arguments/durationSeconds.ts b/src/arguments/durationSeconds.ts index 74d136f..432fd8c 100644 --- a/src/arguments/durationSeconds.ts +++ b/src/arguments/durationSeconds.ts @@ -1,4 +1,4 @@ -import { type BushArgumentTypeCaster } from '#lib'; +import type { BushArgumentTypeCaster } from '#lib'; export const durationSeconds: BushArgumentTypeCaster = (_, phrase) => { phrase += 's'; diff --git a/src/arguments/globalUser.ts b/src/arguments/globalUser.ts index 08350d2..df4f5d9 100644 --- a/src/arguments/globalUser.ts +++ b/src/arguments/globalUser.ts @@ -1,6 +1,7 @@ -import { type BushArgumentTypeCaster, type BushUser } from '#lib'; +import type { BushArgumentTypeCaster } from '#lib'; +import type { User } from 'discord.js'; // resolve non-cached users -export const globalUser: BushArgumentTypeCaster> = async (_, phrase) => { +export const globalUser: BushArgumentTypeCaster> = async (_, phrase) => { return client.users.resolve(phrase) ?? (await client.users.fetch(`${phrase}`).catch(() => null)); }; diff --git a/src/arguments/messageLink.ts b/src/arguments/messageLink.ts index 5c91a4b..457410e 100644 --- a/src/arguments/messageLink.ts +++ b/src/arguments/messageLink.ts @@ -1,5 +1,5 @@ -import { type BushArgumentTypeCaster } from '#lib'; -import { type Message } from 'discord.js'; +import type { BushArgumentTypeCaster } from '#lib'; +import type { Message } from 'discord.js'; export const messageLink: BushArgumentTypeCaster> = async (_, phrase) => { const match = new RegExp(client.consts.regex.messageLink).exec(phrase); diff --git a/src/arguments/permission.ts b/src/arguments/permission.ts index cf4c42c..98bfe74 100644 --- a/src/arguments/permission.ts +++ b/src/arguments/permission.ts @@ -1,4 +1,4 @@ -import { type BushArgumentTypeCaster } from '#lib'; +import type { BushArgumentTypeCaster } from '#lib'; import { PermissionFlagsBits, type PermissionsString } from 'discord.js'; export const permission: BushArgumentTypeCaster = (_, phrase) => { diff --git a/src/arguments/roleWithDuration.ts b/src/arguments/roleWithDuration.ts index 5f1da98..e338b79 100644 --- a/src/arguments/roleWithDuration.ts +++ b/src/arguments/roleWithDuration.ts @@ -1,5 +1,5 @@ -import { type BushArgumentTypeCaster } from '#lib'; -import { type Role } from 'discord.js'; +import type { BushArgumentTypeCaster } from '#lib'; +import type { Role } from 'discord.js'; export const roleWithDuration: BushArgumentTypeCaster> = async (message, phrase) => { // eslint-disable-next-line prefer-const diff --git a/src/arguments/snowflake.ts b/src/arguments/snowflake.ts index 15896ae..dc83909 100644 --- a/src/arguments/snowflake.ts +++ b/src/arguments/snowflake.ts @@ -1,5 +1,5 @@ -import { type BushArgumentTypeCaster } from '#lib'; -import { type Snowflake } from 'discord.js'; +import type { BushArgumentTypeCaster } from '#lib'; +import type { Snowflake } from 'discord.js'; export const snowflake: BushArgumentTypeCaster = (_, phrase) => { if (!phrase) return null; diff --git a/src/arguments/tinyColor.ts b/src/arguments/tinyColor.ts new file mode 100644 index 0000000..8d01928 --- /dev/null +++ b/src/arguments/tinyColor.ts @@ -0,0 +1,10 @@ +import type { BushArgumentTypeCaster } from '#lib'; +import assert from 'assert'; +import tinycolorModule from 'tinycolor2'; +assert(tinycolorModule); + +export const tinyColor: BushArgumentTypeCaster = (_message, phrase) => { + // if the phase is a number it converts it to hex incase it could be representing a color in decimal + const newPhase = isNaN(phrase as any) ? phrase : `#${Number(phrase).toString(16)}`; + return tinycolorModule(newPhase).isValid() ? newPhase : null; +}; diff --git a/src/commands/_fake-command/ironmoon.ts b/src/commands/_fake-command/ironmoon.ts index 1f63c06..cb50c7e 100644 --- a/src/commands/_fake-command/ironmoon.ts +++ b/src/commands/_fake-command/ironmoon.ts @@ -1,4 +1,4 @@ -import { BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, type CommandMessage, type SlashMessage } from '#lib'; export default class IronmoonCommand extends BushCommand { public constructor() { @@ -13,13 +13,13 @@ export default class IronmoonCommand extends BushCommand { }); } - public override condition(message: BushMessage): boolean { + public override condition(message: CommandMessage): boolean { return false; if (message.content.toLowerCase().includes('ironmoon')) return true; else return false; } - public override async exec(message: BushMessage | BushSlashMessage) { + public override async exec(message: CommandMessage | SlashMessage) { return await message.util.reply('Your message included the word ironmoon.'); } } diff --git a/src/commands/admin/channelPermissions.ts b/src/commands/admin/channelPermissions.ts index d58a7da..f6240a5 100644 --- a/src/commands/admin/channelPermissions.ts +++ b/src/commands/admin/channelPermissions.ts @@ -1,4 +1,4 @@ -import { BushCommand, ButtonPaginator, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, ButtonPaginator, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, EmbedBuilder, PermissionFlagsBits } from 'discord.js'; @@ -57,12 +57,8 @@ export default class ChannelPermissionsCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, - args: { - target: ArgType<'member'> | ArgType<'role'>; - permission: ArgType<'permission'>; - state: 'true' | 'false' | 'neutral'; - } + message: CommandMessage | SlashMessage, + args: { target: ArgType<'member' | 'role'>; permission: ArgType<'permission'>; state: 'true' | 'false' | 'neutral' } ) { assert(message.inGuild()); if (message.util.isSlashMessage(message)) await message.interaction.deferReply(); diff --git a/src/commands/admin/roleAll.ts b/src/commands/admin/roleAll.ts index 7cb7346..80952cc 100644 --- a/src/commands/admin/roleAll.ts +++ b/src/commands/admin/roleAll.ts @@ -1,4 +1,5 @@ -import { AllowedMentions, BushCommand, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { AllowedMentions, BushCommand, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; + import assert from 'assert'; import { ApplicationCommandOptionType, PermissionFlagsBits, type GuildMember } from 'discord.js'; @@ -39,7 +40,7 @@ export default class RoleAllCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage, args: { role: ArgType<'role'>; bots: ArgType<'boolean'> }) { + public override async exec(message: CommandMessage | SlashMessage, args: { role: ArgType<'role'>; bots: ArgType<'flag'> }) { assert(message.inGuild()); if (!message.member!.permissions.has(PermissionFlagsBits.Administrator) && !message.member!.user.isOwner()) return await message.util.reply(`${util.emojis.error} You must have admin perms to use this command.`); diff --git a/src/commands/config/_customAutomodPhrases.ts b/src/commands/config/_customAutomodPhrases.ts index bd9bab5..13887ae 100644 --- a/src/commands/config/_customAutomodPhrases.ts +++ b/src/commands/config/_customAutomodPhrases.ts @@ -1,5 +1,5 @@ -// import { BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; -// import { PermissionFlagsBits } from 'discord.js'; +// import { BushCommand, type ArgType, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; +// import { ApplicationCommandOptionType, PermissionFlagsBits } from 'discord.js'; // export default class CustomAutomodPhrasesCommand extends BushCommand { // public constructor() { @@ -36,7 +36,7 @@ // } // public override async exec( -// message: BushMessage | BushSlashMessage, -// args: { required_argument: string; optional_argument: string } +// message: CommandMessage | SlashMessage, +// args: { required_argument: ArgType<'string'>; optional_argument: OptArgType<'string'> } // ) {} // } diff --git a/src/commands/config/blacklist.ts b/src/commands/config/blacklist.ts index ba2d24a..de457c0 100644 --- a/src/commands/config/blacklist.ts +++ b/src/commands/config/blacklist.ts @@ -1,6 +1,6 @@ -import { AllowedMentions, BushCommand, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { AllowedMentions, BushCommand, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; -import { ApplicationCommandOptionType, PermissionFlagsBits, User } from 'discord.js'; +import { ApplicationCommandOptionType, GuildMember, PermissionFlagsBits, User } from 'discord.js'; export default class BlacklistCommand extends BushCommand { public constructor() { @@ -47,11 +47,11 @@ export default class BlacklistCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, + message: CommandMessage | SlashMessage, args: { action?: 'blacklist' | 'unblacklist'; - target: ArgType<'channel'> | ArgType<'user'> | string; // there is no way to combine channel and user in slash commands without making subcommands - global: ArgType<'boolean'>; + target: ArgType<'channel' | 'user'> | string; // there is no way to combine channel and user in slash commands without making subcommands + global: ArgType<'flag'>; } ) { let action: 'blacklist' | 'unblacklist' | 'toggle' = @@ -88,7 +88,7 @@ export default class BlacklistCommand extends BushCommand { const success = await (global ? util.setGlobal(key, newValue) - : message.guild!.setSetting(key, newValue, message.member!) + : message.guild!.setSetting(key, newValue, message.member as GuildMember) ).catch(() => false); if (!success) diff --git a/src/commands/config/config.ts b/src/commands/config/config.ts index e203d12..689a3af 100644 --- a/src/commands/config/config.ts +++ b/src/commands/config/config.ts @@ -4,12 +4,13 @@ import { guildSettingsObj, settingsArr, type ArgType, - type BushMessage, - type BushSlashMessage, + type CommandMessage, type GuildSettings, - type GuildSettingType + type GuildSettingType, + type SlashMessage } from '#lib'; import assert from 'assert'; + import { type ArgumentGeneratorReturn, type SlashOption } from 'discord-akairo'; import { ActionRowBuilder, @@ -149,7 +150,7 @@ export default class ConfigCommand extends BushCommand { }); } - public override *args(message: BushMessage): ArgumentGeneratorReturn { + public override *args(message: CommandMessage): ArgumentGeneratorReturn { const optional = message.util.parsed!.alias === 'settings'; const setting: GuildSettings = yield { id: 'setting', @@ -205,13 +206,13 @@ export default class ConfigCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, + message: CommandMessage | SlashMessage, args: { setting?: GuildSettings; subcommandGroup?: GuildSettings; action?: Action; subcommand?: Action; - value: ArgType<'channel'> | ArgType<'role'> | string; + value: ArgType<'channel' | 'role'> | string; } ) { assert(message.inGuild()); @@ -305,7 +306,7 @@ export default class ConfigCommand extends BushCommand { } public async generateMessageOptions( - message: BushMessage | BushSlashMessage, + message: CommandMessage | SlashMessage, setting?: undefined | keyof typeof guildSettingsObj ): Promise { assert(message.inGuild()); diff --git a/src/commands/config/disable.ts b/src/commands/config/disable.ts index dea7b0f..373b5f6 100644 --- a/src/commands/config/disable.ts +++ b/src/commands/config/disable.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushCommand, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { AllowedMentions, BushCommand, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, AutocompleteInteraction, PermissionFlagsBits } from 'discord.js'; import Fuse from 'fuse.js'; @@ -54,8 +54,8 @@ export default class DisableCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, - args: { action?: 'enable' | 'disable'; command: ArgType<'commandAlias'> | string; global: boolean } + message: CommandMessage | SlashMessage, + args: { action?: 'enable' | 'disable'; command: ArgType<'commandAlias'> | string; global: ArgType<'flag'> } ) { assert(message.inGuild()); diff --git a/src/commands/config/features.ts b/src/commands/config/features.ts index 2eee0fe..95ae544 100644 --- a/src/commands/config/features.ts +++ b/src/commands/config/features.ts @@ -2,9 +2,9 @@ import { BushCommand, guildFeaturesArr, guildFeaturesObj, - type BushMessage, - type BushSlashMessage, - type GuildFeatures + type CommandMessage, + type GuildFeatures, + type SlashMessage } from '#lib'; import assert from 'assert'; import { @@ -32,7 +32,7 @@ export default class FeaturesCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage) { + public override async exec(message: CommandMessage | SlashMessage) { assert(message.inGuild()); const featureEmbed = new EmbedBuilder().setTitle(`${message.guild.name}'s Features`).setColor(util.colors.default); diff --git a/src/commands/config/log.ts b/src/commands/config/log.ts index f99f007..7c76bdf 100644 --- a/src/commands/config/log.ts +++ b/src/commands/config/log.ts @@ -1,4 +1,4 @@ -import { BushCommand, guildLogsArr, type ArgType, type BushMessage, type BushSlashMessage, type GuildLogType } from '#lib'; +import { BushCommand, guildLogsArr, type ArgType, type CommandMessage, type GuildLogType, type SlashMessage } from '#lib'; import assert from 'assert'; import { ArgumentGeneratorReturn } from 'discord-akairo'; import { ApplicationCommandOptionType, ChannelType, PermissionFlagsBits } from 'discord.js'; @@ -70,7 +70,7 @@ export default class LogCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, + message: CommandMessage | SlashMessage, args: { log_type: GuildLogType; channel: ArgType<'textChannel'> } ) { assert(message.inGuild()); diff --git a/src/commands/dev/__template.ts b/src/commands/dev/__template.ts index ace8802..7bcce32 100644 --- a/src/commands/dev/__template.ts +++ b/src/commands/dev/__template.ts @@ -1,4 +1,4 @@ -import { BushCommand, type ArgType, type BushMessage, type BushSlashMessage, type OptArgType } from '#lib'; +import { BushCommand, type ArgType, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; import { ApplicationCommandOptionType } from 'discord.js'; export default class TemplateCommand extends BushCommand { @@ -39,7 +39,7 @@ export default class TemplateCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, + message: CommandMessage | SlashMessage, args: { required_argument: ArgType<'string'>; optional_argument: OptArgType<'string'> } ) { return await message.util.reply(`${util.emojis.error} Do not use the template command.`); diff --git a/src/commands/dev/debug.ts b/src/commands/dev/debug.ts index f8c614d..682a93d 100644 --- a/src/commands/dev/debug.ts +++ b/src/commands/dev/debug.ts @@ -1,4 +1,4 @@ -// import { BushCommand, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; +// import { BushCommand, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; // import { ApplicationCommandOptionType, AutocompleteInteraction, ChatInputCommandInteraction } from 'discord.js'; // import Fuse from 'fuse.js'; @@ -29,7 +29,7 @@ // }); // } -// public override async exec(message: BushMessage | BushSlashMessage, args: { action: ArgType<'string'> }) { +// public override async exec(message: CommandMessage | SlashMessage, args: { action: ArgType<'string'> }) { // if (args.action === 'util.reply') { // return await message.util.reply(`This is a util.reply`); // } else if (args.action === 'util.reply-object') { diff --git a/src/commands/dev/dm.ts b/src/commands/dev/dm.ts index 0646f59..468fb20 100644 --- a/src/commands/dev/dm.ts +++ b/src/commands/dev/dm.ts @@ -1,4 +1,4 @@ -import { BushCommand, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; import { ApplicationCommandOptionType } from 'discord.js'; export default class DMCommand extends BushCommand { @@ -37,7 +37,7 @@ export default class DMCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, + message: CommandMessage | SlashMessage, args: { user: ArgType<'user'>; content: ArgType<'string'> } ) { try { diff --git a/src/commands/dev/eval.ts b/src/commands/dev/eval.ts index 8af280d..b8ee9e4 100644 --- a/src/commands/dev/eval.ts +++ b/src/commands/dev/eval.ts @@ -3,8 +3,6 @@ import { ActivePunishment, BushCommand, BushInspectOptions, - BushMessage, - BushSlashMessage, CodeBlockLang, Global, Guild, @@ -12,7 +10,9 @@ import { ModLog, Shared, StickyRole, - type ArgType + type ArgType, + type CommandMessage, + type SlashMessage } from '#lib'; import { Snowflake as Snowflake_ } from '@sapphire/snowflake'; import assert from 'assert'; @@ -57,7 +57,7 @@ const { transpile } = ts, /* eslint-enable @typescript-eslint/no-unused-vars */ // prettier-ignore -util.assertAll(ActivePunishment, BushCommand, BushMessage, BushSlashMessage, Global, Guild, Level, ModLog, Shared, StickyRole, Snowflake_, Canvas, exec, ActionRow, ButtonComponent, ButtonInteraction, Collection, Collector, CommandInteraction, ContextMenuCommandInteraction, DMChannel, Embed, Emoji, Interaction, InteractionCollector, Message, Attachment, MessageCollector, OAuth2Scopes, PermissionFlagsBits, PermissionsBitField, ReactionCollector, SelectMenuComponent, Util, path, ts, fileURLToPath, promisify, assert, got, transpile, emojis, colors, sh, SnowflakeUtil, __dirname); +util.assertAll(ActivePunishment, BushCommand, Global, Guild, Level, ModLog, Shared, StickyRole, Snowflake_, Canvas, exec, ActionRow, ButtonComponent, ButtonInteraction, Collection, Collector, CommandInteraction, ContextMenuCommandInteraction, DMChannel, Embed, Emoji, Interaction, InteractionCollector, Message, Attachment, MessageCollector, OAuth2Scopes, PermissionFlagsBits, PermissionsBitField, ReactionCollector, SelectMenuComponent, Util, path, ts, fileURLToPath, promisify, assert, got, transpile, emojis, colors, sh, SnowflakeUtil, __dirname); export default class EvalCommand extends BushCommand { public constructor() { @@ -180,7 +180,7 @@ export default class EvalCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, + message: CommandMessage | SlashMessage, { code: argCode, sel_depth: selDepth, @@ -196,15 +196,15 @@ export default class EvalCommand extends BushCommand { }: { code: ArgType<'string'>; sel_depth: ArgType<'integer'>; - sudo: ArgType<'boolean'>; - silent: ArgType<'boolean'>; - delete_msg: ArgType<'boolean'>; - typescript: ArgType<'boolean'>; - hidden: ArgType<'boolean'>; - show_proto: ArgType<'boolean'>; - show_methods: ArgType<'boolean'>; - async: ArgType<'boolean'>; - no_inspect_strings: ArgType<'boolean'>; + sudo: ArgType<'flag'>; + silent: ArgType<'flag'>; + delete_msg: ArgType<'flag'>; + typescript: ArgType<'flag'>; + hidden: ArgType<'flag'>; + show_proto: ArgType<'flag'>; + show_methods: ArgType<'flag'>; + async: ArgType<'flag'>; + no_inspect_strings: ArgType<'flag'>; } ) { if (!message.author.isOwner()) @@ -322,5 +322,3 @@ interface CodeBlockCustomOptions extends CodeBlockOptions { prototype?: boolean; methods?: boolean; } - -/** @typedef {ActivePunishment|Global|Guild|Level|ModLog|StickyRole|ButtonInteraction|Collection|Collector|CommandInteraction|ContextMenuCommandInteraction|DMChannel|Emoji|Interaction|InteractionCollector|Message|ActionRow|Attachment|ButtonComponent|MessageCollector|SelectMenuComponent|ReactionCollector|Util|Canvas|Shared|PermissionsBitField|got} VSCodePleaseDontRemove */ diff --git a/src/commands/dev/javascript.ts b/src/commands/dev/javascript.ts index be8fdce..fd1894b 100644 --- a/src/commands/dev/javascript.ts +++ b/src/commands/dev/javascript.ts @@ -1,4 +1,4 @@ -import { BushCommand, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, type ArgType, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, EmbedBuilder } from 'discord.js'; import { VM } from 'vm2'; @@ -41,11 +41,8 @@ export default class JavascriptCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, - args: { - sel_depth: ArgType<'integer'>; - code: string; - } + message: CommandMessage | SlashMessage, + args: { code: ArgType<'string'>; sel_depth: OptArgType<'integer'> } ) { if (!message.author.isSuperUser()) return await message.util.reply(`${util.emojis.error} Only super users can run this command.`); diff --git a/src/commands/dev/reload.ts b/src/commands/dev/reload.ts index 17802b0..96c395f 100644 --- a/src/commands/dev/reload.ts +++ b/src/commands/dev/reload.ts @@ -1,4 +1,4 @@ -import { BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, type CommandMessage, type SlashMessage } from '#lib'; export default class ReloadCommand extends BushCommand { public constructor() { @@ -13,7 +13,7 @@ export default class ReloadCommand extends BushCommand { // id: 'fast', // description: 'Whether or not to use esbuild for fast compiling.', // match: 'flag', - // flag: '--fast', + // flag: ['--fast'], // prompt: 'Would you like to use esbuild for fast compiling?', // optional: true, // slashType: ApplicationCommandOptionType.Boolean @@ -27,14 +27,14 @@ export default class ReloadCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage /* { fast }: { fast: boolean } */) { + public override async exec(message: CommandMessage | SlashMessage /* args: { fast: ArgType<'flag'> } */) { if (!message.author.isOwner()) return await message.util.reply(`${util.emojis.error} Only my developers can run this command.`); let output: { stdout: string; stderr: string }; try { const s = new Date(); - output = await util.shell(`yarn build:${/* fast ? 'esbuild' : */ 'tsc'}`); + output = await util.shell(`yarn build:${/* args.fast ? 'esbuild' : */ 'tsc'}`); await Promise.all([ client.commandHandler.reloadAll(), client.listenerHandler.reloadAll(), diff --git a/src/commands/dev/say.ts b/src/commands/dev/say.ts index bbada0c..a452126 100644 --- a/src/commands/dev/say.ts +++ b/src/commands/dev/say.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { AllowedMentions, BushCommand, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; import { ApplicationCommandOptionType } from 'discord.js'; export default class SayCommand extends BushCommand { @@ -27,7 +27,7 @@ export default class SayCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage, args: { content: string }) { + public override async exec(message: CommandMessage | SlashMessage, args: { content: ArgType<'string'> }) { if (!message.author.isOwner()) return await message.util.reply(`${util.emojis.error} Only my developers can run this command.`); @@ -35,7 +35,7 @@ export default class SayCommand extends BushCommand { await message.util.send({ content: args.content, allowedMentions: AllowedMentions.none() }).catch(() => null); } - public override async execSlash(message: BushSlashMessage, args: { content: string }) { + public override async execSlash(message: SlashMessage, args: { content: string }) { if (!client.config.owners.includes(message.author.id)) { return await message.interaction.reply({ content: `${util.emojis.error} Only my developers can run this command.`, diff --git a/src/commands/dev/servers.ts b/src/commands/dev/servers.ts index 0701158..e99bcda 100644 --- a/src/commands/dev/servers.ts +++ b/src/commands/dev/servers.ts @@ -1,7 +1,6 @@ -import { BushCommand, ButtonPaginator, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, ButtonPaginator, type CommandMessage, type SlashMessage } from '#lib'; import { stripIndent } from '#tags'; -import type { APIEmbed } from 'discord-api-types/v10'; -import type { Guild } from 'discord.js'; +import { type APIEmbed, type Guild } from 'discord.js'; export default class ServersCommand extends BushCommand { public constructor() { @@ -17,7 +16,7 @@ export default class ServersCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage) { + public override async exec(message: CommandMessage | SlashMessage) { const guilds = [...client.guilds.cache.sort((a, b) => (a.memberCount < b.memberCount ? 1 : -1)).values()]; const chunkedGuilds: Guild[][] = util.chunk(guilds, 10); const embeds: APIEmbed[] = chunkedGuilds.map((chunk) => { diff --git a/src/commands/dev/sh.ts b/src/commands/dev/sh.ts index d47ecd2..8c62f5d 100644 --- a/src/commands/dev/sh.ts +++ b/src/commands/dev/sh.ts @@ -1,4 +1,4 @@ -import { BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { ArgType, BushCommand, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; import chalk from 'chalk'; import { exec } from 'child_process'; @@ -40,10 +40,10 @@ export default class ShCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage, { command }: { command: string }) { + public override async exec(message: CommandMessage | SlashMessage, args: { command: ArgType<'string'> }) { if (!client.config.owners.includes(message.author.id)) return await message.util.reply(`${util.emojis.error} Only my developers can run this command.`); - const input = clean(command); + const input = clean(args.command); const embed = new EmbedBuilder() .setColor(util.colors.gray) @@ -65,7 +65,7 @@ export default class ShCommand extends BushCommand { return abc.replace(new RegExp(pattern, 'g'), ''); } */ try { - const output = await sh(command, { env: { ...process.env, FORCE_COLOR: 'true' } }); + const output = await sh(args.command, { env: { ...process.env, FORCE_COLOR: 'true' } }); const stdout = /* strip( */ clean(output.stdout); /* ) */ const stderr = /* strip( */ clean(output.stderr); /* ) */ diff --git a/src/commands/dev/superUser.ts b/src/commands/dev/superUser.ts index d62ac8e..6a2b745 100644 --- a/src/commands/dev/superUser.ts +++ b/src/commands/dev/superUser.ts @@ -1,6 +1,5 @@ -import { BushCommand, type ArgType, type BushMessage } from '#lib'; -import { ArgumentGeneratorReturn } from 'discord-akairo'; -import { ArgumentTypeCasterReturn } from 'discord-akairo/dist/src/struct/commands/arguments/Argument.js'; +import { BushCommand, type ArgType, type CommandMessage } from '#lib'; +import { type ArgumentGeneratorReturn, type ArgumentTypeCasterReturn } from 'discord-akairo'; export default class SuperUserCommand extends BushCommand { public constructor() { @@ -54,30 +53,30 @@ export default class SuperUserCommand extends BushCommand { return { action, user }; } - public override async exec(message: BushMessage, { action, user }: { action: 'add' | 'remove'; user: ArgType<'user'> }) { + public override async exec(message: CommandMessage, args: { action: 'add' | 'remove'; user: ArgType<'user'> }) { if (!message.author.isOwner()) return await message.util.reply(`${util.emojis.error} Only my developers can run this command.`); const superUsers: string[] = util.getShared('superUsers'); - if (action === 'add' ? superUsers.includes(user.id) : !superUsers.includes(user.id)) + if (args.action === 'add' ? superUsers.includes(args.user.id) : !superUsers.includes(args.user.id)) return message.util.reply( - `${util.emojis.warn} ${util.format.input(user.tag)} is ${action === 'add' ? 'already' : 'not'} a superuser.` + `${util.emojis.warn} ${util.format.input(args.user.tag)} is ${args.action === 'add' ? 'already' : 'not'} a superuser.` ); - const success = await util.insertOrRemoveFromShared(action, 'superUsers', user.id).catch(() => false); + const success = await util.insertOrRemoveFromShared(args.action, 'superUsers', args.user.id).catch(() => false); if (success) { return await message.util.reply( - `${util.emojis.success} ${action == 'remove' ? '' : 'made'} ${util.format.input(user.tag)} ${ - action == 'remove' ? 'is no longer ' : '' + `${util.emojis.success} ${args.action == 'remove' ? '' : 'made'} ${util.format.input(args.user.tag)} ${ + args.action == 'remove' ? 'is no longer ' : '' }a superuser.` ); } else { return await message.util.reply( - `${util.emojis.error} There was an error ${action == 'remove' ? `removing` : 'making'} ${util.format.input(user.tag)} ${ - action == 'remove' ? `from` : 'to' - } the superuser list.` + `${util.emojis.error} There was an error ${args.action == 'remove' ? `removing` : 'making'} ${util.format.input( + args.user.tag + )} ${args.action == 'remove' ? `from` : 'to'} the superuser list.` ); } } diff --git a/src/commands/dev/syncAutomod.ts b/src/commands/dev/syncAutomod.ts index 4d4a6d7..9954e70 100644 --- a/src/commands/dev/syncAutomod.ts +++ b/src/commands/dev/syncAutomod.ts @@ -1,4 +1,4 @@ -import { BushCommand, Shared, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, Shared, type CommandMessage, type SlashMessage } from '#lib'; import got from 'got'; import typescript from 'typescript'; import { NodeVM } from 'vm2'; @@ -18,7 +18,7 @@ export default class SyncAutomodCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage) { + public override async exec(message: CommandMessage | SlashMessage) { if (!message.author.isOwner() && message.author.id !== '497789163555389441') return await message.util.reply(`${util.emojis.error} Only a very select few may use this command.`); diff --git a/src/commands/dev/test.ts b/src/commands/dev/test.ts index 2d7b1f8..d54d544 100644 --- a/src/commands/dev/test.ts +++ b/src/commands/dev/test.ts @@ -1,11 +1,11 @@ -import { BushCommand, ButtonPaginator, Shared, type BushMessage } from '#lib'; -import { Routes } from 'discord-api-types/rest/v9'; +import { BushCommand, ButtonPaginator, OptArgType, Shared, type CommandMessage } from '#lib'; import { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder, GatewayDispatchEvents, + Routes, type ApplicationCommand, type Collection } from 'discord.js'; @@ -38,7 +38,7 @@ export default class TestCommand extends BushCommand { }); } - public override async exec(message: BushMessage, args: { feature: string }) { + public override async exec(message: CommandMessage, args: { feature: OptArgType<'string'> }) { const responses = [ 'Yes master.', 'Test it your self bitch, I am hungry.', @@ -51,166 +51,168 @@ export default class TestCommand extends BushCommand { return await message.util.reply(responses[Math.floor(Math.random() * responses.length)]); } - if (['button', 'buttons'].includes(args.feature?.toLowerCase())) { - const buttonRow = new ActionRowBuilder().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' }) - ]); - return await message.util.reply({ content: 'buttons', components: [buttonRow] }); - } else if (['embed', 'button embed'].includes(args.feature?.toLowerCase())) { - const embed = new EmbedBuilder() - .addFields([{ name: 'Field Name', value: 'Field Content' }]) - .setAuthor({ name: 'Author', iconURL: 'https://www.w3schools.com/w3css/img_snowtops.jpg', url: 'https://google.com/' }) - .setColor(message.member?.displayColor ?? util.colors.default) - .setDescription('Description') - .setFooter({ text: 'Footer', iconURL: message.author.avatarURL() ?? undefined }) - .setURL('https://duckduckgo.com/') - .setTimestamp() - .setImage('https://media.sproutsocial.com/uploads/2017/02/10x-featured-social-media-image-size.png') - .setThumbnail( - 'https://images.unsplash.com/photo-1501183007986-d0d080b147f9?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2134&q=80' - ) - .setTitle('Title'); + if (args.feature) { + if (['button', 'buttons'].includes(args.feature?.toLowerCase())) { + const buttonRow = new ActionRowBuilder().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' }) + ]); + return await message.util.reply({ content: 'buttons', components: [buttonRow] }); + } else if (['embed', 'button embed'].includes(args.feature?.toLowerCase())) { + const embed = new EmbedBuilder() + .addFields([{ name: 'Field Name', value: 'Field Content' }]) + .setAuthor({ name: 'Author', iconURL: 'https://www.w3schools.com/w3css/img_snowtops.jpg', url: 'https://google.com/' }) + .setColor(message.member?.displayColor ?? util.colors.default) + .setDescription('Description') + .setFooter({ text: 'Footer', iconURL: message.author.avatarURL() ?? undefined }) + .setURL('https://duckduckgo.com/') + .setTimestamp() + .setImage('https://media.sproutsocial.com/uploads/2017/02/10x-featured-social-media-image-size.png') + .setThumbnail( + 'https://images.unsplash.com/photo-1501183007986-d0d080b147f9?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2134&q=80' + ) + .setTitle('Title'); - const buttonRow = new ActionRowBuilder().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[] = []; - for (let a = 1; a <= 5; a++) { - const row = new ActionRowBuilder(); - for (let b = 1; b <= 5; b++) { - const id = (a + 5 * (b - 1)).toString(); - const button = new ButtonBuilder({ style: ButtonStyle.Primary, customId: id, label: id }); - row.addComponents([button]); + const buttonRow = new ActionRowBuilder().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[] = []; + for (let a = 1; a <= 5; a++) { + const row = new ActionRowBuilder(); + for (let b = 1; b <= 5; b++) { + const id = (a + 5 * (b - 1)).toString(); + const button = new ButtonBuilder({ style: ButtonStyle.Primary, customId: id, label: id }); + row.addComponents([button]); + } + buttonRows.push(row); } - buttonRows.push(row); - } - return await message.util.reply({ content: 'buttons', components: buttonRows }); - } else if (['paginate'].includes(args.feature?.toLowerCase())) { - const embeds = []; - for (let i = 1; i <= 5; i++) { - embeds.push(new EmbedBuilder().setDescription(i.toString())); - } - return await ButtonPaginator.send(message, embeds); - } else if (['lots of embeds'].includes(args.feature?.toLowerCase())) { - const description = 'This is a description.'; - const _avatar = message.author.avatarURL() ?? undefined; - const author = { name: 'This is a author', iconURL: _avatar }; - const footer = { text: 'This is a footer', iconURL: _avatar }; - const fields = Array(25) - .fill(0) - .map((_, i) => ({ name: `Field ${i}`, value: 'Field Value' })); - const c = util.colors; - const o = { description, author, footer, fields, time: Date.now() }; + return await message.util.reply({ content: 'buttons', components: buttonRows }); + } else if (['paginate'].includes(args.feature?.toLowerCase())) { + const embeds = []; + for (let i = 1; i <= 5; i++) { + embeds.push(new EmbedBuilder().setDescription(i.toString())); + } + return await ButtonPaginator.send(message, embeds); + } else if (['lots of embeds'].includes(args.feature?.toLowerCase())) { + const description = 'This is a description.'; + const _avatar = message.author.avatarURL() ?? undefined; + const author = { name: 'This is a author', iconURL: _avatar }; + const footer = { text: 'This is a footer', iconURL: _avatar }; + const fields = Array(25) + .fill(0) + .map((_, i) => ({ name: `Field ${i}`, value: 'Field Value' })); + const c = util.colors; + const o = { description, author, footer, fields, time: Date.now() }; - const colors = [c.red, c.orange, c.gold, c.yellow, c.green, c.darkGreen, c.aqua, c.blue, c.purple, c.pink]; + const colors = [c.red, c.orange, c.gold, c.yellow, c.green, c.darkGreen, c.aqua, c.blue, c.purple, c.pink]; - const embeds = colors.map((c, i) => ({ ...o, title: `Embed Title ${i}`, color: c })); + const embeds = colors.map((c, i) => ({ ...o, title: `Embed Title ${i}`, color: c })); - const ButtonRows: ActionRowBuilder[] = []; - for (let a = 1; a <= 5; a++) { - const row = new ActionRowBuilder(); - for (let b = 1; b <= 5; b++) { - const id = (a + 5 * (b - 1)).toString(); - const button = new ButtonBuilder({ style: ButtonStyle.Secondary, customId: id, label: id }); - row.addComponents([button]); + const ButtonRows: ActionRowBuilder[] = []; + for (let a = 1; a <= 5; a++) { + const row = new ActionRowBuilder(); + for (let b = 1; b <= 5; b++) { + const id = (a + 5 * (b - 1)).toString(); + const button = new ButtonBuilder({ style: ButtonStyle.Secondary, customId: id, label: id }); + row.addComponents([button]); + } + ButtonRows.push(row); } - ButtonRows.push(row); - } - return await message.util.reply({ content: 'this is content', components: ButtonRows, embeds }); - } else if (['delete slash commands'].includes(args.feature?.toLowerCase())) { - if (!message.guild) return await message.util.reply(`${util.emojis.error} This test can only be run in a guild.`); - await client.guilds.fetch(); - const promises: Promise>[] = []; - client.guilds.cache.each((guild) => { - promises.push(guild.commands.set([])); - }); - await Promise.all(promises); + return await message.util.reply({ content: 'this is content', components: ButtonRows, embeds }); + } else if (['delete slash commands'].includes(args.feature?.toLowerCase())) { + if (!message.guild) return await message.util.reply(`${util.emojis.error} This test can only be run in a guild.`); + await client.guilds.fetch(); + const promises: Promise>[] = []; + client.guilds.cache.each((guild) => { + promises.push(guild.commands.set([])); + }); + await Promise.all(promises); - await client.application!.commands.fetch(); - await client.application!.commands.set([]); + await client.application!.commands.fetch(); + await client.application!.commands.set([]); - return await message.util.reply(`${util.emojis.success} Removed guild commands and global commands.`); - } else if (['drop down', 'drop downs', 'select menu', 'select menus'].includes(args.feature?.toLowerCase())) { - return message.util.reply(`${util.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(`${util.emojis.success} Synced automod.`); - } else if (['modal'].includes(args.feature?.toLowerCase())) { - const m = await message.util.reply({ - content: 'Click for modal', - components: [ - new ActionRowBuilder().addComponents([ - new ButtonBuilder({ style: ButtonStyle.Primary, label: 'Modal', customId: 'test;modal' }) - ]) - ] - }); + return await message.util.reply(`${util.emojis.success} Removed guild commands and global commands.`); + } else if (['drop down', 'drop downs', 'select menu', 'select menus'].includes(args.feature?.toLowerCase())) { + return message.util.reply(`${util.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(`${util.emojis.success} Synced automod.`); + } else if (['modal'].includes(args.feature?.toLowerCase())) { + const m = await message.util.reply({ + content: 'Click for modal', + components: [ + new ActionRowBuilder().addComponents([ + new ButtonBuilder({ style: ButtonStyle.Primary, label: 'Modal', customId: 'test;modal' }) + ]) + ] + }); - // eslint-disable-next-line @typescript-eslint/no-misused-promises - 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; + // eslint-disable-next-line @typescript-eslint/no-misused-promises + 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 }; + 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' - } - ] - } - ] + 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' + } + ] + } + ] + } } - } + }); }); - }); - return; + return; + } } return await message.util.reply(responses[Math.floor(Math.random() * responses.length)]); } diff --git a/src/commands/fun/coinFlip.ts b/src/commands/fun/coinFlip.ts index b0805cd..fea5cb5 100644 --- a/src/commands/fun/coinFlip.ts +++ b/src/commands/fun/coinFlip.ts @@ -1,4 +1,4 @@ -import { BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, type CommandMessage, type SlashMessage } from '#lib'; export default class CoinFlipCommand extends BushCommand { public constructor() { @@ -14,7 +14,7 @@ export default class CoinFlipCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage) { + public override async exec(message: CommandMessage | SlashMessage) { const random = Math.random(); let result: string; const fall = message.author.id === '322862723090219008' ? 0.1 : 0.001; //dw about it diff --git a/src/commands/fun/dice.ts b/src/commands/fun/dice.ts index 53fc9e2..b2bc7e4 100644 --- a/src/commands/fun/dice.ts +++ b/src/commands/fun/dice.ts @@ -1,4 +1,4 @@ -import { BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, type CommandMessage, type SlashMessage } from '#lib'; export default class DiceCommand extends BushCommand { public constructor() { @@ -14,7 +14,7 @@ export default class DiceCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage) { + public override async exec(message: CommandMessage | SlashMessage) { const responses = ['1', '2', '3', '4', '5', '6']; const answer = responses[Math.floor(Math.random() * responses.length)]; return await message.util.reply(`You rolled a **${answer}**.`); diff --git a/src/commands/fun/eightBall.ts b/src/commands/fun/eightBall.ts index ff06fe5..66fcc45 100644 --- a/src/commands/fun/eightBall.ts +++ b/src/commands/fun/eightBall.ts @@ -1,4 +1,4 @@ -import { BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, type CommandMessage, type SlashMessage } from '#lib'; import { ApplicationCommandOptionType } from 'discord.js'; export default class EightBallCommand extends BushCommand { @@ -26,7 +26,7 @@ export default class EightBallCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage) { + public override async exec(message: CommandMessage | SlashMessage) { const responses = [ 'It is certain', 'Without a doubt', diff --git a/src/commands/fun/minesweeper.ts b/src/commands/fun/minesweeper.ts index 72551e9..d25cb5d 100644 --- a/src/commands/fun/minesweeper.ts +++ b/src/commands/fun/minesweeper.ts @@ -1,4 +1,4 @@ -import { BushCommand, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, OptArgType, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; import { Minesweeper } from '@notenoughupdates/discord.js-minesweeper'; import assert from 'assert'; import { ApplicationCommandOptionType } from 'discord.js'; @@ -53,10 +53,10 @@ export default class MinesweeperCommand extends BushCommand { optional: true }, { - id: 'do_not_reveal_first_cell', + id: 'no_reveal', description: 'Whether to not reveal the first cell automatically.', match: 'flag', - flag: ['--doNotRevealFirstCell', 'do_not_reveal_first_cell'], + flag: ['--noReveal', '--no_reveal', '--doNotRevealFirstCell', 'do_not_reveal_first_cell'], prompt: 'Would you like to not automatically reveal the first cell?', slashType: ApplicationCommandOptionType.Boolean, optional: true @@ -69,20 +69,24 @@ export default class MinesweeperCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, + message: CommandMessage | SlashMessage, args: { - rows: ArgType<'integer'>; - columns: ArgType<'integer'>; - mines: ArgType<'integer'>; - spaces: boolean; - do_not_reveal_first_cell: boolean; + rows: OptArgType<'integer'>; + columns: OptArgType<'integer'>; + mines: OptArgType<'integer'>; + spaces: ArgType<'flag'>; + no_reveal: ArgType<'flag'>; } ) { + args.rows ??= 9; + args.columns ??= 9; + args.mines ??= 10; + const minesweeper = new Minesweeper({ rows: args.rows, columns: args.columns, mines: args.mines, - revealFirstCell: args.do_not_reveal_first_cell ? false : true, + revealFirstCell: args.no_reveal ? false : true, spaces: args.spaces ?? false, zeroFirstCell: false }); diff --git a/src/commands/info/avatar.ts b/src/commands/info/avatar.ts index 870908d..544c30a 100644 --- a/src/commands/info/avatar.ts +++ b/src/commands/info/avatar.ts @@ -1,4 +1,4 @@ -import { BushCommand, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; import { ApplicationCommandOptionType, EmbedBuilder, GuildMember, PermissionFlagsBits } from 'discord.js'; export default class AvatarCommand extends BushCommand { @@ -27,11 +27,12 @@ export default class AvatarCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage, args: { user: ArgType<'member'> | ArgType<'globalUser'> }) { + public override async exec(message: CommandMessage | SlashMessage, args: { user: OptArgType<'member' | 'globalUser'> }) { const params: { size: 2048; extension: 'png'; dynamic: true } = { size: 2048, extension: 'png', dynamic: true }; const defaultAvatar = `https://cdn.discordapp.com/embed/avatars/${Math.ceil(Math.random() * 6) - 1}.png`; - const member = (args.user ?? message.member) instanceof GuildMember ? args.user ?? message.member : undefined; + const member = + (args.user ?? message.member) instanceof GuildMember ? args.user ?? (message.member as GuildMember | null) : null; const user = args.user instanceof GuildMember ? args.user.user : args.user ?? message.author; const guildAvatar = member?.avatarURL(params); diff --git a/src/commands/info/botInfo.ts b/src/commands/info/botInfo.ts index e67ae5a..4a8a36a 100644 --- a/src/commands/info/botInfo.ts +++ b/src/commands/info/botInfo.ts @@ -1,4 +1,4 @@ -import { BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; import { EmbedBuilder, PermissionFlagsBits, version as discordJSVersion } from 'discord.js'; import * as os from 'os'; @@ -20,7 +20,7 @@ export default class BotInfoCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage) { + public override async exec(message: CommandMessage | SlashMessage) { enum Platform { aix = 'AIX', android = 'Android', diff --git a/src/commands/info/color.ts b/src/commands/info/color.ts index a74c3f3..f60e28a 100644 --- a/src/commands/info/color.ts +++ b/src/commands/info/color.ts @@ -1,25 +1,9 @@ -import { - AllowedMentions, - BushCommand, - type ArgType, - type BushArgumentTypeCaster, - type BushGuildMember, - type BushMessage, - type BushRole, - type BushSlashMessage -} from '#lib'; +import { AllowedMentions, BushCommand, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; -import { ApplicationCommandOptionType, EmbedBuilder, PermissionFlagsBits, Role } from 'discord.js'; +import { ApplicationCommandOptionType, EmbedBuilder, GuildMember, PermissionFlagsBits, Role } from 'discord.js'; import tinycolor from 'tinycolor2'; - assert(tinycolor); -const isValidTinyColor: BushArgumentTypeCaster = (_message, phase) => { - // if the phase is a number it converts it to hex incase it could be representing a color in decimal - const newPhase = isNaN(phase as any) ? phase : `#${Number(phase).toString(16)}`; - return tinycolor(newPhase).isValid() ? newPhase : null; -}; - export default class ColorCommand extends BushCommand { public constructor() { super('color', { @@ -32,7 +16,7 @@ export default class ColorCommand extends BushCommand { { id: 'color', description: 'The color string, role, or member to find the color of.', - type: util.arg.union(isValidTinyColor as any, 'role', 'member'), + type: util.arg.union('tinyColor', 'role', 'member'), readableType: 'color|role|member', match: 'restContent', prompt: 'What color code, role, or user would you like to find the color of?', @@ -50,15 +34,12 @@ export default class ColorCommand extends BushCommand { return color.substring(4, color.length - 5); } - public override async exec( - message: BushMessage | BushSlashMessage, - args: { color: string | ArgType<'role'> | ArgType<'member'> } - ) { + public override async exec(message: CommandMessage | SlashMessage, args: { color: ArgType<'tinyColor' | 'role' | 'member'> }) { const _color = message.util.isSlashMessage(message) - ? ((await util.arg.cast(util.arg.union(isValidTinyColor, 'role', 'member'), message, args.color as string)) as + ? ((await util.arg.cast(util.arg.union('tinyColor', 'role', 'member'), message, args.color as string)) as | string - | BushRole - | BushGuildMember) + | Role + | GuildMember) : args.color; const color = diff --git a/src/commands/info/guildInfo.ts b/src/commands/info/guildInfo.ts index 4872497..572cf06 100644 --- a/src/commands/info/guildInfo.ts +++ b/src/commands/info/guildInfo.ts @@ -1,10 +1,12 @@ -import { BushCommand, type ArgType, type BushMessage, type BushSlashMessage, type OptArgType } from '#lib'; +import { BushCommand, type ArgType, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; import assert from 'assert'; -import { GuildDefaultMessageNotifications, GuildExplicitContentFilter } from 'discord-api-types/v10'; import { ApplicationCommandOptionType, + ChannelType, EmbedBuilder, Guild, + GuildDefaultMessageNotifications, + GuildExplicitContentFilter, GuildMFALevel, GuildPremiumTier, GuildVerificationLevel, @@ -41,17 +43,14 @@ export default class GuildInfoCommand extends BushCommand { }); } - public override async exec( - message: BushMessage | BushSlashMessage, - args: { guild: OptArgType<'guild'> | OptArgType<'snowflake'> } - ) { + public override async exec(message: CommandMessage | SlashMessage, args: { guild: OptArgType<'guild' | 'snowflake'> }) { if (!args.guild && !message.inGuild()) { return await message.util.reply( `${util.emojis.error} You must either provide an server to provide info about or run this command in a server.` ); } - let guild: ArgType<'guild'> | ArgType<'snowflake'> | GuildPreview = args.guild ?? message.guild!; + let guild: ArgType<'guild' | 'snowflake'> | GuildPreview = args.guild ?? message.guild!; if (typeof guild === 'string') { const preview = await client.fetchGuildPreview(`${args.guild}` as Snowflake).catch(() => undefined); if (preview) guild = preview; @@ -163,10 +162,19 @@ export default class GuildInfoCommand extends BushCommand { const guildStats: string[] = []; - const channelTypes = (['Text', 'Voice', 'News', 'Stage', 'Category', 'Thread'] as const).map( + const channelTypes = ( + [ + ['Text', [ChannelType.GuildText]], + ['Voice', [ChannelType.GuildVoice]], + ['News', [ChannelType.GuildNews]], + ['Stage', [ChannelType.GuildStageVoice]], + ['Category', [ChannelType.GuildCategory]], + ['Thread', [ChannelType.GuildNewsThread, ChannelType.GuildPrivateThread, ChannelType.GuildPublicThread]] + ] as const + ).map( (type) => - `${client.consts.mappings.otherEmojis[`Channel${type}`]} ${guild.channels.cache - .filter((channel) => channel[`is${type}`]()) + `${client.consts.mappings.otherEmojis[`Channel${type[0]}`]} ${guild.channels.cache + .filter((channel) => type[1].some((type) => channel.type === type)) .size.toLocaleString()}` ); diff --git a/src/commands/info/help.ts b/src/commands/info/help.ts index ea1e965..af44980 100644 --- a/src/commands/info/help.ts +++ b/src/commands/info/help.ts @@ -1,10 +1,10 @@ -import { BushCommand, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; -import { ButtonBuilder } from '@discordjs/builders'; +import { BushCommand, type ArgType, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; import assert from 'assert'; import { ActionRowBuilder, ApplicationCommandOptionType, AutocompleteInteraction, + ButtonBuilder, ButtonStyle, EmbedBuilder, PermissionFlagsBits @@ -53,7 +53,7 @@ export default class HelpCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage, args: HelpArgs) { + public override async exec(message: CommandMessage | SlashMessage, args: HelpArgs) { const row = this.addLinks(message); const command = args.command ? typeof args.command === 'string' @@ -70,7 +70,7 @@ export default class HelpCommand extends BushCommand { } } - private helpAll(message: BushMessage | BushSlashMessage, args: HelpArgs, row: ActionRowBuilder) { + private helpAll(message: CommandMessage | SlashMessage, args: HelpArgs, row: ActionRowBuilder) { const prefix = util.prefix(message); const embed = new EmbedBuilder() .setColor(util.colors.default) @@ -99,7 +99,7 @@ export default class HelpCommand extends BushCommand { return message.util.reply({ embeds: [embed], components: row.components.length ? [row] : undefined }); } - private helpIndividual(message: BushMessage | BushSlashMessage, row: ActionRowBuilder, command: BushCommand) { + private helpIndividual(message: CommandMessage | SlashMessage, row: ActionRowBuilder, command: BushCommand) { const embed = new EmbedBuilder().setColor(util.colors.default).setTitle(`${command.id} Command`); let description = `${command.description ?? '*This command does not have a description.*'}`; @@ -207,7 +207,7 @@ export default class HelpCommand extends BushCommand { } } - private addLinks(message: BushMessage | BushSlashMessage) { + private addLinks(message: CommandMessage | SlashMessage) { const row = new ActionRowBuilder(); if (!client.config.isDevelopment && !client.guilds.cache.some((guild) => guild.ownerId === message.author.id)) { @@ -246,4 +246,4 @@ export default class HelpCommand extends BushCommand { } } -type HelpArgs = { command: ArgType<'commandAlias'> | string; showHidden?: boolean }; +type HelpArgs = { command: OptArgType<'commandAlias'> | string; showHidden: ArgType<'flag'> }; diff --git a/src/commands/info/icon.ts b/src/commands/info/icon.ts index db390a5..9c9556b 100644 --- a/src/commands/info/icon.ts +++ b/src/commands/info/icon.ts @@ -1,4 +1,4 @@ -import { BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; import { EmbedBuilder, PermissionFlagsBits } from 'discord.js'; @@ -17,7 +17,7 @@ export default class IconCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage) { + public override async exec(message: CommandMessage | SlashMessage) { assert(message.inGuild()); const embed = new EmbedBuilder() diff --git a/src/commands/info/links.ts b/src/commands/info/links.ts index 91b62ca..0d5bd15 100644 --- a/src/commands/info/links.ts +++ b/src/commands/info/links.ts @@ -1,4 +1,4 @@ -import { BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js'; import packageDotJSON from '../../../package.json' assert { type: 'json' }; @@ -19,7 +19,7 @@ export default class LinksCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage) { + public override async exec(message: CommandMessage | SlashMessage) { const buttonRow = new ActionRowBuilder(); if (!client.config.isDevelopment || message.author.isOwner()) { buttonRow.addComponents([new ButtonBuilder({ style: ButtonStyle.Link, label: 'Invite Me', url: util.invite })]); diff --git a/src/commands/info/ping.ts b/src/commands/info/ping.ts index af0fa98..ad7fdcc 100644 --- a/src/commands/info/ping.ts +++ b/src/commands/info/ping.ts @@ -1,5 +1,5 @@ -import { BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; -import { EmbedBuilder, PermissionFlagsBits } from 'discord.js'; +import { BushCommand, type CommandMessage, type SlashMessage } from '#lib'; +import { EmbedBuilder, PermissionFlagsBits, type Message } from 'discord.js'; export default class PingCommand extends BushCommand { public constructor() { @@ -15,21 +15,21 @@ export default class PingCommand extends BushCommand { }); } - public override async exec(message: BushMessage) { + public override async exec(message: CommandMessage) { const timestamp1 = message.editedTimestamp ? message.editedTimestamp : message.createdTimestamp; const msg = await message.util.reply('Pong!'); const timestamp2 = msg.editedTimestamp ? msg.editedTimestamp : msg.createdTimestamp; void this.command(message, timestamp2 - timestamp1); } - public override async execSlash(message: BushSlashMessage) { + public override async execSlash(message: SlashMessage) { const timestamp1 = message.createdTimestamp; - const msg = (await message.util.reply({ content: 'Pong!', fetchReply: true })) as BushMessage; + const msg = (await message.util.reply({ content: 'Pong!', fetchReply: true })) as Message; const timestamp2 = msg.editedTimestamp ? msg.editedTimestamp : msg.createdTimestamp; void this.command(message, timestamp2 - timestamp1); } - private command(message: BushMessage | BushSlashMessage, msgLatency: number) { + private command(message: CommandMessage | SlashMessage, msgLatency: number) { const botLatency = util.format.codeBlock(`${Math.round(msgLatency)}ms`); const apiLatency = util.format.codeBlock(`${Math.round(message.client.ws.ping)}ms`); const embed = new EmbedBuilder() diff --git a/src/commands/info/pronouns.ts b/src/commands/info/pronouns.ts index b45f9b3..f916687 100644 --- a/src/commands/info/pronouns.ts +++ b/src/commands/info/pronouns.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushCommand, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { AllowedMentions, BushCommand, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; import { ApplicationCommandOptionType, EmbedBuilder, PermissionFlagsBits } from 'discord.js'; export default class PronounsCommand extends BushCommand { @@ -26,7 +26,7 @@ export default class PronounsCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage, args: { user?: ArgType<'globalUser'> }) { + public override async exec(message: CommandMessage | SlashMessage, args: { user: OptArgType<'globalUser'> }) { const user = args.user ?? message.author; const author = user.id === message.author.id; diff --git a/src/commands/info/snowflake.ts b/src/commands/info/snowflake.ts index 07544c9..e74756f 100644 --- a/src/commands/info/snowflake.ts +++ b/src/commands/info/snowflake.ts @@ -1,4 +1,4 @@ -import { BushCommand, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; import { stripIndent } from '#tags'; import { ApplicationCommandOptionType, @@ -37,7 +37,7 @@ export default class SnowflakeCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage, args: { snowflake: ArgType<'snowflake'> }) { + public override async exec(message: CommandMessage | SlashMessage, args: { snowflake: ArgType<'snowflake'> }) { const snowflake = `${args.snowflake}` as Snowflake; const snowflakeEmbed = new EmbedBuilder().setTitle('Unknown :snowflake:').setColor(util.colors.default); @@ -45,7 +45,7 @@ export default class SnowflakeCommand extends BushCommand { if (client.channels.cache.has(snowflake)) { const channel = client.channels.resolve(snowflake)!; const channelInfo = [`**Type:** ${BushChannelType[channel.type] ?? ChannelType[channel.type]}`]; - if (channel.isDM()) { + if (channel.type === ChannelType.DM) { channelInfo.push( `**Recipient:** ${util.discord.escapeMarkdown(channel.recipient?.tag ?? '¯\\_(ツ)_/¯')} (${ channel.recipient?.id ?? '¯\\_(ツ)_/¯' @@ -55,11 +55,11 @@ export default class SnowflakeCommand extends BushCommand { `:snowflake: DM with ${util.discord.escapeMarkdown(channel.recipient?.tag ?? '¯\\_(ツ)_/¯')} \`[Channel]\`` ); } else if ( - channel.isCategory() || - channel.isNews() || - channel.isText() || - channel.isVoice() || - channel.isStage() || + channel.type === ChannelType.GuildCategory || + channel.type === ChannelType.GuildNews || + channel.type === ChannelType.GuildText || + channel.type === ChannelType.GuildVoice || + channel.type === ChannelType.GuildStageVoice || channel.isThread() ) { channelInfo.push( diff --git a/src/commands/info/userInfo.ts b/src/commands/info/userInfo.ts index cb2fc5f..d617756 100644 --- a/src/commands/info/userInfo.ts +++ b/src/commands/info/userInfo.ts @@ -1,22 +1,17 @@ -import { - BushCommand, - Time, - type ArgType, - type BushGuild, - type BushGuildMember, - type BushMessage, - type BushSlashMessage, - type BushUser -} from '#lib'; -import { TeamMemberMembershipState, type APIApplication } from 'discord-api-types/v10'; +import { BushCommand, Time, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; import { ActivityType, ApplicationCommandOptionType, ApplicationFlagsBitField, - ApplicationFlagsString, EmbedBuilder, PermissionFlagsBits, - UserFlags + TeamMemberMembershipState, + UserFlags, + type APIApplication, + type ApplicationFlagsString, + type Guild, + type GuildMember, + type User } from 'discord.js'; export default class UserInfoCommand extends BushCommand { @@ -45,7 +40,7 @@ export default class UserInfoCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage, args: { user: ArgType<'user'> | ArgType<'snowflake'> }) { + public override async exec(message: CommandMessage | SlashMessage, args: { user: OptArgType<'user' | 'snowflake'> }) { const user = args.user === null ? message.author @@ -61,7 +56,7 @@ export default class UserInfoCommand extends BushCommand { return await message.util.reply({ embeds: [userEmbed] }); } - public static async makeUserInfoEmbed(user: BushUser, member?: BushGuildMember, guild?: BushGuild | null) { + public static async makeUserInfoEmbed(user: User, member?: GuildMember, guild?: Guild | null) { const emojis = []; const superUsers = util.getShared('superUsers'); @@ -121,7 +116,7 @@ export default class UserInfoCommand extends BushCommand { return userEmbed; } - public static async generateGeneralInfoField(embed: EmbedBuilder, user: BushUser, title = '» General Information') { + public static async generateGeneralInfoField(embed: EmbedBuilder, user: User, title = '» General Information') { // General Info const generalInfo = [ `**Mention:** <@${user.id}>`, @@ -138,11 +133,7 @@ export default class UserInfoCommand extends BushCommand { embed.addFields([{ name: title, value: generalInfo.join('\n') }]); } - public static generateServerInfoField( - embed: EmbedBuilder, - member?: BushGuildMember | undefined, - title = '» Server Information' - ) { + public static generateServerInfoField(embed: EmbedBuilder, member?: GuildMember | undefined, title = '» Server Information') { if (!member) return; // Server User Info @@ -167,7 +158,7 @@ export default class UserInfoCommand extends BushCommand { if (serverUserInfo.length) embed.addFields([{ name: title, value: serverUserInfo.join('\n') }]); } - public static generatePresenceField(embed: EmbedBuilder, member?: BushGuildMember | undefined, title = '» Presence') { + 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; @@ -207,7 +198,7 @@ export default class UserInfoCommand extends BushCommand { }); } - public static generateRolesField(embed: EmbedBuilder, member?: BushGuildMember | undefined) { + public static generateRolesField(embed: EmbedBuilder, member?: GuildMember | undefined) { if (!member || member.roles.cache.size <= 1) return; // roles @@ -227,7 +218,7 @@ export default class UserInfoCommand extends BushCommand { public static generatePermissionsField( embed: EmbedBuilder, - member: BushGuildMember | undefined, + member: GuildMember | undefined, title = '» Important Permissions' ) { if (!member) return; @@ -247,7 +238,7 @@ export default class UserInfoCommand extends BushCommand { if (perms.length) embed.addFields([{ name: title, value: perms.join(' ') }]); } - public static async generateBotField(embed: EmbedBuilder, user: BushUser, title = '» Bot Information') { + public static async generateBotField(embed: EmbedBuilder, user: User, title = '» Bot Information') { if (!user.bot) return; const applicationInfo = (await client.rest.get(`/applications/${user.id}/rpc`).catch(() => null)) as APIApplication | null; diff --git a/src/commands/leveling/leaderboard.ts b/src/commands/leveling/leaderboard.ts index f476ac1..040ed4a 100644 --- a/src/commands/leveling/leaderboard.ts +++ b/src/commands/leveling/leaderboard.ts @@ -1,4 +1,4 @@ -import { BushCommand, ButtonPaginator, Level, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, ButtonPaginator, Level, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, EmbedBuilder, PermissionFlagsBits } from 'discord.js'; @@ -28,7 +28,7 @@ export default class LeaderboardCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage, args: { page: ArgType<'integer'> }) { + public override async exec(message: CommandMessage | SlashMessage, args: { page: OptArgType<'integer'> }) { assert(message.inGuild()); if (!(await message.guild.hasFeature('leveling'))) diff --git a/src/commands/leveling/level.ts b/src/commands/leveling/level.ts index 3a9a916..34a839a 100644 --- a/src/commands/leveling/level.ts +++ b/src/commands/leveling/level.ts @@ -3,16 +3,14 @@ import { BushCommand, CanvasProgressBar, Level, - type BushGuild, - type BushMessage, - type BushSlashMessage, - type BushUser, - type OptArgType + type CommandMessage, + type OptArgType, + type SlashMessage } from '#lib'; import { SimplifyNumber } from '@notenoughupdates/simplify-number'; import assert from 'assert'; import canvas from 'canvas'; -import { ApplicationCommandOptionType, Attachment, PermissionFlagsBits } from 'discord.js'; +import { ApplicationCommandOptionType, AttachmentBuilder, Guild, PermissionFlagsBits, User } from 'discord.js'; import got from 'got'; import { dirname, join } from 'path'; import { fileURLToPath } from 'url'; @@ -46,7 +44,7 @@ export default class LevelCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage, args: { user: OptArgType<'user'> }) { + public override async exec(message: CommandMessage | SlashMessage, args: { user: OptArgType<'user'> }) { assert(message.inGuild()); if (!(await message.guild.hasFeature('leveling'))) @@ -60,7 +58,7 @@ export default class LevelCommand extends BushCommand { const user = args.user ?? message.author; try { return await message.util.reply({ - files: [new Attachment(await this.getImage(user, message.guild), 'level.png')] + files: [new AttachmentBuilder(await this.getImage(user, message.guild), { name: 'level.png' })] }); } catch (e) { if (e instanceof Error && e.message === 'User does not have a level') { @@ -72,7 +70,7 @@ export default class LevelCommand extends BushCommand { } } - private async getImage(user: BushUser, guild: BushGuild): Promise { + private async getImage(user: User, guild: Guild): Promise { const guildRows = await Level.findAll({ where: { guild: guild.id } }); const rank = guildRows.sort((a, b) => b.xp - a.xp); const userLevelRow = guildRows.find((a) => a.user === user.id); diff --git a/src/commands/leveling/levelRoles.ts b/src/commands/leveling/levelRoles.ts index 9fe7dd0..afa4ab6 100644 --- a/src/commands/leveling/levelRoles.ts +++ b/src/commands/leveling/levelRoles.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushCommand, type ArgType, type BushMessage, type BushSlashMessage, type OptArgType } from '#lib'; +import { AllowedMentions, BushCommand, type ArgType, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, PermissionFlagsBits } from 'discord.js'; @@ -39,7 +39,7 @@ export default class LevelRolesCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, + message: CommandMessage | SlashMessage, args: { level: ArgType<'integer'>; role: OptArgType<'role'> } ) { assert(message.inGuild()); diff --git a/src/commands/leveling/setLevel.ts b/src/commands/leveling/setLevel.ts index ac7df57..8abdb57 100644 --- a/src/commands/leveling/setLevel.ts +++ b/src/commands/leveling/setLevel.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushCommand, Level, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { AllowedMentions, BushCommand, Level, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, PermissionFlagsBits } from 'discord.js'; @@ -36,7 +36,7 @@ export default class SetLevelCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, + message: CommandMessage | SlashMessage, { user, level }: { user: ArgType<'user'>; level: ArgType<'integer'> } ) { assert(message.inGuild()); diff --git a/src/commands/leveling/setXp.ts b/src/commands/leveling/setXp.ts index 1f7c981..60e0b94 100644 --- a/src/commands/leveling/setXp.ts +++ b/src/commands/leveling/setXp.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushCommand, Level, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { AllowedMentions, BushCommand, Level, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, PermissionFlagsBits } from 'discord.js'; @@ -37,7 +37,7 @@ export default class SetXpCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, + message: CommandMessage | SlashMessage, { user, xp }: { user: ArgType<'user'>; xp: ArgType<'abbreviatedNumber'> } ) { assert(message.inGuild()); diff --git a/src/commands/moderation/_activePunishments.ts b/src/commands/moderation/_activePunishments.ts index e751493..92b4242 100644 --- a/src/commands/moderation/_activePunishments.ts +++ b/src/commands/moderation/_activePunishments.ts @@ -1,79 +1,78 @@ -/* import { BushCommand, ModLog, ModLogModel, type BushGuildMember, type BushMessage, type BushSlashMessage } from '#lib'; -import { FindOptions, Op } from 'sequelize'; -import { Permissions } from 'discord.js'; +// import { BushCommand, ModLog, ModLogModel, OptArgType, type CommandMessage, type SlashMessage } from '#lib'; +// import { ApplicationCommandOptionType, PermissionFlagsBits } from 'discord.js'; +// import { FindOptions, Op } from 'sequelize'; -const punishmentTypes = ['ban', 'kick', 'mute', 'warn', 'role'] as const; +// const punishmentTypes = ['ban', 'kick', 'mute', 'warn', 'role'] as const; -export default class ActivePunishmentsCommand extends BushCommand { - public constructor() { - super('activePunishments', { - aliases: ['active-punishments', 'ap'], - category: 'moderation', - description: 'Gets a list of all the active punishment in the server.', - usage: [`active-punishments [--moderator ] [--type <${punishmentTypes.map((v) => `'${v}'`).join('|')}>]`], - examples: ['active-punishments'], - args: [ - { - id: 'moderator', - description: 'Only show active punishments by this moderator.', - type: 'user', - match: 'option', - prompt: 'Only show active punishments from what user?', - optional: true, - slashType: ApplicationCommandOptionType.User, - slashResolve: 'Member' - }, - { - id: 'type', - description: 'Only show active punishments of this type.', - customType: [...punishmentTypes], - readableType: punishmentTypes.map((v) => `'${v}'`).join('|'), - match: 'option', - optional: true, - slashType: ApplicationCommandOptionType.String, - choices: punishmentTypes.map((v) => ({ name: v, value: v })) - } - ], - slash: true, - channel: 'guild', - hidden: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m), - userPermissions: (m) => util.userGuildPermCheck(m, [PermissionFlagsBits.ManageMessages]) - }); - } +// export default class ActivePunishmentsCommand extends BushCommand { +// public constructor() { +// super('activePunishments', { +// aliases: ['active-punishments', 'ap'], +// category: 'moderation', +// description: 'Gets a list of all the active punishment in the server.', +// usage: [`active-punishments [--moderator ] [--type <${punishmentTypes.map((v) => `'${v}'`).join('|')}>]`], +// examples: ['active-punishments'], +// args: [ +// { +// id: 'moderator', +// description: 'Only show active punishments by this moderator.', +// type: 'user', +// match: 'option', +// prompt: 'Only show active punishments from what user?', +// optional: true, +// slashType: ApplicationCommandOptionType.User, +// slashResolve: 'Member' +// }, +// { +// id: 'type', +// description: 'Only show active punishments of this type.', +// customType: [...punishmentTypes], +// readableType: punishmentTypes.map((v) => `'${v}'`).join('|'), +// match: 'option', +// optional: true, +// slashType: ApplicationCommandOptionType.String, +// choices: punishmentTypes.map((v) => ({ name: v, value: v })) +// } +// ], +// slash: true, +// channel: 'guild', +// hidden: true, +// clientPermissions: (m) => util.clientSendAndPermCheck(m), +// userPermissions: (m) => util.userGuildPermCheck(m, [PermissionFlagsBits.ManageMessages]) +// }); +// } - public override async exec( - message: BushMessage | BushSlashMessage, - args: { moderator?: BushGuildMember; type: typeof punishmentTypes[number] } - ) { - const where: FindOptions['where'] = { guild: message.guild!.id }; - if (args.moderator?.id) where.user = args.moderator.id; - if (args.type) { - switch (args.type) { - case 'ban': - where.type = { [Op.or]: ['PERM_BAN', 'TEMP_BAN', 'UNBAN'] }; - break; - case 'kick': - where.type = { [Op.or]: ['KICK'] }; - break; - case 'mute': - where.type = { [Op.or]: ['PERM_MUTE', 'TEMP_MUTE', 'UNMUTE'] }; - break; - case 'warn': - where.type = { [Op.or]: ['WARN'] }; - break; - case 'role': - where.type = { [Op.or]: ['PERM_PUNISHMENT_ROLE', 'TEMP_PUNISHMENT_ROLE', 'REMOVE_PUNISHMENT_ROLE'] }; - break; - default: - return message.util.reply(`${util.emojis.error} You supplied an invalid case type to filter by.`); - } - } +// public override async exec( +// message: CommandMessage | SlashMessage, +// args: { moderator: OptArgType<'user' | 'member'>; type: typeof punishmentTypes[number] } +// ) { +// const where: FindOptions['where'] = { guild: message.guild!.id }; +// if (args.moderator?.id) where.user = args.moderator.id; +// if (args.type) { +// switch (args.type) { +// case 'ban': +// where.type = { [Op.or]: ['PERM_BAN', 'TEMP_BAN', 'UNBAN'] }; +// break; +// case 'kick': +// where.type = { [Op.or]: ['KICK'] }; +// break; +// case 'mute': +// where.type = { [Op.or]: ['PERM_MUTE', 'TEMP_MUTE', 'UNMUTE'] }; +// break; +// case 'warn': +// where.type = { [Op.or]: ['WARN'] }; +// break; +// case 'role': +// where.type = { [Op.or]: ['PERM_PUNISHMENT_ROLE', 'TEMP_PUNISHMENT_ROLE', 'REMOVE_PUNISHMENT_ROLE'] }; +// break; +// default: +// return message.util.reply(`${util.emojis.error} You supplied an invalid case type to filter by.`); +// } +// } - const logs = await ModLog.findAll({ - where, - order: [['createdAt', 'ASC']] - }); - } -} - */ +// const logs = await ModLog.findAll({ +// where, +// order: [['createdAt', 'ASC']] +// }); +// } +// } diff --git a/src/commands/moderation/ban.ts b/src/commands/moderation/ban.ts index 77951c3..f4bd52b 100644 --- a/src/commands/moderation/ban.ts +++ b/src/commands/moderation/ban.ts @@ -4,9 +4,9 @@ import { BushCommand, Moderation, type ArgType, - type BushMessage, - type BushSlashMessage, - type OptArgType + type CommandMessage, + type OptArgType, + type SlashMessage } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, PermissionFlagsBits } from 'discord.js'; @@ -71,12 +71,12 @@ export default class BanCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, + message: CommandMessage | SlashMessage, args: { - user: ArgType<'user'> | ArgType<'snowflake'>; + user: ArgType<'user' | 'snowflake'>; reason_and_duration: OptArgType<'contentWithDuration'> | string; days: OptArgType<'integer'>; - force: boolean; + force: ArgType<'flag'>; } ) { assert(message.inGuild()); diff --git a/src/commands/moderation/block.ts b/src/commands/moderation/block.ts index 722f08b..16beb6f 100644 --- a/src/commands/moderation/block.ts +++ b/src/commands/moderation/block.ts @@ -4,9 +4,9 @@ import { BushCommand, Moderation, type ArgType, - type BushMessage, - type BushSlashMessage, - type OptArgType + type CommandMessage, + type OptArgType, + type SlashMessage } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, PermissionFlagsBits } from 'discord.js'; @@ -58,15 +58,16 @@ export default class BlockCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, + message: CommandMessage | SlashMessage, args: { user: ArgType<'user'>; reason_and_duration: OptArgType<'contentWithDuration'> | string; - force?: ArgType<'boolean'>; + force: ArgType<'flag'>; } ) { assert(message.inGuild()); assert(message.member); + assert(message.channel); if (!message.channel.isTextBased()) return message.util.send(`${util.emojis.error} This command can only be used in text based channels.`); diff --git a/src/commands/moderation/evidence.ts b/src/commands/moderation/evidence.ts index 9b63189..f1fac0d 100644 --- a/src/commands/moderation/evidence.ts +++ b/src/commands/moderation/evidence.ts @@ -1,8 +1,7 @@ -import { BushCommand, ModLog, OptArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, ModLog, OptArgType, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; -import { ArgumentGeneratorReturn } from 'discord-akairo'; -import { Argument, ArgumentTypeCasterReturn } from 'discord-akairo/dist/src/struct/commands/arguments/Argument.js'; -import { ApplicationCommandOptionType, PermissionFlagsBits, User } from 'discord.js'; +import { Argument, ArgumentGeneratorReturn } from 'discord-akairo'; +import { ApplicationCommandOptionType, PermissionFlagsBits, type Message } from 'discord.js'; export default class EvidenceCommand extends BushCommand { public constructor() { @@ -48,8 +47,8 @@ export default class EvidenceCommand extends BushCommand { }); } - public override *args(message: BushMessage): ArgumentGeneratorReturn { - const target: ArgumentTypeCasterReturn<'string'> | ArgumentTypeCasterReturn<'snowflake'> = yield { + public override *args(message: CommandMessage): ArgumentGeneratorReturn { + const target: ArgType<'string' | 'snowflake'> = yield { id: 'target', type: Argument.union('snowflake', 'string'), prompt: { @@ -59,7 +58,7 @@ export default class EvidenceCommand extends BushCommand { } }; - const evidence: ArgumentTypeCasterReturn<'string'> = yield { + const evidence: OptArgType<'string'> = yield { id: 'evidence', type: 'string', match: 'restContent', @@ -74,13 +73,18 @@ export default class EvidenceCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, + message: CommandMessage | SlashMessage, { case_id: caseID, user, target: messageCommandTarget, evidence - }: { case_id?: string; user?: User; target: string | User; evidence: OptArgType<'string'> } + }: { + case_id: OptArgType<'string'>; + user: OptArgType<'user'>; + target: ArgType<'string' | 'snowflake'>; + evidence: OptArgType<'string'>; + } ) { assert(message.inGuild()); @@ -88,22 +92,13 @@ export default class EvidenceCommand extends BushCommand { return message.util.send(`${util.emojis.error} You must provide either a user or a case ID.`); const entry = messageCommandTarget - ? typeof messageCommandTarget == 'string' - ? await ModLog.findByPk(messageCommandTarget) - : await ModLog.findOne({ - where: { - user: messageCommandTarget.id - }, - order: [['createdAt', 'DESC']] - }) + ? util.consts.regex.snowflake.test(messageCommandTarget) + ? await ModLog.findOne({ where: { user: messageCommandTarget }, order: [['createdAt', 'DESC']] }) + : await ModLog.findByPk(messageCommandTarget) : caseID ? await ModLog.findByPk(caseID) - : await ModLog.findOne({ - where: { - user: user!.id - }, - order: [['createdAt', 'DESC']] - }); + : await ModLog.findOne({ where: { user: user!.id }, order: [['createdAt', 'DESC']] }); + if (!entry || entry.pseudo) return message.util.send(`${util.emojis.error} Invalid modlog entry.`); if (entry.guild !== message.guild.id) return message.util.reply(`${util.emojis.error} This modlog is from another server.`); @@ -122,8 +117,8 @@ export default class EvidenceCommand extends BushCommand { ); } - public static getEvidence(message: BushMessage | BushSlashMessage, evidenceArg: OptArgType<'string'>): null | string { - if (evidenceArg && (message as BushMessage).attachments?.size) { + public static getEvidence(message: CommandMessage | SlashMessage, evidenceArg: OptArgType<'string'>): null | string { + if (evidenceArg && (message as Message).attachments?.size) { void message.util.reply(`${util.emojis.error} Please either attach an image or a reason not both.`); return null; } @@ -131,7 +126,7 @@ export default class EvidenceCommand extends BushCommand { const _evidence = evidenceArg ? evidenceArg : !message.util.isSlash - ? (message as BushMessage).attachments.first()?.url + ? (message as Message).attachments.first()?.url : undefined; if (!_evidence) { void message.util.reply(`${util.emojis.error} You must provide evidence for this modlog.`); diff --git a/src/commands/moderation/hideCase.ts b/src/commands/moderation/hideCase.ts index 8cec66e..fc5baf4 100644 --- a/src/commands/moderation/hideCase.ts +++ b/src/commands/moderation/hideCase.ts @@ -1,4 +1,4 @@ -import { BushCommand, ModLog, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, ModLog, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, PermissionFlagsBits } from 'discord.js'; @@ -27,7 +27,7 @@ export default class HideCaseCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage, { case_id: caseID }: { case_id: string }) { + public override async exec(message: CommandMessage | SlashMessage, { case_id: caseID }: { case_id: string }) { assert(message.inGuild()); const entry = await ModLog.findByPk(caseID); diff --git a/src/commands/moderation/kick.ts b/src/commands/moderation/kick.ts index 26098b5..a9a0336 100644 --- a/src/commands/moderation/kick.ts +++ b/src/commands/moderation/kick.ts @@ -4,8 +4,9 @@ import { kickResponse, Moderation, type ArgType, - type BushMessage, - type BushSlashMessage + type CommandMessage, + type OptArgType, + type SlashMessage } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, PermissionFlagsBits } from 'discord.js'; @@ -55,8 +56,8 @@ export default class KickCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, - { user, reason, force }: { user: ArgType<'user'>; reason: ArgType<'string'>; force: boolean } + message: CommandMessage | SlashMessage, + { user, reason, force }: { user: ArgType<'user'>; reason: OptArgType<'string'>; force: ArgType<'flag'> } ) { assert(message.inGuild()); assert(message.member); diff --git a/src/commands/moderation/lockdown.ts b/src/commands/moderation/lockdown.ts index d10a5d4..7d8211c 100644 --- a/src/commands/moderation/lockdown.ts +++ b/src/commands/moderation/lockdown.ts @@ -3,9 +3,9 @@ import { BushCommand, ConfirmationPrompt, type ArgType, - type BushMessage, - type BushSlashMessage, - type OptArgType + type CommandMessage, + type OptArgType, + type SlashMessage } from '#lib'; import assert from 'assert'; import { @@ -72,22 +72,22 @@ export default class LockdownCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, + message: CommandMessage | SlashMessage, args: { - channel: OptArgType<'textChannel'> | OptArgType<'newsChannel'> | OptArgType<'threadChannel'>; + channel: OptArgType<'textChannel' | 'newsChannel' | 'threadChannel' | 'voiceChannel'>; reason: OptArgType<'string'>; - all: ArgType<'boolean'>; + all: ArgType<'flag'>; } ) { return await LockdownCommand.lockdownOrUnlockdown(message, args, 'lockdown'); } public static async lockdownOrUnlockdown( - message: BushMessage | BushSlashMessage, + message: CommandMessage | SlashMessage, args: { - channel: OptArgType<'textChannel'> | OptArgType<'newsChannel'> | OptArgType<'threadChannel'> | OptArgType<'voiceChannel'>; + channel: OptArgType<'textChannel' | 'newsChannel' | 'threadChannel' | 'voiceChannel'>; reason: OptArgType<'string'>; - all: ArgType<'boolean'>; + all: ArgType<'flag'>; }, action: 'lockdown' | 'unlockdown' ) { diff --git a/src/commands/moderation/massBan.ts b/src/commands/moderation/massBan.ts index f1d85ed..a25c64f 100644 --- a/src/commands/moderation/massBan.ts +++ b/src/commands/moderation/massBan.ts @@ -3,9 +3,9 @@ import { banResponse, BushCommand, type ArgType, - type BushMessage, - type BushSlashMessage, - type OptArgType + type CommandMessage, + type OptArgType, + type SlashMessage } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, Collection, PermissionFlagsBits } from 'discord.js'; @@ -61,7 +61,7 @@ export default class MassBanCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, + message: CommandMessage | SlashMessage, args: { users: ArgType<'string'>; reason: OptArgType<'string'>; days: OptArgType<'integer'> } ) { assert(message.inGuild()); diff --git a/src/commands/moderation/massEvidence.ts b/src/commands/moderation/massEvidence.ts index 67f5a25..468d43c 100644 --- a/src/commands/moderation/massEvidence.ts +++ b/src/commands/moderation/massEvidence.ts @@ -1,4 +1,4 @@ -import { BushCommand, ModLog, type ArgType, type BushMessage, type BushSlashMessage, type OptArgType } from '#lib'; +import { BushCommand, ModLog, type ArgType, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, PermissionFlagsBits } from 'discord.js'; import { EvidenceCommand } from '../index.js'; @@ -44,7 +44,7 @@ export default class MassEvidenceCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, + message: CommandMessage | SlashMessage, args: { users: ArgType<'string'>; evidence: OptArgType<'string'> } ) { assert(message.inGuild()); diff --git a/src/commands/moderation/modlog.ts b/src/commands/moderation/modlog.ts index 66368f9..71ed975 100644 --- a/src/commands/moderation/modlog.ts +++ b/src/commands/moderation/modlog.ts @@ -1,4 +1,4 @@ -import { BushCommand, ButtonPaginator, ModLog, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, ButtonPaginator, ModLog, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, PermissionFlagsBits, User } from 'discord.js'; @@ -40,8 +40,8 @@ export default class ModlogCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, - { search, hidden }: { search: ArgType<'user'> | string; hidden: boolean } + message: CommandMessage | SlashMessage, + { search, hidden }: { search: ArgType<'user'> | string; hidden: ArgType<'flag'> } ) { assert(message.inGuild()); diff --git a/src/commands/moderation/mute.ts b/src/commands/moderation/mute.ts index d846817..a665670 100644 --- a/src/commands/moderation/mute.ts +++ b/src/commands/moderation/mute.ts @@ -4,9 +4,9 @@ import { Moderation, muteResponse, type ArgType, - type BushMessage, - type BushSlashMessage, - type OptArgType + type CommandMessage, + type OptArgType, + type SlashMessage } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, PermissionFlagsBits } from 'discord.js'; @@ -57,11 +57,11 @@ export default class MuteCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, + message: CommandMessage | SlashMessage, args: { user: ArgType<'user'>; reason_and_duration: OptArgType<'contentWithDuration'> | string; - force?: ArgType<'boolean'>; + force: ArgType<'flag'>; } ) { assert(message.inGuild()); diff --git a/src/commands/moderation/purge.ts b/src/commands/moderation/purge.ts index 799cf36..1652f6f 100644 --- a/src/commands/moderation/purge.ts +++ b/src/commands/moderation/purge.ts @@ -1,6 +1,6 @@ -import { BushCommand, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, OptArgType, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; -import { ApplicationCommandOptionType, Collection, PermissionFlagsBits } from 'discord.js'; +import { ApplicationCommandOptionType, Collection, PermissionFlagsBits, type Message } from 'discord.js'; export default class PurgeCommand extends BushCommand { public constructor() { @@ -50,14 +50,14 @@ export default class PurgeCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, - args: { amount: number; bot: boolean; user: ArgType<'user'> } + message: CommandMessage | SlashMessage, + args: { amount: ArgType<'integer'>; bot: ArgType<'flag'>; user: OptArgType<'user'> } ) { assert(message.inGuild()); if (args.amount > 100 || args.amount < 1) return message.util.reply(`${util.emojis.error} `); - const messageFilter = (filterMessage: BushMessage): boolean => { + const messageFilter = (filterMessage: Message): boolean => { const shouldFilter: boolean[] = []; if (args.bot) shouldFilter.push(filterMessage.author.bot); if (args.user) shouldFilter.push(filterMessage.author.id === args.user.id); @@ -65,16 +65,16 @@ export default class PurgeCommand extends BushCommand { return shouldFilter.filter((bool) => bool === false).length === 0 && filterMessage.id !== message.id; }; const messages = new Collection( - (await message.channel.messages.fetch({ limit: 100, before: message.id })) + (await message.channel!.messages.fetch({ limit: 100, before: message.id })) .filter((message) => messageFilter(message)) .first(args.amount) .map((m) => [m.id, m] as const) ); - const purged = await message.channel.bulkDelete(messages, true).catch(() => null); + const purged = await message.channel!.bulkDelete(messages, true).catch(() => null); if (!purged) return message.util.reply(`${util.emojis.error} Failed to purge messages.`).catch(() => null); else { - client.emit('bushPurge', message.author, message.guild, message.channel, messages); + client.emit('bushPurge', message.author, message.guild, message.channel!, messages); await message.util.send(`${util.emojis.success} Successfully purged **${purged.size}** messages.`); /* .then(async (purgeMessage) => { if (!message.util.isSlashMessage(message)) { diff --git a/src/commands/moderation/removeReactionEmoji.ts b/src/commands/moderation/removeReactionEmoji.ts index bc24db0..4359c5b 100644 --- a/src/commands/moderation/removeReactionEmoji.ts +++ b/src/commands/moderation/removeReactionEmoji.ts @@ -1,4 +1,4 @@ -import { BushCommand, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, Message, PermissionFlagsBits } from 'discord.js'; @@ -40,8 +40,8 @@ export default class RemoveReactionEmojiCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, - args: { message: ArgType<'guildMessage'> | string; emoji: ArgType<'emoji'> | ArgType<'snowflake'> } + message: CommandMessage | SlashMessage, + args: { message: ArgType<'guildMessage'> | string; emoji: ArgType<'emoji' | 'snowflake'> } ) { assert(message.channel); const resolvedMessage = args.message instanceof Message ? args.message : await message.channel.messages.fetch(args.message); diff --git a/src/commands/moderation/role.ts b/src/commands/moderation/role.ts index a87b2bf..b9e1490 100644 --- a/src/commands/moderation/role.ts +++ b/src/commands/moderation/role.ts @@ -4,10 +4,11 @@ import { BushCommand, removeRoleResponse, type ArgType, - type BushMessage, - type BushSlashMessage, - type OptArgType + type CommandMessage, + type OptArgType, + type SlashMessage } from '#lib'; +import assert from 'assert'; import { type ArgumentGeneratorReturn } from 'discord-akairo'; import { ApplicationCommandOptionType, PermissionFlagsBits, type Snowflake } from 'discord.js'; @@ -59,6 +60,7 @@ export default class RoleCommand extends BushCommand { ], slash: true, channel: 'guild', + flags: ['--force'], typing: true, clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.ManageRoles, PermissionFlagsBits.EmbedLinks], true), @@ -66,7 +68,7 @@ export default class RoleCommand extends BushCommand { }); } - public override *args(message: BushMessage): ArgumentGeneratorReturn { + public override *args(message: CommandMessage): ArgumentGeneratorReturn { const action = (['rr'] as const).includes(message.util.parsed?.alias ?? '') ? 'remove' : (['ar', 'ra'] as const).includes(message.util.parsed?.alias ?? '') @@ -118,15 +120,16 @@ export default class RoleCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, + message: CommandMessage | SlashMessage, args: { action: 'add' | 'remove'; member: ArgType<'member'>; role: ArgType<'role'>; - duration?: OptArgType<'duration'>; - force?: boolean; + duration: OptArgType<'duration'>; + force?: ArgType<'flag'>; } ) { + assert(message.inGuild()); if (!args.role) return await message.util.reply(`${util.emojis.error} You must specify a role.`); args.duration ??= 0; if ( diff --git a/src/commands/moderation/slowmode.ts b/src/commands/moderation/slowmode.ts index 6d93ede..e657b76 100644 --- a/src/commands/moderation/slowmode.ts +++ b/src/commands/moderation/slowmode.ts @@ -1,4 +1,4 @@ -import { BushCommand, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; import assert from 'assert'; import { Argument } from 'discord-akairo'; import { ApplicationCommandOptionType, ChannelType, PermissionFlagsBits } from 'discord.js'; @@ -42,17 +42,22 @@ export default class SlowmodeCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, + message: CommandMessage | SlashMessage, args: { - length: ArgType<'duration'> | ArgType<'durationSeconds'> | 'off' | 'none' | 'disable' | null; - channel: ArgType<'channel'>; + length: OptArgType<'duration' | 'durationSeconds'> | 'off' | 'none' | 'disable'; + channel: OptArgType<'channel'>; } ) { assert(message.inGuild()); - args.channel ??= message.channel; + args.channel ??= message.channel!; - if (!args.channel.isTextBased() || args.channel.isNews()) + if ( + args.channel.type !== ChannelType.GuildText && + args.channel.type !== ChannelType.GuildNews && + args.channel.type !== ChannelType.GuildVoice && + !args.channel.isThread() + ) return await message.util.reply(`${util.emojis.error} <#${args.channel.id}> is not a text or thread channel.`); args.length = diff --git a/src/commands/moderation/timeout.ts b/src/commands/moderation/timeout.ts index b8fb78f..ed3562d 100644 --- a/src/commands/moderation/timeout.ts +++ b/src/commands/moderation/timeout.ts @@ -4,8 +4,8 @@ import { Moderation, timeoutResponse, type ArgType, - type BushMessage, - type BushSlashMessage + type CommandMessage, + type SlashMessage } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, PermissionFlagsBits } from 'discord.js'; @@ -55,8 +55,8 @@ export default class TimeoutCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, - args: { user: ArgType<'user'>; reason_and_duration: ArgType<'contentWithDuration'> | string; force?: ArgType<'boolean'> } + message: CommandMessage | SlashMessage, + args: { user: ArgType<'user'>; reason_and_duration: ArgType<'contentWithDuration'> | string; force?: ArgType<'flag'> } ) { assert(message.inGuild()); assert(message.member); diff --git a/src/commands/moderation/unban.ts b/src/commands/moderation/unban.ts index e6ac6d0..d7549ab 100644 --- a/src/commands/moderation/unban.ts +++ b/src/commands/moderation/unban.ts @@ -3,9 +3,9 @@ import { BushCommand, unbanResponse, type ArgType, - type BushMessage, - type BushSlashMessage, - type OptArgType + type CommandMessage, + type OptArgType, + type SlashMessage } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, PermissionFlagsBits } from 'discord.js'; @@ -46,8 +46,8 @@ export default class UnbanCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, - { user, reason }: { user: ArgType<'user'>; reason: OptArgType<'string'> } + message: CommandMessage | SlashMessage, + { user, reason }: { user: ArgType<'user' | 'globalUser'>; reason: OptArgType<'string'> } ) { assert(message.inGuild()); diff --git a/src/commands/moderation/unblock.ts b/src/commands/moderation/unblock.ts index 22c9949..886d546 100644 --- a/src/commands/moderation/unblock.ts +++ b/src/commands/moderation/unblock.ts @@ -4,9 +4,9 @@ import { Moderation, unblockResponse, type ArgType, - type BushMessage, - type BushSlashMessage, - type OptArgType + type CommandMessage, + type OptArgType, + type SlashMessage } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, PermissionFlagsBits } from 'discord.js'; @@ -57,11 +57,12 @@ export default class UnblockCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, - args: { user: ArgType<'user'>; reason: OptArgType<'string'>; force?: ArgType<'boolean'> } + message: CommandMessage | SlashMessage, + args: { user: ArgType<'user'>; reason: OptArgType<'string'>; force?: ArgType<'flag'> } ) { assert(message.inGuild()); assert(message.member); + assert(message.channel); if (!message.channel.isTextBased()) return message.util.send(`${util.emojis.error} This command can only be used in text based channels.`); diff --git a/src/commands/moderation/unlockdown.ts b/src/commands/moderation/unlockdown.ts index 253ce37..dbcd429 100644 --- a/src/commands/moderation/unlockdown.ts +++ b/src/commands/moderation/unlockdown.ts @@ -1,5 +1,5 @@ import { LockdownCommand } from '#commands'; -import { BushCommand, type ArgType, type BushMessage, type BushSlashMessage, type OptArgType } from '#lib'; +import { BushCommand, type ArgType, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; import { ApplicationCommandOptionType, ChannelType, PermissionFlagsBits } from 'discord.js'; export default class UnlockdownCommand extends BushCommand { @@ -14,7 +14,7 @@ export default class UnlockdownCommand extends BushCommand { { id: 'channel', description: 'Specify a different channel to unlockdown instead of the one you trigger the command in.', - type: util.arg.union('textChannel', 'newsChannel', 'threadChannel'), + type: util.arg.union('textChannel', 'newsChannel', 'threadChannel', 'voiceChannel'), prompt: 'What channel would you like to unlockdown?', slashType: ApplicationCommandOptionType.Channel, channelTypes: [ @@ -22,7 +22,8 @@ export default class UnlockdownCommand extends BushCommand { ChannelType.GuildNews, ChannelType.GuildNewsThread, ChannelType.GuildPublicThread, - ChannelType.GuildPrivateThread + ChannelType.GuildPrivateThread, + ChannelType.GuildVoice ], optional: true }, @@ -53,11 +54,11 @@ export default class UnlockdownCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, + message: CommandMessage | SlashMessage, args: { - channel: OptArgType<'textChannel'> | OptArgType<'newsChannel'> | OptArgType<'threadChannel'>; + channel: OptArgType<'textChannel' | 'newsChannel' | 'threadChannel' | 'voiceChannel'>; reason: OptArgType<'string'>; - all: ArgType<'boolean'>; + all: ArgType<'flag'>; } ) { return await LockdownCommand.lockdownOrUnlockdown(message, args, 'unlockdown'); diff --git a/src/commands/moderation/unmute.ts b/src/commands/moderation/unmute.ts index 094cadd..202e341 100644 --- a/src/commands/moderation/unmute.ts +++ b/src/commands/moderation/unmute.ts @@ -4,10 +4,9 @@ import { Moderation, unmuteResponse, type ArgType, - type BushGuildMember, - type BushMessage, - type BushSlashMessage, - type OptArgType + type CommandMessage, + type OptArgType, + type SlashMessage } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, PermissionFlagsBits } from 'discord.js'; @@ -58,14 +57,14 @@ export default class UnmuteCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, - { user, reason, force = false }: { user: ArgType<'user'>; reason: OptArgType<'string'>; force?: boolean } + message: CommandMessage | SlashMessage, + { user, reason, force = false }: { user: ArgType<'user'>; reason: OptArgType<'string'>; force?: ArgType<'flag'> } ) { assert(message.inGuild()); assert(message.member); const error = util.emojis.error; - const member = message.guild.members.cache.get(user.id) as BushGuildMember; + 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); diff --git a/src/commands/moderation/untimeout.ts b/src/commands/moderation/untimeout.ts index cbaf7d1..7622f29 100644 --- a/src/commands/moderation/untimeout.ts +++ b/src/commands/moderation/untimeout.ts @@ -4,9 +4,9 @@ import { Moderation, removeTimeoutResponse, type ArgType, - type BushMessage, - type BushSlashMessage, - type OptArgType + type CommandMessage, + type OptArgType, + type SlashMessage } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, PermissionFlagsBits } from 'discord.js'; @@ -57,8 +57,8 @@ export default class UntimeoutCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, - args: { user: ArgType<'user'>; reason: OptArgType<'string'>; force?: ArgType<'boolean'> } + message: CommandMessage | SlashMessage, + args: { user: ArgType<'user'>; reason: OptArgType<'string'>; force?: ArgType<'flag'> } ) { assert(message.inGuild()); assert(message.member); diff --git a/src/commands/moderation/warn.ts b/src/commands/moderation/warn.ts index 87baf4a..b3bf965 100644 --- a/src/commands/moderation/warn.ts +++ b/src/commands/moderation/warn.ts @@ -4,9 +4,9 @@ import { Moderation, warnResponse, type ArgType, - type BushMessage, - type BushSlashMessage, - type OptArgType + type CommandMessage, + type OptArgType, + type SlashMessage } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, PermissionFlagsBits } from 'discord.js'; @@ -56,8 +56,8 @@ export default class WarnCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, - { user, reason, force = false }: { user: ArgType<'user'>; reason: OptArgType<'string'>; force?: boolean } + message: CommandMessage | SlashMessage, + { user, reason, force = false }: { user: ArgType<'user'>; reason: OptArgType<'string'>; force?: ArgType<'flag'> } ) { assert(message.inGuild()); assert(message.member); diff --git a/src/commands/moulberry-bush/capePermissions.ts b/src/commands/moulberry-bush/capePermissions.ts index dd210e2..5f15d9e 100644 --- a/src/commands/moulberry-bush/capePermissions.ts +++ b/src/commands/moulberry-bush/capePermissions.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushCommand, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { AllowedMentions, BushCommand, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; import { ApplicationCommandOptionType, EmbedBuilder, PermissionFlagsBits } from 'discord.js'; import got from 'got'; @@ -27,7 +27,7 @@ export default class CapePermissionsCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage, args: { ign: ArgType<'string'> }) { + public override async exec(message: CommandMessage | SlashMessage, args: { ign: ArgType<'string'> }) { let capePerms: CapePerms | null, uuid: string; try { uuid = await util.mcUUID(args.ign); diff --git a/src/commands/moulberry-bush/capes.ts b/src/commands/moulberry-bush/capes.ts index 3f273c1..6bf7854 100644 --- a/src/commands/moulberry-bush/capes.ts +++ b/src/commands/moulberry-bush/capes.ts @@ -1,7 +1,14 @@ -import { AllowedMentions, BushCommand, ButtonPaginator, DeleteButton, type BushMessage, type OptArgType } from '#lib'; +import { + AllowedMentions, + BushCommand, + ButtonPaginator, + DeleteButton, + type CommandMessage, + type OptArgType, + type SlashMessage +} from '#lib'; import assert from 'assert'; -import { APIEmbed } from 'discord-api-types/v10'; -import { ApplicationCommandOptionType, AutocompleteInteraction, PermissionFlagsBits } from 'discord.js'; +import { ApplicationCommandOptionType, PermissionFlagsBits, type APIEmbed, type AutocompleteInteraction } from 'discord.js'; import Fuse from 'fuse.js'; import got from 'got'; @@ -34,7 +41,7 @@ export default class CapesCommand extends BushCommand { }); } - public override async exec(message: BushMessage, args: { cape: OptArgType<'string'> }) { + public override async exec(message: CommandMessage | SlashMessage, args: { cape: OptArgType<'string'> }) { const { tree: neuFileTree }: GithubTreeApi = await got .get('https://api.github.com/repos/Moulberry/NotEnoughUpdates/git/trees/master?recursive=1') .json(); diff --git a/src/commands/moulberry-bush/giveawayPing.ts b/src/commands/moulberry-bush/giveawayPing.ts index dd305c6..23a6a20 100644 --- a/src/commands/moulberry-bush/giveawayPing.ts +++ b/src/commands/moulberry-bush/giveawayPing.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushCommand, type BushMessage } from '#lib'; +import { AllowedMentions, BushCommand, type CommandMessage } from '#lib'; import { PermissionFlagsBits } from 'discord.js'; export default class GiveawayPingCommand extends BushCommand { @@ -28,7 +28,7 @@ export default class GiveawayPingCommand extends BushCommand { }); } - public override async exec(message: BushMessage) { + public override async exec(message: CommandMessage) { if (!message.member!.permissions.has(PermissionFlagsBits.ManageGuild) && !message.member!.user.isOwner()) await message.util.reply(`${util.emojis.error} You are missing the **ManageGuild** permission.`); diff --git a/src/commands/moulberry-bush/moulHammer.ts b/src/commands/moulberry-bush/moulHammer.ts index a5d2c27..f07511a 100644 --- a/src/commands/moulberry-bush/moulHammer.ts +++ b/src/commands/moulberry-bush/moulHammer.ts @@ -1,4 +1,4 @@ -import { BushCommand, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, EmbedBuilder, PermissionFlagsBits } from 'discord.js'; @@ -29,10 +29,10 @@ export default class MoulHammerCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage, { user }: { user: ArgType<'user'> }) { + public override async exec(message: CommandMessage | SlashMessage, { user }: { user: ArgType<'user'> }) { assert(message.inGuild()); - if (message.channel.permissionsFor(message.guild.members.me!).has('ManageMessages')) await message.delete().catch(() => {}); + if (message.channel?.permissionsFor(message.guild.members.me!).has('ManageMessages')) await message.delete().catch(() => {}); const embed = new EmbedBuilder() .setTitle('L') diff --git a/src/commands/moulberry-bush/report.ts b/src/commands/moulberry-bush/report.ts index 0561e13..29eee76 100644 --- a/src/commands/moulberry-bush/report.ts +++ b/src/commands/moulberry-bush/report.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushCommand, type ArgType, type BushMessage } from '#lib'; +import { AllowedMentions, BushCommand, type ArgType, type CommandMessage } from '#lib'; import { stripIndent } from '#tags'; import assert from 'assert'; import { ApplicationCommandOptionType, EmbedBuilder, PermissionFlagsBits } from 'discord.js'; @@ -38,7 +38,7 @@ export default class ReportCommand extends BushCommand { }); } - public override async exec(message: BushMessage, { member, evidence }: { member: ArgType<'member'>; evidence: string }) { + public override async exec(message: CommandMessage, { member, evidence }: { member: ArgType<'member'>; evidence: string }) { assert(message.inGuild()); if (!(await message.guild.hasFeature('reporting'))) diff --git a/src/commands/moulberry-bush/rule.ts b/src/commands/moulberry-bush/rule.ts index cd9994f..17f1bd8 100644 --- a/src/commands/moulberry-bush/rule.ts +++ b/src/commands/moulberry-bush/rule.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushCommand, BushSlashMessage, type BushMessage, type OptArgType } from '#lib'; +import { AllowedMentions, BushCommand, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; import { ApplicationCommandOptionType, EmbedBuilder, PermissionFlagsBits } from 'discord.js'; import { stripIndent } from '../../lib/common/tags.js'; @@ -99,7 +99,7 @@ export default class RuleCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, + message: CommandMessage | SlashMessage, { rule, user }: { rule: OptArgType<'integer'>; user: OptArgType<'user'> } ) { const rulesEmbed = new EmbedBuilder() diff --git a/src/commands/moulberry-bush/serverStatus.ts b/src/commands/moulberry-bush/serverStatus.ts index c0df828..ad3903a 100644 --- a/src/commands/moulberry-bush/serverStatus.ts +++ b/src/commands/moulberry-bush/serverStatus.ts @@ -1,4 +1,4 @@ -import { BushCommand, type BushMessage } from '#lib'; +import { BushCommand, type CommandMessage } from '#lib'; import assert from 'assert'; import { EmbedBuilder, PermissionFlagsBits } from 'discord.js'; import got from 'got'; @@ -19,7 +19,7 @@ export default class ServerStatusCommand extends BushCommand { }); } - public override async exec(message: BushMessage) { + public override async exec(message: CommandMessage) { const msgEmbed = new EmbedBuilder() .setTitle('Server status') .setDescription(`Checking server:\n${util.emojis.loading}`) diff --git a/src/commands/utilities/_poll.ts b/src/commands/utilities/_poll.ts index 81bb5fc..a843561 100644 --- a/src/commands/utilities/_poll.ts +++ b/src/commands/utilities/_poll.ts @@ -1,81 +1,80 @@ -/* import { BushCommand, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; -import { ApplicationCommandOptionType, ComponentType } from 'discord.js'; +// import { BushCommand, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +// import { ApplicationCommandOptionType, ComponentType } from 'discord.js'; -export default class PollCommand extends BushCommand { - public constructor() { - super('poll', { - aliases: ['poll', 'quick-poll'], - category: 'utilities', - description: 'Allows you to create a poll that other users can vote on. Separate options with "," or "|".', - usage: ['poll options'], - examples: ['poll 1 2'], - args: [ - { - id: 'question', - description: 'The question to be answered by a poll.', - type: 'string', - prompt: 'What question would you like to ask?', - retry: '{error} Choose a question.', - slashType: ApplicationCommandOptionType.String, - only: 'slash' - }, - { - id: 'options', - description: 'The options to include in the poll. Separate options with "," or "|".', - type: 'string', - prompt: 'What options you want to include in the poll? Separate options with "," or "|".', - retry: '{error} Choose options for the poll. Separate options with "," or "|".', - slashType: ApplicationCommandOptionType.String - } - ], - slash: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m), - userPermissions: [] - }); - } +// export default class PollCommand extends BushCommand { +// public constructor() { +// super('poll', { +// aliases: ['poll', 'quick-poll'], +// category: 'utilities', +// description: 'Allows you to create a poll that other users can vote on. Separate options with "," or "|".', +// usage: ['poll options'], +// examples: ['poll 1 2'], +// args: [ +// { +// id: 'question', +// description: 'The question to be answered by a poll.', +// type: 'string', +// prompt: 'What question would you like to ask?', +// retry: '{error} Choose a question.', +// slashType: ApplicationCommandOptionType.String, +// only: 'slash' +// }, +// { +// id: 'options', +// description: 'The options to include in the poll. Separate options with "," or "|".', +// type: 'string', +// prompt: 'What options you want to include in the poll? Separate options with "," or "|".', +// retry: '{error} Choose options for the poll. Separate options with "," or "|".', +// slashType: ApplicationCommandOptionType.String +// } +// ], +// slash: true, +// clientPermissions: (m) => util.clientSendAndPermCheck(m), +// userPermissions: [] +// }); +// } - public override async exec(message: BushMessage | BushSlashMessage, args: { question?: string; options: ArgType<'string'> }) { - const { question, options } = this.parseArgs(message, args); - if (!question || !options.length) return; +// public override async exec(message: CommandMessage | SlashMessage, args: { question?: string; options: ArgType<'string'> }) { +// const { question, options } = this.parseArgs(message, args); +// if (!question || !options.length) return; - if (question.length > 256) return await message.util.reply(`${util.emojis.error} Question must be 256 characters or less.`); - if (options.length > 10) return await message.util.reply(`${util.emojis.error} You can only have upto 10 options.`); +// if (question.length > 256) return await message.util.reply(`${util.emojis.error} Question must be 256 characters or less.`); +// if (options.length > 10) return await message.util.reply(`${util.emojis.error} You can only have upto 10 options.`); - return message.util.send({ - embeds: [ - { - author: { name: `asked by: ${message.author.tag}`, icon_url: message.author.displayAvatarURL() || undefined }, - title: question - } - ], - components: [ - { - type: ComponentType.ActionRow, - components: [] - } - ] - }); - } +// return message.util.send({ +// embeds: [ +// { +// author: { name: `asked by: ${message.author.tag}`, icon_url: message.author.displayAvatarURL() || undefined }, +// title: question +// } +// ], +// components: [ +// { +// type: ComponentType.ActionRow, +// components: [] +// } +// ] +// }); +// } - private parseArgs( - message: BushMessage | BushSlashMessage, - args: { question?: string; options: ArgType<'string'> } - ): { question: string; options: string[] } { - const split = args.options.split(/[,|]/).filter((s) => s.trim().length > 0); - if (message.util.isSlash) { - if (split.length < 2) { - void message.util.reply(`${util.emojis.error} You must provide at least two options.`); - return { question: '', options: [] }; - } - return { question: args.question!, options: split }; - } else { - if (split.length < 3) { - void message.util.reply(`${util.emojis.error} You must provide a question and at least two options.`); - return { question: '', options: [] }; - } +// private parseArgs( +// message: CommandMessage | SlashMessage, +// args: { question?: string; options: ArgType<'string'> } +// ): { question: string; options: string[] } { +// const split = args.options.split(/[,|]/).filter((s) => s.trim().length > 0); +// if (message.util.isSlash) { +// if (split.length < 2) { +// void message.util.reply(`${util.emojis.error} You must provide at least two options.`); +// return { question: '', options: [] }; +// } +// return { question: args.question!, options: split }; +// } else { +// if (split.length < 3) { +// void message.util.reply(`${util.emojis.error} You must provide a question and at least two options.`); +// return { question: '', options: [] }; +// } - return { question: split[0], options: split.slice(1) }; - } - } -} - */ +// return { question: split[0], options: split.slice(1) }; +// } +// } +// } diff --git a/src/commands/utilities/activity.ts b/src/commands/utilities/activity.ts index 230cc81..14cc5a0 100644 --- a/src/commands/utilities/activity.ts +++ b/src/commands/utilities/activity.ts @@ -1,4 +1,4 @@ -import { BushCommand, type ArgType, type BushArgumentTypeCaster, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, type ArgType, type BushArgumentTypeCaster, type CommandMessage, type SlashMessage } from '#lib'; import { type ArgumentGeneratorReturn, type ArgumentTypeCaster } from 'discord-akairo'; import { ApplicationCommandOptionType, ChannelType, type DiscordAPIError, type Snowflake } from 'discord.js'; @@ -66,7 +66,7 @@ function map(phase: string): Activity | null { return null; } -const activityTypeCaster: BushArgumentTypeCaster = (message: BushMessage, phrase: string) => { +const activityTypeCaster: BushArgumentTypeCaster = (message: CommandMessage, phrase: string) => { const parsedPhrase = phrase ?? message.util.parsed?.alias !== 'activity' ? message.util.parsed?.alias : undefined; if (!parsedPhrase) return null; const mappedPhrase = map(parsedPhrase)?.id; @@ -120,7 +120,7 @@ export default class ActivityCommand extends BushCommand { }); } - public override *args(message: BushMessage): ArgumentGeneratorReturn { + public override *args(message: CommandMessage): ArgumentGeneratorReturn { const channel: ArgType<'voiceChannel'> = yield { id: 'channel', description: 'The channel to create the activity in.', @@ -151,11 +151,12 @@ export default class ActivityCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, + message: CommandMessage | SlashMessage, args: { channel: ArgType<'voiceChannel'>; activity: string } ) { const channel = typeof args.channel === 'string' ? message.guild?.channels.cache.get(args.channel) : args.channel; - if (!channel || !channel.isVoice()) return await message.util.reply(`${util.emojis.error} Choose a valid voice channel`); + if (channel?.type !== ChannelType.GuildVoice) + return await message.util.reply(`${util.emojis.error} Choose a valid voice channel`); const target_application_id = message.util.isSlashMessage(message) ? args.activity diff --git a/src/commands/utilities/calculator.ts b/src/commands/utilities/calculator.ts index e17ef76..a318a79 100644 --- a/src/commands/utilities/calculator.ts +++ b/src/commands/utilities/calculator.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { AllowedMentions, BushCommand, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, EmbedBuilder } from 'discord.js'; import { evaluate } from 'mathjs'; @@ -30,7 +30,7 @@ export default class CalculatorCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage, args: { expression: string }) { + public override async exec(message: CommandMessage | SlashMessage, args: { expression: string }) { const decodedEmbed = new EmbedBuilder().addFields([ { name: '📥 Input', diff --git a/src/commands/utilities/decode.ts b/src/commands/utilities/decode.ts index 7cb0e83..8c82fcc 100644 --- a/src/commands/utilities/decode.ts +++ b/src/commands/utilities/decode.ts @@ -1,5 +1,4 @@ -import { AllowedMentions, BushCommand, type BushMessage } from '#lib'; -import { type AkairoMessage } from 'discord-akairo'; +import { AllowedMentions, BushCommand, type CommandMessage, type SlashMessage } from '#lib'; import { ApplicationCommandOptionType, EmbedBuilder } from 'discord.js'; const encodingTypesArray = ['ascii', 'utf8', 'utf-8', 'utf16le', 'ucs2', 'ucs-2', 'base64', 'latin1', 'binary', 'hex']; @@ -49,7 +48,7 @@ export default class DecodeCommand extends BushCommand { } public override async exec( - message: BushMessage | AkairoMessage, + message: CommandMessage | SlashMessage, { from, to, data }: { from: BufferEncoding; to: BufferEncoding; data: string } ) { const encodeOrDecode = util.capitalizeFirstLetter(message?.util?.parsed?.alias ?? 'decoded'); diff --git a/src/commands/utilities/hash.ts b/src/commands/utilities/hash.ts index 7cc59f3..f47c388 100644 --- a/src/commands/utilities/hash.ts +++ b/src/commands/utilities/hash.ts @@ -1,4 +1,4 @@ -import { BushCommand, type BushMessage } from '#lib'; +import { BushCommand, type CommandMessage } from '#lib'; import assert from 'assert'; import crypto from 'crypto'; import { ApplicationCommandOptionType } from 'discord.js'; @@ -30,7 +30,7 @@ export default class HashCommand extends BushCommand { }); } - public override async exec(message: BushMessage, { url }: { url: string }) { + public override async exec(message: CommandMessage, { url }: { url: string }) { try { const req = await got.get(url); const rawHash = crypto.createHash('md5'); diff --git a/src/commands/utilities/highlight-!.ts b/src/commands/utilities/highlight-!.ts index 9aeb9ec..6dca545 100644 --- a/src/commands/utilities/highlight-!.ts +++ b/src/commands/utilities/highlight-!.ts @@ -1,7 +1,11 @@ -import { BushCommand, Highlight, HighlightWord, type BushSlashMessage } from '#lib'; +import { BushCommand, Highlight, HighlightWord, type SlashMessage } from '#lib'; import { Flag, type ArgumentGeneratorReturn, type SlashOption } from 'discord-akairo'; -import { ApplicationCommandOptionType } from 'discord-api-types/v10'; -import { ApplicationCommandSubCommandData, AutocompleteInteraction, CacheType } from 'discord.js'; +import { + ApplicationCommandOptionType, + ApplicationCommandSubCommandData, + type AutocompleteInteraction, + type CacheType +} from 'discord.js'; type Unpacked = T extends (infer U)[] ? U : T; @@ -139,7 +143,7 @@ export default class HighlightCommand extends BushCommand { return Flag.continue(`highlight-${subcommand}`); } - public override async execSlash(message: BushSlashMessage, args: { subcommand: keyof typeof highlightSubcommands }) { + public override async execSlash(message: SlashMessage, args: { subcommand: keyof typeof highlightSubcommands }) { // manual `Flag.continue` const subcommand = this.handler.modules.get(`highlight-${args.subcommand}`)!; return subcommand.exec(message, args); diff --git a/src/commands/utilities/highlight-add.ts b/src/commands/utilities/highlight-add.ts index 73c61d2..726b887 100644 --- a/src/commands/utilities/highlight-add.ts +++ b/src/commands/utilities/highlight-add.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushCommand, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { AllowedMentions, BushCommand, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; import { highlightCommandArgs, highlightSubcommands } from './highlight-!.js'; @@ -37,10 +37,7 @@ export default class HighlightAddCommand extends BushCommand { }); } - public override async exec( - message: BushMessage | BushSlashMessage, - args: { word: ArgType<'string'>; regex: ArgType<'boolean'> } - ) { + public override async exec(message: CommandMessage | SlashMessage, args: { word: ArgType<'string'>; regex: ArgType<'flag'> }) { assert(message.inGuild()); args.regex = false; diff --git a/src/commands/utilities/highlight-block.ts b/src/commands/utilities/highlight-block.ts index 4ad1a86..61e1254 100644 --- a/src/commands/utilities/highlight-block.ts +++ b/src/commands/utilities/highlight-block.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushCommand, Highlight, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { AllowedMentions, BushCommand, Highlight, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; import { Argument, ArgumentGeneratorReturn } from 'discord-akairo'; import { Channel, GuildMember } from 'discord.js'; @@ -18,7 +18,7 @@ export default class HighlightBlockCommand extends BushCommand { } public override *args(): ArgumentGeneratorReturn { - const target: ArgType<'member'> | ArgType<'channel'> = yield { + const target: ArgType<'member' | 'channel'> = yield { type: Argument.union('member', 'channel'), match: 'rest', prompt: { @@ -31,10 +31,7 @@ export default class HighlightBlockCommand extends BushCommand { return { target }; } - public override async exec( - message: BushMessage | BushSlashMessage, - args: { target: string | ArgType<'member'> | ArgType<'channel'> } - ) { + public override async exec(message: CommandMessage | SlashMessage, args: { target: string | ArgType<'member' | 'channel'> }) { assert(message.inGuild()); args.target = diff --git a/src/commands/utilities/highlight-clear.ts b/src/commands/utilities/highlight-clear.ts index 274c6fb..b905f3b 100644 --- a/src/commands/utilities/highlight-clear.ts +++ b/src/commands/utilities/highlight-clear.ts @@ -1,4 +1,4 @@ -import { BushCommand, ConfirmationPrompt, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, ConfirmationPrompt, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; import { highlightSubcommands } from './highlight-!.js'; @@ -15,7 +15,7 @@ export default class HighlightClearCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage) { + public override async exec(message: CommandMessage | SlashMessage) { assert(message.inGuild()); if (message.util.isSlashMessage(message)) await message.interaction.deferReply(); diff --git a/src/commands/utilities/highlight-matches.ts b/src/commands/utilities/highlight-matches.ts index 0e98c8d..40020cd 100644 --- a/src/commands/utilities/highlight-matches.ts +++ b/src/commands/utilities/highlight-matches.ts @@ -1,7 +1,7 @@ -import { BushCommand, ButtonPaginator, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, ButtonPaginator, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; -import { ArgumentGeneratorReturn } from 'discord-akairo'; -import { APIEmbed } from 'discord-api-types/v10'; +import { type ArgumentGeneratorReturn } from 'discord-akairo'; +import { type APIEmbed } from 'discord.js'; import { highlightCommandArgs, highlightSubcommands } from './highlight-!.js'; export default class HighlightMatchesCommand extends BushCommand { @@ -31,7 +31,7 @@ export default class HighlightMatchesCommand extends BushCommand { return { phrase }; } - public override async exec(message: BushMessage | BushSlashMessage, args: { phrase: ArgType<'string'> }) { + public override async exec(message: CommandMessage | SlashMessage, args: { phrase: ArgType<'string'> }) { assert(message.inGuild()); const res = await client.highlightManager.checkPhrase(message.guild.id, message.author.id, args.phrase); diff --git a/src/commands/utilities/highlight-remove.ts b/src/commands/utilities/highlight-remove.ts index 7e8c416..a2f2367 100644 --- a/src/commands/utilities/highlight-remove.ts +++ b/src/commands/utilities/highlight-remove.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushCommand, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { AllowedMentions, BushCommand, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; import { highlightCommandArgs, highlightSubcommands } from './highlight-!.js'; @@ -28,7 +28,7 @@ export default class HighlightRemoveCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage, args: { word: ArgType<'string'> }) { + public override async exec(message: CommandMessage | SlashMessage, args: { word: ArgType<'string'> }) { assert(message.inGuild()); const res = await client.highlightManager.removeHighlight(message.guild.id, message.author.id, args.word); diff --git a/src/commands/utilities/highlight-show.ts b/src/commands/utilities/highlight-show.ts index d0a91e6..80ba4ca 100644 --- a/src/commands/utilities/highlight-show.ts +++ b/src/commands/utilities/highlight-show.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushCommand, Highlight, type BushMessage, type BushSlashMessage } from '#lib'; +import { AllowedMentions, BushCommand, Highlight, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; import { EmbedBuilder } from 'discord.js'; import { highlightSubcommands } from './highlight-!.js'; @@ -16,7 +16,7 @@ export default class HighlightShowCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage) { + public override async exec(message: CommandMessage | SlashMessage) { assert(message.inGuild()); const [highlight] = await Highlight.findOrCreate({ diff --git a/src/commands/utilities/highlight-unblock.ts b/src/commands/utilities/highlight-unblock.ts index c437e6f..b738ee9 100644 --- a/src/commands/utilities/highlight-unblock.ts +++ b/src/commands/utilities/highlight-unblock.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushCommand, Highlight, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { AllowedMentions, BushCommand, Highlight, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; import { Argument, ArgumentGeneratorReturn } from 'discord-akairo'; import { Channel, GuildMember } from 'discord.js'; @@ -18,7 +18,7 @@ export default class HighlightUnblockCommand extends BushCommand { } public override *args(): ArgumentGeneratorReturn { - const target: ArgType<'member'> | ArgType<'channel'> = yield { + const target: ArgType<'member' | 'channel'> = yield { type: Argument.union('member', 'channel'), match: 'rest', prompt: { @@ -31,10 +31,7 @@ export default class HighlightUnblockCommand extends BushCommand { return { target }; } - public override async exec( - message: BushMessage | BushSlashMessage, - args: { target: ArgType<'user'> | ArgType<'role'> | ArgType<'member'> } - ) { + public override async exec(message: CommandMessage | SlashMessage, args: { target: ArgType<'user' | 'role' | 'member'> }) { assert(message.inGuild()); if (!(args.target instanceof GuildMember || args.target instanceof Channel)) diff --git a/src/commands/utilities/price.ts b/src/commands/utilities/price.ts index e4e4f50..a4f4c03 100644 --- a/src/commands/utilities/price.ts +++ b/src/commands/utilities/price.ts @@ -1,4 +1,4 @@ -import { BushCommand, type BushMessage } from '#lib'; +import { ArgType, BushCommand, type CommandMessage } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, AutocompleteInteraction, EmbedBuilder, PermissionFlagsBits } from 'discord.js'; import Fuse from 'fuse.js'; @@ -45,7 +45,7 @@ export default class PriceCommand extends BushCommand { }); } - public override async exec(message: BushMessage, { item, strict }: { item: string; strict: boolean }) { + public override async exec(message: CommandMessage, args: { item: ArgType<'string'>; strict: ArgType<'flag'> }) { if (message.util.isSlashMessage(message)) await message.interaction.deferReply(); const errors: string[] = []; @@ -57,7 +57,7 @@ export default class PriceCommand extends BushCommand { got.get('https://moulberry.codes/auction_averages/3day.json').json().catch(() => { errors.push('auction average') }) ])) as [Bazaar | undefined, LowestBIN | undefined, LowestBIN | undefined, AuctionAverages | undefined]; - let parsedItem = item.toString().toUpperCase().replace(/ /g, '_').replace(/'S/g, ''); + let parsedItem = args.item.toString().toUpperCase().replace(/ /g, '_').replace(/'S/g, ''); const priceEmbed = new EmbedBuilder().setColor(errors?.length ? util.colors.warn : util.colors.success).setTimestamp(); if (bazaar?.success === false) errors.push('bazaar'); @@ -75,7 +75,7 @@ export default class PriceCommand extends BushCommand { ]); // fuzzy search - if (!strict) { + if (!args.strict) { parsedItem = new Fuse([...itemNames], { isCaseSensitive: false, findAllMatches: true, diff --git a/src/commands/utilities/remind.ts b/src/commands/utilities/remind.ts index 044d4fc..8727879 100644 --- a/src/commands/utilities/remind.ts +++ b/src/commands/utilities/remind.ts @@ -1,4 +1,4 @@ -import { BushCommand, Reminder, Time, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, Reminder, Time, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; import { ApplicationCommandOptionType } from 'discord.js'; export default class RemindCommand extends BushCommand { @@ -28,8 +28,8 @@ export default class RemindCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, - args: { reminder: ArgType<'contentWithDuration'> | string } + message: CommandMessage | SlashMessage, + args: { reminder: OptArgType<'contentWithDuration'> | string } ) { const { duration, content } = await util.castDurationContent(args.reminder, message); diff --git a/src/commands/utilities/reminders.ts b/src/commands/utilities/reminders.ts index 653274a..79284ac 100644 --- a/src/commands/utilities/reminders.ts +++ b/src/commands/utilities/reminders.ts @@ -1,7 +1,6 @@ -import { BushCommand, ButtonPaginator, Reminder, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, ButtonPaginator, Reminder, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; -import { APIEmbed } from 'discord-api-types/v10'; -import { PermissionFlagsBits } from 'discord.js'; +import { PermissionFlagsBits, type APIEmbed } from 'discord.js'; import { Op } from 'sequelize'; assert(Op); @@ -20,7 +19,7 @@ export default class RemindersCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage) { + public override async exec(message: CommandMessage | SlashMessage) { const reminders = await Reminder.findAll({ where: { user: message.author.id, expires: { [Op.gt]: new Date() } } }); if (!reminders.length) return message.util.send(`${util.emojis.error} You don't have any reminders set.`); diff --git a/src/commands/utilities/steal.ts b/src/commands/utilities/steal.ts index e4f08f1..69d3054 100644 --- a/src/commands/utilities/steal.ts +++ b/src/commands/utilities/steal.ts @@ -1,7 +1,7 @@ -import { BushCommand, OptArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, OptArgType, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; import { type ArgumentGeneratorReturn, type ArgumentType, type ArgumentTypeCaster } from 'discord-akairo'; -import { ApplicationCommandOptionType, PermissionFlagsBits, type Attachment } from 'discord.js'; +import { ApplicationCommandOptionType, Attachment, PermissionFlagsBits } from 'discord.js'; import _ from 'lodash'; import { Stream } from 'stream'; import { URL } from 'url'; @@ -41,7 +41,7 @@ export default class StealCommand extends BushCommand { }); } - public override *args(message: BushMessage): ArgumentGeneratorReturn { + public override *args(message: CommandMessage): ArgumentGeneratorReturn { const hasImage = message.attachments.size && message.attachments.first()?.contentType?.includes('image/'); const emoji = hasImage @@ -60,8 +60,8 @@ export default class StealCommand extends BushCommand { } public override async exec( - message: BushMessage, - args: { emoji: OptArgType<'discordEmoji'> | OptArgType<'snowflake'> | OptArgType<'url'> | string; name: string } + message: CommandMessage, + args: { emoji: OptArgType<'discordEmoji' | 'snowflake' | 'url'>; name: OptArgType<'string'> } ) { assert(message.inGuild()); @@ -88,7 +88,9 @@ export default class StealCommand extends BushCommand { : 'stolen_emoji'; const creationSuccess = await message.guild.emojis - .create(image, emojiName, { + .create({ + attachment: image, + name: emojiName, reason: `Stolen by ${message.author.tag} (${message.author.id})` }) .catch((e: Error) => e); @@ -102,7 +104,7 @@ export default class StealCommand extends BushCommand { } } - public override async execSlash(message: BushSlashMessage, args: { emoji: Attachment; name?: string }) { + public override async execSlash(message: SlashMessage, args: { emoji: Attachment; name: string | null }) { assert(message.inGuild()); const name = args.name ?? args.emoji.name ?? 'stolen_emoji'; @@ -119,7 +121,9 @@ export default class StealCommand extends BushCommand { : args.emoji.attachment; const creationSuccess = await message.guild.emojis - .create(data, name, { + .create({ + attachment: data, + name: name, reason: `Stolen by ${message.author.tag} (${message.author.id})` }) .catch((e: Error) => e); diff --git a/src/commands/utilities/suicide.ts b/src/commands/utilities/suicide.ts index e4609f2..c4151e8 100644 --- a/src/commands/utilities/suicide.ts +++ b/src/commands/utilities/suicide.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { AllowedMentions, BushCommand, type CommandMessage, type SlashMessage } from '#lib'; import { stripIndent } from '#tags'; import { EmbedBuilder } from 'discord.js'; @@ -17,7 +17,7 @@ export default class SuicideCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage) { + public override async exec(message: CommandMessage | SlashMessage) { // stolen from https://github.com/dexbiobot/Zeppelin const suicideEmbed = new EmbedBuilder() .setTitle('Mental Health Resources') diff --git a/src/commands/utilities/uuid.ts b/src/commands/utilities/uuid.ts index 448baf3..1e70e91 100644 --- a/src/commands/utilities/uuid.ts +++ b/src/commands/utilities/uuid.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { AllowedMentions, ArgType, BushCommand, type CommandMessage, type SlashMessage } from '#lib'; import { ApplicationCommandOptionType } from 'discord.js'; export default class UuidCommand extends BushCommand { @@ -14,7 +14,7 @@ export default class UuidCommand extends BushCommand { id: 'ign', description: 'The ign to find the ign of.', customType: /\w{1,16}/im, - readableType: 'ign', + readableType: 'string[1,16]', prompt: 'What ign would you like to find the uuid of?', retry: '{error} Choose a valid ign.', slashType: ApplicationCommandOptionType.String @@ -23,7 +23,7 @@ export default class UuidCommand extends BushCommand { id: 'dashed', description: 'Include dashes in the uuid.', match: 'flag', - flag: '--dashed', + flag: ['--dashed', '-d'], prompt: 'Would you like to include dashes in the uuid?', slashType: ApplicationCommandOptionType.Boolean, optional: true @@ -36,15 +36,15 @@ export default class UuidCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, - { ign, dashed }: { ign: { match: RegExpMatchArray; matches?: any[] } | string; dashed: boolean } + message: CommandMessage | SlashMessage, + args: { ign: ArgType<'regex'> | string; dashed: ArgType<'flag'> } ) { - if (typeof ign === 'string') ign = { match: /\w{1,16}/im.exec(ign)! }; + if (typeof args.ign === 'string') args.ign = { match: /\w{1,16}/im.exec(args.ign)!, matches: [] }; - if (!ign || !ign.match) return await message.util.reply(`${util.emojis.error} Please enter a valid ign.`); - const readableIGN = ign.match[0]; + if (!args.ign.match) return await message.util.reply(`${util.emojis.error} Please enter a valid ign.`); + const readableIGN = args.ign.match[0]; try { - const uuid = await util.mcUUID(readableIGN, dashed); + const uuid = await util.mcUUID(readableIGN, args.dashed); return await message.util.reply({ content: `The uuid for ${util.format.input(readableIGN)} is ${util.format.input(uuid)}`, allowedMentions: AllowedMentions.none() diff --git a/src/commands/utilities/viewRaw.ts b/src/commands/utilities/viewRaw.ts index cb106dd..5c2c146 100644 --- a/src/commands/utilities/viewRaw.ts +++ b/src/commands/utilities/viewRaw.ts @@ -1,4 +1,4 @@ -import { BushCommand, type ArgType, type BushMessage, type BushSlashMessage, type OptArgType } from '#lib'; +import { BushCommand, type ArgType, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, ChannelType, EmbedBuilder, Message, PermissionFlagsBits } from 'discord.js'; @@ -23,7 +23,7 @@ export default class ViewRawCommand extends BushCommand { { id: 'channel', description: 'The channel that the message is in.', - type: util.arg.union('textChannel', 'newsChannel', 'threadChannel'), + type: util.arg.union('textChannel', 'newsChannel', 'threadChannel', 'voiceChannel'), prompt: 'What channel is the message in?', retry: '{error} Choose a valid channel.', optional: true, @@ -34,7 +34,8 @@ export default class ViewRawCommand extends BushCommand { ChannelType.GuildNews, ChannelType.GuildNewsThread, ChannelType.GuildPublicThread, - ChannelType.GuildPrivateThread + ChannelType.GuildPrivateThread, + ChannelType.GuildVoice ] }, { @@ -64,12 +65,12 @@ export default class ViewRawCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, + message: CommandMessage | SlashMessage, args: { - message: ArgType<'message'> | ArgType<'messageLink'>; - channel: OptArgType<'textChannel'> | OptArgType<'newsChannel'> | OptArgType<'threadChannel'>; - json: boolean; - js: boolean; + message: ArgType<'message' | 'messageLink'>; + channel: OptArgType<'textChannel' | 'newsChannel' | 'threadChannel' | 'voiceChannel'>; + json: ArgType<'flag'>; + js: ArgType<'flag'>; } ) { assert(message.inGuild()); @@ -88,7 +89,7 @@ export default class ViewRawCommand extends BushCommand { return await message.util.reply({ embeds: [Embed] }); } - public static async getRawData(message: BushMessage, options: { json?: boolean; js: boolean }): Promise { + public static async getRawData(message: Message, options: { json?: boolean; js: boolean }): Promise { const content = options.json || options.js ? options.json diff --git a/src/commands/utilities/whoHasRole.ts b/src/commands/utilities/whoHasRole.ts index 5f13c02..ae96a65 100644 --- a/src/commands/utilities/whoHasRole.ts +++ b/src/commands/utilities/whoHasRole.ts @@ -1,6 +1,6 @@ -import { BushCommand, BushRole, ButtonPaginator, OptArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, ButtonPaginator, OptArgType, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; -import { ApplicationCommandOptionType, Util, type CommandInteraction } from 'discord.js'; +import { ApplicationCommandOptionType, Util, type CommandInteraction, type Role } from 'discord.js'; export default class WhoHasRoleCommand extends BushCommand { public constructor() { @@ -31,7 +31,7 @@ export default class WhoHasRoleCommand extends BushCommand { } public override async exec( - message: BushMessage | BushSlashMessage, + message: CommandMessage | SlashMessage, args: { [K in `role${NumberRange}`]: OptArgType<'role'>; } @@ -39,7 +39,7 @@ export default class WhoHasRoleCommand extends BushCommand { assert(message.inGuild()); if (message.util.isSlash) await (message.interaction as CommandInteraction).deferReply(); - const rawRoles = Object.values(args).filter((v) => v !== null) as BushRole[]; + const rawRoles = Object.values(args).filter((v) => v !== null) as Role[]; const roles = rawRoles.map((v) => v.id); const members = message.guild.members.cache.filter((m) => roles.every((r) => m.roles.cache.has(r))); diff --git a/src/commands/utilities/wolframAlpha.ts b/src/commands/utilities/wolframAlpha.ts index 0e1cb14..98cac69 100644 --- a/src/commands/utilities/wolframAlpha.ts +++ b/src/commands/utilities/wolframAlpha.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { AllowedMentions, BushCommand, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; import { initializeClass as WolframAlphaAPI } from '@notenoughupdates/wolfram-alpha-api'; import assert from 'assert'; import { ApplicationCommandOptionType, EmbedBuilder, type MessageOptions } from 'discord.js'; @@ -39,7 +39,10 @@ export default class WolframAlphaCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage, args: { expression: string; image: boolean }) { + public override async exec( + message: CommandMessage | SlashMessage, + args: { expression: ArgType<'string'>; image: ArgType<'flag'> } + ) { if (message.util.isSlashMessage(message)) await message.interaction.deferReply(); args.image && void message.util.reply({ content: `${util.emojis.loading} Loading...`, embeds: [] }); diff --git a/src/context-menu-commands/message/viewRaw.ts b/src/context-menu-commands/message/viewRaw.ts index c283635..6cfe552 100644 --- a/src/context-menu-commands/message/viewRaw.ts +++ b/src/context-menu-commands/message/viewRaw.ts @@ -1,7 +1,6 @@ import { ViewRawCommand } from '#commands'; -import { type BushMessage } from '#lib'; import { ContextMenuCommand } from 'discord-akairo'; -import { ApplicationCommandType, type ContextMenuCommandInteraction } from 'discord.js'; +import { ApplicationCommandType, type ContextMenuCommandInteraction, type Message } from 'discord.js'; export default class ViewRawContextMenuCommand extends ContextMenuCommand { public constructor() { @@ -14,7 +13,7 @@ export default class ViewRawContextMenuCommand extends ContextMenuCommand { public override async exec(interaction: ContextMenuCommandInteraction) { await interaction.deferReply({ ephemeral: true }); - const embed = await ViewRawCommand.getRawData(interaction.options.getMessage('message') as BushMessage, { + const embed = await ViewRawCommand.getRawData(interaction.options.getMessage('message') as Message, { json: false, js: false }); diff --git a/src/context-menu-commands/user/modlog.ts b/src/context-menu-commands/user/modlog.ts index ac156cd..bdb311f 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 { BushCommandUtil, BushSlashMessage } from '#lib'; -import { ContextMenuCommand } from 'discord-akairo'; +import { SlashMessage } from '#lib'; +import { CommandUtil, ContextMenuCommand } from 'discord-akairo'; import { ApplicationCommandType, type ContextMenuCommandInteraction } from 'discord.js'; export default class ModlogContextMenuCommand extends ContextMenuCommand { @@ -25,8 +25,8 @@ export default class ModlogContextMenuCommand extends ContextMenuCommand { }); await interaction.deferReply({ ephemeral: true }); - const pseudoMessage = new BushSlashMessage(client, interaction as any); - pseudoMessage.util = new BushCommandUtil(client.commandHandler, pseudoMessage); + const pseudoMessage = new SlashMessage(client, interaction as any); + pseudoMessage.util = new CommandUtil(client.commandHandler, pseudoMessage); void new ModlogCommand().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 8ecde92..80e9883 100644 --- a/src/context-menu-commands/user/userInfo.ts +++ b/src/context-menu-commands/user/userInfo.ts @@ -1,7 +1,6 @@ import { UserInfoCommand } from '#commands'; -import { type BushGuild } from '#lib'; import { ContextMenuCommand } from 'discord-akairo'; -import { ApplicationCommandType, type ContextMenuCommandInteraction } from 'discord.js'; +import { ApplicationCommandType, type ContextMenuCommandInteraction, type Guild } from 'discord.js'; export default class UserInfoContextMenuCommand extends ContextMenuCommand { public constructor() { @@ -18,7 +17,7 @@ export default class UserInfoContextMenuCommand extends ContextMenuCommand { const user = await client.users.fetch(interaction.targetId).catch(() => null); if (!user) return interaction.reply(`⁉ I couldn't find that user`); - const guild = interaction.guild as BushGuild; + const guild = interaction.guild as Guild; const member = await guild.members.fetch(interaction.targetId).catch(() => null); if (!member) return interaction.reply(`${util.format.input(user.tag)} doesn't appear to be a member of this server anymore.`); diff --git a/src/inhibitors/blacklist/channelGlobalBlacklist.ts b/src/inhibitors/blacklist/channelGlobalBlacklist.ts index 34c23af..1ac26f1 100644 --- a/src/inhibitors/blacklist/channelGlobalBlacklist.ts +++ b/src/inhibitors/blacklist/channelGlobalBlacklist.ts @@ -1,4 +1,4 @@ -import { BushInhibitor, type BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushInhibitor, type BushCommand, type CommandMessage, type SlashMessage } from '#lib'; export default class UserGlobalBlacklistInhibitor extends BushInhibitor { public constructor() { @@ -10,7 +10,7 @@ export default class UserGlobalBlacklistInhibitor extends BushInhibitor { }); } - public override exec(message: BushMessage | BushSlashMessage, command: BushCommand): boolean { + public override exec(message: CommandMessage | SlashMessage, command: BushCommand): boolean { if (!message.author || !message.inGuild()) return false; // do not change to message.author.isOwner() if (client.isOwner(message.author) || client.user!.id === message.author.id) return false; diff --git a/src/inhibitors/blacklist/channelGuildBlacklist.ts b/src/inhibitors/blacklist/channelGuildBlacklist.ts index 09f58d0..a634015 100644 --- a/src/inhibitors/blacklist/channelGuildBlacklist.ts +++ b/src/inhibitors/blacklist/channelGuildBlacklist.ts @@ -1,4 +1,4 @@ -import { BushInhibitor, type BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushInhibitor, type BushCommand, type CommandMessage, type SlashMessage } from '#lib'; export default class ChannelGuildBlacklistInhibitor extends BushInhibitor { public constructor() { @@ -10,7 +10,7 @@ export default class ChannelGuildBlacklistInhibitor extends BushInhibitor { }); } - public override async exec(message: BushMessage | BushSlashMessage, command: BushCommand): Promise { + public override async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise { if (!message.author || !message.inGuild()) return false; // do not change to message.author.isOwner() if (client.isOwner(message.author) || client.user!.id === message.author.id) return false; diff --git a/src/inhibitors/blacklist/guildBlacklist.ts b/src/inhibitors/blacklist/guildBlacklist.ts index d540af7..087bdb7 100644 --- a/src/inhibitors/blacklist/guildBlacklist.ts +++ b/src/inhibitors/blacklist/guildBlacklist.ts @@ -1,4 +1,4 @@ -import { BushInhibitor, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushInhibitor, type CommandMessage, type SlashMessage } from '#lib'; export default class GuildBlacklistInhibitor extends BushInhibitor { public constructor() { @@ -10,7 +10,7 @@ export default class GuildBlacklistInhibitor extends BushInhibitor { }); } - public override exec(message: BushMessage | BushSlashMessage): boolean { + public override exec(message: CommandMessage | SlashMessage): boolean { if (!message.author || !message.inGuild()) return false; // do not change to message.author.isOwner() if (client.isOwner(message.author) || client.isSuperUser(message.author) || client.user!.id === message.author.id) diff --git a/src/inhibitors/blacklist/userGlobalBlacklist.ts b/src/inhibitors/blacklist/userGlobalBlacklist.ts index a3b0cdc..ae488c8 100644 --- a/src/inhibitors/blacklist/userGlobalBlacklist.ts +++ b/src/inhibitors/blacklist/userGlobalBlacklist.ts @@ -1,4 +1,4 @@ -import { BushInhibitor, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushInhibitor, type CommandMessage, type SlashMessage } from '#lib'; export default class UserGlobalBlacklistInhibitor extends BushInhibitor { public constructor() { @@ -10,7 +10,7 @@ export default class UserGlobalBlacklistInhibitor extends BushInhibitor { }); } - public override exec(message: BushMessage | BushSlashMessage): boolean { + public override exec(message: CommandMessage | SlashMessage): boolean { if (!message.author) return false; // do not change to message.author.isOwner() if (client.isOwner(message.author) || client.user!.id === message.author.id) return false; diff --git a/src/inhibitors/blacklist/userGuildBlacklist.ts b/src/inhibitors/blacklist/userGuildBlacklist.ts index b6c707a..c4fa527 100644 --- a/src/inhibitors/blacklist/userGuildBlacklist.ts +++ b/src/inhibitors/blacklist/userGuildBlacklist.ts @@ -1,4 +1,4 @@ -import { BushInhibitor, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushInhibitor, type CommandMessage, type SlashMessage } from '#lib'; export default class UserGuildBlacklistInhibitor extends BushInhibitor { public constructor() { @@ -10,7 +10,7 @@ export default class UserGuildBlacklistInhibitor extends BushInhibitor { }); } - public override async exec(message: BushMessage | BushSlashMessage): Promise { + public override async exec(message: CommandMessage | SlashMessage): Promise { if (!message.author || !message.inGuild()) return false; // do not change to message.author.isOwner() if (client.isOwner(message.author) || client.isSuperUser(message.author) || client.user!.id === message.author.id) diff --git a/src/inhibitors/checks/fatal.ts b/src/inhibitors/checks/fatal.ts index 2521b2f..520754a 100644 --- a/src/inhibitors/checks/fatal.ts +++ b/src/inhibitors/checks/fatal.ts @@ -1,4 +1,5 @@ -import { BushInhibitor, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushInhibitor, type SlashMessage } from '#lib'; +import { type Message } from 'discord.js'; export default class FatalInhibitor extends BushInhibitor { public constructor() { @@ -10,7 +11,7 @@ export default class FatalInhibitor extends BushInhibitor { }); } - public override async exec(message: BushMessage | BushSlashMessage): Promise { + public override async exec(message: Message | SlashMessage): Promise { if (client.isOwner(message.author)) return false; for (const property in client.cache.global) { if (!client.cache.global[property as keyof typeof client.cache.global]) { diff --git a/src/inhibitors/checks/guildUnavailable.ts b/src/inhibitors/checks/guildUnavailable.ts index a13944d..45ec0da 100644 --- a/src/inhibitors/checks/guildUnavailable.ts +++ b/src/inhibitors/checks/guildUnavailable.ts @@ -1,4 +1,5 @@ -import { BushInhibitor, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushInhibitor, type SlashMessage } from '#lib'; +import { type Message } from 'discord.js'; export default class GuildUnavailableInhibitor extends BushInhibitor { public constructor() { @@ -10,7 +11,7 @@ export default class GuildUnavailableInhibitor extends BushInhibitor { }); } - public override async exec(message: BushMessage | BushSlashMessage): Promise { + public override async exec(message: Message | SlashMessage): Promise { if (message.inGuild() && !message.guild.available) { void client.console.verbose( 'guildUnavailable', diff --git a/src/inhibitors/command/dm.ts b/src/inhibitors/command/dm.ts index ebcd647..d6c13a9 100644 --- a/src/inhibitors/command/dm.ts +++ b/src/inhibitors/command/dm.ts @@ -1,4 +1,4 @@ -import { BushInhibitor, type BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushInhibitor, type BushCommand, type CommandMessage, type SlashMessage } from '#lib'; export default class DMInhibitor extends BushInhibitor { public constructor() { @@ -10,7 +10,7 @@ export default class DMInhibitor extends BushInhibitor { }); } - public override async exec(message: BushMessage | BushSlashMessage, command: BushCommand): Promise { + public override async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise { if (command.channel === 'dm' && message.guild) { void client.console.verbose( 'dm', diff --git a/src/inhibitors/command/globalDisabledCommand.ts b/src/inhibitors/command/globalDisabledCommand.ts index 0ec1305..ada9716 100644 --- a/src/inhibitors/command/globalDisabledCommand.ts +++ b/src/inhibitors/command/globalDisabledCommand.ts @@ -1,4 +1,4 @@ -import { BushInhibitor, type BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushInhibitor, type BushCommand, type CommandMessage, type SlashMessage } from '#lib'; export default class DisabledGuildCommandInhibitor extends BushInhibitor { public constructor() { @@ -10,7 +10,7 @@ export default class DisabledGuildCommandInhibitor extends BushInhibitor { }); } - public override async exec(message: BushMessage | BushSlashMessage, command: BushCommand): Promise { + public override async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise { if (message.author.isOwner()) return false; if (client.cache.global.disabledCommands.includes(command?.id)) { void client.console.verbose( diff --git a/src/inhibitors/command/guild.ts b/src/inhibitors/command/guild.ts index 69b5df9..8b249ff 100644 --- a/src/inhibitors/command/guild.ts +++ b/src/inhibitors/command/guild.ts @@ -1,4 +1,4 @@ -import { BushInhibitor, type BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushInhibitor, type BushCommand, type CommandMessage, type SlashMessage } from '#lib'; export default class GuildInhibitor extends BushInhibitor { public constructor() { @@ -10,7 +10,7 @@ export default class GuildInhibitor extends BushInhibitor { }); } - public override async exec(message: BushMessage | BushSlashMessage, command: BushCommand): Promise { + public override async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise { if (command.channel === 'guild' && !message.guild) { void client.console.verbose( 'guild', diff --git a/src/inhibitors/command/guildDisabledCommand.ts b/src/inhibitors/command/guildDisabledCommand.ts index a04984d..a97f5cc 100644 --- a/src/inhibitors/command/guildDisabledCommand.ts +++ b/src/inhibitors/command/guildDisabledCommand.ts @@ -1,4 +1,4 @@ -import { BushInhibitor, type BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushInhibitor, type BushCommand, type CommandMessage, type SlashMessage } from '#lib'; export default class DisabledGuildCommandInhibitor extends BushInhibitor { public constructor() { @@ -10,7 +10,7 @@ export default class DisabledGuildCommandInhibitor extends BushInhibitor { }); } - public override async exec(message: BushMessage | BushSlashMessage, command: BushCommand): Promise { + public override async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise { if (!message.guild || !message.guild) return false; if (message.author.isOwner() || message.author.isSuperUser()) return false; // super users bypass guild disabled commands diff --git a/src/inhibitors/command/nsfw.ts b/src/inhibitors/command/nsfw.ts index fb4e51f..6eb2878 100644 --- a/src/inhibitors/command/nsfw.ts +++ b/src/inhibitors/command/nsfw.ts @@ -1,4 +1,4 @@ -import { BushInhibitor, type BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushInhibitor, type BushCommand, type CommandMessage, type SlashMessage } from '#lib'; import { type TextChannel } from 'discord.js'; export default class NsfwInhibitor extends BushInhibitor { @@ -11,7 +11,7 @@ export default class NsfwInhibitor extends BushInhibitor { }); } - public override async exec(message: BushMessage | BushSlashMessage, command: BushCommand): Promise { + public override async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise { if (command.onlyNsfw && !(message.channel as TextChannel).nsfw) { void client.console.verbose( 'notNsfw', diff --git a/src/inhibitors/command/owner.ts b/src/inhibitors/command/owner.ts index 8eee1e0..2331e04 100644 --- a/src/inhibitors/command/owner.ts +++ b/src/inhibitors/command/owner.ts @@ -1,4 +1,4 @@ -import { BushInhibitor, type BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushInhibitor, type BushCommand, type CommandMessage, type SlashMessage } from '#lib'; export default class OwnerInhibitor extends BushInhibitor { public constructor() { @@ -10,7 +10,7 @@ export default class OwnerInhibitor extends BushInhibitor { }); } - public override async exec(message: BushMessage | BushSlashMessage, command: BushCommand): Promise { + public override async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise { if (command.ownerOnly) { if (!client.isOwner(message.author)) { void client.console.verbose( diff --git a/src/inhibitors/command/restrictedChannel.ts b/src/inhibitors/command/restrictedChannel.ts index c8f2e1e..6b06f95 100644 --- a/src/inhibitors/command/restrictedChannel.ts +++ b/src/inhibitors/command/restrictedChannel.ts @@ -1,4 +1,4 @@ -import { BushInhibitor, type BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushInhibitor, type BushCommand, type CommandMessage, type SlashMessage } from '#lib'; export default class RestrictedChannelInhibitor extends BushInhibitor { public constructor() { @@ -10,7 +10,7 @@ export default class RestrictedChannelInhibitor extends BushInhibitor { }); } - public override async exec(message: BushMessage | BushSlashMessage, command: BushCommand): Promise { + public override async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise { if (command.restrictedChannels?.length && message.channel) { if (!command.restrictedChannels.includes(message.channel.id)) { void client.console.verbose( diff --git a/src/inhibitors/command/restrictedGuild.ts b/src/inhibitors/command/restrictedGuild.ts index a8a5097..47c61ce 100644 --- a/src/inhibitors/command/restrictedGuild.ts +++ b/src/inhibitors/command/restrictedGuild.ts @@ -1,4 +1,4 @@ -import { BushInhibitor, type BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushInhibitor, type BushCommand, type CommandMessage, type SlashMessage } from '#lib'; export default class RestrictedGuildInhibitor extends BushInhibitor { public constructor() { @@ -10,7 +10,7 @@ export default class RestrictedGuildInhibitor extends BushInhibitor { }); } - public override async exec(message: BushMessage | BushSlashMessage, command: BushCommand): Promise { + public override async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise { if (command.restrictedChannels?.length && message.channel) { if (!command.restrictedChannels.includes(message.channel.id)) { void client.console.verbose( diff --git a/src/inhibitors/command/superUser.ts b/src/inhibitors/command/superUser.ts index a7c1f47..23b1c64 100644 --- a/src/inhibitors/command/superUser.ts +++ b/src/inhibitors/command/superUser.ts @@ -1,4 +1,4 @@ -import { BushInhibitor, type BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushInhibitor, type BushCommand, type CommandMessage, type SlashMessage } from '#lib'; export default class SuperUserInhibitor extends BushInhibitor { public constructor() { @@ -10,7 +10,7 @@ export default class SuperUserInhibitor extends BushInhibitor { }); } - public override async exec(message: BushMessage | BushSlashMessage, command: BushCommand): Promise { + public override async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise { if (command.superUserOnly) { if (!client.isSuperUser(message.author)) { void client.console.verbose( diff --git a/src/lib/common/AutoMod.ts b/src/lib/common/AutoMod.ts index f30eab7..982e0e8 100644 --- a/src/lib/common/AutoMod.ts +++ b/src/lib/common/AutoMod.ts @@ -1,4 +1,4 @@ -import { banResponse, Moderation, type BushButtonInteraction, type BushMessage } from '#lib'; +import { banResponse, Moderation } from '#lib'; import assert from 'assert'; import chalk from 'chalk'; import { @@ -8,6 +8,8 @@ import { EmbedBuilder, GuildMember, PermissionFlagsBits, + type ButtonInteraction, + type Message, type TextChannel } from 'discord.js'; @@ -18,7 +20,7 @@ export class AutoMod { /** * The message to check for blacklisted phrases on */ - private message: BushMessage; + private message: Message; /** * Whether or not a punishment has already been given to the user @@ -28,7 +30,7 @@ export class AutoMod { /** * @param message The message to check and potentially perform automod actions to */ - public constructor(message: BushMessage) { + public constructor(message: Message) { this.message = message; if (message.author.id === client.user?.id) return; void this.handle(); @@ -355,7 +357,7 @@ export class AutoMod { * Handles the ban button in the automod log. * @param interaction The button interaction. */ - public static async handleInteraction(interaction: BushButtonInteraction) { + public static async handleInteraction(interaction: ButtonInteraction) { if (!interaction.memberPermissions?.has(PermissionFlagsBits.BanMembers)) return interaction.reply({ content: `${util.emojis.error} You are missing the **Ban Members** permission.`, @@ -382,7 +384,7 @@ export class AutoMod { user: userId, reason, moderator: interaction.user.id, - evidence: (interaction.message as BushMessage).url ?? undefined + evidence: (interaction.message as Message).url ?? undefined }); const victimUserFormatted = (await util.resolveNonCachedUser(userId))?.tag ?? userId; diff --git a/src/lib/common/ButtonPaginator.ts b/src/lib/common/ButtonPaginator.ts index cc95601..64870cf 100644 --- a/src/lib/common/ButtonPaginator.ts +++ b/src/lib/common/ButtonPaginator.ts @@ -1,7 +1,14 @@ -import { DeleteButton, type BushMessage, type BushSlashMessage } from '#lib'; +import { DeleteButton, type CommandMessage, type SlashMessage } from '#lib'; import { CommandUtil } from 'discord-akairo'; -import { APIEmbed } from 'discord-api-types/v10'; -import { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder, type MessageComponentInteraction } from 'discord.js'; +import { + ActionRowBuilder, + ButtonBuilder, + ButtonStyle, + EmbedBuilder, + type APIEmbed, + type Message, + type MessageComponentInteraction +} from 'discord.js'; /** * Sends multiple embeds with controls to switch between them @@ -10,7 +17,7 @@ export class ButtonPaginator { /** * The message that triggered the command */ - protected message: BushMessage | BushSlashMessage; + protected message: CommandMessage | SlashMessage; /** * The embeds to paginate @@ -35,7 +42,7 @@ export class ButtonPaginator { /** * The paginator message */ - protected sentMessage: BushMessage | undefined; + protected sentMessage: Message | undefined; /** * @param message The message to respond to @@ -45,7 +52,7 @@ export class ButtonPaginator { * @param startOn The page to start from (**not** the index) */ protected constructor( - message: BushMessage | BushSlashMessage, + message: CommandMessage | SlashMessage, embeds: EmbedBuilder[] | APIEmbed[], text: string | null, deleteOnExit: boolean, @@ -80,11 +87,11 @@ export class ButtonPaginator { * Sends the paginator message */ protected async send() { - this.sentMessage = (await this.message.util.reply({ + this.sentMessage = await this.message.util.reply({ content: this.text, embeds: [this.embeds[this.curPage]], components: [this.getPaginationRow()] - })) as BushMessage; + }); const collector = this.sentMessage.createMessageComponentCollector({ filter: (i) => i.customId.startsWith('paginate_'), @@ -214,7 +221,7 @@ export class ButtonPaginator { * @param startOn The page to start from (**not** the index) */ public static async send( - message: BushMessage | BushSlashMessage, + message: CommandMessage | SlashMessage, embeds: EmbedBuilder[] | APIEmbed[], text: string | null = null, deleteOnExit = true, diff --git a/src/lib/common/ConfirmationPrompt.ts b/src/lib/common/ConfirmationPrompt.ts index c611fd3..c95dbbc 100644 --- a/src/lib/common/ConfirmationPrompt.ts +++ b/src/lib/common/ConfirmationPrompt.ts @@ -1,4 +1,4 @@ -import { type BushMessage, type BushSlashMessage } from '#lib'; +import { type CommandMessage, type SlashMessage } from '#lib'; import { ActionRowBuilder, ButtonBuilder, ButtonStyle, type MessageComponentInteraction, type MessageOptions } from 'discord.js'; /** @@ -13,13 +13,13 @@ export class ConfirmationPrompt { /** * The message that triggered the command */ - protected message: BushMessage | BushSlashMessage; + protected message: CommandMessage | SlashMessage; /** * @param message The message to respond to * @param options The send message options */ - protected constructor(message: BushMessage | BushSlashMessage, messageOptions: MessageOptions) { + protected constructor(message: CommandMessage | SlashMessage, messageOptions: MessageOptions) { this.message = message; this.messageOptions = messageOptions; } @@ -71,7 +71,7 @@ export class ConfirmationPrompt { * @param message The message to respond to * @param options The send message options */ - public static async send(message: BushMessage | BushSlashMessage, sendOptions: MessageOptions): Promise { + public static async send(message: CommandMessage | SlashMessage, sendOptions: MessageOptions): Promise { return new ConfirmationPrompt(message, sendOptions).send(); } } diff --git a/src/lib/common/DeleteButton.ts b/src/lib/common/DeleteButton.ts index 03e2639..91f4bfa 100644 --- a/src/lib/common/DeleteButton.ts +++ b/src/lib/common/DeleteButton.ts @@ -1,4 +1,4 @@ -import { PaginateEmojis, type BushMessage, type BushSlashMessage } from '#lib'; +import { PaginateEmojis, type CommandMessage, type SlashMessage } from '#lib'; import { CommandUtil } from 'discord-akairo'; import { ActionRowBuilder, @@ -22,13 +22,13 @@ export class DeleteButton { /** * The message that triggered the command */ - protected message: BushMessage | BushSlashMessage; + protected message: CommandMessage | SlashMessage; /** * @param message The message to respond to * @param options The send message options */ - protected constructor(message: BushMessage | BushSlashMessage, options: MessageOptions) { + protected constructor(message: CommandMessage | SlashMessage, options: MessageOptions) { this.message = message; this.messageOptions = options; } @@ -39,7 +39,7 @@ export class DeleteButton { protected async send() { this.updateComponents(); - const msg = (await this.message.util.reply(this.messageOptions)) as BushMessage; + const msg = await this.message.util.reply(this.messageOptions); const collector = msg.createMessageComponentCollector({ filter: (interaction) => interaction.customId == 'paginate__stop' && interaction.message?.id == msg.id, @@ -85,7 +85,7 @@ export class DeleteButton { * @param message The message to respond to * @param options The send message options */ - public static async send(message: BushMessage | BushSlashMessage, options: Omit) { + public static async send(message: CommandMessage | SlashMessage, options: Omit) { return new DeleteButton(message, options).send(); } } diff --git a/src/lib/common/HighlightManager.ts b/src/lib/common/HighlightManager.ts index fffb266..fdec322 100644 --- a/src/lib/common/HighlightManager.ts +++ b/src/lib/common/HighlightManager.ts @@ -1,6 +1,6 @@ -import { Highlight, type BushMessage, type HighlightWord } from '#lib'; +import { Highlight, type HighlightWord } from '#lib'; import assert from 'assert'; -import { Collection, type Snowflake } from 'discord.js'; +import { Collection, type Message, type Snowflake } from 'discord.js'; import { Time } from '../utils/BushConstants.js'; const NOTIFY_COOLDOWN = 5 * Time.Minute; @@ -75,7 +75,7 @@ export class HighlightManager { * @param message The message to check. * @returns A collection users mapped to the highlight matched */ - public checkMessage(message: BushMessage): Collection { + public checkMessage(message: Message): Collection { // even if there are multiple matches, only the first one is returned const ret = new Collection(); if (!message.content || !message.inGuild()) return ret; @@ -225,7 +225,7 @@ export class HighlightManager { * @param hl The highlight that was matched. * @returns Whether or a dm was sent. */ - public async notify(message: BushMessage, user: Snowflake, hl: HighlightWord): Promise { + public async notify(message: Message, user: Snowflake, hl: HighlightWord): Promise { assert(message.inGuild()); dmCooldown: { @@ -301,7 +301,7 @@ export class HighlightManager { * Updates the time that a user last talked in a particular guild. * @param message The message the user sent. */ - public updateLastTalked(message: BushMessage): void { + public updateLastTalked(message: Message): void { if (!message.inGuild()) return; const lastTalked = ( this.userLastTalkedCooldown.has(message.guildId) diff --git a/src/lib/common/util/Arg.ts b/src/lib/common/util/Arg.ts index 01d3b0b..51d8065 100644 --- a/src/lib/common/util/Arg.ts +++ b/src/lib/common/util/Arg.ts @@ -1,4 +1,10 @@ -import { type BaseBushArgumentType, type BushArgumentType, type BushArgumentTypeCaster, type BushSlashMessage } from '#lib'; +import { + type BaseBushArgumentType, + type BushArgumentType, + type BushArgumentTypeCaster, + type CommandMessage, + type SlashMessage +} from '#lib'; import { Argument, type Flag, type ParsedValuePredicate } from 'discord-akairo'; import { type Message } from 'discord.js'; @@ -12,10 +18,10 @@ export class Arg { * @param message - Message that called the command. * @param phrase - Phrase to process. */ - public static async cast(type: T, message: Message | BushSlashMessage, phrase: string): Promise>; - public static async cast(type: T, message: Message | BushSlashMessage, phrase: string): Promise; - public static async cast(type: AT | ATC, message: Message | BushSlashMessage, phrase: string): Promise; - public static async cast(type: ATC | AT, message: Message | BushSlashMessage, phrase: string): Promise { + public static async cast(type: T, message: CommandMessage | SlashMessage, phrase: string): Promise>; + public static async cast(type: T, message: CommandMessage | SlashMessage, phrase: string): Promise; + public static async cast(type: AT | ATC, message: CommandMessage | SlashMessage, phrase: string): Promise; + public static async cast(type: ATC | AT, message: CommandMessage | SlashMessage, phrase: string): Promise { return Argument.cast(type as any, client.commandHandler.resolver, message as Message, phrase); } diff --git a/src/lib/common/util/Moderation.ts b/src/lib/common/util/Moderation.ts index 9f93375..6cdc141 100644 --- a/src/lib/common/util/Moderation.ts +++ b/src/lib/common/util/Moderation.ts @@ -1,17 +1,18 @@ -import { - ActivePunishment, - ActivePunishmentType, - Guild, - ModLog, - type BushGuild, - type BushGuildMember, - type BushGuildMemberResolvable, - type BushGuildResolvable, - type BushUserResolvable, - type ModLogType -} from '#lib'; +import { ActivePunishment, ActivePunishmentType, Guild as GuildDB, ModLog, type ModLogType } from '#lib'; import assert from 'assert'; -import { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder, PermissionFlagsBits, type Snowflake } from 'discord.js'; +import { + ActionRowBuilder, + ButtonBuilder, + ButtonStyle, + EmbedBuilder, + PermissionFlagsBits, + type Guild, + type GuildMember, + type GuildMemberResolvable, + type GuildResolvable, + type Snowflake, + type UserResolvable +} from 'discord.js'; enum punishMap { 'warned' = 'warn', @@ -52,8 +53,8 @@ export class Moderation { * @returns `true` if the moderator can perform the action otherwise a reason why they can't. */ public static async permissionCheck( - moderator: BushGuildMember, - victim: BushGuildMember, + moderator: GuildMember, + victim: GuildMember, type: | 'mute' | 'unmute' @@ -146,7 +147,7 @@ export class Moderation { getCaseNumber = false ): Promise<{ log: ModLog | null; caseNum: number | null }> { // If guild does not exist create it so the modlog can reference a guild. - await Guild.findOrCreate({ + await GuildDB.findOrCreate({ where: { id: options.guild }, defaults: { id: options.guild } }); @@ -349,17 +350,17 @@ export interface CreateModLogEntryOptions extends BaseCreateModLogEntryOptions { /** * The user that a modlog entry is created for. */ - user: BushGuildMemberResolvable; + user: GuildMemberResolvable; /** * The moderator that created the modlog entry. */ - moderator: BushGuildMemberResolvable; + moderator: GuildMemberResolvable; /** * The guild that the punishment is created for. */ - guild: BushGuildResolvable; + guild: GuildResolvable; } /** @@ -394,7 +395,7 @@ export interface CreatePunishmentEntryOptions { /** * The user that the punishment is created for. */ - user: BushGuildMemberResolvable; + user: GuildMemberResolvable; /** * The length of time the punishment lasts for. @@ -404,7 +405,7 @@ export interface CreatePunishmentEntryOptions { /** * The guild that the punishment is created for. */ - guild: BushGuildResolvable; + guild: GuildResolvable; /** * The id of the modlog that is linked to the punishment entry. @@ -429,12 +430,12 @@ export interface RemovePunishmentEntryOptions { /** * The user that the punishment is destroyed for. */ - user: BushGuildMemberResolvable; + user: GuildMemberResolvable; /** * The guild that the punishment was in. */ - guild: BushGuildResolvable; + guild: GuildResolvable; /** * Extra information for the punishment. The role for role punishments and the channel for blocks. @@ -454,12 +455,12 @@ export interface PunishDMOptions { /** * The guild that the punishment is taking place in. */ - guild: BushGuild; + guild: Guild; /** * The user that is being punished. */ - user: BushUserResolvable; + user: UserResolvable; /** * The punishment that the user has received. diff --git a/src/lib/extensions/discord-akairo/BushArgumentTypeCaster.ts b/src/lib/extensions/discord-akairo/BushArgumentTypeCaster.ts index 7a9a3db..def7ad6 100644 --- a/src/lib/extensions/discord-akairo/BushArgumentTypeCaster.ts +++ b/src/lib/extensions/discord-akairo/BushArgumentTypeCaster.ts @@ -1,3 +1,3 @@ -import { type BushMessage } from '#lib'; +import { type CommandMessage } from '#lib'; -export type BushArgumentTypeCaster = (message: BushMessage, phrase: string) => R; +export type BushArgumentTypeCaster = (message: CommandMessage, phrase: string) => R; diff --git a/src/lib/extensions/discord-akairo/BushClient.ts b/src/lib/extensions/discord-akairo/BushClient.ts index db0ad91..2644231 100644 --- a/src/lib/extensions/discord-akairo/BushClient.ts +++ b/src/lib/extensions/discord-akairo/BushClient.ts @@ -10,28 +10,20 @@ import { roleWithDuration, snowflake } from '#args'; -import type { - BushBaseGuildEmojiManager, - BushChannelManager, - BushClientEvents, - BushClientUser, - BushGuildManager, - BushUserManager, - BushUserResolvable, - Config -} from '#lib'; +import type { BushClientEvents, Config } from '#lib'; import { patch, type PatchedElements } from '@notenoughupdates/events-intercept'; import * as Sentry from '@sentry/node'; import { AkairoClient, - ArgumentPromptData, ContextMenuCommandHandler, - OtherwiseContentSupplier, - version as akairoVersion + version as akairoVersion, + type ArgumentPromptData, + type ClientUtil, + type OtherwiseContentSupplier } from 'discord-akairo'; -import { GatewayIntentBits } from 'discord-api-types/v10'; import { ActivityType, + GatewayIntentBits, MessagePayload, Options, Partials, @@ -45,19 +37,21 @@ import { type MessageOptions, type ReplyMessageOptions, type Snowflake, + type UserResolvable, type WebhookEditMessageOptions } from 'discord.js'; -import EventEmitter from 'events'; +import type EventEmitter from 'events'; import { google } from 'googleapis'; import path from 'path'; import readline from 'readline'; import type { Options as SequelizeOptions, Sequelize as SequelizeType } from 'sequelize'; import { fileURLToPath } from 'url'; +import { tinyColor } from '../../../arguments/tinyColor.js'; import UpdateCacheTask from '../../../tasks/updateCache.js'; import UpdateStatsTask from '../../../tasks/updateStats.js'; import { HighlightManager } from '../../common/HighlightManager.js'; import { ActivePunishment } from '../../models/instance/ActivePunishment.js'; -import { Guild as GuildModel } from '../../models/instance/Guild.js'; +import { Guild as GuildDB } from '../../models/instance/Guild.js'; import { Highlight } from '../../models/instance/Highlight.js'; import { Level } from '../../models/instance/Level.js'; import { ModLog } from '../../models/instance/ModLog.js'; @@ -71,26 +65,10 @@ import { AllowedMentions } from '../../utils/AllowedMentions.js'; import { BushCache } from '../../utils/BushCache.js'; import { BushConstants } from '../../utils/BushConstants.js'; import { BushLogger } from '../../utils/BushLogger.js'; -import { BushButtonInteraction } from '../discord.js/BushButtonInteraction.js'; -import { BushCategoryChannel } from '../discord.js/BushCategoryChannel.js'; -import { BushChatInputCommandInteraction } from '../discord.js/BushChatInputCommandInteraction.js'; -import { BushDMChannel } from '../discord.js/BushDMChannel.js'; -import { BushGuild } from '../discord.js/BushGuild.js'; -import { BushGuildEmoji } from '../discord.js/BushGuildEmoji.js'; -import { BushGuildMember } from '../discord.js/BushGuildMember.js'; -import { BushMessage } from '../discord.js/BushMessage.js'; -import { BushMessageReaction } from '../discord.js/BushMessageReaction.js'; -import { BushModalSubmitInteraction } from '../discord.js/BushModalSubmitInteraction.js'; -import { BushNewsChannel } from '../discord.js/BushNewsChannel.js'; -import { BushPresence } from '../discord.js/BushPresence.js'; -import { BushRole } from '../discord.js/BushRole.js'; -import { BushSelectMenuInteraction } from '../discord.js/BushSelectMenuInteraction.js'; -import { BushTextChannel } from '../discord.js/BushTextChannel.js'; -import { BushThreadChannel } from '../discord.js/BushThreadChannel.js'; -import { BushThreadMember } from '../discord.js/BushThreadMember.js'; -import { BushUser } from '../discord.js/BushUser.js'; -import { BushVoiceChannel } from '../discord.js/BushVoiceChannel.js'; -import { BushVoiceState } from '../discord.js/BushVoiceState.js'; +import { ExtendedGuild } from '../discord.js/ExtendedGuild.js'; +import { ExtendedGuildMember } from '../discord.js/ExtendedGuildMember.js'; +import { ExtendedMessage } from '../discord.js/ExtendedMessage.js'; +import { ExtendedUser } from '../discord.js/ExtendedUser.js'; import { BushClientUtil } from './BushClientUtil.js'; import { BushCommandHandler } from './BushCommandHandler.js'; import { BushInhibitorHandler } from './BushInhibitorHandler.js'; @@ -98,11 +76,43 @@ import { BushListenerHandler } from './BushListenerHandler.js'; import { BushTaskHandler } from './BushTaskHandler.js'; const { Sequelize } = (await import('sequelize')).default; -export type BushReplyMessageType = string | MessagePayload | ReplyMessageOptions; -export type BushEditMessageType = string | MessageEditOptions | MessagePayload; -export type BushSlashSendMessageType = string | MessagePayload | InteractionReplyOptions; -export type BushSlashEditMessageType = string | MessagePayload | WebhookEditMessageOptions; -export type BushSendMessageType = string | MessagePayload | MessageOptions; +declare module 'discord.js' { + export interface Client extends EventEmitter { + /** + * The ID of the owner(s). + */ + ownerID: Snowflake | Snowflake[]; + /** + * The ID of the superUser(s). + */ + superUserID: Snowflake | Snowflake[]; + /** + * Utility methods. + */ + util: ClientUtil | BushClientUtil; + on(event: K, listener: (...args: BushClientEvents[K]) => Awaitable): this; + once(event: K, listener: (...args: BushClientEvents[K]) => Awaitable): this; + emit(event: K, ...args: BushClientEvents[K]): boolean; + off(event: K, listener: (...args: BushClientEvents[K]) => Awaitable): this; + removeAllListeners(event?: K): this; + /** + * Checks if a user is the owner of this bot. + * @param user - User to check. + */ + isOwner(user: UserResolvable): boolean; + /** + * Checks if a user is a super user of this bot. + * @param user - User to check. + */ + isSuperUser(user: UserResolvable): boolean; + } +} + +export type ReplyMessageType = string | MessagePayload | ReplyMessageOptions; +export type EditMessageType = string | MessageEditOptions | MessagePayload; +export type SlashSendMessageType = string | MessagePayload | InteractionReplyOptions; +export type SlashEditMessageType = string | MessagePayload | WebhookEditMessageOptions; +export type SendMessageType = string | MessagePayload | MessageOptions; const rl = readline.createInterface({ input: process.stdin, @@ -116,12 +126,9 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url)); * The main hub for interacting with the Discord API. */ export class BushClient extends AkairoClient { - public declare channels: BushChannelManager; - public declare guilds: BushGuildManager; - public declare user: If; - public declare users: BushUserManager; - public declare util: BushClientUtil; public declare ownerID: Snowflake[]; + public declare superUserID: Snowflake[]; + public declare util: BushClientUtil; /** * Whether or not the client is ready. @@ -272,7 +279,7 @@ export class BushClient extends AkairoClient { if (this.config.isDevelopment) return 'dev '; if (!guild) return this.config.prefix; - const prefix = await (guild as BushGuild).getSetting('prefix'); + const prefix = await guild.getSetting('prefix'); return (prefix ?? this.config.prefix) as string; }, allowMention: true, @@ -296,7 +303,6 @@ export class BushClient extends AkairoClient extends AkairoClient BushGuildEmoji); - Structures.extend('DMChannel', () => BushDMChannel); - Structures.extend('TextChannel', () => BushTextChannel); - Structures.extend('VoiceChannel', () => BushVoiceChannel); - Structures.extend('CategoryChannel', () => BushCategoryChannel); - Structures.extend('NewsChannel', () => BushNewsChannel); - Structures.extend('ThreadChannel', () => BushThreadChannel); - Structures.extend('GuildMember', () => BushGuildMember); - Structures.extend('ThreadMember', () => BushThreadMember); - Structures.extend('Guild', () => BushGuild); - Structures.extend('Message', () => BushMessage); - Structures.extend('MessageReaction', () => BushMessageReaction); - Structures.extend('Presence', () => BushPresence); - Structures.extend('VoiceState', () => BushVoiceState); - Structures.extend('Role', () => BushRole); - Structures.extend('User', () => BushUser); - Structures.extend('ChatInputCommandInteraction', () => BushChatInputCommandInteraction); - Structures.extend('ButtonInteraction', () => BushButtonInteraction); - Structures.extend('SelectMenuInteraction', () => BushSelectMenuInteraction); - Structures.extend('ModalSubmitInteraction', () => BushModalSubmitInteraction); + Structures.extend('GuildMember', () => ExtendedGuildMember); + Structures.extend('Guild', () => ExtendedGuild); + Structures.extend('Message', () => ExtendedMessage); + Structures.extend('User', () => ExtendedUser); } /** @@ -407,7 +397,8 @@ export class BushClient extends AkairoClient extends AkairoClient extends AkairoClient extends EventEmitter, PatchedElements, AkairoClient { - get emojis(): BushBaseGuildEmojiManager; - on(event: K, listener: (...args: BushClientEvents[K]) => Awaitable): this; - // on(event: Exclude, listener: (...args: any[]) => Awaitable): this; - once(event: K, listener: (...args: BushClientEvents[K]) => Awaitable): this; - // once(event: Exclude, listener: (...args: any[]) => Awaitable): this; - emit(event: K, ...args: BushClientEvents[K]): boolean; - // emit(event: Exclude, ...args: unknown[]): boolean; - off(event: K, listener: (...args: BushClientEvents[K]) => Awaitable): this; - // off(event: Exclude, listener: (...args: any[]) => Awaitable): this; - removeAllListeners(event?: K): this; - // removeAllListeners(event?: Exclude): this; } export interface BushStats { diff --git a/src/lib/extensions/discord-akairo/BushClientUtil.ts b/src/lib/extensions/discord-akairo/BushClientUtil.ts index 9fe70fa..19810bd 100644 --- a/src/lib/extensions/discord-akairo/BushClientUtil.ts +++ b/src/lib/extensions/discord-akairo/BushClientUtil.ts @@ -1,41 +1,43 @@ import { Arg, - BaseBushArgumentType, BushConstants, + CommandMessage, Global, Shared, - SharedCache, + type BaseBushArgumentType, type BushClient, type BushInspectOptions, - type BushMessage, - type BushSlashEditMessageType, - type BushSlashMessage, - type BushSlashSendMessageType, - type BushUser, type CodeBlockLang, type GlobalCache, type Pronoun, - type PronounCode + type PronounCode, + type SharedCache, + type SlashEditMessageType, + type SlashMessage, + type SlashSendMessageType } from '#lib'; import { humanizeDuration } from '@notenoughupdates/humanize-duration'; import assert from 'assert'; import { exec } from 'child_process'; import deepLock from 'deep-lock'; import { ClientUtil, Util as AkairoUtil } from 'discord-akairo'; -import { APIEmbed, APIMessage, OAuth2Scopes, Routes } from 'discord-api-types/v10'; import { Constants as DiscordConstants, EmbedBuilder, GuildMember, Message, + OAuth2Scopes, PermissionFlagsBits, PermissionsBitField, - PermissionsString, + Routes, ThreadMember, User, Util as DiscordUtil, + type APIEmbed, + type APIMessage, type CommandInteraction, type InteractionReplyOptions, + type PermissionsString, type Snowflake, type TextChannel, type UserResolvable @@ -377,7 +379,7 @@ export class BushClientUtil extends ClientUtil { */ public async slashRespond( interaction: CommandInteraction, - responseOptions: BushSlashSendMessageType | BushSlashEditMessageType + responseOptions: SlashSendMessageType | SlashEditMessageType ): Promise { const newResponseOptions = typeof responseOptions === 'string' ? { content: responseOptions } : responseOptions; if (interaction.replied || interaction.deferred) { @@ -696,17 +698,17 @@ export class BushClientUtil extends ClientUtil { * @param user The user to fetch * @returns Undefined if the user is not found, otherwise the user. */ - public async resolveNonCachedUser(user: UserResolvable | undefined | null): Promise { + public async resolveNonCachedUser(user: UserResolvable | undefined | null): Promise { if (user == null) return undefined; const resolvedUser = user instanceof User - ? user + ? user : user instanceof GuildMember - ? user.user + ? user.user : user instanceof ThreadMember - ? user.user + ? user.user : user instanceof Message - ? user.author + ? user.author : undefined; return resolvedUser ?? (await client.users.fetch(user as Snowflake).catch(() => undefined)); @@ -831,9 +833,10 @@ export class BushClientUtil extends ClientUtil { * @returns The missing permissions or null if none are missing. */ public userGuildPermCheck( - message: BushMessage | BushSlashMessage, + message: CommandMessage | SlashMessage, permissions: typeof PermissionFlagsBits[keyof typeof PermissionFlagsBits][] ): PermissionsString[] | null { + if (!message.inGuild()) return null; const missing = message.member?.permissions.missing(permissions) ?? []; return missing.length ? missing : null; @@ -845,7 +848,7 @@ export class BushClientUtil extends ClientUtil { * @param permissions The permissions to check for. * @returns The missing permissions or null if none are missing. */ - public clientGuildPermCheck(message: BushMessage | BushSlashMessage, permissions: bigint[]): PermissionsString[] | null { + public clientGuildPermCheck(message: CommandMessage | SlashMessage, permissions: bigint[]): PermissionsString[] | null { const missing = message.guild?.members.me?.permissions.missing(permissions) ?? []; return missing.length ? missing : null; @@ -860,7 +863,7 @@ export class BushClientUtil extends ClientUtil { * @returns The missing permissions or null if none are missing. */ public clientSendAndPermCheck( - message: BushMessage | BushSlashMessage, + message: CommandMessage | SlashMessage, permissions: bigint[] = [], checkChannel = false ): PermissionsString[] | null { @@ -868,7 +871,7 @@ export class BushClientUtil extends ClientUtil { const sendPerm = message.channel!.isThread() ? 'SendMessages' : 'SendMessagesInThreads'; if (!message.inGuild()) return null; - if (!message.guild.members.me!.permissionsIn(message.channel.id).has(sendPerm)) missing.push(sendPerm); + if (!message.guild.members.me!.permissionsIn(message.channel!.id).has(sendPerm)) missing.push(sendPerm); missing.push( ...(checkChannel @@ -884,7 +887,7 @@ export class BushClientUtil extends ClientUtil { * @param message The message to get the prefix from. * @returns The prefix. */ - public prefix(message: BushMessage | BushSlashMessage): string { + public prefix(message: CommandMessage | SlashMessage): string { return message.util.isSlash ? '/' : client.config.isDevelopment @@ -970,7 +973,7 @@ export class BushClientUtil extends ClientUtil { */ public async castDurationContent( arg: string | ParsedDuration | null, - message: BushMessage | BushSlashMessage + message: CommandMessage | SlashMessage ): Promise { const res = typeof arg === 'string' ? await util.arg.cast('contentWithDuration', message, arg) : arg; @@ -987,7 +990,7 @@ export class BushClientUtil extends ClientUtil { public async cast( type: T, arg: BaseBushArgumentType[T] | string, - message: BushMessage | BushSlashMessage + message: CommandMessage | SlashMessage ) { return typeof arg === 'string' ? await util.arg.cast(type, message, arg) : arg; } diff --git a/src/lib/extensions/discord-akairo/BushCommand.ts b/src/lib/extensions/discord-akairo/BushCommand.ts index c727e98..5fb4e06 100644 --- a/src/lib/extensions/discord-akairo/BushCommand.ts +++ b/src/lib/extensions/discord-akairo/BushCommand.ts @@ -1,33 +1,17 @@ import { type DiscordEmojiInfo, type RoleWithDuration } from '#args'; import { type BushArgumentTypeCaster, - type BushBaseGuildVoiceChannel, - type BushCategoryChannel, type BushClient, type BushCommandHandler, - type BushEmoji, - type BushGuild, - type BushGuildBasedChannel, - type BushGuildChannel, - type BushGuildEmoji, - type BushGuildMember, type BushInhibitor, type BushListener, - type BushMessage, - type BushNewsChannel, - type BushRole, - type BushSlashMessage, - type BushStageChannel, type BushTask, - type BushTextChannel, - type BushThreadChannel, - type BushUser, - type BushVoiceChannel, type ParsedDuration } from '#lib'; import { ArgumentMatch, Command, + CommandUtil, type AkairoApplicationCommandAutocompleteOption, type AkairoApplicationCommandChannelOptionData, type AkairoApplicationCommandChoicesData, @@ -47,51 +31,17 @@ import { type SlashResolveType } from 'discord-akairo'; import { + Message, + User, type ApplicationCommandOptionChoiceData, - type Collection, - type Invite, type PermissionResolvable, type PermissionsString, type Snowflake } from 'discord.js'; import _ from 'lodash'; +import { SlashMessage } from './SlashMessage.js'; export interface OverriddenBaseArgumentType extends BaseArgumentType { - user: BushUser | null; - users: Collection | null; - member: BushGuildMember | null; - members: Collection | null; - relevant: BushUser | BushGuildMember | null; - relevants: Collection | Collection | null; - channel: BushGuildBasedChannel | BushBaseGuildVoiceChannel | null; - channels: Collection | null; - textChannel: BushTextChannel | null; - textChannels: Collection | null; - voiceChannel: BushVoiceChannel | null; - voiceChannels: Collection | null; - categoryChannel: BushCategoryChannel | null; - categoryChannels: Collection | null; - newsChannel: BushNewsChannel | null; - newsChannels: Collection | null; - stageChannel: BushStageChannel | null; - stageChannels: Collection | null; - threadChannel: BushThreadChannel | null; - threadChannels: Collection | null; - role: BushRole | null; - roles: Collection | null; - emoji: BushEmoji | null; - emojis: Collection | null; - guild: BushGuild | null; - guilds: Collection | null; - message: BushMessage | null; - guildMessage: BushMessage | null; - relevantMessage: BushMessage | null; - invite: Invite | null; - userMention: BushUser | null; - memberMention: BushGuildMember | null; - channelMention: BushThreadChannel | BushGuildChannel | null; - roleMention: BushRole | null; - emojiMention: BushGuildEmoji | null; commandAlias: BushCommand | null; command: BushCommand | null; inhibitor: BushInhibitor | null; @@ -108,9 +58,10 @@ export interface BaseBushArgumentType extends OverriddenBaseArgumentType { discordEmoji: DiscordEmojiInfo | null; roleWithDuration: RoleWithDuration | null; abbreviatedNumber: number | null; - globalUser: BushUser | null; - messageLink: BushMessage | null; + globalUser: User | null; + messageLink: Message | null; durationSeconds: number | null; + tinyColor: string | null; } export type BushArgumentType = keyof BaseBushArgumentType | RegExp; @@ -247,6 +198,7 @@ export interface BushArgumentOptions extends BaseBushArgumentOptions { */ type?: BushArgumentType | (keyof BaseBushArgumentType)[] | BushArgumentTypeCaster; } + export interface CustomBushArgumentOptions extends BaseBushArgumentOptions { /** * An array of strings can be used to restrict input to only those strings, case insensitive. @@ -259,7 +211,7 @@ export interface CustomBushArgumentOptions extends BaseBushArgumentOptions { customType?: (string | string[])[] | RegExp | string | null; } -export type BushMissingPermissionSupplier = (message: BushMessage | BushSlashMessage) => Promise | any; +export type BushMissingPermissionSupplier = (message: CommandMessage | SlashMessage) => Promise | any; interface ExtendedCommandOptions { /** @@ -407,9 +359,7 @@ export interface ArgsInfo { export class BushCommand extends Command { public declare client: BushClient; - public declare handler: BushCommandHandler; - public declare description: string; /** @@ -595,13 +545,13 @@ export interface BushCommand extends Command { * @param message - Message that triggered the command. * @param args - Evaluated arguments. */ - exec(message: BushMessage, args: any): any; + exec(message: CommandMessage, args: any): any; /** * Executes the command. * @param message - Message that triggered the command. * @param args - Evaluated arguments. */ - exec(message: BushMessage | BushSlashMessage, args: any): any; + exec(message: CommandMessage | SlashMessage, args: any): any; } type SlashOptionKeys = @@ -615,7 +565,22 @@ type SlashOptionKeys = interface PseudoArguments extends BaseBushArgumentType { boolean: boolean; + flag: boolean; + regex: { match: RegExpMatchArray; matches: RegExpExecArray[] }; } export type ArgType = NonNullable; export type OptArgType = PseudoArguments[T]; + +/** + * `util` is always defined for messages after `'all'` inhibitors + */ +export type CommandMessage = Message & { + /** + * Extra properties applied to the Discord.js message object. + * Utilities for command responding. + * Available on all messages after 'all' inhibitors and built-in inhibitors (bot, client). + * Not all properties of the util are available, depending on the input. + * */ + util: CommandUtil; +}; diff --git a/src/lib/extensions/discord-akairo/BushCommandHandler.ts b/src/lib/extensions/discord-akairo/BushCommandHandler.ts index 2c1903f..f095356 100644 --- a/src/lib/extensions/discord-akairo/BushCommandHandler.ts +++ b/src/lib/extensions/discord-akairo/BushCommandHandler.ts @@ -1,35 +1,30 @@ -import { type BushClient, type BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { type BushClient, type BushCommand, type CommandMessage, type SlashMessage } from '#lib'; import { CommandHandler, type Category, type CommandHandlerEvents, type CommandHandlerOptions } from 'discord-akairo'; -import { type Collection, type PermissionsString } from 'discord.js'; +import { type Collection, type Message, type PermissionsString } from 'discord.js'; export type BushCommandHandlerOptions = CommandHandlerOptions; export interface BushCommandHandlerEvents extends CommandHandlerEvents { - commandBlocked: [message: BushMessage, command: BushCommand, reason: string]; - commandBreakout: [message: BushMessage, command: BushCommand, breakMessage: BushMessage]; - commandCancelled: [message: BushMessage, command: BushCommand, retryMessage?: BushMessage]; - commandFinished: [message: BushMessage, command: BushCommand, args: any, returnValue: any]; - commandInvalid: [message: BushMessage, command: BushCommand]; - commandLocked: [message: BushMessage, command: BushCommand]; - commandStarted: [message: BushMessage, command: BushCommand, args: any]; - cooldown: [message: BushMessage | BushSlashMessage, command: BushCommand, remaining: number]; - error: [error: Error, message: BushMessage, command?: BushCommand]; - inPrompt: [message: BushMessage]; + commandBlocked: [message: CommandMessage, command: BushCommand, reason: string]; + commandBreakout: [message: CommandMessage, command: BushCommand, /* no util */ breakMessage: Message]; + commandCancelled: [message: CommandMessage, command: BushCommand, /* no util */ retryMessage?: Message]; + commandFinished: [message: CommandMessage, command: BushCommand, args: any, returnValue: any]; + commandInvalid: [message: CommandMessage, command: BushCommand]; + commandLocked: [message: CommandMessage, command: BushCommand]; + commandStarted: [message: CommandMessage, command: BushCommand, args: any]; + cooldown: [message: CommandMessage | SlashMessage, command: BushCommand, remaining: number]; + error: [error: Error, message: /* no util */ Message, command?: BushCommand]; + inPrompt: [message: /* no util */ Message]; load: [command: BushCommand, isReload: boolean]; - messageBlocked: [message: BushMessage | BushSlashMessage, reason: string]; - messageInvalid: [message: BushMessage]; - missingPermissions: [message: BushMessage, command: BushCommand, type: 'client' | 'user', missing: PermissionsString[]]; + messageBlocked: [message: /* no util */ Message | CommandMessage | SlashMessage, reason: string]; + messageInvalid: [message: CommandMessage]; + missingPermissions: [message: CommandMessage, command: BushCommand, type: 'client' | 'user', missing: PermissionsString[]]; remove: [command: BushCommand]; - slashBlocked: [message: BushSlashMessage, command: BushCommand, reason: string]; - slashError: [error: Error, message: BushSlashMessage, command: BushCommand]; - slashFinished: [message: BushSlashMessage, command: BushCommand, args: any, returnValue: any]; - slashMissingPermissions: [ - message: BushSlashMessage, - command: BushCommand, - type: 'client' | 'user', - missing: PermissionsString[] - ]; - slashStarted: [message: BushSlashMessage, command: BushCommand, args: any]; + slashBlocked: [message: SlashMessage, command: BushCommand, reason: string]; + slashError: [error: Error, message: SlashMessage, command: BushCommand]; + slashFinished: [message: SlashMessage, command: BushCommand, args: any, returnValue: any]; + slashMissingPermissions: [message: SlashMessage, command: BushCommand, type: 'client' | 'user', missing: PermissionsString[]]; + slashStarted: [message: SlashMessage, command: BushCommand, args: any]; } export class BushCommandHandler extends CommandHandler { diff --git a/src/lib/extensions/discord-akairo/BushCommandUtil.ts b/src/lib/extensions/discord-akairo/BushCommandUtil.ts deleted file mode 100644 index 7a06b35..0000000 --- a/src/lib/extensions/discord-akairo/BushCommandUtil.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { type BushCommand, type BushCommandHandler, type BushMessage, type BushSlashMessage } from '#lib'; -import { CommandUtil, type ParsedComponentData } from 'discord-akairo'; -import { type Collection, type Snowflake } from 'discord.js'; - -export interface BushParsedComponentData extends ParsedComponentData { - command?: BushCommand; -} - -export class BushCommandUtil extends CommandUtil { - public declare parsed: BushParsedComponentData | null; - public declare handler: BushCommandHandler; - public declare message: BushMessageType; - public declare messages: Collection | null; - - public constructor(handler: BushCommandHandler, message: BushMessageType) { - super(handler, message); - } -} - -export interface BushCommandUtil extends CommandUtil { - isSlashMessage(message: BushMessage | BushSlashMessage): message is BushSlashMessage; -} diff --git a/src/lib/extensions/discord-akairo/BushInhibitor.ts b/src/lib/extensions/discord-akairo/BushInhibitor.ts index 7f13594..12b2baf 100644 --- a/src/lib/extensions/discord-akairo/BushInhibitor.ts +++ b/src/lib/extensions/discord-akairo/BushInhibitor.ts @@ -1,4 +1,4 @@ -import { type BushClient, type BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { type BushClient, type BushCommand, type CommandMessage, type SlashMessage } from '#lib'; import { Inhibitor } from 'discord-akairo'; export class BushInhibitor extends Inhibitor { @@ -10,9 +10,12 @@ export interface BushInhibitor { * Checks if message should be blocked. * A return value of true will block the message. * If returning a Promise, a resolved value of true will block the message. + * + * **Note:** `all` type inhibitors do not have `message.util` defined. + * * @param message - Message being handled. * @param command - Command to check. */ - exec(message: BushMessage, command: BushCommand): any; - exec(message: BushMessage | BushSlashMessage, command: BushCommand): any; + exec(message: CommandMessage, command: BushCommand): any; + exec(message: CommandMessage | SlashMessage, command: BushCommand): any; } diff --git a/src/lib/extensions/discord-akairo/BushInhibitorHandler.ts b/src/lib/extensions/discord-akairo/BushInhibitorHandler.ts index a607bf7..5e4fb6c 100644 --- a/src/lib/extensions/discord-akairo/BushInhibitorHandler.ts +++ b/src/lib/extensions/discord-akairo/BushInhibitorHandler.ts @@ -1,6 +1,3 @@ -import { type BushClient } from '#lib'; import { InhibitorHandler } from 'discord-akairo'; -export class BushInhibitorHandler extends InhibitorHandler { - public declare client: BushClient; -} +export class BushInhibitorHandler extends InhibitorHandler {} diff --git a/src/lib/extensions/discord-akairo/BushListener.ts b/src/lib/extensions/discord-akairo/BushListener.ts index f6247ec..3efe527 100644 --- a/src/lib/extensions/discord-akairo/BushListener.ts +++ b/src/lib/extensions/discord-akairo/BushListener.ts @@ -1,10 +1,7 @@ -import { type BushClient } from '#lib'; import { Listener } from 'discord-akairo'; import type EventEmitter from 'events'; export class BushListener extends Listener { - public declare client: BushClient; - public constructor( id: string, options: { diff --git a/src/lib/extensions/discord-akairo/BushListenerHandler.ts b/src/lib/extensions/discord-akairo/BushListenerHandler.ts index 517fb55..9c3e4af 100644 --- a/src/lib/extensions/discord-akairo/BushListenerHandler.ts +++ b/src/lib/extensions/discord-akairo/BushListenerHandler.ts @@ -1,6 +1,3 @@ -import { type BushClient } from '#lib'; import { ListenerHandler } from 'discord-akairo'; -export class BushListenerHandler extends ListenerHandler { - public declare client: BushClient; -} +export class BushListenerHandler extends ListenerHandler {} diff --git a/src/lib/extensions/discord-akairo/BushSlashMessage.ts b/src/lib/extensions/discord-akairo/BushSlashMessage.ts deleted file mode 100644 index 0860964..0000000 --- a/src/lib/extensions/discord-akairo/BushSlashMessage.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { - BushCommandHandler, - BushGuildTextBasedChannel, - type BushClient, - type BushCommandUtil, - type BushGuild, - type BushGuildMember, - type BushTextBasedChannel, - type BushUser -} from '#lib'; -import { AkairoMessage } from 'discord-akairo'; -import { type ChatInputCommandInteraction } from 'discord.js'; - -export class BushSlashMessage extends AkairoMessage { - public declare client: BushClient; - public declare util: BushCommandUtil & { handler: BushCommandHandler }; - public declare author: BushUser; - public declare member: BushGuildMember | null; - public declare interaction: ChatInputCommandInteraction; - public constructor(client: BushClient, interaction: ChatInputCommandInteraction) { - super(client, interaction); - } -} - -export interface BushSlashMessage extends AkairoMessage { - get channel(): BushTextBasedChannel | null; - get guild(): BushGuild | null; - inGuild(): this is BushSlashMessageInGuild & this; -} - -interface BushSlashMessageInGuild { - guild: BushGuild; - channel: BushGuildTextBasedChannel; -} diff --git a/src/lib/extensions/discord-akairo/BushTask.ts b/src/lib/extensions/discord-akairo/BushTask.ts index b4359ce..9f5c0cd 100644 --- a/src/lib/extensions/discord-akairo/BushTask.ts +++ b/src/lib/extensions/discord-akairo/BushTask.ts @@ -1,10 +1,3 @@ -import { type BushClient } from '#lib'; -import { Task, type TaskOptions } from 'discord-akairo'; +import { Task } from 'discord-akairo'; -export class BushTask extends Task { - public declare client: BushClient; - - public constructor(id: string, options: TaskOptions) { - super(id, options); - } -} +export class BushTask extends Task {} diff --git a/src/lib/extensions/discord-akairo/BushTaskHandler.ts b/src/lib/extensions/discord-akairo/BushTaskHandler.ts index ed6b91d..f667ead 100644 --- a/src/lib/extensions/discord-akairo/BushTaskHandler.ts +++ b/src/lib/extensions/discord-akairo/BushTaskHandler.ts @@ -1,12 +1,5 @@ -import { type BushClient } from '#lib'; import { TaskHandler, type AkairoHandlerOptions } from 'discord-akairo'; export type BushTaskHandlerOptions = AkairoHandlerOptions; -export class BushTaskHandler extends TaskHandler { - public declare client: BushClient; - - public constructor(client: BushClient, options: BushTaskHandlerOptions) { - super(client, options); - } -} +export class BushTaskHandler extends TaskHandler {} diff --git a/src/lib/extensions/discord-akairo/SlashMessage.ts b/src/lib/extensions/discord-akairo/SlashMessage.ts new file mode 100644 index 0000000..0a6669b --- /dev/null +++ b/src/lib/extensions/discord-akairo/SlashMessage.ts @@ -0,0 +1,3 @@ +import { AkairoMessage } from 'discord-akairo'; + +export class SlashMessage extends AkairoMessage {} diff --git a/src/lib/extensions/discord.js/BushActivity.ts b/src/lib/extensions/discord.js/BushActivity.ts deleted file mode 100644 index 3f19232..0000000 --- a/src/lib/extensions/discord.js/BushActivity.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { BushEmoji, BushPresence } from '#lib'; -import { Activity } from 'discord.js'; -import type { RawActivityData } from 'discord.js/typings/rawDataTypes'; - -/** - * Represents an activity that is part of a user's presence. - */ -export class BushActivity extends Activity { - public declare emoji: BushEmoji | null; - - public constructor(presence: BushPresence, data?: RawActivityData) { - super(presence, data); - } -} diff --git a/src/lib/extensions/discord.js/BushApplicationCommand.ts b/src/lib/extensions/discord.js/BushApplicationCommand.ts deleted file mode 100644 index 8298830..0000000 --- a/src/lib/extensions/discord.js/BushApplicationCommand.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* eslint-disable @typescript-eslint/ban-types */ -import type { BushClient, BushGuild } from '#lib'; -import { ApplicationCommand, type Snowflake } from 'discord.js'; -import type { RawApplicationCommandData } from 'discord.js/typings/rawDataTypes'; - -/** - * Represents an application command. - */ -export class BushApplicationCommand extends ApplicationCommand { - public declare readonly client: BushClient; - public declare guild: BushGuild | null; - - public constructor(client: BushClient, data: RawApplicationCommandData, guild?: BushGuild, guildID?: Snowflake) { - super(client, data, guild, guildID); - } -} diff --git a/src/lib/extensions/discord.js/BushApplicationCommandManager.ts b/src/lib/extensions/discord.js/BushApplicationCommandManager.ts deleted file mode 100644 index dc27dbf..0000000 --- a/src/lib/extensions/discord.js/BushApplicationCommandManager.ts +++ /dev/null @@ -1,151 +0,0 @@ -import type { - BushApplicationCommand, - BushApplicationCommandPermissionsManager, - BushApplicationCommandResolvable, - BushClient, - BushGuildResolvable, - StripPrivate -} from '#lib'; -import type { APIApplicationCommand } from 'discord-api-types/v10'; -import { - ApplicationCommandManager, - CachedManager, - type ApplicationCommandData, - type Collection, - type FetchApplicationCommandOptions, - type Snowflake -} from 'discord.js'; - -/** - * Manages API methods for application commands and stores their cache. - */ -export declare class BushApplicationCommandManager< - ApplicationCommandScope = BushApplicationCommand<{ guild: BushGuildResolvable }>, - PermissionsOptionsExtras = { guild: BushGuildResolvable }, - PermissionsGuildType = null - > - extends CachedManager - implements StripPrivate> -{ - public constructor(client: BushClient, iterable?: Iterable); - - /** - * The manager for permissions of arbitrary commands on arbitrary guilds - */ - public permissions: BushApplicationCommandPermissionsManager< - { command?: BushApplicationCommandResolvable } & PermissionsOptionsExtras, - { command: BushApplicationCommandResolvable } & PermissionsOptionsExtras, - PermissionsOptionsExtras, - PermissionsGuildType, - null - >; - - /** - * The APIRouter path to the commands - * @param id The application command's id - * @param guildId The guild's id to use in the path, - * ignored when using a {@link GuildApplicationCommandManager} - */ - private commandPath({ id, guildId }: { id?: Snowflake; guildId?: Snowflake }): unknown; - - /** - * Creates an application command. - * @param command The command - * @param guildId The guild's id to create this command in, ignored when using a {@link GuildApplicationCommandManager} - * @example - * // Create a new command - * client.application.commands.create({ - * name: 'test', - * description: 'A test command', - * }) - * .then(console.log) - * .catch(console.error); - */ - public create(command: BushApplicationCommandResolvable, guildId?: Snowflake): Promise; - - /** - * Deletes an application command. - * @param command The command to delete - * @param guildId The guild's id where the command is registered, - * ignored when using a {@link GuildApplicationCommandManager} - * @example - * // Delete a command - * guild.commands.delete('123456789012345678') - * .then(console.log) - * .catch(console.error); - */ - public delete(command: BushApplicationCommandResolvable, guildId?: Snowflake): Promise; - - /** - * Edits an application command. - * @param command The command to edit - * @param data The data to update the command with - * @param guildId The guild's id where the command registered, - * ignored when using a {@link GuildApplicationCommandManager} - * @example - * // Edit an existing command - * client.application.commands.edit('123456789012345678', { - * description: 'New description', - * }) - * .then(console.log) - * .catch(console.error); - */ - public edit(command: BushApplicationCommandResolvable, data: ApplicationCommandData): Promise; - public edit( - command: BushApplicationCommandResolvable, - data: ApplicationCommandData, - guildId: Snowflake - ): Promise; - - /** - * Obtains one or multiple application commands from Discord, or the cache if it's already available. - * @param id The application command's id - * @param options Additional options for this fetch - * @example - * // Fetch a single command - * client.application.commands.fetch('123456789012345678') - * .then(command => console.log(`Fetched command ${command.name}`)) - * .catch(console.error); - * @example - * // Fetch all commands - * guild.commands.fetch() - * .then(commands => console.log(`Fetched ${commands.size} commands`)) - * .catch(console.error); - */ - public fetch(id: Snowflake, options: FetchApplicationCommandOptions & { guildId: Snowflake }): Promise; - public fetch(options: FetchApplicationCommandOptions): Promise>; - public fetch(id: Snowflake, options?: FetchApplicationCommandOptions): Promise; - public fetch(id?: Snowflake, options?: FetchApplicationCommandOptions): Promise>; - - /** - * Sets all the commands for this application or guild. - * @param commands The commands - * @param guildId The guild's id to create the commands in, - * ignored when using a {@link GuildApplicationCommandManager} - * @example - * // Set all commands to just this one - * client.application.commands.set([ - * { - * name: 'test', - * description: 'A test command', - * }, - * ]) - * .then(console.log) - * .catch(console.error); - * @example - * // Remove all commands - * guild.commands.set([]) - * .then(console.log) - * .catch(console.error); - */ - public set(commands: ApplicationCommandData[]): Promise>; - public set(commands: ApplicationCommandData[], guildId: Snowflake): Promise>; - - /** - * Transforms an {@link ApplicationCommandData} object into something that can be used with the API. - * @param command The command to transform - */ - private static transformCommand( - command: ApplicationCommandData - ): Omit; -} diff --git a/src/lib/extensions/discord.js/BushApplicationCommandPermissionsManager.ts b/src/lib/extensions/discord.js/BushApplicationCommandPermissionsManager.ts deleted file mode 100644 index 401f3e2..0000000 --- a/src/lib/extensions/discord.js/BushApplicationCommandPermissionsManager.ts +++ /dev/null @@ -1,184 +0,0 @@ -import type { BushClient, BushRoleResolvable, BushUserResolvable } from '#lib'; -import type { APIApplicationCommandPermission } from 'discord-api-types/v10'; -import { - ApplicationCommandPermissionType, - BaseManager, - type ApplicationCommand, - type ApplicationCommandManager, - type ApplicationCommandPermissionData, - type ApplicationCommandPermissions, - type Collection, - type GuildApplicationCommandManager, - type GuildApplicationCommandPermissionData, - type Snowflake -} from 'discord.js'; - -/** - * Manages API methods for permissions of Application Commands. - */ -export declare class BushApplicationCommandPermissionsManager< - BaseOptions, - FetchSingleOptions, - FullPermissionsOptions, - GuildType, - CommandIdType -> extends BaseManager { - public constructor(manager: ApplicationCommandManager | GuildApplicationCommandManager | ApplicationCommand); - - /** - * The manager or command that this manager belongs to - */ - private manager: ApplicationCommandManager | GuildApplicationCommandManager | ApplicationCommand; - - /** - * The client that instantiated this Manager - */ - public client: BushClient; - - /** - * The id of the command this manager acts on - */ - public commandId: CommandIdType; - - /** - * The guild that this manager acts on - */ - public guild: GuildType; - - /** - * The id of the guild that this manager acts on - */ - public guildId: Snowflake | null; - - /** - * Add permissions to a command. - * @param options Options used to add permissions - * @example - * // Block a role from the command permissions - * guild.commands.permissions.add({ command: '123456789012345678', permissions: [ - * { - * id: '876543211234567890', - * type: 'ROLE', - * permission: false - * }, - * ]}) - * .then(console.log) - * .catch(console.error); - */ - public add( - options: FetchSingleOptions & { permissions: ApplicationCommandPermissionData[] } - ): Promise; - - /** - * Check whether a permission exists for a user or role - * @param options Options used to check permissions - * @example - * // Check whether a user has permission to use a command - * guild.commands.permissions.has({ command: '123456789012345678', permissionId: '876543210123456789' }) - * .then(console.log) - * .catch(console.error); - */ - public has(options: FetchSingleOptions & { permissionId: BushUserResolvable | BushRoleResolvable }): Promise; - - /** - * Fetches the permissions for one or multiple commands. - * @param options Options used to fetch permissions - * @example - * // Fetch permissions for one command - * guild.commands.permissions.fetch({ command: '123456789012345678' }) - * .then(perms => console.log(`Fetched permissions for ${perms.length} users`)) - * .catch(console.error); - * @example - * // Fetch permissions for all commands in a guild - * client.application.commands.permissions.fetch({ guild: '123456789012345678' }) - * .then(perms => console.log(`Fetched permissions for ${perms.size} commands`)) - * .catch(console.error); - */ - public fetch(options: FetchSingleOptions): Promise; - public fetch(options: BaseOptions): Promise>; - - /** - * Remove permissions from a command. - * @param options Options used to remove permissions - * @example - * // Remove a user permission from this command - * guild.commands.permissions.remove({ command: '123456789012345678', users: '876543210123456789' }) - * .then(console.log) - * .catch(console.error); - * @example - * // Remove multiple roles from this command - * guild.commands.permissions.remove({ - * command: '123456789012345678', roles: ['876543210123456789', '765432101234567890'] - * }) - * .then(console.log) - * .catch(console.error); - */ - public remove( - options: - | (FetchSingleOptions & { - users: BushUserResolvable | BushUserResolvable[]; - roles?: BushRoleResolvable | BushRoleResolvable[]; - }) - | (FetchSingleOptions & { - users?: BushUserResolvable | BushUserResolvable[]; - roles: BushRoleResolvable | BushRoleResolvable[]; - }) - ): Promise; - - /** - * Sets the permissions for one or more commands. - * @param options Options used to set permissions - * @example - * // Set the permissions for one command - * client.application.commands.permissions.set({ guild: '892455839386304532', command: '123456789012345678', - * permissions: [ - * { - * id: '876543210987654321', - * type: 'USER', - * permission: false, - * }, - * ]}) - * .then(console.log) - * .catch(console.error); - * @example - * // Set the permissions for all commands - * guild.commands.permissions.set({ fullPermissions: [ - * { - * id: '123456789012345678', - * permissions: [{ - * id: '876543210987654321', - * type: 'USER', - * permission: false, - * }], - * }, - * ]}) - * .then(console.log) - * .catch(console.error); - */ - public set( - options: FetchSingleOptions & { permissions: ApplicationCommandPermissionData[] } - ): Promise; - public set( - options: FullPermissionsOptions & { - fullPermissions: GuildApplicationCommandPermissionData[]; - } - ): Promise>; - - /** - * The APIRouter path to the commands - * @param guildId The guild's id to use in the path, - * @param commandId The application command's id - */ - private permissionsPath(guildId: Snowflake, commandId?: Snowflake): unknown; - - /** - * Transforms an {@link ApplicationCommandPermissionData} object into something that can be used with the API. - * @param permissions The permissions to transform - * @param received Whether these permissions have been received from Discord - */ - private static transformPermissions( - permissions: ApplicationCommandPermissionData, - received: true - ): Omit & { type: keyof ApplicationCommandPermissionType }; - private static transformPermissions(permissions: ApplicationCommandPermissionData): APIApplicationCommandPermission; -} diff --git a/src/lib/extensions/discord.js/BushBaseGuildEmojiManager.ts b/src/lib/extensions/discord.js/BushBaseGuildEmojiManager.ts deleted file mode 100644 index 66abbc2..0000000 --- a/src/lib/extensions/discord.js/BushBaseGuildEmojiManager.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { BushClient, BushEmojiIdentifierResolvable, BushEmojiResolvable, BushGuildEmoji } from '#lib'; -import { BaseGuildEmojiManager, CachedManager, type Snowflake } from 'discord.js'; -import { type RawGuildEmojiData } from 'discord.js/typings/rawDataTypes'; - -/** - * Holds methods to resolve GuildEmojis and stores their cache. - */ -export declare class BushBaseGuildEmojiManager - extends CachedManager - implements BaseGuildEmojiManager -{ - public constructor(client: BushClient, iterable?: Iterable); - - /** - * Resolves an EmojiResolvable to an emoji identifier. - * @param emoji The emoji resolvable to resolve - */ - public resolveIdentifier(emoji: BushEmojiIdentifierResolvable): string | null; -} diff --git a/src/lib/extensions/discord.js/BushBaseGuildTextChannel.ts b/src/lib/extensions/discord.js/BushBaseGuildTextChannel.ts deleted file mode 100644 index 5cc74c4..0000000 --- a/src/lib/extensions/discord.js/BushBaseGuildTextChannel.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { BushCategoryChannel, BushClient, BushGuild, BushGuildMember, BushMessageManager, BushThreadManager } from '#lib'; -import { - BaseGuildTextChannel, - type AllowedThreadTypeForNewsChannel, - type AllowedThreadTypeForTextChannel, - type Collection, - type Snowflake -} from 'discord.js'; -import type { RawGuildChannelData } from 'discord.js/typings/rawDataTypes'; - -/** - * Represents a text-based guild channel on Discord. - */ -export class BushBaseGuildTextChannel extends BaseGuildTextChannel { - public declare messages: BushMessageManager; - public declare threads: BushThreadManager; - public declare readonly client: BushClient; - public declare guild: BushGuild; - - public constructor(guild: BushGuild, data?: RawGuildChannelData, client?: BushClient, immediatePatch?: boolean) { - super(guild, data, client, immediatePatch); - } -} - -export interface BushBaseGuildTextChannel extends BaseGuildTextChannel { - get members(): Collection; - get parent(): BushCategoryChannel | null; -} diff --git a/src/lib/extensions/discord.js/BushBaseGuildVoiceChannel.ts b/src/lib/extensions/discord.js/BushBaseGuildVoiceChannel.ts deleted file mode 100644 index ba41cfe..0000000 --- a/src/lib/extensions/discord.js/BushBaseGuildVoiceChannel.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { BaseGuildVoiceChannel, Collection, Snowflake } from 'discord.js'; -import { BushCategoryChannel } from './BushCategoryChannel.js'; -import { BushGuild } from './BushGuild.js'; -import { BushGuildMember } from './BushGuildMember.js'; - -/** - * Represents a voice-based guild channel on Discord. - */ -export declare class BushBaseGuildVoiceChannel extends BaseGuildVoiceChannel { - public get members(): Collection; - public guild: BushGuild; - public get parent(): BushCategoryChannel | null; -} diff --git a/src/lib/extensions/discord.js/BushButtonInteraction.ts b/src/lib/extensions/discord.js/BushButtonInteraction.ts deleted file mode 100644 index 368d19d..0000000 --- a/src/lib/extensions/discord.js/BushButtonInteraction.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { BushClient, BushGuild, BushGuildMember, BushGuildTextBasedChannel, BushTextBasedChannel, BushUser } from '#lib'; -import type { APIInteractionGuildMember } from 'discord-api-types/v10'; -import { ButtonInteraction, type CacheType, type CacheTypeReducer } from 'discord.js'; -import type { RawMessageButtonInteractionData } from 'discord.js/typings/rawDataTypes'; - -/** - * Represents a button interaction. - */ -export class BushButtonInteraction extends ButtonInteraction { - public declare member: CacheTypeReducer; - public declare user: BushUser; - - public constructor(client: BushClient, data: RawMessageButtonInteractionData) { - super(client, data); - } -} - -export interface BushButtonInteraction extends ButtonInteraction { - get channel(): CacheTypeReducer< - Cached, - BushGuildTextBasedChannel | null, - BushGuildTextBasedChannel | null, - BushGuildTextBasedChannel | null, - BushTextBasedChannel | null - >; - get guild(): CacheTypeReducer; - inGuild(): this is BushButtonInteraction<'raw' | 'cached'>; - inCachedGuild(): this is BushButtonInteraction<'cached'>; - inRawGuild(): this is BushButtonInteraction<'raw'>; -} diff --git a/src/lib/extensions/discord.js/BushCategoryChannel.ts b/src/lib/extensions/discord.js/BushCategoryChannel.ts deleted file mode 100644 index a2e1e1c..0000000 --- a/src/lib/extensions/discord.js/BushCategoryChannel.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { - BushDMChannel, - BushGuildBasedChannel, - BushNewsChannel, - BushStageChannel, - BushTextBasedChannel, - BushTextChannel, - BushThreadChannel, - BushVoiceBasedChannel, - BushVoiceChannel, - type BushCategoryChannelChildManager, - type BushClient, - type BushGuild -} from '#lib'; -import { CategoryChannel } from 'discord.js'; -import { type RawGuildChannelData } from 'discord.js/typings/rawDataTypes'; - -/** - * Represents a guild category channel on Discord. - */ -export class BushCategoryChannel extends CategoryChannel { - public declare readonly client: BushClient; - public declare guild: BushGuild; - - public constructor(guild: BushGuild, data?: RawGuildChannelData, client?: BushClient, immediatePatch?: boolean) { - super(guild, data, client, immediatePatch); - } -} - -export interface BushCategoryChannel extends CategoryChannel { - get children(): BushCategoryChannelChildManager; - isText(): this is BushTextChannel; - isDM(): this is BushDMChannel; - isVoice(): this is BushVoiceChannel; - isCategory(): this is BushCategoryChannel; - isNews(): this is BushNewsChannel; - isThread(): this is BushThreadChannel; - isStage(): this is BushStageChannel; - isTextBased(): this is BushGuildBasedChannel & BushTextBasedChannel; - isVoiceBased(): this is BushVoiceBasedChannel; -} diff --git a/src/lib/extensions/discord.js/BushCategoryChannelChildManager.ts b/src/lib/extensions/discord.js/BushCategoryChannelChildManager.ts deleted file mode 100644 index 2b0d56b..0000000 --- a/src/lib/extensions/discord.js/BushCategoryChannelChildManager.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { - BushCategoryChannel, - BushGuild, - BushGuildChannelResolvable, - BushMappedChannelCategoryTypes, - BushNonCategoryGuildBasedChannel, - BushTextChannel -} from '#lib'; -import type { - CategoryChannelChildManager, - CategoryChannelType, - CategoryCreateChannelOptions, - DataManager, - Snowflake -} from 'discord.js'; - -export declare class BushCategoryChannelChildManager - extends DataManager - implements CategoryChannelChildManager -{ - private constructor(channel: BushCategoryChannel); - - /** - * The category channel this manager belongs to - */ - public channel: BushCategoryChannel; - - /** - * The guild this manager belongs to - */ - public readonly guild: BushGuild; - - /** - * Creates a new channel within this category. - * You cannot create a channel of type {@link ChannelType.GuildCategory} inside a CategoryChannel. - * @param name The name of the new channel - * @param options Options for creating the new channel - */ - public create( - name: string, - options: CategoryCreateChannelOptions & { type: T } - ): Promise; - public create(name: string, options?: CategoryCreateChannelOptions): Promise; -} diff --git a/src/lib/extensions/discord.js/BushChannel.ts b/src/lib/extensions/discord.js/BushChannel.ts deleted file mode 100644 index e66135c..0000000 --- a/src/lib/extensions/discord.js/BushChannel.ts +++ /dev/null @@ -1,39 +0,0 @@ -import type { - BushCategoryChannel, - BushClient, - BushDMChannel, - BushNewsChannel, - BushStageChannel, - BushTextBasedChannel, - BushTextChannel, - BushThreadChannel, - BushVoiceBasedChannel, - BushVoiceChannel -} from '#lib'; -import { Channel, ChannelType, PartialGroupDMChannel, type Snowflake } from 'discord.js'; -import type { RawChannelData } from 'discord.js/typings/rawDataTypes'; - -/** - * Represents any channel on Discord. - */ -export declare class BushChannel extends Channel { - public constructor(client: BushClient, data?: RawChannelData, immediatePatch?: boolean); - public get createdAt(): Date | null; - public get createdTimestamp(): number | null; - public deleted: boolean; - public id: Snowflake; - public get partial(): false; - public type: ChannelType; - public delete(): Promise; - public fetch(force?: boolean): Promise; - public isText(): this is BushTextChannel; - public isDM(): this is BushDMChannel; - public isDMBased(): this is PartialGroupDMChannel | BushDMChannel; - public isVoice(): this is BushVoiceChannel; - public isCategory(): this is BushCategoryChannel; - public isNews(): this is BushNewsChannel; - public isThread(): this is BushThreadChannel; - public isStage(): this is BushStageChannel; - public isTextBased(): this is BushTextBasedChannel; - public isVoiceBased(): this is BushVoiceBasedChannel; -} diff --git a/src/lib/extensions/discord.js/BushChannelManager.ts b/src/lib/extensions/discord.js/BushChannelManager.ts deleted file mode 100644 index ff93209..0000000 --- a/src/lib/extensions/discord.js/BushChannelManager.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { BushAnyChannel, BushChannelResolvable } from '#lib'; -import { CachedManager, ChannelManager, type Client, type FetchChannelOptions, type Snowflake } from 'discord.js'; -import type { RawChannelData } from 'discord.js/typings/rawDataTypes'; - -/** - * A manager of channels belonging to a client - */ -export declare class BushChannelManager - extends CachedManager - implements ChannelManager -{ - public constructor(client: Client, iterable: Iterable); - - /** - * Obtains a channel from Discord, or the channel cache if it's already available. - * @param id The channel's id - * @param options Additional options for this fetch - * @example - * // Fetch a channel by its id - * client.channels.fetch('222109930545610754') - * .then(channel => console.log(channel.name)) - * .catch(console.error); - */ - public fetch(id: Snowflake, options?: FetchChannelOptions): Promise; -} diff --git a/src/lib/extensions/discord.js/BushChatInputCommandInteraction.ts b/src/lib/extensions/discord.js/BushChatInputCommandInteraction.ts deleted file mode 100644 index 2491a68..0000000 --- a/src/lib/extensions/discord.js/BushChatInputCommandInteraction.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { - BushApplicationCommand, - BushClient, - BushGuild, - BushGuildEmoji, - BushGuildMember, - BushGuildTextBasedChannel, - BushNonThreadGuildBasedChannel, - BushRole, - BushTextBasedChannel, - BushUser -} from '#lib'; -import type { APIInteractionGuildMember } from 'discord-api-types/v10'; -import { ChatInputCommandInteraction, type CacheType, type CacheTypeReducer, type Invite, type Snowflake } from 'discord.js'; -import type { RawCommandInteractionData } from 'discord.js/typings/rawDataTypes'; - -export type BushGuildResolvable = - | BushGuild - | BushNonThreadGuildBasedChannel - | BushGuildMember - | BushGuildEmoji - | Invite - | BushRole - | Snowflake; - -/** - * Represents a command interaction. - */ -export class BushChatInputCommandInteraction extends ChatInputCommandInteraction { - public declare readonly client: BushClient; - public declare member: CacheTypeReducer; - public declare user: BushUser; - - public constructor(client: BushClient, data: RawCommandInteractionData) { - super(client, data); - } -} - -export interface BushChatInputCommandInteraction { - get command(): BushApplicationCommand | BushApplicationCommand<{ guild: BushGuildResolvable }> | null; - get channel(): CacheTypeReducer< - Cached, - BushGuildTextBasedChannel | null, - BushGuildTextBasedChannel | null, - BushGuildTextBasedChannel | null, - BushTextBasedChannel | null - >; - get guild(): CacheTypeReducer; -} diff --git a/src/lib/extensions/discord.js/BushClientEvents.ts b/src/lib/extensions/discord.js/BushClientEvents.ts index e1a9954..22bae65 100644 --- a/src/lib/extensions/discord.js/BushClientEvents.ts +++ b/src/lib/extensions/discord.js/BushClientEvents.ts @@ -1,176 +1,29 @@ import type { BanResponse, - BushApplicationCommand, - BushButtonInteraction, - BushClient, - BushDMChannel, - BushGuild, - BushGuildBan, - BushGuildEmoji, - BushGuildMember, - BushGuildTextBasedChannel, - BushMessage, - BushMessageReaction, - BushModalSubmitInteraction, - BushNewsChannel, - BushNonThreadGuildBasedChannel, - BushPresence, - BushRole, - BushSelectMenuInteraction, - BushStageInstance, - BushTextBasedChannel, - BushTextChannel, - BushThreadChannel, - BushThreadMember, - BushUser, - BushVoiceState, - Guild, - GuildSettings, - PartialBushGuildMember, - PartialBushMessage, - PartialBushMessageReaction, - PartialBushUser + CommandMessage, + Guild as GuildDB, + GuildSettings } from '#lib'; import type { AkairoClientEvents } from 'discord-akairo'; import type { + ButtonInteraction, Collection, - GuildScheduledEvent, - Interaction, - Invite, + Guild, + GuildMember, + GuildTextBasedChannel, + Message, + ModalSubmitInteraction, + Role, + SelectMenuInteraction, Snowflake, - Sticker, - Typing + User } from 'discord.js'; export interface BushClientEvents extends AkairoClientEvents { - applicationCommandCreate: [command: BushApplicationCommand]; - applicationCommandDelete: [command: BushApplicationCommand]; - applicationCommandUpdate: [ - oldCommand: BushApplicationCommand | null, - newCommand: BushApplicationCommand - ]; - channelCreate: [channel: BushNonThreadGuildBasedChannel]; - channelDelete: [channel: BushDMChannel | BushNonThreadGuildBasedChannel]; - channelPinsUpdate: [channel: BushTextBasedChannel, date: Date]; - channelUpdate: [ - oldChannel: BushDMChannel | BushNonThreadGuildBasedChannel, - newChannel: BushDMChannel | BushNonThreadGuildBasedChannel - ]; - debug: [message: string]; - warn: [message: string]; - emojiCreate: [emoji: BushGuildEmoji]; - emojiDelete: [emoji: BushGuildEmoji]; - emojiUpdate: [oldEmoji: BushGuildEmoji, newEmoji: BushGuildEmoji]; - error: [error: Error]; - guildBanAdd: [ban: BushGuildBan]; - guildBanRemove: [ban: BushGuildBan]; - guildCreate: [guild: BushGuild]; - guildDelete: [guild: BushGuild]; - guildUnavailable: [guild: BushGuild]; - guildIntegrationsUpdate: [guild: BushGuild]; - guildMemberAdd: [member: BushGuildMember]; - guildMemberAvailable: [member: BushGuildMember | PartialBushGuildMember]; - guildMemberRemove: [member: BushGuildMember | PartialBushGuildMember]; - guildMembersChunk: [ - members: Collection, - guild: BushGuild, - data: { - count: number; - index: number; - nonce: string | undefined; - } - ]; - guildMemberUpdate: [ - oldMember: BushGuildMember | PartialBushGuildMember, - newMember: BushGuildMember - ]; - guildUpdate: [oldGuild: BushGuild, newGuild: BushGuild]; - inviteCreate: [invite: Invite]; - inviteDelete: [invite: Invite]; - messageCreate: [message: BushMessage]; - messageDelete: [message: BushMessage | PartialBushMessage]; - messageReactionRemoveAll: [ - message: BushMessage | PartialBushMessage, - reactions: Collection - ]; - messageReactionRemoveEmoji: [ - reaction: BushMessageReaction | PartialBushMessageReaction - ]; - messageDeleteBulk: [ - messages: Collection, - channel: BushTextBasedChannel - ]; - messageReactionAdd: [ - reaction: BushMessageReaction | PartialBushMessageReaction, - user: BushUser | PartialBushUser - ]; - messageReactionRemove: [ - reaction: BushMessageReaction | PartialBushMessageReaction, - user: BushUser | PartialBushUser - ]; - messageUpdate: [ - oldMessage: BushMessage | PartialBushMessage, - newMessage: BushMessage | PartialBushMessage - ]; - presenceUpdate: [oldPresence: BushPresence | null, newPresence: BushPresence]; - ready: [client: BushClient]; - invalidated: []; - roleCreate: [role: BushRole]; - roleDelete: [role: BushRole]; - roleUpdate: [oldRole: BushRole, newRole: BushRole]; - threadCreate: [thread: BushThreadChannel, newlyCreated: boolean]; - threadDelete: [thread: BushThreadChannel]; - threadListSync: [ - threads: Collection, - guild: BushGuild - ]; - threadMemberUpdate: [ - oldMember: BushThreadMember, - newMember: BushThreadMember - ]; - threadMembersUpdate: [ - oldMembers: Collection, - newMembers: Collection, - thread: BushThreadChannel - ]; - threadUpdate: [oldThread: BushThreadChannel, newThread: BushThreadChannel]; - typingStart: [typing: Typing]; - userUpdate: [oldUser: BushUser | PartialBushUser, newUser: BushUser]; - voiceStateUpdate: [oldState: BushVoiceState, newState: BushVoiceState]; - webhookUpdate: [channel: BushTextChannel]; - interactionCreate: [interaction: Interaction]; - shardError: [error: Error, shardId: number]; - shardReady: [shardId: number, unavailableGuilds: Set | undefined]; - shardReconnecting: [shardId: number]; - shardResume: [shardId: number, replayedEvents: number]; - stageInstanceCreate: [stageInstance: BushStageInstance]; - stageInstanceUpdate: [ - oldStageInstance: BushStageInstance | null, - newStageInstance: BushStageInstance - ]; - stageInstanceDelete: [stageInstance: BushStageInstance]; - stickerCreate: [sticker: Sticker]; - stickerDelete: [sticker: Sticker]; - stickerUpdate: [oldSticker: Sticker, newSticker: Sticker]; - guildScheduledEventCreate: [guildScheduledEvent: GuildScheduledEvent]; - guildScheduledEventUpdate: [ - oldGuildScheduledEvent: GuildScheduledEvent, - newGuildScheduledEvent: GuildScheduledEvent - ]; - guildScheduledEventDelete: [guildScheduledEvent: GuildScheduledEvent]; - guildScheduledEventUserAdd: [ - guildScheduledEvent: GuildScheduledEvent, - user: BushUser - ]; - guildScheduledEventUserRemove: [ - guildScheduledEvent: GuildScheduledEvent, - user: BushUser - ]; - /* Custom */ bushBan: [ - victim: BushGuildMember | BushUser, - moderator: BushUser, - guild: BushGuild, + victim: GuildMember | User, + moderator: User, + guild: Guild, reason: string | undefined, caseID: string, duration: number, @@ -178,29 +31,29 @@ export interface BushClientEvents extends AkairoClientEvents { evidence?: string ]; bushBlock: [ - victim: BushGuildMember, - moderator: BushUser, - guild: BushGuild, + victim: GuildMember, + moderator: User, + guild: Guild, reason: string | undefined, caseID: string, duration: number, dmSuccess: boolean, - channel: BushGuildTextBasedChannel, + channel: GuildTextBasedChannel, evidence?: string ]; bushKick: [ - victim: BushGuildMember, - moderator: BushUser, - guild: BushGuild, + victim: GuildMember, + moderator: User, + guild: Guild, reason: string | undefined, caseID: string, dmSuccess: boolean, evidence?: string ]; bushMute: [ - victim: BushGuildMember, - moderator: BushUser, - guild: BushGuild, + victim: GuildMember, + moderator: User, + guild: Guild, reason: string | undefined, caseID: string, duration: number, @@ -208,43 +61,43 @@ export interface BushClientEvents extends AkairoClientEvents { evidence?: string ]; bushPunishRole: [ - victim: BushGuildMember, - moderator: BushUser, - guild: BushGuild, + victim: GuildMember, + moderator: User, + guild: Guild, reason: string | undefined, caseID: string, duration: number, - role: BushRole, + role: Role, evidence?: string ]; bushPunishRoleRemove: [ - victim: BushGuildMember, - moderator: BushUser, - guild: BushGuild, + victim: GuildMember, + moderator: User, + guild: Guild, reason: string | undefined, caseID: string, - role: BushRole, + role: Role, evidence?: string ]; bushPurge: [ - moderator: BushUser, - guild: BushGuild, - channel: BushTextChannel | BushNewsChannel | BushThreadChannel, - messages: Collection + moderator: User, + guild: Guild, + channel: GuildTextBasedChannel, + messages: Collection ]; bushRemoveTimeout: [ - victim: BushGuildMember, - moderator: BushUser, - guild: BushGuild, + victim: GuildMember, + moderator: User, + guild: Guild, reason: string | undefined, caseID: string, dmSuccess: boolean, evidence?: string ]; bushTimeout: [ - victim: BushGuildMember, - moderator: BushUser, - guild: BushGuild, + victim: GuildMember, + moderator: User, + guild: Guild, reason: string | undefined, caseID: string, duration: number, @@ -252,35 +105,35 @@ export interface BushClientEvents extends AkairoClientEvents { evidence?: string ]; bushUnban: [ - victim: BushUser, - moderator: BushUser, - guild: BushGuild, + victim: User, + moderator: User, + guild: Guild, reason: string | undefined, caseID: string, dmSuccess: boolean, evidence?: string ]; bushUnblock: [ - victim: BushGuildMember | BushUser, - moderator: BushUser, - guild: BushGuild, + victim: GuildMember | User, + moderator: User, + guild: Guild, reason: string | undefined, caseID: string, dmSuccess: boolean, - channel: BushGuildTextBasedChannel, + channel: GuildTextBasedChannel, evidence?: string ]; bushUnmute: [ - victim: BushGuildMember, - moderator: BushUser, - guild: BushGuild, + victim: GuildMember, + moderator: User, + guild: Guild, reason: string | undefined, caseID: string, dmSuccess: boolean, evidence?: string ]; bushUpdateModlog: [ - moderator: BushGuildMember, + moderator: GuildMember, modlogID: string, key: 'evidence' | 'hidden', oldModlog: string | boolean, @@ -288,55 +141,55 @@ export interface BushClientEvents extends AkairoClientEvents { ]; bushUpdateSettings: [ setting: Setting, - guild: BushGuild, - oldValue: Guild[Setting], - newValue: Guild[Setting], - moderator?: BushGuildMember + guild: Guild, + oldValue: GuildDB[Setting], + newValue: GuildDB[Setting], + moderator?: GuildMember ]; bushWarn: [ - victim: BushGuildMember, - moderator: BushUser, - guild: BushGuild, + victim: GuildMember, + moderator: User, + guild: Guild, reason: string | undefined, caseID: string, dmSuccess: boolean, evidence?: string ]; bushLevelUpdate: [ - member: BushGuildMember, + member: GuildMember, oldLevel: number, newLevel: number, currentXp: number, - message: BushMessage & { guild: BushGuild } + message: CommandMessage ]; bushLockdown: [ - moderator: BushGuildMember, + moderator: GuildMember, reason: string | undefined, channelsSuccessMap: Collection, all?: boolean ]; bushUnlockdown: [ - moderator: BushGuildMember, + moderator: GuildMember, reason: string | undefined, channelsSuccessMap: Collection, all?: boolean ]; massBan: [ - moderator: BushGuildMember, - guild: BushGuild, + moderator: GuildMember, + guild: Guild, reason: string | undefined, results: Collection ]; massEvidence: [ - moderator: BushGuildMember, - guild: BushGuild, + moderator: GuildMember, + guild: Guild, evidence: string, lines: string[] ]; /* components */ - button: [button: BushButtonInteraction]; - selectMenu: [selectMenu: BushSelectMenuInteraction]; - modal: [modal: BushModalSubmitInteraction]; + button: [button: ButtonInteraction]; + selectMenu: [selectMenu: SelectMenuInteraction]; + modal: [modal: ModalSubmitInteraction]; } type Setting = diff --git a/src/lib/extensions/discord.js/BushClientUser.ts b/src/lib/extensions/discord.js/BushClientUser.ts deleted file mode 100644 index cee9808..0000000 --- a/src/lib/extensions/discord.js/BushClientUser.ts +++ /dev/null @@ -1,98 +0,0 @@ -import type { - ActivityOptions, - Base64Resolvable, - BufferResolvable, - ClientPresence, - ClientUser, - ClientUserEditData, - PresenceData, - PresenceStatusData -} from 'discord.js'; -import { BushUser } from './BushUser.js'; - -/** - * Represents the logged in client's Discord user. - */ -export declare class BushClientUser extends BushUser implements ClientUser { - /** - * If the bot's {@link ClientApplication.owner Owner} has MFA enabled on their account - */ - public mfaEnabled: boolean; - - /** - * Represents the client user's presence - */ - public readonly presence: ClientPresence; - - /** - * Whether or not this account has been verified - */ - public verified: boolean; - - /** - * Edits the logged in client. - * @param data The new data - */ - public edit(data: ClientUserEditData): Promise; - - /** - * Sets the activity the client user is playing. - * @param name Activity being played, or options for setting the activity - * @param options Options for setting the activity - * @example - * // Set the client user's activity - * client.user.setActivity('discord.js', { type: 'WATCHING' }); - */ - public setActivity(options?: ActivityOptions): ClientPresence; - public setActivity(name: string, options?: ActivityOptions): ClientPresence; - - /** - * Sets/removes the AFK flag for the client user. - * @param afk Whether or not the user is AFK - * @param shardId Shard Id(s) to have the AFK flag set on - */ - public setAFK(afk: boolean, shardId?: number | number[]): ClientPresence; - - /** - * Sets the avatar of the logged in client. - * @param avatar The new avatar - * @example - * // Set avatar - * client.user.setAvatar('./avatar.png') - * .then(user => console.log(`New avatar set!`)) - * .catch(console.error); - */ - public setAvatar(avatar: BufferResolvable | Base64Resolvable | null): Promise; - - /** - * Sets the full presence of the client user. - * @param data Data for the presence - * @example - * // Set the client user's presence - * client.user.setPresence({ activities: [{ name: 'with discord.js' }], status: 'idle' }); - */ - public setPresence(data: PresenceData): ClientPresence; - - /** - * Sets the status of the client user. - * @param status Status to change to - * @param shardId Shard id(s) to have the activity set on - * @example - * // Set the client user's status - * client.user.setStatus('idle'); - */ - public setStatus(status: PresenceStatusData, shardId?: number | number[]): ClientPresence; - - /** - * Sets the username of the logged in client. - * Changing usernames in Discord is heavily rate limited, with only 2 requests - * every hour. Use this sparingly! - * @param username The new username - * @example - * // Set username - * client.user.setUsername('discordjs') - * .then(user => console.log(`My new username is ${user.username}`)) - * .catch(console.error); - */ - public setUsername(username: string): Promise; -} diff --git a/src/lib/extensions/discord.js/BushDMChannel.ts b/src/lib/extensions/discord.js/BushDMChannel.ts deleted file mode 100644 index 87382ec..0000000 --- a/src/lib/extensions/discord.js/BushDMChannel.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { - BushCategoryChannel, - BushClient, - BushMessageManager, - BushNewsChannel, - BushStageChannel, - BushTextBasedChannel, - BushTextChannel, - BushThreadChannel, - BushUser, - BushVoiceBasedChannel, - BushVoiceChannel -} from '#lib'; -import { DMChannel, PartialGroupDMChannel, type Partialize } from 'discord.js'; -import type { RawDMChannelData } from 'discord.js/typings/rawDataTypes'; - -/** - * Represents a direct message channel between two users. - */ -export class BushDMChannel extends DMChannel { - public declare readonly client: BushClient; - public declare messages: BushMessageManager; - - public constructor(client: BushClient, data?: RawDMChannelData) { - super(client, data); - } -} - -export interface BushDMChannel extends DMChannel { - get recipient(): BushUser | null; - isText(): this is BushTextChannel; - isDM(): this is BushDMChannel; - isDMBased(): this is PartialGroupDMChannel | BushDMChannel; - isVoice(): this is BushVoiceChannel; - isCategory(): this is BushCategoryChannel; - isNews(): this is BushNewsChannel; - isThread(): this is BushThreadChannel; - isStage(): this is BushStageChannel; - isTextBased(): this is BushTextBasedChannel; - isVoiceBased(): this is BushVoiceBasedChannel; -} - -export interface PartialBushDMChannel extends Partialize { - lastMessageId: undefined; -} diff --git a/src/lib/extensions/discord.js/BushEmoji.ts b/src/lib/extensions/discord.js/BushEmoji.ts deleted file mode 100644 index 9e42e5d..0000000 --- a/src/lib/extensions/discord.js/BushEmoji.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { BushClient } from '#lib'; -import { Emoji } from 'discord.js'; -import type { RawEmojiData } from 'discord.js/typings/rawDataTypes'; - -/** - * Represents an emoji, see {@link GuildEmoji} and {@link ReactionEmoji}. - */ -export class BushEmoji extends Emoji { - public declare readonly client: BushClient; - - public constructor(client: BushClient, emoji: RawEmojiData) { - super(client, emoji); - } -} diff --git a/src/lib/extensions/discord.js/BushGuild.ts b/src/lib/extensions/discord.js/BushGuild.ts deleted file mode 100644 index a93e35f..0000000 --- a/src/lib/extensions/discord.js/BushGuild.ts +++ /dev/null @@ -1,782 +0,0 @@ -import { - AllowedMentions, - banResponse, - BushGuildChannelManager, - BushGuildMemberManager, - BushMessage, - BushVoiceChannel, - dmResponse, - permissionsResponse, - punishmentEntryRemove, - type BanResponse, - type BushClient, - type BushGuildMember, - type BushGuildMemberResolvable, - type BushNewsChannel, - type BushTextChannel, - type BushThreadChannel, - type BushUser, - type BushUserResolvable, - type GuildFeatures, - type GuildLogType, - type GuildModel -} from '#lib'; -import { APIMessage } from 'discord-api-types/v10'; -import { - Collection, - Guild, - MessageType, - PermissionFlagsBits, - SnowflakeUtil, - ThreadChannel, - Webhook, - WebhookMessageOptions, - type MessageOptions, - type MessagePayload, - type Snowflake -} from 'discord.js'; -import type { RawGuildData } from 'discord.js/typings/rawDataTypes'; -import _ from 'lodash'; -import { Moderation } from '../../common/util/Moderation.js'; -import { Guild as GuildDB } from '../../models/instance/Guild.js'; -import { ModLogType } from '../../models/instance/ModLog.js'; - -/** - * Represents a guild (or a server) on Discord. - * It's recommended to see if a guild is available before performing operations or reading data from it. You can - * check this with {@link BushGuild.available}. - */ -export class BushGuild extends Guild { - public declare readonly client: BushClient; - public declare members: BushGuildMemberManager; - public declare channels: BushGuildChannelManager; - - public constructor(client: BushClient, data: RawGuildData) { - super(client, data); - } - - /** - * Checks if the guild has a certain custom feature. - * @param feature The feature to check for - */ - public async hasFeature(feature: GuildFeatures): Promise { - const features = await this.getSetting('enabledFeatures'); - return features.includes(feature); - } - - /** - * Adds a custom feature to the guild. - * @param feature The feature to add - * @param moderator The moderator responsible for adding a feature - */ - public async addFeature(feature: GuildFeatures, moderator?: BushGuildMember): Promise { - const features = await this.getSetting('enabledFeatures'); - const newFeatures = util.addOrRemoveFromArray('add', features, feature); - return (await this.setSetting('enabledFeatures', newFeatures, moderator)).enabledFeatures; - } - - /** - * Removes a custom feature from the guild. - * @param feature The feature to remove - * @param moderator The moderator responsible for removing a feature - */ - public async removeFeature(feature: GuildFeatures, moderator?: BushGuildMember): Promise { - const features = await this.getSetting('enabledFeatures'); - const newFeatures = util.addOrRemoveFromArray('remove', features, feature); - return (await this.setSetting('enabledFeatures', newFeatures, moderator)).enabledFeatures; - } - - /** - * Makes a custom feature the opposite of what it was before - * @param feature The feature to toggle - * @param moderator The moderator responsible for toggling a feature - */ - public async toggleFeature(feature: GuildFeatures, moderator?: BushGuildMember): Promise { - return (await this.hasFeature(feature)) - ? await this.removeFeature(feature, moderator) - : await this.addFeature(feature, moderator); - } - - /** - * Fetches a custom setting for the guild - * @param setting The setting to get - */ - public async getSetting(setting: K): Promise { - return ( - client.cache.guilds.get(this.id)?.[setting] ?? - ((await GuildDB.findByPk(this.id)) ?? GuildDB.build({ id: this.id }))[setting] - ); - } - - /** - * Sets a custom setting for the guild - * @param setting The setting to change - * @param value The value to change the setting to - * @param moderator The moderator to responsible for changing the setting - */ - public async setSetting>( - setting: K, - value: GuildDB[K], - moderator?: BushGuildMember - ): Promise { - const row = (await GuildDB.findByPk(this.id)) ?? GuildDB.build({ id: this.id }); - const oldValue = row[setting] as GuildDB[K]; - row[setting] = value; - client.cache.guilds.set(this.id, row.toJSON() as GuildDB); - client.emit('bushUpdateSettings', setting, this, oldValue, row[setting], moderator); - return await row.save(); - } - - /** - * Get a the log channel configured for a certain log type. - * @param logType The type of log channel to get. - * @returns Either the log channel or undefined if not configured. - */ - public async getLogChannel(logType: GuildLogType): Promise { - const channelId = (await this.getSetting('logChannels'))[logType]; - if (!channelId) return undefined; - return ( - (this.channels.cache.get(channelId) as BushTextChannel | undefined) ?? - ((await this.channels.fetch(channelId)) as BushTextChannel | null) ?? - undefined - ); - } - - /** - * Sends a message to the guild's specified logging channel - * @param logType The corresponding channel that the message will be sent to - * @param message The parameters for {@link BushTextChannel.send} - */ - public async sendLogChannel(logType: GuildLogType, message: string | MessagePayload | MessageOptions) { - const logChannel = await this.getLogChannel(logType); - if (!logChannel || !logChannel.isTextBased()) return; - if ( - !logChannel - .permissionsFor(this.members.me!.id) - ?.has([PermissionFlagsBits.ViewChannel, PermissionFlagsBits.SendMessages, PermissionFlagsBits.EmbedLinks]) - ) - return; - - return await logChannel.send(message).catch(() => null); - } - - /** - * Sends a formatted error message in a guild's error log channel - * @param title The title of the error embed - * @param message The description of the error embed - */ - public async error(title: string, message: string) { - void client.console.info(_.camelCase(title), message.replace(/\*\*(.*?)\*\*/g, '<<$1>>')); - void this.sendLogChannel('error', { embeds: [{ title: title, description: message, color: util.colors.error }] }); - } - - /** - * Bans a user, dms them, creates a mod log entry, and creates a punishment entry. - * @param options Options for banning the user. - * @returns A string status message of the ban. - */ - public async bushBan(options: GuildBushBanOptions): Promise { - // checks - if (!this.members.me!.permissions.has(PermissionFlagsBits.BanMembers)) return banResponse.MISSING_PERMISSIONS; - - let caseID: string | undefined = undefined; - let dmSuccessEvent: boolean | undefined = undefined; - const user = await util.resolveNonCachedUser(options.user); - const moderator = client.users.resolve(options.moderator ?? client.user!); - if (!user || !moderator) return banResponse.CANNOT_RESOLVE_USER; - - if ((await this.bans.fetch()).has(user.id)) return banResponse.ALREADY_BANNED; - - const ret = await (async () => { - // add modlog entry - const { log: modlog } = await Moderation.createModLogEntry({ - type: options.duration ? ModLogType.TEMP_BAN : ModLogType.PERM_BAN, - user: user, - moderator: moderator.id, - reason: options.reason, - duration: options.duration, - guild: this, - evidence: options.evidence - }); - if (!modlog) return banResponse.MODLOG_ERROR; - caseID = modlog.id; - - // dm user - dmSuccessEvent = await Moderation.punishDM({ - modlog: modlog.id, - guild: this, - user: user, - punishment: 'banned', - duration: options.duration ?? 0, - reason: options.reason ?? undefined, - sendFooter: true - }); - - // ban - const banSuccess = await this.bans - .create(user?.id ?? options.user, { - reason: `${moderator.tag} | ${options.reason ?? 'No reason provided.'}`, - deleteMessageDays: options.deleteDays - }) - .catch(() => false); - if (!banSuccess) return banResponse.ACTION_ERROR; - - // add punishment entry so they can be unbanned later - const punishmentEntrySuccess = await Moderation.createPunishmentEntry({ - type: 'ban', - user: user, - guild: this, - duration: options.duration, - modlog: modlog.id - }); - if (!punishmentEntrySuccess) return banResponse.PUNISHMENT_ENTRY_ADD_ERROR; - - if (!dmSuccessEvent) return banResponse.DM_ERROR; - return banResponse.SUCCESS; - })(); - - if (!([banResponse.ACTION_ERROR, banResponse.MODLOG_ERROR, banResponse.PUNISHMENT_ENTRY_ADD_ERROR] as const).includes(ret)) - client.emit( - 'bushBan', - user, - moderator, - this, - options.reason ?? undefined, - caseID!, - options.duration ?? 0, - dmSuccessEvent, - options.evidence - ); - return ret; - } - - /** - * {@link bushBan} with less resolving and checks - * @param options Options for banning the user. - * @returns A string status message of the ban. - * **Preconditions:** - * - {@link me} has the `BanMembers` permission - * **Warning:** - * - Doesn't emit bushBan Event - */ - public async massBanOne(options: GuildMassBanOneOptions): Promise { - if (this.bans.cache.has(options.user)) return banResponse.ALREADY_BANNED; - - const ret = await (async () => { - // add modlog entry - const { log: modlog } = await Moderation.createModLogEntrySimple({ - type: ModLogType.PERM_BAN, - user: options.user, - moderator: options.moderator, - reason: options.reason, - duration: 0, - guild: this.id - }); - if (!modlog) return banResponse.MODLOG_ERROR; - - let dmSuccessEvent: boolean | undefined = undefined; - // dm user - if (this.members.cache.has(options.user)) { - dmSuccessEvent = await Moderation.punishDM({ - modlog: modlog.id, - guild: this, - user: options.user, - punishment: 'banned', - duration: 0, - reason: options.reason ?? undefined, - sendFooter: true - }); - } - - // ban - const banSuccess = await this.bans - .create(options.user, { - reason: `${options.moderator} | ${options.reason}`, - deleteMessageDays: options.deleteDays - }) - .catch(() => false); - if (!banSuccess) return banResponse.ACTION_ERROR; - - // add punishment entry so they can be unbanned later - const punishmentEntrySuccess = await Moderation.createPunishmentEntry({ - type: 'ban', - user: options.user, - guild: this, - duration: 0, - modlog: modlog.id - }); - if (!punishmentEntrySuccess) return banResponse.PUNISHMENT_ENTRY_ADD_ERROR; - - if (!dmSuccessEvent) return banResponse.DM_ERROR; - return banResponse.SUCCESS; - })(); - return ret; - } - - /** - * Unbans a user, dms them, creates a mod log entry, and destroys the punishment entry. - * @param options Options for unbanning the user. - * @returns A status message of the unban. - */ - public async bushUnban(options: GuildBushUnbanOptions): Promise { - // checks - if (!this.members.me!.permissions.has(PermissionFlagsBits.BanMembers)) return unbanResponse.MISSING_PERMISSIONS; - - let caseID: string | undefined = undefined; - let dmSuccessEvent: boolean | undefined = undefined; - const user = await util.resolveNonCachedUser(options.user); - const moderator = client.users.resolve(options.moderator ?? client.user!); - if (!user || !moderator) return unbanResponse.CANNOT_RESOLVE_USER; - - const ret = await (async () => { - const bans = await this.bans.fetch(); - - let notBanned = false; - if (!bans.has(user.id)) notBanned = true; - - const unbanSuccess = await this.bans - .remove(user, `${moderator.tag} | ${options.reason ?? 'No reason provided.'}`) - .catch((e) => { - if (e?.code === 'UNKNOWN_BAN') { - notBanned = true; - return true; - } else return false; - }); - - if (notBanned) return unbanResponse.NOT_BANNED; - if (!unbanSuccess) return unbanResponse.ACTION_ERROR; - - // add modlog entry - const { log: modlog } = await Moderation.createModLogEntry({ - type: ModLogType.UNBAN, - user: user.id, - moderator: moderator.id, - reason: options.reason, - guild: this, - evidence: options.evidence - }); - if (!modlog) return unbanResponse.MODLOG_ERROR; - caseID = modlog.id; - - // remove punishment entry - const removePunishmentEntrySuccess = await Moderation.removePunishmentEntry({ - type: 'ban', - user: user.id, - guild: this - }); - if (!removePunishmentEntrySuccess) return unbanResponse.PUNISHMENT_ENTRY_REMOVE_ERROR; - - // dm user - dmSuccessEvent = await Moderation.punishDM({ - guild: this, - user: user, - punishment: 'unbanned', - reason: options.reason ?? undefined, - sendFooter: false - }); - - if (!dmSuccessEvent) return unbanResponse.DM_ERROR; - return unbanResponse.SUCCESS; - })(); - if ( - !([unbanResponse.ACTION_ERROR, unbanResponse.MODLOG_ERROR, unbanResponse.PUNISHMENT_ENTRY_REMOVE_ERROR] as const).includes( - ret - ) - ) - client.emit('bushUnban', user, moderator, this, options.reason ?? undefined, caseID!, dmSuccessEvent!, options.evidence); - return ret; - } - - /** - * Denies send permissions in specified channels - * @param options The options for locking down the guild - */ - public async lockdown(options: LockdownOptions): Promise { - if (!options.all && !options.channel) return 'all not chosen and no channel specified'; - const channelIds = options.all ? await this.getSetting('lockdownChannels') : [options.channel!.id]; - - if (!channelIds.length) return 'no channels configured'; - const mappedChannels = channelIds.map((id) => this.channels.cache.get(id)); - - const invalidChannels = mappedChannels.filter((c) => c === undefined); - if (invalidChannels.length) return `invalid channel configured: ${invalidChannels.join(', ')}`; - - const moderator = this.members.resolve(options.moderator); - if (!moderator) return 'moderator not found'; - - const errors = new Collection(); - const success = new Collection(); - const ret = await (async (): Promise => { - for (const _channel of mappedChannels) { - const channel = _channel!; - if (!channel.isTextBased()) { - errors.set(channel.id, new Error('wrong channel type')); - success.set(channel.id, false); - continue; - } - if (!channel.permissionsFor(this.members.me!.id)?.has([PermissionFlagsBits.ManageChannels])) { - errors.set(channel.id, new Error('client no permission')); - success.set(channel.id, false); - continue; - } else if (!channel.permissionsFor(moderator)?.has([PermissionFlagsBits.ManageChannels])) { - errors.set(channel.id, new Error('moderator no permission')); - success.set(channel.id, false); - continue; - } - - const reason = `[${options.unlock ? 'Unlockdown' : 'Lockdown'}] ${moderator.user.tag} | ${ - options.reason ?? 'No reason provided' - }`; - - const permissionOverwrites = channel.isThread() ? channel.parent!.permissionOverwrites : channel.permissionOverwrites; - const perms = { - SendMessagesInThreads: options.unlock ? null : false, - SendMessages: options.unlock ? null : false - }; - const permsForMe = { - [channel.isThread() ? 'SendMessagesInThreads' : 'SendMessages']: options.unlock ? null : true - }; // so I can send messages in the channel - - const changePermSuccess = await permissionOverwrites.edit(this.id, perms, { reason }).catch((e) => e); - if (changePermSuccess instanceof Error) { - errors.set(channel.id, changePermSuccess); - success.set(channel.id, false); - } else { - success.set(channel.id, true); - await permissionOverwrites.edit(this.members.me!, permsForMe, { reason }); - await channel.send({ - embeds: [ - { - author: { name: moderator.user.tag, icon_url: moderator.displayAvatarURL() }, - title: `This channel has been ${options.unlock ? 'un' : ''}locked`, - description: options.reason ?? 'No reason provided', - color: options.unlock ? util.colors.Green : util.colors.Red, - timestamp: new Date().toISOString() - } - ] - }); - } - } - - if (errors.size) return errors; - else return `success: ${success.filter((c) => c === true).size}`; - })(); - - client.emit(options.unlock ? 'bushUnlockdown' : 'bushLockdown', moderator, options.reason, success, options.all); - return ret; - } - - public async quote(rawQuote: APIMessage, channel: BushTextChannel | BushNewsChannel | BushThreadChannel) { - if (!channel.isTextBased() || channel.isDMBased() || channel.guildId !== this.id || !this.members.me) return null; - if (!channel.permissionsFor(this.members.me).has('ManageWebhooks')) return null; - - const quote = new BushMessage(client, rawQuote); - - const target = channel instanceof ThreadChannel ? channel.parent : channel; - if (!target) return null; - - const webhooks: Collection = await target.fetchWebhooks().catch((e) => e); - if (!(webhooks instanceof Collection)) return null; - - // find a webhook that we can use - let webhook = webhooks.find((w) => !!w.token) ?? null; - if (!webhook) - webhook = await target - .createWebhook(`${client.user!.username} Quotes #${target.name}`, { - avatar: client.user!.displayAvatarURL({ size: 2048 }), - reason: 'Creating a webhook for quoting' - }) - .catch(() => null); - - if (!webhook) return null; - - const sendOptions: Omit = {}; - - const displayName = quote.member?.displayName ?? quote.author.username; - - switch (quote.type) { - case MessageType.Default: - case MessageType.Reply: - case MessageType.ChatInputCommand: - case MessageType.ContextMenuCommand: - case MessageType.ThreadStarterMessage: - sendOptions.content = quote.content || undefined; - sendOptions.threadId = channel instanceof ThreadChannel ? channel.id : undefined; - sendOptions.embeds = quote.embeds.length ? quote.embeds : undefined; - sendOptions.attachments = quote.attachments.size ? [...quote.attachments.values()] : undefined; - - if (quote.stickers.size && !(quote.content || quote.embeds.length || quote.attachments.size)) - sendOptions.content = '[[This message has a sticker but not content]]'; - - break; - case MessageType.RecipientAdd: { - const recipient = rawQuote.mentions[0]; - if (!recipient) { - sendOptions.content = `${util.emojis.error} Cannot resolve recipient.`; - break; - } - - if (quote.channel.isThread()) { - const recipientDisplay = quote.guild?.members.cache.get(recipient.id)?.displayName ?? recipient.username; - sendOptions.content = `${util.emojis.join} ${displayName} added ${recipientDisplay} to the thread.`; - } else { - // this should never happen - sendOptions.content = `${util.emojis.join} ${displayName} added ${recipient.username} to the group.`; - } - - break; - } - case MessageType.RecipientRemove: { - const recipient = rawQuote.mentions[0]; - if (!recipient) { - sendOptions.content = `${util.emojis.error} Cannot resolve recipient.`; - break; - } - - if (quote.channel.isThread()) { - const recipientDisplay = quote.guild?.members.cache.get(recipient.id)?.displayName ?? recipient.username; - sendOptions.content = `${util.emojis.leave} ${displayName} removed ${recipientDisplay} from the thread.`; - } else { - // this should never happen - sendOptions.content = `${util.emojis.leave} ${displayName} removed ${recipient.username} from the group.`; - } - - break; - } - - case MessageType.ChannelNameChange: - sendOptions.content = `<:pencil:957988608994861118> ${displayName} changed the channel name: **${quote.content}**`; - - break; - - case MessageType.ChannelPinnedMessage: - throw new Error('Not implemented yet: MessageType.ChannelPinnedMessage case'); - case MessageType.GuildMemberJoin: { - const messages = [ - '{username} joined the party.', - '{username} is here.', - 'Welcome, {username}. We hope you brought pizza.', - 'A wild {username} appeared.', - '{username} just landed.', - '{username} just slid into the server.', - '{username} just showed up!', - 'Welcome {username}. Say hi!', - '{username} hopped into the server.', - 'Everyone welcome {username}!', - "Glad you're here, {username}.", - 'Good to see you, {username}.', - 'Yay you made it, {username}!' - ]; - - const timestamp = SnowflakeUtil.timestampFrom(quote.id); - - // this is the same way that the discord client decides what message to use. - const message = messages[timestamp % messages.length].replace(/{username}/g, displayName); - - sendOptions.content = `${util.emojis.join} ${message}`; - break; - } - case MessageType.UserPremiumGuildSubscription: - sendOptions.content = `<:NitroBoost:585558042309820447> ${displayName} just boosted the server${ - quote.content ? ` **${quote.content}** times` : '' - }!`; - - break; - case MessageType.UserPremiumGuildSubscriptionTier1: - case MessageType.UserPremiumGuildSubscriptionTier2: - case MessageType.UserPremiumGuildSubscriptionTier3: - sendOptions.content = `<:NitroBoost:585558042309820447> ${displayName} just boosted the server${ - quote.content ? ` **${quote.content}** times` : '' - }! ${quote.guild?.name} has achieved **Level ${quote.type - 8}!**`; - - break; - case MessageType.ChannelFollowAdd: - sendOptions.content = `${displayName} has added **${quote.content}** to this channel. Its most important updates will show up here.`; - - break; - case MessageType.GuildDiscoveryDisqualified: - sendOptions.content = - '<:SystemMessageCross:842172192418693173> This server has been removed from Server Discovery because it no longer passes all the requirements. Check Server Settings for more details.'; - - break; - case MessageType.GuildDiscoveryRequalified: - sendOptions.content = - '<:SystemMessageCheck:842172191801212949> This server is eligible for Server Discovery again and has been automatically relisted!'; - - break; - case MessageType.GuildDiscoveryGracePeriodInitialWarning: - sendOptions.content = - '<:SystemMessageWarn:842172192401915971> This server has failed Discovery activity requirements for 1 week. If this server fails for 4 weeks in a row, it will be automatically removed from Discovery.'; - - break; - case MessageType.GuildDiscoveryGracePeriodFinalWarning: - sendOptions.content = - '<:SystemMessageWarn:842172192401915971> This server has failed Discovery activity requirements for 3 weeks in a row. If this server fails for 1 more week, it will be removed from Discovery.'; - - break; - case MessageType.ThreadCreated: { - const threadId = rawQuote.message_reference?.channel_id; - - sendOptions.content = `<:thread:865033845753249813> ${displayName} started a thread: **[${quote.content}](https://discord.com/channels/${quote.guildId}/${threadId} - )**. See all threads.`; - - break; - } - case MessageType.GuildInviteReminder: - sendOptions.content = 'Wondering who to invite? Start by inviting anyone who can help you build the server!'; - - break; - case MessageType.ChannelIconChange: - case MessageType.Call: - default: - sendOptions.content = `${util.emojis.error} I cannot quote **${ - MessageType[quote.type] || quote.type - }** messages, please report this to my developers.`; - - break; - } - - sendOptions.allowedMentions = AllowedMentions.none(); - sendOptions.username = quote.member?.displayName ?? quote.author.username; - sendOptions.avatarURL = quote.member?.displayAvatarURL({ size: 2048 }) ?? quote.author.displayAvatarURL({ size: 2048 }); - - return await webhook.send(sendOptions); /* .catch((e: any) => e); */ - } -} - -/** - * Options for unbanning a user - */ -export interface GuildBushUnbanOptions { - /** - * The user to unban - */ - user: BushUserResolvable | BushUser; - - /** - * The reason for unbanning the user - */ - reason?: string | null; - - /** - * The moderator who unbanned the user - */ - moderator?: BushUserResolvable; - - /** - * The evidence for the unban - */ - evidence?: string; -} - -export interface GuildMassBanOneOptions { - /** - * The user to ban - */ - user: Snowflake; - - /** - * The reason to ban the user - */ - reason: string; - - /** - * The moderator who banned the user - */ - moderator: Snowflake; - - /** - * The number of days to delete the user's messages for - */ - deleteDays?: number; -} - -/** - * Options for banning a user - */ -export interface GuildBushBanOptions { - /** - * The user to ban - */ - user: BushUserResolvable; - - /** - * The reason to ban the user - */ - reason?: string | null; - - /** - * The moderator who banned the user - */ - moderator?: BushUserResolvable; - - /** - * The duration of the ban - */ - duration?: number; - - /** - * The number of days to delete the user's messages for - */ - deleteDays?: number; - - /** - * The evidence for the ban - */ - evidence?: string; -} - -type ValueOf = T[keyof T]; - -export const unbanResponse = Object.freeze({ - ...dmResponse, - ...permissionsResponse, - ...punishmentEntryRemove, - NOT_BANNED: 'user not banned' -} as const); - -/** - * Response returned when unbanning a user - */ -export type UnbanResponse = ValueOf; - -/** - * Options for locking down channel(s) - */ -export interface LockdownOptions { - /** - * The moderator responsible for the lockdown - */ - moderator: BushGuildMemberResolvable; - - /** - * Whether to lock down all (specified) channels - */ - all: boolean; - - /** - * Reason for the lockdown - */ - reason?: string; - - /** - * A specific channel to lockdown - */ - channel?: BushThreadChannel | BushNewsChannel | BushTextChannel | BushVoiceChannel; - - /** - * Whether or not to unlock the channel(s) instead of locking them - */ - unlock?: boolean; -} - -/** - * Response returned when locking down a channel - */ -export type LockdownResponse = - | `success: ${number}` - | 'all not chosen and no channel specified' - | 'no channels configured' - | `invalid channel configured: ${string}` - | 'moderator not found' - | Collection; diff --git a/src/lib/extensions/discord.js/BushGuildApplicationCommandManager.ts b/src/lib/extensions/discord.js/BushGuildApplicationCommandManager.ts deleted file mode 100644 index ba9db66..0000000 --- a/src/lib/extensions/discord.js/BushGuildApplicationCommandManager.ts +++ /dev/null @@ -1,114 +0,0 @@ -/* eslint-disable @typescript-eslint/ban-types */ -import { - BushApplicationCommandManager, - type BushApplicationCommand, - type BushApplicationCommandResolvable, - type BushClient, - type BushGuild -} from '#lib'; -import type { ApplicationCommandData, BaseFetchOptions, Collection, Snowflake } from 'discord.js'; -import type { RawApplicationCommandData } from 'discord.js/typings/rawDataTypes'; - -/** - * An extension for guild-specific application commands. - */ -export declare class BushGuildApplicationCommandManager extends BushApplicationCommandManager< - BushApplicationCommand, - {}, - BushGuild -> { - public constructor(guild: BushGuild, iterable?: Iterable); - public declare readonly client: BushClient; - - /** - * The guild that this manager belongs to - */ - public guild: BushGuild; - - /** - * Creates an application command. - * @param command The command - * @param guildId The guild's id to create this command in, - * ignored when using a {@link GuildApplicationCommandManager} - * @example - * // Create a new command - * client.application.commands.create({ - * name: 'test', - * description: 'A test command', - * }) - * .then(console.log) - * .catch(console.error); - */ - public create(command: BushApplicationCommandResolvable): Promise; - - /** - * Deletes an application command. - * @param command The command to delete - * @param guildId The guild's id where the command is registered, - * ignored when using a {@link GuildApplicationCommandManager} - * @example - * // Delete a command - * guild.commands.delete('123456789012345678') - * .then(console.log) - * .catch(console.error); - */ - public delete(command: BushApplicationCommandResolvable): Promise; - - /** - * Edits an application command. - * @param command The command to edit - * @param data The data to update the command with - * @param guildId The guild's id where the command registered, - * ignored when using a {@link GuildApplicationCommandManager} - * @example - * // Edit an existing command - * client.application.commands.edit('123456789012345678', { - * description: 'New description', - * }) - * .then(console.log) - * .catch(console.error); - */ - public edit(command: BushApplicationCommandResolvable, data: ApplicationCommandData): Promise; - - /** - * Obtains one or multiple application commands from Discord, or the cache if it's already available. - * @param id The application command's id - * @param options Additional options for this fetch - * @example - * // Fetch a single command - * client.application.commands.fetch('123456789012345678') - * .then(command => console.log(`Fetched command ${command.name}`)) - * .catch(console.error); - * @example - * // Fetch all commands - * guild.commands.fetch() - * .then(commands => console.log(`Fetched ${commands.size} commands`)) - * .catch(console.error); - */ - public fetch(id: Snowflake, options?: BaseFetchOptions): Promise; - public fetch(options: BaseFetchOptions): Promise>; - public fetch(id?: undefined, options?: BaseFetchOptions): Promise>; - - /** - * Sets all the commands for this application or guild. - * @param commands The commands - * @param guildId The guild's id to create the commands in, - * ignored when using a {@link GuildApplicationCommandManager} - * @example - * // Set all commands to just this one - * client.application.commands.set([ - * { - * name: 'test', - * description: 'A test command', - * }, - * ]) - * .then(console.log) - * .catch(console.error); - * @example - * // Remove all commands - * guild.commands.set([]) - * .then(console.log) - * .catch(console.error); - */ - public set(commands: ApplicationCommandData[]): Promise>; -} diff --git a/src/lib/extensions/discord.js/BushGuildBan.ts b/src/lib/extensions/discord.js/BushGuildBan.ts deleted file mode 100644 index d56c531..0000000 --- a/src/lib/extensions/discord.js/BushGuildBan.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { BushClient, BushGuild, BushUser } from '#lib'; -import { GuildBan } from 'discord.js'; -import type { RawGuildBanData } from 'discord.js/typings/rawDataTypes'; - -/** - * Represents a ban in a guild on Discord. - */ -export declare class BushGuildBan extends GuildBan { - public constructor(client: BushClient, data: RawGuildBanData, guild: BushGuild); - public guild: BushGuild; - public user: BushUser; - public get partial(): boolean; - public reason?: string | null; - public fetch(force?: boolean): Promise; -} diff --git a/src/lib/extensions/discord.js/BushGuildChannel.ts b/src/lib/extensions/discord.js/BushGuildChannel.ts deleted file mode 100644 index 62bf05a..0000000 --- a/src/lib/extensions/discord.js/BushGuildChannel.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type { - BushCategoryChannel, - BushClient, - BushDMChannel, - BushGuild, - BushGuildBasedChannel, - BushNewsChannel, - BushStageChannel, - BushTextBasedChannel, - BushTextChannel, - BushThreadChannel, - BushVoiceBasedChannel, - BushVoiceChannel -} from '#lib'; -import { GuildChannel, PartialGroupDMChannel } from 'discord.js'; -import type { RawGuildChannelData } from 'discord.js/typings/rawDataTypes'; - -/** - * Represents a guild channel from any of the following: - * - {@link BushTextChannel} - * - {@link BushVoiceChannel} - * - {@link BushCategoryChannel} - * - {@link BushNewsChannel} - * - {@link BushStoreChannel} - * - {@link BushStageChannel} - */ -export class BushGuildChannel extends GuildChannel { - public declare readonly client: BushClient; - public declare guild: BushGuild; - - public constructor(guild: BushGuild, data?: RawGuildChannelData, client?: BushClient, immediatePatch?: boolean) { - super(guild, data, client, immediatePatch); - } -} - -export interface BushGuildChannel extends GuildChannel { - isText(): this is BushTextChannel; - isDMBased(): this is PartialGroupDMChannel | BushDMChannel; - isDM(): this is BushDMChannel; - isVoice(): this is BushVoiceChannel; - isCategory(): this is BushCategoryChannel; - isNews(): this is BushNewsChannel; - isThread(): this is BushThreadChannel; - isStage(): this is BushStageChannel; - isTextBased(): this is BushGuildBasedChannel & BushTextBasedChannel; - isVoiceBased(): this is BushVoiceBasedChannel; -} diff --git a/src/lib/extensions/discord.js/BushGuildChannelManager.ts b/src/lib/extensions/discord.js/BushGuildChannelManager.ts deleted file mode 100644 index 4048b98..0000000 --- a/src/lib/extensions/discord.js/BushGuildChannelManager.ts +++ /dev/null @@ -1,183 +0,0 @@ -import type { - BushFetchedThreads, - BushGuild, - BushGuildBasedChannel, - BushGuildChannel, - BushMappedGuildChannelTypes, - BushNonThreadGuildBasedChannel, - BushTextChannel -} from '#lib'; -import { - CachedManager, - ChannelData, - ChannelWebhookCreateOptions, - SetChannelPositionOptions, - Webhook, - type BaseFetchOptions, - type ChannelPosition, - type Collection, - type GuildChannelCreateOptions, - type GuildChannelManager, - type GuildChannelResolvable, - type GuildChannelTypes, - type Snowflake -} from 'discord.js'; -import type { RawGuildChannelData } from 'discord.js/typings/rawDataTypes'; - -/** - * Manages API methods for GuildChannels and stores their cache. - */ -export declare class BushGuildChannelManager - extends CachedManager - implements GuildChannelManager -{ - public constructor(guild: BushGuild, iterable?: Iterable); - - /** - * The number of channels in this managers cache excluding thread channels - * that do not count towards a guild's maximum channels restriction. - */ - public readonly channelCountWithoutThreads: number; - - /** - * The guild this Manager belongs to - */ - public guild: BushGuild; - - /** - * Creates a new channel in the guild. - * @param name The name of the new channel - * @param options Options for creating the new channel - * @example - * // Create a new text channel - * guild.channels.create('new-general', { reason: 'Needed a cool new channel' }) - * .then(console.log) - * .catch(console.error); - * @example - * // Create a new channel with permission overwrites - * guild.channels.create('new-voice', { - * type: 'GuildVoice', - * permissionOverwrites: [ - * { - * id: message.author.id, - * deny: [PermissionFlagsBits.ViewChannel], - * }, - * ], - * }) - */ - public create( - name: string, - options: GuildChannelCreateOptions & { type: T } - ): Promise; - public create(name: string, options?: GuildChannelCreateOptions): Promise; - - /** - * Creates a webhook for the channel. - * @param channel The channel to create the webhook for - * @param name The name of the webhook - * @param options Options for creating the webhook - * @returns Returns the created Webhook - * @example - * // Create a webhook for the current channel - * guild.channels.createWebhook('222197033908436994', 'Snek', { - * avatar: 'https://i.imgur.com/mI8XcpG.jpg', - * reason: 'Needed a cool new Webhook' - * }) - * .then(console.log) - * .catch(console.error) - */ - public createWebhook(channel: GuildChannelResolvable, name: string, options?: ChannelWebhookCreateOptions): Promise; - - /** - * Edits the channel. - * @param channel The channel to edit - * @param data The new data for the channel - * @param reason Reason for editing this channel - * @example - * // Edit a channel - * guild.channels.edit('222197033908436994', { name: 'new-channel' }) - * .then(console.log) - * .catch(console.error); - */ - public edit(channel: GuildChannelResolvable, data: ChannelData, reason?: string): Promise; - - /** - * Obtains one or more guild channels from Discord, or the channel cache if they're already available. - * @param id The channel's id - * @param options Additional options for this fetch - * @example - * // Fetch all channels from the guild (excluding threads) - * message.guild.channels.fetch() - * .then(channels => console.log(`There are ${channels.size} channels.`)) - * .catch(console.error); - * @example - * // Fetch a single channel - * message.guild.channels.fetch('222197033908436994') - * .then(channel => console.log(`The channel name is: ${channel.name}`)) - * .catch(console.error); - */ - public fetch(id: Snowflake, options?: BaseFetchOptions): Promise; - public fetch(id?: undefined, options?: BaseFetchOptions): Promise>; - - /** - * Fetches all webhooks for the channel. - * @param channel The channel to fetch webhooks for - * @example - * // Fetch webhooks - * guild.channels.fetchWebhooks('769862166131245066') - * .then(hooks => console.log(`This channel has ${hooks.size} hooks`)) - * .catch(console.error); - */ - public fetchWebhooks(channel: GuildChannelResolvable): Promise>; - - /** - * Sets a new position for the guild channel. - * @param channel The channel to set the position for - * @param position The new position for the guild channel - * @param options Options for setting position - * @example - * // Set a new channel position - * guild.channels.setPosition('222078374472843266', 2) - * .then(newChannel => console.log(`Channel's new position is ${newChannel.position}`)) - * .catch(console.error); - */ - public setPosition( - channel: GuildChannelResolvable, - position: number, - options?: SetChannelPositionOptions - ): Promise; - - /** - * Batch-updates the guild's channels' positions. - * Only one channel's parent can be changed at a time - * @param channelPositions Channel positions to update - * @example - * guild.channels.setPositions([{ channel: channelId, position: newChannelIndex }]) - * .then(guild => console.log(`Updated channel positions for ${guild}`)) - * .catch(console.error); - */ - public setPositions(channelPositions: readonly ChannelPosition[]): Promise; - - /** - * Obtains all active thread channels in the guild from Discord - * @param {} [cache=true] Whether to cache the fetched data - * @example - * // Fetch all threads from the guild - * message.guild.channels.fetchActiveThreads() - * .then(fetched => console.log(`There are ${fetched.threads.size} threads.`)) - * .catch(console.error); - */ - public fetchActiveThreads(cache?: boolean): Promise; - - /** - * Deletes the channel. - * @param channel The channel to delete - * @param reason Reason for deleting this channel - * @example - * // Delete the channel - * guild.channels.delete('858850993013260338', 'making room for new channels') - * .then(console.log) - * .catch(console.error); - */ - public delete(channel: GuildChannelResolvable, reason?: string): Promise; -} diff --git a/src/lib/extensions/discord.js/BushGuildEmoji.ts b/src/lib/extensions/discord.js/BushGuildEmoji.ts deleted file mode 100644 index 9b931bb..0000000 --- a/src/lib/extensions/discord.js/BushGuildEmoji.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { BushClient, BushGuild, BushGuildEmojiRoleManager, BushUser } from '#lib'; -import { GuildEmoji } from 'discord.js'; -import type { RawGuildEmojiData } from 'discord.js/typings/rawDataTypes'; - -/** - * Represents a custom emoji. - */ -export class BushGuildEmoji extends GuildEmoji { - public declare readonly client: BushClient; - public declare guild: BushGuild; - public declare author: BushUser | null; - - public constructor(client: BushClient, data: RawGuildEmojiData, guild: BushGuild) { - super(client, data, guild); - } -} - -export interface BushGuildEmoji extends GuildEmoji { - get roles(): BushGuildEmojiRoleManager; -} diff --git a/src/lib/extensions/discord.js/BushGuildEmojiRoleManager.ts b/src/lib/extensions/discord.js/BushGuildEmojiRoleManager.ts deleted file mode 100644 index 8b069ae..0000000 --- a/src/lib/extensions/discord.js/BushGuildEmojiRoleManager.ts +++ /dev/null @@ -1,55 +0,0 @@ -import type { BushClient, BushGuild, BushGuildEmoji, BushRole, BushRoleResolvable } from '#lib'; -import { DataManager, GuildEmojiRoleManager, type Collection, type Snowflake } from 'discord.js'; - -/** - * Manages API methods for roles belonging to emojis and stores their cache. - */ -export declare class BushGuildEmojiRoleManager - extends DataManager - implements GuildEmojiRoleManager -{ - public constructor(emoji: BushGuildEmoji); - public declare readonly client: BushClient; - - /** - * The emoji belonging to this manager - */ - public emoji: BushGuildEmoji; - - /** - * The guild belonging to this manager - */ - public guild: BushGuild; - - /** - * Adds a role (or multiple roles) to the list of roles that can use this emoji. - * @param roleOrRoles The role or roles to add - */ - public add( - roleOrRoles: BushRoleResolvable | readonly BushRoleResolvable[] | Collection - ): Promise; - - /** - * Sets the role(s) that can use this emoji. - * @param roles The roles or role ids to apply - * @example - * // Set the emoji's roles to a single role - * guildEmoji.roles.set(['391156570408615936']) - * .then(console.log) - * .catch(console.error); - * @example - * // Remove all roles from an emoji - * guildEmoji.roles.set([]) - * .then(console.log) - * .catch(console.error); - */ - public set(roles: readonly BushRoleResolvable[] | Collection): Promise; - - /** - * Removes a role (or multiple roles) from the list of roles that can use this emoji. - * @param roleOrRoles The role or roles to remove - */ - public remove( - roleOrRoles: BushRoleResolvable | readonly BushRoleResolvable[] | Collection - ): Promise; -} diff --git a/src/lib/extensions/discord.js/BushGuildManager.ts b/src/lib/extensions/discord.js/BushGuildManager.ts deleted file mode 100644 index 41618e3..0000000 --- a/src/lib/extensions/discord.js/BushGuildManager.ts +++ /dev/null @@ -1,35 +0,0 @@ -import type { BushClient, BushGuild, BushGuildResolvable } from '#lib'; -import { - CachedManager, - GuildManager, - type Collection, - type FetchGuildOptions, - type FetchGuildsOptions, - type GuildCreateOptions, - type OAuth2Guild, - type Snowflake -} from 'discord.js'; -import { type RawGuildData } from 'discord.js/typings/rawDataTypes'; - -/** - * Manages API methods for Guilds and stores their cache. - */ -export declare class BushGuildManager extends CachedManager implements GuildManager { - public constructor(client: BushClient, iterable?: Iterable); - - /** - * Creates a guild. - * This is only available to bots in fewer than 10 guilds. - * @param name The name of the guild - * @param options Options for creating the guild - * @returns The guild that was created - */ - public create(name: string, options?: GuildCreateOptions): Promise; - - /** - * Obtains one or multiple guilds from Discord, or the guild cache if it's already available. - * @param options The guild's id or options - */ - public fetch(options: Snowflake | FetchGuildOptions): Promise; - public fetch(options?: FetchGuildsOptions): Promise>; -} diff --git a/src/lib/extensions/discord.js/BushGuildMember.ts b/src/lib/extensions/discord.js/BushGuildMember.ts deleted file mode 100644 index 20a1f60..0000000 --- a/src/lib/extensions/discord.js/BushGuildMember.ts +++ /dev/null @@ -1,1159 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -import { - BushClientEvents, - Moderation, - ModLogType, - PunishmentTypeDM, - Time, - type BushClient, - type BushGuild, - type BushGuildTextBasedChannel, - type BushGuildTextChannelResolvable, - type BushRole, - type BushThreadChannelResolvable, - type BushUser -} from '#lib'; -import { GuildMember, PermissionFlagsBits, type Partialize, type Role } from 'discord.js'; -import type { RawGuildMemberData } from 'discord.js/typings/rawDataTypes'; -/* eslint-enable @typescript-eslint/no-unused-vars */ - -/** - * Represents a member of a guild on Discord. - */ -export class BushGuildMember extends GuildMember { - public declare readonly client: BushClient; - public declare guild: BushGuild; - public declare user: BushUser; - - public constructor(client: BushClient, data: RawGuildMemberData, guild: BushGuild) { - super(client, data, guild); - } - - /** - * Send a punishment dm to the user. - * @param modlog The modlog case id so the user can make an appeal. - * @param punishment The punishment that the user has received. - * @param reason The reason for the user's punishment. - * @param duration The duration of the punishment. - * @param sendFooter Whether or not to send the guild's punishment footer with the dm. - * @returns Whether or not the dm was sent successfully. - */ - public async bushPunishDM( - punishment: PunishmentTypeDM, - reason?: string | null, - duration?: number, - modlog?: string, - sendFooter = true - ): Promise { - return Moderation.punishDM({ - modlog, - guild: this.guild, - user: this, - punishment, - reason: reason ?? undefined, - duration, - sendFooter - }); - } - - /** - * Warn the user, create a modlog entry, and send a dm to the user. - * @param options Options for warning the user. - * @returns An object with the result of the warning, and the case number of the warn. - * @emits {@link BushClientEvents.bushWarn} - */ - public async bushWarn(options: BushPunishmentOptions): Promise<{ result: WarnResponse; caseNum: number | null }> { - let caseID: string | undefined = undefined; - let dmSuccessEvent: boolean | undefined = undefined; - const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); - if (!moderator) return { result: warnResponse.CANNOT_RESOLVE_USER, caseNum: null }; - - const ret = await (async (): Promise<{ result: WarnResponse; caseNum: number | null }> => { - // add modlog entry - const result = await Moderation.createModLogEntry( - { - type: ModLogType.WARN, - user: this, - moderator: moderator.id, - reason: options.reason, - guild: this.guild, - evidence: options.evidence, - hidden: options.silent ?? false - }, - true - ); - caseID = result.log?.id; - if (!result || !result.log) return { result: warnResponse.MODLOG_ERROR, caseNum: null }; - - if (!options.silent) { - // dm user - const dmSuccess = await this.bushPunishDM('warned', options.reason); - dmSuccessEvent = dmSuccess; - if (!dmSuccess) return { result: warnResponse.DM_ERROR, caseNum: result.caseNum }; - } - - return { result: warnResponse.SUCCESS, caseNum: result.caseNum }; - })(); - if (!([warnResponse.MODLOG_ERROR] as const).includes(ret.result) && !options.silent) - client.emit('bushWarn', this, moderator, this.guild, options.reason ?? undefined, caseID!, dmSuccessEvent!); - return ret; - } - - /** - * Add a role to the user, if it is a punishment create a modlog entry, and create a punishment entry if it is temporary or a punishment. - * @param options Options for adding a role to the user. - * @returns A status message for adding the add. - * @emits {@link BushClientEvents.bushPunishRole} - */ - public async bushAddRole(options: AddRoleOptions): Promise { - // checks - if (!this.guild.members.me!.permissions.has(PermissionFlagsBits.ManageRoles)) return addRoleResponse.MISSING_PERMISSIONS; - const ifShouldAddRole = this.#checkIfShouldAddRole(options.role, options.moderator); - if (ifShouldAddRole !== true) return ifShouldAddRole; - - let caseID: string | undefined = undefined; - const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); - if (!moderator) return addRoleResponse.CANNOT_RESOLVE_USER; - - const ret = await (async () => { - if (options.addToModlog || options.duration) { - const { log: modlog } = await Moderation.createModLogEntry({ - type: options.duration ? ModLogType.TEMP_PUNISHMENT_ROLE : ModLogType.PERM_PUNISHMENT_ROLE, - guild: this.guild, - moderator: moderator.id, - user: this, - reason: 'N/A', - pseudo: !options.addToModlog, - evidence: options.evidence, - hidden: options.silent ?? false - }); - - if (!modlog) return addRoleResponse.MODLOG_ERROR; - caseID = modlog.id; - - if (options.addToModlog || options.duration) { - const punishmentEntrySuccess = await Moderation.createPunishmentEntry({ - type: 'role', - user: this, - guild: this.guild, - modlog: modlog.id, - duration: options.duration, - extraInfo: options.role.id - }); - if (!punishmentEntrySuccess) return addRoleResponse.PUNISHMENT_ENTRY_ADD_ERROR; - } - } - - const removeRoleSuccess = await this.roles.add(options.role, `${moderator.tag}`); - if (!removeRoleSuccess) return addRoleResponse.ACTION_ERROR; - - return addRoleResponse.SUCCESS; - })(); - if ( - !( - [addRoleResponse.ACTION_ERROR, addRoleResponse.MODLOG_ERROR, addRoleResponse.PUNISHMENT_ENTRY_ADD_ERROR] as const - ).includes(ret) && - options.addToModlog && - !options.silent - ) - client.emit( - 'bushPunishRole', - this, - moderator, - this.guild, - options.reason ?? undefined, - caseID!, - options.duration ?? 0, - options.role as BushRole, - options.evidence - ); - return ret; - } - - /** - * Remove a role from the user, if it is a punishment create a modlog entry, and destroy a punishment entry if it was temporary or a punishment. - * @param options Options for removing a role from the user. - * @returns A status message for removing the role. - * @emits {@link BushClientEvents.bushPunishRoleRemove} - */ - public async bushRemoveRole(options: RemoveRoleOptions): Promise { - // checks - if (!this.guild.members.me!.permissions.has(PermissionFlagsBits.ManageRoles)) return removeRoleResponse.MISSING_PERMISSIONS; - const ifShouldAddRole = this.#checkIfShouldAddRole(options.role, options.moderator); - if (ifShouldAddRole !== true) return ifShouldAddRole; - - let caseID: string | undefined = undefined; - const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); - if (!moderator) return removeRoleResponse.CANNOT_RESOLVE_USER; - - const ret = await (async () => { - if (options.addToModlog) { - const { log: modlog } = await Moderation.createModLogEntry({ - type: ModLogType.REMOVE_PUNISHMENT_ROLE, - guild: this.guild, - moderator: moderator.id, - user: this, - reason: 'N/A', - evidence: options.evidence, - hidden: options.silent ?? false - }); - - if (!modlog) return removeRoleResponse.MODLOG_ERROR; - caseID = modlog.id; - - const punishmentEntrySuccess = await Moderation.removePunishmentEntry({ - type: 'role', - user: this, - guild: this.guild, - extraInfo: options.role.id - }); - - if (!punishmentEntrySuccess) return removeRoleResponse.PUNISHMENT_ENTRY_REMOVE_ERROR; - } - - const removeRoleSuccess = await this.roles.remove(options.role, `${moderator.tag}`); - if (!removeRoleSuccess) return removeRoleResponse.ACTION_ERROR; - - return removeRoleResponse.SUCCESS; - })(); - - if ( - !( - [ - removeRoleResponse.ACTION_ERROR, - removeRoleResponse.MODLOG_ERROR, - removeRoleResponse.PUNISHMENT_ENTRY_REMOVE_ERROR - ] as const - ).includes(ret) && - options.addToModlog && - !options.silent - ) - client.emit( - 'bushPunishRoleRemove', - this, - moderator, - this.guild, - options.reason ?? undefined, - caseID!, - options.role as BushRole, - options.evidence - ); - return ret; - } - - /** - * Check whether or not a role should be added/removed from the user based on hierarchy. - * @param role The role to check if can be modified. - * @param moderator The moderator that is trying to add/remove the role. - * @returns `true` if the role should be added/removed or a string for the reason why it shouldn't. - */ - #checkIfShouldAddRole( - role: BushRole | Role, - moderator?: BushGuildMember - ): true | 'user hierarchy' | 'role managed' | 'client hierarchy' { - if (moderator && moderator.roles.highest.position <= role.position && this.guild.ownerId !== this.user.id) { - return shouldAddRoleResponse.USER_HIERARCHY; - } else if (role.managed) { - return shouldAddRoleResponse.ROLE_MANAGED; - } else if (this.guild.members.me!.roles.highest.position <= role.position) { - return shouldAddRoleResponse.CLIENT_HIERARCHY; - } - return true; - } - - /** - * Mute the user, create a modlog entry, creates a punishment entry, and dms the user. - * @param options Options for muting the user. - * @returns A status message for muting the user. - * @emits {@link BushClientEvents.bushMute} - */ - public async bushMute(options: BushTimedPunishmentOptions): Promise { - // checks - if (!this.guild.members.me!.permissions.has(PermissionFlagsBits.ManageRoles)) return muteResponse.MISSING_PERMISSIONS; - const muteRoleID = await this.guild.getSetting('muteRole'); - if (!muteRoleID) return muteResponse.NO_MUTE_ROLE; - const muteRole = this.guild.roles.cache.get(muteRoleID); - if (!muteRole) return muteResponse.MUTE_ROLE_INVALID; - if (muteRole.position >= this.guild.members.me!.roles.highest.position || muteRole.managed) - return muteResponse.MUTE_ROLE_NOT_MANAGEABLE; - - let caseID: string | undefined = undefined; - let dmSuccessEvent: boolean | undefined = undefined; - const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); - if (!moderator) return muteResponse.CANNOT_RESOLVE_USER; - - const ret = await (async () => { - // add role - const muteSuccess = await this.roles - .add(muteRole, `[Mute] ${moderator.tag} | ${options.reason ?? 'No reason provided.'}`) - .catch(async (e) => { - await client.console.warn('muteRoleAddError', e); - client.console.debug(e); - return false; - }); - if (!muteSuccess) return muteResponse.ACTION_ERROR; - - // add modlog entry - const { log: modlog } = await Moderation.createModLogEntry({ - type: options.duration ? ModLogType.TEMP_MUTE : ModLogType.PERM_MUTE, - user: this, - moderator: moderator.id, - reason: options.reason, - duration: options.duration, - guild: this.guild, - evidence: options.evidence, - hidden: options.silent ?? false - }); - - if (!modlog) return muteResponse.MODLOG_ERROR; - caseID = modlog.id; - - // add punishment entry so they can be unmuted later - const punishmentEntrySuccess = await Moderation.createPunishmentEntry({ - type: 'mute', - user: this, - guild: this.guild, - duration: options.duration, - modlog: modlog.id - }); - - if (!punishmentEntrySuccess) return muteResponse.PUNISHMENT_ENTRY_ADD_ERROR; - - if (!options.silent) { - // dm user - const dmSuccess = await this.bushPunishDM('muted', options.reason, options.duration ?? 0, modlog.id); - dmSuccessEvent = dmSuccess; - if (!dmSuccess) return muteResponse.DM_ERROR; - } - - return muteResponse.SUCCESS; - })(); - - if ( - !([muteResponse.ACTION_ERROR, muteResponse.MODLOG_ERROR, muteResponse.PUNISHMENT_ENTRY_ADD_ERROR] as const).includes(ret) && - !options.silent - ) - client.emit( - 'bushMute', - this, - moderator, - this.guild, - options.reason ?? undefined, - caseID!, - options.duration ?? 0, - dmSuccessEvent!, - options.evidence - ); - return ret; - } - - /** - * Unmute the user, create a modlog entry, remove the punishment entry, and dm the user. - * @param options Options for unmuting the user. - * @returns A status message for unmuting the user. - * @emits {@link BushClientEvents.bushUnmute} - */ - public async bushUnmute(options: BushPunishmentOptions): Promise { - // checks - if (!this.guild.members.me!.permissions.has(PermissionFlagsBits.ManageRoles)) return unmuteResponse.MISSING_PERMISSIONS; - const muteRoleID = await this.guild.getSetting('muteRole'); - if (!muteRoleID) return unmuteResponse.NO_MUTE_ROLE; - const muteRole = this.guild.roles.cache.get(muteRoleID); - if (!muteRole) return unmuteResponse.MUTE_ROLE_INVALID; - if (muteRole.position >= this.guild.members.me!.roles.highest.position || muteRole.managed) - return unmuteResponse.MUTE_ROLE_NOT_MANAGEABLE; - - let caseID: string | undefined = undefined; - let dmSuccessEvent: boolean | undefined = undefined; - const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); - if (!moderator) return unmuteResponse.CANNOT_RESOLVE_USER; - - const ret = await (async () => { - // remove role - const muteSuccess = await this.roles - .remove(muteRole, `[Unmute] ${moderator.tag} | ${options.reason ?? 'No reason provided.'}`) - .catch(async (e) => { - await client.console.warn('muteRoleAddError', util.formatError(e, true)); - return false; - }); - if (!muteSuccess) return unmuteResponse.ACTION_ERROR; - - // add modlog entry - const { log: modlog } = await Moderation.createModLogEntry({ - type: ModLogType.UNMUTE, - user: this, - moderator: moderator.id, - reason: options.reason, - guild: this.guild, - evidence: options.evidence, - hidden: options.silent ?? false - }); - - if (!modlog) return unmuteResponse.MODLOG_ERROR; - caseID = modlog.id; - - // remove mute entry - const removePunishmentEntrySuccess = await Moderation.removePunishmentEntry({ - type: 'mute', - user: this, - guild: this.guild - }); - - if (!removePunishmentEntrySuccess) return unmuteResponse.PUNISHMENT_ENTRY_REMOVE_ERROR; - - if (!options.silent) { - // dm user - const dmSuccess = await this.bushPunishDM('unmuted', options.reason, undefined, '', false); - dmSuccessEvent = dmSuccess; - if (!dmSuccess) return unmuteResponse.DM_ERROR; - } - - return unmuteResponse.SUCCESS; - })(); - - if ( - !( - [unmuteResponse.ACTION_ERROR, unmuteResponse.MODLOG_ERROR, unmuteResponse.PUNISHMENT_ENTRY_REMOVE_ERROR] as const - ).includes(ret) && - !options.silent - ) - client.emit( - 'bushUnmute', - this, - moderator, - this.guild, - options.reason ?? undefined, - caseID!, - dmSuccessEvent!, - options.evidence - ); - return ret; - } - - /** - * Kick the user, create a modlog entry, and dm the user. - * @param options Options for kicking the user. - * @returns A status message for kicking the user. - * @emits {@link BushClientEvents.bushKick} - */ - public async bushKick(options: BushPunishmentOptions): Promise { - // checks - if (!this.guild.members.me?.permissions.has(PermissionFlagsBits.KickMembers) || !this.kickable) - return kickResponse.MISSING_PERMISSIONS; - - let caseID: string | undefined = undefined; - let dmSuccessEvent: boolean | undefined = undefined; - const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); - if (!moderator) return kickResponse.CANNOT_RESOLVE_USER; - const ret = await (async () => { - // add modlog entry - const { log: modlog } = await Moderation.createModLogEntry({ - type: ModLogType.KICK, - user: this, - moderator: moderator.id, - reason: options.reason, - guild: this.guild, - evidence: options.evidence, - hidden: options.silent ?? false - }); - if (!modlog) return kickResponse.MODLOG_ERROR; - caseID = modlog.id; - - // dm user - const dmSuccess = options.silent ? null : await this.bushPunishDM('kicked', options.reason, undefined, modlog.id); - dmSuccessEvent = dmSuccess ?? undefined; - - // kick - const kickSuccess = await this.kick(`${moderator?.tag} | ${options.reason ?? 'No reason provided.'}`).catch(() => false); - if (!kickSuccess) return kickResponse.ACTION_ERROR; - - if (dmSuccess === false) return kickResponse.DM_ERROR; - return kickResponse.SUCCESS; - })(); - if (!([kickResponse.ACTION_ERROR, kickResponse.MODLOG_ERROR] as const).includes(ret) && !options.silent) - client.emit( - 'bushKick', - this, - moderator, - this.guild, - options.reason ?? undefined, - caseID!, - dmSuccessEvent!, - options.evidence - ); - return ret; - } - - /** - * Ban the user, create a modlog entry, create a punishment entry, and dm the user. - * @param options Options for banning the user. - * @returns A status message for banning the user. - * @emits {@link BushClientEvents.bushBan} - */ - public async bushBan(options: BushBanOptions): Promise> { - // checks - if (!this.guild.members.me!.permissions.has(PermissionFlagsBits.BanMembers) || !this.bannable) - return banResponse.MISSING_PERMISSIONS; - - let caseID: string | undefined = undefined; - let dmSuccessEvent: boolean | undefined = undefined; - const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); - if (!moderator) return banResponse.CANNOT_RESOLVE_USER; - - // ignore result, they should still be banned even if their mute cannot be removed - await this.bushUnmute({ - reason: 'User is about to be banned, a mute is no longer necessary.', - moderator: this.guild.members.me!, - silent: true - }); - - const ret = await (async () => { - // add modlog entry - const { log: modlog } = await Moderation.createModLogEntry({ - type: options.duration ? ModLogType.TEMP_BAN : ModLogType.PERM_BAN, - user: this, - moderator: moderator.id, - reason: options.reason, - duration: options.duration, - guild: this.guild, - evidence: options.evidence, - hidden: options.silent ?? false - }); - if (!modlog) return banResponse.MODLOG_ERROR; - caseID = modlog.id; - - // dm user - const dmSuccess = options.silent - ? null - : await this.bushPunishDM('banned', options.reason, options.duration ?? 0, modlog.id); - dmSuccessEvent = dmSuccess ?? undefined; - - // ban - const banSuccess = await this.ban({ - reason: `${moderator.tag} | ${options.reason ?? 'No reason provided.'}`, - deleteMessageDays: options.deleteDays - }).catch(() => false); - if (!banSuccess) return banResponse.ACTION_ERROR; - - // add punishment entry so they can be unbanned later - const punishmentEntrySuccess = await Moderation.createPunishmentEntry({ - type: 'ban', - user: this, - guild: this.guild, - duration: options.duration, - modlog: modlog.id - }); - if (!punishmentEntrySuccess) return banResponse.PUNISHMENT_ENTRY_ADD_ERROR; - - if (!dmSuccess) return banResponse.DM_ERROR; - return banResponse.SUCCESS; - })(); - if ( - !([banResponse.ACTION_ERROR, banResponse.MODLOG_ERROR, banResponse.PUNISHMENT_ENTRY_ADD_ERROR] as const).includes(ret) && - !options.silent - ) - client.emit( - 'bushBan', - this, - moderator, - this.guild, - options.reason ?? undefined, - caseID!, - options.duration ?? 0, - dmSuccessEvent!, - options.evidence - ); - return ret; - } - - /** - * Prevents a user from speaking in a channel. - * @param options Options for blocking the user. - */ - public async bushBlock(options: BlockOptions): Promise { - const channel = this.guild.channels.resolve(options.channel); - if (!channel || (!channel.isTextBased() && !channel.isThread())) return blockResponse.INVALID_CHANNEL; - - // checks - if (!channel.permissionsFor(this.guild.members.me!)!.has(PermissionFlagsBits.ManageChannels)) - return blockResponse.MISSING_PERMISSIONS; - - let caseID: string | undefined = undefined; - let dmSuccessEvent: boolean | undefined = undefined; - const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); - if (!moderator) return blockResponse.CANNOT_RESOLVE_USER; - - const ret = await (async () => { - // change channel permissions - const channelToUse = channel.isThread() ? channel.parent! : channel; - const perm = channel.isThread() ? { SendMessagesInThreads: false } : { SendMessages: false }; - const blockSuccess = await channelToUse.permissionOverwrites - .edit(this, perm, { reason: `[Block] ${moderator.tag} | ${options.reason ?? 'No reason provided.'}` }) - .catch(() => false); - if (!blockSuccess) return blockResponse.ACTION_ERROR; - - // add modlog entry - const { log: modlog } = await Moderation.createModLogEntry({ - type: options.duration ? ModLogType.TEMP_CHANNEL_BLOCK : ModLogType.PERM_CHANNEL_BLOCK, - user: this, - moderator: moderator.id, - reason: options.reason, - guild: this.guild, - evidence: options.evidence, - hidden: options.silent ?? false - }); - if (!modlog) return blockResponse.MODLOG_ERROR; - caseID = modlog.id; - - // add punishment entry so they can be unblocked later - const punishmentEntrySuccess = await Moderation.createPunishmentEntry({ - type: 'block', - user: this, - guild: this.guild, - duration: options.duration, - modlog: modlog.id, - extraInfo: channel.id - }); - if (!punishmentEntrySuccess) return blockResponse.PUNISHMENT_ENTRY_ADD_ERROR; - - // dm user - const dmSuccess = options.silent - ? null - : await Moderation.punishDM({ - punishment: 'blocked', - reason: options.reason ?? undefined, - duration: options.duration ?? 0, - modlog: modlog.id, - guild: this.guild, - user: this, - sendFooter: true, - channel: channel.id - }); - dmSuccessEvent = !!dmSuccess; - if (!dmSuccess) return blockResponse.DM_ERROR; - - return blockResponse.SUCCESS; - })(); - - if ( - !([blockResponse.ACTION_ERROR, blockResponse.MODLOG_ERROR, blockResponse.PUNISHMENT_ENTRY_ADD_ERROR] as const).includes( - ret - ) && - !options.silent - ) - client.emit( - 'bushBlock', - this, - moderator, - this.guild, - options.reason ?? undefined, - caseID!, - options.duration ?? 0, - dmSuccessEvent!, - channel, - options.evidence - ); - return ret; - } - - /** - * Allows a user to speak in a channel. - * @param options Options for unblocking the user. - */ - public async bushUnblock(options: UnblockOptions): Promise { - const _channel = this.guild.channels.resolve(options.channel); - if (!_channel || (!_channel.isText() && !_channel.isThread())) return unblockResponse.INVALID_CHANNEL; - const channel = _channel as BushGuildTextBasedChannel; - - // checks - if (!channel.permissionsFor(this.guild.members.me!)!.has(PermissionFlagsBits.ManageChannels)) - return unblockResponse.MISSING_PERMISSIONS; - - let caseID: string | undefined = undefined; - let dmSuccessEvent: boolean | undefined = undefined; - const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); - if (!moderator) return unblockResponse.CANNOT_RESOLVE_USER; - - const ret = await (async () => { - // change channel permissions - const channelToUse = channel.isThread() ? channel.parent! : channel; - const perm = channel.isThread() ? { SendMessagesInThreads: null } : { SendMessages: null }; - const blockSuccess = await channelToUse.permissionOverwrites - .edit(this, perm, { reason: `[Unblock] ${moderator.tag} | ${options.reason ?? 'No reason provided.'}` }) - .catch(() => false); - if (!blockSuccess) return unblockResponse.ACTION_ERROR; - - // add modlog entry - const { log: modlog } = await Moderation.createModLogEntry({ - type: ModLogType.CHANNEL_UNBLOCK, - user: this, - moderator: moderator.id, - reason: options.reason, - guild: this.guild, - evidence: options.evidence, - hidden: options.silent ?? false - }); - if (!modlog) return unblockResponse.MODLOG_ERROR; - caseID = modlog.id; - - // remove punishment entry - const punishmentEntrySuccess = await Moderation.removePunishmentEntry({ - type: 'block', - user: this, - guild: this.guild, - extraInfo: channel.id - }); - if (!punishmentEntrySuccess) return unblockResponse.ACTION_ERROR; - - // dm user - const dmSuccess = options.silent - ? null - : await Moderation.punishDM({ - punishment: 'unblocked', - reason: options.reason ?? undefined, - guild: this.guild, - user: this, - sendFooter: false, - channel: channel.id - }); - dmSuccessEvent = !!dmSuccess; - if (!dmSuccess) return blockResponse.DM_ERROR; - - dmSuccessEvent = !!dmSuccess; - if (!dmSuccess) return unblockResponse.DM_ERROR; - - return unblockResponse.SUCCESS; - })(); - - if ( - !([unblockResponse.ACTION_ERROR, unblockResponse.MODLOG_ERROR, unblockResponse.ACTION_ERROR] as const).includes(ret) && - !options.silent - ) - client.emit( - 'bushUnblock', - this, - moderator, - this.guild, - options.reason ?? undefined, - caseID!, - dmSuccessEvent!, - channel, - options.evidence - ); - return ret; - } - - /** - * Mutes a user using discord's timeout feature. - * @param options Options for timing out the user. - */ - public async bushTimeout(options: BushTimeoutOptions): Promise { - // checks - if (!this.guild.members.me!.permissions.has(PermissionFlagsBits.ModerateMembers)) return timeoutResponse.MISSING_PERMISSIONS; - - const twentyEightDays = Time.Day * 28; - if (options.duration > twentyEightDays) return timeoutResponse.INVALID_DURATION; - - let caseID: string | undefined = undefined; - let dmSuccessEvent: boolean | undefined = undefined; - const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); - if (!moderator) return timeoutResponse.CANNOT_RESOLVE_USER; - - const ret = await (async () => { - // timeout - const timeoutSuccess = await this.timeout( - options.duration, - `${moderator.tag} | ${options.reason ?? 'No reason provided.'}` - ).catch(() => false); - if (!timeoutSuccess) return timeoutResponse.ACTION_ERROR; - - // add modlog entry - const { log: modlog } = await Moderation.createModLogEntry({ - type: ModLogType.TIMEOUT, - user: this, - moderator: moderator.id, - reason: options.reason, - duration: options.duration, - guild: this.guild, - evidence: options.evidence, - hidden: options.silent ?? false - }); - - if (!modlog) return timeoutResponse.MODLOG_ERROR; - caseID = modlog.id; - - if (!options.silent) { - // dm user - const dmSuccess = await this.bushPunishDM('timedout', options.reason, options.duration, modlog.id); - dmSuccessEvent = dmSuccess; - if (!dmSuccess) return timeoutResponse.DM_ERROR; - } - - return timeoutResponse.SUCCESS; - })(); - - if (!([timeoutResponse.ACTION_ERROR, timeoutResponse.MODLOG_ERROR] as const).includes(ret) && !options.silent) - client.emit( - 'bushTimeout', - this, - moderator, - this.guild, - options.reason ?? undefined, - caseID!, - options.duration ?? 0, - dmSuccessEvent!, - options.evidence - ); - return ret; - } - - /** - * Removes a timeout from a user. - * @param options Options for removing the timeout. - */ - public async bushRemoveTimeout(options: BushPunishmentOptions): Promise { - // checks - if (!this.guild.members.me!.permissions.has(PermissionFlagsBits.ModerateMembers)) - return removeTimeoutResponse.MISSING_PERMISSIONS; - - let caseID: string | undefined = undefined; - let dmSuccessEvent: boolean | undefined = undefined; - const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); - if (!moderator) return removeTimeoutResponse.CANNOT_RESOLVE_USER; - - const ret = await (async () => { - // remove timeout - const timeoutSuccess = await this.timeout(null, `${moderator.tag} | ${options.reason ?? 'No reason provided.'}`).catch( - () => false - ); - if (!timeoutSuccess) return removeTimeoutResponse.ACTION_ERROR; - - // add modlog entry - const { log: modlog } = await Moderation.createModLogEntry({ - type: ModLogType.REMOVE_TIMEOUT, - user: this, - moderator: moderator.id, - reason: options.reason, - guild: this.guild, - evidence: options.evidence, - hidden: options.silent ?? false - }); - - if (!modlog) return removeTimeoutResponse.MODLOG_ERROR; - caseID = modlog.id; - - if (!options.silent) { - // dm user - const dmSuccess = await this.bushPunishDM('untimedout', options.reason, undefined, '', false); - dmSuccessEvent = dmSuccess; - if (!dmSuccess) return removeTimeoutResponse.DM_ERROR; - } - - return removeTimeoutResponse.SUCCESS; - })(); - - if (!([removeTimeoutResponse.ACTION_ERROR, removeTimeoutResponse.MODLOG_ERROR] as const).includes(ret) && !options.silent) - client.emit( - 'bushRemoveTimeout', - this, - moderator, - this.guild, - options.reason ?? undefined, - caseID!, - dmSuccessEvent!, - options.evidence - ); - return ret; - } - - /** - * Whether or not the user is an owner of the bot. - */ - public isOwner(): boolean { - return client.isOwner(this); - } - - /** - * Whether or not the user is a super user of the bot. - */ - public isSuperUser(): boolean { - return client.isSuperUser(this); - } -} - -/** - * Options for punishing a user. - */ -export interface BushPunishmentOptions { - /** - * The reason for the punishment. - */ - reason?: string | null; - - /** - * The moderator who punished the user. - */ - moderator?: BushGuildMember; - - /** - * Evidence for the punishment. - */ - evidence?: string; - - /** - * Makes the punishment silent by not sending the user a punishment dm and not broadcasting the event to be logged. - */ - silent?: boolean; -} - -/** - * Punishment options for punishments that can be temporary. - */ -export interface BushTimedPunishmentOptions extends BushPunishmentOptions { - /** - * The duration of the punishment. - */ - duration?: number; -} - -/** - * Options for a role add punishment. - */ -export interface AddRoleOptions extends BushTimedPunishmentOptions { - /** - * The role to add to the user. - */ - role: BushRole | Role; - - /** - * Whether to create a modlog entry for this punishment. - */ - addToModlog: boolean; -} - -/** - * Options for a role remove punishment. - */ -export interface RemoveRoleOptions extends BushTimedPunishmentOptions { - /** - * The role to remove from the user. - */ - role: BushRole | Role; - - /** - * Whether to create a modlog entry for this punishment. - */ - addToModlog: boolean; -} - -/** - * Options for banning a user. - */ -export interface BushBanOptions extends BushTimedPunishmentOptions { - /** - * The number of days to delete the user's messages for. - */ - deleteDays?: number; -} - -/** - * Options for blocking a user from a channel. - */ -export interface BlockOptions extends BushTimedPunishmentOptions { - /** - * The channel to block the user from. - */ - channel: BushGuildTextChannelResolvable | BushThreadChannelResolvable; -} - -/** - * Options for unblocking a user from a channel. - */ -export interface UnblockOptions extends BushPunishmentOptions { - /** - * The channel to unblock the user from. - */ - channel: BushGuildTextChannelResolvable | BushThreadChannelResolvable; -} - -/** - * Punishment options for punishments that can be temporary. - */ -export interface BushTimeoutOptions extends BushPunishmentOptions { - /** - * The duration of the punishment. - */ - duration: number; -} - -type ValueOf = T[keyof T]; - -export const basePunishmentResponse = Object.freeze({ - SUCCESS: 'success', - MODLOG_ERROR: 'error creating modlog entry', - ACTION_ERROR: 'error performing action', - CANNOT_RESOLVE_USER: 'cannot resolve user' -} as const); - -export const dmResponse = Object.freeze({ - ...basePunishmentResponse, - DM_ERROR: 'failed to dm' -} as const); - -export const permissionsResponse = Object.freeze({ - MISSING_PERMISSIONS: 'missing permissions' -} as const); - -export const punishmentEntryAdd = Object.freeze({ - PUNISHMENT_ENTRY_ADD_ERROR: 'error creating punishment entry' -} as const); - -export const punishmentEntryRemove = Object.freeze({ - PUNISHMENT_ENTRY_REMOVE_ERROR: 'error removing punishment entry' -} as const); - -export const shouldAddRoleResponse = Object.freeze({ - USER_HIERARCHY: 'user hierarchy', - CLIENT_HIERARCHY: 'client hierarchy', - ROLE_MANAGED: 'role managed' -} as const); - -export const baseBlockResponse = Object.freeze({ - INVALID_CHANNEL: 'invalid channel' -} as const); - -export const baseMuteResponse = Object.freeze({ - NO_MUTE_ROLE: 'no mute role', - MUTE_ROLE_INVALID: 'invalid mute role', - MUTE_ROLE_NOT_MANAGEABLE: 'mute role not manageable' -} as const); - -export const warnResponse = Object.freeze({ - ...dmResponse -} as const); - -export const addRoleResponse = Object.freeze({ - ...basePunishmentResponse, - ...permissionsResponse, - ...shouldAddRoleResponse, - ...punishmentEntryAdd -} as const); - -export const removeRoleResponse = Object.freeze({ - ...basePunishmentResponse, - ...permissionsResponse, - ...shouldAddRoleResponse, - ...punishmentEntryRemove -} as const); - -export const muteResponse = Object.freeze({ - ...dmResponse, - ...permissionsResponse, - ...baseMuteResponse, - ...punishmentEntryAdd -} as const); - -export const unmuteResponse = Object.freeze({ - ...dmResponse, - ...permissionsResponse, - ...baseMuteResponse, - ...punishmentEntryRemove -} as const); - -export const kickResponse = Object.freeze({ - ...dmResponse, - ...permissionsResponse -} as const); - -export const banResponse = Object.freeze({ - ...dmResponse, - ...permissionsResponse, - ...punishmentEntryAdd, - ALREADY_BANNED: 'already banned' -} as const); - -export const blockResponse = Object.freeze({ - ...dmResponse, - ...permissionsResponse, - ...baseBlockResponse, - ...punishmentEntryAdd -}); - -export const unblockResponse = Object.freeze({ - ...dmResponse, - ...permissionsResponse, - ...baseBlockResponse, - ...punishmentEntryRemove -}); - -export const timeoutResponse = Object.freeze({ - ...dmResponse, - ...permissionsResponse, - INVALID_DURATION: 'duration too long' -} as const); - -export const removeTimeoutResponse = Object.freeze({ - ...dmResponse, - ...permissionsResponse -} as const); - -/** - * Response returned when warning a user. - */ -export type WarnResponse = ValueOf; - -/** - * Response returned when adding a role to a user. - */ -export type AddRoleResponse = ValueOf; - -/** - * Response returned when removing a role from a user. - */ -export type RemoveRoleResponse = ValueOf; - -/** - * Response returned when muting a user. - */ -export type MuteResponse = ValueOf; - -/** - * Response returned when unmuting a user. - */ -export type UnmuteResponse = ValueOf; - -/** - * Response returned when kicking a user. - */ -export type KickResponse = ValueOf; - -/** - * Response returned when banning a user. - */ -export type BanResponse = ValueOf; - -/** - * Response returned when blocking a user. - */ -export type BlockResponse = ValueOf; - -/** - * Response returned when unblocking a user. - */ -export type UnblockResponse = ValueOf; - -/** - * Response returned when timing out a user. - */ -export type TimeoutResponse = ValueOf; - -/** - * Response returned when removing a timeout from a user. - */ -export type RemoveTimeoutResponse = ValueOf; - -export type PartialBushGuildMember = Partialize; - -/** - * @typedef {BushClientEvents} VSCodePleaseDontRemove - */ diff --git a/src/lib/extensions/discord.js/BushGuildMemberManager.ts b/src/lib/extensions/discord.js/BushGuildMemberManager.ts deleted file mode 100644 index b0368b5..0000000 --- a/src/lib/extensions/discord.js/BushGuildMemberManager.ts +++ /dev/null @@ -1,177 +0,0 @@ -import type { BushClient, BushGuild, BushGuildMember, BushGuildMemberResolvable, BushUser, BushUserResolvable } from '#lib'; -import { - CachedManager, - GuildMemberManager, - type AddGuildMemberOptions, - type BanOptions, - type Collection, - type FetchMemberOptions, - type FetchMembersOptions, - type GuildListMembersOptions, - type GuildMemberEditData, - type GuildPruneMembersOptions, - type GuildSearchMembersOptions, - type Snowflake -} from 'discord.js'; -import type { RawGuildMemberData } from 'discord.js/typings/rawDataTypes'; - -/** - * Manages API methods for GuildMembers and stores their cache. - */ -export declare class BushGuildMemberManager - extends CachedManager - implements GuildMemberManager -{ - public constructor(guild: BushGuild, iterable?: Iterable); - public declare readonly client: BushClient; - - /** - * The guild this manager belongs to - */ - public guild: BushGuild; - - /** - * Adds a user to the guild using OAuth2. Requires the `PermissionFlagsBits.CreateInstantInvite` permission. - * @param user The user to add to the guild - * @param options Options for adding the user to the guild - */ - public add( - user: BushUserResolvable, - options: AddGuildMemberOptions & { fetchWhenExisting: false } - ): Promise; - public add(user: BushUserResolvable, options: AddGuildMemberOptions): Promise; - - /** - * Bans a user from the guild. - * @param user The user to ban - * @param options Options for the ban - * @returns Result object will be resolved as specifically as possible. - * If the GuildMember cannot be resolved, the User will instead be attempted to be resolved. If that also cannot - * be resolved, the user id will be the result. - * Internally calls the GuildBanManager#create method. - * @example - * // Ban a user by id (or with a user/guild member object) - * guild.members.ban('84484653687267328') - * .then(banInfo => console.log(`Banned ${banInfo.user?.tag ?? banInfo.tag ?? banInfo}`)) - * .catch(console.error); - */ - public ban(user: BushUserResolvable, options?: BanOptions): Promise; - - /** - * Edits a member of the guild. - * The user must be a member of the guild - * @param user The member to edit - * @param data The data to edit the member with - * @param reason Reason for editing this user - */ - public edit(user: BushUserResolvable, data: GuildMemberEditData, reason?: string): Promise; - - /** - * Fetches member(s) from Discord, even if they're offline. - * @param options If a UserResolvable, the user to fetch. - * If undefined, fetches all members. - * If a query, it limits the results to users with similar usernames. - * @example - * // Fetch all members from a guild - * guild.members.fetch() - * .then(console.log) - * .catch(console.error); - * @example - * // Fetch a single member - * guild.members.fetch('66564597481480192') - * .then(console.log) - * .catch(console.error); - * @example - * // Fetch a single member without checking cache - * guild.members.fetch({ user, force: true }) - * .then(console.log) - * .catch(console.error) - * @example - * // Fetch a single member without caching - * guild.members.fetch({ user, cache: false }) - * .then(console.log) - * .catch(console.error); - * @example - * // Fetch by an array of users including their presences - * guild.members.fetch({ user: ['66564597481480192', '191615925336670208'], withPresences: true }) - * .then(console.log) - * .catch(console.error); - * @example - * // Fetch by query - * guild.members.fetch({ query: 'hydra', limit: 1 }) - * .then(console.log) - * .catch(console.error); - */ - public fetch( - options: BushUserResolvable | FetchMemberOptions | (FetchMembersOptions & { user: BushUserResolvable }) - ): Promise; - public fetch(options?: FetchMembersOptions): Promise>; - - /** - * Kicks a user from the guild. - * The user must be a member of the guild - * @param user The member to kick - * @param reason Reason for kicking - * @returns Result object will be resolved as specifically as possible. - * If the GuildMember cannot be resolved, the User will instead be attempted to be resolved. If that also cannot - * be resolved, the user's id will be the result. - * @example - * // Kick a user by id (or with a user/guild member object) - * guild.members.kick('84484653687267328') - * .then(banInfo => console.log(`Kicked ${banInfo.user?.tag ?? banInfo.tag ?? banInfo}`)) - * .catch(console.error); - */ - public kick(user: BushUserResolvable, reason?: string): Promise; - - /** - * Lists up to 1000 members of the guild. - * @param options Options for listing members - */ - public list(options?: GuildListMembersOptions): Promise>; - - /** - * Prunes members from the guild based on how long they have been inactive. - * @param options Options for pruning - * @returns The number of members that were/will be kicked - * @example - * // See how many members will be pruned - * guild.members.prune({ dry: true }) - * .then(pruned => console.log(`This will prune ${pruned} people!`)) - * .catch(console.error); - * @example - * // Actually prune the members - * guild.members.prune({ days: 1, reason: 'too many people!' }) - * .then(pruned => console.log(`I just pruned ${pruned} people!`)) - * .catch(console.error); - * @example - * // Include members with a specified role - * guild.members.prune({ days: 7, roles: ['657259391652855808'] }) - * .then(pruned => console.log(`I just pruned ${pruned} people!`)) - * .catch(console.error); - */ - public prune(options: GuildPruneMembersOptions & { dry?: false; count: false }): Promise; - public prune(options?: GuildPruneMembersOptions): Promise; - - /** - * Searches for members in the guild based on a query. - * @param options Options for searching members - */ - public search(options: GuildSearchMembersOptions): Promise>; - - /** - * Unbans a user from the guild. Internally calls the {@link GuildBanManager.remove} method. - * @param user The user to unban - * @param reason Reason for unbanning user - * @returns The user that was unbanned - * @example - * // Unban a user by id (or with a user/guild member object) - * guild.members.unban('84484653687267328') - * .then(user => console.log(`Unbanned ${user.username} from ${guild.name}`)) - * .catch(console.error); - */ - public unban(user: BushUserResolvable, reason?: string): Promise; -} - -export interface BushGuildMemberManager extends CachedManager { - get me(): BushGuildMember | null; -} diff --git a/src/lib/extensions/discord.js/BushMessage.ts b/src/lib/extensions/discord.js/BushMessage.ts deleted file mode 100644 index 48e1792..0000000 --- a/src/lib/extensions/discord.js/BushMessage.ts +++ /dev/null @@ -1,63 +0,0 @@ -import type { - BushClient, - BushCommandUtil, - BushGuild, - BushGuildMember, - BushGuildTextBasedChannel, - BushMessageReaction, - BushTextBasedChannel, - BushThreadChannel, - BushUser -} from '#lib'; -import { - Message, - MessageActionRowComponent, - type EmojiIdentifierResolvable, - type If, - type MessageEditOptions, - type MessagePayload, - type Partialize, - type ReplyMessageOptions, - type StartThreadOptions -} from 'discord.js'; -import type { RawMessageData } from 'discord.js/typings/rawDataTypes'; - -export type PartialBushMessage = Partialize< - BushMessage, - 'type' | 'system' | 'pinned' | 'tts', - 'content' | 'cleanContent' | 'author' ->; - -/** - * Represents a message on Discord. - */ -export class BushMessage extends Message { - public declare readonly client: BushClient; - public declare util: BushCommandUtil>; - public declare author: BushUser; - - public constructor(client: BushClient, data: RawMessageData) { - super(client, data); - } -} - -export interface BushMessage extends Message { - get guild(): If; - get member(): BushGuildMember | null; - get channel(): If; - delete(): Promise; - edit(content: string | MessageEditOptions | MessagePayload): Promise; - equals(message: BushMessage, rawData: unknown): boolean; - fetchReference(): Promise; - crosspost(): Promise; - fetch(force?: boolean): Promise; - pin(): Promise; - react(emoji: EmojiIdentifierResolvable): Promise; - removeAttachments(): Promise; - reply(options: string | MessagePayload | ReplyMessageOptions): Promise; - resolveComponent(customId: string): MessageActionRowComponent | null; - startThread(options: StartThreadOptions): Promise; - suppressEmbeds(suppress?: boolean): Promise; - unpin(): Promise; - inGuild(): this is BushMessage & this; -} diff --git a/src/lib/extensions/discord.js/BushMessageManager.ts b/src/lib/extensions/discord.js/BushMessageManager.ts deleted file mode 100644 index edb7982..0000000 --- a/src/lib/extensions/discord.js/BushMessageManager.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { BushMessageResolvable, BushTextBasedChannel, type BushMessage } from '#lib'; -import { - CachedManager, - FetchMessageOptions, - FetchMessagesOptions, - MessageManager, - type Collection, - type EmojiIdentifierResolvable, - type MessageEditOptions, - type MessagePayload, - type Snowflake -} from 'discord.js'; -import type { RawMessageData } from 'discord.js/typings/rawDataTypes'; - -/** - * Manages API methods for Messages and holds their cache. - */ -export declare class BushMessageManager - extends CachedManager - implements MessageManager -{ - public constructor(channel: BushTextBasedChannel, iterable?: Iterable); - - /** - * The channel that the messages belong to - */ - public channel: BushTextBasedChannel; - - /** - * The cache of Messages - */ - public get cache(): Collection; - - /** - * Publishes a message in an announcement channel to all channels following it, even if it's not cached. - * @param message The message to publish - */ - public crosspost(message: BushMessageResolvable): Promise; - - /** - * Deletes a message, even if it's not cached. - * @param message The message to delete - */ - public delete(message: BushMessageResolvable): Promise; - - /** - * Edits a message, even if it's not cached. - * @param message The message to edit - * @param options The options to edit the message - */ - public edit(message: BushMessageResolvable, options: string | MessagePayload | MessageEditOptions): Promise; - - /** - * Gets a message, or messages, from this channel. - * The returned Collection does not contain reaction users of the messages if they were not cached. - * Those need to be fetched separately in such a case. - * @param message The id of the message to fetch, or query parameters. - * @param options Additional options for this fetch - * @example - * // Get message - * channel.messages.fetch('99539446449315840') - * .then(message => console.log(message.content)) - * .catch(console.error); - * @example - * // Get messages - * channel.messages.fetch({ limit: 10 }) - * .then(messages => console.log(`Received ${messages.size} messages`)) - * .catch(console.error); - * @example - * // Get messages and filter by user id - * channel.messages.fetch() - * .then(messages => console.log(`${messages.filter(m => m.author.id === '84484653687267328').size} messages`)) - * .catch(console.error); - */ - public fetch(options: BushMessageResolvable | FetchMessageOptions): Promise; - public fetch(options?: FetchMessagesOptions): Promise>; - - /** - * Fetches the pinned messages of this channel and returns a collection of them. - * The returned Collection does not contain any reaction data of the messages. - * Those need to be fetched separately. - * @param {} [cache=true] Whether to cache the message(s) - * @example - * // Get pinned messages - * channel.messages.fetchPinned() - * .then(messages => console.log(`Received ${messages.size} messages`)) - * .catch(console.error); - */ - public fetchPinned(cache?: boolean): Promise>; - - /** - * Adds a reaction to a message, even if it's not cached. - * @param message The message to react to - * @param emoji The emoji to react with - */ - public react(message: BushMessageResolvable, emoji: EmojiIdentifierResolvable): Promise; - - /** - * Pins a message to the channel's pinned messages, even if it's not cached. - * @param message The message to pin - */ - public pin(message: BushMessageResolvable): Promise; - - /** - * Unpins a message from the channel's pinned messages, even if it's not cached. - * @param message The message to unpin - */ - public unpin(message: BushMessageResolvable): Promise; -} - -export interface BushFetchMessageOptions extends FetchMessageOptions { - message: BushMessageResolvable; -} diff --git a/src/lib/extensions/discord.js/BushMessageReaction.ts b/src/lib/extensions/discord.js/BushMessageReaction.ts deleted file mode 100644 index 7fe110d..0000000 --- a/src/lib/extensions/discord.js/BushMessageReaction.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { BushClient, BushGuildEmoji, BushMessage, BushReactionEmoji } from '#lib'; -import { MessageReaction, type Partialize } from 'discord.js'; -import type { RawMessageReactionData } from 'discord.js/typings/rawDataTypes'; - -export type PartialBushMessageReaction = Partialize; - -/** - * Represents a reaction to a message. - */ -export class BushMessageReaction extends MessageReaction { - public declare readonly client: BushClient; - - public constructor(client: BushClient, data: RawMessageReactionData, message: BushMessage) { - super(client, data, message); - } -} - -export interface BushMessageReaction extends MessageReaction { - get emoji(): BushGuildEmoji | BushReactionEmoji; -} diff --git a/src/lib/extensions/discord.js/BushModalSubmitInteraction.ts b/src/lib/extensions/discord.js/BushModalSubmitInteraction.ts deleted file mode 100644 index 9bdc9e5..0000000 --- a/src/lib/extensions/discord.js/BushModalSubmitInteraction.ts +++ /dev/null @@ -1,96 +0,0 @@ -import type { - BushClient, - BushGuild, - BushGuildCacheMessage, - BushGuildMember, - BushGuildTextBasedChannel, - BushTextBasedChannel, - BushUser -} from '#lib'; -import type { APIInteractionGuildMember, APIModalSubmitInteraction } from 'discord-api-types/v10'; -import { - InteractionDeferUpdateOptions, - InteractionResponse, - InteractionUpdateOptions, - MessagePayload, - ModalSubmitInteraction, - type CacheType, - type CacheTypeReducer -} from 'discord.js'; - -/** - * Represents a button interaction. - */ -export class BushModalSubmitInteraction extends ModalSubmitInteraction { - public declare member: CacheTypeReducer; - public declare user: BushUser; - - public constructor(client: BushClient, data: APIModalSubmitInteraction) { - super(client, data); - } -} - -export interface BushModalSubmitInteraction extends ModalSubmitInteraction { - get channel(): CacheTypeReducer< - Cached, - BushGuildTextBasedChannel | null, - BushGuildTextBasedChannel | null, - BushGuildTextBasedChannel | null, - BushTextBasedChannel | null - >; - get guild(): CacheTypeReducer; - inGuild(): this is BushModalSubmitInteraction<'raw' | 'cached'>; - inCachedGuild(): this is BushModalSubmitInteraction<'cached'>; - inRawGuild(): this is BushModalSubmitInteraction<'raw'>; - isFromMessage(): this is BushModalMessageModalSubmitInteraction; -} - -export interface BushModalMessageModalSubmitInteraction - extends ModalSubmitInteraction { - /** - * The message associated with this interaction - */ - message: BushGuildCacheMessage; - - /** - * Updates the original message of the component on which the interaction was received on. - * @param options The options for the updated message - * @example - * // Remove the components from the message - * interaction.update({ - * content: "A component interaction was received", - * components: [] - * }) - * .then(console.log) - * .catch(console.error); - */ - update(options: InteractionUpdateOptions & { fetchReply: true }): Promise>; - update(options: string | MessagePayload | InteractionUpdateOptions): Promise; - - /** - * Defers an update to the message to which the component was attached. - * @param options Options for deferring the update to this interaction - * @example - * // Defer updating and reset the component's loading state - * interaction.deferUpdate() - * .then(console.log) - * .catch(console.error); - */ - deferUpdate(options: InteractionDeferUpdateOptions & { fetchReply: true }): Promise>; - deferUpdate(options?: InteractionDeferUpdateOptions): Promise; - - /** - * Indicates whether this interaction is received from a guild. - */ - inGuild(): this is BushModalMessageModalSubmitInteraction<'raw' | 'cached'>; - - /** - * Indicates whether or not this interaction is both cached and received from a guild. - */ - inCachedGuild(): this is BushModalMessageModalSubmitInteraction<'cached'>; - - /** - * Indicates whether or not this interaction is received from an uncached guild. - */ - inRawGuild(): this is BushModalMessageModalSubmitInteraction<'raw'>; -} diff --git a/src/lib/extensions/discord.js/BushNewsChannel.ts b/src/lib/extensions/discord.js/BushNewsChannel.ts deleted file mode 100644 index e262188..0000000 --- a/src/lib/extensions/discord.js/BushNewsChannel.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { BushGuild, BushGuildMember, BushMessageManager, BushThreadManager } from '#lib'; -import { NewsChannel, type AllowedThreadTypeForNewsChannel, type Collection, type Snowflake } from 'discord.js'; - -/** - * Represents a guild news channel on Discord. - */ -export class BushNewsChannel extends NewsChannel { - public declare threads: BushThreadManager; - public declare guild: BushGuild; - public declare messages: BushMessageManager; -} - -export interface BushNewsChannel extends NewsChannel { - get members(): Collection; -} diff --git a/src/lib/extensions/discord.js/BushPresence.ts b/src/lib/extensions/discord.js/BushPresence.ts deleted file mode 100644 index 40873ac..0000000 --- a/src/lib/extensions/discord.js/BushPresence.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { BushClient, BushGuild, BushGuildMember, BushUser } from '#lib'; -import { Presence } from 'discord.js'; -import type { RawPresenceData } from 'discord.js/typings/rawDataTypes'; - -/** - * Represents a user's presence. - */ -export class BushPresence extends Presence { - public declare guild: BushGuild | null; - - public constructor(client: BushClient, data?: RawPresenceData) { - super(client, data); - } -} - -export interface BushPresence extends Presence { - get member(): BushGuildMember | null; - get user(): BushUser | null; -} diff --git a/src/lib/extensions/discord.js/BushReactionEmoji.ts b/src/lib/extensions/discord.js/BushReactionEmoji.ts deleted file mode 100644 index b2a7eb0..0000000 --- a/src/lib/extensions/discord.js/BushReactionEmoji.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { BushMessageReaction } from '#lib'; -import { ReactionEmoji } from 'discord.js'; -import type { RawReactionEmojiData } from 'discord.js/typings/rawDataTypes'; - -/** - * Represents a limited emoji set used for both custom and unicode emojis. Custom emojis - * will use this class opposed to the Emoji class when the client doesn't know enough - * information about them. - */ -export class BushReactionEmoji extends ReactionEmoji { - public declare reaction: BushMessageReaction; - - public constructor(reaction: BushMessageReaction, emoji: RawReactionEmojiData) { - super(reaction, emoji); - } -} diff --git a/src/lib/extensions/discord.js/BushRole.ts b/src/lib/extensions/discord.js/BushRole.ts deleted file mode 100644 index a9575bd..0000000 --- a/src/lib/extensions/discord.js/BushRole.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { BushClient, BushGuild, BushGuildMember } from '#lib'; -import { Role, type Collection, type Snowflake } from 'discord.js'; -import type { RawRoleData } from 'discord.js/typings/rawDataTypes'; - -/** - * Represents a role on Discord. - */ -export class BushRole extends Role { - public declare guild: BushGuild; - - public constructor(client: BushClient, data: RawRoleData, guild: BushGuild) { - super(client, data, guild); - } -} - -export interface BushRole extends Role { - get members(): Collection; -} diff --git a/src/lib/extensions/discord.js/BushSelectMenuInteraction.ts b/src/lib/extensions/discord.js/BushSelectMenuInteraction.ts deleted file mode 100644 index 66a5ea9..0000000 --- a/src/lib/extensions/discord.js/BushSelectMenuInteraction.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { BushClient, BushGuild, BushGuildMember, BushGuildTextBasedChannel, BushTextBasedChannel, BushUser } from '#lib'; -import type { APIInteractionGuildMember } from 'discord-api-types/v10'; -import { SelectMenuInteraction, type CacheType, type CacheTypeReducer } from 'discord.js'; -import type { RawMessageSelectMenuInteractionData } from 'discord.js/typings/rawDataTypes'; - -/** - * Represents a select menu interaction. - */ -export class BushSelectMenuInteraction extends SelectMenuInteraction { - public declare member: CacheTypeReducer; - public declare user: BushUser; - - public constructor(client: BushClient, data: RawMessageSelectMenuInteractionData) { - super(client, data); - } -} - -export interface BushSelectMenuInteraction extends SelectMenuInteraction { - get channel(): CacheTypeReducer< - Cached, - BushGuildTextBasedChannel | null, - BushGuildTextBasedChannel | null, - BushGuildTextBasedChannel | null, - BushTextBasedChannel | null - >; - get guild(): CacheTypeReducer; - inGuild(): this is BushSelectMenuInteraction<'raw' | 'cached'>; - inCachedGuild(): this is BushSelectMenuInteraction<'cached'>; - inRawGuild(): this is BushSelectMenuInteraction<'raw'>; -} diff --git a/src/lib/extensions/discord.js/BushStageChannel.ts b/src/lib/extensions/discord.js/BushStageChannel.ts deleted file mode 100644 index 983bd56..0000000 --- a/src/lib/extensions/discord.js/BushStageChannel.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { BushCategoryChannel, BushGuild, BushGuildMember, BushStageInstance } from '#lib'; -import { StageChannel, type Collection, type Snowflake } from 'discord.js'; -import type { RawGuildChannelData } from 'discord.js/typings/rawDataTypes'; - -/** - * Represents a guild stage channel on Discord. - */ -export class BushStageChannel extends StageChannel { - public declare guild: BushGuild; - - public constructor(guild: BushGuild, data?: RawGuildChannelData) { - super(guild, data); - } -} - -export interface BushStageChannel extends StageChannel { - get members(): Collection; - get parent(): BushCategoryChannel | null; - get stageInstance(): BushStageInstance | null; -} diff --git a/src/lib/extensions/discord.js/BushStageInstance.ts b/src/lib/extensions/discord.js/BushStageInstance.ts deleted file mode 100644 index 96453a7..0000000 --- a/src/lib/extensions/discord.js/BushStageInstance.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { BushClient, BushGuild, BushStageChannel } from '#lib'; -import { StageInstance } from 'discord.js'; -import type { RawStageInstanceData } from 'discord.js/typings/rawDataTypes'; - -/** - * Represents a stage instance. - */ -export class BushStageInstance extends StageInstance { - public constructor(client: BushClient, data: RawStageInstanceData, channel: BushStageChannel) { - super(client, data, channel); - } -} - -export interface BushStageInstance extends StageInstance { - get channel(): BushStageChannel | null; - get guild(): BushGuild | null; -} diff --git a/src/lib/extensions/discord.js/BushTextChannel.ts b/src/lib/extensions/discord.js/BushTextChannel.ts deleted file mode 100644 index 575de20..0000000 --- a/src/lib/extensions/discord.js/BushTextChannel.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { - BushCategoryChannel, - BushDMChannel, - BushGuild, - BushGuildBasedChannel, - BushMessageManager, - BushNewsChannel, - BushStageChannel, - BushTextBasedChannel, - BushThreadChannel, - BushThreadManager, - BushVoiceBasedChannel, - BushVoiceChannel -} from '#lib'; -import { PartialGroupDMChannel, TextChannel, type AllowedThreadTypeForTextChannel } from 'discord.js'; -import type { RawGuildChannelData } from 'discord.js/typings/rawDataTypes'; - -/** - * Represents a guild text channel on Discord. - */ -export class BushTextChannel extends TextChannel { - public declare guild: BushGuild; - public declare messages: BushMessageManager; - public declare threads: BushThreadManager; - - public constructor(guild: BushGuild, data?: RawGuildChannelData) { - super(guild, data); - } -} - -export interface BushTextChannel extends TextChannel { - isText(): this is BushTextChannel; - isDM(): this is BushDMChannel; - isDMBased(): this is PartialGroupDMChannel | BushDMChannel; - isVoice(): this is BushVoiceChannel; - isCategory(): this is BushCategoryChannel; - isNews(): this is BushNewsChannel; - isThread(): this is BushThreadChannel; - isStage(): this is BushStageChannel; - isTextBased(): this is BushGuildBasedChannel & BushTextBasedChannel; - isVoiceBased(): this is BushVoiceBasedChannel; -} diff --git a/src/lib/extensions/discord.js/BushThreadChannel.ts b/src/lib/extensions/discord.js/BushThreadChannel.ts deleted file mode 100644 index 8b941f9..0000000 --- a/src/lib/extensions/discord.js/BushThreadChannel.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type { - BushCategoryChannel, - BushClient, - BushDMChannel, - BushGuild, - BushGuildBasedChannel, - BushGuildMember, - BushMessageManager, - BushNewsChannel, - BushStageChannel, - BushTextBasedChannel, - BushTextChannel, - BushThreadMemberManager, - BushVoiceBasedChannel, - BushVoiceChannel -} from '#lib'; -import { PartialGroupDMChannel, ThreadChannel, type Collection, type Snowflake } from 'discord.js'; -import type { RawThreadChannelData } from 'discord.js/typings/rawDataTypes'; - -/** - * Represents a thread channel on Discord. - */ -export class BushThreadChannel extends ThreadChannel { - public declare guild: BushGuild; - public declare messages: BushMessageManager; - public declare members: BushThreadMemberManager; - public declare readonly client: BushClient; - - public constructor(guild: BushGuild, data?: RawThreadChannelData, client?: BushClient, fromInteraction?: boolean) { - super(guild, data, client, fromInteraction); - } -} - -export interface BushThreadChannel extends ThreadChannel { - get guildMembers(): Collection; - get parent(): BushTextChannel | BushNewsChannel | null; - isText(): this is BushTextChannel; - isDM(): this is BushDMChannel; - isDMBased(): this is PartialGroupDMChannel | BushDMChannel; - isVoice(): this is BushVoiceChannel; - isCategory(): this is BushCategoryChannel; - isNews(): this is BushNewsChannel; - isThread(): this is BushThreadChannel; - isStage(): this is BushStageChannel; - isTextBased(): this is BushGuildBasedChannel & BushTextBasedChannel; - isVoiceBased(): this is BushVoiceBasedChannel; -} diff --git a/src/lib/extensions/discord.js/BushThreadManager.ts b/src/lib/extensions/discord.js/BushThreadManager.ts deleted file mode 100644 index 0748a4d..0000000 --- a/src/lib/extensions/discord.js/BushThreadManager.ts +++ /dev/null @@ -1,84 +0,0 @@ -import type { BushFetchedThreads, BushThreadChannel } from '#lib'; -import { - CachedManager, - NewsChannel, - TextChannel, - ThreadManager, - type BaseFetchOptions, - type FetchArchivedThreadOptions, - type FetchThreadsOptions, - type Snowflake, - type ThreadChannelResolvable, - type ThreadCreateOptions -} from 'discord.js'; -import type { RawThreadChannelData } from 'discord.js/typings/rawDataTypes'; - -/** - * Manages API methods for {@link BushThreadChannel} objects and stores their cache. - */ -export declare class BushThreadManager - extends CachedManager - implements ThreadManager -{ - public constructor(channel: TextChannel | NewsChannel, iterable?: Iterable); - - /** - * The channel this Manager belongs to - */ - public channel: TextChannel | NewsChannel; - - /** - * Creates a new thread in the channel. - * @param options Options to create a new thread - * @example - * // Create a new public thread - * channel.threads - * .create({ - * name: 'food-talk', - * autoArchiveDuration: 60, - * reason: 'Needed a separate thread for food', - * }) - * .then(threadChannel => console.log(threadChannel)) - * .catch(console.error); - * @example - * // Create a new private thread - * channel.threads - * .create({ - * name: 'mod-talk', - * autoArchiveDuration: 60, - * type: 'GuildPrivateThread', - * reason: 'Needed a separate thread for moderation', - * }) - * .then(threadChannel => console.log(threadChannel)) - * .catch(console.error); - */ - public create(options: ThreadCreateOptions): Promise; - - /** - * Obtains a thread from Discord, or the channel cache if it's already available. - * @param options The options to fetch threads. If it is a - * ThreadChannelResolvable then the specified thread will be fetched. Fetches all active threads if `undefined` - * @param cacheOptions Additional options for this fetch. The `force` field gets ignored - * if `options` is not a {@link ThreadChannelResolvable} - * @example - * // Fetch a thread by its id - * channel.threads.fetch('831955138126104859') - * .then(channel => console.log(channel.name)) - * .catch(console.error); - */ - public fetch(options: ThreadChannelResolvable, cacheOptions?: BaseFetchOptions): Promise; - public fetch(options?: FetchThreadsOptions, cacheOptions?: { cache?: boolean }): Promise; - - /** - * Obtains a set of archived threads from Discord, requires `READ_MESSAGE_HISTORY` in the parent channel. - * @param options The options to fetch archived threads - * @param cache Whether to cache the new thread objects if they aren't already - */ - public fetchArchived(options?: FetchArchivedThreadOptions, cache?: boolean): Promise; - - /** - * Obtains the accessible active threads from Discord, requires `READ_MESSAGE_HISTORY` in the parent channel. - * @param cache Whether to cache the new thread objects if they aren't already - */ - public fetchActive(cache?: boolean): Promise; -} diff --git a/src/lib/extensions/discord.js/BushThreadMember.ts b/src/lib/extensions/discord.js/BushThreadMember.ts deleted file mode 100644 index 90c9c9b..0000000 --- a/src/lib/extensions/discord.js/BushThreadMember.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { BushGuildMember, BushThreadChannel, BushUser } from '#lib'; -import { ThreadMember } from 'discord.js'; -import type { RawThreadMemberData } from 'discord.js/typings/rawDataTypes'; - -/** - * Represents a Member for a Thread. - */ -export class BushThreadMember extends ThreadMember { - public declare thread: BushThreadChannel; - - public constructor(thread: BushThreadChannel, data?: RawThreadMemberData) { - super(thread, data); - } -} - -export interface BushThreadMember extends ThreadMember { - get guildMember(): BushGuildMember | null; - get user(): BushUser | null; -} diff --git a/src/lib/extensions/discord.js/BushThreadMemberManager.ts b/src/lib/extensions/discord.js/BushThreadMemberManager.ts deleted file mode 100644 index d183b30..0000000 --- a/src/lib/extensions/discord.js/BushThreadMemberManager.ts +++ /dev/null @@ -1,62 +0,0 @@ -import type { BushClient, BushThreadChannel, BushThreadMember, BushThreadMemberResolvable, BushUserResolvable } from '#lib'; -import { - CachedManager, - ThreadMemberManager, - type BaseFetchOptions, - type Collection, - type Snowflake, - type UserResolvable -} from 'discord.js'; -import type { RawThreadMemberData } from 'discord.js/typings/rawDataTypes'; - -/** - * Manages API methods for GuildMembers and stores their cache. - */ -export declare class BushThreadMemberManager - extends CachedManager - implements ThreadMemberManager -{ - public constructor(thread: BushThreadChannel, iterable?: Iterable); - public declare readonly client: BushClient; - - /** - * The thread this manager belongs to - */ - public thread: BushThreadChannel; - - /** - * Adds a member to the thread. - * @param member The member to add - * @param reason The reason for adding this member - */ - public add(member: UserResolvable | '@me', reason?: string): Promise; - - /** - * Fetches member(s) for the thread from Discord, requires access to the `GatewayIntentBits.GuildMembers` gateway intent. - * @param options Additional options for this fetch, when a `boolean` is provided - * all members are fetched with `options.cache` set to the boolean value - */ - public fetch(options?: BushThreadMemberFetchOptions): Promise; - public fetch(cache?: boolean): Promise>; - - /** - * Remove a user from the thread. - * @param id The id of the member to remove - * @param reason The reason for removing this member from the thread - */ - public remove(id: Snowflake | '@me', reason?: string): Promise; -} - -export interface BushThreadMemberManager extends CachedManager { - /** - * The client user as a ThreadMember of this ThreadChannel - */ - get me(): BushThreadMember | null; -} - -export interface BushThreadMemberFetchOptions extends BaseFetchOptions { - /** - * The specific user to fetch from the thread - */ - member?: BushUserResolvable; -} diff --git a/src/lib/extensions/discord.js/BushUser.ts b/src/lib/extensions/discord.js/BushUser.ts deleted file mode 100644 index 27ef2b2..0000000 --- a/src/lib/extensions/discord.js/BushUser.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { BushClient, BushDMChannel } from '#lib'; -import { User, type Partialize } from 'discord.js'; -import type { RawUserData } from 'discord.js/typings/rawDataTypes'; - -export type PartialBushUser = Partialize; - -/** - * Represents a user on Discord. - */ -export class BushUser extends User { - public declare readonly client: BushClient; - - public constructor(client: BushClient, data: RawUserData) { - super(client, data); - } - - /** - * Indicates whether the user is an owner of the bot. - */ - public isOwner(): boolean { - return client.isOwner(this); - } - - /** - * Indicates whether the user is a superuser of the bot. - */ - public isSuperUser(): boolean { - return client.isSuperUser(this); - } -} - -export interface BushUser extends User { - get dmChannel(): BushDMChannel | null; -} diff --git a/src/lib/extensions/discord.js/BushUserManager.ts b/src/lib/extensions/discord.js/BushUserManager.ts deleted file mode 100644 index c26dbde..0000000 --- a/src/lib/extensions/discord.js/BushUserManager.ts +++ /dev/null @@ -1,60 +0,0 @@ -import type { BushClient, BushDMChannel, BushUser, BushUserResolvable } from '#lib'; -import { - CachedManager, - Message, - MessageOptions, - MessagePayload, - UserFlagsBitField, - UserManager, - type BaseFetchOptions, - type Snowflake -} from 'discord.js'; -import type { RawUserData } from 'discord.js/typings/rawDataTypes'; - -/** - * Manages API methods for users and stores their cache. - */ -export declare class BushUserManager extends CachedManager implements UserManager { - private constructor(client: BushClient, iterable?: Iterable); - - /** - * The DM between the client's user and a user - * @param userId The user id - * @private - */ - public dmChannel(userId: Snowflake): BushDMChannel | null; - - /** - * Creates a {@link BushDMChannel} between the client and a user. - * @param user The UserResolvable to identify - * @param options Additional options for this fetch - */ - public createDM(user: BushUserResolvable, options?: BaseFetchOptions): Promise; - - /** - * Deletes a {@link BushDMChannel} (if one exists) between the client and a user. Resolves with the channel if successful. - * @param user The UserResolvable to identify - */ - public deleteDM(user: BushUserResolvable): Promise; - - /** - * Obtains a user from Discord, or the user cache if it's already available. - * @param user The user to fetch - * @param options Additional options for this fetch - */ - public fetch(user: BushUserResolvable, options?: BaseFetchOptions): Promise; - - /** - * Fetches a user's flags. - * @param user The UserResolvable to identify - * @param options Additional options for this fetch - */ - public fetchFlags(user: BushUserResolvable, options?: BaseFetchOptions): Promise; - - /** - * Sends a message to a user. - * @param user The UserResolvable to identify - * @param options The options to provide - */ - public send(user: BushUserResolvable, options: string | MessagePayload | MessageOptions): Promise; -} diff --git a/src/lib/extensions/discord.js/BushVoiceChannel.ts b/src/lib/extensions/discord.js/BushVoiceChannel.ts deleted file mode 100644 index 6966727..0000000 --- a/src/lib/extensions/discord.js/BushVoiceChannel.ts +++ /dev/null @@ -1,40 +0,0 @@ -import type { - BushCategoryChannel, - BushClient, - BushDMChannel, - BushGuild, - BushGuildBasedChannel, - BushGuildMember, - BushNewsChannel, - BushStageChannel, - BushTextBasedChannel, - BushTextChannel, - BushThreadChannel, - BushVoiceBasedChannel -} from '#lib'; -import { VoiceChannel, type Collection, type Snowflake } from 'discord.js'; -import type { RawGuildChannelData } from 'discord.js/typings/rawDataTypes'; - -/** - * Represents a guild voice channel on Discord. - */ -export class BushVoiceChannel extends VoiceChannel { - public declare readonly client: BushClient; - - public constructor(guild: BushGuild, data?: RawGuildChannelData) { - super(guild, data); - } -} - -export interface BushVoiceChannel extends VoiceChannel { - get members(): Collection; - isText(): this is BushTextChannel; - isDM(): this is BushDMChannel; - isVoice(): this is BushVoiceChannel; - isCategory(): this is BushCategoryChannel; - isNews(): this is BushNewsChannel; - isThread(): this is BushThreadChannel; - isStage(): this is BushStageChannel; - isTextBased(): this is BushGuildBasedChannel & BushTextBasedChannel; - isVoiceBased(): this is BushVoiceBasedChannel; -} diff --git a/src/lib/extensions/discord.js/BushVoiceState.ts b/src/lib/extensions/discord.js/BushVoiceState.ts deleted file mode 100644 index bbcdfa8..0000000 --- a/src/lib/extensions/discord.js/BushVoiceState.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { BushClient, BushGuild, BushGuildMember, BushVoiceBasedChannel } from '#lib'; -import { VoiceState } from 'discord.js'; -import type { RawVoiceStateData } from 'discord.js/typings/rawDataTypes'; - -/** - * Represents the voice state for a Guild Member. - */ -export class BushVoiceState extends VoiceState { - public declare readonly client: BushClient; - public declare guild: BushGuild; - - public constructor(guild: BushGuild, data: RawVoiceStateData) { - super(guild, data); - } -} - -export interface BushVoiceState extends VoiceState { - get channel(): BushVoiceBasedChannel | null; - get getmember(): BushGuildMember | null; -} diff --git a/src/lib/extensions/discord.js/ExtendedGuild.ts b/src/lib/extensions/discord.js/ExtendedGuild.ts new file mode 100644 index 0000000..b8b7b22 --- /dev/null +++ b/src/lib/extensions/discord.js/ExtendedGuild.ts @@ -0,0 +1,870 @@ +import { + AllowedMentions, + banResponse, + dmResponse, + permissionsResponse, + punishmentEntryRemove, + type BanResponse, + type GuildFeatures, + type GuildLogType, + type GuildModel +} from '#lib'; +import { + AttachmentBuilder, + AttachmentPayload, + Collection, + Guild, + JSONEncodable, + Message, + MessageType, + PermissionFlagsBits, + SnowflakeUtil, + ThreadChannel, + type APIMessage, + type GuildMember, + type GuildMemberResolvable, + type GuildTextBasedChannel, + type MessageOptions, + type MessagePayload, + type NewsChannel, + type Snowflake, + type TextChannel, + type User, + type UserResolvable, + type VoiceChannel, + type Webhook, + type WebhookMessageOptions +} from 'discord.js'; +import _ from 'lodash'; +import { Moderation } from '../../common/util/Moderation.js'; +import { Guild as GuildDB } from '../../models/instance/Guild.js'; +import { ModLogType } from '../../models/instance/ModLog.js'; + +declare module 'discord.js' { + export interface Guild { + /** + * Checks if the guild has a certain custom feature. + * @param feature The feature to check for + */ + hasFeature(feature: GuildFeatures): Promise; + /** + * Adds a custom feature to the guild. + * @param feature The feature to add + * @param moderator The moderator responsible for adding a feature + */ + addFeature(feature: GuildFeatures, moderator?: GuildMember): Promise; + /** + * Removes a custom feature from the guild. + * @param feature The feature to remove + * @param moderator The moderator responsible for removing a feature + */ + removeFeature(feature: GuildFeatures, moderator?: GuildMember): Promise; + /** + * Makes a custom feature the opposite of what it was before + * @param feature The feature to toggle + * @param moderator The moderator responsible for toggling a feature + */ + toggleFeature(feature: GuildFeatures, moderator?: GuildMember): Promise; + /** + * Fetches a custom setting for the guild + * @param setting The setting to get + */ + getSetting(setting: K): Promise; + /** + * Sets a custom setting for the guild + * @param setting The setting to change + * @param value The value to change the setting to + * @param moderator The moderator to responsible for changing the setting + */ + setSetting>( + setting: K, + value: GuildModel[K], + moderator?: GuildMember + ): Promise; + /** + * Get a the log channel configured for a certain log type. + * @param logType The type of log channel to get. + * @returns Either the log channel or undefined if not configured. + */ + getLogChannel(logType: GuildLogType): Promise; + /** + * Sends a message to the guild's specified logging channel + * @param logType The corresponding channel that the message will be sent to + * @param message The parameters for {@link BushTextChannel.send} + */ + sendLogChannel(logType: GuildLogType, message: string | MessagePayload | MessageOptions): Promise; + /** + * Sends a formatted error message in a guild's error log channel + * @param title The title of the error embed + * @param message The description of the error embed + */ + error(title: string, message: string): Promise; + /** + * Bans a user, dms them, creates a mod log entry, and creates a punishment entry. + * @param options Options for banning the user. + * @returns A string status message of the ban. + */ + bushBan(options: GuildBushBanOptions): Promise; + /** + * {@link bushBan} with less resolving and checks + * @param options Options for banning the user. + * @returns A string status message of the ban. + * **Preconditions:** + * - {@link me} has the `BanMembers` permission + * **Warning:** + * - Doesn't emit bushBan Event + */ + massBanOne(options: GuildMassBanOneOptions): Promise; + /** + * Unbans a user, dms them, creates a mod log entry, and destroys the punishment entry. + * @param options Options for unbanning the user. + * @returns A status message of the unban. + */ + bushUnban(options: GuildBushUnbanOptions): Promise; + /** + * Denies send permissions in specified channels + * @param options The options for locking down the guild + */ + lockdown(options: LockdownOptions): Promise; + quote(rawQuote: APIMessage, channel: GuildTextBasedChannel): Promise; + } +} + +/** + * Represents a guild (or a server) on Discord. + * It's recommended to see if a guild is available before performing operations or reading data from it. You can + * check this with {@link ExtendedGuild.available}. + */ +export class ExtendedGuild extends Guild { + /** + * Checks if the guild has a certain custom feature. + * @param feature The feature to check for + */ + public override async hasFeature(feature: GuildFeatures): Promise { + const features = await this.getSetting('enabledFeatures'); + return features.includes(feature); + } + + /** + * Adds a custom feature to the guild. + * @param feature The feature to add + * @param moderator The moderator responsible for adding a feature + */ + public override async addFeature(feature: GuildFeatures, moderator?: GuildMember): Promise { + const features = await this.getSetting('enabledFeatures'); + const newFeatures = util.addOrRemoveFromArray('add', features, feature); + return (await this.setSetting('enabledFeatures', newFeatures, moderator)).enabledFeatures; + } + + /** + * Removes a custom feature from the guild. + * @param feature The feature to remove + * @param moderator The moderator responsible for removing a feature + */ + public override async removeFeature(feature: GuildFeatures, moderator?: GuildMember): Promise { + const features = await this.getSetting('enabledFeatures'); + const newFeatures = util.addOrRemoveFromArray('remove', features, feature); + return (await this.setSetting('enabledFeatures', newFeatures, moderator)).enabledFeatures; + } + + /** + * Makes a custom feature the opposite of what it was before + * @param feature The feature to toggle + * @param moderator The moderator responsible for toggling a feature + */ + public override async toggleFeature(feature: GuildFeatures, moderator?: GuildMember): Promise { + return (await this.hasFeature(feature)) + ? await this.removeFeature(feature, moderator) + : await this.addFeature(feature, moderator); + } + + /** + * Fetches a custom setting for the guild + * @param setting The setting to get + */ + public override async getSetting(setting: K): Promise { + return ( + client.cache.guilds.get(this.id)?.[setting] ?? + ((await GuildDB.findByPk(this.id)) ?? GuildDB.build({ id: this.id }))[setting] + ); + } + + /** + * Sets a custom setting for the guild + * @param setting The setting to change + * @param value The value to change the setting to + * @param moderator The moderator to responsible for changing the setting + */ + public override async setSetting>( + setting: K, + value: GuildDB[K], + moderator?: GuildMember + ): Promise { + const row = (await GuildDB.findByPk(this.id)) ?? GuildDB.build({ id: this.id }); + const oldValue = row[setting] as GuildDB[K]; + row[setting] = value; + client.cache.guilds.set(this.id, row.toJSON() as GuildDB); + client.emit('bushUpdateSettings', setting, this, oldValue, row[setting], moderator); + return await row.save(); + } + + /** + * Get a the log channel configured for a certain log type. + * @param logType The type of log channel to get. + * @returns Either the log channel or undefined if not configured. + */ + public override async getLogChannel(logType: GuildLogType): Promise { + const channelId = (await this.getSetting('logChannels'))[logType]; + if (!channelId) return undefined; + return ( + (this.channels.cache.get(channelId) as TextChannel | undefined) ?? + ((await this.channels.fetch(channelId)) as TextChannel | null) ?? + undefined + ); + } + + /** + * Sends a message to the guild's specified logging channel + * @param logType The corresponding channel that the message will be sent to + * @param message The parameters for {@link BushTextChannel.send} + */ + public override async sendLogChannel( + logType: GuildLogType, + message: string | MessagePayload | MessageOptions + ): Promise { + const logChannel = await this.getLogChannel(logType); + if (!logChannel || !logChannel.isTextBased()) return; + if ( + !logChannel + .permissionsFor(this.members.me!.id) + ?.has([PermissionFlagsBits.ViewChannel, PermissionFlagsBits.SendMessages, PermissionFlagsBits.EmbedLinks]) + ) + return; + + return await logChannel.send(message).catch(() => null); + } + + /** + * Sends a formatted error message in a guild's error log channel + * @param title The title of the error embed + * @param message The description of the error embed + */ + public override async error(title: string, message: string): Promise { + void client.console.info(_.camelCase(title), message.replace(/\*\*(.*?)\*\*/g, '<<$1>>')); + void this.sendLogChannel('error', { embeds: [{ title: title, description: message, color: util.colors.error }] }); + } + + /** + * Bans a user, dms them, creates a mod log entry, and creates a punishment entry. + * @param options Options for banning the user. + * @returns A string status message of the ban. + */ + public override async bushBan(options: GuildBushBanOptions): Promise { + // checks + if (!this.members.me!.permissions.has(PermissionFlagsBits.BanMembers)) return banResponse.MISSING_PERMISSIONS; + + let caseID: string | undefined = undefined; + let dmSuccessEvent: boolean | undefined = undefined; + const user = await util.resolveNonCachedUser(options.user); + const moderator = client.users.resolve(options.moderator ?? client.user!); + if (!user || !moderator) return banResponse.CANNOT_RESOLVE_USER; + + if ((await this.bans.fetch()).has(user.id)) return banResponse.ALREADY_BANNED; + + const ret = await (async () => { + // add modlog entry + const { log: modlog } = await Moderation.createModLogEntry({ + type: options.duration ? ModLogType.TEMP_BAN : ModLogType.PERM_BAN, + user: user, + moderator: moderator.id, + reason: options.reason, + duration: options.duration, + guild: this, + evidence: options.evidence + }); + if (!modlog) return banResponse.MODLOG_ERROR; + caseID = modlog.id; + + // dm user + dmSuccessEvent = await Moderation.punishDM({ + modlog: modlog.id, + guild: this, + user: user, + punishment: 'banned', + duration: options.duration ?? 0, + reason: options.reason ?? undefined, + sendFooter: true + }); + + // ban + const banSuccess = await this.bans + .create(user?.id ?? options.user, { + reason: `${moderator.tag} | ${options.reason ?? 'No reason provided.'}`, + deleteMessageDays: options.deleteDays + }) + .catch(() => false); + if (!banSuccess) return banResponse.ACTION_ERROR; + + // add punishment entry so they can be unbanned later + const punishmentEntrySuccess = await Moderation.createPunishmentEntry({ + type: 'ban', + user: user, + guild: this, + duration: options.duration, + modlog: modlog.id + }); + if (!punishmentEntrySuccess) return banResponse.PUNISHMENT_ENTRY_ADD_ERROR; + + if (!dmSuccessEvent) return banResponse.DM_ERROR; + return banResponse.SUCCESS; + })(); + + if (!([banResponse.ACTION_ERROR, banResponse.MODLOG_ERROR, banResponse.PUNISHMENT_ENTRY_ADD_ERROR] as const).includes(ret)) + client.emit( + 'bushBan', + user, + moderator, + this, + options.reason ?? undefined, + caseID!, + options.duration ?? 0, + dmSuccessEvent, + options.evidence + ); + return ret; + } + + /** + * {@link bushBan} with less resolving and checks + * @param options Options for banning the user. + * @returns A string status message of the ban. + * **Preconditions:** + * - {@link me} has the `BanMembers` permission + * **Warning:** + * - Doesn't emit bushBan Event + */ + public override async massBanOne(options: GuildMassBanOneOptions): Promise { + if (this.bans.cache.has(options.user)) return banResponse.ALREADY_BANNED; + + const ret = await (async () => { + // add modlog entry + const { log: modlog } = await Moderation.createModLogEntrySimple({ + type: ModLogType.PERM_BAN, + user: options.user, + moderator: options.moderator, + reason: options.reason, + duration: 0, + guild: this.id + }); + if (!modlog) return banResponse.MODLOG_ERROR; + + let dmSuccessEvent: boolean | undefined = undefined; + // dm user + if (this.members.cache.has(options.user)) { + dmSuccessEvent = await Moderation.punishDM({ + modlog: modlog.id, + guild: this, + user: options.user, + punishment: 'banned', + duration: 0, + reason: options.reason ?? undefined, + sendFooter: true + }); + } + + // ban + const banSuccess = await this.bans + .create(options.user, { + reason: `${options.moderator} | ${options.reason}`, + deleteMessageDays: options.deleteDays + }) + .catch(() => false); + if (!banSuccess) return banResponse.ACTION_ERROR; + + // add punishment entry so they can be unbanned later + const punishmentEntrySuccess = await Moderation.createPunishmentEntry({ + type: 'ban', + user: options.user, + guild: this, + duration: 0, + modlog: modlog.id + }); + if (!punishmentEntrySuccess) return banResponse.PUNISHMENT_ENTRY_ADD_ERROR; + + if (!dmSuccessEvent) return banResponse.DM_ERROR; + return banResponse.SUCCESS; + })(); + return ret; + } + + /** + * Unbans a user, dms them, creates a mod log entry, and destroys the punishment entry. + * @param options Options for unbanning the user. + * @returns A status message of the unban. + */ + public override async bushUnban(options: GuildBushUnbanOptions): Promise { + // checks + if (!this.members.me!.permissions.has(PermissionFlagsBits.BanMembers)) return unbanResponse.MISSING_PERMISSIONS; + + let caseID: string | undefined = undefined; + let dmSuccessEvent: boolean | undefined = undefined; + const user = await util.resolveNonCachedUser(options.user); + const moderator = client.users.resolve(options.moderator ?? client.user!); + if (!user || !moderator) return unbanResponse.CANNOT_RESOLVE_USER; + + const ret = await (async () => { + const bans = await this.bans.fetch(); + + let notBanned = false; + if (!bans.has(user.id)) notBanned = true; + + const unbanSuccess = await this.bans + .remove(user, `${moderator.tag} | ${options.reason ?? 'No reason provided.'}`) + .catch((e) => { + if (e?.code === 'UNKNOWN_BAN') { + notBanned = true; + return true; + } else return false; + }); + + if (notBanned) return unbanResponse.NOT_BANNED; + if (!unbanSuccess) return unbanResponse.ACTION_ERROR; + + // add modlog entry + const { log: modlog } = await Moderation.createModLogEntry({ + type: ModLogType.UNBAN, + user: user.id, + moderator: moderator.id, + reason: options.reason, + guild: this, + evidence: options.evidence + }); + if (!modlog) return unbanResponse.MODLOG_ERROR; + caseID = modlog.id; + + // remove punishment entry + const removePunishmentEntrySuccess = await Moderation.removePunishmentEntry({ + type: 'ban', + user: user.id, + guild: this + }); + if (!removePunishmentEntrySuccess) return unbanResponse.PUNISHMENT_ENTRY_REMOVE_ERROR; + + // dm user + dmSuccessEvent = await Moderation.punishDM({ + guild: this, + user: user, + punishment: 'unbanned', + reason: options.reason ?? undefined, + sendFooter: false + }); + + if (!dmSuccessEvent) return unbanResponse.DM_ERROR; + return unbanResponse.SUCCESS; + })(); + if ( + !([unbanResponse.ACTION_ERROR, unbanResponse.MODLOG_ERROR, unbanResponse.PUNISHMENT_ENTRY_REMOVE_ERROR] as const).includes( + ret + ) + ) + client.emit('bushUnban', user, moderator, this, options.reason ?? undefined, caseID!, dmSuccessEvent!, options.evidence); + return ret; + } + + /** + * Denies send permissions in specified channels + * @param options The options for locking down the guild + */ + public override async lockdown(options: LockdownOptions): Promise { + if (!options.all && !options.channel) return 'all not chosen and no channel specified'; + const channelIds = options.all ? await this.getSetting('lockdownChannels') : [options.channel!.id]; + + if (!channelIds.length) return 'no channels configured'; + const mappedChannels = channelIds.map((id) => this.channels.cache.get(id)); + + const invalidChannels = mappedChannels.filter((c) => c === undefined); + if (invalidChannels.length) return `invalid channel configured: ${invalidChannels.join(', ')}`; + + const moderator = this.members.resolve(options.moderator); + if (!moderator) return 'moderator not found'; + + const errors = new Collection(); + const success = new Collection(); + const ret = await (async (): Promise => { + for (const _channel of mappedChannels) { + const channel = _channel!; + if (!channel.isTextBased()) { + errors.set(channel.id, new Error('wrong channel type')); + success.set(channel.id, false); + continue; + } + if (!channel.permissionsFor(this.members.me!.id)?.has([PermissionFlagsBits.ManageChannels])) { + errors.set(channel.id, new Error('client no permission')); + success.set(channel.id, false); + continue; + } else if (!channel.permissionsFor(moderator)?.has([PermissionFlagsBits.ManageChannels])) { + errors.set(channel.id, new Error('moderator no permission')); + success.set(channel.id, false); + continue; + } + + const reason = `[${options.unlock ? 'Unlockdown' : 'Lockdown'}] ${moderator.user.tag} | ${ + options.reason ?? 'No reason provided' + }`; + + const permissionOverwrites = channel.isThread() ? channel.parent!.permissionOverwrites : channel.permissionOverwrites; + const perms = { + SendMessagesInThreads: options.unlock ? null : false, + SendMessages: options.unlock ? null : false + }; + const permsForMe = { + [channel.isThread() ? 'SendMessagesInThreads' : 'SendMessages']: options.unlock ? null : true + }; // so I can send messages in the channel + + const changePermSuccess = await permissionOverwrites.edit(this.id, perms, { reason }).catch((e) => e); + if (changePermSuccess instanceof Error) { + errors.set(channel.id, changePermSuccess); + success.set(channel.id, false); + } else { + success.set(channel.id, true); + await permissionOverwrites.edit(this.members.me!, permsForMe, { reason }); + await channel.send({ + embeds: [ + { + author: { name: moderator.user.tag, icon_url: moderator.displayAvatarURL() }, + title: `This channel has been ${options.unlock ? 'un' : ''}locked`, + description: options.reason ?? 'No reason provided', + color: options.unlock ? util.colors.Green : util.colors.Red, + timestamp: new Date().toISOString() + } + ] + }); + } + } + + if (errors.size) return errors; + else return `success: ${success.filter((c) => c === true).size}`; + })(); + + client.emit(options.unlock ? 'bushUnlockdown' : 'bushLockdown', moderator, options.reason, success, options.all); + return ret; + } + + public override async quote(rawQuote: APIMessage, channel: GuildTextBasedChannel): Promise { + if (!channel.isTextBased() || channel.isDMBased() || channel.guildId !== this.id || !this.members.me) return null; + if (!channel.permissionsFor(this.members.me).has('ManageWebhooks')) return null; + + const quote = new Message(client, rawQuote); + + const target = channel instanceof ThreadChannel ? channel.parent : channel; + if (!target) return null; + + const webhooks: Collection = await target.fetchWebhooks().catch((e) => e); + if (!(webhooks instanceof Collection)) return null; + + // find a webhook that we can use + let webhook = webhooks.find((w) => !!w.token) ?? null; + if (!webhook) + webhook = await target + .createWebhook({ + name: `${client.user!.username} Quotes #${target.name}`, + avatar: client.user!.displayAvatarURL({ size: 2048 }), + reason: 'Creating a webhook for quoting' + }) + .catch(() => null); + + if (!webhook) return null; + + const sendOptions: Omit = {}; + + const displayName = quote.member?.displayName ?? quote.author.username; + + switch (quote.type) { + case MessageType.Default: + case MessageType.Reply: + case MessageType.ChatInputCommand: + case MessageType.ContextMenuCommand: + case MessageType.ThreadStarterMessage: + 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 + sendOptions.attachments = quote.attachments.size + ? [...quote.attachments.values()].map((a) => AttachmentBuilder.from(a as JSONEncodable)) + : undefined; + + if (quote.stickers.size && !(quote.content || quote.embeds.length || quote.attachments.size)) + sendOptions.content = '[[This message has a sticker but not content]]'; + + break; + case MessageType.RecipientAdd: { + const recipient = rawQuote.mentions[0]; + if (!recipient) { + sendOptions.content = `${util.emojis.error} Cannot resolve recipient.`; + break; + } + + if (quote.channel.isThread()) { + const recipientDisplay = quote.guild?.members.cache.get(recipient.id)?.displayName ?? recipient.username; + sendOptions.content = `${util.emojis.join} ${displayName} added ${recipientDisplay} to the thread.`; + } else { + // this should never happen + sendOptions.content = `${util.emojis.join} ${displayName} added ${recipient.username} to the group.`; + } + + break; + } + case MessageType.RecipientRemove: { + const recipient = rawQuote.mentions[0]; + if (!recipient) { + sendOptions.content = `${util.emojis.error} Cannot resolve recipient.`; + break; + } + + if (quote.channel.isThread()) { + const recipientDisplay = quote.guild?.members.cache.get(recipient.id)?.displayName ?? recipient.username; + sendOptions.content = `${util.emojis.leave} ${displayName} removed ${recipientDisplay} from the thread.`; + } else { + // this should never happen + sendOptions.content = `${util.emojis.leave} ${displayName} removed ${recipient.username} from the group.`; + } + + break; + } + + case MessageType.ChannelNameChange: + sendOptions.content = `<:pencil:957988608994861118> ${displayName} changed the channel name: **${quote.content}**`; + + break; + + case MessageType.ChannelPinnedMessage: + throw new Error('Not implemented yet: MessageType.ChannelPinnedMessage case'); + case MessageType.GuildMemberJoin: { + const messages = [ + '{username} joined the party.', + '{username} is here.', + 'Welcome, {username}. We hope you brought pizza.', + 'A wild {username} appeared.', + '{username} just landed.', + '{username} just slid into the server.', + '{username} just showed up!', + 'Welcome {username}. Say hi!', + '{username} hopped into the server.', + 'Everyone welcome {username}!', + "Glad you're here, {username}.", + 'Good to see you, {username}.', + 'Yay you made it, {username}!' + ]; + + const timestamp = SnowflakeUtil.timestampFrom(quote.id); + + // this is the same way that the discord client decides what message to use. + const message = messages[timestamp % messages.length].replace(/{username}/g, displayName); + + sendOptions.content = `${util.emojis.join} ${message}`; + break; + } + case MessageType.UserPremiumGuildSubscription: + sendOptions.content = `<:NitroBoost:585558042309820447> ${displayName} just boosted the server${ + quote.content ? ` **${quote.content}** times` : '' + }!`; + + break; + case MessageType.UserPremiumGuildSubscriptionTier1: + case MessageType.UserPremiumGuildSubscriptionTier2: + case MessageType.UserPremiumGuildSubscriptionTier3: + sendOptions.content = `<:NitroBoost:585558042309820447> ${displayName} just boosted the server${ + quote.content ? ` **${quote.content}** times` : '' + }! ${quote.guild?.name} has achieved **Level ${quote.type - 8}!**`; + + break; + case MessageType.ChannelFollowAdd: + sendOptions.content = `${displayName} has added **${quote.content}** to this channel. Its most important updates will show up here.`; + + break; + case MessageType.GuildDiscoveryDisqualified: + sendOptions.content = + '<:SystemMessageCross:842172192418693173> This server has been removed from Server Discovery because it no longer passes all the requirements. Check Server Settings for more details.'; + + break; + case MessageType.GuildDiscoveryRequalified: + sendOptions.content = + '<:SystemMessageCheck:842172191801212949> This server is eligible for Server Discovery again and has been automatically relisted!'; + + break; + case MessageType.GuildDiscoveryGracePeriodInitialWarning: + sendOptions.content = + '<:SystemMessageWarn:842172192401915971> This server has failed Discovery activity requirements for 1 week. If this server fails for 4 weeks in a row, it will be automatically removed from Discovery.'; + + break; + case MessageType.GuildDiscoveryGracePeriodFinalWarning: + sendOptions.content = + '<:SystemMessageWarn:842172192401915971> This server has failed Discovery activity requirements for 3 weeks in a row. If this server fails for 1 more week, it will be removed from Discovery.'; + + break; + case MessageType.ThreadCreated: { + const threadId = rawQuote.message_reference?.channel_id; + + sendOptions.content = `<:thread:865033845753249813> ${displayName} started a thread: **[${quote.content}](https://discord.com/channels/${quote.guildId}/${threadId} + )**. See all threads.`; + + break; + } + case MessageType.GuildInviteReminder: + sendOptions.content = 'Wondering who to invite? Start by inviting anyone who can help you build the server!'; + + break; + case MessageType.ChannelIconChange: + case MessageType.Call: + default: + sendOptions.content = `${util.emojis.error} I cannot quote **${ + MessageType[quote.type] || quote.type + }** messages, please report this to my developers.`; + + break; + } + + sendOptions.allowedMentions = AllowedMentions.none(); + sendOptions.username = quote.member?.displayName ?? quote.author.username; + sendOptions.avatarURL = quote.member?.displayAvatarURL({ size: 2048 }) ?? quote.author.displayAvatarURL({ size: 2048 }); + + return await webhook.send(sendOptions); /* .catch((e: any) => e); */ + } +} + +/** + * Options for unbanning a user + */ +export interface GuildBushUnbanOptions { + /** + * The user to unban + */ + user: UserResolvable | User; + + /** + * The reason for unbanning the user + */ + reason?: string | null; + + /** + * The moderator who unbanned the user + */ + moderator?: UserResolvable; + + /** + * The evidence for the unban + */ + evidence?: string; +} + +export interface GuildMassBanOneOptions { + /** + * The user to ban + */ + user: Snowflake; + + /** + * The reason to ban the user + */ + reason: string; + + /** + * The moderator who banned the user + */ + moderator: Snowflake; + + /** + * The number of days to delete the user's messages for + */ + deleteDays?: number; +} + +/** + * Options for banning a user + */ +export interface GuildBushBanOptions { + /** + * The user to ban + */ + user: UserResolvable; + + /** + * The reason to ban the user + */ + reason?: string | null; + + /** + * The moderator who banned the user + */ + moderator?: UserResolvable; + + /** + * The duration of the ban + */ + duration?: number; + + /** + * The number of days to delete the user's messages for + */ + deleteDays?: number; + + /** + * The evidence for the ban + */ + evidence?: string; +} + +type ValueOf = T[keyof T]; + +export const unbanResponse = Object.freeze({ + ...dmResponse, + ...permissionsResponse, + ...punishmentEntryRemove, + NOT_BANNED: 'user not banned' +} as const); + +/** + * Response returned when unbanning a user + */ +export type UnbanResponse = ValueOf; + +/** + * Options for locking down channel(s) + */ +export interface LockdownOptions { + /** + * The moderator responsible for the lockdown + */ + moderator: GuildMemberResolvable; + + /** + * Whether to lock down all (specified) channels + */ + all: boolean; + + /** + * Reason for the lockdown + */ + reason?: string; + + /** + * A specific channel to lockdown + */ + channel?: ThreadChannel | NewsChannel | TextChannel | VoiceChannel; + + /** + * Whether or not to unlock the channel(s) instead of locking them + */ + unlock?: boolean; +} + +/** + * Response returned when locking down a channel + */ +export type LockdownResponse = + | `success: ${number}` + | 'all not chosen and no channel specified' + | 'no channels configured' + | `invalid channel configured: ${string}` + | 'moderator not found' + | Collection; diff --git a/src/lib/extensions/discord.js/ExtendedGuildMember.ts b/src/lib/extensions/discord.js/ExtendedGuildMember.ts new file mode 100644 index 0000000..28acc1a --- /dev/null +++ b/src/lib/extensions/discord.js/ExtendedGuildMember.ts @@ -0,0 +1,1240 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import { BushClientEvents, Moderation, ModLogType, PunishmentTypeDM, Time } from '#lib'; +import { + ChannelType, + GuildChannelResolvable, + GuildMember, + GuildTextBasedChannel, + PermissionFlagsBits, + type Role +} from 'discord.js'; +/* eslint-enable @typescript-eslint/no-unused-vars */ + +declare module 'discord.js' { + export interface GuildMember { + /** + * Send a punishment dm to the user. + * @param punishment The punishment that the user has received. + * @param reason The reason for the user's punishment. + * @param duration The duration of the punishment. + * @param modlog The modlog case id so the user can make an appeal. + * @param sendFooter Whether or not to send the guild's punishment footer with the dm. + * @returns Whether or not the dm was sent successfully. + */ + bushPunishDM( + punishment: PunishmentTypeDM, + reason?: string | null, + duration?: number, + modlog?: string, + sendFooter?: boolean + ): Promise; + /** + * Warn the user, create a modlog entry, and send a dm to the user. + * @param options Options for warning the user. + * @returns An object with the result of the warning, and the case number of the warn. + * @emits {@link BushClientEvents.bushWarn} + */ + bushWarn(options: BushPunishmentOptions): Promise<{ result: WarnResponse; caseNum: number | null }>; + /** + * Add a role to the user, if it is a punishment create a modlog entry, and create a punishment entry if it is temporary or a punishment. + * @param options Options for adding a role to the user. + * @returns A status message for adding the add. + * @emits {@link BushClientEvents.bushPunishRole} + */ + bushAddRole(options: AddRoleOptions): Promise; + /** + * Remove a role from the user, if it is a punishment create a modlog entry, and destroy a punishment entry if it was temporary or a punishment. + * @param options Options for removing a role from the user. + * @returns A status message for removing the role. + * @emits {@link BushClientEvents.bushPunishRoleRemove} + */ + bushRemoveRole(options: RemoveRoleOptions): Promise; + /** + * Mute the user, create a modlog entry, creates a punishment entry, and dms the user. + * @param options Options for muting the user. + * @returns A status message for muting the user. + * @emits {@link BushClientEvents.bushMute} + */ + bushMute(options: BushTimedPunishmentOptions): Promise; + /** + * Unmute the user, create a modlog entry, remove the punishment entry, and dm the user. + * @param options Options for unmuting the user. + * @returns A status message for unmuting the user. + * @emits {@link BushClientEvents.bushUnmute} + */ + bushUnmute(options: BushPunishmentOptions): Promise; + /** + * Kick the user, create a modlog entry, and dm the user. + * @param options Options for kicking the user. + * @returns A status message for kicking the user. + * @emits {@link BushClientEvents.bushKick} + */ + bushKick(options: BushPunishmentOptions): Promise; + /** + * Ban the user, create a modlog entry, create a punishment entry, and dm the user. + * @param options Options for banning the user. + * @returns A status message for banning the user. + * @emits {@link BushClientEvents.bushBan} + */ + bushBan(options: BushBanOptions): Promise>; + /** + * Prevents a user from speaking in a channel. + * @param options Options for blocking the user. + */ + bushBlock(options: BlockOptions): Promise; + /** + * Allows a user to speak in a channel. + * @param options Options for unblocking the user. + */ + bushUnblock(options: UnblockOptions): Promise; + /** + * Mutes a user using discord's timeout feature. + * @param options Options for timing out the user. + */ + bushTimeout(options: BushTimeoutOptions): Promise; + /** + * Removes a timeout from a user. + * @param options Options for removing the timeout. + */ + bushRemoveTimeout(options: BushPunishmentOptions): Promise; + /** + * Whether or not the user is an owner of the bot. + */ + isOwner(): boolean; + /** + * Whether or not the user is a super user of the bot. + */ + isSuperUser(): boolean; + } +} + +/** + * Represents a member of a guild on Discord. + */ +export class ExtendedGuildMember extends GuildMember { + /** + * Send a punishment dm to the user. + * @param punishment The punishment that the user has received. + * @param reason The reason for the user's punishment. + * @param duration The duration of the punishment. + * @param modlog The modlog case id so the user can make an appeal. + * @param sendFooter Whether or not to send the guild's punishment footer with the dm. + * @returns Whether or not the dm was sent successfully. + */ + public override async bushPunishDM( + punishment: PunishmentTypeDM, + reason?: string | null, + duration?: number, + modlog?: string, + sendFooter = true + ): Promise { + return Moderation.punishDM({ + modlog, + guild: this.guild, + user: this, + punishment, + reason: reason ?? undefined, + duration, + sendFooter + }); + } + + /** + * Warn the user, create a modlog entry, and send a dm to the user. + * @param options Options for warning the user. + * @returns An object with the result of the warning, and the case number of the warn. + * @emits {@link BushClientEvents.bushWarn} + */ + public override async bushWarn(options: BushPunishmentOptions): Promise<{ result: WarnResponse; caseNum: number | null }> { + let caseID: string | undefined = undefined; + let dmSuccessEvent: boolean | undefined = undefined; + const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); + if (!moderator) return { result: warnResponse.CANNOT_RESOLVE_USER, caseNum: null }; + + const ret = await (async (): Promise<{ result: WarnResponse; caseNum: number | null }> => { + // add modlog entry + const result = await Moderation.createModLogEntry( + { + type: ModLogType.WARN, + user: this, + moderator: moderator.id, + reason: options.reason, + guild: this.guild, + evidence: options.evidence, + hidden: options.silent ?? false + }, + true + ); + caseID = result.log?.id; + if (!result || !result.log) return { result: warnResponse.MODLOG_ERROR, caseNum: null }; + + if (!options.silent) { + // dm user + const dmSuccess = await this.bushPunishDM('warned', options.reason); + dmSuccessEvent = dmSuccess; + if (!dmSuccess) return { result: warnResponse.DM_ERROR, caseNum: result.caseNum }; + } + + return { result: warnResponse.SUCCESS, caseNum: result.caseNum }; + })(); + if (!([warnResponse.MODLOG_ERROR] as const).includes(ret.result) && !options.silent) + client.emit('bushWarn', this, moderator, this.guild, options.reason ?? undefined, caseID!, dmSuccessEvent!); + return ret; + } + + /** + * Add a role to the user, if it is a punishment create a modlog entry, and create a punishment entry if it is temporary or a punishment. + * @param options Options for adding a role to the user. + * @returns A status message for adding the add. + * @emits {@link BushClientEvents.bushPunishRole} + */ + public override async bushAddRole(options: AddRoleOptions): Promise { + // checks + if (!this.guild.members.me!.permissions.has(PermissionFlagsBits.ManageRoles)) return addRoleResponse.MISSING_PERMISSIONS; + const ifShouldAddRole = this.#checkIfShouldAddRole(options.role, options.moderator); + if (ifShouldAddRole !== true) return ifShouldAddRole; + + let caseID: string | undefined = undefined; + const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); + if (!moderator) return addRoleResponse.CANNOT_RESOLVE_USER; + + const ret = await (async () => { + if (options.addToModlog || options.duration) { + const { log: modlog } = await Moderation.createModLogEntry({ + type: options.duration ? ModLogType.TEMP_PUNISHMENT_ROLE : ModLogType.PERM_PUNISHMENT_ROLE, + guild: this.guild, + moderator: moderator.id, + user: this, + reason: 'N/A', + pseudo: !options.addToModlog, + evidence: options.evidence, + hidden: options.silent ?? false + }); + + if (!modlog) return addRoleResponse.MODLOG_ERROR; + caseID = modlog.id; + + if (options.addToModlog || options.duration) { + const punishmentEntrySuccess = await Moderation.createPunishmentEntry({ + type: 'role', + user: this, + guild: this.guild, + modlog: modlog.id, + duration: options.duration, + extraInfo: options.role.id + }); + if (!punishmentEntrySuccess) return addRoleResponse.PUNISHMENT_ENTRY_ADD_ERROR; + } + } + + const removeRoleSuccess = await this.roles.add(options.role, `${moderator.tag}`); + if (!removeRoleSuccess) return addRoleResponse.ACTION_ERROR; + + return addRoleResponse.SUCCESS; + })(); + if ( + !( + [addRoleResponse.ACTION_ERROR, addRoleResponse.MODLOG_ERROR, addRoleResponse.PUNISHMENT_ENTRY_ADD_ERROR] as const + ).includes(ret) && + options.addToModlog && + !options.silent + ) + client.emit( + 'bushPunishRole', + this, + moderator, + this.guild, + options.reason ?? undefined, + caseID!, + options.duration ?? 0, + options.role, + options.evidence + ); + return ret; + } + + /** + * Remove a role from the user, if it is a punishment create a modlog entry, and destroy a punishment entry if it was temporary or a punishment. + * @param options Options for removing a role from the user. + * @returns A status message for removing the role. + * @emits {@link BushClientEvents.bushPunishRoleRemove} + */ + public override async bushRemoveRole(options: RemoveRoleOptions): Promise { + // checks + if (!this.guild.members.me!.permissions.has(PermissionFlagsBits.ManageRoles)) return removeRoleResponse.MISSING_PERMISSIONS; + const ifShouldAddRole = this.#checkIfShouldAddRole(options.role, options.moderator); + if (ifShouldAddRole !== true) return ifShouldAddRole; + + let caseID: string | undefined = undefined; + const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); + if (!moderator) return removeRoleResponse.CANNOT_RESOLVE_USER; + + const ret = await (async () => { + if (options.addToModlog) { + const { log: modlog } = await Moderation.createModLogEntry({ + type: ModLogType.REMOVE_PUNISHMENT_ROLE, + guild: this.guild, + moderator: moderator.id, + user: this, + reason: 'N/A', + evidence: options.evidence, + hidden: options.silent ?? false + }); + + if (!modlog) return removeRoleResponse.MODLOG_ERROR; + caseID = modlog.id; + + const punishmentEntrySuccess = await Moderation.removePunishmentEntry({ + type: 'role', + user: this, + guild: this.guild, + extraInfo: options.role.id + }); + + if (!punishmentEntrySuccess) return removeRoleResponse.PUNISHMENT_ENTRY_REMOVE_ERROR; + } + + const removeRoleSuccess = await this.roles.remove(options.role, `${moderator.tag}`); + if (!removeRoleSuccess) return removeRoleResponse.ACTION_ERROR; + + return removeRoleResponse.SUCCESS; + })(); + + if ( + !( + [ + removeRoleResponse.ACTION_ERROR, + removeRoleResponse.MODLOG_ERROR, + removeRoleResponse.PUNISHMENT_ENTRY_REMOVE_ERROR + ] as const + ).includes(ret) && + options.addToModlog && + !options.silent + ) + client.emit( + 'bushPunishRoleRemove', + this, + moderator, + this.guild, + options.reason ?? undefined, + caseID!, + options.role, + options.evidence + ); + return ret; + } + + /** + * Check whether or not a role should be added/removed from the user based on hierarchy. + * @param role The role to check if can be modified. + * @param moderator The moderator that is trying to add/remove the role. + * @returns `true` if the role should be added/removed or a string for the reason why it shouldn't. + */ + #checkIfShouldAddRole( + role: Role | Role, + moderator?: GuildMember + ): true | 'user hierarchy' | 'role managed' | 'client hierarchy' { + if (moderator && moderator.roles.highest.position <= role.position && this.guild.ownerId !== this.user.id) { + return shouldAddRoleResponse.USER_HIERARCHY; + } else if (role.managed) { + return shouldAddRoleResponse.ROLE_MANAGED; + } else if (this.guild.members.me!.roles.highest.position <= role.position) { + return shouldAddRoleResponse.CLIENT_HIERARCHY; + } + return true; + } + + /** + * Mute the user, create a modlog entry, creates a punishment entry, and dms the user. + * @param options Options for muting the user. + * @returns A status message for muting the user. + * @emits {@link BushClientEvents.bushMute} + */ + public override async bushMute(options: BushTimedPunishmentOptions): Promise { + // checks + if (!this.guild.members.me!.permissions.has(PermissionFlagsBits.ManageRoles)) return muteResponse.MISSING_PERMISSIONS; + const muteRoleID = await this.guild.getSetting('muteRole'); + if (!muteRoleID) return muteResponse.NO_MUTE_ROLE; + const muteRole = this.guild.roles.cache.get(muteRoleID); + if (!muteRole) return muteResponse.MUTE_ROLE_INVALID; + if (muteRole.position >= this.guild.members.me!.roles.highest.position || muteRole.managed) + return muteResponse.MUTE_ROLE_NOT_MANAGEABLE; + + let caseID: string | undefined = undefined; + let dmSuccessEvent: boolean | undefined = undefined; + const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); + if (!moderator) return muteResponse.CANNOT_RESOLVE_USER; + + const ret = await (async () => { + // add role + const muteSuccess = await this.roles + .add(muteRole, `[Mute] ${moderator.tag} | ${options.reason ?? 'No reason provided.'}`) + .catch(async (e) => { + await client.console.warn('muteRoleAddError', e); + client.console.debug(e); + return false; + }); + if (!muteSuccess) return muteResponse.ACTION_ERROR; + + // add modlog entry + const { log: modlog } = await Moderation.createModLogEntry({ + type: options.duration ? ModLogType.TEMP_MUTE : ModLogType.PERM_MUTE, + user: this, + moderator: moderator.id, + reason: options.reason, + duration: options.duration, + guild: this.guild, + evidence: options.evidence, + hidden: options.silent ?? false + }); + + if (!modlog) return muteResponse.MODLOG_ERROR; + caseID = modlog.id; + + // add punishment entry so they can be unmuted later + const punishmentEntrySuccess = await Moderation.createPunishmentEntry({ + type: 'mute', + user: this, + guild: this.guild, + duration: options.duration, + modlog: modlog.id + }); + + if (!punishmentEntrySuccess) return muteResponse.PUNISHMENT_ENTRY_ADD_ERROR; + + if (!options.silent) { + // dm user + const dmSuccess = await this.bushPunishDM('muted', options.reason, options.duration ?? 0, modlog.id); + dmSuccessEvent = dmSuccess; + if (!dmSuccess) return muteResponse.DM_ERROR; + } + + return muteResponse.SUCCESS; + })(); + + if ( + !([muteResponse.ACTION_ERROR, muteResponse.MODLOG_ERROR, muteResponse.PUNISHMENT_ENTRY_ADD_ERROR] as const).includes(ret) && + !options.silent + ) + client.emit( + 'bushMute', + this, + moderator, + this.guild, + options.reason ?? undefined, + caseID!, + options.duration ?? 0, + dmSuccessEvent!, + options.evidence + ); + return ret; + } + + /** + * Unmute the user, create a modlog entry, remove the punishment entry, and dm the user. + * @param options Options for unmuting the user. + * @returns A status message for unmuting the user. + * @emits {@link BushClientEvents.bushUnmute} + */ + public override async bushUnmute(options: BushPunishmentOptions): Promise { + // checks + if (!this.guild.members.me!.permissions.has(PermissionFlagsBits.ManageRoles)) return unmuteResponse.MISSING_PERMISSIONS; + const muteRoleID = await this.guild.getSetting('muteRole'); + if (!muteRoleID) return unmuteResponse.NO_MUTE_ROLE; + const muteRole = this.guild.roles.cache.get(muteRoleID); + if (!muteRole) return unmuteResponse.MUTE_ROLE_INVALID; + if (muteRole.position >= this.guild.members.me!.roles.highest.position || muteRole.managed) + return unmuteResponse.MUTE_ROLE_NOT_MANAGEABLE; + + let caseID: string | undefined = undefined; + let dmSuccessEvent: boolean | undefined = undefined; + const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); + if (!moderator) return unmuteResponse.CANNOT_RESOLVE_USER; + + const ret = await (async () => { + // remove role + const muteSuccess = await this.roles + .remove(muteRole, `[Unmute] ${moderator.tag} | ${options.reason ?? 'No reason provided.'}`) + .catch(async (e) => { + await client.console.warn('muteRoleAddError', util.formatError(e, true)); + return false; + }); + if (!muteSuccess) return unmuteResponse.ACTION_ERROR; + + // add modlog entry + const { log: modlog } = await Moderation.createModLogEntry({ + type: ModLogType.UNMUTE, + user: this, + moderator: moderator.id, + reason: options.reason, + guild: this.guild, + evidence: options.evidence, + hidden: options.silent ?? false + }); + + if (!modlog) return unmuteResponse.MODLOG_ERROR; + caseID = modlog.id; + + // remove mute entry + const removePunishmentEntrySuccess = await Moderation.removePunishmentEntry({ + type: 'mute', + user: this, + guild: this.guild + }); + + if (!removePunishmentEntrySuccess) return unmuteResponse.PUNISHMENT_ENTRY_REMOVE_ERROR; + + if (!options.silent) { + // dm user + const dmSuccess = await this.bushPunishDM('unmuted', options.reason, undefined, '', false); + dmSuccessEvent = dmSuccess; + if (!dmSuccess) return unmuteResponse.DM_ERROR; + } + + return unmuteResponse.SUCCESS; + })(); + + if ( + !( + [unmuteResponse.ACTION_ERROR, unmuteResponse.MODLOG_ERROR, unmuteResponse.PUNISHMENT_ENTRY_REMOVE_ERROR] as const + ).includes(ret) && + !options.silent + ) + client.emit( + 'bushUnmute', + this, + moderator, + this.guild, + options.reason ?? undefined, + caseID!, + dmSuccessEvent!, + options.evidence + ); + return ret; + } + + /** + * Kick the user, create a modlog entry, and dm the user. + * @param options Options for kicking the user. + * @returns A status message for kicking the user. + * @emits {@link BushClientEvents.bushKick} + */ + public override async bushKick(options: BushPunishmentOptions): Promise { + // checks + if (!this.guild.members.me?.permissions.has(PermissionFlagsBits.KickMembers) || !this.kickable) + return kickResponse.MISSING_PERMISSIONS; + + let caseID: string | undefined = undefined; + let dmSuccessEvent: boolean | undefined = undefined; + const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); + if (!moderator) return kickResponse.CANNOT_RESOLVE_USER; + const ret = await (async () => { + // add modlog entry + const { log: modlog } = await Moderation.createModLogEntry({ + type: ModLogType.KICK, + user: this, + moderator: moderator.id, + reason: options.reason, + guild: this.guild, + evidence: options.evidence, + hidden: options.silent ?? false + }); + if (!modlog) return kickResponse.MODLOG_ERROR; + caseID = modlog.id; + + // dm user + const dmSuccess = options.silent ? null : await this.bushPunishDM('kicked', options.reason, undefined, modlog.id); + dmSuccessEvent = dmSuccess ?? undefined; + + // kick + const kickSuccess = await this.kick(`${moderator?.tag} | ${options.reason ?? 'No reason provided.'}`).catch(() => false); + if (!kickSuccess) return kickResponse.ACTION_ERROR; + + if (dmSuccess === false) return kickResponse.DM_ERROR; + return kickResponse.SUCCESS; + })(); + if (!([kickResponse.ACTION_ERROR, kickResponse.MODLOG_ERROR] as const).includes(ret) && !options.silent) + client.emit( + 'bushKick', + this, + moderator, + this.guild, + options.reason ?? undefined, + caseID!, + dmSuccessEvent!, + options.evidence + ); + return ret; + } + + /** + * Ban the user, create a modlog entry, create a punishment entry, and dm the user. + * @param options Options for banning the user. + * @returns A status message for banning the user. + * @emits {@link BushClientEvents.bushBan} + */ + public override async bushBan(options: BushBanOptions): Promise> { + // checks + if (!this.guild.members.me!.permissions.has(PermissionFlagsBits.BanMembers) || !this.bannable) + return banResponse.MISSING_PERMISSIONS; + + let caseID: string | undefined = undefined; + let dmSuccessEvent: boolean | undefined = undefined; + const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); + if (!moderator) return banResponse.CANNOT_RESOLVE_USER; + + // ignore result, they should still be banned even if their mute cannot be removed + await this.bushUnmute({ + reason: 'User is about to be banned, a mute is no longer necessary.', + moderator: this.guild.members.me!, + silent: true + }); + + const ret = await (async () => { + // add modlog entry + const { log: modlog } = await Moderation.createModLogEntry({ + type: options.duration ? ModLogType.TEMP_BAN : ModLogType.PERM_BAN, + user: this, + moderator: moderator.id, + reason: options.reason, + duration: options.duration, + guild: this.guild, + evidence: options.evidence, + hidden: options.silent ?? false + }); + if (!modlog) return banResponse.MODLOG_ERROR; + caseID = modlog.id; + + // dm user + const dmSuccess = options.silent + ? null + : await this.bushPunishDM('banned', options.reason, options.duration ?? 0, modlog.id); + dmSuccessEvent = dmSuccess ?? undefined; + + // ban + const banSuccess = await this.ban({ + reason: `${moderator.tag} | ${options.reason ?? 'No reason provided.'}`, + deleteMessageDays: options.deleteDays + }).catch(() => false); + if (!banSuccess) return banResponse.ACTION_ERROR; + + // add punishment entry so they can be unbanned later + const punishmentEntrySuccess = await Moderation.createPunishmentEntry({ + type: 'ban', + user: this, + guild: this.guild, + duration: options.duration, + modlog: modlog.id + }); + if (!punishmentEntrySuccess) return banResponse.PUNISHMENT_ENTRY_ADD_ERROR; + + if (!dmSuccess) return banResponse.DM_ERROR; + return banResponse.SUCCESS; + })(); + if ( + !([banResponse.ACTION_ERROR, banResponse.MODLOG_ERROR, banResponse.PUNISHMENT_ENTRY_ADD_ERROR] as const).includes(ret) && + !options.silent + ) + client.emit( + 'bushBan', + this, + moderator, + this.guild, + options.reason ?? undefined, + caseID!, + options.duration ?? 0, + dmSuccessEvent!, + options.evidence + ); + return ret; + } + + /** + * Prevents a user from speaking in a channel. + * @param options Options for blocking the user. + */ + public override async bushBlock(options: BlockOptions): Promise { + const channel = this.guild.channels.resolve(options.channel); + if (!channel || (!channel.isTextBased() && !channel.isThread())) return blockResponse.INVALID_CHANNEL; + + // checks + if (!channel.permissionsFor(this.guild.members.me!)!.has(PermissionFlagsBits.ManageChannels)) + return blockResponse.MISSING_PERMISSIONS; + + let caseID: string | undefined = undefined; + let dmSuccessEvent: boolean | undefined = undefined; + const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); + if (!moderator) return blockResponse.CANNOT_RESOLVE_USER; + + const ret = await (async () => { + // change channel permissions + const channelToUse = channel.isThread() ? channel.parent! : channel; + const perm = channel.isThread() ? { SendMessagesInThreads: false } : { SendMessages: false }; + const blockSuccess = await channelToUse.permissionOverwrites + .edit(this, perm, { reason: `[Block] ${moderator.tag} | ${options.reason ?? 'No reason provided.'}` }) + .catch(() => false); + if (!blockSuccess) return blockResponse.ACTION_ERROR; + + // add modlog entry + const { log: modlog } = await Moderation.createModLogEntry({ + type: options.duration ? ModLogType.TEMP_CHANNEL_BLOCK : ModLogType.PERM_CHANNEL_BLOCK, + user: this, + moderator: moderator.id, + reason: options.reason, + guild: this.guild, + evidence: options.evidence, + hidden: options.silent ?? false + }); + if (!modlog) return blockResponse.MODLOG_ERROR; + caseID = modlog.id; + + // add punishment entry so they can be unblocked later + const punishmentEntrySuccess = await Moderation.createPunishmentEntry({ + type: 'block', + user: this, + guild: this.guild, + duration: options.duration, + modlog: modlog.id, + extraInfo: channel.id + }); + if (!punishmentEntrySuccess) return blockResponse.PUNISHMENT_ENTRY_ADD_ERROR; + + // dm user + const dmSuccess = options.silent + ? null + : await Moderation.punishDM({ + punishment: 'blocked', + reason: options.reason ?? undefined, + duration: options.duration ?? 0, + modlog: modlog.id, + guild: this.guild, + user: this, + sendFooter: true, + channel: channel.id + }); + dmSuccessEvent = !!dmSuccess; + if (!dmSuccess) return blockResponse.DM_ERROR; + + return blockResponse.SUCCESS; + })(); + + if ( + !([blockResponse.ACTION_ERROR, blockResponse.MODLOG_ERROR, blockResponse.PUNISHMENT_ENTRY_ADD_ERROR] as const).includes( + ret + ) && + !options.silent + ) + client.emit( + 'bushBlock', + this, + moderator, + this.guild, + options.reason ?? undefined, + caseID!, + options.duration ?? 0, + dmSuccessEvent!, + channel, + options.evidence + ); + return ret; + } + + /** + * Allows a user to speak in a channel. + * @param options Options for unblocking the user. + */ + public override async bushUnblock(options: UnblockOptions): Promise { + const _channel = this.guild.channels.resolve(options.channel); + if (!_channel || (_channel.type !== ChannelType.GuildText && !_channel.isThread())) return unblockResponse.INVALID_CHANNEL; + const channel = _channel as GuildTextBasedChannel; + + // checks + if (!channel.permissionsFor(this.guild.members.me!)!.has(PermissionFlagsBits.ManageChannels)) + return unblockResponse.MISSING_PERMISSIONS; + + let caseID: string | undefined = undefined; + let dmSuccessEvent: boolean | undefined = undefined; + const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); + if (!moderator) return unblockResponse.CANNOT_RESOLVE_USER; + + const ret = await (async () => { + // change channel permissions + const channelToUse = channel.isThread() ? channel.parent! : channel; + const perm = channel.isThread() ? { SendMessagesInThreads: null } : { SendMessages: null }; + const blockSuccess = await channelToUse.permissionOverwrites + .edit(this, perm, { reason: `[Unblock] ${moderator.tag} | ${options.reason ?? 'No reason provided.'}` }) + .catch(() => false); + if (!blockSuccess) return unblockResponse.ACTION_ERROR; + + // add modlog entry + const { log: modlog } = await Moderation.createModLogEntry({ + type: ModLogType.CHANNEL_UNBLOCK, + user: this, + moderator: moderator.id, + reason: options.reason, + guild: this.guild, + evidence: options.evidence, + hidden: options.silent ?? false + }); + if (!modlog) return unblockResponse.MODLOG_ERROR; + caseID = modlog.id; + + // remove punishment entry + const punishmentEntrySuccess = await Moderation.removePunishmentEntry({ + type: 'block', + user: this, + guild: this.guild, + extraInfo: channel.id + }); + if (!punishmentEntrySuccess) return unblockResponse.ACTION_ERROR; + + // dm user + const dmSuccess = options.silent + ? null + : await Moderation.punishDM({ + punishment: 'unblocked', + reason: options.reason ?? undefined, + guild: this.guild, + user: this, + sendFooter: false, + channel: channel.id + }); + dmSuccessEvent = !!dmSuccess; + if (!dmSuccess) return blockResponse.DM_ERROR; + + dmSuccessEvent = !!dmSuccess; + if (!dmSuccess) return unblockResponse.DM_ERROR; + + return unblockResponse.SUCCESS; + })(); + + if ( + !([unblockResponse.ACTION_ERROR, unblockResponse.MODLOG_ERROR, unblockResponse.ACTION_ERROR] as const).includes(ret) && + !options.silent + ) + client.emit( + 'bushUnblock', + this, + moderator, + this.guild, + options.reason ?? undefined, + caseID!, + dmSuccessEvent!, + channel, + options.evidence + ); + return ret; + } + + /** + * Mutes a user using discord's timeout feature. + * @param options Options for timing out the user. + */ + public override async bushTimeout(options: BushTimeoutOptions): Promise { + // checks + if (!this.guild.members.me!.permissions.has(PermissionFlagsBits.ModerateMembers)) return timeoutResponse.MISSING_PERMISSIONS; + + const twentyEightDays = Time.Day * 28; + if (options.duration > twentyEightDays) return timeoutResponse.INVALID_DURATION; + + let caseID: string | undefined = undefined; + let dmSuccessEvent: boolean | undefined = undefined; + const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); + if (!moderator) return timeoutResponse.CANNOT_RESOLVE_USER; + + const ret = await (async () => { + // timeout + const timeoutSuccess = await this.timeout( + options.duration, + `${moderator.tag} | ${options.reason ?? 'No reason provided.'}` + ).catch(() => false); + if (!timeoutSuccess) return timeoutResponse.ACTION_ERROR; + + // add modlog entry + const { log: modlog } = await Moderation.createModLogEntry({ + type: ModLogType.TIMEOUT, + user: this, + moderator: moderator.id, + reason: options.reason, + duration: options.duration, + guild: this.guild, + evidence: options.evidence, + hidden: options.silent ?? false + }); + + if (!modlog) return timeoutResponse.MODLOG_ERROR; + caseID = modlog.id; + + if (!options.silent) { + // dm user + const dmSuccess = await this.bushPunishDM('timedout', options.reason, options.duration, modlog.id); + dmSuccessEvent = dmSuccess; + if (!dmSuccess) return timeoutResponse.DM_ERROR; + } + + return timeoutResponse.SUCCESS; + })(); + + if (!([timeoutResponse.ACTION_ERROR, timeoutResponse.MODLOG_ERROR] as const).includes(ret) && !options.silent) + client.emit( + 'bushTimeout', + this, + moderator, + this.guild, + options.reason ?? undefined, + caseID!, + options.duration ?? 0, + dmSuccessEvent!, + options.evidence + ); + return ret; + } + + /** + * Removes a timeout from a user. + * @param options Options for removing the timeout. + */ + public override async bushRemoveTimeout(options: BushPunishmentOptions): Promise { + // checks + if (!this.guild.members.me!.permissions.has(PermissionFlagsBits.ModerateMembers)) + return removeTimeoutResponse.MISSING_PERMISSIONS; + + let caseID: string | undefined = undefined; + let dmSuccessEvent: boolean | undefined = undefined; + const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); + if (!moderator) return removeTimeoutResponse.CANNOT_RESOLVE_USER; + + const ret = await (async () => { + // remove timeout + const timeoutSuccess = await this.timeout(null, `${moderator.tag} | ${options.reason ?? 'No reason provided.'}`).catch( + () => false + ); + if (!timeoutSuccess) return removeTimeoutResponse.ACTION_ERROR; + + // add modlog entry + const { log: modlog } = await Moderation.createModLogEntry({ + type: ModLogType.REMOVE_TIMEOUT, + user: this, + moderator: moderator.id, + reason: options.reason, + guild: this.guild, + evidence: options.evidence, + hidden: options.silent ?? false + }); + + if (!modlog) return removeTimeoutResponse.MODLOG_ERROR; + caseID = modlog.id; + + if (!options.silent) { + // dm user + const dmSuccess = await this.bushPunishDM('untimedout', options.reason, undefined, '', false); + dmSuccessEvent = dmSuccess; + if (!dmSuccess) return removeTimeoutResponse.DM_ERROR; + } + + return removeTimeoutResponse.SUCCESS; + })(); + + if (!([removeTimeoutResponse.ACTION_ERROR, removeTimeoutResponse.MODLOG_ERROR] as const).includes(ret) && !options.silent) + client.emit( + 'bushRemoveTimeout', + this, + moderator, + this.guild, + options.reason ?? undefined, + caseID!, + dmSuccessEvent!, + options.evidence + ); + return ret; + } + + /** + * Whether or not the user is an owner of the bot. + */ + public override isOwner(): boolean { + return client.isOwner(this); + } + + /** + * Whether or not the user is a super user of the bot. + */ + public override isSuperUser(): boolean { + return client.isSuperUser(this); + } +} + +/** + * Options for punishing a user. + */ +export interface BushPunishmentOptions { + /** + * The reason for the punishment. + */ + reason?: string | null; + + /** + * The moderator who punished the user. + */ + moderator?: GuildMember; + + /** + * Evidence for the punishment. + */ + evidence?: string; + + /** + * Makes the punishment silent by not sending the user a punishment dm and not broadcasting the event to be logged. + */ + silent?: boolean; +} + +/** + * Punishment options for punishments that can be temporary. + */ +export interface BushTimedPunishmentOptions extends BushPunishmentOptions { + /** + * The duration of the punishment. + */ + duration?: number; +} + +/** + * Options for a role add punishment. + */ +export interface AddRoleOptions extends BushTimedPunishmentOptions { + /** + * The role to add to the user. + */ + role: Role; + + /** + * Whether to create a modlog entry for this punishment. + */ + addToModlog: boolean; +} + +/** + * Options for a role remove punishment. + */ +export interface RemoveRoleOptions extends BushTimedPunishmentOptions { + /** + * The role to remove from the user. + */ + role: Role; + + /** + * Whether to create a modlog entry for this punishment. + */ + addToModlog: boolean; +} + +/** + * Options for banning a user. + */ +export interface BushBanOptions extends BushTimedPunishmentOptions { + /** + * The number of days to delete the user's messages for. + */ + deleteDays?: number; +} + +/** + * Options for blocking a user from a channel. + */ +export interface BlockOptions extends BushTimedPunishmentOptions { + /** + * The channel to block the user from. + */ + channel: GuildChannelResolvable; +} + +/** + * Options for unblocking a user from a channel. + */ +export interface UnblockOptions extends BushPunishmentOptions { + /** + * The channel to unblock the user from. + */ + channel: GuildChannelResolvable; +} + +/** + * Punishment options for punishments that can be temporary. + */ +export interface BushTimeoutOptions extends BushPunishmentOptions { + /** + * The duration of the punishment. + */ + duration: number; +} + +type ValueOf = T[keyof T]; + +export const basePunishmentResponse = Object.freeze({ + SUCCESS: 'success', + MODLOG_ERROR: 'error creating modlog entry', + ACTION_ERROR: 'error performing action', + CANNOT_RESOLVE_USER: 'cannot resolve user' +} as const); + +export const dmResponse = Object.freeze({ + ...basePunishmentResponse, + DM_ERROR: 'failed to dm' +} as const); + +export const permissionsResponse = Object.freeze({ + MISSING_PERMISSIONS: 'missing permissions' +} as const); + +export const punishmentEntryAdd = Object.freeze({ + PUNISHMENT_ENTRY_ADD_ERROR: 'error creating punishment entry' +} as const); + +export const punishmentEntryRemove = Object.freeze({ + PUNISHMENT_ENTRY_REMOVE_ERROR: 'error removing punishment entry' +} as const); + +export const shouldAddRoleResponse = Object.freeze({ + USER_HIERARCHY: 'user hierarchy', + CLIENT_HIERARCHY: 'client hierarchy', + ROLE_MANAGED: 'role managed' +} as const); + +export const baseBlockResponse = Object.freeze({ + INVALID_CHANNEL: 'invalid channel' +} as const); + +export const baseMuteResponse = Object.freeze({ + NO_MUTE_ROLE: 'no mute role', + MUTE_ROLE_INVALID: 'invalid mute role', + MUTE_ROLE_NOT_MANAGEABLE: 'mute role not manageable' +} as const); + +export const warnResponse = Object.freeze({ + ...dmResponse +} as const); + +export const addRoleResponse = Object.freeze({ + ...basePunishmentResponse, + ...permissionsResponse, + ...shouldAddRoleResponse, + ...punishmentEntryAdd +} as const); + +export const removeRoleResponse = Object.freeze({ + ...basePunishmentResponse, + ...permissionsResponse, + ...shouldAddRoleResponse, + ...punishmentEntryRemove +} as const); + +export const muteResponse = Object.freeze({ + ...dmResponse, + ...permissionsResponse, + ...baseMuteResponse, + ...punishmentEntryAdd +} as const); + +export const unmuteResponse = Object.freeze({ + ...dmResponse, + ...permissionsResponse, + ...baseMuteResponse, + ...punishmentEntryRemove +} as const); + +export const kickResponse = Object.freeze({ + ...dmResponse, + ...permissionsResponse +} as const); + +export const banResponse = Object.freeze({ + ...dmResponse, + ...permissionsResponse, + ...punishmentEntryAdd, + ALREADY_BANNED: 'already banned' +} as const); + +export const blockResponse = Object.freeze({ + ...dmResponse, + ...permissionsResponse, + ...baseBlockResponse, + ...punishmentEntryAdd +}); + +export const unblockResponse = Object.freeze({ + ...dmResponse, + ...permissionsResponse, + ...baseBlockResponse, + ...punishmentEntryRemove +}); + +export const timeoutResponse = Object.freeze({ + ...dmResponse, + ...permissionsResponse, + INVALID_DURATION: 'duration too long' +} as const); + +export const removeTimeoutResponse = Object.freeze({ + ...dmResponse, + ...permissionsResponse +} as const); + +/** + * Response returned when warning a user. + */ +export type WarnResponse = ValueOf; + +/** + * Response returned when adding a role to a user. + */ +export type AddRoleResponse = ValueOf; + +/** + * Response returned when removing a role from a user. + */ +export type RemoveRoleResponse = ValueOf; + +/** + * Response returned when muting a user. + */ +export type MuteResponse = ValueOf; + +/** + * Response returned when unmuting a user. + */ +export type UnmuteResponse = ValueOf; + +/** + * Response returned when kicking a user. + */ +export type KickResponse = ValueOf; + +/** + * Response returned when banning a user. + */ +export type BanResponse = ValueOf; + +/** + * Response returned when blocking a user. + */ +export type BlockResponse = ValueOf; + +/** + * Response returned when unblocking a user. + */ +export type UnblockResponse = ValueOf; + +/** + * Response returned when timing out a user. + */ +export type TimeoutResponse = ValueOf; + +/** + * Response returned when removing a timeout from a user. + */ +export type RemoveTimeoutResponse = ValueOf; + +/** + * @typedef {BushClientEvents} VSCodePleaseDontRemove + */ diff --git a/src/lib/extensions/discord.js/ExtendedMessage.ts b/src/lib/extensions/discord.js/ExtendedMessage.ts new file mode 100644 index 0000000..4431077 --- /dev/null +++ b/src/lib/extensions/discord.js/ExtendedMessage.ts @@ -0,0 +1,12 @@ +import { CommandUtil } from 'discord-akairo'; +import { Message, type Client } from 'discord.js'; +import { type RawMessageData } from 'discord.js/typings/rawDataTypes.js'; + +export class ExtendedMessage extends Message { + public declare util: CommandUtil; + + public constructor(client_: Client, data: RawMessageData) { + super(client_, data); + this.util = new CommandUtil(client.commandHandler, this); + } +} diff --git a/src/lib/extensions/discord.js/ExtendedUser.ts b/src/lib/extensions/discord.js/ExtendedUser.ts new file mode 100644 index 0000000..556ab85 --- /dev/null +++ b/src/lib/extensions/discord.js/ExtendedUser.ts @@ -0,0 +1,35 @@ +import { User, type Partialize } from 'discord.js'; + +declare module 'discord.js' { + export interface User { + /** + * Indicates whether the user is an owner of the bot. + */ + isOwner(): boolean; + /** + * Indicates whether the user is a superuser of the bot. + */ + isSuperUser(): boolean; + } +} + +export type PartialBushUser = Partialize; + +/** + * Represents a user on Discord. + */ +export class ExtendedUser extends User { + /** + * Indicates whether the user is an owner of the bot. + */ + public override isOwner(): boolean { + return client.isOwner(this); + } + + /** + * Indicates whether the user is a superuser of the bot. + */ + public override isSuperUser(): boolean { + return client.isSuperUser(this); + } +} diff --git a/src/lib/extensions/discord.js/other.ts b/src/lib/extensions/discord.js/other.ts deleted file mode 100644 index 0560ffc..0000000 --- a/src/lib/extensions/discord.js/other.ts +++ /dev/null @@ -1,188 +0,0 @@ -import type { - BushApplicationCommand, - BushCategoryChannel, - BushDMChannel, - BushGuild, - BushGuildEmoji, - BushGuildMember, - BushMessage, - BushNewsChannel, - BushReactionEmoji, - BushRole, - BushStageChannel, - BushTextChannel, - BushThreadChannel, - BushThreadMember, - BushUser, - BushVoiceChannel, - PartialBushDMChannel -} from '#lib'; -import { APIMessage } from 'discord-api-types/v10'; -import type { - ApplicationCommandResolvable, - CacheType, - CacheTypeReducer, - ChannelResolvable, - ChannelType, - Collection, - EmojiIdentifierResolvable, - EmojiResolvable, - FetchedThreads, - GuildChannelResolvable, - GuildMemberResolvable, - GuildTextChannelResolvable, - MessageResolvable, - PartialGroupDMChannel, - RoleResolvable, - Snowflake, - ThreadChannelResolvable, - ThreadMemberResolvable, - UserResolvable -} from 'discord.js'; - -/** - * Data that resolves to give a ThreadMember object. - */ -export type BushThreadMemberResolvable = ThreadMemberResolvable | BushThreadMember | BushUserResolvable; - -/** - * Data that resolves to give a User object. - */ -export type BushUserResolvable = UserResolvable | BushUser | Snowflake | BushMessage | BushGuildMember | BushThreadMember; - -/** - * Data that resolves to give a GuildMember object. - */ -export type BushGuildMemberResolvable = GuildMemberResolvable | BushGuildMember | BushUserResolvable; - -/** - * Data that can be resolved to a Role object. - */ -export type BushRoleResolvable = RoleResolvable | BushRole | Snowflake; - -/** - * Data that can be resolved to a Message object. - */ -export type BushMessageResolvable = MessageResolvable | BushMessage | Snowflake; - -/** - * Data that can be resolved into a GuildEmoji object. - */ -export type BushEmojiResolvable = EmojiResolvable | Snowflake | BushGuildEmoji | BushReactionEmoji; - -/** - * Data that can be resolved to give an emoji identifier. This can be: - * * The unicode representation of an emoji - * * The ``, `<:name:id>`, `a:name:id` or `name:id` emoji identifier string of an emoji - * * An EmojiResolvable - */ -export type BushEmojiIdentifierResolvable = EmojiIdentifierResolvable | string | BushEmojiResolvable; - -/** - * Data that can be resolved to a Thread Channel object. - */ -export type BushThreadChannelResolvable = ThreadChannelResolvable | BushThreadChannel | Snowflake; - -/** - * Data that resolves to give an ApplicationCommand object. - */ -export type BushApplicationCommandResolvable = ApplicationCommandResolvable | BushApplicationCommand | Snowflake; - -/** - * Data that can be resolved to a GuildTextChannel object. - */ -export type BushGuildTextChannelResolvable = GuildTextChannelResolvable | BushTextChannel | BushNewsChannel | Snowflake; - -/** - * Data that can be resolved to give a Channel object. - */ -export type BushChannelResolvable = ChannelResolvable | BushAnyChannel | Snowflake; - -/** - * Data that can be resolved to give a Guild Channel object. - */ -export type BushGuildChannelResolvable = GuildChannelResolvable | Snowflake | BushGuildBasedChannel; - -export type BushAnyChannel = - | BushCategoryChannel - | BushDMChannel - | PartialBushDMChannel - | PartialGroupDMChannel - | BushNewsChannel - | BushStageChannel - | BushTextChannel - | BushThreadChannel - | BushVoiceChannel; - -/** - * The channels that are text-based. - */ -export type BushTextBasedChannel = - | BushDMChannel - | PartialBushDMChannel - | BushNewsChannel - | BushTextChannel - | BushThreadChannel - | BushVoiceChannel; - -/** - * The types of channels that are text-based. - */ -export type BushTextBasedChannelTypes = BushTextBasedChannel['type']; - -export type BushVoiceBasedChannel = Extract; - -export type BushGuildBasedChannel = Extract; - -export type BushNonCategoryGuildBasedChannel = Exclude; - -export type BushNonThreadGuildBasedChannel = Exclude; - -export type BushGuildTextBasedChannel = Extract; - -/** - * Data that can be resolved to a Text Channel object. - */ -export type BushTextChannelResolvable = Snowflake | BushTextChannel; - -/** - * Data that can be resolved to a GuildVoiceChannel object. - */ -export type BushGuildVoiceChannelResolvable = BushVoiceBasedChannel | Snowflake; - -export interface BushMappedChannelCategoryTypes { - [ChannelType.GuildNews]: BushNewsChannel; - [ChannelType.GuildVoice]: BushVoiceChannel; - [ChannelType.GuildText]: BushTextChannel; - [ChannelType.GuildStageVoice]: BushStageChannel; - [ChannelType.GuildForum]: never; // TODO: Fix when guild forums come out -} - -export type BushMappedGuildChannelTypes = { - [ChannelType.GuildCategory]: BushCategoryChannel; -} & BushMappedChannelCategoryTypes; - -/** - * The data returned from a thread fetch that returns multiple threads. - */ -export interface BushFetchedThreads extends FetchedThreads { - /** - * The threads that were fetched, with any members returned - */ - threads: Collection; - - /** - * Whether there are potentially additional threads that require a subsequent call - */ - hasMore?: boolean; -} - -export type BushGuildCacheMessage = CacheTypeReducer< - Cached, - BushMessage, - APIMessage, - BushMessage | APIMessage, - BushMessage | APIMessage ->; - -export { ApplicationCommandOptionType as SlashType } from 'discord.js'; diff --git a/src/lib/index.ts b/src/lib/index.ts index 8a6b75d..221f360 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -12,61 +12,18 @@ export * from './extensions/discord-akairo/BushClient.js'; export * from './extensions/discord-akairo/BushClientUtil.js'; export * from './extensions/discord-akairo/BushCommand.js'; export * from './extensions/discord-akairo/BushCommandHandler.js'; -export * from './extensions/discord-akairo/BushCommandUtil.js'; export * from './extensions/discord-akairo/BushInhibitor.js'; export * from './extensions/discord-akairo/BushInhibitorHandler.js'; export * from './extensions/discord-akairo/BushListener.js'; export * from './extensions/discord-akairo/BushListenerHandler.js'; -export * from './extensions/discord-akairo/BushSlashMessage.js'; export * from './extensions/discord-akairo/BushTask.js'; export * from './extensions/discord-akairo/BushTaskHandler.js'; -export * from './extensions/discord.js/BushActivity.js'; -export * from './extensions/discord.js/BushApplicationCommand.js'; -export type { BushApplicationCommandManager } from './extensions/discord.js/BushApplicationCommandManager.js'; -export type { BushApplicationCommandPermissionsManager } from './extensions/discord.js/BushApplicationCommandPermissionsManager.js'; -export type { BushBaseGuildEmojiManager } from './extensions/discord.js/BushBaseGuildEmojiManager.js'; -export type { BushBaseGuildVoiceChannel } from './extensions/discord.js/BushBaseGuildVoiceChannel.js'; -export * from './extensions/discord.js/BushButtonInteraction.js'; -export * from './extensions/discord.js/BushCategoryChannel.js'; -export type { BushCategoryChannelChildManager } from './extensions/discord.js/BushCategoryChannelChildManager.js'; -export type { BushChannel } from './extensions/discord.js/BushChannel.js'; -export type { BushChannelManager } from './extensions/discord.js/BushChannelManager.js'; -export * from './extensions/discord.js/BushChatInputCommandInteraction.js'; +export * from './extensions/discord-akairo/SlashMessage.js'; export type { BushClientEvents } from './extensions/discord.js/BushClientEvents.js'; -export type { BushClientUser } from './extensions/discord.js/BushClientUser.js'; -export * from './extensions/discord.js/BushDMChannel.js'; -export * from './extensions/discord.js/BushEmoji.js'; -export * from './extensions/discord.js/BushGuild.js'; -export type { BushGuildApplicationCommandManager } from './extensions/discord.js/BushGuildApplicationCommandManager.js'; -export type { BushGuildBan } from './extensions/discord.js/BushGuildBan.js'; -export * from './extensions/discord.js/BushGuildChannel.js'; -export type { BushGuildChannelManager } from './extensions/discord.js/BushGuildChannelManager.js'; -export * from './extensions/discord.js/BushGuildEmoji.js'; -export type { BushGuildEmojiRoleManager } from './extensions/discord.js/BushGuildEmojiRoleManager.js'; -export type { BushGuildManager } from './extensions/discord.js/BushGuildManager.js'; -export * from './extensions/discord.js/BushGuildMember.js'; -export type { BushGuildMemberManager } from './extensions/discord.js/BushGuildMemberManager.js'; -export * from './extensions/discord.js/BushMessage.js'; -export type { BushMessageManager } from './extensions/discord.js/BushMessageManager.js'; -export * from './extensions/discord.js/BushMessageReaction.js'; -export * from './extensions/discord.js/BushModalSubmitInteraction.js'; -export * from './extensions/discord.js/BushNewsChannel.js'; -export * from './extensions/discord.js/BushPresence.js'; -export * from './extensions/discord.js/BushReactionEmoji.js'; -export * from './extensions/discord.js/BushRole.js'; -export * from './extensions/discord.js/BushSelectMenuInteraction.js'; -export * from './extensions/discord.js/BushStageChannel.js'; -export * from './extensions/discord.js/BushStageInstance.js'; -export * from './extensions/discord.js/BushTextChannel.js'; -export * from './extensions/discord.js/BushThreadChannel.js'; -export type { BushThreadManager } from './extensions/discord.js/BushThreadManager.js'; -export * from './extensions/discord.js/BushThreadMember.js'; -export type { BushThreadMemberManager } from './extensions/discord.js/BushThreadMemberManager.js'; -export * from './extensions/discord.js/BushUser.js'; -export type { BushUserManager } from './extensions/discord.js/BushUserManager.js'; -export * from './extensions/discord.js/BushVoiceChannel.js'; -export * from './extensions/discord.js/BushVoiceState.js'; -export * from './extensions/discord.js/other.js'; +export * from './extensions/discord.js/ExtendedGuild.js'; +export * from './extensions/discord.js/ExtendedGuildMember.js'; +export * from './extensions/discord.js/ExtendedMessage.js'; +export * from './extensions/discord.js/ExtendedUser.js'; export * from './models/BaseModel.js'; export * from './models/instance/ActivePunishment.js'; export * from './models/instance/Guild.js'; diff --git a/src/lib/utils/BushLogger.ts b/src/lib/utils/BushLogger.ts index 91c23d3..073b8e2 100644 --- a/src/lib/utils/BushLogger.ts +++ b/src/lib/utils/BushLogger.ts @@ -4,7 +4,7 @@ import { EmbedBuilder, Util, type Message, type PartialTextBasedChannelFields } import repl, { REPLServer, REPL_MODE_STRICT } from 'repl'; import { WriteStream } from 'tty'; import { inspect } from 'util'; -import { type BushSendMessageType } from '../extensions/discord-akairo/BushClient.js'; +import { type SendMessageType } from '../extensions/discord-akairo/BushClient.js'; let REPL: REPLServer; let replGone = false; @@ -147,7 +147,7 @@ export class BushLogger { * @param message The parameter to pass to {@link PartialTextBasedChannelFields.send}. * @returns The message sent. */ - public static async channelLog(message: BushSendMessageType): Promise { + public static async channelLog(message: SendMessageType): Promise { const channel = await util.getConfigChannel('log'); return await channel.send(message).catch(() => null); } @@ -157,7 +157,7 @@ export class BushLogger { * @param message The parameter to pass to {@link PartialTextBasedChannelFields.send}. * @returns The message sent. */ - public static async channelError(message: BushSendMessageType): Promise { + public static async channelError(message: SendMessageType): Promise { const channel = await util.getConfigChannel('error'); if (!channel) { void this.error( diff --git a/src/listeners/bush/joinAutoBan.ts b/src/listeners/bush/joinAutoBan.ts index 0b38766..83efa06 100644 --- a/src/listeners/bush/joinAutoBan.ts +++ b/src/listeners/bush/joinAutoBan.ts @@ -1,4 +1,5 @@ -import { AllowedMentions, BushListener, type BushClientEvents, type BushTextChannel } from '#lib'; +import { AllowedMentions, BushListener, type BushClientEvents } from '#lib'; +import { TextChannel } from 'discord.js'; export default class JoinAutoBanListener extends BushListener { public constructor() { @@ -52,7 +53,7 @@ export default class JoinAutoBanListener extends BushListener { ? `${util.emojis.warn} Banned ${util.format.input(member.user.tag)} however I could not send them a dm.` : `${util.emojis.success} Successfully banned ${util.format.input(member.user.tag)}.`; - (guild.channels.cache.find((c) => c.name === 'general')) + (guild.channels.cache.find((c) => c.name === 'general')) ?.send({ content, allowedMentions: AllowedMentions.none() }) .catch(() => {}); } diff --git a/src/listeners/bush/supportThread.ts b/src/listeners/bush/supportThread.ts index e809176..46ac96f 100644 --- a/src/listeners/bush/supportThread.ts +++ b/src/listeners/bush/supportThread.ts @@ -1,7 +1,7 @@ -import { BushListener, BushTextChannel, type BushClientEvents } from '#lib'; +import { BushListener, type BushClientEvents } from '#lib'; import { stripIndent } from '#tags'; import assert from 'assert'; -import { EmbedBuilder, MessageType, PermissionFlagsBits } from 'discord.js'; +import { EmbedBuilder, MessageType, PermissionFlagsBits, TextChannel } from 'discord.js'; export default class SupportThreadListener extends BushListener { public constructor() { @@ -12,7 +12,7 @@ export default class SupportThreadListener extends BushListener { }); } - public override async exec(...[message]: BushClientEvents['messageCreate']): Promise | undefined> { + public override async exec(...[message]: BushClientEvents['messageCreate']): Promise { if (!client.config.isProduction || !message.inGuild()) return; if (![MessageType.Default, MessageType.Reply].includes(message.type)) return; if (message.thread) return; @@ -30,7 +30,7 @@ export default class SupportThreadListener extends BushListener { ) return; - assert(message.channel instanceof BushTextChannel); + assert(message.channel instanceof TextChannel); if (!message.channel.permissionsFor(message.guild.members.me!).has(PermissionFlagsBits.CreatePublicThreads)) return; const thread = await message diff --git a/src/listeners/bush/userUpdateAutoBan.ts b/src/listeners/bush/userUpdateAutoBan.ts index 68dfa38..b9485b3 100644 --- a/src/listeners/bush/userUpdateAutoBan.ts +++ b/src/listeners/bush/userUpdateAutoBan.ts @@ -1,4 +1,5 @@ -import { AllowedMentions, BushGuildMember, BushListener, type BushClientEvents, type BushTextChannel } from '#lib'; +import { AllowedMentions, BushListener, type BushClientEvents } from '#lib'; +import { GuildMember, type TextChannel } from 'discord.js'; export default class UserUpdateAutoBanListener extends BushListener { public constructor() { @@ -21,7 +22,7 @@ export default class UserUpdateAutoBanListener extends BushListener { .get(client.consts.mappings.guilds.bush) ?.members.fetch(newUser.id) .catch(() => undefined); - if (!member || !(member instanceof BushGuildMember)) return; + if (!member || !(member instanceof GuildMember)) return; const guild = member.guild; @@ -58,7 +59,7 @@ export default class UserUpdateAutoBanListener extends BushListener { ? `${util.emojis.warn} Banned ${util.format.input(member.user.tag)} however I could not send them a dm.` : `${util.emojis.success} Successfully banned ${util.format.input(member.user.tag)}.`; - (guild.channels.cache.find((c) => c.name === 'general')) + (guild.channels.cache.find((c) => c.name === 'general')) ?.send({ content, allowedMentions: AllowedMentions.none() }) .catch(() => {}); } diff --git a/src/listeners/commands/commandBlocked.ts b/src/listeners/commands/commandBlocked.ts index 0d3c1d9..feb85d1 100644 --- a/src/listeners/commands/commandBlocked.ts +++ b/src/listeners/commands/commandBlocked.ts @@ -1,5 +1,5 @@ -import { BushListener, type BushCommand, type BushCommandHandlerEvents, type BushMessage, type BushSlashMessage } from '#lib'; -import { type InteractionReplyOptions, type Message, type MessagePayload, type ReplyMessageOptions } from 'discord.js'; +import { BushListener, type BushCommand, type BushCommandHandlerEvents, type CommandMessage, type SlashMessage } from '#lib'; +import { type InteractionReplyOptions, type MessagePayload, type ReplyMessageOptions } from 'discord.js'; export default class CommandBlockedListener extends BushListener { public constructor() { @@ -14,11 +14,7 @@ export default class CommandBlockedListener extends BushListener { return await CommandBlockedListener.handleBlocked(message, command, reason); } - public static async handleBlocked( - message: Message | BushMessage | BushSlashMessage, - command: BushCommand | null, - reason?: string - ) { + public static async handleBlocked(message: CommandMessage | SlashMessage, command: BushCommand | null, reason?: string) { const isSlash = !!command && !!message.util?.isSlash; void client.console.info( @@ -64,7 +60,7 @@ export default class CommandBlockedListener extends BushListener { content: `${util.emojis.error} You cannot use this bot in this channel.`, ephemeral: true }) - : await (message as BushMessage).react(util.emojis.cross); + : await (message as CommandMessage).react(util.emojis.cross); case reasons.USER_GLOBAL_BLACKLIST: case reasons.USER_GUILD_BLACKLIST: return isSlash @@ -72,14 +68,14 @@ export default class CommandBlockedListener extends BushListener { content: `${util.emojis.error} You are blacklisted from using this bot.`, ephemeral: true }) - : await (message as BushMessage).react(util.emojis.cross); + : await (message as CommandMessage).react(util.emojis.cross); case reasons.ROLE_BLACKLIST: { return isSlash ? await respond({ content: `${util.emojis.error} One of your roles blacklists you from using this bot.`, ephemeral: true }) - : await (message as BushMessage).react(util.emojis.cross); + : await (message as CommandMessage).react(util.emojis.cross); } case reasons.RESTRICTED_CHANNEL: { if (!command) break; diff --git a/src/listeners/commands/commandError.ts b/src/listeners/commands/commandError.ts index f830cca..878e459 100644 --- a/src/listeners/commands/commandError.ts +++ b/src/listeners/commands/commandError.ts @@ -1,6 +1,6 @@ -import { type BushCommandHandlerEvents } from '#lib'; +import { SlashMessage, type BushCommandHandlerEvents } from '#lib'; import { type AkairoMessage, type Command } from 'discord-akairo'; -import { EmbedBuilder, Formatters, GuildTextBasedChannel, type Message } from 'discord.js'; +import { ChannelType, EmbedBuilder, Formatters, GuildTextBasedChannel, type Message } from 'discord.js'; import { BushListener } from '../../lib/extensions/discord-akairo/BushListener.js'; export default class CommandErrorListener extends BushListener { @@ -20,10 +20,13 @@ export default class CommandErrorListener extends BushListener { ...[error, message, _command]: BushCommandHandlerEvents['error'] | BushCommandHandlerEvents['slashError'] ) { try { - const isSlash = message.util.isSlash; + const isSlash = message.util?.isSlash; const errorNum = Math.floor(Math.random() * 6969696969) + 69; // hehe funny number - const channel = message.channel?.isDM() ? message.channel.recipient?.tag : (message.channel)?.name; - const command = _command ?? message.util.parsed?.command; + const channel = + message.channel?.type === ChannelType.DM + ? message.channel.recipient?.tag + : (message.channel)?.name; + const command = _command ?? message.util?.parsed?.command; client.sentry.captureException(error, { level: 'error', @@ -31,9 +34,10 @@ export default class CommandErrorListener extends BushListener { extra: { 'command.name': command?.id, 'message.id': message.id, - 'message.type': message.util.isSlash ? 'slash' : 'normal', - 'message.parsed.content': message.util.parsed?.content, - 'channel.id': (message.channel?.isDM() ? message.channel.recipient?.id : message.channel?.id) ?? '¯\\_(ツ)_/¯', + 'message.type': message.util ? (message.util.isSlash ? 'slash' : 'normal') : 'unknown', + 'message.parsed.content': message.util?.parsed?.content, + 'channel.id': + (message.channel?.type === ChannelType.DM ? message.channel.recipient?.id : message.channel?.id) ?? '¯\\_(ツ)_/¯', 'channel.name': channel, 'guild.id': message.guild?.id ?? '¯\\_(ツ)_/¯', 'guild.name': message.guild?.name ?? '¯\\_(ツ)_/¯', @@ -87,7 +91,7 @@ export default class CommandErrorListener extends BushListener { | { message: Message | AkairoMessage; error: Error | any; - isSlash: boolean; + isSlash?: boolean; type: 'command-log' | 'command-dev' | 'command-user'; errorNum: number; command?: Command; @@ -105,9 +109,9 @@ export default class CommandErrorListener extends BushListener { private static _generateErrorEmbed( options: | { - message: Message | AkairoMessage; + message: Message | SlashMessage; error: Error | any; - isSlash: boolean; + isSlash?: boolean; type: 'command-log' | 'command-dev' | 'command-user'; errorNum: number; command?: Command; diff --git a/src/listeners/commands/commandMissingPermissions.ts b/src/listeners/commands/commandMissingPermissions.ts index 19c0860..2cbf17c 100644 --- a/src/listeners/commands/commandMissingPermissions.ts +++ b/src/listeners/commands/commandMissingPermissions.ts @@ -1,5 +1,5 @@ import { BushListener, type BushCommandHandlerEvents } from '#lib'; -import { PermissionsString } from 'discord.js'; +import { type PermissionsString } from 'discord.js'; export default class CommandMissingPermissionsListener extends BushListener { public constructor() { diff --git a/src/listeners/contextCommands/contextCommandBlocked.ts b/src/listeners/contextCommands/contextCommandBlocked.ts index 7fe381e..ba4af8c 100644 --- a/src/listeners/contextCommands/contextCommandBlocked.ts +++ b/src/listeners/contextCommands/contextCommandBlocked.ts @@ -1,5 +1,5 @@ import { BushListener } from '#lib'; -import { ContextMenuCommandHandlerEvents } from 'discord-akairo'; +import { type ContextMenuCommandHandlerEvents } from 'discord-akairo'; export default class ContextCommandBlockedListener extends BushListener { public constructor() { diff --git a/src/listeners/contextCommands/contextCommandError.ts b/src/listeners/contextCommands/contextCommandError.ts index a532d5e..19b5708 100644 --- a/src/listeners/contextCommands/contextCommandError.ts +++ b/src/listeners/contextCommands/contextCommandError.ts @@ -1,6 +1,6 @@ -import { ContextMenuCommand, ContextMenuCommandHandlerEvents } from 'discord-akairo'; -import { ContextMenuCommandInteraction, EmbedBuilder, GuildTextBasedChannel } from 'discord.js'; -import { BushListener } from '../../lib/extensions/discord-akairo/BushListener.js'; +import { BushListener } from '#lib'; +import { type ContextMenuCommand, type ContextMenuCommandHandlerEvents } from 'discord-akairo'; +import { ChannelType, ContextMenuCommandInteraction, EmbedBuilder, GuildTextBasedChannel } from 'discord.js'; import CommandErrorListener, { IFuckedUpError } from '../commands/commandError.js'; export default class ContextCommandErrorListener extends BushListener { @@ -19,9 +19,10 @@ export default class ContextCommandErrorListener extends BushListener { public static async handleError(...[error, interaction, command]: ContextMenuCommandHandlerEvents['error']) { try { const errorNum = Math.floor(Math.random() * 6969696969) + 69; // hehe funny number - const channel = interaction.channel?.isDM() - ? interaction.channel.recipient?.tag - : (interaction.channel)?.name; + const channel = + interaction.channel?.type === ChannelType.DM + ? interaction.channel.recipient?.tag + : (interaction.channel)?.name; client.sentry.captureException(error, { level: 'error', @@ -31,7 +32,8 @@ export default class ContextCommandErrorListener extends BushListener { 'message.id': interaction.id, 'message.type': 'context command', 'channel.id': - (interaction.channel?.isDM() ? interaction.channel.recipient?.id : interaction.channel?.id) ?? '¯\\_(ツ)_/¯', + (interaction.channel?.type === ChannelType.DM ? interaction.channel.recipient?.id : interaction.channel?.id) ?? + '¯\\_(ツ)_/¯', 'channel.name': channel, 'guild.id': interaction.guild?.id ?? '¯\\_(ツ)_/¯', 'guild.name': interaction.guild?.name ?? '¯\\_(ツ)_/¯', diff --git a/src/listeners/contextCommands/contextCommandNotFound.ts b/src/listeners/contextCommands/contextCommandNotFound.ts index b0a8f62..ca97ad1 100644 --- a/src/listeners/contextCommands/contextCommandNotFound.ts +++ b/src/listeners/contextCommands/contextCommandNotFound.ts @@ -1,5 +1,5 @@ import { BushListener } from '#lib'; -import { ContextMenuCommandHandlerEvents } from 'discord-akairo'; +import { type ContextMenuCommandHandlerEvents } from 'discord-akairo'; export default class ContextCommandNotFoundListener extends BushListener { public constructor() { diff --git a/src/listeners/guild/guildMemberAdd.ts b/src/listeners/guild/guildMemberAdd.ts index 94d35a5..de1f859 100644 --- a/src/listeners/guild/guildMemberAdd.ts +++ b/src/listeners/guild/guildMemberAdd.ts @@ -1,5 +1,5 @@ -import { BushListener, type BushClientEvents, type BushGuildMember, type BushTextChannel } from '#lib'; -import { EmbedBuilder } from 'discord.js'; +import { BushListener, type BushClientEvents } from '#lib'; +import { EmbedBuilder, type GuildMember, type TextChannel } from 'discord.js'; export default class GuildMemberAddListener extends BushListener { public constructor() { @@ -14,11 +14,11 @@ export default class GuildMemberAddListener extends BushListener { void this.sendWelcomeMessage(member); } - private async sendWelcomeMessage(member: BushGuildMember) { + private async sendWelcomeMessage(member: GuildMember) { if (client.config.isDevelopment) return; const welcomeChannel = await member.guild.getSetting('welcomeChannel'); if (!welcomeChannel) return; - const welcome = client.channels.cache.get(welcomeChannel) as BushTextChannel | undefined; + const welcome = client.channels.cache.get(welcomeChannel) as TextChannel | undefined; if (!welcome) return; if (member.guild.id !== welcome?.guild.id) throw new Error('Welcome channel must be in the guild.'); diff --git a/src/listeners/guild/guildMemberRemove.ts b/src/listeners/guild/guildMemberRemove.ts index 8e7ce0d..59f8800 100644 --- a/src/listeners/guild/guildMemberRemove.ts +++ b/src/listeners/guild/guildMemberRemove.ts @@ -1,13 +1,5 @@ -import { - BushListener, - StickyRole, - Time, - type BushClientEvents, - type BushGuildMember, - type BushTextChannel, - type PartialBushGuildMember -} from '#lib'; -import { EmbedBuilder } from 'discord.js'; +import { BushListener, StickyRole, Time, type BushClientEvents } from '#lib'; +import { EmbedBuilder, type GuildMember, type PartialGuildMember, type TextChannel } from 'discord.js'; export default class GuildMemberRemoveListener extends BushListener { public constructor() { @@ -23,14 +15,14 @@ export default class GuildMemberRemoveListener extends BushListener { void this.stickyRoles(member); } - private async sendWelcomeMessage(member: BushGuildMember | PartialBushGuildMember) { + private async sendWelcomeMessage(member: GuildMember | PartialGuildMember) { if (client.config.isDevelopment) return; const user = member.partial ? await client.users.fetch(member.id) : member.user; await util.sleep(50 * Time.Millisecond); // ban usually triggers after member leave const isBan = member.guild.bans.cache.has(member.id); const welcomeChannel = await member.guild.getSetting('welcomeChannel'); if (!welcomeChannel) return; - const welcome = client.channels.cache.get(welcomeChannel) as BushTextChannel | undefined; + const welcome = client.channels.cache.get(welcomeChannel) as TextChannel | undefined; if (member.guild.id !== welcome?.guild.id) throw new Error('Welcome channel must be in the guild.'); const embed: EmbedBuilder = new EmbedBuilder() .setDescription( @@ -55,7 +47,7 @@ export default class GuildMemberRemoveListener extends BushListener { ); } - private async stickyRoles(member: BushGuildMember | PartialBushGuildMember) { + private async stickyRoles(member: GuildMember | PartialGuildMember) { if (!(await member.guild.hasFeature('stickyRoles'))) return; if (member.partial) { await member.guild.members.fetch(); // try to prevent in the future diff --git a/src/listeners/guild/joinRoles.ts b/src/listeners/guild/joinRoles.ts index 81a07e5..dab623f 100644 --- a/src/listeners/guild/joinRoles.ts +++ b/src/listeners/guild/joinRoles.ts @@ -1,5 +1,5 @@ -import { BushListener, StickyRole, type BushClientEvents, type BushGuildMember } from '#lib'; -import { type Snowflake } from 'discord.js'; +import { BushListener, StickyRole, type BushClientEvents } from '#lib'; +import { type GuildMember, type Snowflake } from 'discord.js'; export default class JoinRolesListener extends BushListener { public constructor() { @@ -37,7 +37,7 @@ export default class JoinRolesListener extends BushListener { * @param member The member to add sticky roles to. * @returns Whether or not sticky roles were added. */ - private async stickyRoles(member: BushGuildMember): Promise { + private async stickyRoles(member: GuildMember): Promise { const hadRoles = await StickyRole.findOne({ where: { guild: member.guild.id, user: member.id } }); if (hadRoles?.roles?.length) { @@ -98,7 +98,7 @@ export default class JoinRolesListener extends BushListener { * Add the guild's join roles to the member. * @param member The member to add the join roles to. */ - private async joinRoles(member: BushGuildMember): Promise { + private async joinRoles(member: GuildMember): Promise { const joinRoles = await member.guild.getSetting('joinRoles'); if (!joinRoles || !joinRoles.length) return; await member.roles diff --git a/src/listeners/interaction/interactionCreate.ts b/src/listeners/interaction/interactionCreate.ts index 13c4c74..48a2149 100644 --- a/src/listeners/interaction/interactionCreate.ts +++ b/src/listeners/interaction/interactionCreate.ts @@ -1,5 +1,5 @@ -import { AutoMod, BushListener, type BushButtonInteraction, type BushClientEvents } from '#lib'; -import { InteractionType } from 'discord-api-types/v10'; +import { AutoMod, BushListener, type BushClientEvents } from '#lib'; +import { InteractionType } from 'discord.js'; export default class InteractionCreateListener extends BushListener { public constructor() { @@ -17,12 +17,12 @@ export default class InteractionCreateListener extends BushListener { 'interactionVerbose', `An interaction of type <<${InteractionType[interaction.type]}>> was received from <<${interaction.user.tag}>>.` ); - if (interaction.isCommand()) { + 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 AutoMod.handleInteraction(interaction as BushButtonInteraction); + else if (id.startsWith('automod;')) void AutoMod.handleInteraction(interaction); else if (id.startsWith('button-role;') && interaction.inCachedGuild()) { const [, roleId] = id.split(';'); const role = interaction.guild.roles.cache.get(roleId); diff --git a/src/listeners/member-custom/bushLevelUpdate.ts b/src/listeners/member-custom/bushLevelUpdate.ts index c43e341..2a7b75b 100644 --- a/src/listeners/member-custom/bushLevelUpdate.ts +++ b/src/listeners/member-custom/bushLevelUpdate.ts @@ -1,4 +1,5 @@ import { BushListener, type BushClientEvents } from '#lib'; +import assert from 'assert'; import { type TextChannel } from 'discord.js'; type Args = BushClientEvents['bushLevelUpdate']; @@ -18,6 +19,7 @@ export default class BushLevelUpdateListener extends BushListener { } private async sendLevelUpMessages(member: Args[0], newLevel: Args[2], message: Args[4]) { + assert(message.inGuild()); if (!(await message.guild.hasFeature('sendLevelUpMessages'))) return; const channel = ((await message.guild.channels @@ -36,6 +38,7 @@ export default class BushLevelUpdateListener extends BushListener { } private async assignLevelRoles(member: Args[0], newLevel: Args[2], message: Args[4]) { + assert(message.inGuild()); const levelRoles = await message.guild.getSetting('levelRoles'); if (!Object.keys(levelRoles).length) return; diff --git a/src/listeners/member-custom/bushPurge.ts b/src/listeners/member-custom/bushPurge.ts index 49d848f..3334bad 100644 --- a/src/listeners/member-custom/bushPurge.ts +++ b/src/listeners/member-custom/bushPurge.ts @@ -1,5 +1,5 @@ import { BushListener, type BushClientEvents } from '#lib'; -import { EmbedBuilder } from '@discordjs/builders'; +import { EmbedBuilder } from 'discord.js'; export default class BushPurgeListener extends BushListener { public constructor() { diff --git a/src/listeners/message/autoPublisher.ts b/src/listeners/message/autoPublisher.ts index b074fda..8f3462f 100644 --- a/src/listeners/message/autoPublisher.ts +++ b/src/listeners/message/autoPublisher.ts @@ -1,4 +1,5 @@ import { BushListener, type BushClientEvents } from '#lib'; +import { ChannelType } from 'discord.js'; export default class autoPublisherListener extends BushListener { public constructor() { @@ -13,7 +14,7 @@ export default class autoPublisherListener extends BushListener { if (!message.guild || !(await message.guild.hasFeature('autoPublish'))) return; const autoPublishChannels = await message.guild.getSetting('autoPublishChannels'); if (autoPublishChannels) { - if (message.channel.isNews() && autoPublishChannels.some((x) => message.channel.id.includes(x))) { + if (message.channel.type === ChannelType.GuildNews && autoPublishChannels.some((x) => message.channel.id.includes(x))) { await message .crosspost() .then( diff --git a/src/listeners/message/blacklistedFile.ts b/src/listeners/message/blacklistedFile.ts index a6216a5..122f289 100644 --- a/src/listeners/message/blacklistedFile.ts +++ b/src/listeners/message/blacklistedFile.ts @@ -128,7 +128,7 @@ export default class BlacklistedFileListener extends BushListener { } await message.delete(); - await message.util.send( + await message.util!.send( `<@!${message.author.id}>, please do not send ${foundFiles.map((f) => f.description).join(' or ')}.` ); if (message.channel.type === ChannelType.DM) return; @@ -139,7 +139,7 @@ export default class BlacklistedFileListener extends BushListener { }.` ); } catch (e) { - void message.util.send( + void message.util!.send( `<@!${message.author.id}>, please do not send ${foundFiles.map((f) => f.description).join(' or ')}.` ); void client.console.warn( diff --git a/src/listeners/message/level.ts b/src/listeners/message/level.ts index 5abfd6f..08a8a7c 100644 --- a/src/listeners/message/level.ts +++ b/src/listeners/message/level.ts @@ -1,4 +1,4 @@ -import { BushListener, Level, type BushCommandHandlerEvents, type BushGuild, type BushMessage } from '#lib'; +import { BushListener, Level, type BushCommandHandlerEvents } from '#lib'; import { MessageType } from 'discord.js'; export default class LevelListener extends BushListener { @@ -37,15 +37,7 @@ export default class LevelListener extends BushListener { return false; }); const newLevel = Level.convertXpToLevel(user.xp); - if (previousLevel !== newLevel) - client.emit( - 'bushLevelUpdate', - message.member!, - previousLevel, - newLevel, - user.xp, - message as BushMessage & { guild: BushGuild } - ); + if (previousLevel !== newLevel) client.emit('bushLevelUpdate', message.member!, previousLevel, newLevel, user.xp, message); if (success) void client.logger.verbose(`level`, `Gave <<${xpToGive}>> XP to <<${message.author.tag}>> in <<${message.guild}>>.`); this.#levelCooldowns.add(`${message.guildId}-${message.author.id}`); diff --git a/src/listeners/rest/rateLimit.ts b/src/listeners/rest/rateLimit.ts index 600ef07..4884e9f 100644 --- a/src/listeners/rest/rateLimit.ts +++ b/src/listeners/rest/rateLimit.ts @@ -1,5 +1,5 @@ import { BushListener } from '#lib'; -import { RestEvents } from '@discordjs/rest'; +import type { RestEvents } from '@discordjs/rest'; export default class RateLimitedListener extends BushListener { public constructor() { diff --git a/src/listeners/track-manual-punishments/modlogSyncBan.ts b/src/listeners/track-manual-punishments/modlogSyncBan.ts index 504992f..be937b9 100644 --- a/src/listeners/track-manual-punishments/modlogSyncBan.ts +++ b/src/listeners/track-manual-punishments/modlogSyncBan.ts @@ -1,7 +1,5 @@ -import { BushListener, BushUser, Moderation, ModLogType, Time, type BushClientEvents } from '#lib'; -import { EmbedBuilder } from '@discordjs/builders'; -import { AuditLogEvent } from 'discord-api-types/v10'; -import { PermissionFlagsBits } from 'discord.js'; +import { BushListener, Moderation, ModLogType, Time, type BushClientEvents } from '#lib'; +import { AuditLogEvent, EmbedBuilder, PermissionFlagsBits } from 'discord.js'; export default class ModlogSyncBanListener extends BushListener { public constructor() { @@ -43,7 +41,7 @@ export default class ModlogSyncBanListener extends BushListener { const { log } = await Moderation.createModLogEntry({ type: ModLogType.PERM_BAN, user: ban.user, - moderator: first.executor, + moderator: first.executor, reason: `[Manual] ${first.reason ? first.reason : 'No reason given'}`, guild: ban.guild }); diff --git a/src/listeners/track-manual-punishments/modlogSyncKick.ts b/src/listeners/track-manual-punishments/modlogSyncKick.ts index 7a2c154..d136780 100644 --- a/src/listeners/track-manual-punishments/modlogSyncKick.ts +++ b/src/listeners/track-manual-punishments/modlogSyncKick.ts @@ -1,6 +1,5 @@ -import { BushListener, BushUser, Moderation, ModLogType, Time, type BushClientEvents } from '#lib'; -import { AuditLogEvent } from 'discord-api-types/v10'; -import { EmbedBuilder, PermissionFlagsBits } from 'discord.js'; +import { BushListener, Moderation, ModLogType, Time, type BushClientEvents } from '#lib'; +import { AuditLogEvent, EmbedBuilder, PermissionFlagsBits } from 'discord.js'; export default class ModlogSyncKickListener extends BushListener { public constructor() { @@ -42,7 +41,7 @@ export default class ModlogSyncKickListener extends BushListener { const { log } = await Moderation.createModLogEntry({ type: ModLogType.KICK, user: member.user, - moderator: first.executor, + moderator: first.executor, reason: `[Manual] ${first.reason ? first.reason : 'No reason given'}`, guild: member.guild }); diff --git a/src/listeners/track-manual-punishments/modlogSyncTimeout.ts b/src/listeners/track-manual-punishments/modlogSyncTimeout.ts index f6235a9..adcd7fe 100644 --- a/src/listeners/track-manual-punishments/modlogSyncTimeout.ts +++ b/src/listeners/track-manual-punishments/modlogSyncTimeout.ts @@ -1,6 +1,5 @@ -import { BushListener, BushUser, Moderation, ModLogType, Time, type BushClientEvents } from '#lib'; -import { AuditLogEvent } from 'discord-api-types/v10'; -import { EmbedBuilder, PermissionFlagsBits } from 'discord.js'; +import { BushListener, Moderation, ModLogType, Time, type BushClientEvents } from '#lib'; +import { AuditLogEvent, EmbedBuilder, PermissionFlagsBits } from 'discord.js'; export default class ModlogSyncTimeoutListener extends BushListener { public constructor() { @@ -46,7 +45,7 @@ export default class ModlogSyncTimeoutListener extends BushListener { const { log } = await Moderation.createModLogEntry({ type: newTime ? ModLogType.TIMEOUT : ModLogType.REMOVE_TIMEOUT, user: newMember.user, - moderator: first.executor, + moderator: first.executor, reason: `[Manual] ${first.reason ? first.reason : 'No reason given'}`, guild: newMember.guild, duration: newTime ? newTime.getTime() - now.getTime() : undefined diff --git a/src/listeners/track-manual-punishments/modlogSyncUnban.ts b/src/listeners/track-manual-punishments/modlogSyncUnban.ts index cdf18d9..af0b21f 100644 --- a/src/listeners/track-manual-punishments/modlogSyncUnban.ts +++ b/src/listeners/track-manual-punishments/modlogSyncUnban.ts @@ -1,7 +1,5 @@ -import { BushListener, BushUser, Moderation, ModLogType, Time, type BushClientEvents } from '#lib'; -import { EmbedBuilder } from '@discordjs/builders'; -import { AuditLogEvent } from 'discord-api-types/v10'; -import { PermissionFlagsBits } from 'discord.js'; +import { BushListener, Moderation, ModLogType, Time, type BushClientEvents } from '#lib'; +import { AuditLogEvent, EmbedBuilder, PermissionFlagsBits } from 'discord.js'; export default class ModlogSyncUnbanListener extends BushListener { public constructor() { @@ -42,7 +40,7 @@ export default class ModlogSyncUnbanListener extends BushListener { const { log } = await Moderation.createModLogEntry({ type: ModLogType.UNBAN, user: ban.user, - moderator: first.executor, + moderator: first.executor, reason: `[Manual] ${first.reason ? first.reason : 'No reason given'}`, guild: ban.guild }); diff --git a/src/listeners/ws/INTERACTION_CREATE.ts b/src/listeners/ws/INTERACTION_CREATE.ts index d3ca70c..408e7e5 100644 --- a/src/listeners/ws/INTERACTION_CREATE.ts +++ b/src/listeners/ws/INTERACTION_CREATE.ts @@ -1,22 +1,25 @@ -import { BushListener, BushUser, Moderation, PunishmentTypePresent } from '#lib'; -import { EmbedBuilder } from '@discordjs/builders'; +import { BushListener, Moderation, PunishmentTypePresent } from '#lib'; import assert from 'assert'; import { - APIEmbed, - APIInteraction, - APIInteractionResponseChannelMessageWithSource, - APIInteractionResponseDeferredMessageUpdate, - APIInteractionResponseUpdateMessage, - APIModalInteractionResponse, + ActionRowBuilder, + ButtonBuilder, ButtonStyle, ComponentType, + EmbedBuilder, GatewayDispatchEvents, InteractionResponseType, InteractionType, Routes, - TextInputStyle -} from 'discord-api-types/v10'; -import { ActionRowBuilder, ButtonBuilder, Snowflake } from 'discord.js'; + Snowflake, + TextInputStyle, + User, + type APIEmbed, + type APIInteraction, + type APIInteractionResponseChannelMessageWithSource, + type APIInteractionResponseDeferredMessageUpdate, + type APIInteractionResponseUpdateMessage, + type APIModalInteractionResponse +} from 'discord.js'; export default class WsInteractionCreateListener extends BushListener { public constructor() { @@ -188,7 +191,7 @@ export default class WsInteractionCreateListener extends BushListener { }); assert(interaction.user); - const user = new BushUser(client, interaction.user as any); + const user = new User(client, interaction.user); assert(user); // const caseId = await ModLog.findOne({ where: { user: userId, guild: guildId, id: modlogCase } }); diff --git a/src/tasks/removeExpiredPunishements.ts b/src/tasks/removeExpiredPunishements.ts index cba939e..6b36473 100644 --- a/src/tasks/removeExpiredPunishements.ts +++ b/src/tasks/removeExpiredPunishements.ts @@ -1,4 +1,4 @@ -import { ActivePunishment, ActivePunishmentType, BushTask, Time, type BushGuild, type BushUser } from '#lib'; +import { ActivePunishment, ActivePunishmentType, BushTask, Time } from '#lib'; import assert from 'assert'; const { Op } = (await import('sequelize')).default; @@ -25,13 +25,13 @@ export default class RemoveExpiredPunishmentsTask extends BushTask { ); for (const entry of expiredEntries) { - const guild = client.guilds.cache.get(entry.guild) as BushGuild; + const guild = client.guilds.cache.get(entry.guild); if (!guild) continue; // eslint-disable-next-line @typescript-eslint/no-misused-promises setTimeout(async () => { const member = guild.members.cache.get(entry.user); - const user = (await util.resolveNonCachedUser(entry.user)) as BushUser; + const user = await util.resolveNonCachedUser(entry.user); assert(guild); switch (entry.type) { diff --git a/yarn.lock b/yarn.lock index 8f630f3..a07ae63 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14,9 +14,9 @@ __metadata: languageName: node linkType: hard -"@discordjs/builders@npm:^0.14.0-dev": - version: 0.14.0 - resolution: "@discordjs/builders@npm:0.14.0" +"@discordjs/builders@npm:^0.15.0": + version: 0.15.0 + resolution: "@discordjs/builders@npm:0.15.0" dependencies: "@sapphire/shapeshift": ^3.1.0 "@sindresorhus/is": ^4.6.0 @@ -24,32 +24,39 @@ __metadata: fast-deep-equal: ^3.1.3 ts-mixer: ^6.0.1 tslib: ^2.4.0 - checksum: 044f1864bc5a027c6a4021cc428e98362a8466d7539ae4ec17681157ffc3e9a36a5a8f52b8874eabff11c5d6dca9f7870c516d27d8e090a05a9b23eeb81d6bb8 + checksum: 1a8a4ffe048243ebb9fbbc76a113e4994c68904a5b3d256a6b10c0123f1d0fb58b45809d134ba993293fb2190c9f3506663ddfb5068a86ec6b9c53b5db61f6a6 languageName: node linkType: hard -"@discordjs/builders@npm:^0.15.0": - version: 0.15.0 - resolution: "@discordjs/builders@npm:0.15.0" +"@discordjs/builders@npm:^0.16.0-dev": + version: 0.16.0-dev.1655165432-b4e28a8 + resolution: "@discordjs/builders@npm:0.16.0-dev.1655165432-b4e28a8" dependencies: "@sapphire/shapeshift": ^3.1.0 "@sindresorhus/is": ^4.6.0 - discord-api-types: ^0.33.3 + discord-api-types: ^0.33.5 fast-deep-equal: ^3.1.3 ts-mixer: ^6.0.1 tslib: ^2.4.0 - checksum: 1a8a4ffe048243ebb9fbbc76a113e4994c68904a5b3d256a6b10c0123f1d0fb58b45809d134ba993293fb2190c9f3506663ddfb5068a86ec6b9c53b5db61f6a6 + checksum: 6e1f10093a61d942e953380b081a5f7a63e5e5db5c2c3e943a21a34a240eb04bf9c8659d3c68f9c8585fc7518dabe28f0795287a70134752c568057be0ecf81c languageName: node linkType: hard -"@discordjs/collection@npm:^0.7.0, @discordjs/collection@npm:^0.7.0-dev": +"@discordjs/collection@npm:^0.7.0": version: 0.7.0 resolution: "@discordjs/collection@npm:0.7.0" checksum: 141aa35a5433bacba3617b533557b4948388c7b59cdaecee51ccd721c1b9242e50d95bdef53ee2491535a017095f5072ace3c3e9e594193f67a1c5a8a4b7db93 languageName: node linkType: hard -"@discordjs/rest@npm:^0.5.0, @discordjs/rest@npm:^0.5.0-dev": +"@discordjs/collection@npm:^0.8.0-dev": + version: 0.8.0-dev.1655165444-b4e28a8 + resolution: "@discordjs/collection@npm:0.8.0-dev.1655165444-b4e28a8" + checksum: 3a505484e6bc1a85fd891c5f934224b70696988b612c8db4dceec203d7e0a9856545124674a2fa069a6470c88254e601ef20876e1cb8cc6ec80282cc0f52f6c2 + languageName: node + linkType: hard + +"@discordjs/rest@npm:^0.5.0": version: 0.5.0 resolution: "@discordjs/rest@npm:0.5.0" dependencies: @@ -63,6 +70,20 @@ __metadata: languageName: node linkType: hard +"@discordjs/rest@npm:^0.6.0-dev": + version: 0.6.0-dev.1655165427-b4e28a8 + resolution: "@discordjs/rest@npm:0.6.0-dev.1655165427-b4e28a8" + dependencies: + "@discordjs/collection": ^0.8.0-dev + "@sapphire/async-queue": ^1.3.1 + "@sapphire/snowflake": ^3.2.2 + discord-api-types: ^0.33.5 + tslib: ^2.4.0 + undici: ^5.4.0 + checksum: 8ea6857fb3c00220ca1ceafd15aa34ec3b3864a04359269fe35f0a991009475f7da1e743e1e821bf46de7e7b78c7e11b88f6db28e728fdc15e2518a862c87fd6 + languageName: node + linkType: hard + "@eslint/eslintrc@npm:^1.3.0": version: 1.3.0 resolution: "@eslint/eslintrc@npm:1.3.0" @@ -214,13 +235,13 @@ __metadata: linkType: hard "@sapphire/shapeshift@npm:^3.1.0": - version: 3.1.0 - resolution: "@sapphire/shapeshift@npm:3.1.0" - checksum: d28035fe9dd2b59feda24daad4ae5f3ae16bf98312e726df85c79cf071de3447a2a0e81f818471a7abb8d6678423e7e6ece57687d162269624e503e62cae26a4 + version: 3.2.0 + resolution: "@sapphire/shapeshift@npm:3.2.0" + checksum: 166c590e66e222f20bc92e21b50390b687eca5c948a4bbf6a4cf8745189fa35972f41b1022a3a35c4e0f2e2c808232f18f46ddb1db12b84c10bd1c9145740ff0 languageName: node linkType: hard -"@sapphire/snowflake@npm:^3.2.1, @sapphire/snowflake@npm:^3.2.2": +"@sapphire/snowflake@npm:^3.2.2": version: 3.2.2 resolution: "@sapphire/snowflake@npm:3.2.2" checksum: 315fecef4738092c2a2f3509b132b811fcbfa6c98d5d45d951adaf3ca21608be69043bcc137cc6933a7c3e55cbdc066daa5bb484603e6575422b335445b59315 @@ -590,13 +611,13 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:^5.27.1": - version: 5.27.1 - resolution: "@typescript-eslint/eslint-plugin@npm:5.27.1" +"@typescript-eslint/eslint-plugin@npm:^5.28.0": + version: 5.28.0 + resolution: "@typescript-eslint/eslint-plugin@npm:5.28.0" dependencies: - "@typescript-eslint/scope-manager": 5.27.1 - "@typescript-eslint/type-utils": 5.27.1 - "@typescript-eslint/utils": 5.27.1 + "@typescript-eslint/scope-manager": 5.28.0 + "@typescript-eslint/type-utils": 5.28.0 + "@typescript-eslint/utils": 5.28.0 debug: ^4.3.4 functional-red-black-tree: ^1.0.1 ignore: ^5.2.0 @@ -609,53 +630,53 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: ee00d8d3a7b395e346801b7bf30209e278f06b5c283ad71c03b34db9e2d68a43ca0e292e315fa7e5bf131a8839ff4a24e0ed76c37811d230f97aae7e123d73ea + checksum: 49e08865abd35acdc44829b929f2cd01d01a1f91d3c3c357963b6980e938de365f178efcec21e0ed6ec13a2ad9373f52b73001ddd5cdc7b0245fcf02b9564dd3 languageName: node linkType: hard "@typescript-eslint/experimental-utils@npm:^5.0.0": - version: 5.27.1 - resolution: "@typescript-eslint/experimental-utils@npm:5.27.1" + version: 5.28.0 + resolution: "@typescript-eslint/experimental-utils@npm:5.28.0" dependencies: - "@typescript-eslint/utils": 5.27.1 + "@typescript-eslint/utils": 5.28.0 peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: 180c1dc79b5c0e954eb9ab9c449eec3a5a808f3c5b21ead9661feb542e41c186f49c6da42a7197c2c6e2614a71dadb328ff10e2eaf87bfc317fb03de02e16c8f + checksum: 500c5955621f0b15d69c32c068ad64c7121f1343887c347140943a353909bb6c00242d6fcb3ea36fd3de004a76a2d4e07d9a08c5a09bcec989d17e987ed2e3f5 languageName: node linkType: hard -"@typescript-eslint/parser@npm:^5.27.1": - version: 5.27.1 - resolution: "@typescript-eslint/parser@npm:5.27.1" +"@typescript-eslint/parser@npm:^5.28.0": + version: 5.28.0 + resolution: "@typescript-eslint/parser@npm:5.28.0" dependencies: - "@typescript-eslint/scope-manager": 5.27.1 - "@typescript-eslint/types": 5.27.1 - "@typescript-eslint/typescript-estree": 5.27.1 + "@typescript-eslint/scope-manager": 5.28.0 + "@typescript-eslint/types": 5.28.0 + "@typescript-eslint/typescript-estree": 5.28.0 debug: ^4.3.4 peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: typescript: optional: true - checksum: 0f1df76142c9d7a6c6dbfc5d19fdee02bbc0e79def6e6df4b126c7eaae1c3a46a3871ad498d4b1fc7ad5cb58d6eb70f020807f600d99c0b9add98441fc12f23b + checksum: cb18ff47b0a10373ba1c05c90901d08f5f99180e624f3f2faa85f13d1048fc59601a3cab6b852f72d13287b314d94c4d4997129ff6c639496a9144c762f6d31e languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:5.27.1": - version: 5.27.1 - resolution: "@typescript-eslint/scope-manager@npm:5.27.1" +"@typescript-eslint/scope-manager@npm:5.28.0": + version: 5.28.0 + resolution: "@typescript-eslint/scope-manager@npm:5.28.0" dependencies: - "@typescript-eslint/types": 5.27.1 - "@typescript-eslint/visitor-keys": 5.27.1 - checksum: 401bf2b46de08ddb80ec9f36df7d58bf5de7837185a472b190b670d421d685743aad4c9fa8a6893f65ba933b822c5d7060c640e87cf0756d7aa56abdd25689cc + "@typescript-eslint/types": 5.28.0 + "@typescript-eslint/visitor-keys": 5.28.0 + checksum: f187fd295d152508aa85233ef3ac89031952300fbbe277e188dfba3fbfd82656b15d3d8daa6d85984970ce00a30fdf75da912c4024df982004b24f3a95420b8f languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:5.27.1": - version: 5.27.1 - resolution: "@typescript-eslint/type-utils@npm:5.27.1" +"@typescript-eslint/type-utils@npm:5.28.0": + version: 5.28.0 + resolution: "@typescript-eslint/type-utils@npm:5.28.0" dependencies: - "@typescript-eslint/utils": 5.27.1 + "@typescript-eslint/utils": 5.28.0 debug: ^4.3.4 tsutils: ^3.21.0 peerDependencies: @@ -663,23 +684,23 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 43b7da26ea1bd7d249c45d168ec88f971fb71362bbc21ec4748d73b1ecb43f4ca59f5ed338e8dbc74272ae4ebac1cab87a9b62c0fa616c6f9bd833a212dc8a40 + checksum: 05563dab5414a42b7781f5ce65ee540b10a946c419bde3fbc45593aa3b1225b2a70558581f311720d670dc82ab699a3f9ecb4b1447d6fd557bd330cf8890d8ca languageName: node linkType: hard -"@typescript-eslint/types@npm:5.27.1": - version: 5.27.1 - resolution: "@typescript-eslint/types@npm:5.27.1" - checksum: 81faa50256ba67c23221273744c51676774fe6a1583698c3a542f3e2fd21ab34a4399019527c9cf7ab4e5a1577272f091d5848d3af937232ddb2dbf558a7c39a +"@typescript-eslint/types@npm:5.28.0": + version: 5.28.0 + resolution: "@typescript-eslint/types@npm:5.28.0" + checksum: e948915d6f24ece98043763a48e34ced5e16a1aa88cc86ea7d9057010ed92ce39457a753dd7a140be52f9b546b27f8a3b33bdc7d671427a386aa1aa381d908ef languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:5.27.1": - version: 5.27.1 - resolution: "@typescript-eslint/typescript-estree@npm:5.27.1" +"@typescript-eslint/typescript-estree@npm:5.28.0": + version: 5.28.0 + resolution: "@typescript-eslint/typescript-estree@npm:5.28.0" dependencies: - "@typescript-eslint/types": 5.27.1 - "@typescript-eslint/visitor-keys": 5.27.1 + "@typescript-eslint/types": 5.28.0 + "@typescript-eslint/visitor-keys": 5.28.0 debug: ^4.3.4 globby: ^11.1.0 is-glob: ^4.0.3 @@ -688,33 +709,33 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 59d2a0885be7d54bd86472a446d84930cc52d2690ea432d9164075ea437b3b4206dadd49799764ad0fb68f3e4ebb4e36db9717c7a443d0f3c82d5659e41fbd05 + checksum: e7be6e9ff778ab2728bdc545713f29cd40bbe1282662461453fe46bc18f676f9b33c60e3514347fbc4e5e94d764525c20b8ef3d47baa62fec6bd3ce05fdde6eb languageName: node linkType: hard -"@typescript-eslint/utils@npm:5.27.1": - version: 5.27.1 - resolution: "@typescript-eslint/utils@npm:5.27.1" +"@typescript-eslint/utils@npm:5.28.0": + version: 5.28.0 + resolution: "@typescript-eslint/utils@npm:5.28.0" dependencies: "@types/json-schema": ^7.0.9 - "@typescript-eslint/scope-manager": 5.27.1 - "@typescript-eslint/types": 5.27.1 - "@typescript-eslint/typescript-estree": 5.27.1 + "@typescript-eslint/scope-manager": 5.28.0 + "@typescript-eslint/types": 5.28.0 + "@typescript-eslint/typescript-estree": 5.28.0 eslint-scope: ^5.1.1 eslint-utils: ^3.0.0 peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: 51add038226cddad2b3322225de18d53bc1ed44613f7b3a379eb597114b8830a632990b0f4321e0ddf3502b460d80072d7e789be89135b5e11e8dae167005625 + checksum: d30958552470c3f46b2183b690fa8c922a375a84ef83ccfda4785148b8dafb7bf428ab01de6608f67cefbcae35c6a2b0c54b5a6a89bba31566ec3b41f098c02e languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:5.27.1": - version: 5.27.1 - resolution: "@typescript-eslint/visitor-keys@npm:5.27.1" +"@typescript-eslint/visitor-keys@npm:5.28.0": + version: 5.28.0 + resolution: "@typescript-eslint/visitor-keys@npm:5.28.0" dependencies: - "@typescript-eslint/types": 5.27.1 + "@typescript-eslint/types": 5.28.0 eslint-visitor-keys: ^3.3.0 - checksum: 8f104eda321cd6c613daf284fbebbd32b149d4213d137b0ce1caec7a1334c9f46c82ed64aff1243b712ac8c13f67ac344c996cd36d21fbb15032c24d9897a64a + checksum: e97251968ea273ce33fa0de8a9c04426499b797f6f7800379ff880c4be6e6e02fe023038be0092c595be394a8636f73ee8911974214d5232b3d59492a50771bf languageName: node linkType: hard @@ -1031,13 +1052,13 @@ __metadata: "@types/rimraf": ^3.0.2 "@types/tinycolor2": ^1.4.3 "@types/validator": ^13.7.3 - "@typescript-eslint/eslint-plugin": ^5.27.1 - "@typescript-eslint/parser": ^5.27.1 + "@typescript-eslint/eslint-plugin": ^5.28.0 + "@typescript-eslint/parser": ^5.28.0 canvas: ^2.9.1 chalk: ^5.0.1 deep-lock: ^1.0.0 discord-akairo: "npm:@notenoughupdates/discord-akairo@dev" - discord-api-types: 0.33.5 + discord-api-types: 0.34.0 discord.js: "npm:@notenoughupdates/discord.js@dev" eslint: ^8.17.0 eslint-config-prettier: ^8.5.0 @@ -1045,17 +1066,17 @@ __metadata: eslint-plugin-import: ^2.26.0 fuse.js: ^6.6.2 gif-to-apng: ^0.1.2 - googleapis: ^101.0.0 + googleapis: ^102.0.0 got: ^12.1.0 lodash: ^4.17.21 mathjs: ^10.6.1 nanoid: ^4.0.0 node-fetch: ^3.2.6 - node-os-utils: ^1.3.6 + node-os-utils: ^1.3.7 numeral: ^2.0.6 pg: ^8.7.3 pg-hstore: ^2.3.4 - prettier: ^2.6.2 + prettier: ^2.7.0 pretty-bytes: ^6.0.0 rimraf: ^3.0.2 sequelize: 6.20.1 @@ -1468,13 +1489,20 @@ __metadata: linkType: hard "discord-akairo@npm:@notenoughupdates/discord-akairo@dev": - version: 9.1.3-dev.1653854301.456923a - resolution: "@notenoughupdates/discord-akairo@npm:9.1.3-dev.1653854301.456923a" - checksum: 0a53fecf84daade8a3aac16b2f9c968149501c70add3ec59591783eff79d60803c52842067ecc9984fcacc5548807c1b57ff4f2d6309657ad5db365443a39368 + version: 9.1.3-dev.1655212954.1d6b00f + resolution: "@notenoughupdates/discord-akairo@npm:9.1.3-dev.1655212954.1d6b00f" + checksum: 0d17ecd941d594723dd1d82f75bf48a007c030798466400d47d88352766d46b25ed310fb5b946da363cd7b6d84727cf65ea1035883caf4ed35f25dbd7e8d30e4 languageName: node linkType: hard -"discord-api-types@npm:0.33.5, discord-api-types@npm:^0.33.0, discord-api-types@npm:^0.33.3": +"discord-api-types@npm:0.34.0": + version: 0.34.0 + resolution: "discord-api-types@npm:0.34.0" + checksum: 16ef4a7e82c442808447d2062adb907c050f1c0b10b0dd7ffd459b5cbc2ae8e5ba46d17c645e401500a0263ee661c651d29ab5b41bd2af15a72281d03cba90dc + languageName: node + linkType: hard + +"discord-api-types@npm:^0.33.3, discord-api-types@npm:^0.33.5": version: 0.33.5 resolution: "discord-api-types@npm:0.33.5" checksum: 6dcaad640c5693a69c9a4f5e444e739dde11ba835164ae6fd3dd5a1ab7b4d7f96cd022ed653eeaff2c8051ead0d998a5d502a2915cfacdde596364b82d9e3b3f @@ -1482,21 +1510,21 @@ __metadata: linkType: hard "discord.js@npm:@notenoughupdates/discord.js@dev": - version: 14.0.0-dev.1653272547-60b1118 - resolution: "@notenoughupdates/discord.js@npm:14.0.0-dev.1653272547-60b1118" + version: 14.0.0-dev.1655126646-6dfc4f06d + resolution: "@notenoughupdates/discord.js@npm:14.0.0-dev.1655126646-6dfc4f06d" dependencies: - "@discordjs/builders": ^0.14.0-dev - "@discordjs/collection": ^0.7.0-dev - "@discordjs/rest": ^0.5.0-dev - "@sapphire/snowflake": ^3.2.1 + "@discordjs/builders": ^0.16.0-dev + "@discordjs/collection": ^0.8.0-dev + "@discordjs/rest": ^0.6.0-dev + "@sapphire/snowflake": ^3.2.2 "@types/ws": ^8.5.3 - discord-api-types: ^0.33.0 + discord-api-types: ^0.33.5 fast-deep-equal: ^3.1.3 lodash.snakecase: ^4.1.1 - tslib: ^2.3.1 - undici: ^5.2.0 - ws: ^8.5.0 - checksum: 4151631e445e282772af1dd69d53a8f350470ac8e9a3a77d106c591a7ead83e2a77e5e9db91b97999e6e77004e782e8667dbabe7cd2da5788b87674303d44c2e + tslib: ^2.4.0 + undici: ^5.4.0 + ws: ^8.8.0 + checksum: ad0e12b2c99232a7e5b8f728b0a29c61832e5ad9507b5fa24835a593b7ba0639107d16eb15d4354bd20296127331af4fdfe0617640224b8433f775cfd126cf63 languageName: node linkType: hard @@ -2338,13 +2366,13 @@ __metadata: languageName: node linkType: hard -"googleapis@npm:^101.0.0": - version: 101.0.0 - resolution: "googleapis@npm:101.0.0" +"googleapis@npm:^102.0.0": + version: 102.0.0 + resolution: "googleapis@npm:102.0.0" dependencies: google-auth-library: ^8.0.2 googleapis-common: ^6.0.0 - checksum: ee0b2d3b2250195231f016a951696422233bcf80387624e7ae17f7693352ec1031b7ce6f2f068a3511147405b2e1eb3ba9f53bce13dd46210e6cf3b24e7723f7 + checksum: 599cb57d00f623ea968aaa8e75f421d29545b3b2e0a29b454165839db25379a793a338c71e1235c8f4e8f9f4f01c4b33b6e98bbd46e05e720631ac77a09d64b4 languageName: node linkType: hard @@ -3297,10 +3325,10 @@ __metadata: languageName: node linkType: hard -"node-os-utils@npm:^1.3.6": - version: 1.3.6 - resolution: "node-os-utils@npm:1.3.6" - checksum: 8ce66b648061dff3978c6e63ca7fa8748b285d28a5ebb57c6e6ce2250c34d8be408344d24a79590725a542eb4b6b297b1fa32ac6d73d85a3871b16afa204df6c +"node-os-utils@npm:^1.3.7": + version: 1.3.7 + resolution: "node-os-utils@npm:1.3.7" + checksum: 4059d74def168e2ffcd28e879731fd68f6e9b1e623804224e34bf0ca3c8968070e9e2cbc0cb57d80dd05d27552495a65c865b93ae5f54e61389e5af0b92ee818 languageName: node linkType: hard @@ -3697,12 +3725,12 @@ __metadata: languageName: node linkType: hard -"prettier@npm:^2.6.2": - version: 2.6.2 - resolution: "prettier@npm:2.6.2" +"prettier@npm:^2.7.0": + version: 2.7.0 + resolution: "prettier@npm:2.7.0" bin: prettier: bin-prettier.js - checksum: 48d08dde8e9fb1f5bccdd205baa7f192e9fc8bc98f86e1b97d919de804e28c806b0e6cc685e4a88211aa7987fa9668f30baae19580d87ced3ed0f2ec6572106f + checksum: 5b55bb1dced9d16635b83229df8e670d150890fdb343f19e8a66e610094a108e960c0f57352b3e5cdbc4eff4ef00a834406047ffcd9f20bd22a6497ba143c81f languageName: node linkType: hard @@ -4468,10 +4496,10 @@ __metadata: languageName: node linkType: hard -"undici@npm:^5.2.0, undici@npm:^5.4.0": - version: 5.4.0 - resolution: "undici@npm:5.4.0" - checksum: 2852397ee1dbb2b3ad58166520ec30f4f0143895f6e7b157dbedfac4e43b522a710a3927be61a67b2ae791190973f206d30785564ece7675e08133427cebaaa1 +"undici@npm:^5.4.0": + version: 5.5.1 + resolution: "undici@npm:5.5.1" + checksum: c041c9093df7ec683479a9555581206a7c7bebfc1ed6e8669920e55618461fc4ce08938e2fbf8ef7d692c2813c44392b5ed25c58396ee4a72ffd1f1f953712c9 languageName: node linkType: hard @@ -4642,7 +4670,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.5.0": +"ws@npm:^8.8.0": version: 8.8.0 resolution: "ws@npm:8.8.0" peerDependencies: -- cgit