Skip to content

Commit

Permalink
feat(Interaction): option to auto-fetch replies
Browse files Browse the repository at this point in the history
  • Loading branch information
monbrey committed Jun 15, 2021
1 parent 671436c commit 9d06570
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 18 deletions.
1 change: 1 addition & 0 deletions src/errors/Messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ const Messages = {
"or from a guild's application command manager.",

INTERACTION_ALREADY_REPLIED: 'This interaction has already been deferred or replied to.',
INTERACTION_FETCH_EPHEMERAL: 'Ephemeral responses cannot be fetched.',
};

for (const [name, message] of Object.entries(Messages)) register(name, message);
4 changes: 2 additions & 2 deletions src/structures/MessageComponentInteraction.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ class MessageComponentInteraction extends Interaction {

/**
* The message to which the component was attached
* @type {?Message|Object}
* @type {Message|Object}
*/
this.message = data.message ? this.channel?.messages.add(data.message) ?? data.message : null;
this.message = this.channel?.messages.add(data.message) ?? data.message;

/**
* The custom ID of the component which was interacted with
Expand Down
37 changes: 27 additions & 10 deletions src/structures/interfaces/InteractionResponses.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class InteractionResponses {
/**
* Defers the reply to this interaction.
* @param {InteractionDeferOptions} [options] Options for deferring the reply to this interaction
* @returns {Promise<void>}
* @returns {Promise<Message|void>}
* @example
* // Defer the reply to this interaction
* interaction.defer()
Expand All @@ -38,23 +38,26 @@ class InteractionResponses {
* .then(console.log)
* .catch(console.error);
*/
async defer({ ephemeral } = {}) {
async defer(options = {}) {
if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED');
if (options.fetchReply && options.ephemeral) throw new Error('INTERACTION_FETCH_EPHEMERAL');
await this.client.api.interactions(this.id, this.token).callback.post({
data: {
type: InteractionResponseTypes.DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE,
data: {
flags: ephemeral ? MessageFlags.FLAGS.EPHEMERAL : undefined,
flags: options.ephemeral ? MessageFlags.FLAGS.EPHEMERAL : undefined,
},
},
});
this.deferred = true;

return options.fetchReply ? this.fetchReply() : undefined;
}

/**
* Creates a reply to this interaction.
* @param {string|APIMessage|InteractionReplyOptions} options The options for the reply
* @returns {Promise<void>}
* @returns {Promise<Message|void>}
* @example
* // Reply to the interaction with an embed
* const embed = new MessageEmbed().setDescription('Pong!');
Expand All @@ -70,7 +73,7 @@ class InteractionResponses {
*/
async reply(options) {
if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED');

if (options.fetchReply && options.ephemeral) throw new Error('INTERACTION_FETCH_EPHEMERAL');
let apiMessage;
if (options instanceof APIMessage) apiMessage = options;
else apiMessage = APIMessage.create(this, options);
Expand All @@ -85,6 +88,8 @@ class InteractionResponses {
files,
});
this.replied = true;

return options.fetchReply ? this.fetchReply() : undefined;
}

/**
Expand Down Expand Up @@ -140,28 +145,34 @@ class InteractionResponses {
}

/**
* Defers an update to the message to which the component was attached
* @returns {Promise<void>}
* Defers an update to the message to which the component was attached.
* @param {InteractionDeferUpdateOptions} [options] Options for deferring the update to this interaction
* @returns {Promise<Message|void>}
* @example
* // Defer updating and reset the component's loading state
* interaction.deferUpdate()
* .then(console.log)
* .catch(console.error);
*/
async deferUpdate() {
async deferUpdate(options = {}) {
if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED');
if (options.fetchReply && (this.message.flags & MessageFlags.FLAGS.EPHEMERAL) === MessageFlags.FLAGS.EPHEMERAL) {
throw new Error('INTERACTION_FETCH_EPHEMERAL');
}
await this.client.api.interactions(this.id, this.token).callback.post({
data: {
type: InteractionResponseTypes.DEFERRED_MESSAGE_UPDATE,
},
});
this.deferred = true;

return options.fetchReply ? this.fetchReply() : undefined;
}

/**
* Updates the original message whose button was pressed
* Updates the original message whose button was pressed.
* @param {string|APIMessage|WebhookEditMessageOptions} options The options for the reply
* @returns {Promise<void>}
* @returns {Promise<Message|void>}
* @example
* // Remove the components from the message
* interaction.update({
Expand All @@ -174,6 +185,10 @@ class InteractionResponses {
async update(options) {
if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED');

if (options.fetchReply && (this.message.flags & MessageFlags.FLAGS.EPHEMERAL) === MessageFlags.FLAGS.EPHEMERAL) {
throw new Error('INTERACTION_FETCH_EPHEMERAL');
}

let apiMessage;
if (options instanceof APIMessage) apiMessage = options;
else apiMessage = APIMessage.create(this, options);
Expand All @@ -188,6 +203,8 @@ class InteractionResponses {
files,
});
this.replied = true;

return options.fetchReply ? this.fetchReply() : undefined;
}

static applyToClass(structure, ignore = []) {
Expand Down
30 changes: 24 additions & 6 deletions typings/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -453,12 +453,16 @@ declare module 'discord.js' {
public options: Collection<string, CommandInteractionOption>;
public replied: boolean;
public webhook: InteractionWebhook;
public defer(options?: InteractionDeferOptions): Promise<void>;
public defer(options?: InteractionDeferOptions & { fetchReply?: false }): Promise<void>;
public defer(options?: InteractionDeferOptions & { fetchReply: true }): Promise<Message | RawMessage>;
public deleteReply(): Promise<void>;
public editReply(options: string | APIMessage | WebhookEditMessageOptions): Promise<Message | RawMessage>;
public fetchReply(): Promise<Message | RawMessage>;
public followUp(options: string | APIMessage | InteractionReplyOptions): Promise<Message | RawMessage>;
public reply(options: string | APIMessage | InteractionReplyOptions): Promise<void>;
public reply(options: string | APIMessage | (InteractionReplyOptions & { fetchReply?: false })): Promise<void>;
public reply(
options: string | APIMessage | (InteractionReplyOptions & { fetchReply: true }),
): Promise<Message | RawMessage>;
private transformOption(option: unknown, resolved: unknown): CommandInteractionOption;
private _createOptionsCollection(options: unknown, resolved: unknown): Collection<string, CommandInteractionOption>;
}
Expand Down Expand Up @@ -1298,14 +1302,22 @@ declare module 'discord.js' {
public message: Message | RawMessage;
public replied: boolean;
public webhook: InteractionWebhook;
public defer(options?: InteractionDeferOptions): Promise<void>;
public deferUpdate(): Promise<void>;
public defer(options?: InteractionDeferOptions & { fetchReply?: false }): Promise<void>;
public defer(options?: InteractionDeferOptions & { fetchReply: true }): Promise<Message | RawMessage>;
public deferUpdate(options?: InteractionDeferUpdateOptions & { fetchReply?: false }): Promise<void>;
public deferUpdate(options?: InteractionDeferUpdateOptions & { fetchReply: true }): Promise<Message | RawMessage>;
public deleteReply(): Promise<void>;
public editReply(options: string | APIMessage | WebhookEditMessageOptions): Promise<Message | RawMessage>;
public fetchReply(): Promise<Message | RawMessage>;
public followUp(options: string | APIMessage | InteractionReplyOptions): Promise<Message | RawMessage>;
public reply(options: string | APIMessage | InteractionReplyOptions): Promise<void>;
public update(content: string | APIMessage | WebhookEditMessageOptions): Promise<void>;
public reply(options: string | APIMessage | (InteractionReplyOptions & { fetchReply?: false })): Promise<void>;
public reply(
options: string | APIMessage | (InteractionReplyOptions & { fetchReply: true }),
): Promise<Message | RawMessage>;
public update(content: string | APIMessage | (InteractionUpdateOptions & { fetchReply?: false })): Promise<void>;
public update(
content: string | APIMessage | (InteractionUpdateOptions & { fetchReply: true }),
): Promise<Message | RawMessage>;
public static resolveType(type: MessageComponentTypeResolvable): MessageComponentType;
}

Expand Down Expand Up @@ -3215,16 +3227,22 @@ declare module 'discord.js' {

interface InteractionDeferOptions {
ephemeral?: boolean;
fetchReply?: boolean;
}

interface InteractionDeferUpdateOptions extends Omit<InteractionDeferOptions, 'ephemeral'> {}

interface InteractionReplyOptions extends Omit<WebhookMessageOptions, 'username' | 'avatarURL'> {
ephemeral?: boolean;
fetchReply?: boolean;
}

type InteractionResponseType = keyof typeof InteractionResponseTypes;

type InteractionType = keyof typeof InteractionTypes;

interface InteractionUpdateOptions extends Omit<InteractionReplyOptions, 'ephemeral'> {}

type IntentsString =
| 'GUILDS'
| 'GUILD_MEMBERS'
Expand Down

0 comments on commit 9d06570

Please sign in to comment.