Merge pull request #343 from aiko-chan-ai/modal-reply

feat: awaitModal
This commit is contained in:
Cinnamon 2022-10-09 19:37:40 +07:00 committed by GitHub
commit 5fa5b32e49
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 335 additions and 67 deletions

View File

@ -204,6 +204,8 @@ class Client extends BaseClient {
this.token = null; this.token = null;
} }
this._interactionCache = new Collection();
/** /**
* User that the client is logged in as * User that the client is logged in as
* @type {?ClientUser} * @type {?ClientUser}
@ -340,7 +342,7 @@ class Client extends BaseClient {
this.emit(Events.DEBUG, `Added Fingerprint: ${res.data.fingerprint}`); this.emit(Events.DEBUG, `Added Fingerprint: ${res.data.fingerprint}`);
}) })
.catch(err => { .catch(err => {
this.emit(Events.DEBUG, `Finding Cookie and Fingerprint failed: ${err.message}`); this.emit(Events.DEBUG, `Update Cookie and Fingerprint failed: ${err.message}`);
}); });
} }

View File

@ -12,4 +12,6 @@ module.exports = (client, { d: data }) => {
status: false, status: false,
metadata: data, metadata: data,
}); });
// Delete cache
client._interactionCache.delete(data.nonce);
}; };

View File

@ -8,8 +8,21 @@ module.exports = (client, { d: data }) => {
* @param {InteractionResponseBody} data data * @param {InteractionResponseBody} data data
*/ */
client.emit(Events.INTERACTION_SUCCESS, data); client.emit(Events.INTERACTION_SUCCESS, data);
// Get channel data
const cache = client._interactionCache.get(data.nonce);
const channel = cache.guildId
? client.guilds.cache.get(cache.guildId)?.channels.cache.get(cache.channelId)
: client.channels.cache.get(cache.channelId);
// Set data
const interaction = {
...cache,
...data,
};
const data_ = channel.interactions._add(interaction);
client.emit('interactionResponse', { client.emit('interactionResponse', {
status: true, status: true,
metadata: data, metadata: data_,
}); });
// Delete cache
// client._interactionCache.delete(data.nonce);
}; };

View File

@ -0,0 +1,39 @@
'use strict';
const CachedManager = require('./CachedManager');
const InteractionResponse = require('../structures/InteractionResponse');
/**
* Manages API methods for InteractionResponse and holds their cache.
* @extends {CachedManager}
*/
class InteractionManager extends CachedManager {
constructor(channel, iterable) {
super(channel.client, InteractionResponse, iterable);
/**
* The channel that the messages belong to
* @type {TextBasedChannels}
*/
this.channel = channel;
}
/**
* The cache of InteractionResponse
* @type {Collection<Snowflake, InteractionResponse>}
* @name InteractionManager#cache
*/
_add(data, cache) {
data = {
...data,
channelId: this.channel.id,
guildId: this.channel.guild?.id,
};
if (!data.id) return;
// eslint-disable-next-line consistent-return
return super._add(data, cache);
}
}
module.exports = InteractionManager;

View File

