refactor(SendSlash): Perfect 🐛
This commit is contained in:
parent
89349b9eac
commit
347caf9dce
@ -2,6 +2,10 @@
|
|||||||
- Support Autocomplete feature (half)
|
- Support Autocomplete feature (half)
|
||||||
- Unused `guild.searchInteraction()` (Use only if not working properly)
|
- Unused `guild.searchInteraction()` (Use only if not working properly)
|
||||||
|
|
||||||
|
# <strong>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</strong>
|
||||||
|
|
||||||
|
# All image demo : v2.3
|
||||||
|
|
||||||
# Slash Command (no options)
|
# Slash Command (no options)
|
||||||
|
|
||||||
### Demo
|
### Demo
|
||||||
@ -14,7 +18,6 @@
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
await message.channel.sendSlash('botid', 'aiko')
|
await message.channel.sendSlash('botid', 'aiko')
|
||||||
// Return nonce (view document)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Result
|
### Result
|
||||||
@ -23,15 +26,17 @@ await message.channel.sendSlash('botid', 'aiko')
|
|||||||
|
|
||||||
# Slash Command + Sub option (group)
|
# Slash Command + Sub option (group)
|
||||||
|
|
||||||
### Demo
|
### Demo (v2.5)
|
||||||
|
|
||||||
![image](https://user-images.githubusercontent.com/71698422/173346438-678009a1-870c-49a2-97fe-8ceed4f1ab64.png)
|
![image](https://user-images.githubusercontent.com/71698422/173346438-678009a1-870c-49a2-97fe-8ceed4f1ab64.png)
|
||||||
|
|
||||||
### Code test
|
### Code test
|
||||||
|
|
||||||
```js
|
```diff
|
||||||
await message.channel.sendSlash('450323683840491530', 'animal', 'chat', 'bye')
|
v2.5
|
||||||
// Return nonce (view document)
|
- await message.channel.sendSlash('450323683840491530', 'animal', 'chat', 'bye')
|
||||||
|
v2.6+
|
||||||
|
+ await message.channel.sendSlash('450323683840491530', 'animal chat', 'bye')
|
||||||
```
|
```
|
||||||
|
|
||||||
### Result
|
### Result
|
||||||
@ -51,7 +56,6 @@ const { MessageAttachment } = require('discord.js-selfbot-v13')
|
|||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const a = new MessageAttachment(fs.readFileSync('./wallpaper.jpg') , 'test.jpg')
|
const a = new MessageAttachment(fs.readFileSync('./wallpaper.jpg') , 'test.jpg')
|
||||||
await message.channel.sendSlash('718642000898818048', 'sauce', a)
|
await message.channel.sendSlash('718642000898818048', 'sauce', a)
|
||||||
// Return nonce (view document)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Result
|
### Result
|
||||||
|
@ -50,6 +50,7 @@ const Messages = {
|
|||||||
/* Add */
|
/* Add */
|
||||||
MISSING_PERMISSIONS: (...permission) => `You can't do this action [Missing Permission(s): ${permission.join(', ')}]`,
|
MISSING_PERMISSIONS: (...permission) => `You can't do this action [Missing Permission(s): ${permission.join(', ')}]`,
|
||||||
EMBED_PROVIDER_NAME: 'MessageEmbed provider name must be a string.',
|
EMBED_PROVIDER_NAME: 'MessageEmbed provider name must be a string.',
|
||||||
|
INVALID_COMMAND_NAME: allCMD => `Could not parse subGroupCommand and subCommand due to too long: ${allCMD.join(' ')}`,
|
||||||
|
|
||||||
BUTTON_LABEL: 'MessageButton label must be a string',
|
BUTTON_LABEL: 'MessageButton label must be a string',
|
||||||
BUTTON_URL: 'MessageButton URL must be a string',
|
BUTTON_URL: 'MessageButton URL must be a string',
|
||||||
|
@ -10,6 +10,7 @@ const Permissions = require('../util/Permissions');
|
|||||||
const SnowflakeUtil = require('../util/SnowflakeUtil');
|
const SnowflakeUtil = require('../util/SnowflakeUtil');
|
||||||
const { lazy } = require('../util/Util');
|
const { lazy } = require('../util/Util');
|
||||||
const Message = lazy(() => require('../structures/Message').Message);
|
const Message = lazy(() => require('../structures/Message').Message);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an application command.
|
* Represents an application command.
|
||||||
* @extends {Base}
|
* @extends {Base}
|
||||||
@ -588,11 +589,15 @@ class ApplicationCommand extends Base {
|
|||||||
/**
|
/**
|
||||||
* Send Slash command to channel
|
* Send Slash command to channel
|
||||||
* @param {Message} message Discord Message
|
* @param {Message} message Discord Message
|
||||||
|
* @param {Array<string>} subCommandArray SubCommand Array
|
||||||
* @param {Array<string>} options The options to Slash Command
|
* @param {Array<string>} options The options to Slash Command
|
||||||
* @returns {Promise<InteractionResponseBody>}
|
* @returns {Promise<InteractionResponseBody>}
|
||||||
*/
|
*/
|
||||||
async sendSlashCommand(message, options = []) {
|
// eslint-disable-next-line consistent-return
|
||||||
|
async sendSlashCommand(message, subCommandArray = [], options = []) {
|
||||||
// Todo: Refactor
|
// Todo: Refactor
|
||||||
|
const buildError = (type, value, array, msg) =>
|
||||||
|
new Error(`Invalid ${type}: ${value} ${msg}\nList of ${type}:\n${array}`);
|
||||||
// Check Options
|
// Check Options
|
||||||
if (!(message instanceof Message())) {
|
if (!(message instanceof Message())) {
|
||||||
throw new TypeError('The message must be a Discord.Message');
|
throw new TypeError('The message must be a Discord.Message');
|
||||||
@ -604,6 +609,167 @@ class ApplicationCommand extends Base {
|
|||||||
const optionFormat = [];
|
const optionFormat = [];
|
||||||
const attachments = [];
|
const attachments = [];
|
||||||
const attachmentsBuffer = [];
|
const attachmentsBuffer = [];
|
||||||
|
const parseChoices = (list_choices, value) => {
|
||||||
|
if (value !== undefined) {
|
||||||
|
if (Array.isArray(list_choices) && list_choices.length) {
|
||||||
|
const choice = list_choices.find(c => c.name === value) || list_choices.find(c => c.value === value);
|
||||||
|
if (choice) {
|
||||||
|
return choice.value;
|
||||||
|
}
|
||||||
|
throw buildError(
|
||||||
|
'choice',
|
||||||
|
value,
|
||||||
|
list_choices.map((c, i) => ` #${i + 1} Name: ${c.name} Value: ${c.value}`).join('\n'),
|
||||||
|
'is not a valid choice for this option',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const parseOption = async (optionCommand, value) => {
|
||||||
|
const data = {
|
||||||
|
type: ApplicationCommandOptionTypes[optionCommand.type],
|
||||||
|
name: optionCommand.name,
|
||||||
|
};
|
||||||
|
if (value !== undefined) {
|
||||||
|
value = parseChoices(optionCommand.choices, value);
|
||||||
|
switch (optionCommand.type) {
|
||||||
|
case 'BOOLEAN': {
|
||||||
|
data.value = Boolean(value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'INTEGER': {
|
||||||
|
data.value = Number(value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'ATTACHMENT': {
|
||||||
|
data.value = await addDataFromAttachment(value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'SUB_COMMAND_GROUP': {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
if (optionCommand.autocomplete) {
|
||||||
|
let optionsBuild;
|
||||||
|
switch (subCommandArray.length) {
|
||||||
|
case 0: {
|
||||||
|
optionsBuild = [
|
||||||
|
...optionFormat,
|
||||||
|
{
|
||||||
|
type: ApplicationCommandOptionTypes[optionCommand.type],
|
||||||
|
name: optionCommand.name,
|
||||||
|
value,
|
||||||
|
focused: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
const subCommand = this.options.find(o => o.name == subCommandArray[0] && o.type == 'SUB_COMMAND');
|
||||||
|
optionsBuild = [
|
||||||
|
{
|
||||||
|
type: ApplicationCommandOptionTypes[subCommand.type],
|
||||||
|
name: subCommand.name,
|
||||||
|
options: [
|
||||||
|
...optionFormat,
|
||||||
|
{
|
||||||
|
type: ApplicationCommandOptionTypes[optionCommand.type],
|
||||||
|
name: optionCommand.name,
|
||||||
|
value,
|
||||||
|
focused: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
const subGroup = this.options.find(
|
||||||
|
o => o.name == subCommandArray[0] && o.type == 'SUB_COMMAND_GROUP',
|
||||||
|
);
|
||||||
|
const subCommand = this.options.find(o => o.name == subCommandArray[1] && o.type == 'SUB_COMMAND');
|
||||||
|
optionsBuild = [
|
||||||
|
{
|
||||||
|
type: ApplicationCommandOptionTypes[subGroup.type],
|
||||||
|
name: subGroup.name,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
type: ApplicationCommandOptionTypes[subCommand.type],
|
||||||
|
name: subCommand.name,
|
||||||
|
options: [
|
||||||
|
...optionFormat,
|
||||||
|
{
|
||||||
|
type: ApplicationCommandOptionTypes[optionCommand.type],
|
||||||
|
name: optionCommand.name,
|
||||||
|
value,
|
||||||
|
focused: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const autoValue = await getAutoResponse(optionsBuild, value);
|
||||||
|
data.value = autoValue;
|
||||||
|
} else {
|
||||||
|
data.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
optionFormat.push(data);
|
||||||
|
}
|
||||||
|
return optionFormat;
|
||||||
|
};
|
||||||
|
const parseSubCommand = async (subCommandName, options) => {
|
||||||
|
const subCommand = this.options.find(o => o.name == subCommandName && o.type == 'SUB_COMMAND');
|
||||||
|
if (!subCommand) {
|
||||||
|
throw buildError(
|
||||||
|
'SubCommand',
|
||||||
|
subCommandName,
|
||||||
|
this.options.map((o, i) => ` #${i + 1} Name: ${o.name}`).join('\n'),
|
||||||
|
'is not a valid sub command',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const valueRequired = subCommand.options.filter(o => o.required).length;
|
||||||
|
for (let i = 0; i < options.length; i++) {
|
||||||
|
const optionInput = subCommand.options[i];
|
||||||
|
const value = options[i];
|
||||||
|
await parseOption(optionInput, value);
|
||||||
|
}
|
||||||
|
if (valueRequired > options.length) {
|
||||||
|
throw new Error(`Value required missing\nDebug:
|
||||||
|
Required: ${valueRequired} - Options: ${optionFormat.length}`);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: ApplicationCommandOptionTypes[subCommand.type],
|
||||||
|
name: subCommand.name,
|
||||||
|
options: optionFormat,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const parseSubGroupCommand = async (subGroupName, subName) => {
|
||||||
|
const subGroup = this.options.find(o => o.name == subGroupName && o.type == 'SUB_COMMAND_GROUP');
|
||||||
|
if (!subGroup) {
|
||||||
|
throw buildError(
|
||||||
|
'SubGroupCommand',
|
||||||
|
subGroupName,
|
||||||
|
this.options.map((o, i) => ` #${i + 1} Name: ${o.name}`).join('\n'),
|
||||||
|
'is not a valid sub group command',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const data = await parseSubCommand(subName, options);
|
||||||
|
return {
|
||||||
|
type: ApplicationCommandOptionTypes[subGroup.type],
|
||||||
|
name: subGroup.name,
|
||||||
|
options: [data],
|
||||||
|
};
|
||||||
|
};
|
||||||
async function addDataFromAttachment(data) {
|
async function addDataFromAttachment(data) {
|
||||||
if (!(data instanceof MessageAttachment)) {
|
if (!(data instanceof MessageAttachment)) {
|
||||||
throw new TypeError('The attachment data must be a Discord.MessageAttachment');
|
throw new TypeError('The attachment data must be a Discord.MessageAttachment');
|
||||||
@ -617,34 +783,14 @@ class ApplicationCommand extends Base {
|
|||||||
attachmentsBuffer.push({ attachment: data.attachment, name: data.name, file: resource });
|
attachmentsBuffer.push({ attachment: data.attachment, name: data.name, file: resource });
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
let option_ = [];
|
const getDataPost = (guildAdd = false, dataAdd = [], nonce, autocomplete = false) => {
|
||||||
let i = 0;
|
if (!Array.isArray(dataAdd) && typeof dataAdd == 'object') {
|
||||||
// Check Command type is Sub group ?
|
dataAdd = [dataAdd];
|
||||||
const subCommandCheck = this.options.some(option => ['SUB_COMMAND', 'SUB_COMMAND_GROUP'].includes(option.type));
|
|
||||||
let subCommand;
|
|
||||||
if (subCommandCheck) {
|
|
||||||
subCommand = this.options.find(option => option.name == options[0]);
|
|
||||||
options.shift();
|
|
||||||
option_[0] = {
|
|
||||||
type: ApplicationCommandOptionTypes[subCommand.type],
|
|
||||||
name: subCommand.name,
|
|
||||||
options: optionFormat,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
option_ = optionFormat;
|
|
||||||
}
|
}
|
||||||
// Autoresponse
|
if (guildAdd) {
|
||||||
const getAutoResponse = async (options_, type, name, value) => {
|
return {
|
||||||
const op = Array.from(options_);
|
type: autocomplete ? 4 : 2, // Slash command, context menu
|
||||||
op.push({
|
// Type: 4: Auto-complete
|
||||||
type,
|
|
||||||
name,
|
|
||||||
value,
|
|
||||||
focused: true,
|
|
||||||
});
|
|
||||||
let nonce = SnowflakeUtil.generate();
|
|
||||||
const data = {
|
|
||||||
type: 4, // Auto-complete
|
|
||||||
application_id: this.applicationId,
|
application_id: this.applicationId,
|
||||||
guild_id: message.guildId,
|
guild_id: message.guildId,
|
||||||
channel_id: message.channelId,
|
channel_id: message.channelId,
|
||||||
@ -655,11 +801,36 @@ class ApplicationCommand extends Base {
|
|||||||
id: this.id,
|
id: this.id,
|
||||||
name: this.name,
|
name: this.name,
|
||||||
type: ApplicationCommandTypes[this.type],
|
type: ApplicationCommandTypes[this.type],
|
||||||
options: op,
|
options: dataAdd,
|
||||||
|
attachments: attachments,
|
||||||
|
guild_id: message.guildId,
|
||||||
|
},
|
||||||
|
nonce,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
type: autocomplete ? 4 : 2, // Slash command, context menu
|
||||||
|
// Type: 4: Auto-complete
|
||||||
|
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: dataAdd,
|
||||||
attachments: attachments,
|
attachments: attachments,
|
||||||
},
|
},
|
||||||
nonce,
|
nonce,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const getAutoResponse = async (sendData, value) => {
|
||||||
|
let nonce = SnowflakeUtil.generate();
|
||||||
|
const data = getDataPost(false, sendData, nonce, true);
|
||||||
await this.client.api.interactions
|
await this.client.api.interactions
|
||||||
.post({
|
.post({
|
||||||
data,
|
data,
|
||||||
@ -667,10 +838,9 @@ class ApplicationCommand extends Base {
|
|||||||
})
|
})
|
||||||
.catch(async () => {
|
.catch(async () => {
|
||||||
nonce = SnowflakeUtil.generate();
|
nonce = SnowflakeUtil.generate();
|
||||||
data.data.guild_id = message.guildId;
|
const data_ = getDataPost(true, sendData, nonce);
|
||||||
data.nonce = nonce;
|
|
||||||
await this.client.api.interactions.post({
|
await this.client.api.interactions.post({
|
||||||
data,
|
body: data_,
|
||||||
files: attachmentsBuffer,
|
files: attachmentsBuffer,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -693,112 +863,9 @@ class ApplicationCommand extends Base {
|
|||||||
this.client.on(Events.APPLICATION_COMMAND_AUTOCOMPLETE_RESPONSE, handler);
|
this.client.on(Events.APPLICATION_COMMAND_AUTOCOMPLETE_RESPONSE, handler);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
for (i; i < options.length; i++) {
|
const sendData = async (optionsData = []) => {
|
||||||
const value = options[i];
|
|
||||||
if (!subCommandCheck && !this?.options[i]) continue;
|
|
||||||
if (subCommandCheck && subCommand?.options && !subCommand?.options[i]) continue;
|
|
||||||
if (!subCommandCheck) {
|
|
||||||
// Check value is invalid
|
|
||||||
let choice;
|
|
||||||
if (this.options[i].choices && this.options[i].choices.length > 0) {
|
|
||||||
choice =
|
|
||||||
this.options[i].choices.find(c => c.name == value) || this.options[i].choices.find(c => c.value == value);
|
|
||||||
if (!choice && value !== undefined) {
|
|
||||||
throw new Error(
|
|
||||||
`Invalid option: ${value} is not a valid choice for this option\nList of choices:\n${this.options[
|
|
||||||
i
|
|
||||||
].choices
|
|
||||||
.map((c, i) => ` #${i + 1} Name: ${c.name} Value: ${c.value}`)
|
|
||||||
.join('\n')}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const data = {
|
|
||||||
type: ApplicationCommandOptionTypes[this.options[i].type],
|
|
||||||
name: this.options[i].name,
|
|
||||||
value:
|
|
||||||
choice?.value || this.options[i].type == 'ATTACHMENT'
|
|
||||||
? await addDataFromAttachment(value)
|
|
||||||
: this.options[i].type == 'INTEGER'
|
|
||||||
? Number(value)
|
|
||||||
: this.options[i].type == 'BOOLEAN'
|
|
||||||
? Boolean(value)
|
|
||||||
: this.options[i].autocomplete
|
|
||||||
? await getAutoResponse(
|
|
||||||
optionFormat,
|
|
||||||
ApplicationCommandOptionTypes[this.options[i].type],
|
|
||||||
this.options[i].name,
|
|
||||||
value,
|
|
||||||
)
|
|
||||||
: value,
|
|
||||||
};
|
|
||||||
if (value !== undefined) optionFormat.push(data);
|
|
||||||
} else {
|
|
||||||
// First element is sub command and removed
|
|
||||||
if (!value) continue;
|
|
||||||
// Check value is invalid
|
|
||||||
let choice;
|
|
||||||
if (subCommand?.options && subCommand.options[i].choices && subCommand.options[i].choices.length > 0) {
|
|
||||||
choice =
|
|
||||||
subCommand.options[i].choices.find(c => c.name == value) ||
|
|
||||||
subCommand.options[i].choices.find(c => c.value == value);
|
|
||||||
if (!choice && value !== undefined) {
|
|
||||||
throw new Error(
|
|
||||||
`Invalid option: ${value} is not a valid choice for this option\nList of choices: \n${subCommand.options[
|
|
||||||
i
|
|
||||||
].choices
|
|
||||||
.map((c, i) => `#${i + 1} Name: ${c.name} Value: ${c.value}\n`)
|
|
||||||
.join('')}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const data = {
|
|
||||||
type: ApplicationCommandOptionTypes[subCommand.options[i].type],
|
|
||||||
name: subCommand.options[i].name,
|
|
||||||
value:
|
|
||||||
choice?.value || subCommand.options[i].type == 'ATTACHMENT'
|
|
||||||
? await addDataFromAttachment(value)
|
|
||||||
: subCommand.options[i].type == 'INTEGER'
|
|
||||||
? Number(value)
|
|
||||||
: subCommand.options[i].type == 'BOOLEAN'
|
|
||||||
? Boolean(value)
|
|
||||||
: this.options[i].autocomplete
|
|
||||||
? await getAutoResponse(
|
|
||||||
optionFormat,
|
|
||||||
ApplicationCommandOptionTypes[subCommand.options[i].type],
|
|
||||||
subCommand.options[i].name,
|
|
||||||
value,
|
|
||||||
)
|
|
||||||
: value,
|
|
||||||
};
|
|
||||||
if (value !== undefined) optionFormat.push(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!subCommandCheck && this.options[i]?.required) {
|
|
||||||
throw new Error('Value required missing');
|
|
||||||
}
|
|
||||||
if (subCommandCheck && subCommand?.options && subCommand?.options[i]?.required) {
|
|
||||||
throw new Error('Value required missing');
|
|
||||||
}
|
|
||||||
let nonce = SnowflakeUtil.generate();
|
let nonce = SnowflakeUtil.generate();
|
||||||
const data = {
|
const data = getDataPost(false, optionsData, nonce);
|
||||||
type: 2, // Slash command, context menu
|
|
||||||
// Type: 4: Auto-complete
|
|
||||||
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: option_,
|
|
||||||
attachments: attachments,
|
|
||||||
},
|
|
||||||
nonce,
|
|
||||||
};
|
|
||||||
await this.client.api.interactions
|
await this.client.api.interactions
|
||||||
.post({
|
.post({
|
||||||
body: data,
|
body: data,
|
||||||
@ -806,10 +873,9 @@ class ApplicationCommand extends Base {
|
|||||||
})
|
})
|
||||||
.catch(async () => {
|
.catch(async () => {
|
||||||
nonce = SnowflakeUtil.generate();
|
nonce = SnowflakeUtil.generate();
|
||||||
data.data.guild_id = message.guildId;
|
const data_ = getDataPost(true, optionsData, nonce);
|
||||||
data.nonce = nonce;
|
|
||||||
await this.client.api.interactions.post({
|
await this.client.api.interactions.post({
|
||||||
body: data,
|
body: data_,
|
||||||
files: attachmentsBuffer,
|
files: attachmentsBuffer,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -831,55 +897,34 @@ class ApplicationCommand extends Base {
|
|||||||
this.client.incrementMaxListeners();
|
this.client.incrementMaxListeners();
|
||||||
this.client.on('interactionResponse', handler);
|
this.client.on('interactionResponse', handler);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Message Context Menu
|
|
||||||
* @param {Message} message Discord Message
|
|
||||||
* @param {boolean} sendFromMessage nothing .-. not used
|
|
||||||
* @returns {Promise<InteractionResponseBody>}
|
|
||||||
*/
|
|
||||||
async sendContextMenu(message, sendFromMessage = false) {
|
|
||||||
if (!sendFromMessage && !(message instanceof Message())) {
|
|
||||||
throw new TypeError('The message must be a Discord.Message');
|
|
||||||
}
|
|
||||||
if (this.type == 'CHAT_INPUT') return false;
|
|
||||||
const nonce = SnowflakeUtil.generate();
|
|
||||||
await this.client.api.interactions.post({
|
|
||||||
body: {
|
|
||||||
type: 2, // Slash command, context menu
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
nonce,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const handler = data => {
|
|
||||||
timeout.refresh();
|
|
||||||
if (data.metadata.nonce !== nonce) return;
|
|
||||||
clearTimeout(timeout);
|
|
||||||
this.client.removeListener('interactionResponse', handler);
|
|
||||||
this.client.decrementMaxListeners();
|
|
||||||
if (data.status) resolve(data.metadata);
|
|
||||||
else reject(data.metadata);
|
|
||||||
};
|
};
|
||||||
const timeout = setTimeout(() => {
|
// SubCommandArray length max 2
|
||||||
this.client.removeListener('interactionResponse', handler);
|
// length = 0 => no sub command
|
||||||
this.client.decrementMaxListeners();
|
// length = 1 => sub command
|
||||||
reject(new Error('INTERACTION_TIMEOUT'));
|
// length = 2 => sub command group + sub command
|
||||||
}, 15_000).unref();
|
switch (subCommandArray.length) {
|
||||||
this.client.incrementMaxListeners();
|
case 0: {
|
||||||
this.client.on('interactionResponse', handler);
|
const valueRequired = this.options.filter(o => o.required).length;
|
||||||
});
|
for (let i = 0; i < options.length; i++) {
|
||||||
|
const optionInput = this.options[i];
|
||||||
|
const value = options[i];
|
||||||
|
await parseOption(optionInput, value);
|
||||||
|
}
|
||||||
|
if (valueRequired > options.length) {
|
||||||
|
throw new Error(`Value required missing\nDebug:
|
||||||
|
Required: ${valueRequired} - Options: ${optionFormat.length}`);
|
||||||
|
}
|
||||||
|
return sendData(optionFormat);
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
const optionsData = await parseSubCommand(subCommandArray[0], options);
|
||||||
|
return sendData(optionsData);
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
const optionsData = await parseSubGroupCommand(subCommandArray[0], subCommandArray[1], options);
|
||||||
|
return sendData(optionsData);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,14 @@ const { TypeError, Error } = require('../../errors');
|
|||||||
const InteractionCollector = require('../InteractionCollector');
|
const InteractionCollector = require('../InteractionCollector');
|
||||||
const { lazy } = require('../../util/Util');
|
const { lazy } = require('../../util/Util');
|
||||||
const Message = lazy(() => require('../Message').Message);
|
const Message = lazy(() => require('../Message').Message);
|
||||||
|
const { s } = require('@sapphire/shapeshift');
|
||||||
|
const validateName = stringName =>
|
||||||
|
s.string
|
||||||
|
.lengthGreaterThanOrEqual(1)
|
||||||
|
.lengthLessThanOrEqual(32)
|
||||||
|
.regex(/^[\p{Ll}\p{Lm}\p{Lo}\p{N}\p{sc=Devanagari}\p{sc=Thai}_-]+$/u)
|
||||||
|
.setValidationEnabled(true)
|
||||||
|
.parse(stringName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for classes that have text-channel-like features.
|
* Interface for classes that have text-channel-like features.
|
||||||
@ -395,7 +403,7 @@ class TextBasedChannel {
|
|||||||
/**
|
/**
|
||||||
* Send Slash to this channel
|
* Send Slash to this channel
|
||||||
* @param {Snowflake} botId Bot Id (Supports application ID - not bot)
|
* @param {Snowflake} botId Bot Id (Supports application ID - not bot)
|
||||||
* @param {string} commandName Command name
|
* @param {string} commandString Command name (and sub / group formats)
|
||||||
* @param {...?string|string[]} args Command arguments
|
* @param {...?string|string[]} args Command arguments
|
||||||
* @returns {Promise<InteractionResponseBody>}
|
* @returns {Promise<InteractionResponseBody>}
|
||||||
* @example
|
* @example
|
||||||
@ -407,9 +415,21 @@ class TextBasedChannel {
|
|||||||
* channel.sendSlash('123456789012345678', 'embed', 'title', 'description', 'author', '#00ff00')
|
* channel.sendSlash('123456789012345678', 'embed', 'title', 'description', 'author', '#00ff00')
|
||||||
* // Send embed with Title and Color:
|
* // Send embed with Title and Color:
|
||||||
* channel.sendSlash('123456789012345678', 'embed', 'title', undefined, undefined, '#00ff00')
|
* channel.sendSlash('123456789012345678', 'embed', 'title', undefined, undefined, '#00ff00')
|
||||||
|
* // CommandName is Group Command / Sub Command
|
||||||
|
* channel.sendSlash('123456789012345678', 'embed title', 'description', 'author', '#00ff00')
|
||||||
*/
|
*/
|
||||||
async sendSlash(botId, commandName, ...args) {
|
async sendSlash(botId, commandString, ...args) {
|
||||||
args = args.flat(2);
|
args = args.flat(2);
|
||||||
|
const cmd = commandString.trim().split(' ');
|
||||||
|
// Validate CommandName
|
||||||
|
const commandName = validateName(cmd[0]);
|
||||||
|
const sub = cmd.slice(1);
|
||||||
|
for (let i = 0; i < sub.length; i++) {
|
||||||
|
if (sub.length > 2) {
|
||||||
|
throw new Error('INVALID_COMMAND_NAME', cmd);
|
||||||
|
}
|
||||||
|
validateName(sub[i]);
|
||||||
|
}
|
||||||
if (!botId) throw new Error('Bot ID is required');
|
if (!botId) throw new Error('Bot ID is required');
|
||||||
// ? maybe ...
|
// ? maybe ...
|
||||||
const user = await this.client.users.fetch(botId).catch(() => {});
|
const user = await this.client.users.fetch(botId).catch(() => {});
|
||||||
@ -463,6 +483,7 @@ class TextBasedChannel {
|
|||||||
content: '',
|
content: '',
|
||||||
id: this.client.user.id,
|
id: this.client.user.id,
|
||||||
}),
|
}),
|
||||||
|
sub && sub.length > 0 ? sub : [],
|
||||||
args && args.length ? args : [],
|
args && args.length ? args : [],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
2
typings/index.d.ts
vendored
2
typings/index.d.ts
vendored
@ -474,7 +474,7 @@ export class ApplicationCommand<PermissionsFetchType = {}> extends Base {
|
|||||||
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
|
// Add
|
||||||
public static sendSlashCommand(message: Message, options?: string[]): Promise<InteractionResponseBody>;
|
public static sendSlashCommand(message: Message, subCommandArray?: string[], options?: string[]): Promise<InteractionResponseBody>;
|
||||||
public static sendContextMenu(message: Message): Promise<InteractionResponseBody>;
|
public static sendContextMenu(message: Message): Promise<InteractionResponseBody>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user