Slash Command and Context Menu

- Doc. comming soon
This commit is contained in:
March 7th 2022-03-25 22:57:21 +07:00
parent b9ba04fecc
commit fd4a6fa509
8 changed files with 118 additions and 22 deletions

View File

@ -155,10 +155,11 @@ const Messages = {
SWEEP_FILTER_RETURN: 'The return value of the sweepFilter function was not false or a Function', 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_BOT_METHOD: `Bot accounts cannot use this method`,
INVALID_USER_METHOD: `User accounts cannot use this method`, INVALID_USER_METHOD: `User accounts cannot use this method`,
INVALID_LOCALE: 'Unable to select this location', INVALID_LOCALE: 'Unable to select this location',
FOLDER_NOT_FOUND: 'Server directory not found', FOLDER_NOT_FOUND: 'Server directory not found',
FOLDER_POSITION_INVALID: 'The server index in the directory is invalid', 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); for (const [name, message] of Object.entries(Messages)) register(name, message);

View File

@ -12,14 +12,15 @@ const { ApplicationCommandTypes } = require('../util/Constants');
* @extends {CachedManager} * @extends {CachedManager}
*/ */
class ApplicationCommandManager extends CachedManager { class ApplicationCommandManager extends CachedManager {
constructor(client, iterable) { constructor(client, iterable, user) {
super(client, ApplicationCommand, iterable); super(client, ApplicationCommand, iterable);
/** /**
* The manager for permissions of arbitrary commands on arbitrary guilds * The manager for permissions of arbitrary commands on arbitrary guilds
* @type {ApplicationCommandPermissionsManager} * @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 * @private
*/ */
commandPath({ id, guildId } = {}) { 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); if (this.guild ?? guildId) path = path.guilds(this.guild?.id ?? guildId);
return id ? path.commands(id) : path.commands; return id ? path.commands(id) : path.commands;
} }
@ -114,6 +115,7 @@ class ApplicationCommandManager extends CachedManager {
* .catch(console.error); * .catch(console.error);
*/ */
async create(command, guildId) { async create(command, guildId) {
if(!this.client.user.bot) throw new Error("INVALID_USER_METHOD");
const data = await this.commandPath({ guildId }).post({ const data = await this.commandPath({ guildId }).post({
data: this.constructor.transformCommand(command), data: this.constructor.transformCommand(command),
}); });
@ -143,6 +145,7 @@ class ApplicationCommandManager extends CachedManager {
* .catch(console.error); * .catch(console.error);
*/ */
async set(commands, guildId) { async set(commands, guildId) {
if(!this.client.user.bot) throw new Error("INVALID_USER_METHOD");
const data = await this.commandPath({ guildId }).put({ const data = await this.commandPath({ guildId }).put({
data: commands.map(c => this.constructor.transformCommand(c)), data: commands.map(c => this.constructor.transformCommand(c)),
}); });
@ -165,6 +168,7 @@ class ApplicationCommandManager extends CachedManager {
* .catch(console.error); * .catch(console.error);
*/ */
async edit(command, data, guildId) { async edit(command, data, guildId) {
if(!this.client.user.bot) throw new Error("INVALID_USER_METHOD");
const id = this.resolveId(command); const id = this.resolveId(command);
if (!id) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable'); if (!id) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable');
@ -187,6 +191,7 @@ class ApplicationCommandManager extends CachedManager {
* .catch(console.error); * .catch(console.error);
*/ */
async delete(command, guildId) { async delete(command, guildId) {
if(!this.client.user.bot) throw new Error("INVALID_USER_METHOD");
const id = this.resolveId(command); const id = this.resolveId(command);
if (!id) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable'); if (!id) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable');

View File

@ -10,7 +10,7 @@ const { ApplicationCommandPermissionTypes, APIErrors } = require('../util/Consta
* @extends {BaseManager} * @extends {BaseManager}
*/ */
class ApplicationCommandPermissionsManager extends BaseManager { class ApplicationCommandPermissionsManager extends BaseManager {
constructor(manager) { constructor(manager, user) {
super(manager.client); super(manager.client);
/** /**
@ -37,6 +37,8 @@ class ApplicationCommandPermissionsManager extends BaseManager {
* @type {?Snowflake} * @type {?Snowflake}
*/ */
this.commandId = manager.id ?? null; this.commandId = manager.id ?? null;
this.user = user;
} }
/** /**
@ -47,7 +49,7 @@ class ApplicationCommandPermissionsManager extends BaseManager {
* @private * @private
*/ */
permissionsPath(guildId, commandId) { 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); * .catch(console.error);
*/ */
async set({ guild, command, permissions, fullPermissions } = {}) { 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); const { guildId, commandId } = this._validateOptions(guild, command);
if (commandId) { if (commandId) {
@ -220,6 +223,7 @@ class ApplicationCommandPermissionsManager extends BaseManager {
* .catch(console.error); * .catch(console.error);
*/ */
async add({ guild, command, permissions }) { async add({ guild, command, permissions }) {
if(!this.manager.client.user.bot) throw new Error("INVALID_USER_METHOD");
const { guildId, commandId } = this._validateOptions(guild, command); const { guildId, commandId } = this._validateOptions(guild, command);
if (!commandId) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable'); if (!commandId) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable');
if (!Array.isArray(permissions)) { if (!Array.isArray(permissions)) {
@ -271,6 +275,7 @@ class ApplicationCommandPermissionsManager extends BaseManager {
* .catch(console.error); * .catch(console.error);
*/ */
async remove({ guild, command, users, roles }) { 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); const { guildId, commandId } = this._validateOptions(guild, command);
if (!commandId) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable'); if (!commandId) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable');

View File

@ -4,6 +4,7 @@ const Base = require('./Base');
const ApplicationCommandPermissionsManager = require('../managers/ApplicationCommandPermissionsManager'); const ApplicationCommandPermissionsManager = require('../managers/ApplicationCommandPermissionsManager');
const { ApplicationCommandOptionTypes, ApplicationCommandTypes, ChannelTypes } = require('../util/Constants'); const { ApplicationCommandOptionTypes, ApplicationCommandTypes, ChannelTypes } = require('../util/Constants');
const SnowflakeUtil = require('../util/SnowflakeUtil'); const SnowflakeUtil = require('../util/SnowflakeUtil');
const { Message } = require('..');
/** /**
* Represents an application command. * 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 * The manager for permissions of this command on its guild or arbitrary guilds when the command is global
* @type {ApplicationCommandPermissionsManager} * @type {ApplicationCommandPermissionsManager}
*/ */
this.permissions = new ApplicationCommandPermissionsManager(this); this.permissions = new ApplicationCommandPermissionsManager(this, user);
/** /**
* The type of this application command * The type of this application command
@ -50,6 +51,8 @@ class ApplicationCommand extends Base {
*/ */
this.type = ApplicationCommandTypes[data.type]; this.type = ApplicationCommandTypes[data.type];
this.user = client.users.cache.get(this.applicationId);
this._patch(data); this._patch(data);
} }
@ -392,6 +395,87 @@ class ApplicationCommand extends Base {
[maxValueKey]: option.maxValue ?? option.max_value, [maxValueKey]: option.maxValue ?? option.max_value,
}; };
} }
/**
* Send Slash command to channel
* @param {Message} message Discord Message
* @param {Array<string>} options The options to Slash Command
* @returns {Promise<Boolean>}
* @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<Boolean>}
* @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; module.exports = ApplicationCommand;

View File

@ -18,7 +18,7 @@ class ClientApplication extends Application {
* The application command manager for this application * The application command manager for this application
* @type {ApplicationCommandManager} * @type {ApplicationCommandManager}
*/ */
this.commands = new ApplicationCommandManager(this.client); this.commands = null // Selfbot
} }
_patch(data) { _patch(data) {
@ -97,7 +97,7 @@ class ClientApplication extends Application {
* @returns {Promise<ClientApplication>} * @returns {Promise<ClientApplication>}
*/ */
async fetch() { 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(); const app = await this.client.api.oauth2.applications('@me').get();
this._patch(app); this._patch(app);
return this; return this;

View File

@ -57,12 +57,6 @@ class Guild extends AnonymousGuild {
constructor(client, data) { constructor(client, data) {
super(client, data, false); 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 * A manager of the members belonging to this guild
* @type {GuildMemberManager} * @type {GuildMemberManager}

View File

@ -6,6 +6,7 @@ const { Error } = require('../errors');
const SnowflakeUtil = require('../util/SnowflakeUtil'); const SnowflakeUtil = require('../util/SnowflakeUtil');
const UserFlags = require('../util/UserFlags'); const UserFlags = require('../util/UserFlags');
const { default: Collection } = require('@discordjs/collection'); const { default: Collection } = require('@discordjs/collection');
const ApplicationCommandManager = require('../managers/ApplicationCommandManager');
/** /**
* Represents a user on Discord. * Represents a user on Discord.
@ -37,7 +38,7 @@ class User extends Base {
this.premiumSince = null; this.premiumSince = null;
this.premiumGuildSince = null; this.premiumGuildSince = null;
this.mutualGuilds = new Collection(); this.mutualGuilds = new Collection();
this.applications = null;
this._patch(data); this._patch(data);
} }
@ -58,6 +59,9 @@ class User extends Base {
* @type {?boolean} * @type {?boolean}
*/ */
this.bot = Boolean(data.bot); 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') { } else if (!this.partial && typeof this.bot !== 'boolean') {
this.bot = false; this.bot = false;
} }

9
typings/index.d.ts vendored
View File

@ -251,6 +251,9 @@ export class ApplicationCommand<PermissionsFetchType = {}> extends Base {
private static transformOption(option: ApplicationCommandOptionData, received?: boolean): unknown; private static transformOption(option: ApplicationCommandOptionData, received?: boolean): unknown;
private static transformCommand(command: ApplicationCommandData): RESTPostAPIApplicationCommandsJSONBody; private static transformCommand(command: ApplicationCommandData): RESTPostAPIApplicationCommandsJSONBody;
private static isAPICommandData(command: object): command is RESTPostAPIApplicationCommandsJSONBody; private static isAPICommandData(command: object): command is RESTPostAPIApplicationCommandsJSONBody;
// Add
public static sendSlashCommand(message: Message, options?: Array<string>): Promise<Boolean>;
public static sendContextMenu(message: Message): Promise<Boolean>;
} }
export type ApplicationResolvable = Application | Activity | Snowflake; export type ApplicationResolvable = Application | Activity | Snowflake;
@ -901,7 +904,7 @@ export class Guild extends AnonymousGuild {
public available: boolean; public available: boolean;
public bans: GuildBanManager; public bans: GuildBanManager;
public channels: GuildChannelManager; public channels: GuildChannelManager;
public commands: GuildApplicationCommandManager; // public commands: GuildApplicationCommandManager;
public defaultMessageNotifications: DefaultMessageNotificationLevel | number; public defaultMessageNotifications: DefaultMessageNotificationLevel | number;
/** @deprecated This will be removed in the next major version, see https://github.com/discordjs/discord.js/issues/7091 */ /** @deprecated This will be removed in the next major version, see https://github.com/discordjs/discord.js/issues/7091 */
public deleted: boolean; public deleted: boolean;
@ -2902,7 +2905,7 @@ export class ApplicationCommandManager<
PermissionsOptionsExtras = { guild: GuildResolvable }, PermissionsOptionsExtras = { guild: GuildResolvable },
PermissionsGuildType = null, PermissionsGuildType = null,
> extends CachedManager<Snowflake, ApplicationCommandScope, ApplicationCommandResolvable> { > extends CachedManager<Snowflake, ApplicationCommandScope, ApplicationCommandResolvable> {
protected constructor(client: Client, iterable?: Iterable<unknown>); protected constructor(client: Client, iterable?: Iterable<unknown>, user: User);
public permissions: ApplicationCommandPermissionsManager< public permissions: ApplicationCommandPermissionsManager<
{ command?: ApplicationCommandResolvable } & PermissionsOptionsExtras, { command?: ApplicationCommandResolvable } & PermissionsOptionsExtras,
{ command: ApplicationCommandResolvable } & PermissionsOptionsExtras, { command: ApplicationCommandResolvable } & PermissionsOptionsExtras,
@ -2949,7 +2952,7 @@ export class ApplicationCommandPermissionsManager<
GuildType, GuildType,
CommandIdType, CommandIdType,
> extends BaseManager { > extends BaseManager {
private constructor(manager: ApplicationCommandManager | GuildApplicationCommandManager | ApplicationCommand); private constructor(manager: ApplicationCommandManager | GuildApplicationCommandManager | ApplicationCommand, user: User);
private manager: ApplicationCommandManager | GuildApplicationCommandManager | ApplicationCommand; private manager: ApplicationCommandManager | GuildApplicationCommandManager | ApplicationCommand;
public client: Client; public client: Client;