Skip to content

Commit

Permalink
refactor: enforce single param on sending/editing methods (#5758)
Browse files Browse the repository at this point in the history
Co-authored-by: ckohen <chaikohen@gmail.com>
Co-authored-by: Jan <66554238+vaporox@users.noreply.github.com>
Co-authored-by: SpaceEEC <spaceeec@yahoo.com>
  • Loading branch information
4 people committed Jun 9, 2021
1 parent dda5ee2 commit 0467a90
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 260 deletions.
82 changes: 5 additions & 77 deletions src/structures/APIMessage.js
@@ -1,7 +1,6 @@
'use strict';

const BaseMessageComponent = require('./BaseMessageComponent');
const MessageAttachment = require('./MessageAttachment');
const MessageEmbed = require('./MessageEmbed');
const { RangeError } = require('../errors');
const DataResolver = require('../util/DataResolver');
Expand Down Expand Up @@ -308,82 +307,16 @@ class APIMessage {
return { attachment, name, file: resource };
}

/**
* Partitions embeds and attachments.
* @param {Array<MessageEmbed|MessageAttachment>} items Items to partition
* @returns {Array<MessageEmbed[], MessageAttachment[]>}
*/
static partitionMessageAdditions(items) {
const embeds = [];
const files = [];
for (const item of items) {
if (item instanceof MessageEmbed) {
embeds.push(item);
} else if (item instanceof MessageAttachment) {
files.push(item);
}
}

return [embeds, files];
}

/**
* Transforms the user-level arguments into a final options object. Passing a transformed options object alone into
* this method will keep it the same, allowing for the reuse of the final options object.
* @param {string} [content] Content to send
* @param {MessageOptions|WebhookMessageOptions|MessageAdditions} [options={}] Options to use
* @param {MessageOptions|WebhookMessageOptions} [extra={}] Extra options to add onto transformed options
* @param {boolean} [isWebhook=false] Whether or not to use WebhookMessageOptions as the result
* @returns {MessageOptions|WebhookMessageOptions}
*/
static transformOptions(content, options, extra = {}, isWebhook = false) {
if (!options && typeof content === 'object' && !Array.isArray(content)) {
options = content;
content = undefined;
}

if (!options) {
options = {};
} else if (options instanceof MessageEmbed) {
return isWebhook ? { content, embeds: [options], ...extra } : { content, embed: options, ...extra };
} else if (options instanceof MessageAttachment) {
return { content, files: [options], ...extra };
}

if (Array.isArray(options)) {
const [embeds, files] = this.partitionMessageAdditions(options);
return isWebhook ? { content, embeds, files, ...extra } : { content, embed: embeds[0], files, ...extra };
} else if (Array.isArray(content)) {
const [embeds, files] = this.partitionMessageAdditions(content);
if (embeds.length || files.length) {
return isWebhook ? { embeds, files, ...extra } : { embed: embeds[0], files, ...extra };
}
}

return { content, ...options, ...extra };
}

/**
* Creates an `APIMessage` from user-level arguments.
* @param {MessageTarget} target Target to send to
* @param {string} [content] Content to send
* @param {MessageOptions|WebhookMessageOptions|MessageAdditions} [options={}] Options to use
* @param {MessageOptions|WebhookMessageOptions} [extra={}] - Extra options to add onto transformed options
* @param {string|MessageOptions|WebhookMessageOptions} options Options or content to use
* @param {MessageOptions|WebhookMessageOptions} [extra={}] - Extra options to add onto specified options
* @returns {MessageOptions|WebhookMessageOptions}
*/
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 InteractionWebhook ||
target instanceof Webhook ||
target instanceof WebhookClient;
const transformed = this.transformOptions(content, options, extra, isWebhook);
return new this(target, transformed);
static create(target, options, extra = {}) {
if (typeof options === 'string') return new this(target, { content: options, ...extra });
else return new this(target, { ...options, ...extra });
}
}

Expand All @@ -393,8 +326,3 @@ module.exports = APIMessage;
* A target for a message.
* @typedef {TextChannel|DMChannel|User|GuildMember|Webhook|WebhookClient|Interaction|InteractionWebhook} MessageTarget
*/

