Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Interactions): add InteractionWebhook for better internals #5712

Merged
merged 1 commit into from Jun 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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