mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-10 08:33:30 +01:00
refactor: rewrite message creation (#2774)
* Rework createMessage - MessageAttachment is now structurally similar to FileOptions - No longer mutates the object passed as options - Supports more permutations of arguments * Ignore complexity warning * Refactor name finding * Fix typo * Update typings * Default name to null for MessageAttachment * Make Message#reply use transformOptions * Move transformOptions * Fix Message#reply * Fix mutation * Update tests * Fix options passing * Refactor into APIMessage * Fix webhook send * Expose APIMessage * Add documentation * Add types * Fix type doc * Fix another type doc * Fix another another type doc (is this one even right!?) * Remove trailing comma * Properly clone split options * Add support for sending file as stream * Missed a doc * Resolve files only once when splitting messages * This looks nicer * Assign directly * Don't cache data and files * Missing return type * Use object spread instead Object.assign * Document constructors * Crawl is a little dot * comp pls * tests: sanitize local file path, disable no-await-in-loop
This commit is contained in:
@@ -51,6 +51,7 @@ module.exports = {
|
||||
// Structures
|
||||
Base: require('./structures/Base'),
|
||||
Activity: require('./structures/Presence').Activity,
|
||||
APIMessage: require('./structures/APIMessage'),
|
||||
CategoryChannel: require('./structures/CategoryChannel'),
|
||||
Channel: require('./structures/Channel'),
|
||||
ClientApplication: require('./structures/ClientApplication'),
|
||||
|
||||
292
src/structures/APIMessage.js
Normal file
292
src/structures/APIMessage.js
Normal file
@@ -0,0 +1,292 @@
|
||||
const DataResolver = require('../util/DataResolver');
|
||||
const MessageEmbed = require('./MessageEmbed');
|
||||
const MessageAttachment = require('./MessageAttachment');
|
||||
const { browser } = require('../util/Constants');
|
||||
const Util = require('../util/Util');
|
||||
const { RangeError } = require('../errors');
|
||||
|
||||
/**
|
||||
* Represents a message to be sent to the API.
|
||||
*/
|
||||
class APIMessage {
|
||||
/**
|
||||
* @param {MessageTarget} target - The target for this message to be sent to
|
||||
* @param {MessageOptions|WebhookMessageOptions} options - Options passed in from send
|
||||
*/
|
||||
constructor(target, options) {
|
||||
/**
|
||||
* The target for this message to be sent to
|
||||
* @type {MessageTarget}
|
||||
*/
|
||||
this.target = target;
|
||||
|
||||
/**
|
||||
* Options passed in from send
|
||||
* @type {MessageOptions|WebhookMessageOptions}
|
||||
*/
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the target is a webhook
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
*/
|
||||
get isWebhook() {
|
||||
const Webhook = require('./Webhook');
|
||||
const WebhookClient = require('../client/WebhookClient');
|
||||
return this.target instanceof Webhook || this.target instanceof WebhookClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the target is a user
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
*/
|
||||
get isUser() {
|
||||
const User = require('./User');
|
||||
const GuildMember = require('./GuildMember');
|
||||
return this.target instanceof User || this.target instanceof GuildMember;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the content of this message.
|
||||
* @returns {string|string[]}
|
||||
*/
|
||||
makeContent() { // eslint-disable-line complexity
|
||||
const GuildMember = require('./GuildMember');
|
||||
|
||||
// eslint-disable-next-line eqeqeq
|
||||
let content = Util.resolveString(this.options.content == null ? '' : this.options.content);
|
||||
const isSplit = typeof this.options.split !== 'undefined' && this.options.split !== false;
|
||||
const isCode = typeof this.options.code !== 'undefined' && this.options.code !== false;
|
||||
const splitOptions = isSplit ? { ...this.options.split } : undefined;
|
||||
|
||||
let mentionPart = '';
|
||||
if (this.options.reply && !this.isUser && this.target.type !== 'dm') {
|
||||
const id = this.target.client.users.resolveID(this.options.reply);
|
||||
mentionPart = `<@${this.options.reply instanceof GuildMember && this.options.reply.nickname ? '!' : ''}${id}>, `;
|
||||
if (isSplit) {
|
||||
splitOptions.prepend = `${mentionPart}${splitOptions.prepend || ''}`;
|
||||
}
|
||||
}
|
||||
|
||||
if (content || mentionPart) {
|
||||
if (isCode) {
|
||||
const codeName = typeof this.options.code === 'string' ? this.options.code : '';
|
||||
content = `${mentionPart}\`\`\`${codeName}\n${Util.escapeMarkdown(content, true)}\n\`\`\``;
|
||||
if (isSplit) {
|
||||
splitOptions.prepend = `${splitOptions.prepend || ''}\`\`\`${codeName}\n`;
|
||||
splitOptions.append = `\n\`\`\`${splitOptions.append || ''}`;
|
||||
}
|
||||
} else if (mentionPart) {
|
||||
content = `${mentionPart}${content}`;
|
||||
}
|
||||
|
||||
const disableEveryone = typeof this.options.disableEveryone === 'undefined' ?
|
||||
this.target.client.options.disableEveryone :
|
||||
this.options.disableEveryone;
|
||||
if (disableEveryone) {
|
||||
content = content.replace(/@(everyone|here)/g, '@\u200b$1');
|
||||
}
|
||||
|
||||
if (isSplit) {
|
||||
content = Util.splitMessage(content, splitOptions);
|
||||
}
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves data.
|
||||
* @returns {Object}
|
||||
*/
|
||||
resolveData() {
|
||||
const content = this.makeContent();
|
||||
const tts = Boolean(this.options.tts);
|
||||
let nonce;
|
||||
if (typeof this.options.nonce !== 'undefined') {
|
||||
nonce = parseInt(this.options.nonce);
|
||||
if (isNaN(nonce) || nonce < 0) throw new RangeError('MESSAGE_NONCE_TYPE');
|
||||
}
|
||||
|
||||
const embedLikes = [];
|
||||
if (this.isWebhook) {
|
||||
if (this.options.embeds) {
|
||||
embedLikes.push(...this.options.embeds);
|
||||
}
|
||||
} else if (this.options.embed) {
|
||||
embedLikes.push(this.options.embed);
|
||||
}
|
||||
const embeds = embedLikes.map(e => new MessageEmbed(e)._apiTransform());
|
||||
|
||||
let username;
|
||||
let avatarURL;
|
||||
if (this.isWebhook) {
|
||||
username = this.options.username || this.target.name;
|
||||
if (this.options.avatarURL) avatarURL = this.options.avatarURL;
|
||||
}
|
||||
|
||||
return {
|
||||
content,
|
||||
tts,
|
||||
nonce,
|
||||
embed: this.options.embed === null ? null : embeds[0],
|
||||
embeds,
|
||||
username,
|
||||
avatar_url: avatarURL,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves files.
|
||||
* @returns {Promise<Object[]>}
|
||||
*/
|
||||
resolveFiles() {
|
||||
const embedLikes = [];
|
||||
if (this.isWebhook) {
|
||||
if (this.options.embeds) {
|
||||
embedLikes.push(...this.options.embeds);
|
||||
}
|
||||
} else if (this.options.embed) {
|
||||
embedLikes.push(this.options.embed);
|
||||
}
|
||||
|
||||
const fileLikes = [];
|
||||
if (this.options.files) {
|
||||
fileLikes.push(...this.options.files);
|
||||
}
|
||||
for (const embed of embedLikes) {
|
||||
if (embed.files) {
|
||||
fileLikes.push(...embed.files);
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.all(fileLikes.map(f => this.constructor.resolveFile(f)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a single file into an object sendable to the API.
|
||||
* @param {BufferResolvable|Stream|FileOptions|MessageAttachment} fileLike Something that could be resolved to a file
|
||||
* @returns {Object}
|
||||
*/
|
||||
static async resolveFile(fileLike) {
|
||||
let attachment;
|
||||
let name;
|
||||
|
||||
const findName = thing => {
|
||||
if (typeof thing === 'string') {
|
||||
return Util.basename(thing);
|
||||
}
|
||||
|
||||
if (thing.path) {
|
||||
return Util.basename(thing.path);
|
||||
}
|
||||
|
||||
return 'file.jpg';
|
||||
};
|
||||
|
||||
const ownAttachment = typeof fileLike === 'string' ||
|
||||
fileLike instanceof (browser ? ArrayBuffer : Buffer) ||
|
||||
typeof fileLike.pipe === 'function';
|
||||
if (ownAttachment) {
|
||||
attachment = fileLike;
|
||||
name = findName(attachment);
|
||||
} else {
|
||||
attachment = fileLike.attachment;
|
||||
name = fileLike.name || findName(attachment);
|
||||
}
|
||||
|
||||
const resource = await DataResolver.resolveFile(attachment);
|
||||
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 {StringResolvable} [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' && !(content instanceof Array)) {
|
||||
options = content;
|
||||
content = '';
|
||||
}
|
||||
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
|
||||
if (options instanceof MessageEmbed) {
|
||||
return isWebhook ? { content, embeds: [options], ...extra } : { content, embed: options, ...extra };
|
||||
}
|
||||
|
||||
if (options instanceof MessageAttachment) {
|
||||
return { content, files: [options], ...extra };
|
||||
}
|
||||
|
||||
if (options instanceof Array) {
|
||||
const [embeds, files] = this.partitionMessageAdditions(options);
|
||||
return isWebhook ? { content, embeds, files, ...extra } : { content, embed: embeds[0], files, ...extra };
|
||||
} else if (content instanceof Array) {
|
||||
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 {StringResolvable} [content=''] Content to send
|
||||
* @param {MessageOptions|WebhookMessageOptions|MessageAdditions} [options={}] Options to use
|
||||
* @param {MessageOptions|WebhookMessageOptions} [extra={}] - Extra options to add onto transformed options
|
||||
* @returns {MessageOptions|WebhookMessageOptions}
|
||||
*/
|
||||
static create(target, content, options, extra = {}) {
|
||||
const Webhook = require('./Webhook');
|
||||
const WebhookClient = require('../client/WebhookClient');
|
||||
|
||||
const isWebhook = target instanceof Webhook || target instanceof WebhookClient;
|
||||
const transformed = this.transformOptions(content, options, extra, isWebhook);
|
||||
return new this(target, transformed);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = APIMessage;
|
||||
|
||||
/**
|
||||
* A target for a message.
|
||||
* @typedef {TextChannel|DMChannel|GroupDMChannel|User|GuildMember|Webhook|WebhookClient} MessageTarget
|
||||
*/
|
||||
|
||||
/**
|
||||
* Additional items that can be sent with a message.
|
||||
* @typedef {MessageEmbed|MessageAttachment|Array<MessageEmbed|MessageAttachment>} MessageAdditions
|
||||
*/
|
||||
@@ -10,7 +10,7 @@ const { MessageTypes } = require('../util/Constants');
|
||||
const Permissions = require('../util/Permissions');
|
||||
const Base = require('./Base');
|
||||
const { Error, TypeError } = require('../errors');
|
||||
const { createMessage } = require('./shared');
|
||||
const APIMessage = require('./APIMessage');
|
||||
|
||||
/**
|
||||
* Represents a message on Discord.
|
||||
@@ -359,7 +359,7 @@ class Message extends Base {
|
||||
|
||||
/**
|
||||
* Edits the content of the message.
|
||||
* @param {StringResolvable} [content] The new content for the message
|
||||
* @param {StringResolvable} [content=''] The new content for the message
|
||||
* @param {MessageEditOptions|MessageEmbed} [options] The options to provide
|
||||
* @returns {Promise<Message>}
|
||||
* @example
|
||||
@@ -368,17 +368,8 @@ class Message extends Base {
|
||||
* .then(msg => console.log(`Updated the content of a message to ${msg.content}`))
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async edit(content, options) {
|
||||
if (!options && typeof content === 'object' && !(content instanceof Array)) {
|
||||
options = content;
|
||||
content = null;
|
||||
} else if (!options) {
|
||||
options = {};
|
||||
}
|
||||
if (!options.content) options.content = content;
|
||||
|
||||
const { data } = await createMessage(this, options);
|
||||
|
||||
edit(content, options) {
|
||||
const data = APIMessage.create(this, content, options).resolveData();
|
||||
return this.client.api.channels[this.channel.id].messages[this.id]
|
||||
.patch({ data })
|
||||
.then(d => {
|
||||
@@ -467,8 +458,8 @@ class Message extends Base {
|
||||
|
||||
/**
|
||||
* Replies to the message.
|
||||
* @param {StringResolvable} [content] The content for the message
|
||||
* @param {MessageOptions} [options] The options to provide
|
||||
* @param {StringResolvable} [content=''] The content for the message
|
||||
* @param {MessageOptions|MessageAdditions} [options={}] The options to provide
|
||||
* @returns {Promise<Message|Message[]>}
|
||||
* @example
|
||||
* // Reply to a message
|
||||
@@ -477,13 +468,7 @@ class Message extends Base {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
reply(content, options) {
|
||||
if (!options && typeof content === 'object' && !(content instanceof Array)) {
|
||||
options = content;
|
||||
content = '';
|
||||
} else if (!options) {
|
||||
options = {};
|
||||
}
|
||||
return this.channel.send(content, Object.assign(options, { reply: this.member || this.author }));
|
||||
return this.channel.send(APIMessage.transformOptions(content, options, { reply: this.member || this.author }));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,77 +2,41 @@ const Util = require('../util/Util');
|
||||
|
||||
/**
|
||||
* Represents an attachment in a message.
|
||||
* @param {BufferResolvable|Stream} file The file
|
||||
* @param {string} [name] The name of the file, if any
|
||||
*/
|
||||
class MessageAttachment {
|
||||
constructor(file, name, data) {
|
||||
this.file = null;
|
||||
/**
|
||||
* @param {BufferResolvable|Stream} attachment The file
|
||||
* @param {string} [name=null] The name of the file, if any
|
||||
* @param {Object} [data] Extra data
|
||||
*/
|
||||
constructor(attachment, name = null, data) {
|
||||
this.attachment = attachment;
|
||||
this.name = name;
|
||||
if (data) this._patch(data);
|
||||
if (name) this.setAttachment(file, name);
|
||||
else this._attach(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the file
|
||||
* @type {?string}
|
||||
* @readonly
|
||||
*/
|
||||
get name() {
|
||||
return this.file.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* The file
|
||||
* @type {?BufferResolvable|Stream}
|
||||
* @readonly
|
||||
*/
|
||||
get attachment() {
|
||||
return this.file.attachment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the file of this attachment.
|
||||
* @param {BufferResolvable|Stream} file The file
|
||||
* @param {string} name The name of the file
|
||||
* @returns {MessageAttachment} This attachment
|
||||
*/
|
||||
setAttachment(file, name) {
|
||||
this.file = { attachment: file, name };
|
||||
* Sets the file of this attachment.
|
||||
* @param {BufferResolvable|Stream} attachment The file
|
||||
* @param {string} [name=null] The name of the file, if any
|
||||
* @returns {MessageAttachment} This attachment
|
||||
*/
|
||||
setFile(attachment, name = null) {
|
||||
this.attachment = attachment;
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the file of this attachment.
|
||||
* @param {BufferResolvable|Stream} attachment The file
|
||||
* @returns {MessageAttachment} This attachment
|
||||
*/
|
||||
setFile(attachment) {
|
||||
this.file = { attachment };
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of this attachment.
|
||||
* @param {string} name The name of the image
|
||||
* @returns {MessageAttachment} This attachment
|
||||
*/
|
||||
* Sets the name of this attachment.
|
||||
* @param {string} name The name of the file
|
||||
* @returns {MessageAttachment} This attachment
|
||||
*/
|
||||
setName(name) {
|
||||
this.file.name = name;
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the file of this attachment.
|
||||
* @param {BufferResolvable|Stream} file The file
|
||||
* @param {string} name The name of the file
|
||||
* @private
|
||||
*/
|
||||
_attach(file, name) {
|
||||
if (typeof file === 'string') this.file = file;
|
||||
else this.setAttachment(file, name);
|
||||
}
|
||||
|
||||
_patch(data) {
|
||||
/**
|
||||
* The ID of this attachment
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
const MessageAttachment = require('./MessageAttachment');
|
||||
const Util = require('../util/Util');
|
||||
const { RangeError } = require('../errors');
|
||||
|
||||
@@ -141,14 +140,8 @@ class MessageEmbed {
|
||||
* @type {Array<FileOptions|string|MessageAttachment>}
|
||||
*/
|
||||
this.files = [];
|
||||
|
||||
if (data.files) {
|
||||
this.files = data.files.map(file => {
|
||||
if (file instanceof MessageAttachment) {
|
||||
return typeof file.file === 'string' ? file.file : Util.cloneObject(file.file);
|
||||
}
|
||||
return file;
|
||||
});
|
||||
this.files = data.files;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,7 +196,6 @@ class MessageEmbed {
|
||||
* @returns {MessageEmbed}
|
||||
*/
|
||||
attachFiles(files) {
|
||||
files = files.map(file => file instanceof MessageAttachment ? file.file : file);
|
||||
this.files = this.files.concat(files);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const DataResolver = require('../util/DataResolver');
|
||||
const Channel = require('./Channel');
|
||||
const { createMessage } = require('./shared');
|
||||
const APIMessage = require('./APIMessage');
|
||||
|
||||
/**
|
||||
* Represents a webhook.
|
||||
@@ -82,11 +82,10 @@ class Webhook {
|
||||
* it exceeds the character limit. If an object is provided, these are the options for splitting the message.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
/**
|
||||
* Sends a message with this webhook.
|
||||
* @param {StringResolvable} [content] The content to send
|
||||
* @param {WebhookMessageOptions|MessageEmbed|MessageAttachment|MessageAttachment[]} [options={}] The options to provide
|
||||
* @param {StringResolvable} [content=''] The content to send
|
||||
* @param {WebhookMessageOptions|MessageAdditions} [options={}] The options to provide
|
||||
* @returns {Promise<Message|Object>}
|
||||
* @example
|
||||
* // Send a basic message
|
||||
@@ -127,20 +126,18 @@ class Webhook {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async send(content, options) {
|
||||
if (!options && typeof content === 'object' && !(content instanceof Array)) {
|
||||
options = content;
|
||||
content = null;
|
||||
} else if (!options) {
|
||||
options = {};
|
||||
}
|
||||
if (!options.content) options.content = content;
|
||||
|
||||
const { data, files } = await createMessage(this, options);
|
||||
|
||||
const apiMessage = APIMessage.create(this, content, options);
|
||||
const data = apiMessage.resolveData();
|
||||
if (data.content instanceof Array) {
|
||||
const messages = [];
|
||||
for (let i = 0; i < data.content.length; i++) {
|
||||
const opt = i === data.content.length - 1 ? { embeds: data.embeds, files } : {};
|
||||
let opt;
|
||||
if (i === data.content.length - 1) {
|
||||
opt = { embeds: data.embeds, files: apiMessage.options.files };
|
||||
} else {
|
||||
opt = {};
|
||||
}
|
||||
|
||||
Object.assign(opt, { avatarURL: data.avatar_url, content: data.content[i], username: data.username });
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const message = await this.send(data.content[i], opt);
|
||||
@@ -149,7 +146,7 @@ class Webhook {
|
||||
return messages;
|
||||
}
|
||||
|
||||
|
||||
const files = await apiMessage.resolveFiles();
|
||||
return this.client.api.webhooks(this.id, this.token).post({
|
||||
data, files,
|
||||
query: { wait: true },
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
const MessageCollector = require('../MessageCollector');
|
||||
const Shared = require('../shared');
|
||||
const Snowflake = require('../../util/Snowflake');
|
||||
const Collection = require('../../util/Collection');
|
||||
const { RangeError, TypeError } = require('../../errors');
|
||||
const APIMessage = require('../APIMessage');
|
||||
|
||||
/**
|
||||
* Interface for classes that have text-channel-like features.
|
||||
@@ -66,8 +66,8 @@ class TextBasedChannel {
|
||||
|
||||
/**
|
||||
* Sends a message to this channel.
|
||||
* @param {StringResolvable} [content] Text for the message
|
||||
* @param {MessageOptions|MessageEmbed|MessageAttachment|MessageAttachment[]} [options={}] Options for the message
|
||||
* @param {StringResolvable} [content=''] The content to send
|
||||
* @param {MessageOptions|MessageAdditions} [options={}] The options to provide
|
||||
* @returns {Promise<Message|Message[]>}
|
||||
* @example
|
||||
* // Send a basic message
|
||||
@@ -107,16 +107,35 @@ class TextBasedChannel {
|
||||
* .then(console.log)
|
||||
* .catch(console.error);
|
||||
*/
|
||||
send(content, options) { // eslint-disable-line complexity
|
||||
if (!options && typeof content === 'object' && !(content instanceof Array)) {
|
||||
options = content;
|
||||
content = null;
|
||||
} else if (!options) {
|
||||
options = {};
|
||||
async send(content, 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));
|
||||
}
|
||||
if (!options.content) options.content = content;
|
||||
|
||||
return Shared.sendMessage(this, options);
|
||||
const apiMessage = APIMessage.create(this, content, options);
|
||||
const data = apiMessage.resolveData();
|
||||
if (data.content instanceof Array) {
|
||||
const messages = [];
|
||||
for (let i = 0; i < data.content.length; i++) {
|
||||
let opt;
|
||||
if (i === data.content.length - 1) {
|
||||
opt = { tts: data.tts, embed: data.embed, files: apiMessage.options.files };
|
||||
} else {
|
||||
opt = { tts: data.tts };
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const message = await this.send(data.content[i], opt);
|
||||
messages.push(message);
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
const files = await apiMessage.resolveFiles();
|
||||
return this.client.api.channels[this.id].messages.post({ data, files })
|
||||
.then(d => this.client.actions.MessageCreate.handle(d).message);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
const Embed = require('../MessageEmbed');
|
||||
const DataResolver = require('../../util/DataResolver');
|
||||
const MessageEmbed = require('../MessageEmbed');
|
||||
const MessageAttachment = require('../MessageAttachment');
|
||||
const { browser } = require('../../util/Constants');
|
||||
const Util = require('../../util/Util');
|
||||
const { RangeError } = require('../../errors');
|
||||
|
||||
// eslint-disable-next-line complexity
|
||||
module.exports = async function createMessage(channel, options) {
|
||||
const User = require('../User');
|
||||
const GuildMember = require('../GuildMember');
|
||||
const Webhook = require('../Webhook');
|
||||
const WebhookClient = require('../../client/WebhookClient');
|
||||
|
||||
const webhook = channel instanceof Webhook || channel instanceof WebhookClient;
|
||||
|
||||
if (typeof options.nonce !== 'undefined') {
|
||||
options.nonce = parseInt(options.nonce);
|
||||
if (isNaN(options.nonce) || options.nonce < 0) throw new RangeError('MESSAGE_NONCE_TYPE');
|
||||
}
|
||||
|
||||
let { content, reply } = options;
|
||||
if (options instanceof MessageEmbed) options = webhook ? { embeds: [options] } : { embed: options };
|
||||
if (options instanceof MessageAttachment) options = { files: [options.file] };
|
||||
|
||||
if (content instanceof Array || options instanceof Array) {
|
||||
const which = content instanceof Array ? content : options;
|
||||
const attachments = which.filter(item => item instanceof MessageAttachment);
|
||||
const embeds = which.filter(item => item instanceof MessageEmbed);
|
||||
if (attachments.length) options = { files: attachments };
|
||||
if (embeds.length) options = { embeds };
|
||||
if ((embeds.length || attachments.length) && content instanceof Array) {
|
||||
content = null;
|
||||
options.content = '';
|
||||
}
|
||||
}
|
||||
|
||||
if (options.split && typeof options.split !== 'object') options.split = {};
|
||||
let mentionPart = '';
|
||||
if (reply && !(channel instanceof User || channel instanceof GuildMember) && channel.type !== 'dm') {
|
||||
const id = channel.client.users.resolveID(reply);
|
||||
mentionPart = `<@${reply instanceof GuildMember && reply.nickname ? '!' : ''}${id}>, `;
|
||||
if (options.split) options.split.prepend = `${mentionPart}${options.split.prepend || ''}`;
|
||||
}
|
||||
|
||||
if (content || mentionPart) {
|
||||
options.content = Util.resolveString(content || '');
|
||||
// Wrap everything in a code block
|
||||
if (typeof options.code !== 'undefined' && (typeof options.code !== 'boolean' || options.code === true)) {
|
||||
options.content = Util.escapeMarkdown(options.content, true);
|
||||
options.content = `${mentionPart}\`\`\`${typeof options.code !== 'boolean' ?
|
||||
options.code || '' : ''}\n${options.content}\n\`\`\``;
|
||||
if (options.split) {
|
||||
options.split.prepend =
|
||||
`${options.split.prepend || ''}\`\`\`${typeof options.code !== 'boolean' ? options.code || '' : ''}\n`;
|
||||
|
||||
options.split.append = `\n\`\`\`${options.split.append || ''}`;
|
||||
}
|
||||
} else if (mentionPart) {
|
||||
options.content = mentionPart + (options.content || '');
|
||||
}
|
||||
|
||||
// Add zero-width spaces to @everyone/@here
|
||||
if (options.disableEveryone ||
|
||||
(typeof options.disableEveryone === 'undefined' && channel.client.options.disableEveryone)) {
|
||||
options.content = options.content.replace(/@(everyone|here)/g, '@\u200b$1');
|
||||
}
|
||||
|
||||
if (options.split) options.content = Util.splitMessage(options.content, options.split);
|
||||
}
|
||||
|
||||
if (options.embed && options.embed.files) {
|
||||
if (options.files) options.files = options.files.concat(options.embed.files);
|
||||
else options.files = options.embed.files;
|
||||
}
|
||||
|
||||
if (options.embed && webhook) options.embeds = [new Embed(options.embed)._apiTransform()];
|
||||
else if (options.embed) options.embed = new Embed(options.embed)._apiTransform();
|
||||
else if (options.embeds) options.embeds = options.embeds.map(e => new Embed(e)._apiTransform());
|
||||
|
||||
let files;
|
||||
|
||||
if (options.files) {
|
||||
for (let i = 0; i < options.files.length; i++) {
|
||||
let file = options.files[i];
|
||||
if (typeof file === 'string' || (!browser && Buffer.isBuffer(file))) file = { attachment: file };
|
||||
if (!file.name) {
|
||||
if (typeof file.attachment === 'string') {
|
||||
file.name = Util.basename(file.attachment);
|
||||
} else if (file.attachment && file.attachment.path) {
|
||||
file.name = Util.basename(file.attachment.path);
|
||||
} else if (file instanceof MessageAttachment) {
|
||||
file = { attachment: file.file, name: Util.basename(file.file) || 'file.jpg' };
|
||||
} else {
|
||||
file.name = 'file.jpg';
|
||||
}
|
||||
} else if (file instanceof MessageAttachment) {
|
||||
file = file.file;
|
||||
}
|
||||
options.files[i] = file;
|
||||
}
|
||||
|
||||
files = await Promise.all(options.files.map(file =>
|
||||
DataResolver.resolveFile(file.attachment).then(resource => {
|
||||
file.file = resource;
|
||||
return file;
|
||||
})
|
||||
));
|
||||
}
|
||||
|
||||
if (webhook) {
|
||||
if (!options.username) options.username = this.name;
|
||||
if (options.avatarURL) options.avatar_url = options.avatarURL;
|
||||
}
|
||||
|
||||
return { data: {
|
||||
content: options.content,
|
||||
tts: options.tts,
|
||||
nonce: options.nonce,
|
||||
embed: options.embed,
|
||||
embeds: options.embeds,
|
||||
username: options.username,
|
||||
avatar_url: options.avatar_url,
|
||||
}, files };
|
||||
};
|
||||
@@ -1,23 +0,0 @@
|
||||
const createMessage = require('./CreateMessage');
|
||||
|
||||
module.exports = async function sendMessage(channel, options) { // eslint-disable-line complexity
|
||||
const User = require('../User');
|
||||
const GuildMember = require('../GuildMember');
|
||||
if (channel instanceof User || channel instanceof GuildMember) return channel.createDM().then(dm => dm.send(options));
|
||||
|
||||
const { data, files } = await createMessage(channel, options);
|
||||
|
||||
if (data.content instanceof Array) {
|
||||
const messages = [];
|
||||
for (let i = 0; i < data.content.length; i++) {
|
||||
const opt = i === data.content.length - 1 ? { tts: data.tts, embed: data.embed, files } : { tts: data.tts };
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const message = await channel.send(data.content[i], opt);
|
||||
messages.push(message);
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
return channel.client.api.channels[channel.id].messages.post({ data, files })
|
||||
.then(d => channel.client.actions.MessageCreate.handle(d).message);
|
||||
};
|
||||
@@ -1,4 +0,0 @@
|
||||
module.exports = {
|
||||
sendMessage: require('./SendMessage'),
|
||||
createMessage: require('./CreateMessage'),
|
||||
};
|
||||
@@ -103,7 +103,7 @@ class DataResolver {
|
||||
});
|
||||
});
|
||||
}
|
||||
} else if (resource.pipe && typeof resource.pipe === 'function') {
|
||||
} else if (typeof resource.pipe === 'function') {
|
||||
return new Promise((resolve, reject) => {
|
||||
const buffers = [];
|
||||
resource.once('error', reject);
|
||||
|
||||
Reference in New Issue
Block a user