@sapphire/framework
Version:
Discord bot framework built for advanced and amazing bots.
302 lines (300 loc) • 16.5 kB
JavaScript
"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