UNPKG

@sapphire/framework

Version:

Discord bot framework built for advanced and amazing bots.

1 lines 31.9 kB
{"version":3,"file":"ApplicationCommandRegistry.cjs","names":["Collection","container","normalizeChatInputCommand","getDefaultBehaviorWhenNotIdentical","InternalRegistryAPIType","normalizeContextMenuCommand","RegisterBehavior","guildIdsToRegister: ApplicationCommandRegistry.RegisterOptions['guildIds']","getDefaultGuildIds","ApplicationCommandType","type: string","differences: CommandDifference[]","getCommandDifferences","convertApplicationCommandToApiData","getCommandDifferencesFast","finalMessage: string[]"],"sources":["../../../../../src/lib/utils/application-commands/ApplicationCommandRegistry.ts"],"sourcesContent":["import type {\n\tContextMenuCommandBuilder,\n\tSlashCommandBuilder,\n\tSlashCommandOptionsOnlyBuilder,\n\tSlashCommandSubcommandsOnlyBuilder\n} from '@discordjs/builders';\nimport { container } from '@sapphire/pieces';\nimport { isNullishOrEmpty } from '@sapphire/utilities';\nimport {\n\tApplicationCommandType,\n\ttype RESTPostAPIChatInputApplicationCommandsJSONBody,\n\ttype RESTPostAPIContextMenuApplicationCommandsJSONBody\n} from 'discord-api-types/v10';\nimport {\n\tCollection,\n\ttype ApplicationCommand,\n\ttype ApplicationCommandManager,\n\ttype ChatInputApplicationCommandData,\n\ttype MessageApplicationCommandData,\n\ttype UserApplicationCommandData\n} from 'discord.js';\nimport type { Args } from '../../parsers/Args';\nimport type { Command } from '../../structures/Command';\nimport type { CommandOptions } from '../../types/CommandTypes';\nimport { InternalRegistryAPIType, RegisterBehavior } from '../../types/Enums';\nimport { allGuildIdsToFetchCommandsFor, getDefaultBehaviorWhenNotIdentical, getDefaultGuildIds } from './ApplicationCommandRegistries';\nimport type { CommandDifference } from './compute-differences/_shared';\nimport { getCommandDifferences, getCommandDifferencesFast } from './computeDifferences';\nimport { convertApplicationCommandToApiData, normalizeChatInputCommand, normalizeContextMenuCommand } from './normalizeInputs';\n\nexport class ApplicationCommandRegistry {\n\t/**\n\t * The piece this registry is for.\n\t */\n\tpublic readonly commandName: string;\n\n\t/**\n\t * A set of all chat input command names and ids that point to this registry.\n\t * You should not use this field directly, but instead use {@link ApplicationCommandRegistry.globalChatInputCommandIds}\n\t */\n\tpublic readonly chatInputCommands = new Set<string>();\n\n\t/**\n\t * A set of all context menu command names and ids that point to this registry.\n\t * You should not use this field directly, but instead use {@link ApplicationCommandRegistry.globalContextMenuCommandIds}\n\t */\n\tpublic readonly contextMenuCommands = new Set<string>();\n\n\t/**\n\t * The guild ids that we need to fetch the commands for.\n\t */\n\tpublic readonly guildIdsToFetch = new Set<string>();\n\n\t/**\n\t * The global slash command id for this command.\n\t * @deprecated This field will only show the first global command id registered for this registry.\n\t * Use {@link ApplicationCommandRegistry.globalChatInputCommandIds} instead.\n\t */\n\tpublic globalCommandId: string | null = null;\n\n\t/**\n\t * A set of all registered and valid global chat input command ids that point to this registry.\n\t */\n\tpublic readonly globalChatInputCommandIds = new Set<string>();\n\n\t/**\n\t * A set of all registered and valid global context menu command ids that point to this registry.\n\t */\n\tpublic readonly globalContextMenuCommandIds = new Set<string>();\n\n\t/**\n\t * The guild command ids for this command.\n\t * @deprecated This field will only show the first guild command id registered for this registry per guild.\n\t * Use {@link ApplicationCommandRegistry.guildIdToChatInputCommandIds} and {@link ApplicationCommandRegistry.guildIdToContextMenuCommandIds} instead.\n\t */\n\tpublic readonly guildCommandIds = new Collection<string, string>();\n\n\t/**\n\t * A map of guild ids to a set of registered and valid chat input command ids that point to this registry.\n\t */\n\tpublic readonly guildIdToChatInputCommandIds = new Collection<string, Set<string>>();\n\n\t/**\n\t * A map of guild ids to a set of registered and valid context menu command ids that point to this registry.\n\t */\n\tpublic readonly guildIdToContextMenuCommandIds = new Collection<string, Set<string>>();\n\n\tprivate readonly apiCalls: InternalAPICall[] = [];\n\n\tpublic constructor(commandName: string) {\n\t\tthis.commandName = commandName;\n\t}\n\n\tpublic get command(): Command<Args, CommandOptions> | undefined {\n\t\treturn container.stores.get('commands').get(this.commandName);\n\t}\n\n\tpublic registerChatInputCommand(\n\t\tcommand:\n\t\t\t| ChatInputApplicationCommandData\n\t\t\t| SlashCommandBuilder\n\t\t\t| SlashCommandSubcommandsOnlyBuilder\n\t\t\t| SlashCommandOptionsOnlyBuilder\n\t\t\t| Omit<SlashCommandBuilder, 'addSubcommand' | 'addSubcommandGroup'>\n\t\t\t| ((builder: SlashCommandBuilder) => unknown),\n\t\toptions?: ApplicationCommandRegistryRegisterOptions\n\t) {\n\t\tconst builtData = normalizeChatInputCommand(command);\n\n\t\tthis.chatInputCommands.add(builtData.name);\n\n\t\tconst guildIdsToRegister = this.getGuildIdsToRegister(options);\n\n\t\tconst registerOptions = {\n\t\t\tregisterCommandIfMissing: true,\n\t\t\tbehaviorWhenNotIdentical: getDefaultBehaviorWhenNotIdentical(),\n\t\t\tguildIds: guildIdsToRegister,\n\t\t\t...(options ?? {})\n\t\t};\n\n\t\tthis.apiCalls.push({\n\t\t\tbuiltData,\n\t\t\tregisterOptions,\n\t\t\ttype: InternalRegistryAPIType.ChatInput\n\t\t});\n\n\t\tif (options?.idHints) {\n\t\t\tfor (const hint of options.idHints) {\n\t\t\t\tthis.chatInputCommands.add(hint);\n\t\t\t}\n\t\t}\n\n\t\tthis.processGuildIds(guildIdsToRegister);\n\n\t\treturn this;\n\t}\n\n\tpublic registerContextMenuCommand(\n\t\tcommand:\n\t\t\t| UserApplicationCommandData\n\t\t\t| MessageApplicationCommandData\n\t\t\t| ContextMenuCommandBuilder\n\t\t\t| ((builder: ContextMenuCommandBuilder) => unknown),\n\t\toptions?: ApplicationCommandRegistryRegisterOptions\n\t) {\n\t\tconst builtData = normalizeContextMenuCommand(command);\n\n\t\tthis.contextMenuCommands.add(builtData.name);\n\n\t\tconst guildIdsToRegister = this.getGuildIdsToRegister(options);\n\n\t\tconst registerOptions = {\n\t\t\tregisterCommandIfMissing: true,\n\t\t\tbehaviorWhenNotIdentical: getDefaultBehaviorWhenNotIdentical(),\n\t\t\tguildIds: guildIdsToRegister,\n\t\t\t...(options ?? {})\n\t\t};\n\n\t\tthis.apiCalls.push({\n\t\t\tbuiltData,\n\t\t\tregisterOptions,\n\t\t\ttype: InternalRegistryAPIType.ContextMenu\n\t\t});\n\n\t\tif (options?.idHints) {\n\t\t\tfor (const hint of options.idHints) {\n\t\t\t\tthis.contextMenuCommands.add(hint);\n\t\t\t}\n\t\t}\n\n\t\tthis.processGuildIds(guildIdsToRegister);\n\n\t\treturn this;\n\t}\n\n\tpublic addChatInputCommandNames(...names: string[] | string[][]) {\n\t\tconst flattened = names.flat(Infinity) as string[];\n\n\t\tfor (const command of flattened) {\n\t\t\tthis.debug(`Registering name \"${command}\" to internal chat input map`);\n\t\t\tthis.warn(\n\t\t\t\t`Registering the chat input command \"${command}\" using a name is not recommended.`,\n\t\t\t\t'Please use the \"addChatInputCommandIds\" method instead with a command id.'\n\t\t\t);\n\t\t\tthis.chatInputCommands.add(command);\n\t\t}\n\n\t\treturn this;\n\t}\n\n\tpublic addContextMenuCommandNames(...names: string[] | string[][]) {\n\t\tconst flattened = names.flat(Infinity) as string[];\n\n\t\tfor (const command of flattened) {\n\t\t\tthis.debug(`Registering name \"${command}\" to internal context menu map`);\n\t\t\tthis.warn(\n\t\t\t\t`Registering the context menu command \"${command}\" using a name is not recommended.`,\n\t\t\t\t'Please use the \"addContextMenuCommandIds\" method instead with a command id.'\n\t\t\t);\n\t\t\tthis.contextMenuCommands.add(command);\n\t\t}\n\n\t\treturn this;\n\t}\n\n\tpublic addChatInputCommandIds(...commandIds: string[] | string[][]) {\n\t\tconst flattened = commandIds.flat(Infinity) as string[];\n\n\t\tfor (const entry of flattened) {\n\t\t\ttry {\n\t\t\t\tBigInt(entry);\n\t\t\t\tthis.debug(`Registering id \"${entry}\" to internal chat input map`);\n\t\t\t} catch {\n\t\t\t\t// Don't be silly, save yourself the headaches and do as we say\n\t\t\t\tthis.debug(`Registering name \"${entry}\" to internal chat input map`);\n\t\t\t\tthis.warn(\n\t\t\t\t\t`Registering the chat input command \"${entry}\" using a name *and* trying to bypass this warning by calling \"addChatInputCommandIds\" is not recommended.`,\n\t\t\t\t\t'Please use the \"addChatInputCommandIds\" method with a valid command id instead.'\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.chatInputCommands.add(entry);\n\t\t}\n\n\t\treturn this;\n\t}\n\n\tpublic addContextMenuCommandIds(...commandIds: string[] | string[][]) {\n\t\tconst flattened = commandIds.flat(Infinity) as string[];\n\n\t\tfor (const entry of flattened) {\n\t\t\ttry {\n\t\t\t\tBigInt(entry);\n\t\t\t\tthis.debug(`Registering id \"${entry}\" to internal context menu map`);\n\t\t\t} catch {\n\t\t\t\tthis.debug(`Registering name \"${entry}\" to internal context menu map`);\n\t\t\t\t// Don't be silly, save yourself the headaches and do as we say\n\t\t\t\tthis.warn(\n\t\t\t\t\t`Registering the context menu command \"${entry}\" using a name *and* trying to bypass this warning by calling \"addContextMenuCommandIds\" is not recommended.`,\n\t\t\t\t\t'Please use the \"addContextMenuCommandIds\" method with a valid command id instead.'\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.contextMenuCommands.add(entry);\n\t\t}\n\n\t\treturn this;\n\t}\n\n\tprotected async runAPICalls(\n\t\tapplicationCommands: ApplicationCommandManager,\n\t\tglobalCommands: Collection<string, ApplicationCommand>,\n\t\tguildCommands: Map<string, Collection<string, ApplicationCommand>>\n\t) {\n\t\t// Early return for no API calls\n\t\tif (this.apiCalls.length === 0) {\n\t\t\t// If we have no API calls to do then we simply return (can happen if the registry is used directly)\n\t\t\tthis.trace('No API calls to run, and no command to register');\n\n\t\t\treturn;\n\t\t}\n\n\t\tif (getDefaultBehaviorWhenNotIdentical() === RegisterBehavior.BulkOverwrite) {\n\t\t\tthrow new RangeError(\n\t\t\t\t`\"runAPICalls\" was called for \"${this.commandName}\" but the defaultBehaviorWhenNotIdentical is \"BulkOverwrite\". This should not happen.`\n\t\t\t);\n\t\t}\n\n\t\tthis.debug(`Preparing to process ${this.apiCalls.length} possible command registrations / updates...`);\n\n\t\tconst results = await Promise.allSettled(\n\t\t\tthis.apiCalls.map((call) => this.handleAPICall(applicationCommands, globalCommands, guildCommands, call))\n\t\t);\n\n\t\tconst errored = results.filter((result) => result.status === 'rejected') as PromiseRejectedResult[];\n\n\t\tif (errored.length) {\n\t\t\tthis.error(`Received ${errored.length} errors while processing command registrations / updates`);\n\n\t\t\tfor (const error of errored) {\n\t\t\t\tthis.error(error.reason.stack ?? error.reason);\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected handleIdAddition(type: InternalRegistryAPIType, id: string, guildId?: string | null) {\n\t\tswitch (type) {\n\t\t\tcase InternalRegistryAPIType.ChatInput: {\n\t\t\t\tthis.addChatInputCommandIds(id);\n\n\t\t\t\tif (guildId) {\n\t\t\t\t\tthis.guildIdToChatInputCommandIds.ensure(guildId, () => new Set()).add(id);\n\t\t\t\t} else {\n\t\t\t\t\tthis.globalChatInputCommandIds.add(id);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase InternalRegistryAPIType.ContextMenu: {\n\t\t\t\tthis.addContextMenuCommandIds(id);\n\n\t\t\t\tif (guildId) {\n\t\t\t\t\tthis.guildIdToContextMenuCommandIds.ensure(guildId, () => new Set()).add(id);\n\t\t\t\t} else {\n\t\t\t\t\tthis.globalContextMenuCommandIds.add(id);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// Old field handling\n\t\tif (guildId) {\n\t\t\t// Old, wrongly typed field (thx kyra for spotting >_>)\n\t\t\tif (!this.guildCommandIds.has(guildId)) {\n\t\t\t\tthis.guildCommandIds.set(guildId, id);\n\t\t\t}\n\t\t} else {\n\t\t\t// First come, first serve (thx kyra for spotting >_>)\n\t\t\tthis.globalCommandId ??= id;\n\t\t}\n\t}\n\n\tprivate getGuildIdsToRegister(options?: ApplicationCommandRegistryRegisterOptions) {\n\t\tlet guildIdsToRegister: ApplicationCommandRegistry.RegisterOptions['guildIds'] = undefined;\n\n\t\tif (!isNullishOrEmpty(options?.guildIds)) {\n\t\t\tguildIdsToRegister = options!.guildIds;\n\t\t} else if (!isNullishOrEmpty(getDefaultGuildIds())) {\n\t\t\tguildIdsToRegister = getDefaultGuildIds();\n\t\t}\n\n\t\treturn guildIdsToRegister;\n\t}\n\n\tprivate processGuildIds(guildIdsToRegister: ApplicationCommandRegistry.RegisterOptions['guildIds']) {\n\t\tif (!isNullishOrEmpty(guildIdsToRegister)) {\n\t\t\tfor (const id of guildIdsToRegister) {\n\t\t\t\tthis.guildIdsToFetch.add(id);\n\t\t\t\tallGuildIdsToFetchCommandsFor.add(id);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async handleAPICall(\n\t\tcommandsManager: ApplicationCommandManager,\n\t\tglobalCommands: Collection<string, ApplicationCommand>,\n\t\tallGuildsCommands: Map<string, Collection<string, ApplicationCommand>>,\n\t\tapiCall: InternalAPICall\n\t) {\n\t\tconst { builtData, registerOptions } = apiCall;\n\t\tconst commandName = builtData.name;\n\t\tconst behaviorIfNotEqual = registerOptions.behaviorWhenNotIdentical ?? getDefaultBehaviorWhenNotIdentical();\n\n\t\tconst findCallback = (entry: ApplicationCommand) => {\n\t\t\t// If the command is a chat input command, we need to check if the entry is a chat input command\n\t\t\tif (apiCall.type === InternalRegistryAPIType.ChatInput && entry.type !== ApplicationCommandType.ChatInput) return false;\n\t\t\t// If the command is a context menu command, we need to check if the entry is a context menu command of the same type\n\t\t\tif (apiCall.type === InternalRegistryAPIType.ContextMenu) {\n\t\t\t\t// If its a chat input command, it doesn't match\n\t\t\t\tif (entry.type === ApplicationCommandType.ChatInput) return false;\n\t\t\t\t// Check the command type (must match)\n\t\t\t\tif (apiCall.builtData.type !== entry.type) return false;\n\t\t\t}\n\n\t\t\t// Find the command by name or by id hint (mostly useful for context menus)\n\t\t\tconst isInIdHint = registerOptions.idHints?.includes(entry.id);\n\t\t\treturn typeof isInIdHint === 'boolean' ? isInIdHint || entry.name === commandName : entry.name === commandName;\n\t\t};\n\n\t\tlet type: string;\n\n\t\tswitch (apiCall.type) {\n\t\t\tcase InternalRegistryAPIType.ChatInput:\n\t\t\t\ttype = 'chat input';\n\t\t\t\tbreak;\n\t\t\tcase InternalRegistryAPIType.ContextMenu:\n\t\t\t\tswitch (apiCall.builtData.type) {\n\t\t\t\t\tcase ApplicationCommandType.Message:\n\t\t\t\t\t\ttype = 'message context menu';\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ApplicationCommandType.User:\n\t\t\t\t\t\ttype = 'user context menu';\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\ttype = 'unknown-type context menu';\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttype = 'unknown';\n\t\t}\n\n\t\tif (!registerOptions.guildIds?.length) {\n\t\t\tconst globalCommand = globalCommands.find(findCallback);\n\n\t\t\tif (globalCommand) {\n\t\t\t\tthis.debug(`Checking if command \"${commandName}\" is identical with global ${type} command with id \"${globalCommand.id}\"`);\n\t\t\t\tthis.handleIdAddition(apiCall.type, globalCommand.id);\n\t\t\t\tawait this.handleCommandPresent(globalCommand, builtData, behaviorIfNotEqual, null);\n\t\t\t} else if (registerOptions.registerCommandIfMissing ?? true) {\n\t\t\t\tthis.debug(`Creating new global ${type} command with name \"${commandName}\"`);\n\t\t\t\tawait this.createMissingCommand(commandsManager, builtData, type);\n\t\t\t} else {\n\t\t\t\tthis.debug(`Doing nothing about missing global ${type} command with name \"${commandName}\"`);\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\tfor (const guildId of registerOptions.guildIds) {\n\t\t\tconst guildCommands = allGuildsCommands.get(guildId);\n\n\t\t\tif (!guildCommands) {\n\t\t\t\tthis.debug(`There are no commands for guild with id \"${guildId}\". Will create ${type} command \"${commandName}\".`);\n\t\t\t\tawait this.createMissingCommand(commandsManager, builtData, type, guildId);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst existingGuildCommand = guildCommands.find(findCallback);\n\n\t\t\tif (existingGuildCommand) {\n\t\t\t\tthis.debug(`Checking if guild ${type} command \"${commandName}\" is identical to command \"${existingGuildCommand.id}\"`);\n\t\t\t\tthis.handleIdAddition(apiCall.type, existingGuildCommand.id, guildId);\n\t\t\t\tawait this.handleCommandPresent(existingGuildCommand, builtData, behaviorIfNotEqual, guildId);\n\t\t\t} else if (registerOptions.registerCommandIfMissing ?? true) {\n\t\t\t\tthis.debug(`Creating new guild ${type} command with name \"${commandName}\" for guild \"${guildId}\"`);\n\t\t\t\tawait this.createMissingCommand(commandsManager, builtData, type, guildId);\n\t\t\t} else {\n\t\t\t\tthis.debug(`Doing nothing about missing guild ${type} command with name \"${commandName}\" for guild \"${guildId}\"`);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async handleCommandPresent(\n\t\tapplicationCommand: ApplicationCommand,\n\t\tapiData: InternalAPICall['builtData'],\n\t\tbehaviorIfNotEqual: RegisterBehavior,\n\t\tguildId: string | null\n\t) {\n\t\tif (behaviorIfNotEqual === RegisterBehavior.BulkOverwrite) {\n\t\t\tthis.debug(\n\t\t\t\t`Command \"${this.commandName}\" has the behaviorIfNotEqual set to \"BulkOverwrite\" which is invalid. Using defaultBehaviorWhenNotIdentical instead`\n\t\t\t);\n\n\t\t\tbehaviorIfNotEqual = getDefaultBehaviorWhenNotIdentical();\n\n\t\t\tif (behaviorIfNotEqual === RegisterBehavior.BulkOverwrite) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid behaviorIfNotEqual value (\"BulkOverwrite\") for command \"${this.commandName}\", and defaultBehaviorWhenNotIdentical is also \"BulkOverwrite\". This should not happen.`\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tlet differences: CommandDifference[] = [];\n\n\t\tif (behaviorIfNotEqual === RegisterBehavior.VerboseOverwrite) {\n\t\t\tconst now = Date.now();\n\n\t\t\t// Step 0: compute differences\n\t\t\tdifferences = [...getCommandDifferences(convertApplicationCommandToApiData(applicationCommand), apiData, guildId !== null)];\n\n\t\t\tconst later = Date.now() - now;\n\t\t\tthis.debug(`Took ${later}ms to process differences via computing differences`);\n\n\t\t\t// Step 1: if there are no differences, return\n\t\t\tif (!differences.length) {\n\t\t\t\tthis.debug(\n\t\t\t\t\t`${guildId ? 'Guild command' : 'Command'} \"${apiData.name}\" is identical to command \"${applicationCommand.name}\" (${\n\t\t\t\t\t\tapplicationCommand.id\n\t\t\t\t\t})`\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// Run the fast path even if the user wants to just log if the command has a difference\n\t\tif (behaviorIfNotEqual === RegisterBehavior.Overwrite || behaviorIfNotEqual === RegisterBehavior.LogToConsole) {\n\t\t\tconst now = Date.now();\n\n\t\t\t// Step 0: compute differences\n\t\t\tconst areThereDifferences = getCommandDifferencesFast(convertApplicationCommandToApiData(applicationCommand), apiData, guildId !== null);\n\n\t\t\tconst later = Date.now() - now;\n\t\t\tthis.debug(`Took ${later}ms to process differences via fast compute differences`);\n\n\t\t\t// Step 1: if there are no differences, return\n\t\t\tif (!areThereDifferences) {\n\t\t\t\tthis.debug(\n\t\t\t\t\t`${guildId ? 'Guild command' : 'Command'} \"${apiData.name}\" is identical to command \"${applicationCommand.name}\" (${\n\t\t\t\t\t\tapplicationCommand.id\n\t\t\t\t\t})`\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tthis.logCommandDifferencesFound(applicationCommand, behaviorIfNotEqual === RegisterBehavior.LogToConsole, differences);\n\n\t\t// Step 2: if the behavior is to log to console, only log the differences\n\t\tif (behaviorIfNotEqual === RegisterBehavior.LogToConsole) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Step 3: if the behavior is to update, update the command\n\t\ttry {\n\t\t\tawait applicationCommand.edit(apiData as ChatInputApplicationCommandData);\n\t\t\tthis.debug(`Updated command ${applicationCommand.name} (${applicationCommand.id}) with new api data`);\n\t\t} catch (error) {\n\t\t\tthis.error(`Failed to update command ${applicationCommand.name} (${applicationCommand.id})`, error);\n\t\t}\n\t}\n\n\tprivate logCommandDifferencesFound(applicationCommand: ApplicationCommand, logAsWarn: boolean, differences: CommandDifference[]) {\n\t\tconst finalMessage: string[] = [];\n\t\tconst pad = ' '.repeat(5);\n\n\t\tfor (const difference of differences) {\n\t\t\tfinalMessage.push(\n\t\t\t\t[\n\t\t\t\t\t`└── At path: ${difference.key}`, //\n\t\t\t\t\t`${pad}├── Received: ${difference.original}`,\n\t\t\t\t\t`${pad}└── Expected: ${difference.expected}`,\n\t\t\t\t\t''\n\t\t\t\t].join('\\n')\n\t\t\t);\n\t\t}\n\n\t\tconst finalMessageNewLine = finalMessage.length ? '\\n' : '';\n\t\tconst header = `Found differences for command \"${applicationCommand.name}\" (${applicationCommand.id}) versus provided api data.${finalMessageNewLine}`;\n\n\t\tlogAsWarn ? this.warn(header, ...finalMessage) : this.debug(header, ...finalMessage);\n\t}\n\n\tprivate async createMissingCommand(\n\t\tcommandsManager: ApplicationCommandManager,\n\t\tapiData: InternalAPICall['builtData'],\n\t\ttype: string,\n\t\tguildId?: string\n\t) {\n\t\ttry {\n\t\t\tconst result = await commandsManager.create(apiData, guildId);\n\n\t\t\tthis.info(\n\t\t\t\t`Successfully created ${type}${guildId ? ' guild' : ''} command \"${apiData.name}\" with id \"${\n\t\t\t\t\tresult.id\n\t\t\t\t}\". You should add the id to the \"idHints\" property of the register method you used!`\n\t\t\t);\n\n\t\t\tswitch (apiData.type) {\n\t\t\t\tcase undefined:\n\t\t\t\tcase ApplicationCommandType.ChatInput: {\n\t\t\t\t\tthis.handleIdAddition(InternalRegistryAPIType.ChatInput, result.id, guildId);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase ApplicationCommandType.Message:\n\t\t\t\tcase ApplicationCommandType.User: {\n\t\t\t\t\tthis.handleIdAddition(InternalRegistryAPIType.ContextMenu, result.id, guildId);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tthis.error(\n\t\t\t\t`Failed to register${guildId ? ' guild' : ''} application command with name \"${apiData.name}\"${\n\t\t\t\t\tguildId ? ` for guild \"${guildId}\"` : ''\n\t\t\t\t}`,\n\t\t\t\terr\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate info(message: string, ...other: unknown[]) {\n\t\tcontainer.logger.info(`ApplicationCommandRegistry[${this.commandName}] ${message}`, ...other);\n\t}\n\n\tprivate error(message: string, ...other: unknown[]) {\n\t\tcontainer.logger.error(`ApplicationCommandRegistry[${this.commandName}] ${message}`, ...other);\n\t}\n\n\tprivate warn(message: string, ...other: unknown[]) {\n\t\tcontainer.logger.warn(`ApplicationCommandRegistry[${this.commandName}] ${message}`, ...other);\n\t}\n\n\tprivate debug(message: string, ...other: unknown[]) {\n\t\tcontainer.logger.debug(`ApplicationCommandRegistry[${this.commandName}] ${message}`, ...other);\n\t}\n\n\tprivate trace(message: string, ...other: unknown[]) {\n\t\tcontainer.logger.trace(`ApplicationCommandRegistry[${this.commandName}] ${message}`, ...other);\n\t}\n}\n\nexport namespace ApplicationCommandRegistry {\n\texport interface RegisterOptions {\n\t\t/**\n\t\t * If this is specified, the application commands will only be registered for these guild ids.\n\t\t */\n\t\tguildIds?: string[];\n\t\t/**\n\t\t * If we should register the command when it is missing\n\t\t * @default true\n\t\t */\n\t\tregisterCommandIfMissing?: boolean;\n\t\t/**\n\t\t * Specifies what we should do when the command is present, but not identical with the data you provided\n\t\t * @default `ApplicationCommandRegistries.getDefaultBehaviorWhenNotIdentical()`\n\t\t */\n\t\tbehaviorWhenNotIdentical?: Exclude<RegisterBehavior, RegisterBehavior.BulkOverwrite>;\n\t\t/**\n\t\t * Specifies a list of command ids that we should check in the event of a name mismatch\n\t\t * @default []\n\t\t */\n\t\tidHints?: string[];\n\t}\n}\n\nexport type ApplicationCommandRegistryRegisterOptions = ApplicationCommandRegistry.RegisterOptions;\n\ntype InternalRegisterOptions = Omit<ApplicationCommandRegistry.RegisterOptions, 'behaviorWhenNotIdentical'> & {\n\tbehaviorWhenNotIdentical?: RegisterBehavior;\n};\n\nexport type InternalAPICall =\n\t| {\n\t\t\tbuiltData: RESTPostAPIChatInputApplicationCommandsJSONBody;\n\t\t\tregisterOptions: InternalRegisterOptions;\n\t\t\ttype: InternalRegistryAPIType.ChatInput;\n\t }\n\t| {\n\t\t\tbuiltData: RESTPostAPIContextMenuApplicationCommandsJSONBody;\n\t\t\tregisterOptions: InternalRegisterOptions;\n\t\t\ttype: InternalRegistryAPIType.ContextMenu;\n\t };\n"],"mappings":"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"}