From fd4a6fa5091aacc0ce50edb27cd686286b9dc37c Mon Sep 17 00:00:00 2001 From: March 7th <71698422+aiko-chan-ai@users.noreply.github.com> Date: Fri, 25 Mar 2022 22:57:21 +0700 Subject: [PATCH] Slash Command and Context Menu - Doc. comming soon --- src/errors/Messages.js | 9 +- src/managers/ApplicationCommandManager.js | 11 ++- .../ApplicationCommandPermissionsManager.js | 9 +- src/structures/ApplicationCommand.js | 86 ++++++++++++++++++- src/structures/ClientApplication.js | 4 +- src/structures/Guild.js | 6 -- src/structures/User.js | 6 +- typings/index.d.ts | 9 +- 8 files changed, 118 insertions(+), 22 deletions(-) diff --git a/src/errors/Messages.js b/src/errors/Messages.js index a3695d5..416f163 100644 --- a/src/errors/Messages.js +++ b/src/errors/Messages.js @@ -155,10 +155,11 @@ const Messages = { SWEEP_FILTER_RETURN: 'The return value of the sweepFilter function was not false or a Function', INVALID_BOT_METHOD: `Bot accounts cannot use this method`, - INVALID_USER_METHOD: `User accounts cannot use this method`, - 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', + INVALID_USER_METHOD: `User accounts cannot use this method`, + 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', + APPLICATION_ID_INVALID: 'The application isn\'t BOT', }; for (const [name, message] of Object.entries(Messages)) register(name, message); diff --git a/src/managers/ApplicationCommandManager.js b/src/managers/ApplicationCommandManager.js index 5456dd5..b6b8641 100644 --- a/src/managers/ApplicationCommandManager.js +++ b/src/managers/ApplicationCommandManager.js @@ -12,14 +12,15 @@ const { ApplicationCommandTypes } = require('../util/Constants'); * @extends {CachedManager} */ class ApplicationCommandManager extends CachedManager { - constructor(client, iterable) { + constructor(client, iterable, user) { super(client, ApplicationCommand, iterable); /** * The manager for permissions of arbitrary commands on arbitrary guilds * @type {ApplicationCommandPermissionsManager} */ - this.permissions = new ApplicationCommandPermissionsManager(this); + this.permissions = new ApplicationCommandPermissionsManager(this, user); + this.user = user; } /** @@ -41,7 +42,7 @@ class ApplicationCommandManager extends CachedManager { * @private */ commandPath({ id, guildId } = {}) { - let path = this.client.api.applications(this.client.application.id); + let path = this.client.api.applications(this.user.id); if (this.guild ?? guildId) path = path.guilds(this.guild?.id ?? guildId); return id ? path.commands(id) : path.commands; } @@ -114,6 +115,7 @@ class ApplicationCommandManager extends CachedManager { * .catch(console.error); */ async create(command, guildId) { + if(!this.client.user.bot) throw new Error("INVALID_USER_METHOD"); const data = await this.commandPath({ guildId }).post({ data: this.constructor.transformCommand(command), }); @@ -143,6 +145,7 @@ class ApplicationCommandManager extends CachedManager { * .catch(console.error); */ async set(commands, guildId) { + if(!this.client.user.bot) throw new Error("INVALID_USER_METHOD"); const data = await this.commandPath({ guildId }).put({ data: commands.map(c => this.constructor.transformCommand(c)), }); @@ -165,6 +168,7 @@ class ApplicationCommandManager extends CachedManager { * .catch(console.error); */ async edit(command, data, guildId) { + if(!this.client.user.bot) throw new Error("INVALID_USER_METHOD"); const id = this.resolveId(command); if (!id) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable'); @@ -187,6 +191,7 @@ class ApplicationCommandManager extends CachedManager { * .catch(console.error); */ async delete(command, guildId) { + if(!this.client.user.bot) throw new Error("INVALID_USER_METHOD"); const id = this.resolveId(command); if (!id) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable'); diff --git a/src/managers/ApplicationCommandPermissionsManager.js b/src/managers/ApplicationCommandPermissionsManager.js index a93a8f2..05081d6 100644 --- a/src/managers/ApplicationCommandPermissionsManager.js +++ b/src/managers/ApplicationCommandPermissionsManager.js @@ -10,7 +10,7 @@ const { ApplicationCommandPermissionTypes, APIErrors } = require('../util/Consta * @extends {BaseManager} */ class ApplicationCommandPermissionsManager extends BaseManager { - constructor(manager) { + constructor(manager, user) { super(manager.client); /** @@ -37,6 +37,8 @@ class ApplicationCommandPermissionsManager extends BaseManager { * @type {?Snowflake} */ this.commandId = manager.id ?? null; + + this.user = user; } /** @@ -47,7 +49,7 @@ class ApplicationCommandPermissionsManager extends BaseManager { * @private */ permissionsPath(guildId, commandId) { - return this.client.api.applications(this.client.application.id).guilds(guildId).commands(commandId).permissions; + return this.client.api.applications(this.user.id).guilds(guildId).commands(commandId).permissions; } /** @@ -159,6 +161,7 @@ class ApplicationCommandPermissionsManager extends BaseManager { * .catch(console.error); */ async set({ guild, command, permissions, fullPermissions } = {}) { + if(!this.manager.client.user.bot) throw new Error("INVALID_USER_METHOD"); const { guildId, commandId } = this._validateOptions(guild, command); if (commandId) { @@ -220,6 +223,7 @@ class ApplicationCommandPermissionsManager extends BaseManager { * .catch(console.error); */ async add({ guild, command, permissions }) { + if(!this.manager.client.user.bot) throw new Error("INVALID_USER_METHOD"); const { guildId, commandId } = this._validateOptions(guild, command); if (!commandId) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable'); if (!Array.isArray(permissions)) { @@ -271,6 +275,7 @@ class ApplicationCommandPermissionsManager extends BaseManager { * .catch(console.error); */ async remove({ guild, command, users, roles }) { + if(!this.manager.client.user.bot) throw new Error("INVALID_USER_METHOD"); const { guildId, commandId } = this._validateOptions(guild, command); if (!commandId) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable'); diff --git a/src/structures/ApplicationCommand.js b/src/structures/ApplicationCommand.js index ea4558e..2d1381d 100644 --- a/src/structures/ApplicationCommand.js +++ b/src/structures/ApplicationCommand.js @@ -4,6 +4,7 @@ const Base = require('./Base'); const ApplicationCommandPermissionsManager = require('../managers/ApplicationCommandPermissionsManager'); const { ApplicationCommandOptionTypes, ApplicationCommandTypes, ChannelTypes } = require('../util/Constants'); const SnowflakeUtil = require('../util/SnowflakeUtil'); +const { Message } = require('..'); /** * Represents an application command. @@ -42,7 +43,7 @@ class ApplicationCommand extends Base { * The manager for permissions of this command on its guild or arbitrary guilds when the command is global * @type {ApplicationCommandPermissionsManager} */ - this.permissions = new ApplicationCommandPermissionsManager(this); + this.permissions = new ApplicationCommandPermissionsManager(this, user); /** * The type of this application command @@ -50,6 +51,8 @@ class ApplicationCommand extends Base { */ this.type = ApplicationCommandTypes[data.type]; + this.user = client.users.cache.get(this.applicationId); + this._patch(data); } @@ -392,6 +395,87 @@ class ApplicationCommand extends Base { [maxValueKey]: option.maxValue ?? option.max_value, }; } + /** + * Send Slash command to channel + * @param {Message} message Discord Message + * @param {Array} options The options to Slash Command + * @returns {Promise} + * @example + * const botID = '12345678987654321' + * const user = await client.users.fetch(botID); + * const application = await user.applications.fetch(); + * const command = application.commands.first(); + * await command.sendSlashCommand(messsage, ['option1', 'option2']); + */ + async sendSlashCommand(message, options = []) { + // Check Options + if (!message instanceof Message) throw new TypeError('The message must be a Discord.Message'); + if (!Array.isArray(options)) throw new TypeError('The options must be an array of strings'); + if (this.type !== 'CHAT_INPUT') return false; + const optionFormat = []; + let i = 0; + for (i; i < options.length ; i++) { + const value = options[i]; + if (typeof value !== 'string') { + throw new TypeError(`Expected option to be a String, got ${typeof value}`); + } + if (!this.options[i]) continue; + const data = { + type: ApplicationCommandOptionTypes[this.options[i].type], + name: this.options[i].name, + value: value, + } + optionFormat.push(data); + } + if (this.options[i]?.required) throw new Error('Value required missing'); + await this.client.api.interactions.post({ body: { + type: 2, // ??? + application_id: this.applicationId, + guild_id: message.guildId, + channel_id: message.channelId, + session_id: this.client.session_id, + data: { + // ApplicationCommandData + version: this.version, + id: this.id, + name: this.name, + type: ApplicationCommandTypes[this.type], + options: optionFormat, + }, + }}) + return true; + } + /** + * Message Context Menu + * @param {Message} message Discord Message + * @returns {Promise} + * @example + * const botID = '12345678987654321' + * const user = await client.users.fetch(botID); + * const application = await user.applications.fetch(); + * const command = application.commands.first(); + * await command.sendContextMenu(messsage); + */ + async sendContextMenu(message) { + if (!message instanceof Message) throw new TypeError('The message must be a Discord.Message'); + if (this.type == 'CHAT_INPUT') return false; + await this.client.api.interactions.post({ body: { + type: 2, // ??? + application_id: this.applicationId, + guild_id: message.guildId, + channel_id: message.channelId, + session_id: this.client.session_id, + data: { + // ApplicationCommandData + version: this.version, + id: this.id, + name: this.name, + type: ApplicationCommandTypes[this.type], + target_id: ApplicationCommandTypes[this.type] == 1 ? message.author.id : message.id, + }, + }}) + return true; + } } module.exports = ApplicationCommand; diff --git a/src/structures/ClientApplication.js b/src/structures/ClientApplication.js index 80dfd36..88a4745 100644 --- a/src/structures/ClientApplication.js +++ b/src/structures/ClientApplication.js @@ -18,7 +18,7 @@ class ClientApplication extends Application { * The application command manager for this application * @type {ApplicationCommandManager} */ - this.commands = new ApplicationCommandManager(this.client); + this.commands = null // Selfbot } _patch(data) { @@ -97,7 +97,7 @@ class ClientApplication extends Application { * @returns {Promise} */ async fetch() { - if(!this.client.bot) throw new Error("INVALID_USER_METHOD"); + if(!this.client.user.bot) throw new Error("INVALID_USER_METHOD"); const app = await this.client.api.oauth2.applications('@me').get(); this._patch(app); return this; diff --git a/src/structures/Guild.js b/src/structures/Guild.js index 9c00fb8..5ff0a2e 100644 --- a/src/structures/Guild.js +++ b/src/structures/Guild.js @@ -57,12 +57,6 @@ class Guild extends AnonymousGuild { constructor(client, data) { super(client, data, false); - /** - * A manager of the application commands belonging to this guild - * @type {GuildApplicationCommandManager} - */ - this.commands = new GuildApplicationCommandManager(this); - /** * A manager of the members belonging to this guild * @type {GuildMemberManager} diff --git a/src/structures/User.js b/src/structures/User.js index ecb9987..ced6bf3 100644 --- a/src/structures/User.js +++ b/src/structures/User.js @@ -6,6 +6,7 @@ const { Error } = require('../errors'); const SnowflakeUtil = require('../util/SnowflakeUtil'); const UserFlags = require('../util/UserFlags'); const { default: Collection } = require('@discordjs/collection'); +const ApplicationCommandManager = require('../managers/ApplicationCommandManager'); /** * Represents a user on Discord. @@ -37,7 +38,7 @@ class User extends Base { this.premiumSince = null; this.premiumGuildSince = null; this.mutualGuilds = new Collection(); - + this.applications = null; this._patch(data); } @@ -58,6 +59,9 @@ class User extends Base { * @type {?boolean} */ this.bot = Boolean(data.bot); + if (this.bot == true) { + this.applications = new ApplicationCommandManager(this.client, undefined, this); + } } else if (!this.partial && typeof this.bot !== 'boolean') { this.bot = false; } diff --git a/typings/index.d.ts b/typings/index.d.ts index 1a4e556..59c3db2 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -251,6 +251,9 @@ export class ApplicationCommand extends Base { private static transformOption(option: ApplicationCommandOptionData, received?: boolean): unknown; private static transformCommand(command: ApplicationCommandData): RESTPostAPIApplicationCommandsJSONBody; private static isAPICommandData(command: object): command is RESTPostAPIApplicationCommandsJSONBody; + // Add + public static sendSlashCommand(message: Message, options?: Array): Promise; + public static sendContextMenu(message: Message): Promise; } export type ApplicationResolvable = Application | Activity | Snowflake; @@ -901,7 +904,7 @@ export class Guild extends AnonymousGuild { public available: boolean; public bans: GuildBanManager; public channels: GuildChannelManager; - public commands: GuildApplicationCommandManager; + // public commands: GuildApplicationCommandManager; public defaultMessageNotifications: DefaultMessageNotificationLevel | number; /** @deprecated This will be removed in the next major version, see https://github.com/discordjs/discord.js/issues/7091 */ public deleted: boolean; @@ -2902,7 +2905,7 @@ export class ApplicationCommandManager< PermissionsOptionsExtras = { guild: GuildResolvable }, PermissionsGuildType = null, > extends CachedManager { - protected constructor(client: Client, iterable?: Iterable); + protected constructor(client: Client, iterable?: Iterable, user: User); public permissions: ApplicationCommandPermissionsManager< { command?: ApplicationCommandResolvable } & PermissionsOptionsExtras, { command: ApplicationCommandResolvable } & PermissionsOptionsExtras, @@ -2949,7 +2952,7 @@ export class ApplicationCommandPermissionsManager< GuildType, CommandIdType, > extends BaseManager { - private constructor(manager: ApplicationCommandManager | GuildApplicationCommandManager | ApplicationCommand); + private constructor(manager: ApplicationCommandManager | GuildApplicationCommandManager | ApplicationCommand, user: User); private manager: ApplicationCommandManager | GuildApplicationCommandManager | ApplicationCommand; public client: Client;