From 14eb0e617b084080c4cffc5b781b311c65c5f928 Mon Sep 17 00:00:00 2001
From: IRONM00N <64110067+IRONM00N@users.noreply.github.com>
Date: Sun, 28 Aug 2022 21:51:17 -0400
Subject: rebrand v3
---
.eslintrc.cjs | 2 +-
.prettierrc.json | 2 +-
.vscode/launch.json | 2 +-
.vscode/settings.json | 1 +
.vscode/typescript.code-snippets | 4 +-
README.md | 31 +-
ecosystem.config.cjs | 6 +-
lib/arguments/abbreviatedNumber.ts | 4 +-
lib/arguments/contentWithDuration.ts | 4 +-
lib/arguments/discordEmoji.ts | 4 +-
lib/arguments/duration.ts | 4 +-
lib/arguments/durationSeconds.ts | 4 +-
lib/arguments/globalUser.ts | 4 +-
lib/arguments/messageLink.ts | 4 +-
lib/arguments/permission.ts | 4 +-
lib/arguments/roleWithDuration.ts | 4 +-
lib/arguments/snowflake.ts | 4 +-
lib/arguments/tinyColor.ts | 4 +-
lib/automod/AutomodShared.ts | 6 +-
lib/automod/MessageAutomod.ts | 10 +-
lib/common/BotCache.ts | 26 +
lib/common/BushCache.ts | 26 -
lib/common/HighlightManager.ts | 2 +-
.../discord-akairo/BotArgumentTypeCaster.ts | 3 +
lib/extensions/discord-akairo/BotCommand.ts | 597 ++++++++++++++++++++
lib/extensions/discord-akairo/BotCommandHandler.ts | 37 ++
lib/extensions/discord-akairo/BotInhibitor.ts | 18 +
.../discord-akairo/BotInhibitorHandler.ts | 3 +
lib/extensions/discord-akairo/BotListener.ts | 3 +
.../discord-akairo/BotListenerHandler.ts | 3 +
lib/extensions/discord-akairo/BotTask.ts | 3 +
lib/extensions/discord-akairo/BotTaskHandler.ts | 3 +
.../discord-akairo/BushArgumentTypeCaster.ts | 3 -
lib/extensions/discord-akairo/BushClient.ts | 601 --------------------
lib/extensions/discord-akairo/BushCommand.ts | 597 --------------------
.../discord-akairo/BushCommandHandler.ts | 37 --
lib/extensions/discord-akairo/BushInhibitor.ts | 19 -
.../discord-akairo/BushInhibitorHandler.ts | 3 -
lib/extensions/discord-akairo/BushListener.ts | 3 -
.../discord-akairo/BushListenerHandler.ts | 3 -
lib/extensions/discord-akairo/BushTask.ts | 3 -
lib/extensions/discord-akairo/BushTaskHandler.ts | 3 -
lib/extensions/discord-akairo/TanzaniteClient.ts | 591 ++++++++++++++++++++
lib/extensions/discord.js/BotClientEvents.ts | 211 +++++++
lib/extensions/discord.js/BushClientEvents.ts | 200 -------
lib/extensions/discord.js/ExtendedGuild.ts | 46 +-
lib/extensions/discord.js/ExtendedGuildMember.ts | 148 ++---
lib/extensions/discord.js/ExtendedUser.ts | 4 +-
lib/index.ts | 47 +-
lib/models/instance/Guild.ts | 4 +-
lib/types/BushInspectOptions.ts | 123 -----
lib/types/InspectOptions.ts | 127 +++++
lib/types/misc.ts | 14 +
lib/utils/Arg.ts | 36 +-
lib/utils/BotClientUtils.ts | 496 +++++++++++++++++
lib/utils/BushClientUtils.ts | 496 -----------------
lib/utils/BushConstants.ts | 554 -------------------
lib/utils/BushLogger.ts | 315 -----------
lib/utils/BushUtils.ts | 615 ---------------------
lib/utils/Constants.ts | 554 +++++++++++++++++++
lib/utils/ErrorHandler.ts | 8 +-
lib/utils/FormatResponse.ts | 4 +-
lib/utils/Logger.ts | 314 +++++++++++
lib/utils/Minecraft.ts | 4 -
lib/utils/Minecraft_Test.ts | 10 +-
lib/utils/Utils.ts | 615 +++++++++++++++++++++
package.json | 6 +-
src/bot.ts | 8 +-
src/commands/_fake-command/ironmoon.ts | 4 +-
src/commands/admin/channelPermissions.ts | 4 +-
src/commands/admin/roleAll.ts | 4 +-
src/commands/config/blacklist.ts | 4 +-
src/commands/config/config.ts | 4 +-
src/commands/config/disable.ts | 6 +-
src/commands/config/features.ts | 4 +-
src/commands/config/log.ts | 4 +-
src/commands/dev/__template.ts | 4 +-
src/commands/dev/dm.ts | 4 +-
src/commands/dev/eval.ts | 10 +-
src/commands/dev/javascript.ts | 4 +-
src/commands/dev/reload.ts | 4 +-
src/commands/dev/say.ts | 4 +-
src/commands/dev/servers.ts | 4 +-
src/commands/dev/sh.ts | 4 +-
src/commands/dev/superUser.ts | 4 +-
src/commands/dev/syncAutomod.ts | 4 +-
src/commands/dev/test.ts | 4 +-
src/commands/fun/coinFlip.ts | 4 +-
src/commands/fun/dice.ts | 4 +-
src/commands/fun/eightBall.ts | 4 +-
src/commands/fun/minesweeper.ts | 4 +-
src/commands/info/avatar.ts | 4 +-
src/commands/info/botInfo.ts | 4 +-
src/commands/info/color.ts | 4 +-
src/commands/info/guildInfo.ts | 16 +-
src/commands/info/help.ts | 16 +-
src/commands/info/icon.ts | 4 +-
src/commands/info/inviteInfo.ts | 4 +-
src/commands/info/links.ts | 4 +-
src/commands/info/ping.ts | 4 +-
src/commands/info/pronouns.ts | 4 +-
src/commands/info/snowflake.ts | 8 +-
src/commands/info/userInfo.ts | 4 +-
src/commands/leveling/leaderboard.ts | 4 +-
src/commands/leveling/level.ts | 4 +-
src/commands/leveling/levelRoles.ts | 4 +-
src/commands/leveling/setLevel.ts | 4 +-
src/commands/leveling/setXp.ts | 4 +-
src/commands/moderation/_activePunishments.ts | 4 +-
src/commands/moderation/ban.ts | 6 +-
src/commands/moderation/block.ts | 6 +-
src/commands/moderation/evidence.ts | 7 +-
src/commands/moderation/hideCase.ts | 7 +-
src/commands/moderation/kick.ts | 6 +-
src/commands/moderation/lockdown.ts | 4 +-
src/commands/moderation/massBan.ts | 13 +-
src/commands/moderation/massEvidence.ts | 7 +-
src/commands/moderation/modlog.ts | 4 +-
src/commands/moderation/mute.ts | 6 +-
src/commands/moderation/myLogs.ts | 4 +-
src/commands/moderation/purge.ts | 7 +-
src/commands/moderation/removeReactionEmoji.ts | 4 +-
src/commands/moderation/role.ts | 6 +-
src/commands/moderation/slowmode.ts | 4 +-
src/commands/moderation/timeout.ts | 6 +-
src/commands/moderation/unban.ts | 6 +-
src/commands/moderation/unblock.ts | 6 +-
src/commands/moderation/unlockdown.ts | 4 +-
src/commands/moderation/unmute.ts | 6 +-
src/commands/moderation/untimeout.ts | 6 +-
src/commands/moderation/warn.ts | 6 +-
src/commands/moulberry-bush/capePermissions.ts | 4 +-
src/commands/moulberry-bush/capes.ts | 4 +-
src/commands/moulberry-bush/giveawayPing.ts | 4 +-
src/commands/moulberry-bush/moulHammer.ts | 12 +-
src/commands/moulberry-bush/neuRepo.ts | 4 +-
src/commands/moulberry-bush/report.ts | 4 +-
src/commands/moulberry-bush/rule.ts | 4 +-
src/commands/moulberry-bush/serverStatus.ts | 4 +-
src/commands/moulberry-bush/solved.ts | 4 +-
src/commands/tickets/ticket-!.ts | 4 +-
src/commands/utilities/_poll.ts | 4 +-
src/commands/utilities/activity.ts | 8 +-
src/commands/utilities/calculator.ts | 4 +-
src/commands/utilities/decode.ts | 4 +-
src/commands/utilities/hash.ts | 4 +-
src/commands/utilities/highlight-!.ts | 4 +-
src/commands/utilities/highlight-add.ts | 4 +-
src/commands/utilities/highlight-block.ts | 4 +-
src/commands/utilities/highlight-clear.ts | 4 +-
src/commands/utilities/highlight-matches.ts | 4 +-
src/commands/utilities/highlight-remove.ts | 4 +-
src/commands/utilities/highlight-show.ts | 4 +-
src/commands/utilities/highlight-unblock.ts | 4 +-
src/commands/utilities/price.ts | 4 +-
src/commands/utilities/remind.ts | 4 +-
src/commands/utilities/reminders.ts | 4 +-
src/commands/utilities/steal.ts | 4 +-
src/commands/utilities/suicide.ts | 4 +-
src/commands/utilities/uuid.ts | 4 +-
src/commands/utilities/viewRaw.ts | 4 +-
src/commands/utilities/whoHasRole.ts | 4 +-
src/commands/utilities/wolframAlpha.ts | 4 +-
src/inhibitors/blacklist/channelGlobalBlacklist.ts | 7 +-
src/inhibitors/blacklist/channelGuildBlacklist.ts | 7 +-
src/inhibitors/blacklist/guildBlacklist.ts | 5 +-
src/inhibitors/blacklist/userGlobalBlacklist.ts | 5 +-
src/inhibitors/blacklist/userGuildBlacklist.ts | 5 +-
src/inhibitors/checks/fatal.ts | 5 +-
src/inhibitors/checks/guildUnavailable.ts | 5 +-
src/inhibitors/command/dm.ts | 7 +-
src/inhibitors/command/globalDisabledCommand.ts | 7 +-
src/inhibitors/command/guild.ts | 7 +-
src/inhibitors/command/guildDisabledCommand.ts | 7 +-
src/inhibitors/command/nsfw.ts | 7 +-
src/inhibitors/command/owner.ts | 7 +-
src/inhibitors/command/restrictedChannel.ts | 7 +-
src/inhibitors/command/restrictedGuild.ts | 7 +-
src/inhibitors/command/superUser.ts | 7 +-
src/listeners/automod/automodCreate.ts | 9 +-
src/listeners/automod/automodUpdate.ts | 9 +-
src/listeners/automod/memberAutomod.ts | 6 +-
src/listeners/automod/presenceAutomod.ts | 6 +-
src/listeners/bush/appealListener.ts | 9 +-
src/listeners/bush/joinAutoBan.ts | 11 +-
src/listeners/bush/supportThread.ts | 9 +-
src/listeners/bush/userUpdateAutoBan.ts | 11 +-
src/listeners/client/akairoDebug.ts | 9 +-
src/listeners/client/dcjsDebug.ts | 15 -
src/listeners/client/dcjsError.ts | 15 -
src/listeners/client/dcjsWarn.ts | 15 -
src/listeners/client/djsDebug.ts | 14 +
src/listeners/client/djsError.ts | 14 +
src/listeners/client/djsWarn.ts | 14 +
src/listeners/client/ready.ts | 6 +-
src/listeners/commands/commandBlocked.ts | 15 +-
src/listeners/commands/commandCooldown.ts | 9 +-
src/listeners/commands/commandError.ts | 9 +-
src/listeners/commands/commandLocked.ts | 9 +-
.../commands/commandMissingPermissions.ts | 13 +-
src/listeners/commands/commandStarted.ts | 9 +-
src/listeners/commands/messageBlocked.ts | 9 +-
src/listeners/commands/slashBlocked.ts | 9 +-
src/listeners/commands/slashCommandError.ts | 9 +-
src/listeners/commands/slashMissingPermissions.ts | 9 +-
src/listeners/commands/slashNotFound.ts | 9 +-
src/listeners/commands/slashStarted.ts | 9 +-
.../contextCommands/contextCommandBlocked.ts | 7 +-
.../contextCommands/contextCommandError.ts | 7 +-
.../contextCommands/contextCommandNotFound.ts | 7 +-
.../contextCommands/contextCommandStarted.ts | 7 +-
src/listeners/guild-custom/bushLockdown.ts | 33 --
src/listeners/guild-custom/bushUnlockdown.ts | 33 --
src/listeners/guild-custom/lockdown.ts | 32 ++
src/listeners/guild-custom/unlockdown.ts | 32 ++
src/listeners/guild/guildCreate.ts | 9 +-
src/listeners/guild/guildDelete.ts | 9 +-
src/listeners/guild/guildMemberAdd.ts | 9 +-
src/listeners/guild/guildMemberRemove.ts | 9 +-
src/listeners/guild/joinRoles.ts | 9 +-
src/listeners/guild/syncUnbanPunishmentModel.ts | 9 +-
src/listeners/interaction/interactionCreate.ts | 9 +-
src/listeners/member-custom/bushBan.ts | 33 --
src/listeners/member-custom/bushBlock.ts | 35 --
src/listeners/member-custom/bushKick.ts | 32 --
src/listeners/member-custom/bushLevelUpdate.ts | 64 ---
src/listeners/member-custom/bushMute.ts | 33 --
src/listeners/member-custom/bushPunishRole.ts | 32 --
.../member-custom/bushPunishRoleRemove.ts | 33 --
src/listeners/member-custom/bushPurge.ts | 44 --
src/listeners/member-custom/bushRemoveTimeout.ts | 32 --
src/listeners/member-custom/bushTimeout.ts | 33 --
src/listeners/member-custom/bushUnban.ts | 32 --
src/listeners/member-custom/bushUnblock.ts | 33 --
src/listeners/member-custom/bushUnmute.ts | 32 --
src/listeners/member-custom/bushUpdateModlog.ts | 41 --
src/listeners/member-custom/bushUpdateSettings.ts | 34 --
src/listeners/member-custom/bushWarn.ts | 32 --
src/listeners/member-custom/customBan.ts | 32 ++
src/listeners/member-custom/customBlock.ts | 36 ++
src/listeners/member-custom/customKick.ts | 31 ++
src/listeners/member-custom/customMute.ts | 32 ++
src/listeners/member-custom/customPurge.ts | 43 ++
src/listeners/member-custom/customRemoveTimeout.ts | 31 ++
src/listeners/member-custom/customTimeout.ts | 32 ++
src/listeners/member-custom/customUnban.ts | 31 ++
src/listeners/member-custom/customUnblock.ts | 32 ++
src/listeners/member-custom/customUnmute.ts | 31 ++
src/listeners/member-custom/customWarnMember.ts | 31 ++
src/listeners/member-custom/levelUpdate.ts | 63 +++
src/listeners/member-custom/massBan.ts | 11 +-
src/listeners/member-custom/massEvidence.ts | 11 +-
src/listeners/member-custom/punishRole.ts | 31 ++
src/listeners/member-custom/punishRoleRemove.ts | 32 ++
src/listeners/member-custom/updateModlog.ts | 40 ++
src/listeners/member-custom/updateSettings.ts | 33 ++
src/listeners/message/autoPublisher.ts | 9 +-
src/listeners/message/blacklistedFile.ts | 7 +-
src/listeners/message/boosterMessage.ts | 9 +-
src/listeners/message/directMessage.ts | 9 +-
src/listeners/message/highlight.ts | 9 +-
src/listeners/message/level.ts | 11 +-
src/listeners/message/quoteCreate.ts | 9 +-
src/listeners/message/quoteEdit.ts | 7 +-
src/listeners/message/verbose.ts | 9 +-
src/listeners/other/consoleListener.ts | 116 ++--
src/listeners/other/exit.ts | 4 +-
src/listeners/other/promiseRejection.ts | 4 +-
src/listeners/other/uncaughtException.ts | 4 +-
src/listeners/other/warning.ts | 4 +-
src/listeners/rest/rateLimit.ts | 7 +-
.../track-manual-punishments/modlogSyncBan.ts | 9 +-
.../track-manual-punishments/modlogSyncKick.ts | 9 +-
.../track-manual-punishments/modlogSyncTimeout.ts | 9 +-
.../track-manual-punishments/modlogSyncUnban.ts | 9 +-
src/listeners/ws/INTERACTION_CREATE.ts | 7 +-
src/tasks/cache/cpuUsage.ts | 4 +-
src/tasks/cache/updateCache.ts | 4 +-
src/tasks/cache/updateHighlightCache.ts | 5 +-
src/tasks/cache/updateNeuItemCache.ts | 4 +-
src/tasks/cache/updatePriceItemCache.ts | 4 +-
src/tasks/feature/handleReminders.ts | 4 +-
src/tasks/feature/removeExpiredPunishements.ts | 12 +-
src/tasks/feature/updateStats.ts | 4 +-
src/tasks/stats/guildCount.ts | 5 +-
src/tasks/stats/memberCount.ts | 4 +-
yarn.lock | 118 ++--
287 files changed, 5134 insertions(+), 5191 deletions(-)
create mode 100644 lib/common/BotCache.ts
delete mode 100644 lib/common/BushCache.ts
create mode 100644 lib/extensions/discord-akairo/BotArgumentTypeCaster.ts
create mode 100644 lib/extensions/discord-akairo/BotCommand.ts
create mode 100644 lib/extensions/discord-akairo/BotCommandHandler.ts
create mode 100644 lib/extensions/discord-akairo/BotInhibitor.ts
create mode 100644 lib/extensions/discord-akairo/BotInhibitorHandler.ts
create mode 100644 lib/extensions/discord-akairo/BotListener.ts
create mode 100644 lib/extensions/discord-akairo/BotListenerHandler.ts
create mode 100644 lib/extensions/discord-akairo/BotTask.ts
create mode 100644 lib/extensions/discord-akairo/BotTaskHandler.ts
delete mode 100644 lib/extensions/discord-akairo/BushArgumentTypeCaster.ts
delete mode 100644 lib/extensions/discord-akairo/BushClient.ts
delete mode 100644 lib/extensions/discord-akairo/BushCommand.ts
delete mode 100644 lib/extensions/discord-akairo/BushCommandHandler.ts
delete mode 100644 lib/extensions/discord-akairo/BushInhibitor.ts
delete mode 100644 lib/extensions/discord-akairo/BushInhibitorHandler.ts
delete mode 100644 lib/extensions/discord-akairo/BushListener.ts
delete mode 100644 lib/extensions/discord-akairo/BushListenerHandler.ts
delete mode 100644 lib/extensions/discord-akairo/BushTask.ts
delete mode 100644 lib/extensions/discord-akairo/BushTaskHandler.ts
create mode 100644 lib/extensions/discord-akairo/TanzaniteClient.ts
create mode 100644 lib/extensions/discord.js/BotClientEvents.ts
delete mode 100644 lib/extensions/discord.js/BushClientEvents.ts
delete mode 100644 lib/types/BushInspectOptions.ts
create mode 100644 lib/types/InspectOptions.ts
create mode 100644 lib/types/misc.ts
create mode 100644 lib/utils/BotClientUtils.ts
delete mode 100644 lib/utils/BushClientUtils.ts
delete mode 100644 lib/utils/BushConstants.ts
delete mode 100644 lib/utils/BushLogger.ts
delete mode 100644 lib/utils/BushUtils.ts
create mode 100644 lib/utils/Constants.ts
create mode 100644 lib/utils/Logger.ts
create mode 100644 lib/utils/Utils.ts
delete mode 100644 src/listeners/client/dcjsDebug.ts
delete mode 100644 src/listeners/client/dcjsError.ts
delete mode 100644 src/listeners/client/dcjsWarn.ts
create mode 100644 src/listeners/client/djsDebug.ts
create mode 100644 src/listeners/client/djsError.ts
create mode 100644 src/listeners/client/djsWarn.ts
delete mode 100644 src/listeners/guild-custom/bushLockdown.ts
delete mode 100644 src/listeners/guild-custom/bushUnlockdown.ts
create mode 100644 src/listeners/guild-custom/lockdown.ts
create mode 100644 src/listeners/guild-custom/unlockdown.ts
delete mode 100644 src/listeners/member-custom/bushBan.ts
delete mode 100644 src/listeners/member-custom/bushBlock.ts
delete mode 100644 src/listeners/member-custom/bushKick.ts
delete mode 100644 src/listeners/member-custom/bushLevelUpdate.ts
delete mode 100644 src/listeners/member-custom/bushMute.ts
delete mode 100644 src/listeners/member-custom/bushPunishRole.ts
delete mode 100644 src/listeners/member-custom/bushPunishRoleRemove.ts
delete mode 100644 src/listeners/member-custom/bushPurge.ts
delete mode 100644 src/listeners/member-custom/bushRemoveTimeout.ts
delete mode 100644 src/listeners/member-custom/bushTimeout.ts
delete mode 100644 src/listeners/member-custom/bushUnban.ts
delete mode 100644 src/listeners/member-custom/bushUnblock.ts
delete mode 100644 src/listeners/member-custom/bushUnmute.ts
delete mode 100644 src/listeners/member-custom/bushUpdateModlog.ts
delete mode 100644 src/listeners/member-custom/bushUpdateSettings.ts
delete mode 100644 src/listeners/member-custom/bushWarn.ts
create mode 100644 src/listeners/member-custom/customBan.ts
create mode 100644 src/listeners/member-custom/customBlock.ts
create mode 100644 src/listeners/member-custom/customKick.ts
create mode 100644 src/listeners/member-custom/customMute.ts
create mode 100644 src/listeners/member-custom/customPurge.ts
create mode 100644 src/listeners/member-custom/customRemoveTimeout.ts
create mode 100644 src/listeners/member-custom/customTimeout.ts
create mode 100644 src/listeners/member-custom/customUnban.ts
create mode 100644 src/listeners/member-custom/customUnblock.ts
create mode 100644 src/listeners/member-custom/customUnmute.ts
create mode 100644 src/listeners/member-custom/customWarnMember.ts
create mode 100644 src/listeners/member-custom/levelUpdate.ts
create mode 100644 src/listeners/member-custom/punishRole.ts
create mode 100644 src/listeners/member-custom/punishRoleRemove.ts
create mode 100644 src/listeners/member-custom/updateModlog.ts
create mode 100644 src/listeners/member-custom/updateSettings.ts
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index b8ef98d..3666e7a 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -161,7 +161,7 @@ module.exports = {
'@typescript-eslint/explicit-module-boundary-types': 'off',
'prefer-template': 'warn',
'@typescript-eslint/no-this-alias': ['error', { allowDestructuring: true, allowedNames: ['that'] }],
- '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
+ '@typescript-eslint/no-unused-vars': 'off' /* ['warn', { argsIgnorePattern: '^_' }] */,
'no-implied-eval': 'off',
'@typescript-eslint/no-implied-eval': ['error'],
'deprecation/deprecation': 'warn',
diff --git a/.prettierrc.json b/.prettierrc.json
index d704546..5c947a7 100644
--- a/.prettierrc.json
+++ b/.prettierrc.json
@@ -7,7 +7,7 @@
"endOfLine": "lf",
"overrides": [
{
- "files": ["*BushClientEvents.ts"],
+ "files": ["*CustomClientEvents.ts"],
"options": {
"printWidth": 80
}
diff --git a/.vscode/launch.json b/.vscode/launch.json
index d6eb609..cce0ba4 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -3,7 +3,7 @@
{
"type": "node-terminal",
"command": "yarn start",
- "name": "BushBot",
+ "name": "Tanzanite",
"request": "launch",
"cwd": "${workspaceFolder}",
"program": "${workspaceFolder}/dist/src/bot.js",
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 651c2c2..600c85e 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -20,6 +20,7 @@
"source.organizeImports": true,
"source.format": true
},
+ "editor.formatOnSave": true,
"diffEditor.wordWrap": "on",
"editor.insertSpaces": false,
"editor.wordWrap": "on",
diff --git a/.vscode/typescript.code-snippets b/.vscode/typescript.code-snippets
index 77b2f7f..4e2bec4 100644
--- a/.vscode/typescript.code-snippets
+++ b/.vscode/typescript.code-snippets
@@ -10,8 +10,8 @@
"description": "A bot command template",
"body": [
"import {",
- "\tBushCommand,",
"\tclientSendAndPermCheck,",
+ "\tCustomCommand,",
"\temojis,",
"\ttype ArgType,",
"\ttype CommandMessage,",
@@ -20,7 +20,7 @@
"} from '#lib';",
"",
"import { ApplicationCommandOptionType } from 'discord.js';",
- "export default class ${1:CommandName} extends BushCommand {",
+ "export default class ${1:CommandName} extends CustomCommand {",
"\tpublic constructor() {",
"\t\tsuper('${2:commandId}', {",
"\t\t\taliases: ['${3:alias}'],",
diff --git a/README.md b/README.md
index 714965d..3cc7955 100644
--- a/README.md
+++ b/README.md
@@ -1,41 +1,38 @@
- BushBot
+ Tanzanite
-BushBot is an open-sourced multi-purpose moderation, and leveling bot.
+Tanzanite is a multipurpose moderation and utility bot. It contains an extensive auto-moderation system, leveling features, message highlighting, and information commands.
Set Up
@@ -61,4 +58,4 @@ If you would like to contribute to the bot feel free to open a pull request and
Credits
- discord.js - The main library used to interface with discord
-- discord-akairo - The framework the bot is built on
+- discord-akairo - The framework the bot is built on
diff --git a/ecosystem.config.cjs b/ecosystem.config.cjs
index 3bd532f..32971cf 100644
--- a/ecosystem.config.cjs
+++ b/ecosystem.config.cjs
@@ -1,7 +1,7 @@
module.exports = {
apps: [
...['', '-beta'].map((e) => ({
- name: `bush-bot${e}`,
+ name: `tanzanite${e}`,
script: 'yarn',
args: 'start:raw',
out_file: `../bushbot${e}.log`,
@@ -22,9 +22,9 @@ module.exports = {
'user': 'pi',
'host': '192.168.1.210',
'ref': `origin/${e === 'production' ? 'master' : 'beta'}`,
- 'repo': 'https://github.com/NotEnoughUpdates/bush-bot.git',
+ 'repo': 'https://github.com/TanzaniteBot/tanzanite.git',
'path': `/code/bush-bot${e === 'beta' ? '-beta' : ''}`,
- 'post-deploy': `yarn install && yarn build && pm2 start ecosystem.config.cjs --only bush-bot${
+ 'post-deploy': `yarn install && yarn build && pm2 start ecosystem.config.cjs --only tanzanite${
e === 'beta' ? '-beta' : ''
}`
}
diff --git a/lib/arguments/abbreviatedNumber.ts b/lib/arguments/abbreviatedNumber.ts
index a7d8ce5..5fe39b5 100644
--- a/lib/arguments/abbreviatedNumber.ts
+++ b/lib/arguments/abbreviatedNumber.ts
@@ -1,9 +1,9 @@
-import type { BushArgumentTypeCaster } from '#lib';
+import type { BotArgumentTypeCaster } from '#lib';
import assert from 'assert/strict';
import numeral from 'numeral';
assert(typeof numeral === 'function');
-export const abbreviatedNumber: BushArgumentTypeCaster = (_, phrase) => {
+export const abbreviatedNumber: BotArgumentTypeCaster = (_, phrase) => {
if (!phrase) return null;
const num = numeral(phrase?.toLowerCase()).value();
diff --git a/lib/arguments/contentWithDuration.ts b/lib/arguments/contentWithDuration.ts
index 0efba39..ec015dc 100644
--- a/lib/arguments/contentWithDuration.ts
+++ b/lib/arguments/contentWithDuration.ts
@@ -1,5 +1,5 @@
-import { parseDuration, type BushArgumentTypeCaster, type ParsedDuration } from '#lib';
+import { parseDuration, type BotArgumentTypeCaster, type ParsedDuration } from '#lib';
-export const contentWithDuration: BushArgumentTypeCaster> = async (_, phrase) => {
+export const contentWithDuration: BotArgumentTypeCaster> = async (_, phrase) => {
return parseDuration(phrase);
};
diff --git a/lib/arguments/discordEmoji.ts b/lib/arguments/discordEmoji.ts
index 92d6502..0a0d168 100644
--- a/lib/arguments/discordEmoji.ts
+++ b/lib/arguments/discordEmoji.ts
@@ -1,7 +1,7 @@
-import { regex, type BushArgumentTypeCaster } from '#lib';
+import { regex, type BotArgumentTypeCaster } from '#lib';
import type { Snowflake } from 'discord.js';
-export const discordEmoji: BushArgumentTypeCaster = (_, phrase) => {
+export const discordEmoji: BotArgumentTypeCaster = (_, phrase) => {
if (!phrase) return null;
const validEmoji: RegExpExecArray | null = regex.discordEmoji.exec(phrase);
if (!validEmoji || !validEmoji.groups) return null;
diff --git a/lib/arguments/duration.ts b/lib/arguments/duration.ts
index 09dd3d5..4952dc4 100644
--- a/lib/arguments/duration.ts
+++ b/lib/arguments/duration.ts
@@ -1,5 +1,5 @@
-import { parseDuration, type BushArgumentTypeCaster } from '#lib';
+import { parseDuration, type BotArgumentTypeCaster } from '#lib';
-export const duration: BushArgumentTypeCaster = (_, phrase) => {
+export const duration: BotArgumentTypeCaster = (_, phrase) => {
return parseDuration(phrase).duration;
};
diff --git a/lib/arguments/durationSeconds.ts b/lib/arguments/durationSeconds.ts
index d8d6749..8deee5b 100644
--- a/lib/arguments/durationSeconds.ts
+++ b/lib/arguments/durationSeconds.ts
@@ -1,6 +1,6 @@
-import { parseDuration, type BushArgumentTypeCaster } from '#lib';
+import { parseDuration, type BotArgumentTypeCaster } from '#lib';
-export const durationSeconds: BushArgumentTypeCaster = (_, phrase) => {
+export const durationSeconds: BotArgumentTypeCaster = (_, phrase) => {
phrase += 's';
return parseDuration(phrase).duration;
};
diff --git a/lib/arguments/globalUser.ts b/lib/arguments/globalUser.ts
index 4324aa9..4198e3c 100644
--- a/lib/arguments/globalUser.ts
+++ b/lib/arguments/globalUser.ts
@@ -1,7 +1,7 @@
-import type { BushArgumentTypeCaster } from '#lib';
+import type { BotArgumentTypeCaster } from '#lib';
import type { User } from 'discord.js';
// resolve non-cached users
-export const globalUser: BushArgumentTypeCaster> = async (message, phrase) => {
+export const globalUser: BotArgumentTypeCaster> = async (message, phrase) => {
return message.client.users.resolve(phrase) ?? (await message.client.users.fetch(`${phrase}`).catch(() => null));
};
diff --git a/lib/arguments/messageLink.ts b/lib/arguments/messageLink.ts
index c95e42d..ffb48a0 100644
--- a/lib/arguments/messageLink.ts
+++ b/lib/arguments/messageLink.ts
@@ -1,7 +1,7 @@
-import { BushArgumentTypeCaster, regex } from '#lib';
+import { BotArgumentTypeCaster, regex } from '#lib';
import type { Message } from 'discord.js';
-export const messageLink: BushArgumentTypeCaster> = async (message, phrase) => {
+export const messageLink: BotArgumentTypeCaster> = async (message, phrase) => {
const match = new RegExp(regex.messageLink).exec(phrase);
if (!match || !match.groups) return null;
diff --git a/lib/arguments/permission.ts b/lib/arguments/permission.ts
index 98bfe74..4d09e9c 100644
--- a/lib/arguments/permission.ts
+++ b/lib/arguments/permission.ts
@@ -1,7 +1,7 @@
-import type { BushArgumentTypeCaster } from '#lib';
+import type { BotArgumentTypeCaster } from '#lib';
import { PermissionFlagsBits, type PermissionsString } from 'discord.js';
-export const permission: BushArgumentTypeCaster = (_, phrase) => {
+export const permission: BotArgumentTypeCaster = (_, phrase) => {
if (!phrase) return null;
phrase = phrase.toUpperCase().replace(/ /g, '_');
if (!(phrase in PermissionFlagsBits)) {
diff --git a/lib/arguments/roleWithDuration.ts b/lib/arguments/roleWithDuration.ts
index b97f205..9391c75 100644
--- a/lib/arguments/roleWithDuration.ts
+++ b/lib/arguments/roleWithDuration.ts
@@ -1,7 +1,7 @@
-import { Arg, BushArgumentTypeCaster, parseDuration } from '#lib';
+import { Arg, BotArgumentTypeCaster, parseDuration } from '#lib';
import type { Role } from 'discord.js';
-export const roleWithDuration: BushArgumentTypeCaster> = async (message, phrase) => {
+export const roleWithDuration: BotArgumentTypeCaster> = async (message, phrase) => {
// eslint-disable-next-line prefer-const
let { duration, content } = parseDuration(phrase);
if (content === null || content === undefined) return null;
diff --git a/lib/arguments/snowflake.ts b/lib/arguments/snowflake.ts
index b98a20f..ab0c7fc 100644
--- a/lib/arguments/snowflake.ts
+++ b/lib/arguments/snowflake.ts
@@ -1,7 +1,7 @@
-import { BushArgumentTypeCaster, regex } from '#lib';
+import { BotArgumentTypeCaster, regex } from '#lib';
import type { Snowflake } from 'discord.js';
-export const snowflake: BushArgumentTypeCaster = (_, phrase) => {
+export const snowflake: BotArgumentTypeCaster = (_, phrase) => {
if (!phrase) return null;
if (regex.snowflake.test(phrase)) return phrase;
return null;
diff --git a/lib/arguments/tinyColor.ts b/lib/arguments/tinyColor.ts
index 148c078..2eb6ab2 100644
--- a/lib/arguments/tinyColor.ts
+++ b/lib/arguments/tinyColor.ts
@@ -1,9 +1,9 @@
-import type { BushArgumentTypeCaster } from '#lib';
+import type { BotArgumentTypeCaster } from '#lib';
import assert from 'assert/strict';
import tinycolorModule from 'tinycolor2';
assert(tinycolorModule);
-export const tinyColor: BushArgumentTypeCaster = (_message, phrase) => {
+export const tinyColor: BotArgumentTypeCaster = (_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/lib/automod/AutomodShared.ts b/lib/automod/AutomodShared.ts
index 9cdb020..29b0536 100644
--- a/lib/automod/AutomodShared.ts
+++ b/lib/automod/AutomodShared.ts
@@ -1,6 +1,6 @@
import * as Moderation from '#lib/common/Moderation.js';
import { unmuteResponse } from '#lib/extensions/discord.js/ExtendedGuildMember.js';
-import { colors, emojis } from '#lib/utils/BushConstants.js';
+import { colors, emojis } from '#lib/utils/Constants.js';
import * as Format from '#lib/utils/Format.js';
import { formatUnmuteResponse } from '#lib/utils/FormatResponse.js';
import {
@@ -165,7 +165,7 @@ export async function handleAutomodInteraction(interaction: ButtonInteraction) {
const check = victim ? await Moderation.permissionCheck(moderator, victim, 'ban', true) : true;
if (check !== true) return interaction.reply({ content: check, ephemeral: true });
- const result = await interaction.guild?.bushBan({
+ const result = await interaction.guild?.customBan({
user: userId,
reason,
moderator: interaction.user.id,
@@ -209,7 +209,7 @@ export async function handleAutomodInteraction(interaction: ButtonInteraction) {
const check2 = await Moderation.checkMutePermissions(interaction.guild);
if (check2 !== true) return interaction.reply({ content: formatUnmuteResponse('/', victim!, check2), ephemeral: true });
- const result = await victim.bushUnmute({
+ const result = await victim.customUnmute({
reason,
moderator: interaction.member as GuildMember,
evidence: (interaction.message as Message).url ?? undefined
diff --git a/lib/automod/MessageAutomod.ts b/lib/automod/MessageAutomod.ts
index 0abd34c..0b6ebba 100644
--- a/lib/automod/MessageAutomod.ts
+++ b/lib/automod/MessageAutomod.ts
@@ -2,8 +2,8 @@ import { stripIndent } from '#tags';
import assert from 'assert/strict';
import chalk from 'chalk';
import { EmbedBuilder, GuildTextBasedChannel, PermissionFlagsBits, type Message } from 'discord.js';
-import { colors } from '../utils/BushConstants.js';
-import { format, formatError } from '../utils/BushUtils.js';
+import { colors } from '../utils/Constants.js';
+import { format, formatError } from '../utils/Utils.js';
import { Automod, BadWordDetails, Severity } from './AutomodShared.js';
/**
@@ -202,7 +202,7 @@ export class MessageAutomod extends Automod {
}
case Severity.WARN: {
void this.message.delete().catch((e) => deleteError.bind(this, e));
- void this.member.bushWarn({
+ void this.member.customWarn({
moderator: this.guild!.members.me!,
reason: `[Automod] ${highestOffense.reason}`
});
@@ -211,7 +211,7 @@ export class MessageAutomod extends Automod {
}
case Severity.TEMP_MUTE: {
void this.message.delete().catch((e) => deleteError.bind(this, e));
- void this.member.bushMute({
+ void this.member.customMute({
moderator: this.guild!.members.me!,
reason: `[Automod] ${highestOffense.reason}`,
duration: 900_000 // 15 minutes
@@ -221,7 +221,7 @@ export class MessageAutomod extends Automod {
}
case Severity.PERM_MUTE: {
void this.message.delete().catch((e) => deleteError.bind(this, e));
- void this.member.bushMute({
+ void this.member.customMute({
moderator: this.guild!.members.me!,
reason: `[Automod] ${highestOffense.reason}`,
duration: 0 // permanent
diff --git a/lib/common/BotCache.ts b/lib/common/BotCache.ts
new file mode 100644
index 0000000..e91d9e5
--- /dev/null
+++ b/lib/common/BotCache.ts
@@ -0,0 +1,26 @@
+import { BadWords, GlobalModel, SharedModel, type Guild } from '#lib';
+import { Collection, type Snowflake } from 'discord.js';
+
+export class BotCache {
+ public global = new GlobalCache();
+ public shared = new SharedCache();
+ public guilds = new GuildCache();
+}
+
+export class GlobalCache implements Omit {
+ public disabledCommands: string[] = [];
+ public blacklistedChannels: Snowflake[] = [];
+ public blacklistedGuilds: Snowflake[] = [];
+ public blacklistedUsers: Snowflake[] = [];
+}
+
+export class SharedCache implements Omit {
+ public superUsers: Snowflake[] = [];
+ public privilegedUsers: Snowflake[] = [];
+ public badLinksSecret: string[] = [];
+ public badLinks: string[] = [];
+ public badWords: BadWords = {};
+ public autoBanCode: string | null = null;
+}
+
+export class GuildCache extends Collection {}
diff --git a/lib/common/BushCache.ts b/lib/common/BushCache.ts
deleted file mode 100644
index 22a13ef..0000000
--- a/lib/common/BushCache.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { BadWords, GlobalModel, SharedModel, type Guild } from '#lib';
-import { Collection, type Snowflake } from 'discord.js';
-
-export class BushCache {
- public global = new GlobalCache();
- public shared = new SharedCache();
- public guilds = new GuildCache();
-}
-
-export class GlobalCache implements Omit {
- public disabledCommands: string[] = [];
- public blacklistedChannels: Snowflake[] = [];
- public blacklistedGuilds: Snowflake[] = [];
- public blacklistedUsers: Snowflake[] = [];
-}
-
-export class SharedCache implements Omit {
- public superUsers: Snowflake[] = [];
- public privilegedUsers: Snowflake[] = [];
- public badLinksSecret: string[] = [];
- public badLinks: string[] = [];
- public badWords: BadWords = {};
- public autoBanCode: string | null = null;
-}
-
-export class GuildCache extends Collection {}
diff --git a/lib/common/HighlightManager.ts b/lib/common/HighlightManager.ts
index cc31413..ca71a83 100644
--- a/lib/common/HighlightManager.ts
+++ b/lib/common/HighlightManager.ts
@@ -10,7 +10,7 @@ import {
type Snowflake,
type TextBasedChannel
} from 'discord.js';
-import { colors, Time } from '../utils/BushConstants.js';
+import { colors, Time } from '../utils/Constants.js';
import { sanitizeInputForDiscord } from '../utils/Format.js';
const NOTIFY_COOLDOWN = 5 * Time.Minute;
diff --git a/lib/extensions/discord-akairo/BotArgumentTypeCaster.ts b/lib/extensions/discord-akairo/BotArgumentTypeCaster.ts
new file mode 100644
index 0000000..5f4f32f
--- /dev/null
+++ b/lib/extensions/discord-akairo/BotArgumentTypeCaster.ts
@@ -0,0 +1,3 @@
+import { type CommandMessage } from '#lib';
+
+export type BotArgumentTypeCaster = (message: CommandMessage, phrase: string) => R;
diff --git a/lib/extensions/discord-akairo/BotCommand.ts b/lib/extensions/discord-akairo/BotCommand.ts
new file mode 100644
index 0000000..abd945e
--- /dev/null
+++ b/lib/extensions/discord-akairo/BotCommand.ts
@@ -0,0 +1,597 @@
+import { type DiscordEmojiInfo, type RoleWithDuration } from '#args';
+import {
+ type BotArgumentTypeCaster,
+ type BotCommandHandler,
+ type BotInhibitor,
+ type BotListener,
+ type BotTask,
+ type ParsedDuration,
+ type TanzaniteClient
+} from '#lib';
+import {
+ ArgumentMatch,
+ Command,
+ CommandUtil,
+ type AkairoApplicationCommandAutocompleteOption,
+ type AkairoApplicationCommandChannelOptionData,
+ type AkairoApplicationCommandChoicesData,
+ type AkairoApplicationCommandNonOptionsData,
+ type AkairoApplicationCommandNumericOptionData,
+ type AkairoApplicationCommandOptionData,
+ type AkairoApplicationCommandSubCommandData,
+ type AkairoApplicationCommandSubGroupData,
+ type ArgumentOptions,
+ type ArgumentType,
+ type ArgumentTypeCaster,
+ type BaseArgumentType,
+ type CommandOptions,
+ type ContextMenuCommand,
+ type MissingPermissionSupplier,
+ type SlashOption,
+ type SlashResolveType
+} from 'discord-akairo';
+import {
+ Message,
+ PermissionsBitField,
+ User,
+ type ApplicationCommandOptionChoiceData,
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ type ApplicationCommandOptionType,
+ type PermissionResolvable,
+ type PermissionsString,
+ type Snowflake
+} from 'discord.js';
+import _ from 'lodash';
+import { SlashMessage } from './SlashMessage.js';
+
+export interface OverriddenBaseArgumentType extends BaseArgumentType {
+ commandAlias: BotCommand | null;
+ command: BotCommand | null;
+ inhibitor: BotInhibitor | null;
+ listener: BotListener | null;
+ task: BotTask | null;
+ contextMenuCommand: ContextMenuCommand | null;
+}
+
+export interface BaseBotArgumentType extends OverriddenBaseArgumentType {
+ duration: number | null;
+ contentWithDuration: ParsedDuration;
+ permission: PermissionsString | null;
+ snowflake: Snowflake | null;
+ discordEmoji: DiscordEmojiInfo | null;
+ roleWithDuration: RoleWithDuration | null;
+ abbreviatedNumber: number | null;
+ globalUser: User | null;
+ messageLink: Message | null;
+ durationSeconds: number | null;
+ tinyColor: string | null;
+}
+
+export type BotArgumentType = keyof BaseBotArgumentType | RegExp;
+
+interface BaseBotArgumentOptions extends Omit, ExtraArgumentOptions {
+ id: string;
+ description: string;
+
+ /**
+ * The message sent for the prompt and the slash command description.
+ */
+ prompt?: string;
+
+ /**
+ * The message set for the retry prompt.
+ */
+ retry?: string;
+
+ /**
+ * Whether or not the argument is optional.
+ */
+ optional?: boolean;
+
+ /**
+ * The type used for slash commands. Set to false to disable this argument for slash commands.
+ */
+ slashType: AkairoApplicationCommandOptionData['type'] | false;
+
+ /**
+ * Allows you to get a discord resolved object
+ *
+ * ex. get the resolved member object when the type is {@link ApplicationCommandOptionType.User User}
+ */
+ slashResolve?: SlashResolveType;
+
+ /**
+ * The choices of the option for the user to pick from
+ */
+ choices?: ApplicationCommandOptionChoiceData[];
+
+ /**
+ * Whether the option is an autocomplete option
+ */
+ autocomplete?: boolean;
+
+ /**
+ * When the option type is channel, the allowed types of channels that can be selected
+ */
+ channelTypes?: AkairoApplicationCommandChannelOptionData['channelTypes'];
+
+ /**
+ * The minimum value for an {@link ApplicationCommandOptionType.Integer Integer} or {@link ApplicationCommandOptionType.Number Number} option
+ */
+ minValue?: number;
+
+ /**
+ * The maximum value for an {@link ApplicationCommandOptionType.Integer Integer} or {@link ApplicationCommandOptionType.Number Number} option
+ */
+ maxValue?: number;
+}
+
+interface ExtraArgumentOptions {
+ /**
+ * Restrict this argument to only slash or only text commands.
+ */
+ only?: 'slash' | 'text';
+
+ /**
+ * Readable type for the help command.
+ */
+ readableType?: string;
+
+ /**
+ * Whether the argument is only accessible to the owners.
+ * @default false
+ */
+ ownerOnly?: boolean;
+
+ /**
+ * Whether the argument is only accessible to the super users.
+ * @default false
+ */
+ superUserOnly?: boolean;
+}
+
+export interface BotArgumentOptions extends BaseBotArgumentOptions {
+ /**
+ * The type that the argument should be cast to.
+ * - `string` does not cast to any type.
+ * - `lowercase` makes the input lowercase.
+ * - `uppercase` makes the input uppercase.
+ * - `charCodes` transforms the input to an array of char codes.
+ * - `number` casts to a number.
+ * - `integer` casts to an integer.
+ * - `bigint` casts to a big integer.
+ * - `url` casts to an `URL` object.
+ * - `date` casts to a `Date` object.
+ * - `color` casts a hex code to an integer.
+ * - `commandAlias` tries to resolve to a command from an alias.
+ * - `command` matches the ID of a command.
+ * - `inhibitor` matches the ID of an inhibitor.
+ * - `listener` matches the ID of a listener.
+ *
+ * Possible Discord-related types.
+ * These types can be plural (add an 's' to the end) and a collection of matching objects will be used.
+ * - `user` tries to resolve to a user.
+ * - `member` tries to resolve to a member.
+ * - `relevant` tries to resolve to a relevant user, works in both guilds and DMs.
+ * - `channel` tries to resolve to a channel.
+ * - `textChannel` tries to resolve to a text channel.
+ * - `voiceChannel` tries to resolve to a voice channel.
+ * - `stageChannel` tries to resolve to a stage channel.
+ * - `threadChannel` tries to resolve a thread channel.
+ * - `role` tries to resolve to a role.
+ * - `emoji` tries to resolve to a custom emoji.
+ * - `guild` tries to resolve to a guild.
+ * - `permission` tries to resolve to a permissions.
+ *
+ * Other Discord-related types:
+ * - `message` tries to fetch a message from an ID within the channel.
+ * - `guildMessage` tries to fetch a message from an ID within the guild.
+ * - `relevantMessage` is a combination of the above, works in both guilds and DMs.
+ * - `invite` tries to fetch an invite object from a link.
+ * - `userMention` matches a mention of a user.
+ * - `memberMention` matches a mention of a guild member.
+ * - `channelMention` matches a mention of a channel.
+ * - `roleMention` matches a mention of a role.
+ * - `emojiMention` matches a mention of an emoji.
+ *
+ * Misc:
+ * - `duration` tries to parse duration in milliseconds
+ * - `contentWithDuration` tries to parse duration in milliseconds and returns the remaining content with the duration
+ * removed
+ */
+ type?: BotArgumentType | (keyof BaseBotArgumentType)[] | BotArgumentTypeCaster;
+}
+
+export interface CustomBotArgumentOptions extends BaseBotArgumentOptions {
+ /**
+ * An array of strings can be used to restrict input to only those strings, case insensitive.
+ * The array can also contain an inner array of strings, for aliases.
+ * If so, the first entry of the array will be used as the final argument.
+ *
+ * A regular expression can also be used.
+ * The evaluated argument will be an object containing the `match` and `matches` if global.
+ */
+ customType?: (string | string[])[] | RegExp | string | null;
+}
+
+export type CustomMissingPermissionSupplier = (message: CommandMessage | SlashMessage) => Promise | any;
+
+interface ExtendedCommandOptions {
+ /**
+ * Whether the command is hidden from the help command.
+ */
+ hidden?: boolean;
+
+ /**
+ * The channels the command is limited to run in.
+ */
+ restrictedChannels?: Snowflake[];
+
+ /**
+ * The guilds the command is limited to run in.
+ */
+ restrictedGuilds?: Snowflake[];
+
+ /**
+ * Show how to use the command.
+ */
+ usage: string[];
+
+ /**
+ * Examples for how to use the command.
+ */
+ examples: string[];
+
+ /**
+ * A fake command, completely hidden from the help command.
+ */
+ pseudo?: boolean;
+
+ /**
+ * Allow this command to be run in channels that are blacklisted.
+ */
+ bypassChannelBlacklist?: boolean;
+
+ /**
+ * Use instead of {@link BaseBotCommandOptions.args} when using argument generators or custom slashOptions
+ */
+ helpArgs?: ArgsInfo[];
+
+ /**
+ * Extra information about the command, displayed in the help command.
+ */
+ note?: string;
+}
+
+export interface BaseBotCommandOptions
+ extends Omit,
+ ExtendedCommandOptions {
+ /**
+ * The description of the command.
+ */
+ description: string;
+
+ /**
+ * The arguments for the command.
+ */
+ args?: BotArgumentOptions[] & CustomBotArgumentOptions[];
+
+ category: string;
+
+ /**
+ * Permissions required by the client to run this command.
+ */
+ clientPermissions: bigint | bigint[] | CustomMissingPermissionSupplier;
+
+ /**
+ * Permissions required by the user to run this command.
+ */
+ userPermissions: bigint | bigint[] | CustomMissingPermissionSupplier;
+
+ /**
+ * Whether the argument is only accessible to the owners.
+ */
+ ownerOnly?: boolean;
+
+ /**
+ * Whether the argument is only accessible to the super users.
+ */
+ superUserOnly?: boolean;
+}
+
+export type CustomCommandOptions = Omit | Omit;
+
+export interface ArgsInfo {
+ /**
+ * The name of the argument.
+ */
+ name: string;
+
+ /**
+ * The description of the argument.
+ */
+ description: string;
+
+ /**
+ * Whether the argument is optional.
+ * @default false
+ */
+ optional?: boolean;
+
+ /**
+ * Whether or not the argument has autocomplete enabled.
+ * @default false
+ */
+ autocomplete?: boolean;
+
+ /**
+ * Whether the argument is restricted a certain command.
+ * @default 'slash & text'
+ */
+ only?: 'slash & text' | 'slash' | 'text';
+
+ /**
+ * The method that arguments are matched for text commands.
+ * @default 'phrase'
+ */
+ match?: ArgumentMatch;
+
+ /**
+ * The readable type of the argument.
+ */
+ type: string;
+
+ /**
+ * If {@link match} is 'flag' or 'option', these are the flags that are matched
+ * @default []
+ */
+ flag?: string[];
+
+ /**
+ * Whether the argument is only accessible to the owners.
+ * @default false
+ */
+ ownerOnly?: boolean;
+
+ /**
+ * Whether the argument is only accessible to the super users.
+ * @default false
+ */
+ superUserOnly?: boolean;
+}
+
+export abstract class BotCommand extends Command {
+ public declare client: TanzaniteClient;
+ public declare handler: BotCommandHandler;
+ public declare description: string;
+
+ /**
+ * Show how to use the command.
+ */
+ public usage: string[];
+
+ /**
+ * Examples for how to use the command.
+ */
+ public examples: string[];
+
+ /**
+ * The options sent to the constructor
+ */
+ public options: CustomCommandOptions;
+
+ /**
+ * The options sent to the super call
+ */
+ public parsedOptions: CommandOptions;
+
+ /**
+ * The channels the command is limited to run in.
+ */
+ public restrictedChannels: Snowflake[] | undefined;
+
+ /**
+ * The guilds the command is limited to run in.
+ */
+ public restrictedGuilds: Snowflake[] | undefined;
+
+ /**
+ * Whether the command is hidden from the help command.
+ */
+ public hidden: boolean;
+
+ /**
+ * A fake command, completely hidden from the help command.
+ */
+ public pseudo: boolean;
+
+ /**
+ * Allow this command to be run in channels that are blacklisted.
+ */
+ public bypassChannelBlacklist: boolean;
+
+ /**
+ * Info about the arguments for the help command.
+ */
+ public argsInfo?: ArgsInfo[];
+
+ /**
+ * Extra information about the command, displayed in the help command.
+ */
+ public note?: string;
+
+ public constructor(id: string, options: CustomCommandOptions) {
+ const options_ = options as BaseBotCommandOptions;
+
+ if (options_.args && typeof options_.args !== 'function') {
+ options_.args.forEach((_, index: number) => {
+ if ('customType' in (options_.args?.[index] ?? {})) {
+ if (!options_.args![index]['type']) options_.args![index]['type'] = options_.args![index]['customType']! as any;
+ delete options_.args![index]['customType'];
+ }
+ });
+ }
+
+ const newOptions: Partial = {};
+ for (const _key in options_) {
+ const key = _key as keyof typeof options_; // you got to love typescript
+ if (key === 'args' && 'args' in options_ && typeof options_.args === 'object') {
+ const newTextArgs: (ArgumentOptions & ExtraArgumentOptions)[] = [];
+ const newSlashArgs: SlashOption[] = [];
+ for (const arg of options_.args) {
+ if (arg.only !== 'slash' && !options_.slashOnly) {
+ const newArg: ArgumentOptions & ExtraArgumentOptions = {};
+ if ('default' in arg) newArg.default = arg.default;
+ if ('description' in arg) newArg.description = arg.description;
+ if ('flag' in arg) newArg.flag = arg.flag;
+ if ('id' in arg) newArg.id = arg.id;
+ if ('index' in arg) newArg.index = arg.index;
+ if ('limit' in arg) newArg.limit = arg.limit;
+ if ('match' in arg) newArg.match = arg.match;
+ if ('modifyOtherwise' in arg) newArg.modifyOtherwise = arg.modifyOtherwise;
+ if ('multipleFlags' in arg) newArg.multipleFlags = arg.multipleFlags;
+ if ('otherwise' in arg) newArg.otherwise = arg.otherwise;
+ if ('prompt' in arg || 'retry' in arg || 'optional' in arg) {
+ newArg.prompt = {};
+ if ('prompt' in arg) newArg.prompt.start = arg.prompt;
+ if ('retry' in arg) newArg.prompt.retry = arg.retry;
+ if ('optional' in arg) newArg.prompt.optional = arg.optional;
+ }
+ if ('type' in arg) newArg.type = arg.type as ArgumentType | ArgumentTypeCaster;
+ if ('unordered' in arg) newArg.unordered = arg.unordered;
+ if ('ownerOnly' in arg) newArg.ownerOnly = arg.ownerOnly;
+ if ('superUserOnly' in arg) newArg.superUserOnly = arg.superUserOnly;
+ newTextArgs.push(newArg);
+ }
+ if (
+ arg.only !== 'text' &&
+ !('slashOptions' in options_) &&
+ (options_.slash || options_.slashOnly) &&
+ arg.slashType !== false
+ ) {
+ const newArg: {
+ [key in SlashOptionKeys]?: any;
+ } = {
+ name: arg.id,
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+ description: arg.prompt || arg.description || 'No description provided.',
+ type: arg.slashType
+ };
+ if ('slashResolve' in arg) newArg.resolve = arg.slashResolve;
+ if ('autocomplete' in arg) newArg.autocomplete = arg.autocomplete;
+ if ('channelTypes' in arg) newArg.channelTypes = arg.channelTypes;
+ if ('choices' in arg) newArg.choices = arg.choices;
+ if ('minValue' in arg) newArg.minValue = arg.minValue;
+ if ('maxValue' in arg) newArg.maxValue = arg.maxValue;
+ newArg.required = 'optional' in arg ? !arg.optional : true;
+ newSlashArgs.push(newArg as SlashOption);
+ }
+ }
+ if (newTextArgs.length > 0) newOptions.args = newTextArgs;
+ if (newSlashArgs.length > 0) newOptions.slashOptions = options_.slashOptions ?? newSlashArgs;
+ } else if (key === 'clientPermissions' || key === 'userPermissions') {
+ newOptions[key] = options_[key] as PermissionResolvable | PermissionResolvable[] | MissingPermissionSupplier;
+ } else {
+ newOptions[key] = options_[key];
+ }
+ }
+
+ if (
+ 'userPermissions' in newOptions &&
+ !('slashDefaultMemberPermissions' in newOptions) &&
+ typeof newOptions.userPermissions !== 'function'
+ ) {
+ const perms = new PermissionsBitField(newOptions.userPermissions);
+
+ newOptions.slashDefaultMemberPermissions = perms.toArray().length === 0 ? null : perms;
+ }
+
+ super(id, newOptions);
+
+ if (options_.args ?? options_.helpArgs) {
+ const argsInfo: ArgsInfo[] = [];
+ const combined = (options_.args ?? options_.helpArgs)!.map((arg) => {
+ const norm = options_.args
+ ? options_.args.find((_arg) => _arg.id === ('id' in arg ? arg.id : arg.name)) ?? ({} as BotArgumentOptions)
+ : ({} as BotArgumentOptions);
+ const help = options_.helpArgs
+ ? options_.helpArgs.find((_arg) => _arg.name === ('id' in arg ? arg.id : arg.name)) ?? ({} as ArgsInfo)
+ : ({} as ArgsInfo);
+ return { ...norm, ...help };
+ });
+
+ for (const arg of combined) {
+ const name = _.camelCase('id' in arg ? arg.id : arg.name),
+ description = arg.description || '*No description provided.*',
+ optional = arg.optional ?? false,
+ autocomplete = arg.autocomplete ?? false,
+ only = arg.only ?? 'slash & text',
+ match = arg.match ?? 'phrase',
+ type = match === 'flag' ? 'flag' : arg.readableType ?? arg.type ?? 'string',
+ flag = arg.flag ? (Array.isArray(arg.flag) ? arg.flag : [arg.flag]) : [],
+ ownerOnly = arg.ownerOnly ?? false,
+ superUserOnly = arg.superUserOnly ?? false;
+
+ argsInfo.push({ name, description, optional, autocomplete, only, match, type, flag, ownerOnly, superUserOnly });
+ }
+
+ this.argsInfo = argsInfo;
+ }
+
+ this.description = options_.description;
+ this.usage = options_.usage;
+ this.examples = options_.examples;
+ this.options = options_;
+ this.parsedOptions = newOptions;
+ this.hidden = !!options_.hidden;
+ this.restrictedChannels = options_.restrictedChannels;
+ this.restrictedGuilds = options_.restrictedGuilds;
+ this.pseudo = !!options_.pseudo;
+ this.bypassChannelBlacklist = !!options_.bypassChannelBlacklist;
+ this.note = options_.note;
+ }
+
+ /**
+ * Executes the command.
+ * @param message - Message that triggered the command.
+ * @param args - Evaluated arguments.
+ */
+ public abstract override exec(message: CommandMessage, args: any): any;
+ /**
+ * Executes the command.
+ * @param message - Message that triggered the command.
+ * @param args - Evaluated arguments.
+ */
+ public abstract override exec(message: CommandMessage | SlashMessage, args: any): any;
+}
+
+type SlashOptionKeys =
+ | keyof AkairoApplicationCommandSubGroupData
+ | keyof AkairoApplicationCommandNonOptionsData
+ | keyof AkairoApplicationCommandChannelOptionData
+ | keyof AkairoApplicationCommandChoicesData
+ | keyof AkairoApplicationCommandAutocompleteOption
+ | keyof AkairoApplicationCommandNumericOptionData
+ | keyof AkairoApplicationCommandSubCommandData;
+
+interface PseudoArguments extends BaseBotArgumentType {
+ boolean: boolean;
+ flag: boolean;
+ 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/lib/extensions/discord-akairo/BotCommandHandler.ts b/lib/extensions/discord-akairo/BotCommandHandler.ts
new file mode 100644
index 0000000..8a4fe60
--- /dev/null
+++ b/lib/extensions/discord-akairo/BotCommandHandler.ts
@@ -0,0 +1,37 @@
+import { type BotCommand, type CommandMessage, type SlashMessage } from '#lib';
+import { CommandHandler, type Category, type CommandHandlerEvents, type CommandHandlerOptions } from 'discord-akairo';
+import { type Collection, type Message, type PermissionsString } from 'discord.js';
+
+export type CustomCommandHandlerOptions = CommandHandlerOptions;
+
+export interface BotCommandHandlerEvents extends CommandHandlerEvents {
+ commandBlocked: [message: CommandMessage, command: BotCommand, reason: string];
+ commandBreakout: [message: CommandMessage, command: BotCommand, /* no util */ breakMessage: Message];
+ commandCancelled: [message: CommandMessage, command: BotCommand, /* no util */ retryMessage?: Message];
+ commandFinished: [message: CommandMessage, command: BotCommand, args: any, returnValue: any];
+ commandInvalid: [message: CommandMessage, command: BotCommand];
+ commandLocked: [message: CommandMessage, command: BotCommand];
+ commandStarted: [message: CommandMessage, command: BotCommand, args: any];
+ cooldown: [message: CommandMessage | SlashMessage, command: BotCommand, remaining: number];
+ error: [error: Error, message: /* no util */ Message, command?: BotCommand];
+ inPrompt: [message: /* no util */ Message];
+ load: [command: BotCommand, isReload: boolean];
+ messageBlocked: [message: /* no util */ Message | CommandMessage | SlashMessage, reason: string];
+ messageInvalid: [message: CommandMessage];
+ missingPermissions: [message: CommandMessage, command: BotCommand, type: 'client' | 'user', missing: PermissionsString[]];
+ remove: [command: BotCommand];
+ slashBlocked: [message: SlashMessage, command: BotCommand, reason: string];
+ slashError: [error: Error, message: SlashMessage, command: BotCommand];
+ slashFinished: [message: SlashMessage, command: BotCommand, args