@ -597,7 +597,7 @@ class ApplicationCommand extends Base {
* @param {Message} message Discord Message * @param {Message} message Discord Message
* @param {Array<string>} subCommandArray SubCommand Array * @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<InteractionResponse>}
*/ */
// eslint-disable-next-line consistent-return // eslint-disable-next-line consistent-return
async sendSlashCommand(message, subCommandArray = [], options = []) { async sendSlashCommand(message, subCommandArray = [], options = []) {
@ -861,6 +861,11 @@ class ApplicationCommand extends Base {
body: data, body: data,
files: attachmentsBuffer, files: attachmentsBuffer,
}); });
this.client._interactionCache.set(nonce, {
channelId: message.channelId,
guildId: message.guildId,
metadata: data,
});
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const handler = data => { const handler = data => {
timeout.refresh(); timeout.refresh();
@ -911,7 +916,7 @@ class ApplicationCommand extends Base {
/** /**
* Message Context Menu * Message Context Menu
* @param {Message} message Discord Message * @param {Message} message Discord Message
* @returns {Promise<InteractionResponseBody>} * @returns {Promise<InteractionResponse>}
*/ */
async sendContextMenu(message) { async sendContextMenu(message) {
if (!(message instanceof Message())) { if (!(message instanceof Message())) {
@ -941,6 +946,11 @@ class ApplicationCommand extends Base {
await this.client.api.interactions.post({ await this.client.api.interactions.post({
body: data, body: data,
}); });
this.client._interactionCache.set(nonce, {
channelId: message.channelId,
guildId: message.guildId,
metadata: data,
});
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const handler = data => { const handler = data => {
timeout.refresh(); timeout.refresh();

View File

@ -3,6 +3,7 @@
const GuildChannel = require('./GuildChannel'); const GuildChannel = require('./GuildChannel');
const TextBasedChannel = require('./interfaces/TextBasedChannel'); const TextBasedChannel = require('./interfaces/TextBasedChannel');
const GuildTextThreadManager = require('../managers/GuildTextThreadManager'); const GuildTextThreadManager = require('../managers/GuildTextThreadManager');
const InteractionManager = require('../managers/InteractionManager');
const MessageManager = require('../managers/MessageManager'); const MessageManager = require('../managers/MessageManager');
/** /**
@ -20,6 +21,12 @@ class BaseGuildTextChannel extends GuildChannel {
*/ */
this.messages = new MessageManager(this); this.messages = new MessageManager(this);
/**
* A manager of the interactions sent to this channel
* @type {InteractionManager}
*/
this.interactions = new InteractionManager(this);
/** /**
* A manager of the threads belonging to this channel * A manager of the threads belonging to this channel
* @type {GuildTextThreadManager} * @type {GuildTextThreadManager}

View File

@ -4,6 +4,7 @@ const { Collection } = require('@discordjs/collection');
const { joinVoiceChannel, entersState, VoiceConnectionStatus } = require('@discordjs/voice'); const { joinVoiceChannel, entersState, VoiceConnectionStatus } = require('@discordjs/voice');
const { Channel } = require('./Channel'); const { Channel } = require('./Channel');
const TextBasedChannel = require('./interfaces/TextBasedChannel'); const TextBasedChannel = require('./interfaces/TextBasedChannel');
const InteractionManager = require('../managers/InteractionManager');
const MessageManager = require('../managers/MessageManager'); const MessageManager = require('../managers/MessageManager');
const { Status, Opcodes } = require('../util/Constants'); const { Status, Opcodes } = require('../util/Constants');
@ -24,6 +25,12 @@ class DMChannel extends Channel {
* @type {MessageManager} * @type {MessageManager}
*/ */
this.messages = new MessageManager(this); this.messages = new MessageManager(this);
/**
* A manager of the interactions sent to this channel
* @type {InteractionManager}
*/
this.interactions = new InteractionManager(this);
} }
_patch(data) { _patch(data) {

View File

@ -0,0 +1,113 @@
'use strict';
const { setTimeout } = require('node:timers');
const Base = require('./Base');
const { Events } = require('../util/Constants');
const SnowflakeUtil = require('../util/SnowflakeUtil');
/**
* Represents a interaction on Discord.
* @extends {Base}
*/
class InteractionResponse extends Base {
constructor(client, data) {
super(client);
/**
* The id of the channel the interaction was sent in
* @type {Snowflake}
*/
this.channelId = data.channelId;
/**
* The id of the guild the interaction was sent in, if any
* @type {?Snowflake}
*/
this.guildId = data.guildId ?? this.channel?.guild?.id ?? null;
/**
* The interaction data was sent in
* @type {Object}
*/
this.sendData = data.metadata;
this._patch(data);
}
_patch(data) {
if ('id' in data) {
/**
* The interaction response's ID
* @type {Snowflake}
*/
this.id = data.id;
}
if ('nonce' in data) {
/**
* The interaction response's nonce
* @type {Snowflake}
*/
this.nonce = data.nonce;
}
}
/**
* The timestamp the interaction response was created at
* @type {number}
* @readonly
*/
get createdTimestamp() {
return SnowflakeUtil.timestampFrom(this.id);
}
/**
* The time the interaction response was created at
* @type {Date}
* @readonly
*/
get createdAt() {
return new Date(this.createdTimestamp);
}
/**
* The channel that the interaction was sent in
* @type {TextBasedChannels}
* @readonly
*/
get channel() {
return this.client.channels.resolve(this.channelId);
}
/**
* The guild the inteaaction was sent in (if in a guild channel)
* @type {?Guild}
* @readonly
*/
get guild() {
return this.client.guilds.resolve(this.guildId) ?? this.channel?.guild ?? null;
}
/**
* Get Modal send from interaction
* @param {?number} time Time to wait for modal (Default: 120000)
* @returns {Modal}
*/
awaitModal(time = 120_000) {
return new Promise((resolve, reject) => {
const handler = modal => {
timeout.refresh();
if (modal.nonce != this.nonce || modal.id != this.id) return;
clearTimeout(timeout);
this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
this.client.decrementMaxListeners();
resolve(modal);
};
const timeout = setTimeout(() => {
this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
this.client.decrementMaxListeners();
reject(new Error('MODAL_TIMEOUT'));
}, time || 120_000).unref();
this.client.incrementMaxListeners();
this.client.on(Events.INTERACTION_MODAL_CREATE, handler);
});
}
}
module.exports = InteractionResponse;

View File

@ -1043,7 +1043,7 @@ class Message extends Base {
/** /**
* Click specific button * Click specific button
* @param {MessageButton|string} button Button ID * @param {MessageButton|string} button Button ID
* @returns {Promise<InteractionResponseBody>} * @returns {Promise<InteractionResponse>}
*/ */
clickButton(button) { clickButton(button) {
let buttonID; let buttonID;
@ -1066,7 +1066,7 @@ class Message extends Base {
* Select specific menu or First Menu * Select specific menu or First Menu
* @param {string|Array<string>} menuID Select Menu specific id or auto select first Menu * @param {string|Array<string>} menuID Select Menu specific id or auto select first Menu
* @param {Array<string>} options Menu Options * @param {Array<string>} options Menu Options
* @returns {Promise<InteractionResponseBody>} * @returns {Promise<InteractionResponse>}
*/ */
async selectMenu(menuID, options = []) { async selectMenu(menuID, options = []) {
if (!this.components[0]) throw new TypeError('MESSAGE_NO_COMPONENTS'); if (!this.components[0]) throw new TypeError('MESSAGE_NO_COMPONENTS');
@ -1100,7 +1100,7 @@ class Message extends Base {
* Send context Menu v2 * Send context Menu v2
* @param {Snowflake} botId Bot id * @param {Snowflake} botId Bot id
* @param {string} commandName Command name in Context Menu * @param {string} commandName Command name in Context Menu
* @returns {Promise<InteractionResponseBody>} * @returns {Promise<InteractionResponse>}
*/ */
async contextMenu(botId, commandName) { async contextMenu(botId, commandName) {
if (!botId) throw new Error('Bot ID is required'); if (!botId) throw new Error('Bot ID is required');

View File

@ -168,14 +168,13 @@ class MessageButton extends BaseMessageComponent {
/** /**
* Click the button * Click the button
* @param {Message} message Discord Message * @param {Message} message Discord Message
* @returns {Promise<InteractionResponseBody>} * @returns {Promise<InteractionResponse>}
*/ */
async click(message) { async click(message) {
const nonce = SnowflakeUtil.generate(); const nonce = SnowflakeUtil.generate();
if (!(message instanceof Message())) throw new Error('[UNKNOWN_MESSAGE] Please pass a valid Message'); if (!(message instanceof Message())) throw new Error('[UNKNOWN_MESSAGE] Please pass a valid Message');
if (!this.customId || this.style == 5 || this.disabled) return false; // Button URL, Disabled if (!this.customId || this.style == 5 || this.disabled) return false; // Button URL, Disabled
await message.client.api.interactions.post({ const data = {
data: {
type: 3, // ? type: 3, // ?
nonce, nonce,
guild_id: message.guild?.id ?? null, // In DMs guild_id: message.guild?.id ?? null, // In DMs
@ -188,7 +187,14 @@ class MessageButton extends BaseMessageComponent {
component_type: 2, // Button component_type: 2, // Button
custom_id: this.customId, custom_id: this.customId,
}, },
}, };
await message.client.api.interactions.post({
data,
});
message.client._interactionCache.set(nonce, {
channelId: message.channelId,
guildId: message.guildId,
metadata: data,
}); });
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const handler = data => { const handler = data => {

View File

@ -215,7 +215,7 @@ class MessageSelectMenu extends BaseMessageComponent {
* Mesage select menu * Mesage select menu
* @param {Message} message The message this select menu is for * @param {Message} message The message this select menu is for
* @param {Array<string>} values The values of the select menu * @param {Array<string>} values The values of the select menu
* @returns {Promise<InteractionResponseBody>} * @returns {Promise<InteractionResponse>}
*/ */
async select(message, values = []) { async select(message, values = []) {
// Github copilot is the best :)) // Github copilot is the best :))
@ -242,8 +242,7 @@ class MessageSelectMenu extends BaseMessageComponent {
); );
} }
const nonce = SnowflakeUtil.generate(); const nonce = SnowflakeUtil.generate();
await message.client.api.interactions.post({ const data = {
data: {
type: 3, // ? type: 3, // ?
guild_id: message.guild?.id ?? null, // In DMs guild_id: message.guild?.id ?? null, // In DMs
channel_id: message.channel.id, channel_id: message.channel.id,
@ -258,7 +257,14 @@ class MessageSelectMenu extends BaseMessageComponent {
values, values,
}, },
nonce, nonce,
}, };
await message.client.api.interactions.post({
data,
});
message.client._interactionCache.set(nonce, {
channelId: message.channelId,
guildId: message.guildId,
metadata: data,
}); });
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const handler = data => { const handler = data => {

View File

@ -59,13 +59,29 @@ class Modal {
this.application = data.application this.application = data.application
? { ? {
...data.application, ...data.application,
bot: data.application.bot ? new User(client, data.application.bot) : null, bot: data.application.bot ? new User(client, data.application.bot, data.application) : null,
} }
: null; : null;
this.client = client; this.client = client;
} }
/**
* Get Interaction Response
* @type {?InteractionResponse}
* @readonly
*/
get sendFromInteraction() {
if (this.id && this.nonce && this.client) {
const cache = this.client._interactionCache.get(this.nonce);
const channel = cache.guildId
? this.client.guilds.cache.get(cache.guildId)?.channels.cache.get(cache.channelId)
: this.client.channels.cache.get(cache.channelId);
return channel.interactions.cache.get(this.id);
}
return null;
}
/** /**
* Adds components to the modal. * Adds components to the modal.
* @param {...MessageActionRowResolvable[]} components The components to add * @param {...MessageActionRowResolvable[]} components The components to add
@ -135,24 +151,27 @@ class Modal {
/** /**
* @typedef {Object} ModalReplyData * @typedef {Object} ModalReplyData
* @property {GuildResolvable} [guild] Guild to send the modal to * @property {?GuildResolvable} [guild] Guild to send the modal to
* @property {TextChannelResolvable} [channel] User to send the modal to * @property {?TextChannelResolvable} [channel] User to send the modal to
* @property {TextInputComponentReplyData[]} [data] Reply data * @property {TextInputComponentReplyData[]} [data] Reply data
*/ */
/** /**
* Reply to this modal with data. (Event only) * Reply to this modal with data. (Event only)
* @param {ModalReplyData} data Data to send with the modal * @param {ModalReplyData} data Data to send with the modal
* @returns {Promise<InteractionResponseBody>} * @returns {Promise<InteractionResponse>}
* @example * @example
* // With Event
* client.on('interactionModalCreate', modal => { * client.on('interactionModalCreate', modal => {
* modal.reply('guildId', 'channelId', { * modal.reply({
* data: [
* {
* customId: 'code', * customId: 'code',
* value: '1+1' * value: '1+1'
* }, { * }, {
* customId: 'message', * customId: 'message',
* value: 'hello' * value: 'hello'
* }
* ]
* }) * })
* }) * })
*/ */
@ -160,8 +179,9 @@ class Modal {
if (typeof data !== 'object') throw new TypeError('ModalReplyData must be an object'); if (typeof data !== 'object') throw new TypeError('ModalReplyData must be an object');
if (!Array.isArray(data.data)) throw new TypeError('ModalReplyData.data must be an array'); if (!Array.isArray(data.data)) throw new TypeError('ModalReplyData.data must be an array');
if (!this.application) throw new Error('Modal cannot reply (Missing Application)'); if (!this.application) throw new Error('Modal cannot reply (Missing Application)');
const guild = this.client.guilds.resolveId(data.guild); const data_cache = this.sendFromInteraction;
const channel = this.client.channels.resolveId(data.channel); const guild = this.client.guilds.resolveId(data.guild) || data_cache.guildId || null;
const channel = this.client.channels.resolveId(data.channel) || data_cache.channelId;
// Add data to components // Add data to components
// this.components = [ MessageActionRow.components = [ TextInputComponent ] ] // this.components = [ MessageActionRow.components = [ TextInputComponent ] ]
// 5 MessageActionRow / Modal, 1 TextInputComponent / 1 MessageActionRow // 5 MessageActionRow / Modal, 1 TextInputComponent / 1 MessageActionRow

View File

@ -3,6 +3,7 @@
const { Channel } = require('./Channel'); const { Channel } = require('./Channel');
const TextBasedChannel = require('./interfaces/TextBasedChannel'); const TextBasedChannel = require('./interfaces/TextBasedChannel');
const { RangeError } = require('../errors'); const { RangeError } = require('../errors');
const InteractionManager = require('../managers/InteractionManager');
const MessageManager = require('../managers/MessageManager'); const MessageManager = require('../managers/MessageManager');
const ThreadMemberManager = require('../managers/ThreadMemberManager'); const ThreadMemberManager = require('../managers/ThreadMemberManager');
const Permissions = require('../util/Permissions'); const Permissions = require('../util/Permissions');
@ -35,6 +36,12 @@ class ThreadChannel extends Channel {
*/ */
this.messages = new MessageManager(this); this.messages = new MessageManager(this);
/**
* A manager of the interactions sent to this channel
* @type {InteractionManager}
*/
this.interactions = new InteractionManager(this);
/** /**
* A manager of the members that are part of this thread * A manager of the members that are part of this thread
* @type {ThreadMemberManager} * @type {ThreadMemberManager}

View File

@ -16,7 +16,7 @@ const UserFlags = require('../util/UserFlags');
* @extends {Base} * @extends {Base}
*/ */
class User extends Base { class User extends Base {
constructor(client, data) { constructor(client, data, application) {
super(client); super(client);
/** /**
* The user's id * The user's id
@ -78,7 +78,7 @@ class User extends Base {
* @type {?ClientApplication} * @type {?ClientApplication}
* @readonly * @readonly
*/ */
this.application = null; this.application = application ? new ClientApplication(this.client, application, this) : null;
this._partial = true; this._partial = true;
this._patch(data); this._patch(data);
} }
@ -100,7 +100,7 @@ class User extends Base {
* @type {?boolean} * @type {?boolean}
*/ */
this.bot = Boolean(data.bot); this.bot = Boolean(data.bot);
if (this.bot === true) { if (this.bot === true && !this.application) {
this.application = new ClientApplication(this.client, { id: this.id }, this); this.application = new ClientApplication(this.client, { id: this.id }, this);
this.botInGuildsCount = null; this.botInGuildsCount = null;
} }

View File

@ -3,6 +3,7 @@
const process = require('node:process'); const process = require('node:process');
const BaseGuildVoiceChannel = require('./BaseGuildVoiceChannel'); const BaseGuildVoiceChannel = require('./BaseGuildVoiceChannel');
const TextBasedChannel = require('./interfaces/TextBasedChannel'); const TextBasedChannel = require('./interfaces/TextBasedChannel');
const InteractionManager = require('../managers/InteractionManager');
const MessageManager = require('../managers/MessageManager'); const MessageManager = require('../managers/MessageManager');
const { VideoQualityModes } = require('../util/Constants'); const { VideoQualityModes } = require('../util/Constants');
const Permissions = require('../util/Permissions'); const Permissions = require('../util/Permissions');
@ -24,6 +25,12 @@ class VoiceChannel extends BaseGuildVoiceChannel {
*/ */
this.messages = new MessageManager(this); this.messages = new MessageManager(this);
/**
* A manager of the interactions sent to this channel
* @type {InteractionManager}
*/
this.interactions = new InteractionManager(this);
/** /**
* If the guild considers this channel NSFW * If the guild considers this channel NSFW
* @type {boolean} * @type {boolean}

View File

@ -1,6 +1,7 @@
'use strict'; 'use strict';
/* eslint-disable import/order */ /* eslint-disable import/order */
const InteractionManager = require('../../managers/InteractionManager');
const MessageCollector = require('../MessageCollector'); const MessageCollector = require('../MessageCollector');
const MessagePayload = require('../MessagePayload'); const MessagePayload = require('../MessagePayload');
const SnowflakeUtil = require('../../util/SnowflakeUtil'); const SnowflakeUtil = require('../../util/SnowflakeUtil');
@ -31,6 +32,12 @@ class TextBasedChannel {
*/ */
this.messages = new MessageManager(this); this.messages = new MessageManager(this);
/**
* A manager of the interactions sent to this channel
* @type {InteractionManager}
*/
this.interactions = new InteractionManager(this);
/** /**
* The channel's last message id, if one was sent * The channel's last message id, if one was sent
* @type {?Snowflake} * @type {?Snowflake}
@ -405,7 +412,7 @@ class TextBasedChannel {
* @param {UserResolvable} bot Bot user * @param {UserResolvable} bot Bot user
* @param {string} commandString Command name (and sub / group formats) * @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<InteractionResponse>}
* @example * @example
* // Send Slash to this channel * // Send Slash to this channel
* // Demo: * // Demo:

48
typings/index.d.ts vendored
View File

@ -509,8 +509,8 @@ export class ApplicationCommand<PermissionsFetchType = {}> extends Base {
message: Message, message: Message,
subCommandArray?: string[], subCommandArray?: string[],
options?: string[], options?: string[],
): Promise<InteractionResponseBody>; ): Promise<InteractionResponse>;
public static sendContextMenu(message: Message): Promise<InteractionResponseBody>; public static sendContextMenu(message: Message): Promise<InteractionResponse>;
} }
export type ApplicationResolvable = Application | Activity | Snowflake; export type ApplicationResolvable = Application | Activity | Snowflake;
@ -1934,10 +1934,10 @@ export class Message<Cached extends boolean = boolean> extends Base {
// Added // Added
public markUnread(): Promise<boolean>; public markUnread(): Promise<boolean>;
public markRead(): Promise<boolean>; public markRead(): Promise<boolean>;
public clickButton(button: MessageButton | string): Promise<InteractionResponseBody>; public clickButton(button: MessageButton | string): Promise<InteractionResponse>;
public selectMenu(menuID: string, options: string[]): Promise<InteractionResponseBody>; public selectMenu(menuID: string, options: string[]): Promise<InteractionResponse>;
public selectMenu(options: string[]): Promise<InteractionResponseBody>; public selectMenu(options: string[]): Promise<InteractionResponse>;
public contextMenu(botID: Snowflake, commandName: string): Promise<InteractionResponseBody>; public contextMenu(botID: Snowflake, commandName: string): Promise<InteractionResponse>;
} }
export class MessageActionRow< export class MessageActionRow<
@ -2001,7 +2001,7 @@ export class MessageButton extends BaseMessageComponent {
public setStyle(style: MessageButtonStyleResolvable): this; public setStyle(style: MessageButtonStyleResolvable): this;
public setURL(url: string): this; public setURL(url: string): this;
public toJSON(): APIButtonComponent; public toJSON(): APIButtonComponent;
public click(message: Message): Promise<InteractionResponseBody>; public click(message: Message): Promise<InteractionResponse>;
private static resolveStyle(style: MessageButtonStyleResolvable): MessageButtonStyle; private static resolveStyle(style: MessageButtonStyleResolvable): MessageButtonStyle;
} }
@ -2241,7 +2241,7 @@ export class MessageSelectMenu extends BaseMessageComponent {
...options: MessageSelectOptionData[] | MessageSelectOptionData[][] ...options: MessageSelectOptionData[] | MessageSelectOptionData[][]
): this; ): this;
public toJSON(): APISelectMenuComponent; public toJSON(): APISelectMenuComponent;
public select(message: Message, values: string[]): Promise<InteractionResponseBody>; public select(message: Message, values: string[]): Promise<InteractionResponse>;
} }
// Todo // Todo
@ -2253,6 +2253,7 @@ export class Modal {
public application: object | null; public application: object | null;
public client: Client | null; public client: Client | null;
public nonce: Snowflake | null; public nonce: Snowflake | null;
public readonly sendFromInteraction: InteractionResponse | null;
public addComponents( public addComponents(
...components: ( ...components: (
| MessageActionRow<ModalActionRowComponent> | MessageActionRow<ModalActionRowComponent>
@ -2276,12 +2277,12 @@ export class Modal {
): this; ): this;
public setTitle(title: string): this; public setTitle(title: string): this;
public toJSON(): RawModalSubmitInteractionData; public toJSON(): RawModalSubmitInteractionData;
public reply(data: ModalReplyData): Promise<InteractionResponseBody>; public reply(data: ModalReplyData): Promise<InteractionResponse>;
} }
export interface ModalReplyData { export interface ModalReplyData {
guild: GuildResolvable; guild?: GuildResolvable;
channel: TextChannelResolvable; channel?: TextChannelResolvable;
data: TextInputComponentReplyData[]; data: TextInputComponentReplyData[];
} }
@ -3901,6 +3902,26 @@ export class MessageManager extends CachedManager<Snowflake, Message, MessageRes
public search(options: MessageSearchOptions): Promise<MessageSearchResult>; public search(options: MessageSearchOptions): Promise<MessageSearchResult>;
} }
export class InteractionManager extends CachedManager<Snowflake, Message, MessageResolvable> {
private constructor(channel: TextBasedChannel, iterable?: Iterable<RawMessageData>);
public channel: TextBasedChannel;
public cache: Collection<Snowflake, Message>;
}
export class InteractionResponse extends Base {
private constructor(client: Client, data: Object);
public readonly channel: GuildTextBasedChannel | TextBasedChannel;
public channelId: Snowflake;
public readonly createdAt: Date;
public createdTimestamp: number;
public guildId: Snowflake | null;
public readonly guild: Snowflake | null;
public id: Snowflake;
public nonce: Snowflake;
public sendData: Object;
public awaitModal(time?: number): Modal;
}
export interface MessageSearchOptions { export interface MessageSearchOptions {
author: Snowflake[]; author: Snowflake[];
content: string; content: string;
@ -4069,6 +4090,7 @@ export interface TextBasedChannelFields extends PartialTextBasedChannelFields {
lastPinTimestamp: number | null; lastPinTimestamp: number | null;
readonly lastPinAt: Date | null; readonly lastPinAt: Date | null;
messages: MessageManager; messages: MessageManager;
interactions: InteractionManager;
awaitMessageComponent<T extends MessageComponentTypeResolvable = 'ACTION_ROW'>( awaitMessageComponent<T extends MessageComponentTypeResolvable = 'ACTION_ROW'>(
options?: AwaitMessageCollectorOptionsParams<T, true>, options?: AwaitMessageCollectorOptionsParams<T, true>,
): Promise<MappedInteractionTypes[T]>; ): Promise<MappedInteractionTypes[T]>;
@ -4086,7 +4108,7 @@ export interface TextBasedChannelFields extends PartialTextBasedChannelFields {
setNSFW(nsfw?: boolean, reason?: string): Promise<this>; setNSFW(nsfw?: boolean, reason?: string): Promise<this>;
fetchWebhooks(): Promise<Collection<Snowflake, Webhook>>; fetchWebhooks(): Promise<Collection<Snowflake, Webhook>>;
sendTyping(): Promise<void>; sendTyping(): Promise<void>;
sendSlash(bot: UserResolvable, commandName: string, ...args: any): Promise<InteractionResponseBody>; sendSlash(bot: UserResolvable, commandName: string, ...args: any): Promise<InteractionResponse>;
} }
export function PartialWebhookMixin<T>(Base?: Constructable<T>): Constructable<T & PartialWebhookFields>; export function PartialWebhookMixin<T>(Base?: Constructable<T>): Constructable<T & PartialWebhookFields>;
@ -6616,7 +6638,7 @@ export type AnyChannel =
| VoiceChannel | VoiceChannel
| ForumChannel; | ForumChannel;
export type TextBasedChannel = Exclude<Extract<AnyChannel, { messages: MessageManager }>, ForumChannel>; export type TextBasedChannel = Exclude<Extract<AnyChannel, { messages: MessageManager, interactions: InteractionManager }>, ForumChannel>;
export type TextBasedChannelTypes = TextBasedChannel['type']; export type TextBasedChannelTypes = TextBasedChannel['type'];