/**
* Additional items that can be sent with a message.
* @typedef {MessageEmbed|MessageAttachment|Array<MessageEmbed|MessageAttachment>} MessageAdditions
*/
3 changes: 1 addition & 2 deletions src/structures/InteractionWebhook.js
Expand Up @@ -28,8 +28,7 @@ class InteractionWebhook {
/* 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
* @param {string|APIMessage|InteractionReplyOptions} options The content for the reply
* @returns {Promise<Message|Object>}
*/
send() {}
Expand Down
34 changes: 17 additions & 17 deletions src/structures/Message.js
Expand Up @@ -544,17 +544,15 @@ class Message extends Base {

/**
* Edits the content of the message.
* @param {?(string|APIMessage)} [content] The new content for the message
* @param {MessageEditOptions|MessageEmbed|MessageAttachment|MessageAttachment[]} [options] The options to provide
* @param {string|APIMessage|MessageEditOptions} options The options to provide
* @returns {Promise<Message>}
* @example
* // Update the content of a message
* message.edit('This is my new content!')
* .then(msg => console.log(`Updated the content of a message to ${msg.content}`))
* .catch(console.error);
*/
edit(content, options) {
options = content instanceof APIMessage ? content : APIMessage.create(this, content, options);
edit(options) {
return this.channel.messages.edit(this.id, options);
}

Expand Down Expand Up @@ -650,26 +648,28 @@ class Message extends Base {

/**
* Send an inline reply to this message.
* @param {string|APIMessage} [content=''] The content for the message
* @param {ReplyMessageOptions|MessageAdditions} [options] The additional options to provide
* @param {string|APIMessage|ReplyMessageOptions} options The options to provide
* @returns {Promise<Message|Message[]>}
* @example
* // Reply to a message
* message.reply('This is a reply!')
* .then(() => console.log(`Replied to message "${message.content}"`))
* .catch(console.error);
*/
reply(content, options) {
return this.channel.send(
content instanceof APIMessage
? content
: APIMessage.transformOptions(content, options, {
reply: {
messageReference: this,
failIfNotExists: options?.failIfNotExists ?? content?.failIfNotExists ?? true,
},
}),
);
reply(options) {
let data;

if (options instanceof APIMessage) {
data = options;
} else {
data = APIMessage.create(this, options, {
reply: {
messageReference: this,
failIfNotExists: options?.failIfNotExists ?? true,
},
});
}
return this.channel.send(data);
}

/**
Expand Down
29 changes: 16 additions & 13 deletions src/structures/Webhook.js
Expand Up @@ -107,8 +107,7 @@ class Webhook {

/**
* Sends a message with this webhook.
* @param {string|APIMessage} [content=''] The content to send
* @param {WebhookMessageOptions|MessageAdditions} [options={}] The options to provide
* @param {string|APIMessage|WebhookMessageOptions} options The options to provide
* @returns {Promise<Message|Object>}
* @example
* // Send a basic message
Expand All @@ -134,7 +133,8 @@ class Webhook {
* .catch(console.error);
* @example
* // Send an embed with a local image inside
* webhook.send('This is an embed', {
* webhook.send({
* content: 'This is an embed',
* embeds: [{
* thumbnail: {
* url: 'attachment://file.jpg'
Expand All @@ -148,13 +148,13 @@ class Webhook {
* .then(console.log)
* .catch(console.error);
*/
async send(content, options) {
async send(options) {
let apiMessage;

if (content instanceof APIMessage) {
apiMessage = content.resolveData();
if (options instanceof APIMessage) {
apiMessage = options.resolveData();
} else {
apiMessage = APIMessage.create(this, content, options).resolveData();
apiMessage = APIMessage.create(this, options).resolveData();
if (Array.isArray(apiMessage.data.content)) {
return Promise.all(apiMessage.split().map(this.send.bind(this)));
}
Expand Down Expand Up @@ -244,15 +244,18 @@ class Webhook {
/**
* Edits a message that was sent by this webhook.
* @param {MessageResolvable|'@original'} message The message to edit
* @param {?(string|APIMessage)} [content] The new content for the message
* @param {WebhookEditMessageOptions|MessageAdditions} [options] The options to provide
* @param {string|APIMessage|WebhookEditMessageOptions} options The options to provide
* @returns {Promise<Message|Object>} 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
*/
async editMessage(message, content, options) {
const { data, files } = await (
content?.resolveData?.() ?? APIMessage.create(this, content, options).resolveData()
).resolveFiles();
async editMessage(message, options) {
let apiMessage;

if (options instanceof APIMessage) apiMessage = options;
else apiMessage = APIMessage.create(this, options);

const { data, files } = await apiMessage.resolveData().resolveFiles();

const d = await this.client.api
.webhooks(this.id, this.token)
.messages(typeof message === 'string' ? message : message.id)
Expand Down
38 changes: 21 additions & 17 deletions src/structures/interfaces/InteractionResponses.js
Expand Up @@ -53,8 +53,7 @@ class InteractionResponses {

/**
* Creates a reply to this interaction.
* @param {string|APIMessage|MessageAdditions} content The content for the reply
* @param {InteractionReplyOptions} [options] Additional options for the reply
* @param {string|APIMessage|InteractionReplyOptions} options The options for the reply
* @returns {Promise<void>}
* @example
* // Reply to the interaction with an embed
Expand All @@ -65,13 +64,17 @@ class InteractionResponses {
* .catch(console.error);
* @example
* // Create an ephemeral reply
* interaction.reply('Pong!', { ephemeral: true })
* interaction.reply({ content: 'Pong!', ephemeral: true })
* .then(console.log)
* .catch(console.error);
*/
async reply(content, options) {
async reply(options) {
if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED');
const apiMessage = content instanceof APIMessage ? content : APIMessage.create(this, content, options);

let apiMessage;
if (options instanceof APIMessage) apiMessage = options;
else apiMessage = APIMessage.create(this, options);

const { data, files } = await apiMessage.resolveData().resolveFiles();

await this.client.api.interactions(this.id, this.token).callback.post({
Expand Down Expand Up @@ -101,17 +104,16 @@ class InteractionResponses {
/**
* Edits the initial reply to this interaction.
* @see Webhook#editMessage
* @param {string|APIMessage|MessageAdditions} content The new content for the message
* @param {WebhookEditMessageOptions} [options] The options to provide
* @param {string|APIMessage|WebhookEditMessageOptions} options The new options for the message
* @returns {Promise<Message|Object>}
* @example
* // Edit the reply to this interaction
* interaction.editReply('New content')
* .then(console.log)
* .catch(console.error);
*/
editReply(content, options) {
return this.webhook.editMessage('@original', content, options);
editReply(options) {
return this.webhook.editMessage('@original', options);
}

/**
Expand All @@ -130,12 +132,11 @@ class InteractionResponses {

/**
* Send a follow-up message to this interaction.
* @param {string|APIMessage|MessageAdditions} content The content for the reply
* @param {InteractionReplyOptions} [options] Additional options for the reply
* @param {string|APIMessage|InteractionReplyOptions} options The options for the reply
* @returns {Promise<Message|Object>}
*/
followUp(content, options) {
return this.webhook.send(content, options);
followUp(options) {
return this.webhook.send(options);
}

/**
Expand All @@ -159,18 +160,21 @@ class InteractionResponses {

/**
* Updates the original message whose button was pressed
* @param {string|APIMessage|MessageAdditions} content The content for the reply
* @param {WebhookEditMessageOptions} [options] Additional options for the reply
* @param {string|APIMessage|WebhookEditMessageOptions} options The options for the reply
* @returns {Promise<void>}
* @example
* // Remove the buttons from the message
* interaction.update("A button was clicked", { components: [] })
* .then(console.log)
* .catch(console.error);
*/
async update(content, options) {
async update(options) {
if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED');
const apiMessage = content instanceof APIMessage ? content : APIMessage.create(this, content, options);

let apiMessage;
if (options instanceof APIMessage) apiMessage = options;
else apiMessage = APIMessage.create(this, options);

const { data, files } = await apiMessage.resolveData().resolveFiles();

await this.client.api.interactions(this.id, this.token).callback.post({
Expand Down
13 changes: 6 additions & 7 deletions src/structures/interfaces/TextBasedChannel.js
Expand Up @@ -117,8 +117,7 @@ class TextBasedChannel {

/**
* Sends a message to this channel.
* @param {string|APIMessage} [content=''] The content to send
* @param {MessageOptions|MessageAdditions} [options={}] The options to provide
* @param {string|APIMessage|MessageOptions} options The options to provide
* @returns {Promise<Message|Message[]>}
* @example
* // Send a basic message
Expand Down Expand Up @@ -158,20 +157,20 @@ class TextBasedChannel {
* .then(console.log)
* .catch(console.error);
*/
async send(content, options) {
async send(options) {
const User = require('../User');
const GuildMember = require('../GuildMember');

if (this instanceof User || this instanceof GuildMember) {
return this.createDM().then(dm => dm.send(content, options));
return this.createDM().then(dm => dm.send(options));
}

let apiMessage;

if (content instanceof APIMessage) {
apiMessage = content.resolveData();
if (options instanceof APIMessage) {
apiMessage = options.resolveData();
} else {
apiMessage = APIMessage.create(this, content, options).resolveData();
apiMessage = APIMessage.create(this, options).resolveData();
if (Array.isArray(apiMessage.data.content)) {
return Promise.all(apiMessage.split().map(this.send.bind(this)));
}
Expand Down

0 comments on commit 0467a90

Please sign in to comment.