UNPKG

@sapphire/framework

Version:

Discord bot framework built for advanced and amazing bots.

302 lines (300 loc) 16.5 kB
"use strict"; const require_lib_types_Enums = require('../../types/Enums.cjs'); const require_lib_utils_application_commands_computeDifferences = require('./computeDifferences.cjs'); const require_lib_utils_application_commands_normalizeInputs = require('./normalizeInputs.cjs'); const require_lib_utils_application_commands_ApplicationCommandRegistries = require('./ApplicationCommandRegistries.cjs'); let __sapphire_pieces = require("@sapphire/pieces"); let __sapphire_utilities = require("@sapphire/utilities"); let discord_js = require("discord.js"); let discord_api_types_v10 = require("discord-api-types/v10"); //#region src/lib/utils/application-commands/ApplicationCommandRegistry.ts var ApplicationCommandRegistry = class { constructor(commandName) { this.chatInputCommands = /* @__PURE__ */ new Set(); this.contextMenuCommands = /* @__PURE__ */ new Set(); this.guildIdsToFetch = /* @__PURE__ */ new Set(); this.globalCommandId = null; this.globalChatInputCommandIds = /* @__PURE__ */ new Set(); this.globalContextMenuCommandIds = /* @__PURE__ */ new Set(); this.guildCommandIds = new discord_js.Collection(); this.guildIdToChatInputCommandIds = new discord_js.Collection(); this.guildIdToContextMenuCommandIds = new discord_js.Collection(); this.apiCalls = []; this.commandName = commandName; } get command() { return __sapphire_pieces.container.stores.get("commands").get(this.commandName); } registerChatInputCommand(command, options) { const builtData = require_lib_utils_application_commands_normalizeInputs.normalizeChatInputCommand(command); this.chatInputCommands.add(builtData.name); const guildIdsToRegister = this.getGuildIdsToRegister(options); const registerOptions = { registerCommandIfMissing: true, behaviorWhenNotIdentical: require_lib_utils_application_commands_ApplicationCommandRegistries.getDefaultBehaviorWhenNotIdentical(), guildIds: guildIdsToRegister, ...options ?? {} }; this.apiCalls.push({ builtData, registerOptions, type: require_lib_types_Enums.InternalRegistryAPIType.ChatInput }); if (options?.idHints) for (const hint of options.idHints) this.chatInputCommands.add(hint); this.processGuildIds(guildIdsToRegister); return this; } registerContextMenuCommand(command, options) { const builtData = require_lib_utils_application_commands_normalizeInputs.normalizeContextMenuCommand(command); this.contextMenuCommands.add(builtData.name); const guildIdsToRegister = this.getGuildIdsToRegister(options); const registerOptions = { registerCommandIfMissing: true, behaviorWhenNotIdentical: require_lib_utils_application_commands_ApplicationCommandRegistries.getDefaultBehaviorWhenNotIdentical(), guildIds: guildIdsToRegister, ...options ?? {} }; this.apiCalls.push({ builtData, registerOptions, type: require_lib_types_Enums.InternalRegistryAPIType.ContextMenu }); if (options?.idHints) for (const hint of options.idHints) this.contextMenuCommands.add(hint); this.processGuildIds(guildIdsToRegister); return this; } addChatInputCommandNames(...names) { const flattened = names.flat(Infinity); for (const command of flattened) { this.debug(`Registering name "${command}" to internal chat input map`); this.warn(`Registering the chat input command "${command}" using a name is not recommended.`, "Please use the \"addChatInputCommandIds\" method instead with a command id."); this.chatInputCommands.add(command); } return this; } addContextMenuCommandNames(...names) { const flattened = names.flat(Infinity); for (const command of flattened) { this.debug(`Registering name "${command}" to internal context menu map`); this.warn(`Registering the context menu command "${command}" using a name is not recommended.`, "Please use the \"addContextMenuCommandIds\" method instead with a command id."); this.contextMenuCommands.add(command); } return this; } addChatInputCommandIds(...commandIds) { const flattened = commandIds.flat(Infinity); for (const entry of flattened) { try { BigInt(entry); this.debug(`Registering id "${entry}" to internal chat input map`); } catch { this.debug(`Registering name "${entry}" to internal chat input map`); this.warn(`Registering the chat input command "${entry}" using a name *and* trying to bypass this warning by calling "addChatInputCommandIds" is not recommended.`, "Please use the \"addChatInputCommandIds\" method with a valid command id instead."); } this.chatInputCommands.add(entry); } return this; } addContextMenuCommandIds(...commandIds) { const flattened = commandIds.flat(Infinity); for (const entry of flattened) { try { BigInt(entry); this.debug(`Registering id "${entry}" to internal context menu map`); } catch { this.debug(`Registering name "${entry}" to internal context menu map`); this.warn(`Registering the context menu command "${entry}" using a name *and* trying to bypass this warning by calling "addContextMenuCommandIds" is not recommended.`, "Please use the \"addContextMenuCommandIds\" method with a valid command id instead."); } this.contextMenuCommands.add(entry); } return this; } async runAPICalls(applicationCommands, globalCommands, guildCommands) { if (this.apiCalls.length === 0) { this.trace("No API calls to run, and no command to register"); return; } if (require_lib_utils_application_commands_ApplicationCommandRegistries.getDefaultBehaviorWhenNotIdentical() === require_lib_types_Enums.RegisterBehavior.BulkOverwrite) throw new RangeError(`"runAPICalls" was called for "${this.commandName}" but the defaultBehaviorWhenNotIdentical is "BulkOverwrite". This should not happen.`); this.debug(`Preparing to process ${this.apiCalls.length} possible command registrations / updates...`); const errored = (await Promise.allSettled(this.apiCalls.map((call) => this.handleAPICall(applicationCommands, globalCommands, guildCommands, call)))).filter((result) => result.status === "rejected"); if (errored.length) { this.error(`Received ${errored.length} errors while processing command registrations / updates`); for (const error of errored) this.error(error.reason.stack ?? error.reason); } } handleIdAddition(type, id, guildId) { switch (type) { case require_lib_types_Enums.InternalRegistryAPIType.ChatInput: this.addChatInputCommandIds(id); if (guildId) this.guildIdToChatInputCommandIds.ensure(guildId, () => /* @__PURE__ */ new Set()).add(id); else this.globalChatInputCommandIds.add(id); break; case require_lib_types_Enums.InternalRegistryAPIType.ContextMenu: this.addContextMenuCommandIds(id); if (guildId) this.guildIdToContextMenuCommandIds.ensure(guildId, () => /* @__PURE__ */ new Set()).add(id); else this.globalContextMenuCommandIds.add(id); break; } if (guildId) { if (!this.guildCommandIds.has(guildId)) this.guildCommandIds.set(guildId, id); } else this.globalCommandId ??= id; } getGuildIdsToRegister(options) { let guildIdsToRegister = void 0; if (!(0, __sapphire_utilities.isNullishOrEmpty)(options?.guildIds)) guildIdsToRegister = options.guildIds; else if (!(0, __sapphire_utilities.isNullishOrEmpty)(require_lib_utils_application_commands_ApplicationCommandRegistries.getDefaultGuildIds())) guildIdsToRegister = require_lib_utils_application_commands_ApplicationCommandRegistries.getDefaultGuildIds(); return guildIdsToRegister; } processGuildIds(guildIdsToRegister) { if (!(0, __sapphire_utilities.isNullishOrEmpty)(guildIdsToRegister)) for (const id of guildIdsToRegister) { this.guildIdsToFetch.add(id); require_lib_utils_application_commands_ApplicationCommandRegistries.allGuildIdsToFetchCommandsFor.add(id); } } async handleAPICall(commandsManager, globalCommands, allGuildsCommands, apiCall) { const { builtData, registerOptions } = apiCall; const commandName = builtData.name; const behaviorIfNotEqual = registerOptions.behaviorWhenNotIdentical ?? require_lib_utils_application_commands_ApplicationCommandRegistries.getDefaultBehaviorWhenNotIdentical(); const findCallback = (entry) => { if (apiCall.type === require_lib_types_Enums.InternalRegistryAPIType.ChatInput && entry.type !== discord_api_types_v10.ApplicationCommandType.ChatInput) return false; if (apiCall.type === require_lib_types_Enums.InternalRegistryAPIType.ContextMenu) { if (entry.type === discord_api_types_v10.ApplicationCommandType.ChatInput) return false; if (apiCall.builtData.type !== entry.type) return false; } const isInIdHint = registerOptions.idHints?.includes(entry.id); return typeof isInIdHint === "boolean" ? isInIdHint || entry.name === commandName : entry.name === commandName; }; let type; switch (apiCall.type) { case require_lib_types_Enums.InternalRegistryAPIType.ChatInput: type = "chat input"; break; case require_lib_types_Enums.InternalRegistryAPIType.ContextMenu: switch (apiCall.builtData.type) { case discord_api_types_v10.ApplicationCommandType.Message: type = "message context menu"; break; case discord_api_types_v10.ApplicationCommandType.User: type = "user context menu"; break; default: type = "unknown-type context menu"; } break; default: type = "unknown"; } if (!registerOptions.guildIds?.length) { const globalCommand = globalCommands.find(findCallback); if (globalCommand) { this.debug(`Checking if command "${commandName}" is identical with global ${type} command with id "${globalCommand.id}"`); this.handleIdAddition(apiCall.type, globalCommand.id); await this.handleCommandPresent(globalCommand, builtData, behaviorIfNotEqual, null); } else if (registerOptions.registerCommandIfMissing ?? true) { this.debug(`Creating new global ${type} command with name "${commandName}"`); await this.createMissingCommand(commandsManager, builtData, type); } else this.debug(`Doing nothing about missing global ${type} command with name "${commandName}"`); return; } for (const guildId of registerOptions.guildIds) { const guildCommands = allGuildsCommands.get(guildId); if (!guildCommands) { this.debug(`There are no commands for guild with id "${guildId}". Will create ${type} command "${commandName}".`); await this.createMissingCommand(commandsManager, builtData, type, guildId); continue; } const existingGuildCommand = guildCommands.find(findCallback); if (existingGuildCommand) { this.debug(`Checking if guild ${type} command "${commandName}" is identical to command "${existingGuildCommand.id}"`); this.handleIdAddition(apiCall.type, existingGuildCommand.id, guildId); await this.handleCommandPresent(existingGuildCommand, builtData, behaviorIfNotEqual, guildId); } else if (registerOptions.registerCommandIfMissing ?? true) { this.debug(`Creating new guild ${type} command with name "${commandName}" for guild "${guildId}"`); await this.createMissingCommand(commandsManager, builtData, type, guildId); } else this.debug(`Doing nothing about missing guild ${type} command with name "${commandName}" for guild "${guildId}"`); } } async handleCommandPresent(applicationCommand, apiData, behaviorIfNotEqual, guildId) { if (behaviorIfNotEqual === require_lib_types_Enums.RegisterBehavior.BulkOverwrite) { this.debug(`Command "${this.commandName}" has the behaviorIfNotEqual set to "BulkOverwrite" which is invalid. Using defaultBehaviorWhenNotIdentical instead`); behaviorIfNotEqual = require_lib_utils_application_commands_ApplicationCommandRegistries.getDefaultBehaviorWhenNotIdentical(); if (behaviorIfNotEqual === require_lib_types_Enums.RegisterBehavior.BulkOverwrite) throw new Error(`Invalid behaviorIfNotEqual value ("BulkOverwrite") for command "${this.commandName}", and defaultBehaviorWhenNotIdentical is also "BulkOverwrite". This should not happen.`); } let differences = []; if (behaviorIfNotEqual === require_lib_types_Enums.RegisterBehavior.VerboseOverwrite) { const now = Date.now(); differences = [...require_lib_utils_application_commands_computeDifferences.getCommandDifferences(require_lib_utils_application_commands_normalizeInputs.convertApplicationCommandToApiData(applicationCommand), apiData, guildId !== null)]; const later = Date.now() - now; this.debug(`Took ${later}ms to process differences via computing differences`); if (!differences.length) { this.debug(`${guildId ? "Guild command" : "Command"} "${apiData.name}" is identical to command "${applicationCommand.name}" (${applicationCommand.id})`); return; } } if (behaviorIfNotEqual === require_lib_types_Enums.RegisterBehavior.Overwrite || behaviorIfNotEqual === require_lib_types_Enums.RegisterBehavior.LogToConsole) { const now = Date.now(); const areThereDifferences = require_lib_utils_application_commands_computeDifferences.getCommandDifferencesFast(require_lib_utils_application_commands_normalizeInputs.convertApplicationCommandToApiData(applicationCommand), apiData, guildId !== null); const later = Date.now() - now; this.debug(`Took ${later}ms to process differences via fast compute differences`); if (!areThereDifferences) { this.debug(`${guildId ? "Guild command" : "Command"} "${apiData.name}" is identical to command "${applicationCommand.name}" (${applicationCommand.id})`); return; } } this.logCommandDifferencesFound(applicationCommand, behaviorIfNotEqual === require_lib_types_Enums.RegisterBehavior.LogToConsole, differences); if (behaviorIfNotEqual === require_lib_types_Enums.RegisterBehavior.LogToConsole) return; try { await applicationCommand.edit(apiData); this.debug(`Updated command ${applicationCommand.name} (${applicationCommand.id}) with new api data`); } catch (error) { this.error(`Failed to update command ${applicationCommand.name} (${applicationCommand.id})`, error); } } logCommandDifferencesFound(applicationCommand, logAsWarn, differences) { const finalMessage = []; const pad = " ".repeat(5); for (const difference of differences) finalMessage.push([ `└── At path: ${difference.key}`, `${pad}├── Received: ${difference.original}`, `${pad}└── Expected: ${difference.expected}`, "" ].join("\n")); const finalMessageNewLine = finalMessage.length ? "\n" : ""; const header = `Found differences for command "${applicationCommand.name}" (${applicationCommand.id}) versus provided api data.${finalMessageNewLine}`; logAsWarn ? this.warn(header, ...finalMessage) : this.debug(header, ...finalMessage); } async createMissingCommand(commandsManager, apiData, type, guildId) { try { const result = await commandsManager.create(apiData, guildId); this.info(`Successfully created ${type}${guildId ? " guild" : ""} command "${apiData.name}" with id "${result.id}". You should add the id to the "idHints" property of the register method you used!`); switch (apiData.type) { case void 0: case discord_api_types_v10.ApplicationCommandType.ChatInput: this.handleIdAddition(require_lib_types_Enums.InternalRegistryAPIType.ChatInput, result.id, guildId); break; case discord_api_types_v10.ApplicationCommandType.Message: case discord_api_types_v10.ApplicationCommandType.User: this.handleIdAddition(require_lib_types_Enums.InternalRegistryAPIType.ContextMenu, result.id, guildId); break; } } catch (err) { this.error(`Failed to register${guildId ? " guild" : ""} application command with name "${apiData.name}"${guildId ? ` for guild "${guildId}"` : ""}`, err); } } info(message, ...other) { __sapphire_pieces.container.logger.info(`ApplicationCommandRegistry[${this.commandName}] ${message}`, ...other); } error(message, ...other) { __sapphire_pieces.container.logger.error(`ApplicationCommandRegistry[${this.commandName}] ${message}`, ...other); } warn(message, ...other) { __sapphire_pieces.container.logger.warn(`ApplicationCommandRegistry[${this.commandName}] ${message}`, ...other); } debug(message, ...other) { __sapphire_pieces.container.logger.debug(`ApplicationCommandRegistry[${this.commandName}] ${message}`, ...other); } trace(message, ...other) { __sapphire_pieces.container.logger.trace(`ApplicationCommandRegistry[${this.commandName}] ${message}`, ...other); } }; //#endregion exports.ApplicationCommandRegistry = ApplicationCommandRegistry; //# sourceMappingURL=ApplicationCommandRegistry.cjs.map