diff --git a/src/errors/Messages.js b/src/errors/Messages.js index 70f7cde..617dcc2 100644 --- a/src/errors/Messages.js +++ b/src/errors/Messages.js @@ -165,6 +165,9 @@ const Messages = { INVALID_BOT_METHOD: 'Bot accounts cannot use this method', INVALID_USER_METHOD: 'User accounts cannot use this method', + + INTERACTION_SEND_FAILURE: msg => `${msg}`, + INVALID_LOCALE: 'Unable to select this location', FOLDER_NOT_FOUND: 'Server directory not found', FOLDER_POSITION_INVALID: 'The server index in the directory is invalid', diff --git a/src/structures/Message.js b/src/structures/Message.js index 8175c79..08daf38 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -1041,8 +1041,8 @@ class Message extends Base { } /** * Select specific menu or First Menu - * @param {string|Array} menuID Select Menu specific id or auto select first Menu - * @param {Array} options Menu Options + * @param {string|Array} menuID Select Menu specific id or auto select first Menu + * @param {Array} options Menu Options */ async selectMenu(menuID, options = []) { if (!this.components[0]) throw new TypeError('MESSAGE_NO_COMPONENTS'); @@ -1074,37 +1074,54 @@ class Message extends Base { // /** * Send context Menu v2 - * @param {DiscordBotID} botID Bot id - * @param {string} commandName Command name in Context Menu - * @returns {Promise} + * @param {Snowflake} botId Bot id + * @param {string} commandName Command name in Context Menu + * @param {boolean} [search=true] Search for command + * @returns {Promise} */ - async contextMenu(botID, commandName) { - if (!botID) throw new Error('Bot ID is required'); - const user = await this.client.users.fetch(botID).catch(() => {}); + async contextMenu(botId, commandName, search = true) { + if (!botId) throw new Error('Bot ID is required'); + const user = await this.client.users.fetch(botId).catch(() => {}); if (!user || !user.bot || !user.applications) { throw new Error('BotID is not a bot or does not have an application slash command'); } if (!commandName || typeof commandName !== 'string') { throw new Error('Command name is required'); } - const listApplication = - user.applications.cache.size == 0 ? await user.applications.fetch() : user.applications.cache; - let contextCMD; - await Promise.all( - listApplication.map(application => { - if (commandName == application.name && application.type !== 'CHAT_INPUT') { - contextCMD = application; - } - return true; - }), - ); - if (!contextCMD) { + // https://discord.com/api/v9/channels/817671035813888030/application-commands/search?type=3&application_id=817229550684471297 + let contextCMD = user.applications.cache.find(c => c.name == commandName && c.type === 'MESSAGE'); + if (!contextCMD && !search) { throw new Error( - `Command ${commandName} is not found\nList command avalible: ${listApplication - .filter(a => a.type !== 'CHAT_INPUT') + 'INTERACTION_SEND_FAILURE', + `Command ${commandName} is not found (without search)\nList command avalible: ${user.applications.cache + .filter(a => a.type == 'MESSAGE') .map(a => a.name) .join(', ')}`, ); + } else { + const data = await this.client.api.channels[this.channelId]['application-commands'].search.get({ + query: { + type: 3, // MESSAGE, + // include_applications: false, + // query: commandName, + limit: 25, + // Shet + application_id: botId, + }, + }); + for (const command of data.application_commands) { + user.applications._add(command, true); + } + contextCMD = user.applications.cache.find(c => c.name == commandName && c.type === 'MESSAGE'); + if (!contextCMD) { + throw new Error( + 'INTERACTION_SEND_FAILURE', + `Command ${commandName} is not found (with search)\nList command avalible: ${user.applications.cache + .filter(a => a.type == 'MESSAGE') + .map(a => a.name) + .join(', ')}`, + ); + } } return contextCMD.sendContextMenu(this, true); } diff --git a/src/structures/interfaces/TextBasedChannel.js b/src/structures/interfaces/TextBasedChannel.js index dcb5e1c..085ee86 100644 --- a/src/structures/interfaces/TextBasedChannel.js +++ b/src/structures/interfaces/TextBasedChannel.js @@ -391,22 +391,56 @@ class TextBasedChannel { /** * Send Slash to this channel - * @param {Snowflake} botId Bot Id + * @param {Snowflake} botId Bot Id (Supports application ID - not bot) * @param {string} commandName Command name + * @param {boolean} [search=true] Whether to search for the command * @param {...?string} args Command arguments * @returns {Promise} */ - async sendSlash(botId, commandName, ...args) { - // If (!this.isText()) throw new Error('This channel is not text-based.'); + async sendSlash(botId, commandName, search = true, ...args) { if (!botId) throw new Error('Bot ID is required'); + // ? maybe ... const user = await this.client.users.fetch(botId).catch(() => {}); if (!user || !user.bot || !user.applications) { throw new Error('botId is not a bot or does not have an application slash command'); } if (!commandName || typeof commandName !== 'string') throw new Error('Command name is required'); - const commandTarget = user.applications.cache.find(c => c.name === commandName && c.type === 'CHAT_INPUT'); - if (!commandTarget) { - throw new Error(`Command ${commandName} is not found\nDebug:\n+ botId: ${botId}\n+ args: ${args.join(' | ')}`); + let commandTarget = user.applications.cache.find(c => c.name === commandName && c.type === 'CHAT_INPUT'); + if (!commandTarget && !search) { + throw new Error( + 'INTERACTION_SEND_FAILURE', + `SlashCommand ${commandName} is not found (Without search)\nDebug:\n+ botId: ${botId}\n+ args: ${args.join( + ' | ', + )}`, + ); + } else { + // Using API to search (without opcode ~ehehe) + // https://discord.com/api/v9/channels/id/application-commands/search?type=1&query=aiko&limit=7&include_applications=false&application_id=id + const data = await this.client.api.channels[this.id]['application-commands'].search.get({ + query: { + type: 1, // CHAT_INPUT, + include_applications: false, + query: commandName, + limit: 25, + // Shet + // application_id: botId, + }, + }); + for (const command of data.application_commands) { + if (user.id == command.application_id) { + commandTarget = user.applications._add(command, true); + } + } + // Remove + // commandTarget = user.applications.cache.find(c => c.name === commandName && c.type === 'CHAT_INPUT'); + if (!commandTarget) { + throw new Error( + 'INTERACTION_SEND_FAILURE', + `SlashCommand ${commandName} is not found (With search)\nDebug:\n+ botId: ${botId}\n+ args: ${args.join( + ' | ', + )}`, + ); + } } return commandTarget.sendSlashCommand( new Message(this.client, { diff --git a/typings/index.d.ts b/typings/index.d.ts index 4ca4ac6..12b36f2 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -1672,7 +1672,7 @@ export class Message extends Base { public clickButton(buttonID: string): Promise; public selectMenu(menuID: string, options: string[]): Promise; public selectMenu(options: string[]): Promise; - public contextMenu(botID: Snowflake, commandName: string): Promise; + public contextMenu(botID: Snowflake, commandName: string, search?: boolean): Promise; } export class MessageActionRow< @@ -2555,7 +2555,6 @@ export class TextChannel extends BaseGuildTextChannel { public rateLimitPerUser: number; public threads: ThreadManager; public type: 'GUILD_TEXT'; - public sendSlash(botID: Snowflake, commandName: string, args?: Options[]): Promise; } export class TextInputComponent extends BaseMessageComponent { @@ -3720,6 +3719,7 @@ export interface TextBasedChannelFields extends PartialTextBasedChannelFields { setNSFW(nsfw?: boolean, reason?: string): Promise; fetchWebhooks(): Promise>; sendTyping(): Promise; + sendSlash(botId: Snowflake, commandName: string, search?: boolean, ...args: any): Promise; } export function PartialWebhookMixin(Base?: Constructable): Constructable;