diff --git a/Document/Message.md b/Document/Message.md index 81d1fcf..a655c11 100644 --- a/Document/Message.md +++ b/Document/Message.md @@ -47,17 +47,6 @@ await message.contextMenu(botID, commandName); ```
-Issue ? - -- It has some minor bugs. -```js -DiscordAPIError [20012] You are not authorized to perform this action on this application -Fix it: creating 1 DMs with bot -In this way, all Slash commands can be obtained -``` -- With Gateway guild.searchInteraction() (using gateway) -- With REST: Working ! [TextBasedChannel.sendSlash()]. -
## MessageEmbed ? - Because Discord has removed the ability to send Embeds in its API, that means MessageEmbed is unusable. But I have created a constructor that uses oEmbed with help [from this site](https://www.reddit.com/r/discordapp/comments/82p8i6/a_basic_tutorial_on_how_to_get_the_most_out_of/) diff --git a/Document/SlashCommand.md b/Document/SlashCommand.md index 53f2e86..cd7c993 100644 --- a/Document/SlashCommand.md +++ b/Document/SlashCommand.md @@ -1,6 +1,6 @@ # Slash command demo - Support Autocomplete feature (half) -- Unused `guild.searchInteraction()` (Use only if not working properly) +- Unused `guild.searchInteraction()` (Deleted) # BREAKING CHANGE: Using Slash Command (Sub Command / Sub Group Command) will not accept subCommand argument in args. That means Command Name needs to be changed same as Discord Client diff --git a/src/client/websocket/handlers/INTERACTION_FAILURE.js b/src/client/websocket/handlers/INTERACTION_FAILURE.js index 27540b2..1365e3d 100644 --- a/src/client/websocket/handlers/INTERACTION_FAILURE.js +++ b/src/client/websocket/handlers/INTERACTION_FAILURE.js @@ -11,6 +11,7 @@ module.exports = (client, { d: data }) => { client.emit('interactionResponse', { status: false, metadata: data, + error: 'No response from bot', }); // Delete cache client._interactionCache.delete(data.nonce); diff --git a/src/client/websocket/handlers/INTERACTION_SUCCESS.js b/src/client/websocket/handlers/INTERACTION_SUCCESS.js index e4dc620..43c0a4c 100644 --- a/src/client/websocket/handlers/INTERACTION_SUCCESS.js +++ b/src/client/websocket/handlers/INTERACTION_SUCCESS.js @@ -23,6 +23,7 @@ module.exports = (client, { d: data }) => { client.emit('interactionResponse', { status: true, metadata: data_, + error: '', }); // Delete cache // client._interactionCache.delete(data.nonce); diff --git a/src/structures/ApplicationCommand.js b/src/structures/ApplicationCommand.js index 1257523..8dc3dc1 100644 --- a/src/structures/ApplicationCommand.js +++ b/src/structures/ApplicationCommand.js @@ -873,8 +873,15 @@ class ApplicationCommand extends Base { clearTimeout(timeout); this.client.removeListener('interactionResponse', handler); this.client.decrementMaxListeners(); - if (data.status) resolve(data.metadata); - else reject(data.metadata); + if (data.status) { + resolve(data.metadata); + } else { + reject( + new Error('INTERACTION_ERROR', { + cause: data, + }), + ); + } }; const timeout = setTimeout(() => { this.client.removeListener('interactionResponse', handler); @@ -958,8 +965,15 @@ class ApplicationCommand extends Base { clearTimeout(timeout); this.client.removeListener('interactionResponse', handler); this.client.decrementMaxListeners(); - if (data.status) resolve(data.metadata); - else reject(data.metadata); + if (data.status) { + resolve(data.metadata); + } else { + reject( + new Error('INTERACTION_ERROR', { + cause: data, + }), + ); + } }; const timeout = setTimeout(() => { this.client.removeListener('interactionResponse', handler); diff --git a/src/structures/BaseGuildTextChannel.js b/src/structures/BaseGuildTextChannel.js index 582e65b..3a19550 100644 --- a/src/structures/BaseGuildTextChannel.js +++ b/src/structures/BaseGuildTextChannel.js @@ -185,6 +185,7 @@ class BaseGuildTextChannel extends GuildChannel { setRateLimitPerUser() {} setNSFW() {} sendSlash() {} + searchInteraction() {} } TextBasedChannel.applyToClass(BaseGuildTextChannel, true); diff --git a/src/structures/DMChannel.js b/src/structures/DMChannel.js index 9fb18bc..d9b7738 100644 --- a/src/structures/DMChannel.js +++ b/src/structures/DMChannel.js @@ -149,6 +149,7 @@ class DMChannel extends Channel { createMessageComponentCollector() {} awaitMessageComponent() {} sendSlash() {} + searchInteraction() {} // Doesn't work on DM channels; bulkDelete() {} // Doesn't work on DM channels; setRateLimitPerUser() {} // Doesn't work on DM channels; setNSFW() {} diff --git a/src/structures/Guild.js b/src/structures/Guild.js index ceba3dc..4ba7d17 100644 --- a/src/structures/Guild.js +++ b/src/structures/Guild.js @@ -1,7 +1,6 @@ 'use strict'; const process = require('node:process'); -const setTimeout = require('node:timers').setTimeout; const { Collection } = require('@discordjs/collection'); const AnonymousGuild = require('./AnonymousGuild'); const GuildAuditLogs = require('./GuildAuditLogs'); @@ -32,12 +31,8 @@ const { Status, MFALevels, PremiumTiers, - Opcodes, - Events, - ApplicationCommandTypes, } = require('../util/Constants'); const DataResolver = require('../util/DataResolver'); -const SnowflakeUtil = require('../util/SnowflakeUtil'); const SystemChannelFlags = require('../util/SystemChannelFlags'); const Util = require('../util/Util'); @@ -625,67 +620,6 @@ class Guild extends AnonymousGuild { } } - /** - * Options for guildSearchInteraction - * @typedef {Object} GuildSearchInteractionOptions - * @property {string} query Command name - * @property {?number} [limit=10] Maximum number of results - * @property {?number} [offset=0] Only return entries for actions made by this user - * @property {?Snowflake} [botId] BotID - * @property {?ApplicationCommandType} [type=CHAT_INPUT] Type of command - */ - /** - * Searches for guild interactions - * @param {GuildSearchInteractionOptions} options Options for the search - * @deprecated - * @returns {void | Promise} - */ - searchInteraction(options = {}) { - let { query, limit, offset, botId, type } = Object.assign( - { query: undefined, limit: 10, offset: 0, botId: undefined, type: 'CHAT_INPUT' }, - options, - ); - if (!query) throw new Error('MISSING_VALUE', 'searchInteraction', 'query'); - const nonce = SnowflakeUtil.generate(); - this.shard.send({ - op: Opcodes.REQUEST_APPLICATION_COMMANDS, - d: { - guild_id: this.id, - applications: false, - limit, - offset, - query, - nonce, - }, - }); - if (!botId || !type) return undefined; - return new Promise((resolve, reject) => { - const handler = applications => { - timeout.refresh(); - if (applications.nonce !== nonce) return; - const cmd = applications.application_commands.find(app => app.name == query && app.application_id == botId); - if (!cmd) return; - clearTimeout(timeout); - this.client.removeListener(Events.GUILD_APPLICATION_COMMANDS_UPDATE, handler); - this.client.decrementMaxListeners(); - resolve( - this.client.users.cache - .get(botId) - ?.application?.commands?.cache?.find( - c => (c.name === query && c.type == type) || c.type == ApplicationCommandTypes[type], - ), - ); - }; - const timeout = setTimeout(() => { - this.client.removeListener(Events.GUILD_APPLICATION_COMMANDS_UPDATE, handler); - this.client.decrementMaxListeners(); - reject(new Error('GUILD_APPLICATION_COMMANDS_SEARCH_TIMEOUT')); - }, 10000).unref(); - this.client.incrementMaxListeners(); - this.client.on(Events.GUILD_APPLICATION_COMMANDS_UPDATE, handler); - }); - } - /** * Fetches a collection of integrations to this guild. * Resolves with a collection mapping integrations by their ids. diff --git a/src/structures/MessageButton.js b/src/structures/MessageButton.js index 7fa9ff5..bd6da9a 100644 --- a/src/structures/MessageButton.js +++ b/src/structures/MessageButton.js @@ -203,8 +203,15 @@ class MessageButton extends BaseMessageComponent { clearTimeout(timeout); message.client.removeListener('interactionResponse', handler); message.client.decrementMaxListeners(); - if (data.status) resolve(data.metadata); - else reject(data.metadata); + if (data.status) { + resolve(data.metadata); + } else { + reject( + new Error('INTERACTION_ERROR', { + cause: data, + }), + ); + } }; const timeout = setTimeout(() => { message.client.removeListener('interactionResponse', handler); diff --git a/src/structures/MessageSelectMenu.js b/src/structures/MessageSelectMenu.js index 08de0bb..e27685e 100644 --- a/src/structures/MessageSelectMenu.js +++ b/src/structures/MessageSelectMenu.js @@ -273,8 +273,15 @@ class MessageSelectMenu extends BaseMessageComponent { clearTimeout(timeout); message.client.removeListener('interactionResponse', handler); message.client.decrementMaxListeners(); - if (data.status) resolve(data.metadata); - else reject(data.metadata); + if (data.status) { + resolve(data.metadata); + } else { + reject( + new Error('INTERACTION_ERROR', { + cause: data, + }), + ); + } }; const timeout = setTimeout(() => { message.client.removeListener('interactionResponse', handler); diff --git a/src/structures/interfaces/TextBasedChannel.js b/src/structures/interfaces/TextBasedChannel.js index 0be10b1..361f5f1 100644 --- a/src/structures/interfaces/TextBasedChannel.js +++ b/src/structures/interfaces/TextBasedChannel.js @@ -407,6 +407,34 @@ class TextBasedChannel { return this.edit({ nsfw }, reason); } + /** + * Search Slash Command (return raw data) + * @param {Snowflake} applicationId Application ID + * @param {?ApplicationCommandType} type Command Type + * @returns {Object} + */ + searchInteraction(applicationId, type = 'CHAT_INPUT') { + switch (type) { + case 'USER': + case 2: + type = 2; + break; + case 'MESSAGE': + case 3: + type = 3; + break; + default: + type = 1; + break; + } + return this.client.api.channels[this.id]['application-commands'].search.get({ + query: { + type, + application_id: applicationId, + }, + }); + } + /** * Send Slash to this channel * @param {UserResolvable} bot Bot user @@ -459,15 +487,7 @@ class TextBasedChannel { } if (user._partial) await user.getProfile().catch(() => {}); if (!commandName || typeof commandName !== 'string') throw new Error('Command name is required'); - // Using API to search (without opcode ~ehehe) - // https://discord.com/api/v9/channels/id/application-commands/search?type=1&application_id=161660517914509312 - const query = { - type: 1, // Slash commands - application_id: user.application?.id ?? user.id, - }; - const data = await this.client.api.channels[this.id]['application-commands'].search.get({ - query, - }); + const data = await this.searchInteraction(user.application?.id ?? user.id, 'CHAT_INPUT'); for (const command of data.application_commands) { if (user.id == command.application_id || user.application.id == command.application_id) { user.application?.commands?._add(command, true); @@ -515,6 +535,7 @@ class TextBasedChannel { 'setRateLimitPerUser', 'setNSFW', 'sendSlash', + 'searchInteraction', ); } for (const prop of props) { diff --git a/typings/index.d.ts b/typings/index.d.ts index 4dd50c6..f751e77 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -1300,7 +1300,6 @@ export class Guild extends AnonymousGuild { public fetchAuditLogs( options?: GuildAuditLogsFetchOptions, ): Promise>; - public searchInteraction(options?: guildSearchInteraction): Promise>; public fetchIntegrations(): Promise>; public fetchOwner(options?: BaseFetchOptions): Promise; public fetchPreview(): Promise; @@ -4132,6 +4131,10 @@ export interface TextBasedChannelFields extends PartialTextBasedChannelFields { fetchWebhooks(): Promise>; sendTyping(): Promise; sendSlash(bot: UserResolvable, commandName: string, ...args: any): Promise; + searchInteraction( + applicationId: ApplicationCommandTypes, + type?: ApplicationCommandTypes, + ): Promise; } export function PartialWebhookMixin(Base?: Constructable): Constructable; @@ -4379,15 +4382,6 @@ export interface ApplicationAsset { type: 'BIG' | 'SMALL'; } -// copy -export interface guildSearchInteraction { - type?: ApplicationCommandTypes; - query?: string | null | undefined; - limit?: number; - offset?: number; - botID?: Snowflake; -} - export interface ClientEvents extends BaseClientEvents { /** @deprecated See [this issue](https://github.com/discord/discord-api-docs/issues/3690) for more information. */ applicationCommandCreate: [command: ApplicationCommand];