From 84d3ca257514952b8ea5a32e220f57aaca50553f Mon Sep 17 00:00:00 2001 From: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com> Date: Thu, 25 Aug 2022 20:14:57 +0000 Subject: [PATCH] fix: remove old files --- template/bigbot/old/.env.example | 33 -- template/bigbot/old/.vscode/settings.json | 14 - template/bigbot/old/README.md | 78 ----- template/bigbot/old/configs.ts | 94 ----- template/bigbot/old/deno.json | 8 - template/bigbot/old/deps.ts | 8 - template/bigbot/old/importMap.json | 8 - template/bigbot/old/makefile | 8 - template/bigbot/old/package.json | 0 template/bigbot/old/src/bot/botClient.ts | 10 - .../old/src/bot/database/commandVersion.ts | 44 --- template/bigbot/old/src/bot/database/kwik.ts | 42 --- template/bigbot/old/src/bot/database/mod.ts | 0 template/bigbot/old/src/bot/events.ts | 0 .../bot/events/handlers/interactionCreate.ts | 32 -- .../interactions/executeSlashCommand.ts | 126 ------- .../old/src/bot/events/interactions/mod.ts | 9 - .../interactions/slash/createCommand.ts | 7 - .../events/interactions/slash/dev/update.ts | 53 --- .../events/interactions/slash/general/ping.ts | 24 -- .../interactions/slash/setGuildCommands.ts | 41 --- template/bigbot/old/src/bot/events/mod.ts | 9 - .../bigbot/old/src/bot/languages/english.ts | 23 -- .../bigbot/old/src/bot/languages/languages.ts | 13 - template/bigbot/old/src/bot/languages/mod.ts | 2 - .../bigbot/old/src/bot/languages/translate.ts | 92 ----- template/bigbot/old/src/bot/mod.ts | 106 ------ template/bigbot/old/src/bot/types/command.ts | 328 ------------------ .../bigbot/old/src/constants/milliseconds.ts | 9 - template/bigbot/old/src/constants/regexes.ts | 1 - template/bigbot/old/src/gateway/mod.ts | 169 --------- template/bigbot/old/src/gateway/worker.ts | 300 ---------------- template/bigbot/old/src/rest/mod.ts | 85 ----- template/bigbot/old/src/utils/Components.ts | 72 ---- template/bigbot/old/src/utils/Embeds.ts | 187 ---------- template/bigbot/old/src/utils/helpers.ts | 148 -------- template/bigbot/old/src/utils/logger.ts | 101 ------ template/bigbot/old/src/utils/options.ts | 288 --------------- template/bigbot/old/src/utils/permLevels.ts | 56 --- template/bigbot/old/src/utils/replies.ts | 36 -- template/bigbot/old/src/utils/slashWebhook.ts | 39 --- template/bigbot/old/src/utils/updateSlash.ts | 165 --------- 42 files changed, 2868 deletions(-) delete mode 100644 template/bigbot/old/.env.example delete mode 100644 template/bigbot/old/.vscode/settings.json delete mode 100644 template/bigbot/old/README.md delete mode 100644 template/bigbot/old/configs.ts delete mode 100644 template/bigbot/old/deno.json delete mode 100644 template/bigbot/old/deps.ts delete mode 100644 template/bigbot/old/importMap.json delete mode 100644 template/bigbot/old/makefile delete mode 100644 template/bigbot/old/package.json delete mode 100644 template/bigbot/old/src/bot/botClient.ts delete mode 100644 template/bigbot/old/src/bot/database/commandVersion.ts delete mode 100644 template/bigbot/old/src/bot/database/kwik.ts delete mode 100644 template/bigbot/old/src/bot/database/mod.ts delete mode 100644 template/bigbot/old/src/bot/events.ts delete mode 100644 template/bigbot/old/src/bot/events/handlers/interactionCreate.ts delete mode 100644 template/bigbot/old/src/bot/events/interactions/executeSlashCommand.ts delete mode 100644 template/bigbot/old/src/bot/events/interactions/mod.ts delete mode 100644 template/bigbot/old/src/bot/events/interactions/slash/createCommand.ts delete mode 100644 template/bigbot/old/src/bot/events/interactions/slash/dev/update.ts delete mode 100644 template/bigbot/old/src/bot/events/interactions/slash/general/ping.ts delete mode 100644 template/bigbot/old/src/bot/events/interactions/slash/setGuildCommands.ts delete mode 100644 template/bigbot/old/src/bot/events/mod.ts delete mode 100644 template/bigbot/old/src/bot/languages/english.ts delete mode 100644 template/bigbot/old/src/bot/languages/languages.ts delete mode 100644 template/bigbot/old/src/bot/languages/mod.ts delete mode 100644 template/bigbot/old/src/bot/languages/translate.ts delete mode 100644 template/bigbot/old/src/bot/mod.ts delete mode 100644 template/bigbot/old/src/bot/types/command.ts delete mode 100644 template/bigbot/old/src/constants/milliseconds.ts delete mode 100644 template/bigbot/old/src/constants/regexes.ts delete mode 100644 template/bigbot/old/src/gateway/mod.ts delete mode 100644 template/bigbot/old/src/gateway/worker.ts delete mode 100644 template/bigbot/old/src/rest/mod.ts delete mode 100644 template/bigbot/old/src/utils/Components.ts delete mode 100644 template/bigbot/old/src/utils/Embeds.ts delete mode 100644 template/bigbot/old/src/utils/helpers.ts delete mode 100644 template/bigbot/old/src/utils/logger.ts delete mode 100644 template/bigbot/old/src/utils/options.ts delete mode 100644 template/bigbot/old/src/utils/permLevels.ts delete mode 100644 template/bigbot/old/src/utils/replies.ts delete mode 100644 template/bigbot/old/src/utils/slashWebhook.ts delete mode 100644 template/bigbot/old/src/utils/updateSlash.ts diff --git a/template/bigbot/old/.env.example b/template/bigbot/old/.env.example deleted file mode 100644 index bc66631c6..000000000 --- a/template/bigbot/old/.env.example +++ /dev/null @@ -1,33 +0,0 @@ -# Get this from https://discord.com/developers/applications/${applicationId}/bot -DISCORD_TOKEN= - -# Sharding setup. This is largely dependant on your system specs. -# For **development purposes** the defaults are fine. -MAX_SHARDS=1 -FIRST_SHARD_ID=0 -LAST_SHARD_ID=0 -SHARDS_PER_CLUSTER=10 -MAX_CLUSTERS=10 - -# For the event handler process, change the key! -# (url is fine unless hosted on a different machine) -EVENT_HANDLER_PORT=1235 -EVENT_HANDLER_SECRET_KEY=secreteventhandlerkey -EVENT_HANDLER_URL=localhost - -# For the gateway process, change the key! -REST_PORT=1236 -REST_AUTHORIZATION_KEY=secretrestkey - -# For the gateway process, change the key! -# (url is fine unless hosted on a different machine) -GATEWAY_PORT=1237 -GATEWAY_SECRET_KEY=secretgatewaykey -GATEWAY_PROXY_URL=localhost - -# Change to false for production use -DEVELOPMENT=true - -# Optional, but very helpful for development. -MISSING_TRANSLATION_WEBHOOK= -DEV_GUILD_ID= diff --git a/template/bigbot/old/.vscode/settings.json b/template/bigbot/old/.vscode/settings.json deleted file mode 100644 index b7b42d1be..000000000 --- a/template/bigbot/old/.vscode/settings.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "deno.enable": true, - "deno.lint": true, - "deno.importMap": "./importMap.json", - "editor.formatOnSave": true, - "editor.codeActionsOnSave": { - "source.organizeImports": true, - "source.fixAll": true - }, - "editor.defaultFormatter": "denoland.vscode-deno", - "deno.suggest.imports.hosts": { - "https://deno.land": true - } -} diff --git a/template/bigbot/old/README.md b/template/bigbot/old/README.md deleted file mode 100644 index 9eb27c9ec..000000000 --- a/template/bigbot/old/README.md +++ /dev/null @@ -1,78 +0,0 @@ -# Discordeno Big Bot Template - -Support: - -This template is designed for bots that aim or are already in millions of Discord servers. - -## Setup - -- Clone this repository can move this directory into your desired project location. -- Move all files from the `bigbot` folder to the root of the project. - - You may encounter an issue with .vscode but force move the files to the root of the project. We have setup special - import maps in this template that should override the general .vscode folder already in the root folder. -- Rename the `.env.example` file to `.env` -- Fill out the `.env` file -- Go to `configs.ts` file and remove all the intents you don't want in your bot. - -## Usage - -> Note: Please install at least `deno@^1.22` on your system. (This is due to the requirement of the `Deno` namespace in -> workers for the `gateway` process.) - -- Always run the `rest` process first with `deno task rest`. -- Start the `bot` process next with `deno task bot`. (If you are developing a bot, use `deno task watch-bot` instead to - auto reload any changes. This won't restart any other processes, just your bot.) -- Lastly, start the `gateway` process with `deno task gateway`. - -> Important: The `gateway` process and `rest` are designed not to be shut off. So once those are on, the only thing you -> should be doing is restarting your `bot` process. This saves API requests - -## Details - -### Translating Application Commands - -The template supports translations for application commands. This is possible using guild commands. If you use global -commands, translations will not work and will default to english. - -If you prefer a different default(not english), please use the Find And Replace to change the `'english'` everywhere -necessary. - -#### Autocomplete & Type Checking - -One cool thing about the translations is that you will get autocomplete and type checking built in for all the keys. -This will ensure you do not miss a key to be translated. It will also make it easier to code by providing the -autocomplete functionality. - -### Updating Application Commands - -The template is designed in a way that you will no longer need to worry about updating or maintaing your commands. - -- Global Commands: For simplicity you can add a line in mod.ts to update them globally. This generally takes 1 call and - isn't a deal breaker. - - `/update global` is also available on your development server, to trigger manually. -- Guild Commands: This is a bit more complicated. By default, our system will update guild commands on demand! Instead - of making a million requests for all your servers, we will update them as needed. - -**Guild Commands Kwik & Command Versioning** - -For Global Commands you can make 1 request to api to update all commands on restart. Its not a big deal. But with Guild -commands essentially you need to make a request per guild. This can get spammy. That would be crazy. To solve this we -created the concept of `commandVersions`. This basically will decide whether or not guild commands should be updated. - -Kwik is a file based database I used in order to make this setup easy and allow any dev using this template to use a -database of their choice for their bot. I do not recommend using Kwik as your database. Please add a full database of -your choice for your bot. You can even replace Kwik should you choose in the database folder. - -Process: - -1. You update your command options/args or create new commands etc... -2. Increment the `CURRENT_SLASH_COMMAND_VERSION` in `src/database/commandVersion.ts` - -- I recommend moving this into your database so you can build a dev command or eval and update this on the fly as you - wish. - -3. Now whenever a guild emits any event, this will make sure to update the guild commands if necessary. If it already - has the latest commands, it will just ignore. If it was never updated or is using an outdated version, it will update - it. - -Aside from the automated system, there is also the option of `/update guild id` to update a guild manually. diff --git a/template/bigbot/old/configs.ts b/template/bigbot/old/configs.ts deleted file mode 100644 index d8765397f..000000000 --- a/template/bigbot/old/configs.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { dotEnvConfig, GatewayIntents } from "./deps.ts"; - -// Get the .env file that the user should have created, and load the configs. -const env = dotEnvConfig({ export: true }); - -// TODO: REMOVE THESE! THEY ARE BAD FOR YOU! DUH! Seriously, only keep the ones your bot needs! -export const GATEWAY_INTENTS: (keyof typeof GatewayIntents)[] = [ - "DirectMessageReactions", - "DirectMessageTyping", - "DirectMessages", - "GuildBans", - "GuildEmojis", - "GuildIntegrations", - "GuildInvites", - "GuildMembers", - "GuildMessageReactions", - "GuildMessageTyping", - "GuildMessages", - "GuildPresences", - "GuildVoiceStates", - "GuildWebhooks", - "Guilds", -]; - -if (!env.DISCORD_TOKEN) { - throw new Error("DUDE! You did not provide a Discord token!"); -} -export const DISCORD_TOKEN = env.DISCORD_TOKEN!; - -// Set as 0 to make it use default values. NOT RECOMMENDED TO DEFAULT FOR BIG BOTS!!!! -export const MAX_SHARDS = env.MAX_SHARDS ? parseInt(env.MAX_SHARDS, 10) : 0; -export const FIRST_SHARD_ID = env.FIRST_SHARD_ID ? parseInt(env.FIRST_SHARD_ID, 10) : 0; -export const LAST_SHARD_ID = env.LAST_SHARD_ID ? parseInt(env.LAST_SHARD_ID, 10) : 0; -// Default to 10 -export const SHARDS_PER_CLUSTER = env.SHARDS_PER_CLUSTER ? parseInt(env.SHARDS_PER_CLUSTER, 10) : 10; -export const MAX_CLUSTERS = parseInt(env.MAX_CLUSTERS!, 10); -if (!MAX_CLUSTERS) { - throw new Error( - "How many clusters can you run on your machine (MAX_CLUSTERS)? Check your .env file!", - ); -} - -export const GATEWAY_PROXY_URL = env - .GATEWAY_PROXY_URL!; -if (!GATEWAY_PROXY_URL) { - throw new Error( - "Hmm, it seems like you don't have somewhere to send gateway events to (GATEWAY_PROXY_URL). Please check your .env file!", - ); -} - -export const EVENT_HANDLER_URL = env - .EVENT_HANDLER_URL!; -if (!EVENT_HANDLER_URL) { - throw new Error( - "Hmm, it seems like you don't have somewhere to send events to (EVENT_HANDLER_URL). Please check your .env file!", - ); -} - -export const GATEWAY_SECRET_KEY = env.GATEWAY_SECRET_KEY!; -if (!GATEWAY_SECRET_KEY) { - throw new Error( - "You need to add a GATEWAY_SECRET_KEY to your .env file!", - ); -} - -export const REST_AUTHORIZATION_KEY = env.REST_AUTHORIZATION_KEY!; -if (!REST_AUTHORIZATION_KEY) { - throw new Error( - "You need to add a REST_AUTHORIZATION_KEY to your .env file!", - ); -} - -export const EVENT_HANDLER_SECRET_KEY = env.EVENT_HANDLER_SECRET_KEY!; -if (!EVENT_HANDLER_SECRET_KEY) { - throw new Error( - "You need to add an EVENT_HANDLER_SECRET_KEY to your .env file!", - ); -} - -export const BOT_ID = BigInt(atob(env.DISCORD_TOKEN.split(".")[0])); -if (!BOT_ID) { - throw new Error( - "Hmm, it seems like you didn't put in a valid DISCORD_TOKEN. Check your .env file!", - ); -} - -export const REST_PORT = env.REST_PORT ? parseInt(env.REST_PORT, 10) : 5000; -export const GATEWAY_PORT = env.GATEWAY_PORT ? parseInt(env.GATEWAY_PORT, 10) : 8080; -export const EVENT_HANDLER_PORT = env.EVENT_HANDLER_PORT ? parseInt(env.EVENT_HANDLER_PORT, 10) : 7050; - -export const DEVELOPMENT = env.DEVELOPMENT ?? true; -export const MISSING_TRANSLATION_WEBHOOK = env.MISSING_TRANSLATION_WEBHOOK || - ""; -export const DEV_GUILD_ID = env.DEV_GUILD_ID ? BigInt(env.DEV_GUILD_ID) : 0n; diff --git a/template/bigbot/old/deno.json b/template/bigbot/old/deno.json deleted file mode 100644 index 98fa9778c..000000000 --- a/template/bigbot/old/deno.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "tasks": { - "rest": "deno run -A --unstable --import-map ./importMap.json ./src/rest/mod.ts", - "bot": "deno run -A --unstable --import-map ./importMap.json ./src/bot/mod.ts", - "watch-bot": "deno run --watch -A --unstable --import-map ./importMap.json ./src/bot/mod.ts", - "gateway": "deno run -A --unstable --import-map ./importMap.json ./src/gateway/mod.ts" - } -} diff --git a/template/bigbot/old/deps.ts b/template/bigbot/old/deps.ts deleted file mode 100644 index 314ae00c8..000000000 --- a/template/bigbot/old/deps.ts +++ /dev/null @@ -1,8 +0,0 @@ -export * from "https://deno.land/x/discordeno@13.0.0-rc31/mod.ts"; -export * from "https://deno.land/x/discordeno@13.0.0-rc31/plugins/mod.ts"; - -// Terminal Colors! -export * from "https://deno.land/std@0.117.0/fmt/colors.ts"; -// Get data from .env files -export { config as dotEnvConfig } from "https://deno.land/x/dotenv@v3.1.0/mod.ts"; -export * from "https://deno.land/x/kwik@v1.2.3/mod.ts"; diff --git a/template/bigbot/old/importMap.json b/template/bigbot/old/importMap.json deleted file mode 100644 index ffddb133b..000000000 --- a/template/bigbot/old/importMap.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "imports": { - "/": "./", - "./": "./", - "@deps": "./deps.ts", - "@configs": "./configs.ts" - } -} diff --git a/template/bigbot/old/makefile b/template/bigbot/old/makefile deleted file mode 100644 index c3e21b895..000000000 --- a/template/bigbot/old/makefile +++ /dev/null @@ -1,8 +0,0 @@ -rest: - deno run -A --unstable --import-map ./importMap.json ./src/rest/mod.ts - -gateway: - deno run -A --unstable --import-map ./importMap.json ./src/gateway/mod.ts - -bot: - deno run -A --unstable --import-map ./importMap.json ./src/bot/mod.ts \ No newline at end of file diff --git a/template/bigbot/old/package.json b/template/bigbot/old/package.json deleted file mode 100644 index e69de29bb..000000000 diff --git a/template/bigbot/old/src/bot/botClient.ts b/template/bigbot/old/src/bot/botClient.ts deleted file mode 100644 index 63ef5d009..000000000 --- a/template/bigbot/old/src/bot/botClient.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Bot, Collection } from "../../deps.ts"; - -/** These are custom properties you want to add to `bot` and have accessible everywhere. */ -export interface BotClient extends Bot { - commandVersions: Collection; -} - -export function setupBotClient(bot: BotClient) { - bot.commandVersions = new Collection(); -} diff --git a/template/bigbot/old/src/bot/database/commandVersion.ts b/template/bigbot/old/src/bot/database/commandVersion.ts deleted file mode 100644 index 2998678fc..000000000 --- a/template/bigbot/old/src/bot/database/commandVersion.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { BotClient } from "../botClient.ts"; -import { commandVersions } from "./kwik.ts"; - -export const CURRENT_SLASH_COMMAND_VERSION = 1; - -/** Whether the guild has the latest slash command version */ -export async function usesLatestCommandVersion( - bot: BotClient, - guildId: bigint, -): Promise { - return (await getCurrentCommandVersion(bot, guildId)) === - CURRENT_SLASH_COMMAND_VERSION; -} - -/** Get the current slash command version for this guild */ -export async function getCurrentCommandVersion( - bot: BotClient, - guildId: bigint, -): Promise { - const current = await commandVersions.get(guildId.toString()); - if (current) return current.version; - - await commandVersions.set( - guildId.toString(), - { version: CURRENT_SLASH_COMMAND_VERSION }, - ); - bot.commandVersions.set(guildId, CURRENT_SLASH_COMMAND_VERSION); - - return CURRENT_SLASH_COMMAND_VERSION; -} - -export async function updateCommandVersion( - bot: BotClient, - guildId: bigint, -): Promise { - // UPDATE THE VERSION SAVED IN THE DB - await commandVersions.set(guildId.toString(), { - version: CURRENT_SLASH_COMMAND_VERSION, - }); - // UPDATE THE CACHED VERSION FOR NEXT CHECK - bot.commandVersions.set(guildId, CURRENT_SLASH_COMMAND_VERSION); - - return CURRENT_SLASH_COMMAND_VERSION; -} diff --git a/template/bigbot/old/src/bot/database/kwik.ts b/template/bigbot/old/src/bot/database/kwik.ts deleted file mode 100644 index d509121d5..000000000 --- a/template/bigbot/old/src/bot/database/kwik.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { logger } from "../../utils/logger.ts"; -import { decode, encode, Kwik, KwikTable } from "../../../deps.ts"; - -const log = logger({ name: "DB" }); - -log.info("Initializing KwikDB Database."); - -interface CommandVersionsSchema { - version: number; -} - -export const kwik = new Kwik(); -export const commandVersions = new KwikTable( - kwik, - "commandVersions", -); - -// Add BigInt Support -kwik.msgpackExtensionCodec.register({ - type: 0, - encode: (object: unknown): Uint8Array | null => { - if (typeof object === "bigint") { - if ( - object <= Number.MAX_SAFE_INTEGER && object >= Number.MIN_SAFE_INTEGER - ) { - return encode(parseInt(object.toString(), 10), {}); - } else { - return encode(object.toString(), {}); - } - } else { - return null; - } - }, - decode: (data: Uint8Array) => { - return BigInt(decode(data, {}) as string); - }, -}); - -// Initialize the Database -await kwik.init(); - -log.info("KwikDB Initialized!"); diff --git a/template/bigbot/old/src/bot/database/mod.ts b/template/bigbot/old/src/bot/database/mod.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/template/bigbot/old/src/bot/events.ts b/template/bigbot/old/src/bot/events.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/template/bigbot/old/src/bot/events/handlers/interactionCreate.ts b/template/bigbot/old/src/bot/events/handlers/interactionCreate.ts deleted file mode 100644 index e57f52f1d..000000000 --- a/template/bigbot/old/src/bot/events/handlers/interactionCreate.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { InteractionTypes, MessageComponentTypes } from "../../../../deps.ts"; -import { bot } from "../../mod.ts"; -import { executeSlashCommand } from "../interactions/executeSlashCommand.ts"; -import { logger, LogLevels } from "../../../utils/logger.ts"; - -const log = logger({ name: "InteractionHandler" }); - -export function setInteractionCreateEvent() { - log.info("Adding `bot.events.interactionCreate` handler."); - bot.events.interactionCreate = async (_, interaction) => { - log.debug("New event fired:\n", interaction); - // SLASH COMMAND - if (interaction.type === InteractionTypes.ApplicationCommand) { - log.debug("Slash Command Fired!"); - return await executeSlashCommand(bot, interaction); - } - - if (interaction.type === InteractionTypes.MessageComponent) { - if (!interaction.data) return; - - // THE INTERACTION CAME FROM A BUTTON - if ( - interaction.data.componentType === - MessageComponentTypes.Button - ) { - log.debug("Button Event!"); - // processButtonCollectors(bot, interaction) - } - } - }; - log.debug("All handlers:\n", bot.events); -} diff --git a/template/bigbot/old/src/bot/events/interactions/executeSlashCommand.ts b/template/bigbot/old/src/bot/events/interactions/executeSlashCommand.ts deleted file mode 100644 index b30f5211d..000000000 --- a/template/bigbot/old/src/bot/events/interactions/executeSlashCommand.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { - bgBlack, - bgGreen, - bgMagenta, - bgYellow, - black, - green, - Interaction, - InteractionResponseTypes, - red, - sendPrivateInteractionResponse, - white, -} from "../../../../deps.ts"; -import { optionParser, translateOptionNames } from "../../../utils/options.ts"; -import { privateReplyToInteraction, replyToInteraction } from "../../../utils/replies.ts"; -import slashLogWebhook from "../../../utils/slashWebhook.ts"; -import { BotClient } from "../../botClient.ts"; -import { loadLanguage, serverLanguages, translate } from "../../languages/translate.ts"; -import { Command, ConvertArgumentDefinitionsToArgs } from "../../types/command.ts"; -import commands from "./mod.ts"; -import { logger, LogLevels } from "../../../utils/logger.ts"; - -const log = logger({ name: "CommandHandler" }); - -function logCommand( - info: Interaction, - type: "Failure" | "Success" | "Trigger" | "Slowmode" | "Missing" | "Inhibit", - commandName: string, -) { - const command = `[COMMAND: ${bgYellow(black(commandName || "Unknown"))} - ${ - bgBlack( - ["Failure", "Slowmode", "Missing"].includes(type) ? red(type) : type === "Success" ? green(type) : white(type), - ) - }]`; - - const user = bgGreen( - black( - `${info.user.username}#${info.user.discriminator}(${info.id})`, - ), - ); - const guild = bgMagenta( - black(`${info.guildId ? `Guild ID: (${info.guildId})` : "DM"}`), - ); - - log.info(`${command} by ${user} in ${guild} with MessageID: ${info.id}`); -} - -export async function executeSlashCommand( - bot: BotClient, - interaction: Interaction, -) { - log.debug(`New interaction:\n`, interaction); - - const data = interaction.data; - const name = data?.name as keyof typeof commands; - - // deno-lint-ignore no-explicit-any - const command: Command | undefined = commands[name]; - - // Command could not be found - if (!command?.execute) { - return await sendPrivateInteractionResponse( - bot, - interaction.id, - interaction.token, - { - type: InteractionResponseTypes.ChannelMessageWithSource, - data: { - content: translate( - bot, - interaction.guildId!, - "EXECUTE_COMMAND_NOT_FOUND", - ), - }, - }, - ) - .catch(log.error); - } - - // HAVE TO CONVERT OUTSIDE OF TRY SO IT CAN BE USED IN CATCH TOO - try { - logCommand(interaction, "Trigger", name); - - // Load the language for this guild - if (interaction.guildId && !serverLanguages.has(interaction.guildId)) { - // TODO: Check if this is deferrable - await replyToInteraction(bot, interaction, { - type: InteractionResponseTypes.DeferredChannelMessageWithSource, - }); - loadLanguage(interaction.guildId); - } // Load the language for this guild - else if (command.acknowledge) { - // Acknowledge the command - await replyToInteraction(bot, interaction, { - type: InteractionResponseTypes.DeferredChannelMessageWithSource, - }); - } - - // FIRST GET THE TRANSLATIONS FOR ALL OPTIONS - const translatedOptionNames = interaction.guildId && command.options - ? translateOptionNames(bot, interaction.guildId, command.options) - : {}; - - // PARSE THE OPTIONS TO A NICE OBJECT AND TRANSLATE THE KEYS TO ENGLISH - const parsedArguments = optionParser( - interaction.data?.options, - interaction.data?.resolved, - translatedOptionNames, - ); - - await command.execute( - bot, - interaction, - // deno-lint-ignore no-explicit-any - parsedArguments as ConvertArgumentDefinitionsToArgs, - ); - logCommand(interaction, "Success", name); - } catch (error) { - log.error(error); - logCommand(interaction, "Failure", name); - await slashLogWebhook(bot, interaction, name).catch(log.error); - return await privateReplyToInteraction(bot, interaction, { - content: translate(bot, interaction.id, "EXECUTE_COMMAND_ERROR"), - }).catch(log.error); - } -} diff --git a/template/bigbot/old/src/bot/events/interactions/mod.ts b/template/bigbot/old/src/bot/events/interactions/mod.ts deleted file mode 100644 index df2d7c6e0..000000000 --- a/template/bigbot/old/src/bot/events/interactions/mod.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Command } from "../../types/command.ts"; -import ping from "./slash/general/ping.ts"; - -// deno-lint-ignore no-explicit-any -export const commands: Record> = { - ping, -}; - -export default commands; diff --git a/template/bigbot/old/src/bot/events/interactions/slash/createCommand.ts b/template/bigbot/old/src/bot/events/interactions/slash/createCommand.ts deleted file mode 100644 index 035c48530..000000000 --- a/template/bigbot/old/src/bot/events/interactions/slash/createCommand.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { ArgumentDefinition, Command } from "../../../types/command.ts"; - -export function createCommand( - command: Command, -) { - return command; -} diff --git a/template/bigbot/old/src/bot/events/interactions/slash/dev/update.ts b/template/bigbot/old/src/bot/events/interactions/slash/dev/update.ts deleted file mode 100644 index cd62f9dfc..000000000 --- a/template/bigbot/old/src/bot/events/interactions/slash/dev/update.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { ApplicationCommandOptionTypes } from "../../../../../../deps.ts"; -import { replyToInteraction } from "../../../../../utils/replies.ts"; -import { updateGlobalCommands, updateGuildCommands } from "../../../../../utils/updateSlash.ts"; -import { createCommand } from "../createCommand.ts"; - -const command = createCommand({ - name: "UPDATE_NAME", - description: "UPDATE_DESCRIPTION", - dev: true, - acknowledge: true, - options: [ - { - name: "UPDATE_GLOBAL_NAME", - description: "UPDATE_GLOBAL_DESCRIPTION", - type: ApplicationCommandOptionTypes.SubCommand, - }, - { - name: "UPDATE_GUILD_NAME", - description: "UPDATE_GUILD_DESCRIPTION", - type: ApplicationCommandOptionTypes.SubCommand, - options: [ - { - name: "UPDATE_GUILD_ID_NAME", - description: "UPDATE_GUILD_ID_DESCRIPTION", - type: ApplicationCommandOptionTypes.String, - required: true, - }, - ], - }, - ] as const, - execute: async function (bot, interaction, args) { - if (args.global) { - await updateGlobalCommands(bot); - return await replyToInteraction( - bot, - interaction, - "Updated Global Commands!", - ); - } - - if (args.guild) { - // GUILD COMMANDS - await updateGuildCommands(bot, bot.transformers.snowflake(args.guild.id)); - return await replyToInteraction( - bot, - interaction, - `Updated Guild Commands for Guild ID: ${args.guild.id}!`, - ); - } - }, -}); - -export default command; diff --git a/template/bigbot/old/src/bot/events/interactions/slash/general/ping.ts b/template/bigbot/old/src/bot/events/interactions/slash/general/ping.ts deleted file mode 100644 index 3c51b027e..000000000 --- a/template/bigbot/old/src/bot/events/interactions/slash/general/ping.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { snowflakeToTimestamp } from "../../../../../utils/helpers.ts"; -import { replyToInteraction } from "../../../../../utils/replies.ts"; -import { translate } from "../../../../languages/translate.ts"; -import { createCommand } from "../createCommand.ts"; - -const command = createCommand({ - name: "PING_NAME", - dev: true, - description: "PING_DESCRIPTION", - execute: async function (bot, interaction) { - return await replyToInteraction( - bot, - interaction, - translate( - bot, - interaction.guildId!, - "PING_RESPONSE_WITH_TIME", - Date.now() - snowflakeToTimestamp(interaction.id), - ), - ); - }, -}); - -export default command; diff --git a/template/bigbot/old/src/bot/events/interactions/slash/setGuildCommands.ts b/template/bigbot/old/src/bot/events/interactions/slash/setGuildCommands.ts deleted file mode 100644 index 49b6f8f92..000000000 --- a/template/bigbot/old/src/bot/events/interactions/slash/setGuildCommands.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { DEV_GUILD_ID } from "../../../../../configs.ts"; -import { DiscordGatewayPayload, DiscordUnavailableGuild } from "../../../../../deps.ts"; -import logger from "../../../../utils/logger.ts"; -import { updateGuildCommands } from "../../../../utils/updateSlash.ts"; -import { BotClient } from "../../../botClient.ts"; -import { usesLatestCommandVersion } from "../../../database/commandVersion.ts"; -import { commandVersions } from "../../../database/kwik.ts"; - -export async function setGuildCommands( - bot: BotClient, - data: DiscordGatewayPayload, -) { - if (!data.t) return; - - if (data.t === "GUILD_DELETE") { - const id = (data.d as DiscordUnavailableGuild).id; - await commandVersions.delete(id); - bot.commandVersions.delete(bot.transformers.snowflake(id)); - return; - } - - const id = bot.transformers.snowflake( - (["GUILD_CREATE", "GUILD_UPDATE"].includes(data.t) - ? // deno-lint-ignore no-explicit-any - (data.d as any).id - : // deno-lint-ignore no-explicit-any - (data.d as any).guild_id ?? "") ?? "", - ); - - // IF NO ID FOUND CANCEL. IF ALREADY ON LATEST VERSION CANCEL. - if (!id || await usesLatestCommandVersion(bot, id)) return; - - // DEV GUILD SHOULD BE IGNORED - if (id === DEV_GUILD_ID) return; - - // NEW GUILD AVAILABLE OR NOT USING LATEST VERSION - logger.info( - `[Slash Setup] Installing slash commands on Guild ${id} for Event ${data.t}`, - ); - await updateGuildCommands(bot, id); -} diff --git a/template/bigbot/old/src/bot/events/mod.ts b/template/bigbot/old/src/bot/events/mod.ts deleted file mode 100644 index 52449a8e1..000000000 --- a/template/bigbot/old/src/bot/events/mod.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { setInteractionCreateEvent } from "./handlers/interactionCreate.ts"; -import { logger } from "../../utils/logger.ts"; - -const log = logger({ name: "EventHandlers" }); - -export function setupEventHandlers() { - log.debug("Adding Event Handlers!"); - setInteractionCreateEvent(); -} diff --git a/template/bigbot/old/src/bot/languages/english.ts b/template/bigbot/old/src/bot/languages/english.ts deleted file mode 100644 index 022296614..000000000 --- a/template/bigbot/old/src/bot/languages/english.ts +++ /dev/null @@ -1,23 +0,0 @@ -const english = { - // Execute Command - EXECUTE_COMMAND_NOT_FOUND: "Something went wrong. I was not able to find this command.", - EXECUTE_COMMAND_ERROR: "Something went wrong. The command execution has thrown an error.", - - // Ping Command - PING_NAME: "ping", - PING_DESCRIPTION: "🏓 Check whether the bot is online and responsive.", - PING_RESPONSE: "🏓 Pong! I am online and responsive! :clock10:", - PING_RESPONSE_WITH_TIME: (time: number) => `🏓 Pong! ${time / 1000} seconds! I am online and responsive! :clock10:`, - - // Update Command - UPDATE_NAME: "update", - UPDATE_DESCRIPTION: "🎉 Update the commands for the bot.", - UPDATE_GLOBAL_NAME: "global", - UPDATE_GLOBAL_DESCRIPTION: "Update the global commands.", - UPDATE_GUILD_NAME: "guild", - UPDATE_GUILD_DESCRIPTION: "Update guild commands for a guild.", - UPDATE_GUILD_ID_NAME: "id", - UPDATE_GUILD_ID_DESCRIPTION: "The guild id you wish to manually update.", -} as const; - -export default english; diff --git a/template/bigbot/old/src/bot/languages/languages.ts b/template/bigbot/old/src/bot/languages/languages.ts deleted file mode 100644 index 1ccf2b7f1..000000000 --- a/template/bigbot/old/src/bot/languages/languages.ts +++ /dev/null @@ -1,13 +0,0 @@ -import english from "./english.ts"; - -const languages: Record = { - english, -}; - -export default languages; - -export type Language = Record< - string, - // deno-lint-ignore no-explicit-any - string | string[] | ((...args: any[]) => string) ->; diff --git a/template/bigbot/old/src/bot/languages/mod.ts b/template/bigbot/old/src/bot/languages/mod.ts deleted file mode 100644 index 06eb4d24a..000000000 --- a/template/bigbot/old/src/bot/languages/mod.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./languages.ts"; -export * from "./translate.ts"; diff --git a/template/bigbot/old/src/bot/languages/translate.ts b/template/bigbot/old/src/bot/languages/translate.ts deleted file mode 100644 index 37103ce2b..000000000 --- a/template/bigbot/old/src/bot/languages/translate.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { MISSING_TRANSLATION_WEBHOOK } from "../../../configs.ts"; -import { Bot } from "../../../deps.ts"; -import Embeds from "../../utils/Embeds.ts"; -import logger from "../../utils/logger.ts"; -import english from "./english.ts"; -import languages from "./languages.ts"; - -/** This should hold the language names per guild id. */ -export const serverLanguages = new Map(); - -export function translate( - bot: Bot, - guildIdOrLanguage: bigint | keyof typeof languages, - key: K, - ...params: getArgs -): string { - const language = getLanguage(guildIdOrLanguage); - // deno-lint-ignore no-explicit-any - let value: string | ((...any: any[]) => string) | string[] = languages[language][key]; - - // Was not able to be translated - if (!value) { - // Check if this key is available in english - if (language !== "english") { - value = languages.english[key]; - } - - // Still not found in english so default to using the KEY_ITSELF - if (!value) value = key; - - // Send a log webhook so the devs know sth is missing - missingTranslation(bot, language, key); - } - - if (Array.isArray(value)) return value.join("\n"); - - if (typeof value === "function") return value(...(params || [])); - - return value as string; -} - -/** Get the language this guild has set, will always return "english" if it is not in cache */ -export function getLanguage( - guildIdOrLanguage: bigint | keyof typeof languages, -) { - return typeof guildIdOrLanguage === "string" - ? guildIdOrLanguage - : serverLanguages.get(guildIdOrLanguage) ?? "english"; -} - -export function loadLanguage(guildId: bigint) { - // TODO: add this settings - // const settings = await database.findOne('guilds', guildId) - const settings = { language: "undefined" }; - - if (settings?.language && languages[settings.language]) { - serverLanguages.set(guildId, settings.language); - } else serverLanguages.set(guildId, "english"); -} - -const [id, token] = MISSING_TRANSLATION_WEBHOOK.substring( - MISSING_TRANSLATION_WEBHOOK.indexOf("webhooks/") + 9, -).split( - "/", -); -/** Send a webhook for a missing translation key */ -export async function missingTranslation( - bot: Bot, - language: keyof typeof languages, - key: string, -) { - if (!id || !token) return; - - const embeds = new Embeds(bot) - .setTitle("Missing Translation") - .setColor("RANDOM") - .addField("Language", language, true) - .addField("Key", key, true); - - await bot.helpers - .sendWebhook(bot.transformers.snowflake(id), token, { - embeds, - wait: false, - }) - .catch(logger.error); -} - -// type translationKeys = keyof typeof english | string -export type translationKeys = keyof typeof english; -type getArgs = typeof english[K] extends // deno-lint-ignore no-explicit-any -(...any: any[]) => unknown ? Parameters - : []; diff --git a/template/bigbot/old/src/bot/mod.ts b/template/bigbot/old/src/bot/mod.ts deleted file mode 100644 index e79f1ec93..000000000 --- a/template/bigbot/old/src/bot/mod.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { - BOT_ID, - DEVELOPMENT, - DISCORD_TOKEN, - EVENT_HANDLER_PORT, - EVENT_HANDLER_SECRET_KEY, - GATEWAY_INTENTS, - REST_AUTHORIZATION_KEY, - REST_PORT, -} from "../../configs.ts"; -import { createBot, createRestManager, DiscordGatewayPayload } from "../../deps.ts"; -import logger from "../utils/logger.ts"; -import { updateDevCommands } from "../utils/updateSlash.ts"; -import { BotClient, setupBotClient } from "./botClient.ts"; -import { setGuildCommands } from "./events/interactions/slash/setGuildCommands.ts"; -import { setupEventHandlers } from "./events/mod.ts"; - -export const bot = createBot({ - token: DISCORD_TOKEN, - botId: BOT_ID, - events: {}, - intents: GATEWAY_INTENTS, -}) as BotClient; - -setupEventHandlers(); -// customizeBotInternals(bot); -setupBotClient(bot); - -bot.rest = createRestManager({ - token: DISCORD_TOKEN, - secretKey: REST_AUTHORIZATION_KEY, - customUrl: `http://localhost:${REST_PORT}`, -}); - -if (DEVELOPMENT) { - logger.info(`[DEV MODE] Updating slash commands for dev server.`); - await updateDevCommands(bot); -} else { - // THIS WILL UPDATE ALL YOUR GLOBAL COMMANDS ON STARTUP - // await updateGlobalCommands(bot); -} - -// Start listening on localhost. -const server = Deno.listen({ port: EVENT_HANDLER_PORT }); -logger.info( - `HTTP webserver running. Access it at: http://localhost:${EVENT_HANDLER_PORT}/`, -); - -// Connections to the server will be yielded up as an async iterable. -for await (const conn of server) { - // In order to not be blocking, we need to handle each connection individually - // in its own async function. - handleRequest(conn); -} - -async function handleRequest(conn: Deno.Conn) { - // This "upgrades" a network connection into an HTTP connection. - const httpConn = Deno.serveHttp(conn); - // Each request sent over the HTTP connection will be yielded as an async - // iterator from the HTTP connection. - for await (const requestEvent of httpConn) { - if ( - !EVENT_HANDLER_SECRET_KEY || - EVENT_HANDLER_SECRET_KEY !== - requestEvent.request.headers.get("AUTHORIZATION") - ) { - return requestEvent.respondWith( - new Response(JSON.stringify({ error: "Invalid secret key." }), { - status: 401, - }), - ); - } - - if (requestEvent.request.method !== "POST") { - return requestEvent.respondWith( - new Response(JSON.stringify({ error: "Method not allowed." }), { - status: 405, - }), - ); - } - - const json = (await requestEvent.request.json()) as { - data: DiscordGatewayPayload; - shardId: number; - }; - // EMITS RAW EVENT - bot.events.raw(bot, json.data, json.shardId); - - if (json.data.t && json.data.t !== "RESUMED") { - // When a guild or something isn't in cache this will fetch it before doing anything else - if (!["READY", "GUILD_LOADED_DD"].includes(json.data.t)) { - await bot.events.dispatchRequirements(bot, json.data, json.shardId); - // WE ALSO WANT TO UPDATE GUILD SLASH IF NECESSARY AT THIS POINT - await setGuildCommands(bot, json.data); - } - - bot.handlers[json.data.t]?.(bot, json.data, json.shardId); - } - - requestEvent.respondWith( - new Response(undefined, { - status: 204, - }), - ); - } -} diff --git a/template/bigbot/old/src/bot/types/command.ts b/template/bigbot/old/src/bot/types/command.ts deleted file mode 100644 index 6a15f5d90..000000000 --- a/template/bigbot/old/src/bot/types/command.ts +++ /dev/null @@ -1,328 +0,0 @@ -import { - ApplicationCommandOptionTypes, - ApplicationCommandTypes, - Channel, - Interaction, - Member, - PermissionStrings, - Role, - User, -} from "../../../deps.ts"; -import { PermissionLevelHandlers } from "../../utils/permLevels.ts"; -import { BotClient } from "../botClient.ts"; -import english from "../languages/english.ts"; -import { translationKeys } from "../languages/translate.ts"; - -// deno-lint-ignore no-explicit-any -type UnionToIntersection = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never; - -type Identity = { [P in keyof T]: T[P] }; - -// TODO: make required by default true -// Define each of the types here -type BaseDefinition = { - description: translationKeys; -}; - -// Subcommand -type SubcommandArgumentDefinition = - & BaseDefinition - & { - name: N; - type: ApplicationCommandOptionTypes.SubCommand; - // options: Omit[] - options?: readonly ArgumentDefinition[]; - }; - -// SubcommandGroup -type SubcommandGroupArgumentDefinition< - N extends translationKeys = translationKeys, -> = BaseDefinition & { - name: N; - type: ApplicationCommandOptionTypes.SubCommandGroup; - options: readonly SubcommandArgumentDefinition[]; -}; - -// String -type StringArgumentDefinition = - & BaseDefinition - & { - name: N; - type: ApplicationCommandOptionTypes.String; - choices?: readonly { name: string; value: string }[]; - required?: true; - }; -type StringOptionalArgumentDefinition< - N extends translationKeys = translationKeys, -> = BaseDefinition & { - name: N; - type: ApplicationCommandOptionTypes.String; - choices?: readonly { name: string; value: string }[]; - required?: false; -}; - -// Integer -type IntegerArgumentDefinition = - & BaseDefinition - & { - name: N; - type: ApplicationCommandOptionTypes.Integer; - choices?: readonly { name: string; value: number }[]; - required: true; - }; -type IntegerOptionalArgumentDefinition< - N extends translationKeys = translationKeys, -> = BaseDefinition & { - name: N; - type: ApplicationCommandOptionTypes.Integer; - choices?: readonly { name: string; value: number }[]; - required?: false; -}; - -// BOOLEAN -type BooleanArgumentDefinition = - & BaseDefinition - & { - name: N; - type: ApplicationCommandOptionTypes.Boolean; - required: true; - }; -type BooleanOptionalArgumentDefinition< - N extends translationKeys = translationKeys, -> = BaseDefinition & { - name: N; - type: ApplicationCommandOptionTypes.Boolean; - required?: false; -}; - -// USER -type UserArgumentDefinition = - & BaseDefinition - & { - name: N; - type: ApplicationCommandOptionTypes.User; - required: true; - }; -type UserOptionalArgumentDefinition< - N extends translationKeys = translationKeys, -> = BaseDefinition & { - name: N; - type: ApplicationCommandOptionTypes.User; - required?: false; -}; - -// CHANNEL -type ChannelArgumentDefinition = - & BaseDefinition - & { - name: N; - type: ApplicationCommandOptionTypes.Channel; - required: true; - }; -type ChannelOptionalArgumentDefinition< - N extends translationKeys = translationKeys, -> = BaseDefinition & { - name: N; - type: ApplicationCommandOptionTypes.Channel; - required?: false; -}; - -// ROLE -type RoleArgumentDefinition = - & BaseDefinition - & { - name: N; - type: ApplicationCommandOptionTypes.Role; - required: true; - }; -type RoleOptionalArgumentDefinition< - N extends translationKeys = translationKeys, -> = BaseDefinition & { - name: N; - type: ApplicationCommandOptionTypes.Role; - required?: false; -}; - -// MENTIONABLE -type MentionableArgumentDefinition< - N extends translationKeys = translationKeys, -> = BaseDefinition & { - name: N; - type: ApplicationCommandOptionTypes.Mentionable; - required: true; -}; -type MentionableOptionalArgumentDefinition< - N extends translationKeys = translationKeys, -> = BaseDefinition & { - name: N; - type: ApplicationCommandOptionTypes.Mentionable; - required?: false; -}; - -// Add each of known ArgumentDefinitions to this union. -export type ArgumentDefinition = - | StringArgumentDefinition - | StringOptionalArgumentDefinition - | IntegerArgumentDefinition - | IntegerOptionalArgumentDefinition - | BooleanArgumentDefinition - | BooleanOptionalArgumentDefinition - | UserArgumentDefinition - | UserOptionalArgumentDefinition - | ChannelArgumentDefinition - | ChannelOptionalArgumentDefinition - | RoleArgumentDefinition - | RoleOptionalArgumentDefinition - | MentionableArgumentDefinition - | MentionableOptionalArgumentDefinition - | SubcommandArgumentDefinition - | SubcommandGroupArgumentDefinition; - -type getName = typeof english[K] extends string ? typeof english[K] - : never; - -// OPTIONALS MUST BE FIRST!!! -export type ConvertArgumentDefinitionsToArgs< - T extends readonly ArgumentDefinition[], -> = Identity< - UnionToIntersection< - { - [P in keyof T]: T[P] extends StringOptionalArgumentDefinition // STRING - ? { - [_ in getName]?: T[P]["choices"] extends readonly { name: string; value: string }[] ? // @ts-ignore ts being dumb - T[P]["choices"][number]["value"] - : string; - } - : T[P] extends StringArgumentDefinition ? { - [_ in getName]: T[P]["choices"] extends readonly { name: string; value: string }[] ? // @ts-ignore ts being dumb - T[P]["choices"][number]["value"] - : string; - } - : // INTEGER - T[P] extends IntegerOptionalArgumentDefinition ? { - [_ in getName]?: T[P]["choices"] extends readonly { name: string; value: number }[] ? // @ts-ignore ts being dumb - T[P]["choices"][number]["value"] - : number; - } - : T[P] extends IntegerArgumentDefinition ? { - [_ in getName]: T[P]["choices"] extends readonly { name: string; value: number }[] ? // @ts-ignore ts being dumb - T[P]["choices"][number]["value"] - : number; - } - : // BOOLEAN - T[P] extends BooleanOptionalArgumentDefinition ? { [_ in getName]?: boolean } - : T[P] extends BooleanArgumentDefinition ? { [_ in getName]: boolean } - : // USER - T[P] extends UserOptionalArgumentDefinition ? { - [_ in getName]?: { - user: User; - member: Member; - }; - } - : T[P] extends UserArgumentDefinition ? { - [_ in getName]: { - user: User; - member: Member; - }; - } - : // CHANNEL - T[P] extends ChannelOptionalArgumentDefinition ? { [_ in getName]?: Channel } - : T[P] extends ChannelArgumentDefinition ? { [_ in getName]: Channel } - : // ROLE - T[P] extends RoleOptionalArgumentDefinition ? { [_ in getName]?: Role } - : T[P] extends RoleArgumentDefinition ? { [_ in getName]: Role } - : // MENTIONABLE - T[P] extends MentionableOptionalArgumentDefinition ? { - [_ in getName]?: Role | { - user: User; - member: Member; - }; - } - : T[P] extends MentionableArgumentDefinition ? { - [_ in getName]: Role | { - user: User; - member: Member; - }; - } - : // SUBCOMMAND - T[P] extends SubcommandArgumentDefinition ? { - [_ in getName]?: T[P]["options"] extends readonly ArgumentDefinition[] ? // @ts-ignore ignore this for a bit - ConvertArgumentDefinitionsToArgs - : // deno-lint-ignore ban-types - {}; - } - : // SUBCOMMAND GROUP - T[P] extends SubcommandGroupArgumentDefinition ? { - [_ in getName]?: ConvertArgumentDefinitionsToArgs< - T[P]["options"] - >; - } - : never; - }[number] - > ->; - -export interface Command { - /** The name of the command, used for both slash and message commands. */ - name: translationKeys; - /** The type of command. */ - type?: ApplicationCommandTypes; - /** The description of the command*/ - description: translationKeys; - // TODO: consider type being a string like "number" | "user" for better ux - /** The options for the command, used for both slash and message commands. */ - // options?: ApplicationCommandOption[]; - options?: T; - execute: ( - bot: BotClient, - data: Interaction, - args: ConvertArgumentDefinitionsToArgs, - ) => unknown; - subcommands?: Record< - string, - // deno-lint-ignore no-explicit-any - Omit, "subcommands"> & { group?: string } - >; - /** Whether the command should have a cooldown */ - cooldown?: { - /** How long the user needs to wait after the first execution until he can use the command again */ - seconds: number; - /** How often the user is allowed to use the command until he is in cooldown */ - allowedUses?: number; - }; - nsfw?: boolean; - /** By default false */ - global?: boolean; - /** Dm only by default false */ - dmOnly?: boolean; - - advanced?: boolean; - - /** Whether or not this slash command should be enabled right now. Defaults to true. */ - enabled?: boolean; - /** Whether or not this command is still in development and should be setup in the dev server for testing. */ - dev?: boolean; - /** Whether or not this command will take longer than 3s and need to acknowledge to discord. */ - acknowledge?: boolean; - - permissionLevels?: - | (keyof typeof PermissionLevelHandlers)[] - | (( - data: Interaction, - command: Command, - ) => boolean | Promise); - botServerPermissions?: PermissionStrings[]; - botChannelPermissions?: PermissionStrings[]; - userServerPermissions?: PermissionStrings[]; - userChannelPermissions?: PermissionStrings[]; -} - -export enum PermissionLevels { - Member, - Moderator, - Admin, - ServerOwner, - BotSupporter, - BotDev, - BotOwner, -} diff --git a/template/bigbot/old/src/constants/milliseconds.ts b/template/bigbot/old/src/constants/milliseconds.ts deleted file mode 100644 index 8df58a3b7..000000000 --- a/template/bigbot/old/src/constants/milliseconds.ts +++ /dev/null @@ -1,9 +0,0 @@ -export enum Milliseconds { - Year = 1000 * 60 * 60 * 24 * 30 * 12, - Month = 1000 * 60 * 60 * 24 * 30, - Week = 1000 * 60 * 60 * 24 * 7, - Day = 1000 * 60 * 60 * 24, - Hour = 1000 * 60 * 60, - Minute = 1000 * 60, - Second = 1000, -} diff --git a/template/bigbot/old/src/constants/regexes.ts b/template/bigbot/old/src/constants/regexes.ts deleted file mode 100644 index 67d8ffcd6..000000000 --- a/template/bigbot/old/src/constants/regexes.ts +++ /dev/null @@ -1 +0,0 @@ -export const SNOWFLAKE_REGEX = /[0-9]{17,19}/; diff --git a/template/bigbot/old/src/gateway/mod.ts b/template/bigbot/old/src/gateway/mod.ts deleted file mode 100644 index cb29c5855..000000000 --- a/template/bigbot/old/src/gateway/mod.ts +++ /dev/null @@ -1,169 +0,0 @@ -import { Collection, createGatewayManager, createRestManager, endpoints } from "../../deps.ts"; -import { DISCORD_TOKEN, EVENT_HANDLER_SECRET_KEY, REST_AUTHORIZATION_KEY, REST_PORT } from "../../configs.ts"; -import { logger } from "../utils/logger.ts"; - -const log = logger({ name: "Gateway" }); - -// CREATE A SIMPLE MANAGER FOR REST -const rest = createRestManager({ - token: DISCORD_TOKEN, - secretKey: REST_AUTHORIZATION_KEY, - customUrl: `http://localhost:${REST_PORT}`, -}); - -const gateway = createGatewayManager({ - // THE AUTHORIZATION WE WILL USE ON OUR EVENT HANDLER PROCESS - secretKey: EVENT_HANDLER_SECRET_KEY, - token: DISCORD_TOKEN, - intents: ["GuildMessages", "Guilds"], - // THIS WILL BASICALLY BE YOUR HANDLER FOR YOUR EVENTS. - handleDiscordPayload: async (_, data, shardId) => {}, -}); - -const workers = new Collection(); - -async function startGateway() { - // CALL THE REST PROCESS TO GET GATEWAY DATA - const result = await rest.runMethod(rest, "get", endpoints.GATEWAY_BOT()) - .then((res) => ({ - url: res.url, - shards: res.shards, - sessionStartLimit: { - total: res.session_start_limit.total, - remaining: res.session_start_limit.remaining, - resetAfter: res.session_start_limit.reset_after, - maxConcurrency: res.session_start_limit.max_concurrency, - }, - })); - - // LOAD DATA FROM DISCORDS RECOMMENDATIONS OR YOUR OWN CUSTOM ONES HERE - gateway.shardsRecommended = result.shards; - gateway.sessionStartLimitTotal = result.sessionStartLimit.total; - gateway.sessionStartLimitRemaining = result.sessionStartLimit.remaining; - gateway.sessionStartLimitResetAfter = result.sessionStartLimit.resetAfter; - gateway.maxConcurrency = result.sessionStartLimit.maxConcurrency; - gateway.maxShards = result.shards; - gateway.lastShardId = result.shards; - - // PREPARE BUCKETS FOR IDENTIFYING - gateway.prepareBuckets(gateway, 0, result.shards); - - function startWorker( - workerId: number, - bucketId: number, - firstShardId: number, - lastShardId: number, - ) { - const worker = workers.get(workerId); - if (!worker) return; - - // TRIGGER IDENTIFY IN WORKER - worker.postMessage( - JSON.stringify({ - type: "IDENTIFY", - shardId: firstShardId, - shardsRecommended: result.shards, - sessionStartLimitTotal: result.sessionStartLimit.total, - sessionStartLimitRemaining: result.sessionStartLimit.remaining, - sessionStartLimitResetAfter: result.sessionStartLimit.resetAfter, - maxConcurrency: result.sessionStartLimit.maxConcurrency, - maxShards: gateway.maxShards, - lastShardId: lastShardId, - workerId, - }), - ); - } - - gateway.buckets.forEach((bucket, bucketId) => { - for (let i = 0; i < bucket.workers.length; i++) { - const workerId = bucket.workers[i][0]; - const worker = new Worker( - new URL("./worker.ts", import.meta.url).href, - { - name: `w-${workerId}-b${bucketId}`, - type: "module", - }, - ); - workers.set(workerId, worker); - - if (bucket.workers[i + 1]) { - worker.onmessage = function (message) { - const data = JSON.parse(message.data); - if (data.type === "ALL_SHARDS_READY") { - const queue = bucket.workers[i + 1]; - if (queue) { - startWorker( - queue[0], - bucketId, - queue[1], - queue[queue.length - 1], - ); - } - } - - if (data.type === "RESHARDED") { - const nextWorker = workers.get(workerId + 1); - if (nextWorker) { - nextWorker.postMessage( - JSON.stringify({ - type: "RESHARD", - results: data.results, - }), - ); - } - } - }; - } else { - // THIS IS FINAL WORKER - worker.onmessage = function (message) { - const data = JSON.parse(message.data); - if (data.type === "RESHARDED") { - // THERE IS NO NEXT WORKER SO TELL ALL WORKERS TO CLOSE OLD GATEWAYS - workers.forEach((workerx) => { - workerx.postMessage( - JSON.stringify({ - type: "RESHARDED-CLOSEOLD", - }), - ); - }); - } - }; - } - } - - const queue = bucket.workers[0]; - startWorker(queue[0], bucketId, queue[1], queue[queue.length - 1]); - }); -} - -startGateway(); - -setInterval(async () => { - console.log("GW DEBUG", "[Resharding] Checking if resharding is needed."); - - const results = await rest.runMethod(rest, "get", endpoints.GATEWAY_BOT()) - .then((res) => ({ - url: res.url, - shards: res.shards, - sessionStartLimit: { - total: res.session_start_limit.total, - remaining: res.session_start_limit.remaining, - resetAfter: res.session_start_limit.reset_after, - maxConcurrency: res.session_start_limit.max_concurrency, - }, - })); - const percentage = ((results.shards - gateway.maxShards) / gateway.maxShards) * 100; - // Less than necessary% being used so do nothing - if (percentage < gateway.reshardPercentage) return; - - // Don't have enough identify rate limits to reshard - if (results.sessionStartLimit.remaining < results.shards) return; - - workers.first()?.postMessage( - JSON.stringify({ - type: "RESHARD", - results, - }), - ); - // DAILY -}, 1000 * 60 * 60 * 24); diff --git a/template/bigbot/old/src/gateway/worker.ts b/template/bigbot/old/src/gateway/worker.ts deleted file mode 100644 index f7ba4c241..000000000 --- a/template/bigbot/old/src/gateway/worker.ts +++ /dev/null @@ -1,300 +0,0 @@ -import { - DEVELOPMENT, - DISCORD_TOKEN, - EVENT_HANDLER_PORT, - EVENT_HANDLER_SECRET_KEY, - EVENT_HANDLER_URL, -} from "../../configs.ts"; -import { Collection, createGatewayManager, DiscordReady, GatewayManager, GetGatewayBot } from "../../deps.ts"; -import { logger } from "../utils/logger.ts"; - -let gateway: GatewayManager; -// FOR RESHARDED -let gatewayPendingClosing: GatewayManager; -let workerId: number; - -function spawnGateway(shardId: number, options: Partial) { - const log = logger({ - name: `GatewayWorker: ${workerId}`, - }); - - log.info( - `Spawning the worker gateway for shard #${shardId}\n`, - options, - ); - gateway = createGatewayManager({ - // LOAD DATA FROM DISCORDS RECOMMENDATIONS OR YOUR OWN CUSTOM ONES HERE - shardsRecommended: options.shardsRecommended, - sessionStartLimitTotal: options.sessionStartLimitTotal, - sessionStartLimitRemaining: options.sessionStartLimitRemaining, - sessionStartLimitResetAfter: options.sessionStartLimitResetAfter, - maxConcurrency: options.maxConcurrency, - maxShards: options.maxShards, - // SET STARTING SHARD ID - firstShardId: shardId, - // SET LAST SHARD ID - lastShardId: options.lastShardId ?? shardId, - // THE AUTHORIZATION WE WILL USE ON OUR EVENT HANDLER PROCESS - secretKey: EVENT_HANDLER_SECRET_KEY, - token: DISCORD_TOKEN, - intents: ["GuildMessages", "Guilds", "GuildMembers"], - handleDiscordPayload: async function (_, data, shardId) { - // TRIGGER RAW EVENT - if (!data.t) return; - - const id = (data.t && - ["GUILD_CREATE", "GUILD_DELETE", "GUILD_UPDATE"].includes(data.t) - ? (data.d as any)?.id - : (data.d as any)?.guild_id) ?? "000000000000000000"; - - // IF FINAL SHARD BECAME READY TRIGGER NEXT WORKER - if (data.t === "READY") { - log.info( - `Shard online`, - ); - - if (shardId === gateway.lastShardId) { - // @ts-ignore - postMessage( - JSON.stringify({ - type: "ALL_SHARDS_READY", - }), - ); - } - } - - // DONT SEND THESE EVENTS USELESS TO BOT - if (["GUILD_LOADED_DD"].includes(data.t)) return; - - // Debug mode only - log.debug(`New Event:\n`, data); - - await fetch(`http://${EVENT_HANDLER_URL}:${EVENT_HANDLER_PORT}`, { - headers: { - Authorization: gateway.secretKey, - "Content-Type": "application/json", - }, - method: "POST", - body: JSON.stringify({ - shardId, - data, - }), - }) - .then((res) => { - // BELOW IS FOR DENO MEMORY LEAK - return res.text(); - }) - .catch((err) => log.error("Error Sending Event:\n", err)); - }, - }); - - // START THE GATEWAY - gateway.spawnShards(gateway, shardId); - - return gateway; -} - -interface IdentifyPayload { - type: "IDENTIFY"; - shardId: number; - shards: number; - sessionStartLimit: { - total: number; - remaining: number; - resetAfter: number; - maxConcurrency: number; - }; - shardsRecommended: number; - sessionStartLimitTotal: number; - sessionStartLimitRemaining: number; - sessionStartLimitResetAfter: number; - maxConcurrency: number; - maxShards: number; - lastShardId: number; - workerId: number; -} - -interface ReshardPayload { - type: "RESHARD"; - results: GetGatewayBot; -} - -interface FullyReshardedPayload { - type: "RESHARDED-CLOSEOLD"; -} - -// @ts-ignore this should not be erroring -self.onmessage = async function (message: MessageEvent) { - const log = logger({ - name: `GatewayWorker${JSON.parse(message.data).workerId ? `: ${JSON.parse(message.data).workerId}` : undefined}`, - }); - - log.debug(`New Message:\n`, message.data); - - const data = JSON.parse(message.data) as - | IdentifyPayload - | ReshardPayload - | FullyReshardedPayload; - - if (data.type === "IDENTIFY") { - workerId = data.workerId; - - gateway = spawnGateway(data.shardId, { - shardsRecommended: data.shardsRecommended, - sessionStartLimitTotal: data.sessionStartLimitTotal, - sessionStartLimitRemaining: data.sessionStartLimitRemaining, - sessionStartLimitResetAfter: data.sessionStartLimitResetAfter, - maxConcurrency: data.maxConcurrency, - maxShards: data.maxShards, - lastShardId: data.lastShardId, - spawnShardDelay: 5000, - }); - } - - if (data.type === "RESHARDED-CLOSEOLD") { - log.info("[Resharding] Closing old gateways."); - await gateway.resharding.closeOldShards(gatewayPendingClosing); - } - - if (data.type === "RESHARD") { - log.info("[Worker] Resharding the worker."); - gateway.resharding.isPending = async function (gateway: GatewayManager) { - for (let i = gateway.firstShardId; i < gateway.lastShardId; i++) { - const shard = gateway.shards.get(i); - if (!shard?.ready) { - return true; - } - } - - return false; - }; - - async function processResharding( - oldGateway: GatewayManager, - results: GetGatewayBot, - ) { - oldGateway.debug( - "GW DEBUG", - "[Resharding] Starting the reshard process.", - ); - - const gateway = createGatewayManager({ - ...oldGateway, - // RESET THE SETS AND COLLECTIONS - cache: { - guildIds: new Set(), - loadingGuildIds: new Set(), - editedMessages: new Collection(), - }, - shards: new Collection(), - loadingShards: new Collection(), - buckets: new Collection(), - utf8decoder: new TextDecoder(), - }); - - for (const [key, value] of Object.entries(oldGateway)) { - if (key === "handleDiscordPayload") { - gateway.handleDiscordPayload = async function (_, data, shardId) { - if (data.t === "READY") { - const payload = data.d as DiscordReady; - log.info( - `Shard #${payload.shard?.[0]} online`, - ); - if (shardId === gateway.lastShardId) { - // @ts-ignore - postMessage( - JSON.stringify({ - type: "RESHARDED", - results, - }), - ); - } - - await gateway.resharding.markNewGuildShardId( - payload.guilds.map((g) => BigInt(g.id)), - shardId, - ); - } - }; - continue; - } - - // DON"T OVERRIDE THESE - if ( - ["cache", "shards", "loadingShards", "buckets", "utf8decoder"] - .includes(key) - ) { - continue; - } - - // USE ANY CUSTOMIZED OPTIONS FROM OLD GATEWAY - // @ts-ignore silly ts error - gateway[key] = oldGateway[key as keyof typeof oldGateway]; - } - - // Begin resharding - // If more than 100K servers, begin switching to 16x sharding - if (gateway.useOptimalLargeBotSharding) { - log.info( - "[Resharding] Using optimal large bot sharding solution.", - ); - gateway.maxShards = gateway.calculateMaxShards( - results.shards, - results.sessionStartLimit.maxConcurrency, - ); - } else { - gateway.maxShards = results.shards; - } - - // FOR MANUAL SHARD CONTROL, OVERRIDE THIS SHARD ID! - gateway.lastShardId = oldGateway.lastShardId === oldGateway.maxShards - 1 - ? gateway.maxShards - 1 - : oldGateway.lastShardId; - gateway.shardsRecommended = results.shards; - gateway.sessionStartLimitTotal = results.sessionStartLimit.total; - gateway.sessionStartLimitRemaining = results.sessionStartLimit.remaining; - gateway.sessionStartLimitResetAfter = results.sessionStartLimit.resetAfter; - gateway.maxConcurrency = results.sessionStartLimit.maxConcurrency; - - gateway.spawnShards(gateway, gateway.firstShardId); - - return new Promise((resolve) => { - // TIMER TO KEEP CHECKING WHEN ALL SHARDS HAVE RESHARDED - const timer = setInterval(async () => { - const pending = await gateway.resharding.isPending( - gateway, - oldGateway, - ); - // STILL PENDING ON SOME SHARDS TO BE CREATED - if (pending) return; - - // ENABLE EVENTS ON NEW SHARDS AND IGNORE EVENTS ON OLD - const oldHandler = oldGateway.handleDiscordPayload; - gateway.handleDiscordPayload = oldHandler; - oldGateway.handleDiscordPayload = function (og, data, shardId) { - // ALLOW EXCEPTION FOR CHUNKING TO PREVENT REQUESTS FREEZING - if (data.t !== "GUILD_MEMBERS_CHUNK") return; - oldHandler(og, data, shardId); - }; - - // STOP TIMER - clearInterval(timer); - await gateway.resharding.editGuildShardIds(); - gatewayPendingClosing = oldGateway; - gateway.debug("GW DEBUG", "[Resharding] Complete."); - resolve(gateway); - }, 30000); - }) as Promise; - } - - gateway = await processResharding(gateway, data.results); - log.info(`Resharded the worker.`); - // @ts-ignore this should not be erroring - postMessage( - JSON.stringify({ - type: "RESHARDED", - results: data.results, - }), - ); - } -}; diff --git a/template/bigbot/old/src/rest/mod.ts b/template/bigbot/old/src/rest/mod.ts deleted file mode 100644 index 6a8d5b714..000000000 --- a/template/bigbot/old/src/rest/mod.ts +++ /dev/null @@ -1,85 +0,0 @@ -// START FILE FOR REST PROCESS -import { DISCORD_TOKEN, REST_AUTHORIZATION_KEY, REST_PORT } from "../../configs.ts"; -import { BASE_URL, createRestManager } from "../../deps.ts"; -import { logger } from "../utils/logger.ts"; - -const log = logger({ name: "REST" }); - -// CREATES THE FUNCTIONALITY FOR MANAGING THE REST REQUESTS -const rest = createRestManager({ - token: DISCORD_TOKEN, - secretKey: REST_AUTHORIZATION_KEY, - customUrl: `http://localhost:${REST_PORT}`, - debug: console.log, -}); - -// START LISTENING TO THE URL(localhost) -const server = Deno.listen({ port: REST_PORT }); -log.info( - `HTTP webserver running. Access it at: http://localhost:${REST_PORT}/`, -); - -// Connections to the server will be yielded up as an async iterable. -for await (const conn of server) { - // In order to not be blocking, we need to handle each connection individually - // in its own async function. - handleRequest(conn); -} - -async function handleRequest(conn: Deno.Conn) { - // This "upgrades" a network connection into an HTTP connection. - const httpConn = Deno.serveHttp(conn); - // Each request sent over the HTTP connection will be yielded as an async - // iterator from the HTTP connection. - for await (const requestEvent of httpConn) { - if ( - !REST_AUTHORIZATION_KEY || - REST_AUTHORIZATION_KEY !== - requestEvent.request.headers.get("AUTHORIZATION") - ) { - return requestEvent.respondWith( - new Response(JSON.stringify({ error: "Invalid authorization key." }), { - status: 401, - }), - ); - } - - const json = requestEvent.request.body ? (await requestEvent.request.json()) : undefined; - - try { - const result = await rest.runMethod( - rest, - requestEvent.request.method as RequestMethod, - `${BASE_URL}${ - requestEvent.request.url.substring( - `http://localhost:${REST_PORT}`.length, - ) - }`, - json, - ); - - if (result) { - requestEvent.respondWith( - new Response(JSON.stringify(result), { - status: 200, - }), - ); - } else { - requestEvent.respondWith( - new Response(undefined, { - status: 204, - }), - ); - } - } catch (error) { - log.error(error); - requestEvent.respondWith( - new Response(JSON.stringify(error), { - status: error.code, - }), - ); - } - } -} - -type RequestMethod = "post" | "put" | "delete" | "patch"; diff --git a/template/bigbot/old/src/utils/Components.ts b/template/bigbot/old/src/utils/Components.ts deleted file mode 100644 index 4c0a3d9fc..000000000 --- a/template/bigbot/old/src/utils/Components.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { ActionRow, ButtonStyles, MessageComponentTypes } from "../../deps.ts"; -import { SNOWFLAKE_REGEX } from "../constants/regexes.ts"; - -export class Components extends Array { - constructor(...args: ActionRow[]) { - super(...args); - - return this; - } - - addActionRow() { - // Don't allow more than 5 Action Rows - if (this.length === 5) return this; - - this.push({ - type: 1, - components: [] as unknown as ActionRow["components"], - }); - return this; - } - - addButton( - label: string, - style: keyof typeof ButtonStyles, - customIdOrLink: string, - options?: { emoji?: string | bigint; disabled?: boolean }, - ) { - // No Action Row has been created so do it - if (!this.length) this.addActionRow(); - - // Get the last Action Row - let row = this[this.length - 1]; - - // If the Action Row already has 5 buttons create a new one - if (row.components.length === 5) { - this.addActionRow(); - row = this[this.length - 1]; - - // Apparently there are already 5 Full Action Rows so don't add the button - if (row.components.length === 5) return this; - } - - row.components.push({ - type: MessageComponentTypes.Button, - label: label, - customId: style !== "Link" ? customIdOrLink : undefined, - style: ButtonStyles[style], - emoji: this.#stringToEmoji(options?.emoji), - url: style === "Link" ? customIdOrLink : undefined, - disabled: options?.disabled, - }); - return this; - } - - #stringToEmoji(emoji?: string | bigint) { - if (!emoji) return; - - emoji = emoji.toString(); - - // A snowflake id was provided - if (SNOWFLAKE_REGEX.test(emoji)) { - return { - id: BigInt(emoji.match(SNOWFLAKE_REGEX)![0]), - }; - } - - // A unicode emoji was provided - return { - name: emoji, - }; - } -} diff --git a/template/bigbot/old/src/utils/Embeds.ts b/template/bigbot/old/src/utils/Embeds.ts deleted file mode 100644 index 6d5c48d3f..000000000 --- a/template/bigbot/old/src/utils/Embeds.ts +++ /dev/null @@ -1,187 +0,0 @@ -import { Bot, Embed, User } from "../../deps.ts"; - -const embedLimits = { - title: 256, - description: 4096, - fieldName: 256, - fieldValue: 1024, - footerText: 2048, - authorName: 256, - fields: 25, - total: 6000, -}; - -export class Embeds extends Array { - /** The amount of characters in the embed. */ - currentTotal = 0; - /** Whether the limits should be enforced or not. */ - enforceLimits = true; - /** If a file is attached to the message it will be added here. */ - file?: EmbedFile; - bot: Bot; - - constructor(bot: Bot, enforceLimits = true) { - super(); - this.bot = bot; - // By default we will always want to enforce discord limits but this option allows us to bypass for whatever reason. - if (!enforceLimits) this.enforceLimits = false; - - return this; - } - - fitData(data: string, max: number) { - // If the string is bigger then the allowed max shorten it. - if (data.length > max) data = data.substring(0, max); - // Check the amount of characters left for this embed - const availableCharacters = embedLimits.total - this.currentTotal; - // If it is maxed out already return empty string as nothing can be added anymore - if (!availableCharacters) return ``; - // If the string breaks the maximum embed limit then shorten it. - if (this.currentTotal + data.length > embedLimits.total) { - return data.substring(0, availableCharacters); - } - // Return the data as is with no changes. - return data; - } - - setAuthor(name: string, iconUrl?: string | User, url?: string) { - const embed = this.#getLastEmbed(); - const finalName = this.enforceLimits ? this.fitData(name, embedLimits.authorName) : name; - - if (typeof iconUrl === "string") { - embed.author = { name: finalName, iconUrl, url }; - } else if (iconUrl) { - embed.author = { - name: finalName, - iconUrl: this.bot.helpers.avatarURL( - iconUrl.id, - iconUrl?.discriminator, - { - avatar: iconUrl.avatar!, - }, - ), - url, - }; - } else { - embed.author = { name: finalName, url }; - } - - return this; - } - - setColor(color: string) { - this.#getLastEmbed().color = color.toLowerCase() === `random` - ? // Random color - Math.floor(Math.random() * (0xffffff + 1)) - : // Convert the hex to a acceptable color for discord - parseInt(color.replace("#", ""), 16); - - return this; - } - - setDescription(description: string | string[]) { - if (Array.isArray(description)) description = description.join("\n"); - this.#getLastEmbed().description = this.fitData( - description, - embedLimits.description, - ); - - return this; - } - - addField(name: string, value: string, inline = false) { - const embed = this.#getLastEmbed(); - - if (embed.fields!.length >= 25) return this; - - embed.fields!.push({ - name: this.fitData(name, embedLimits.fieldName), - value: this.fitData(value, embedLimits.fieldValue), - inline, - }); - - return this; - } - - addBlankField(inline = false) { - return this.addField("\u200B", "\u200B", inline); - } - - attachFile(file: unknown, name: string) { - this.file = { - blob: file, - name, - }; - this.setImage(`attachment://${name}`); - - return this; - } - - setFooter(text: string, icon?: string) { - this.#getLastEmbed().footer = { - text: this.fitData(text, embedLimits.footerText), - iconUrl: icon, - }; - - return this; - } - - setImage(url: string | User) { - if (typeof url === "string") this.#getLastEmbed().image = { url }; - else { - this.#getLastEmbed().image = { - url: this.bot.helpers.avatarURL(url.id, url.discriminator, { - avatar: url.avatar!, - size: 2048, - }), - }; - } - - return this; - } - - setTimestamp(time: number | string = Date.now()) { - this.#getLastEmbed().timestamp = typeof time === "string" ? Date.parse(time) : time; - - return this; - } - - setTitle(title: string, url?: string) { - this.#getLastEmbed().title = this.fitData(title, embedLimits.title); - if (url) this.#getLastEmbed().url = url; - - return this; - } - - setThumbnail(url: string) { - this.#getLastEmbed().thumbnail = { url }; - - return this; - } - - addEmbed(embed?: Embed) { - if (this.length === 10) return this; - - this.push({ ...embed, fields: embed?.fields ?? [] }); - - return this; - } - - /** Get the last Embed, if there is no it will create one */ - #getLastEmbed() { - if (this.length) return this[this.length - 1]; - - this.push({ - fields: [], - }); - - return this[0]; - } -} - -export interface EmbedFile { - blob: unknown; - name: string; -} - -export default Embeds; diff --git a/template/bigbot/old/src/utils/helpers.ts b/template/bigbot/old/src/utils/helpers.ts deleted file mode 100644 index 84570afde..000000000 --- a/template/bigbot/old/src/utils/helpers.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { User } from "../../deps.ts"; -import { Milliseconds } from "../constants/milliseconds.ts"; - -export function chooseRandom(array: T[]) { - return array[Math.floor(Math.random() * array.length)]!; -} - -export function getUserTag(user: User) { - return `${user.username}#${user.discriminator}`; -} - -export function toTitleCase(text: string) { - return text - .split(" ") - .map(( - word, - ) => (word[0] ? `${word[0].toUpperCase()}${word.substring(1).toLowerCase()}` : word)) - .join(" "); -} - -/** This function should be used when you want to convert milliseconds to a human readable format like 1d5h. */ -export function humanizeMilliseconds(milliseconds: number) { - const years = Math.floor(milliseconds / Milliseconds.Year); - const months = Math.floor( - (milliseconds % Milliseconds.Year) / Milliseconds.Month, - ); - const weeks = Math.floor( - ((milliseconds % Milliseconds.Year) % Milliseconds.Month) / - Milliseconds.Week, - ); - const days = Math.floor( - (((milliseconds % Milliseconds.Year) % Milliseconds.Month) % - Milliseconds.Week) / Milliseconds.Day, - ); - const hours = Math.floor( - ((((milliseconds % Milliseconds.Year) % Milliseconds.Month) % - Milliseconds.Week) % Milliseconds.Day) / - Milliseconds.Hour, - ); - const minutes = Math.floor( - (((((milliseconds % Milliseconds.Year) % Milliseconds.Month) % - Milliseconds.Week) % Milliseconds.Day) % - Milliseconds.Hour) / - Milliseconds.Minute, - ); - const seconds = Math.floor( - ((((((milliseconds % Milliseconds.Year) % Milliseconds.Month) % - Milliseconds.Week) % Milliseconds.Day) % - Milliseconds.Hour) % - Milliseconds.Minute) / - Milliseconds.Second, - ); - - const YearString = years ? `${years}y ` : ""; - const monthString = months ? `${months}mo ` : ""; - const weekString = weeks ? `${weeks}w ` : ""; - const dayString = days ? `${days}d ` : ""; - const hourString = hours ? `${hours}h ` : ""; - const minuteString = minutes ? `${minutes}m ` : ""; - const secondString = seconds ? `${seconds}s ` : ""; - - return ( - `${YearString}${monthString}${weekString}${dayString}${hourString}${minuteString}${secondString}` - .trimEnd() || "1s" - ); -} - -/** This function helps convert a string like 1d5h to milliseconds. */ -export function stringToMilliseconds(text: string) { - const matches = text.match(/\d+(y|mo|w|d|h|m|s){1}/gi); - if (!matches) return; - - let total = 0; - - for (const match of matches) { - // Finds the first of these letters - const validMatch = /(y|mo|w|d|h|m|s)/.exec(match); - // if none of them were found cancel - if (!validMatch) return; - // Get the number which should be before the index of that match - const number = match.substring(0, validMatch.index); - // Get the letter that was found - const [letter] = validMatch; - if (!number || !letter) return; - - let multiplier = Milliseconds.Second; - switch (letter.toLowerCase()) { - case "y": - multiplier = Milliseconds.Year; - break; - case "mo": - multiplier = Milliseconds.Month; - break; - case "w": - multiplier = Milliseconds.Week; - break; - case "d": - multiplier = Milliseconds.Day; - break; - case "h": - multiplier = Milliseconds.Hour; - break; - case "m": - multiplier = Milliseconds.Minute; - break; - } - - const amount = number ? parseInt(number, 10) : undefined; - if (!amount) return; - - total += amount * multiplier; - } - - return total; -} - -export function chunkStrings( - array: string[], - size = 2000, - lineSeparator = "\n", -) { - const responses: string[] = []; - let response = ""; - for (const text of array) { - const nextText = response.length && lineSeparator ? `${lineSeparator}${text}` : text; - if (response.length + nextText.length >= size) { - responses.push(response); - response = ""; - } - response += nextText; - } - responses.push(response); - return responses; -} - -export const timestamps = { - ShortTime: "t", - LongTime: "T", - ShortDate: "d", - LongDate: "D", - ShortDateTime: "f", - LongDateTime: "F", - Relative: "R", -} as const; - -export function snowflakeToTimestamp(id: bigint) { - return Number(id / 4194304n + 1420070400000n); -} diff --git a/template/bigbot/old/src/utils/logger.ts b/template/bigbot/old/src/utils/logger.ts deleted file mode 100644 index 2d586cdbe..000000000 --- a/template/bigbot/old/src/utils/logger.ts +++ /dev/null @@ -1,101 +0,0 @@ -// deno-lint-ignore-file no-explicit-any -import { bold, cyan, gray, italic, red, yellow } from "../../deps.ts"; - -export enum LogLevels { - Debug, - Info, - Warn, - Error, - Fatal, -} - -const prefixes = new Map([ - [LogLevels.Debug, "DEBUG"], - [LogLevels.Info, "INFO"], - [LogLevels.Warn, "WARN"], - [LogLevels.Error, "ERROR"], - [LogLevels.Fatal, "FATAL"], -]); - -const noColor: (str: string) => string = (msg) => msg; -const colorFunctions = new Map string>([ - [LogLevels.Debug, gray], - [LogLevels.Info, cyan], - [LogLevels.Warn, yellow], - [LogLevels.Error, (str: string) => red(str)], - [LogLevels.Fatal, (str: string) => red(bold(italic(str)))], -]); - -export function logger({ - logLevel = LogLevels.Info, - name, -}: { - logLevel?: LogLevels; - name?: string; -} = {}) { - function log(level: LogLevels, ...args: any[]) { - if (level < logLevel) return; - - let color = colorFunctions.get(level); - if (!color) color = noColor; - - const date = new Date(); - const log = [ - `[${date.toLocaleDateString()} ${date.toLocaleTimeString()}]`, - color(prefixes.get(level) || "DEBUG"), - name ? `${name} >` : ">", - ...args, - ]; - - switch (level) { - case LogLevels.Debug: - return console.debug(...log); - case LogLevels.Info: - return console.info(...log); - case LogLevels.Warn: - return console.warn(...log); - case LogLevels.Error: - return console.error(...log); - case LogLevels.Fatal: - return console.error(...log); - default: - return console.log(...log); - } - } - - function setLevel(level: LogLevels) { - logLevel = level; - } - - function debug(...args: any[]) { - log(LogLevels.Debug, ...args); - } - - function info(...args: any[]) { - log(LogLevels.Info, ...args); - } - - function warn(...args: any[]) { - log(LogLevels.Warn, ...args); - } - - function error(...args: any[]) { - log(LogLevels.Error, ...args); - } - - function fatal(...args: any[]) { - log(LogLevels.Fatal, ...args); - } - - return { - log, - setLevel, - debug, - info, - warn, - error, - fatal, - }; -} - -export default logger(); diff --git a/template/bigbot/old/src/utils/options.ts b/template/bigbot/old/src/utils/options.ts deleted file mode 100644 index a62d3aa6c..000000000 --- a/template/bigbot/old/src/utils/options.ts +++ /dev/null @@ -1,288 +0,0 @@ -import { - ApplicationCommandOption, - ApplicationCommandOptionTypes, - Bot, - Channel, - ChannelTypes, - Collection, - InteractionDataOption, - Member, - Message, - Role, - User, -} from "../../deps.ts"; -import { getLanguage, translate } from "../bot/languages/translate.ts"; -import { SNOWFLAKE_REGEX } from "../constants/regexes.ts"; - -// Mapped by `language-commandName` -const translatedOptionNamesCache = new Map>(); - -/** Translates all options of the command to an object: translatedOptionName: optionName */ -export function translateOptionNames( - bot: Bot, - guildId: bigint, - options: ApplicationCommandOption[], - commandName?: string, -): Record { - const language = getLanguage(guildId); - // RETURN THE ALREADY TRANSLATED OPTIONS WHICH ARE IN CACHE - if ( - commandName && translatedOptionNamesCache.has(`${language}-${commandName}`) - ) { - return translatedOptionNamesCache.get(`${language}-${commandName}`)!; - } - - // TRANSLATE ALL OPTIONS - let translated: Record = {}; - for (const option of options) { - // @ts-ignore ts being dumb - translated[translate(bot, guildId, option.name).toLowerCase()] = translate( - bot, - "english", - // @ts-ignore ts being dumb - option.name, - ); - if (option.options) { - translated = { - ...translated, - ...translateOptionNames(bot, guildId, option.options), - }; - } - } - - // SAVE THE TRANSLATED OPTIONS IN CACHE FOR FASTER ACCESS - if (commandName) { - translatedOptionNamesCache.set(`${language}-${commandName}`, translated); - } - - return translated; -} - -function convertOptionValue( - option: InteractionDataOption, - resolved?: { - /** The Ids and Message objects */ - messages?: Collection; - /** The Ids and User objects */ - users?: Collection; - /** The Ids and partial Member objects */ - members?: Collection; - /** The Ids and Role objects */ - roles?: Collection; - /** The Ids and partial Channel objects */ - channels?: Collection< - bigint, - { - id: bigint; - name: string; - type: ChannelTypes; - permissions: bigint; - } - >; - }, - translateOptions?: Record, -): [ - string, - ( - | { user: User; member: Member } - | Role - | { - id: bigint; - name: string; - type: ChannelTypes; - permissions: bigint; - } - | boolean - | string - | number - ), -] { - const value = typeof option.value === "string" && SNOWFLAKE_REGEX.test(option.value) ? BigInt(option.value) : 0n; - // THE OPTION IS A CHANNEL - if (option.type === ApplicationCommandOptionTypes.Channel) { - const channel = resolved?.channels?.get(value); - - // SAVE THE ARGUMENT WITH THE CORRECT NAME - return [translateOptions?.[option.name] ?? option.name, channel!]; - } - - // THE OPTION IS A ROLE - if (option.type === ApplicationCommandOptionTypes.Role) { - const role = resolved?.roles?.get(value); - - // SAVE THE ARGUMENT WITH THE CORRECT NAME - return [translateOptions?.[option.name] ?? option.name, role!]; - } - - // THE OPTION IS A USER - if (option.type === ApplicationCommandOptionTypes.User) { - const user = resolved?.users?.get(value); - const member = resolved?.members?.get(value); - - // SAVE THE ARGUMENT WITH THE CORRECT NAME - return [ - translateOptions?.[option.name] ?? option.name, - { - member: member!, - user: user!, - }, - ]; - } - - // THE OPTION IS A MENTIONABLE - if (option.type === ApplicationCommandOptionTypes.Mentionable) { - const role = resolved?.roles?.get(value); - const user = resolved?.users?.get(value); - const member = resolved?.members?.get(value); - - const final = user && member ? { user, member } : role!; - - // SAVE THE ARGUMENT WITH THE CORRECT NAME - return [translateOptions?.[option.name] ?? option.name, final]; - } - - // THE REST OF OPTIONS DON'T NEED ANY CONVERSION - // SAVE THE ARGUMENT WITH THE CORRECT NAME - // @ts-ignore ts leave me alone - return [translateOptions?.[option.name] ?? option.name, option.value]; -} - -/** Parse the options to a nice object. - * NOTE: this does not work with subcommands - */ -export function optionParser( - options?: InteractionDataOption[], - resolved?: { - /** The Ids and Message objects */ - messages?: Collection; - /** The Ids and User objects */ - users?: Collection; - /** The Ids and partial Member objects */ - members?: Collection; - /** The Ids and Role objects */ - roles?: Collection; - /** The Ids and partial Channel objects */ - channels?: Collection< - bigint, - { - id: bigint; - name: string; - type: ChannelTypes; - permissions: bigint; - } - >; - }, - translateOptions?: Record, -): - | InteractionCommandArgs - | { [key: string]: InteractionCommandArgs } - | { [key: string]: { [key: string]: InteractionCommandArgs } } { - // OPTIONS CAN BE UNDEFINED SO WE JUST RETURN AN EMPTY OBJECT - if (!options) return {}; - - // A SUBCOMMAND WAS USED - if (options[0].type === ApplicationCommandOptionTypes.SubCommand) { - const convertedOptions: Record< - string, - | { user: User; member: Member } - | Role - | { - id: bigint; - name: string; - type: ChannelTypes; - permissions: bigint; - } - | boolean - | string - | number - > = {}; - // CONVERT ALL THE OPTIONS - for (const option of options[0].options ?? []) { - const [name, value] = convertOptionValue( - option, - resolved, - translateOptions, - ); - convertedOptions[name] = value; - } - - // @ts-ignore ts leave me alone - return { - [translateOptions?.[options[0].name] ?? options[0].name]: convertedOptions, - }; - } - - // A SUBCOMMAND GROUP WAS USED - if (options[0].type === ApplicationCommandOptionTypes.SubCommandGroup) { - const convertedOptions: Record< - string, - | Member - | Role - | Channel - | boolean - | string - | number - > = {}; - // CONVERT ALL THE OPTIONS - for (const option of options[0].options![0].options ?? []) { - const [name, value] = convertOptionValue( - option, - resolved, - translateOptions, - ); - // @ts-ignore ts leave me alone - convertedOptions[name] = value; - } - - // @ts-ignore ts leave me alone - return { - [translateOptions?.[options[0].name] ?? options[0].name]: { - [ - translateOptions?.[options[0].options![0].name] ?? - options[0].options![0].name - ]: convertedOptions, - }, - }; - } - - // A NORMAL COMMAND WAS USED - const convertedOptions: Record< - string, - | Member - | Role - | Record< - string, - Pick - > - | boolean - | string - | number - > = {}; - for (const option of options ?? []) { - const [name, value] = convertOptionValue( - option, - resolved, - translateOptions, - ); - // @ts-ignore ts leave me alone - convertedOptions[name] = value; - } - - return convertedOptions; -} - -/** The interaction arguments. - * Important the members `deaf` and `mute` properties will always be false. - */ -export type InteractionCommandArgs = Record< - string, - | Member - | Role - | Record< - string, - Pick - > - | boolean - | string - | number ->; diff --git a/template/bigbot/old/src/utils/permLevels.ts b/template/bigbot/old/src/utils/permLevels.ts deleted file mode 100644 index 303833f93..000000000 --- a/template/bigbot/old/src/utils/permLevels.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Interaction, validatePermissions } from "../../deps.ts"; -import { Command } from "../bot/types/command.ts"; - -export default async function hasPermissionLevel( - // deno-lint-ignore no-explicit-any - command: Command, - payload: Interaction, -) { - // This command doesn't require a perm level so allow the command. - if (!command.permissionLevels) return true; - - // If a custom function was provided - if (typeof command.permissionLevels === "function") { - return await command.permissionLevels(payload, command); - } - - // If an array of perm levels was provided - for (const permLevel of command.permissionLevels) { - // If this user has one of the allowed perm level, the loop is canceled and command is allowed. - if (await PermissionLevelHandlers[permLevel](payload, command)) return true; - } - - // None of the perm levels were met. So cancel the command - return false; -} - -export const PermissionLevelHandlers: Record< - keyof typeof PermissionLevels, - ( - payload: Interaction, - // deno-lint-ignore no-explicit-any - command: Command, - ) => boolean | Promise -> = { - MEMBER: () => true, - MODERATOR: (payload) => - Boolean(payload.member?.permissions) && - validatePermissions(payload.member!.permissions!, ["MANAGE_GUILD"]), - ADMIN: (payload) => - Boolean(payload.member?.permissions) && - validatePermissions(payload.member!.permissions!, ["ADMINISTRATOR"]), - SERVER_OWNER: () => false, - BOT_SUPPORT: () => false, - BOT_DEVS: () => false, - BOT_OWNERS: () => false, -}; - -export enum PermissionLevels { - MEMBER, - MODERATOR, - ADMIN, - SERVER_OWNER, - BOT_SUPPORT, - BOT_DEVS, - BOT_OWNERS, -} diff --git a/template/bigbot/old/src/utils/replies.ts b/template/bigbot/old/src/utils/replies.ts deleted file mode 100644 index d70f4d59f..000000000 --- a/template/bigbot/old/src/utils/replies.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Bot, Interaction, InteractionApplicationCommandCallbackData, InteractionResponseTypes } from "../../deps.ts"; - -export async function replyToInteraction( - bot: Bot, - payload: Interaction, - options: - | string - | (InteractionApplicationCommandCallbackData & { - /** Type of the reply */ - type?: InteractionResponseTypes; - }), -) { - if (typeof options === "string") options = { content: options }; - - return await bot.helpers.sendInteractionResponse(payload.id, payload.token, { - type: options.type ?? InteractionResponseTypes.ChannelMessageWithSource, - data: options, - }); -} - -export async function privateReplyToInteraction( - bot: Bot, - payload: Interaction, - options: - | string - | (InteractionApplicationCommandCallbackData & { - /** Type of the reply */ - type?: InteractionResponseTypes; - }), -) { - if (typeof options === "string") options = { content: options }; - // SET PRIVATE - options.flags = 64; - - return await replyToInteraction(bot, payload, { ...options }); -} diff --git a/template/bigbot/old/src/utils/slashWebhook.ts b/template/bigbot/old/src/utils/slashWebhook.ts deleted file mode 100644 index 34d6bf391..000000000 --- a/template/bigbot/old/src/utils/slashWebhook.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Bot, Interaction } from "../../deps.ts"; -import Embeds from "./Embeds.ts"; -import { getUserTag } from "./helpers.ts"; -import logger from "./logger.ts"; - -export async function slashLogWebhook( - bot: Bot, - payload: Interaction, - name: string, -) { - const webhook = Deno.env.get("DISCORD_LOGS_WEBHOOK"); - if (!webhook) return; - - const [id, token] = webhook.substring(webhook.indexOf("webhooks/") + 9).split( - "/", - ); - - const embeds = new Embeds(bot) - .setAuthor(`${getUserTag(payload.user)} used ${name}`, payload.user) - .addField( - "Channel", - payload.channelId?.toString() || "Channel ID unavailable", - true, - ) - .addField( - "Guild", - payload.guildId?.toString() || "Guild ID unavailable", - true, - ); - - await bot.helpers - .sendWebhook(bot.transformers.snowflake(id), token, { - embeds, - wait: false, - }) - .catch(logger.error); -} - -export default slashLogWebhook; diff --git a/template/bigbot/old/src/utils/updateSlash.ts b/template/bigbot/old/src/utils/updateSlash.ts deleted file mode 100644 index fa7579301..000000000 --- a/template/bigbot/old/src/utils/updateSlash.ts +++ /dev/null @@ -1,165 +0,0 @@ -import { DEV_GUILD_ID } from "../../configs.ts"; -import { ApplicationCommandOption, ApplicationCommandTypes, Bot } from "../../deps.ts"; -import { BotClient } from "../bot/botClient.ts"; -import { updateCommandVersion } from "../bot/database/commandVersion.ts"; -import commands from "../bot/events/interactions/mod.ts"; -import { serverLanguages, translate } from "../bot/languages/translate.ts"; -import { ArgumentDefinition } from "../bot/types/command.ts"; - -export async function updateDevCommands(bot: Bot) { - if (!DEV_GUILD_ID) return; - - const cmds = Object.entries(commands) - // ONLY DEV COMMANDS - .filter(([_name, command]) => command.dev); - - if (!cmds.length) return; - - // DEV RELATED COMMANDS - await bot.helpers.upsertApplicationCommands( - cmds.map(([name, command]) => { - const translatedName = translate(bot, DEV_GUILD_ID, command.name); - const translatedDescription = command.description ? translate(bot, DEV_GUILD_ID, command.description) : ""; - - if (command.type && command.type !== ApplicationCommandTypes.ChatInput) { - return { - name: (translatedName || name).toLowerCase(), - type: command.type, - }; - } - - return { - name: (translatedName || name).toLowerCase(), - description: translatedDescription || command!.description, - options: command.options ? createOptions(bot, DEV_GUILD_ID, command.options, command.name) : undefined, - }; - }), - DEV_GUILD_ID, - ); -} - -// USED TO CACHE CONVERTED COMMANDS AFTER START TO PREVENT UNNECESSARY LOOPS -const convertedCache = new Map(); - -/** Creates the commands options including subcommands. Also translates them. */ -function createOptions( - bot: Bot, - guildId: bigint | "english", - options: ArgumentDefinition[], - commandName?: string, -): ApplicationCommandOption[] | undefined { - const language = guildId === "english" ? "english" : serverLanguages.get(guildId) ?? "english"; - if (commandName && convertedCache.has(`${language}-${commandName}`)) { - return convertedCache.get(`${language}-${commandName}`)!; - } - - const newOptions: ApplicationCommandOption[] = []; - - for (const option of options || []) { - const optionName = translate(bot, guildId, option.name); - const optionDescription = translate(bot, guildId, option.description); - - // TODO: remove this ts ignore - // @ts-ignore ts stop being dumb - const choices = option.choices?.map((choice) => ({ - ...choice, - name: translate(bot, guildId, choice.name), - })); - - newOptions.push({ - ...option, - name: optionName.toLowerCase(), - description: optionDescription || "No description available.", - choices, - // @ts-ignore fix this - options: option.options - ? // @ts-ignore fix this - createOptions(bot, guildId, option.options) - : undefined, - } as ApplicationCommandOption); - } - if (commandName) convertedCache.set(`${language}-${commandName}`, newOptions); - - return newOptions; -} - -export async function updateGlobalCommands(bot: Bot) { - // UPDATE GLOBAL COMMANDS - await bot.helpers.upsertApplicationCommands( - Object.entries(commands) - // ONLY GLOBAL COMMANDS - .filter(([_name, command]) => command?.global && !command.dev) - .map(([name, command]) => { - return { - name, - description: translate(bot, "english", command.description), - options: createOptions(bot, "english", command.options, command.name), - }; - }), - ); -} - -export async function updateGuildCommands(bot: BotClient, guildId: bigint) { - if (guildId === DEV_GUILD_ID) return await updateDevCommands(bot); - - await updateCommandVersion(bot, guildId); - - Object.entries(commands) - // ONLY GUILD COMMANDS - .filter(([_name, command]) => !command.global && !command.dev) - .map(([name, command]) => { - // USER OPTED TO USE BASIC VERSION ONLY - if (command.advanced === false) { - return { - name, - description: translate(bot, "english", command.description), - options: command.options, - }; - } - - // ADVANCED VERSION WILL ALLOW TRANSLATION - const translatedName = translate(bot, guildId, command.name); - const translatedDescription = translate( - bot, - guildId, - command.description, - ); - - return { - name: translatedName.toLowerCase(), - description: translatedDescription, - options: createOptions(bot, guildId, command.options, command.name), - }; - }), - // GUILD RELATED COMMANDS - await bot.helpers.upsertApplicationCommands( - Object.entries(commands) - // ONLY GUILD COMMANDS - .filter(([_name, command]) => !command.global && !command.dev) - .map(([name, command]) => { - // USER OPTED TO USE BASIC VERSION ONLY - if (command.advanced === false) { - return { - name, - description: command!.description || "No description available.", - options: command!.options, - }; - } - - // ADVANCED VERSION WILL ALLOW TRANSLATION - const translatedName = translate(bot, guildId, command.name); - const translatedDescription = translate( - bot, - guildId, - command.description, - ); - - return { - name: (translatedName || name).toLowerCase(), - description: translatedDescription || command!.description, - options: createOptions(bot, guildId, command.options, command.name), - }; - }), - guildId, - ); -}