Skip to content

Commit

Permalink
refactor: always return Message instances in interactions (#7917)
Browse files Browse the repository at this point in the history
Co-authored-by: Almeida <almeidx@pm.me>
  • Loading branch information
suneettipirneni and almeidx committed Jun 5, 2022
1 parent ad36c0b commit 9720e55
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ class CommandInteractionOptionResolver {
* Gets a message option.
* @param {string} name The name of the option.
* @param {boolean} [required=false] Whether to throw an error if the option is not found.
* @returns {?(Message|APIMessage)}
* @returns {?Message}
* The value of the option, or null if not set and not required.
*/
getMessage(name, required = false) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
const { ApplicationCommandOptionType } = require('discord-api-types/v10');
const CommandInteraction = require('./CommandInteraction');
const CommandInteractionOptionResolver = require('./CommandInteractionOptionResolver');
const { lazy } = require('../util/Util');

const getMessage = lazy(() => require('./Message').Message);

/**
* Represents a context menu interaction.
Expand Down Expand Up @@ -48,7 +51,9 @@ class ContextMenuCommandInteraction extends CommandInteraction {
name: 'message',
type: '_MESSAGE',
value: target_id,
message: this.channel?.messages._add(resolved.messages[target_id]) ?? resolved.messages[target_id],
message:
this.channel?.messages._add(resolved.messages[target_id]) ??
new (getMessage())(this.client, resolved.messages[target_id]),
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
const Interaction = require('./Interaction');
const InteractionWebhook = require('./InteractionWebhook');
const InteractionResponses = require('./interfaces/InteractionResponses');
const { lazy } = require('../util/Util');

const getMessage = lazy(() => require('./Message').Message);

/**
* Represents a message component interaction.
Expand All @@ -21,9 +24,9 @@ class MessageComponentInteraction extends Interaction {

/**
* The message to which the component was attached
* @type {Message|APIMessage}
* @type {Message}
*/
this.message = this.channel?.messages._add(data.message) ?? data.message;
this.message = this.channel?.messages._add(data.message) ?? new (getMessage())(client, data.message);

/**
* The custom id of the component which was interacted with
Expand Down
7 changes: 5 additions & 2 deletions packages/discord.js/src/structures/ModalSubmitInteraction.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ const Interaction = require('./Interaction');
const InteractionWebhook = require('./InteractionWebhook');
const ModalSubmitFields = require('./ModalSubmitFields');
const InteractionResponses = require('./interfaces/InteractionResponses');
const { lazy } = require('../util/Util');

const getMessage = lazy(() => require('./Message').Message);

/**
* @typedef {Object} ModalData
Expand Down Expand Up @@ -34,9 +37,9 @@ class ModalSubmitInteraction extends Interaction {
if ('message' in data) {
/**
* The message associated with this interaction
* @type {?(Message|APIMessage)}
* @type {?Message}
*/
this.message = this.channel?.messages._add(data.message) ?? data.message;
this.message = this.channel?.messages._add(data.message) ?? new (getMessage())(this.client, data.message);
} else {
this.message = null;
}
Expand Down
20 changes: 12 additions & 8 deletions packages/discord.js/src/structures/Webhook.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ const { Routes, WebhookType } = require('discord-api-types/v10');
const MessagePayload = require('./MessagePayload');
const { Error } = require('../errors');
const DataResolver = require('../util/DataResolver');
const { lazy } = require('../util/Util');

const getMessage = lazy(() => require('./Message').Message);

/**
* Represents a webhook.
Expand Down Expand Up @@ -145,7 +148,7 @@ class Webhook {
/**
* Sends a message with this webhook.
* @param {string|MessagePayload|WebhookMessageOptions} options The options to provide
* @returns {Promise<Message|APIMessage>}
* @returns {Promise<Message>}
* @example
* // Send a basic message
* webhook.send('hello!')
Expand Down Expand Up @@ -208,7 +211,7 @@ class Webhook {

const { body, files } = await messagePayload.resolveFiles();
const d = await this.client.rest.post(Routes.webhook(this.id, this.token), { body, files, query, auth: false });
return this.client.channels?.cache.get(d.channel_id)?.messages._add(d, false) ?? d;
return this.client.channels?.cache.get(d.channel_id)?.messages._add(d, false) ?? new (getMessage())(this.client, d);
}

/**
Expand Down Expand Up @@ -283,8 +286,7 @@ class Webhook {
* Gets a message that was sent by this webhook.
* @param {Snowflake|'@original'} message The id of the message to fetch
* @param {WebhookFetchMessageOptions} [options={}] The options to provide to fetch the message.
* @returns {Promise<Message|APIMessage>} Returns the raw message data if the webhook was instantiated as a
* {@link WebhookClient} or if the channel is uncached, otherwise a {@link Message} will be returned
* @returns {Promise<Message>} Returns the message sent by this webhook
*/
async fetchMessage(message, { cache = true, threadId } = {}) {
if (!this.token) throw new Error('WEBHOOK_TOKEN_UNAVAILABLE');
Expand All @@ -293,15 +295,17 @@ class Webhook {
query: threadId ? makeURLSearchParams({ thread_id: threadId }) : undefined,
auth: false,
});
return this.client.channels?.cache.get(data.channel_id)?.messages._add(data, cache) ?? data;
return (
this.client.channels?.cache.get(data.channel_id)?.messages._add(data, cache) ??
new (getMessage())(this.client, data)
);
}

