Skip to content

Commit

Permalink
feat(Interactions): add InteractionWebhook for better internals (#5712)
Browse files Browse the repository at this point in the history
  • Loading branch information
ckohen committed Jun 5, 2021
1 parent 1d57754 commit dec191a
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 36 deletions.
13 changes: 13 additions & 0 deletions src/client/WebhookClient.js
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions src/index.js
Expand Up @@ -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'),
Expand Down
12 changes: 9 additions & 3 deletions src/structures/APIMessage.js
Expand Up @@ -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;
}

/**
Expand Down Expand Up @@ -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);
}
Expand All @@ -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
*/

/**
Expand Down
8 changes: 4 additions & 4 deletions 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');

Expand Down Expand Up @@ -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);
}

/**
Expand Down
44 changes: 44 additions & 0 deletions 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<Message|Object>}
*/
send() {}
fetchMessage() {}
editMessage() {}
deleteMessage() {}
get url() {}
}

Webhook.applyToClass(InteractionWebhook, ['sendSlackMessage', 'edit', 'delete', 'createdTimestamp', 'createdAt']);

module.exports = InteractionWebhook;
8 changes: 4 additions & 4 deletions 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');

/**
Expand Down Expand Up @@ -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);
}

/**
Expand Down
3 changes: 2 additions & 1 deletion src/structures/Webhook.js
Expand Up @@ -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',
Expand All @@ -338,6 +338,7 @@ class Webhook {
'createdAt',
'url',
]) {
if (ignore.includes(prop)) continue;
Object.defineProperty(structure.prototype, prop, Object.getOwnPropertyDescriptor(Webhook.prototype, prop));
}
}
Expand Down
22 changes: 6 additions & 16 deletions src/structures/interfaces/InteractionResponses.js
Expand Up @@ -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');
}

/**
Expand All @@ -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);
}

/**
Expand All @@ -135,16 +133,8 @@ class InteractionResponses {
* @param {InteractionReplyOptions} [options] Additional options for the reply
* @returns {Promise<Message|Object>}
*/
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);
}

/**
Expand Down
45 changes: 37 additions & 8 deletions typings/index.d.ts
Expand Up @@ -467,7 +467,7 @@ declare module 'discord.js' {
public deferred: boolean;
public options: Collection<string, CommandInteractionOption>;
public replied: boolean;
public webhook: WebhookClient;
public webhook: InteractionWebhook;
public defer(options?: InteractionDeferOptions): Promise<void>;
public deleteReply(): Promise<void>;
public editReply(
Expand Down Expand Up @@ -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<Message | RawMessage>;
public send(options: InteractionReplyOptions & { split: true | SplitOptions }): Promise<(Message | RawMessage)[]>;
public send(
options: InteractionReplyOptions | APIMessage,
): Promise<Message | RawMessage | (Message | RawMessage)[]>;
public send(
content: string | null,
options: (InteractionReplyOptions & { split?: false }) | MessageAdditions,
): Promise<Message | RawMessage>;
public send(
content: string | null,
options: InteractionReplyOptions & { split: true | SplitOptions },
): Promise<(Message | RawMessage)[]>;
public send(
content: string | null,
options: InteractionReplyOptions,
): Promise<Message | RawMessage | (Message | RawMessage)[]>;
}

export class Invite extends Base {
constructor(client: Client, data: unknown);
public channel: GuildChannel | PartialGroupDMChannel;
Expand Down Expand Up @@ -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<void>;
public deferUpdate(): Promise<void>;
public deleteReply(): Promise<void>;
Expand Down Expand Up @@ -2498,18 +2522,15 @@ declare module 'discord.js' {
stopTyping(force?: boolean): void;
}

function PartialWebhookMixin<T>(Base?: Constructable<T>): Constructable<T & PartialWebhookFields>;
function WebhookMixin<T>(Base?: Constructable<T>): Constructable<T & WebhookFields>;

function VolumeMixin<T>(base: Constructable<T>): Constructable<T & VolumeInterface>;

interface WebhookFields {
interface PartialWebhookFields {
id: Snowflake;
readonly createdAt: Date;
readonly createdTimestamp: number;
readonly url: string;
delete(reason?: string): Promise<void>;
deleteMessage(message: MessageResolvable | '@original'): Promise<void>;
edit(options: WebhookEditData): Promise<Webhook>;
editMessage(
message: MessageResolvable | '@original',
content: string | null | APIMessage | MessageAdditions,
Expand Down Expand Up @@ -2537,7 +2558,14 @@ declare module 'discord.js' {
content: string | null,
options: WebhookMessageOptions,
): Promise<Message | RawMessage | (Message | RawMessage)[]>;
sendSlackMessage(body: unknown): Promise<boolean>;
}

interface WebhookFields extends PartialWebhookFields {
readonly createdAt: Date;
readonly createdTimestamp: number;
delete(reason?: string): Promise<void>;
edit(options: WebhookEditData): Promise<Webhook>;
sendSlackMessage(body: object): Promise<boolean>;
}

//#endregion
Expand Down Expand Up @@ -3532,6 +3560,7 @@ declare module 'discord.js' {

type MessageTarget =
| Interaction
| InteractionWebhook
| TextChannel
| NewsChannel
| DMChannel
Expand Down

0 comments on commit dec191a

Please sign in to comment.