From 415c013c42f1d2b65d5528dcc82bc036537dd2f2 Mon Sep 17 00:00:00 2001 From: ckohen Date: Mon, 31 May 2021 02:10:28 -0700 Subject: [PATCH] feat(Interactions): add InteractionWebhook for better internals --- src/client/WebhookClient.js | 13 ++++++ src/index.js | 1 + src/structures/APIMessage.js | 12 +++-- src/structures/CommandInteraction.js | 8 ++-- src/structures/InteractionWebhook.js | 44 ++++++++++++++++++ src/structures/MessageComponentInteraction.js | 8 ++-- src/structures/Webhook.js | 3 +- .../interfaces/InteractionResponses.js | 22 +++------ typings/index.d.ts | 45 +++++++++++++++---- 9 files changed, 120 insertions(+), 36 deletions(-) create mode 100644 src/structures/InteractionWebhook.js diff --git a/src/client/WebhookClient.js b/src/client/WebhookClient.js index cea66319f60f..84a2bc04fb57 100644 --- a/src/client/WebhookClient.js +++ b/src/client/WebhookClient.js @@ -24,6 +24,19 @@ class WebhookClient extends BaseClient { this.id = id; Object.defineProperty(this, 'token', { value: token, writable: true, configurable: true }); } + + // These are here only for documentation purposes - they are implemented by Webhook + /* eslint-disable no-empty-function */ + send() {} + sendSlackMessage() {} + fetchMessage() {} + edit() {} + editMessage() {} + delete() {} + deleteMessage() {} + get createdTimestamp() {} + get createdAt() {} + get url() {} } Webhook.applyToClass(WebhookClient); diff --git a/src/index.js b/src/index.js index e8ebb000abdc..a358a303bbc8 100644 --- a/src/index.js +++ b/src/index.js @@ -91,6 +91,7 @@ module.exports = { Integration: require('./structures/Integration'), IntegrationApplication: require('./structures/IntegrationApplication'), Interaction: require('./structures/Interaction'), + InteractionWebhook: require('./structures/InteractionWebhook'), Invite: require('./structures/Invite'), Message: require('./structures/Message'), MessageActionRow: require('./structures/MessageActionRow'), diff --git a/src/structures/APIMessage.js b/src/structures/APIMessage.js index b91699560a7b..c9a0528b83a2 100644 --- a/src/structures/APIMessage.js +++ b/src/structures/APIMessage.js @@ -81,7 +81,8 @@ class APIMessage { */ get isInteraction() { const Interaction = require('./Interaction'); - return this.target instanceof Interaction; + const InteractionWebhook = require('./InteractionWebhook'); + return this.target instanceof Interaction || this.target instanceof InteractionWebhook; } /** @@ -372,10 +373,15 @@ class APIMessage { */ static create(target, content, options, extra = {}) { const Interaction = require('./Interaction'); + const InteractionWebhook = require('./InteractionWebhook'); const Webhook = require('./Webhook'); const WebhookClient = require('../client/WebhookClient'); - const isWebhook = target instanceof Interaction || target instanceof Webhook || target instanceof WebhookClient; + const isWebhook = + target instanceof Interaction || + target instanceof InteractionWebhook || + target instanceof Webhook || + target instanceof WebhookClient; const transformed = this.transformOptions(content, options, extra, isWebhook); return new this(target, transformed); } @@ -385,7 +391,7 @@ module.exports = APIMessage; /** * A target for a message. - * @typedef {TextChannel|DMChannel|User|GuildMember|Webhook|WebhookClient|Interaction} MessageTarget + * @typedef {TextChannel|DMChannel|User|GuildMember|Webhook|WebhookClient|Interaction|InteractionWebhook} MessageTarget */ /** diff --git a/src/structures/CommandInteraction.js b/src/structures/CommandInteraction.js index 7888d5c7e550..a139d06ebb38 100644 --- a/src/structures/CommandInteraction.js +++ b/src/structures/CommandInteraction.js @@ -1,8 +1,8 @@ 'use strict'; const Interaction = require('./Interaction'); +const InteractionWebhook = require('./InteractionWebhook'); const InteractionResponses = require('./interfaces/InteractionResponses'); -const WebhookClient = require('../client/WebhookClient'); const Collection = require('../util/Collection'); const { ApplicationCommandOptionTypes } = require('../util/Constants'); @@ -53,10 +53,10 @@ class CommandInteraction extends Interaction { this.replied = false; /** - * An associated webhook client, can be used to create deferred replies - * @type {WebhookClient} + * An associated interaction webhook, can be used to further interact with this interaction + * @type {InteractionWebhook} */ - this.webhook = new WebhookClient(this.applicationID, this.token, this.client.options); + this.webhook = new InteractionWebhook(this.client, this.applicationID, this.token); } /** diff --git a/src/structures/InteractionWebhook.js b/src/structures/InteractionWebhook.js new file mode 100644 index 000000000000..2bb197202400 --- /dev/null +++ b/src/structures/InteractionWebhook.js @@ -0,0 +1,44 @@ +'use strict'; + +const Webhook = require('./Webhook'); + +/** + * Represents a webhook for an Interaction + * @implements {Webhook} + */ +class InteractionWebhook { + /** + * @param {Client} client The instantiating client + * @param {Snowflake} id ID of the application + * @param {string} token Token of the interaction + */ + constructor(client, id, token) { + /** + * The client that instantiated the interaction webhook + * @name InteractionWebhook#client + * @type {Client} + * @readonly + */ + Object.defineProperty(this, 'client', { value: client }); + this.id = id; + Object.defineProperty(this, 'token', { value: token, writable: true, configurable: true }); + } + + // These are here only for documentation purposes - they are implemented by Webhook + /* eslint-disable no-empty-function, valid-jsdoc */ + /** + * Sends a message with this webhook. + * @param {string|APIMessage|MessageAdditions} content The content for the reply + * @param {InteractionReplyOptions} [options] Additional options for the reply + * @returns {Promise} + */ + send() {} + fetchMessage() {} + editMessage() {} + deleteMessage() {} + get url() {} +} + +Webhook.applyToClass(InteractionWebhook, ['sendSlackMessage', 'edit', 'delete', 'createdTimestamp', 'createdAt']); + +module.exports = InteractionWebhook; diff --git a/src/structures/MessageComponentInteraction.js b/src/structures/MessageComponentInteraction.js index 711f7c282522..2ca201adf3bd 100644 --- a/src/structures/MessageComponentInteraction.js +++ b/src/structures/MessageComponentInteraction.js @@ -1,8 +1,8 @@ 'use strict'; const Interaction = require('./Interaction'); +const InteractionWebhook = require('./InteractionWebhook'); const InteractionResponses = require('./interfaces/InteractionResponses'); -const WebhookClient = require('../client/WebhookClient'); const { MessageComponentTypes } = require('../util/Constants'); /** @@ -45,10 +45,10 @@ class MessageComponentInteraction extends Interaction { this.replied = false; /** - * An associated webhook client, can be used to create deferred replies - * @type {WebhookClient} + * An associated interaction webhook, can be used to further interact with this interaction + * @type {InteractionWebhook} */ - this.webhook = new WebhookClient(this.applicationID, this.token, this.client.options); + this.webhook = new InteractionWebhook(this.client, this.applicationID, this.token); } /** diff --git a/src/structures/Webhook.js b/src/structures/Webhook.js index 903c97586775..082c1b7cb409 100644 --- a/src/structures/Webhook.js +++ b/src/structures/Webhook.js @@ -325,7 +325,7 @@ class Webhook { return this.client.rest.cdn.Avatar(this.id, this.avatar, format, size); } - static applyToClass(structure) { + static applyToClass(structure, ignore = []) { for (const prop of [ 'send', 'sendSlackMessage', @@ -338,6 +338,7 @@ class Webhook { 'createdAt', 'url', ]) { + if (ignore.includes(prop)) continue; Object.defineProperty(structure.prototype, prop, Object.getOwnPropertyDescriptor(Webhook.prototype, prop)); } } diff --git a/src/structures/interfaces/InteractionResponses.js b/src/structures/interfaces/InteractionResponses.js index 99b1f5245ed1..94086b6b6af5 100644 --- a/src/structures/interfaces/InteractionResponses.js +++ b/src/structures/interfaces/InteractionResponses.js @@ -93,9 +93,8 @@ class InteractionResponses { * .then(reply => console.log(`Replied with ${reply.content}`)) * .catch(console.error); */ - async fetchReply() { - const raw = await this.webhook.fetchMessage('@original'); - return this.channel?.messages.add(raw) ?? raw; + fetchReply() { + return this.webhook.fetchMessage('@original'); } /** @@ -110,9 +109,8 @@ class InteractionResponses { * .then(console.log) * .catch(console.error); */ - async editReply(content, options) { - const raw = await this.webhook.editMessage('@original', content, options); - return this.channel?.messages.add(raw) ?? raw; + editReply(content, options) { + return this.webhook.editMessage('@original', content, options); } /** @@ -135,16 +133,8 @@ class InteractionResponses { * @param {InteractionReplyOptions} [options] Additional options for the reply * @returns {Promise} */ - async followUp(content, options) { - const apiMessage = content instanceof APIMessage ? content : APIMessage.create(this, content, options); - const { data, files } = await apiMessage.resolveData().resolveFiles(); - - const raw = await this.client.api.webhooks(this.applicationID, this.token).post({ - data, - files, - }); - - return this.channel?.messages.add(raw) ?? raw; + followUp(content, options) { + return this.webhook.send(content, options); } /** diff --git a/typings/index.d.ts b/typings/index.d.ts index 865cf1d01408..7e71a54efc8c 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -467,7 +467,7 @@ declare module 'discord.js' { public deferred: boolean; public options: Collection; public replied: boolean; - public webhook: WebhookClient; + public webhook: InteractionWebhook; public defer(options?: InteractionDeferOptions): Promise; public deleteReply(): Promise; public editReply( @@ -1139,6 +1139,30 @@ declare module 'discord.js' { public isMessageComponent(): this is MessageComponentInteraction; } + export class InteractionWebhook extends PartialWebhookMixin() { + constructor(client: Client, id: Snowflake, token: string); + public token: string; + public send( + content: string | (InteractionReplyOptions & { split?: false }) | MessageAdditions, + ): Promise; + public send(options: InteractionReplyOptions & { split: true | SplitOptions }): Promise<(Message | RawMessage)[]>; + public send( + options: InteractionReplyOptions | APIMessage, + ): Promise; + public send( + content: string | null, + options: (InteractionReplyOptions & { split?: false }) | MessageAdditions, + ): Promise; + public send( + content: string | null, + options: InteractionReplyOptions & { split: true | SplitOptions }, + ): Promise<(Message | RawMessage)[]>; + public send( + content: string | null, + options: InteractionReplyOptions, + ): Promise; + } + export class Invite extends Base { constructor(client: Client, data: unknown); public channel: GuildChannel | PartialGroupDMChannel; @@ -1333,7 +1357,7 @@ declare module 'discord.js' { public deferred: boolean; public message: Message | RawMessage; public replied: boolean; - public webhook: WebhookClient; + public webhook: InteractionWebhook; public defer(options?: InteractionDeferOptions): Promise; public deferUpdate(): Promise; public deleteReply(): Promise; @@ -2498,18 +2522,15 @@ declare module 'discord.js' { stopTyping(force?: boolean): void; } + function PartialWebhookMixin(Base?: Constructable): Constructable; function WebhookMixin(Base?: Constructable): Constructable; function VolumeMixin(base: Constructable): Constructable; - interface WebhookFields { + interface PartialWebhookFields { id: Snowflake; - readonly createdAt: Date; - readonly createdTimestamp: number; readonly url: string; - delete(reason?: string): Promise; deleteMessage(message: MessageResolvable | '@original'): Promise; - edit(options: WebhookEditData): Promise; editMessage( message: MessageResolvable | '@original', content: string | null | APIMessage | MessageAdditions, @@ -2537,7 +2558,14 @@ declare module 'discord.js' { content: string | null, options: WebhookMessageOptions, ): Promise; - sendSlackMessage(body: unknown): Promise; + } + + interface WebhookFields extends PartialWebhookFields { + readonly createdAt: Date; + readonly createdTimestamp: number; + delete(reason?: string): Promise; + edit(options: WebhookEditData): Promise; + sendSlackMessage(body: object): Promise; } //#endregion @@ -3532,6 +3560,7 @@ declare module 'discord.js' { type MessageTarget = | Interaction + | InteractionWebhook | TextChannel | NewsChannel | DMChannel