/**
* Edits a message that was sent by this webhook.
* @param {MessageResolvable|'@original'} message The message to edit
* @param {string|MessagePayload|WebhookEditMessageOptions} options The options to provide
* @returns {Promise<Message|APIMessage>} Returns the raw message data if the webhook was instantiated as a
* {@link WebhookClient} or if the channel is uncached, otherwise a {@link Message} will be returned
* @returns {Promise<Message>} Returns the message edited by this webhook
*/
async editMessage(message, options) {
if (!this.token) throw new Error('WEBHOOK_TOKEN_UNAVAILABLE');
Expand All @@ -326,7 +330,7 @@ class Webhook {
);

const messageManager = this.client.channels?.cache.get(d.channel_id)?.messages;
if (!messageManager) return d;
if (!messageManager) return new (getMessage())(this.client, d);

const existing = messageManager.cache.get(d.id);
if (!existing) return messageManager._add(d);
Expand Down
10 changes: 10 additions & 0 deletions packages/discord.js/src/util/Util.js
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,16 @@ class Util extends null {
static cleanCodeBlockContent(text) {
return text.replaceAll('```', '`\u200b``');
}

/**
* Lazily evaluates a callback function
* @param {Function} cb The callback to lazily evaluate
* @returns {Function}
*/
static lazy(cb) {
let defaultValue;
return () => (defaultValue ??= cb());
}
}

module.exports = Util;
Expand Down
80 changes: 45 additions & 35 deletions packages/discord.js/typings/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -391,14 +391,14 @@ export interface InteractionResponseFields<Cached extends CacheType = CacheType>
ephemeral: boolean | null;
replied: boolean;
webhook: InteractionWebhook;
reply(options: InteractionReplyOptions & { fetchReply: true }): Promise<GuildCacheMessage<Cached>>;
reply(options: InteractionReplyOptions & { fetchReply: true }): Promise<Message>;
reply(options: string | MessagePayload | InteractionReplyOptions): Promise<void>;
deleteReply(): Promise<void>;
editReply(options: string | MessagePayload | WebhookEditMessageOptions): Promise<GuildCacheMessage<Cached>>;
deferReply(options: InteractionDeferReplyOptions & { fetchReply: true }): Promise<GuildCacheMessage<Cached>>;
editReply(options: string | MessagePayload | WebhookEditMessageOptions): Promise<Message>;
deferReply(options: InteractionDeferReplyOptions & { fetchReply: true }): Promise<Message>;
deferReply(options?: InteractionDeferReplyOptions): Promise<void>;
fetchReply(): Promise<GuildCacheMessage<Cached>>;
followUp(options: string | MessagePayload | InteractionReplyOptions): Promise<GuildCacheMessage<Cached>>;
fetchReply(): Promise<Message>;
followUp(options: string | MessagePayload | InteractionReplyOptions): Promise<Message>;
showModal(
modal: JSONEncodable<APIModalInteractionResponseCallbackData> | ModalData | APIModalInteractionResponseCallbackData,
): Promise<void>;
Expand Down Expand Up @@ -435,13 +435,15 @@ export abstract class CommandInteraction<Cached extends CacheType = CacheType> e
public inGuild(): this is CommandInteraction<'raw' | 'cached'>;
public inCachedGuild(): this is CommandInteraction<'cached'>;
public inRawGuild(): this is CommandInteraction<'raw'>;
public deferReply(options: InteractionDeferReplyOptions & { fetchReply: true }): Promise<GuildCacheMessage<Cached>>;
public deferReply(
options: InteractionDeferReplyOptions & { fetchReply: true },
): Promise<Message<BooleanCache<Cached>>>;
public deferReply(options?: InteractionDeferReplyOptions): Promise<InteractionResponse<BooleanCache<Cached>>>;
public deleteReply(): Promise<void>;
public editReply(options: string | MessagePayload | WebhookEditMessageOptions): Promise<GuildCacheMessage<Cached>>;
public fetchReply(): Promise<GuildCacheMessage<Cached>>;
public followUp(options: string | MessagePayload | InteractionReplyOptions): Promise<GuildCacheMessage<Cached>>;
public reply(options: InteractionReplyOptions & { fetchReply: true }): Promise<GuildCacheMessage<Cached>>;
public editReply(options: string | MessagePayload | WebhookEditMessageOptions): Promise<Message>;
public fetchReply(): Promise<Message>;
public followUp(options: string | MessagePayload | InteractionReplyOptions): Promise<Message>;
public reply(options: InteractionReplyOptions & { fetchReply: true }): Promise<Message<BooleanCache<Cached>>>;
public reply(
options: string | MessagePayload | InteractionReplyOptions,
): Promise<InteractionResponse<BooleanCache<Cached>>>;
Expand Down Expand Up @@ -1536,7 +1538,7 @@ export class InteractionCollector<T extends Interaction> extends Collector<Snowf
export class InteractionWebhook extends PartialWebhookMixin() {
public constructor(client: Client, id: Snowflake, token: string);
public token: string;
public send(options: string | MessagePayload | InteractionReplyOptions): Promise<Message | APIMessage>;
public send(options: string | MessagePayload | InteractionReplyOptions): Promise<Message>;
}

export class Invite extends Base {
Expand Down Expand Up @@ -1758,25 +1760,29 @@ export class MessageComponentInteraction<Cached extends CacheType = CacheType> e
public channelId: Snowflake;
public deferred: boolean;
public ephemeral: boolean | null;
public message: GuildCacheMessage<Cached>;
public message: Message<BooleanCache<Cached>>;
public replied: boolean;
public webhook: InteractionWebhook;
public inGuild(): this is MessageComponentInteraction<'raw' | 'cached'>;
public inCachedGuild(): this is MessageComponentInteraction<'cached'>;
public inRawGuild(): this is MessageComponentInteraction<'raw'>;
public deferReply(options: InteractionDeferReplyOptions & { fetchReply: true }): Promise<GuildCacheMessage<Cached>>;
public deferReply(
options: InteractionDeferReplyOptions & { fetchReply: true },
): Promise<Message<BooleanCache<Cached>>>;
public deferReply(options?: InteractionDeferReplyOptions): Promise<InteractionResponse<BooleanCache<Cached>>>;
public deferUpdate(options: InteractionDeferUpdateOptions & { fetchReply: true }): Promise<GuildCacheMessage<Cached>>;
public deferUpdate(
options: InteractionDeferUpdateOptions & { fetchReply: true },
): Promise<Message<BooleanCache<Cached>>>;
public deferUpdate(options?: InteractionDeferUpdateOptions): Promise<InteractionResponse<BooleanCache<Cached>>>;
public deleteReply(): Promise<void>;
public editReply(options: string | MessagePayload | WebhookEditMessageOptions): Promise<GuildCacheMessage<Cached>>;
public fetchReply(): Promise<GuildCacheMessage<Cached>>;
public followUp(options: string | MessagePayload | InteractionReplyOptions): Promise<GuildCacheMessage<Cached>>;
public reply(options: InteractionReplyOptions & { fetchReply: true }): Promise<GuildCacheMessage<Cached>>;
public editReply(options: string | MessagePayload | WebhookEditMessageOptions): Promise<Message>;
public fetchReply(): Promise<Message>;
public followUp(options: string | MessagePayload | InteractionReplyOptions): Promise<Message>;
public reply(options: InteractionReplyOptions & { fetchReply: true }): Promise<Message<BooleanCache<Cached>>>;
public reply(
options: string | MessagePayload | InteractionReplyOptions,
): Promise<InteractionResponse<BooleanCache<Cached>>>;
public update(options: InteractionUpdateOptions & { fetchReply: true }): Promise<GuildCacheMessage<Cached>>;
public update(options: InteractionUpdateOptions & { fetchReply: true }): Promise<Message>;
public update(
options: string | MessagePayload | InteractionUpdateOptions,
): Promise<InteractionResponse<BooleanCache<Cached>>>;
Expand Down Expand Up @@ -1904,12 +1910,12 @@ export class ModalSubmitFields {

export interface ModalMessageModalSubmitInteraction<Cached extends CacheType = CacheType>
extends ModalSubmitInteraction<Cached> {
message: GuildCacheMessage<Cached>;
update(options: InteractionUpdateOptions & { fetchReply: true }): Promise<GuildCacheMessage<Cached>>;
message: Message<BooleanCache<Cached>>;
update(options: InteractionUpdateOptions & { fetchReply: true }): Promise<Message>;
update(
options: string | MessagePayload | InteractionUpdateOptions,
): Promise<InteractionResponse<BooleanCache<Cached>>>;
deferUpdate(options: InteractionDeferUpdateOptions & { fetchReply: true }): Promise<GuildCacheMessage<Cached>>;
deferUpdate(options: InteractionDeferUpdateOptions & { fetchReply: true }): Promise<Message>;
deferUpdate(options?: InteractionDeferUpdateOptions): Promise<InteractionResponse<BooleanCache<Cached>>>;
inGuild(): this is ModalMessageModalSubmitInteraction<'raw' | 'cached'>;
inCachedGuild(): this is ModalMessageModalSubmitInteraction<'cached'>;
Expand All @@ -1923,19 +1929,23 @@ export class ModalSubmitInteraction<Cached extends CacheType = CacheType> extend
public readonly fields: ModalSubmitFields;
public deferred: boolean;
public ephemeral: boolean | null;
public message: GuildCacheMessage<Cached> | null;
public message: Message<BooleanCache<Cached>> | null;
public replied: boolean;
public readonly webhook: InteractionWebhook;
public reply(options: InteractionReplyOptions & { fetchReply: true }): Promise<GuildCacheMessage<Cached>>;
public reply(options: InteractionReplyOptions & { fetchReply: true }): Promise<Message>;
public reply(
options: string | MessagePayload | InteractionReplyOptions,
): Promise<InteractionResponse<BooleanCache<Cached>>>;
public deleteReply(): Promise<void>;
public editReply(options: string | MessagePayload | WebhookEditMessageOptions): Promise<GuildCacheMessage<Cached>>;
public deferReply(options: InteractionDeferReplyOptions & { fetchReply: true }): Promise<GuildCacheMessage<Cached>>;
public editReply(
options: string | MessagePayload | WebhookEditMessageOptions,
): Promise<Message<BooleanCache<Cached>>>;
public deferReply(
options: InteractionDeferReplyOptions & { fetchReply: true },
): Promise<Message<BooleanCache<Cached>>>;
public deferReply(options?: InteractionDeferReplyOptions): Promise<InteractionResponse<BooleanCache<Cached>>>;
public fetchReply(): Promise<GuildCacheMessage<Cached>>;
public followUp(options: string | MessagePayload | InteractionReplyOptions): Promise<GuildCacheMessage<Cached>>;
public fetchReply(): Promise<Message<BooleanCache<Cached>>>;
public followUp(options: string | MessagePayload | InteractionReplyOptions): Promise<Message<BooleanCache<Cached>>>;
public inGuild(): this is ModalSubmitInteraction<'raw' | 'cached'>;
public inCachedGuild(): this is ModalSubmitInteraction<'cached'>;
public inRawGuild(): this is ModalSubmitInteraction<'raw'>;
Expand Down Expand Up @@ -2740,9 +2750,9 @@ export class WebhookClient extends WebhookMixin(BaseClient) {
public editMessage(
message: MessageResolvable,
options: string | MessagePayload | WebhookEditMessageOptions,
): Promise<APIMessage>;
public fetchMessage(message: Snowflake, options?: WebhookFetchMessageOptions): Promise<APIMessage>;
public send(options: string | MessagePayload | WebhookMessageOptions): Promise<APIMessage>;
): Promise<Message>;
public fetchMessage(message: Snowflake, options?: WebhookFetchMessageOptions): Promise<Message>;
public send(options: string | MessagePayload | WebhookMessageOptions): Promise<Message>;
}

export class WebSocketManager extends EventEmitter {
Expand Down Expand Up @@ -3428,9 +3438,9 @@ export interface PartialWebhookFields {
editMessage(
message: MessageResolvable | '@original',
options: string | MessagePayload | WebhookEditMessageOptions,
): Promise<Message | APIMessage>;
fetchMessage(message: Snowflake | '@original', options?: WebhookFetchMessageOptions): Promise<Message | APIMessage>;
send(options: string | MessagePayload | Omit<WebhookMessageOptions, 'flags'>): Promise<Message | APIMessage>;
): Promise<Message>;
fetchMessage(message: Snowflake | '@original', options?: WebhookFetchMessageOptions): Promise<Message>;
send(options: string | MessagePayload | Omit<WebhookMessageOptions, 'flags'>): Promise<Message>;
}

export interface WebhookFields extends PartialWebhookFields {
Expand Down Expand Up @@ -3962,7 +3972,7 @@ export interface CommandInteractionOption<Cached extends CacheType = CacheType>
channel?: CacheTypeReducer<Cached, GuildBasedChannel, APIInteractionDataResolvedChannel>;
role?: CacheTypeReducer<Cached, Role, APIRole>;
attachment?: AttachmentBuilder;
message?: GuildCacheMessage<Cached>;
message?: Message<BooleanCache<Cached>>;
}

export interface CommandInteractionResolvedData<Cached extends CacheType = CacheType> {
Expand Down
Loading

0 comments on commit 9720e55

Please sign in to comment.