diff --git a/src/structures/Message.js b/src/structures/Message.js index ee4c9b662..a409e411a 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -8,9 +8,9 @@ const Collection = require('../util/Collection'); const ReactionStore = require('../stores/ReactionStore'); const { MessageTypes } = require('../util/Constants'); const Permissions = require('../util/Permissions'); -const GuildMember = require('./GuildMember'); const Base = require('./Base'); const { Error, TypeError } = require('../errors'); +const { createMessage } = require('./shared'); /** * Represents a message on Discord. @@ -368,41 +368,22 @@ class Message extends Base { * .then(msg => console.log(`Updated the content of a message from ${msg.author}`)) * .catch(console.error); */ - edit(content, options) { + async edit(content, options) { if (!options && typeof content === 'object' && !(content instanceof Array)) { options = content; - content = ''; + content = null; } else if (!options) { options = {}; } - if (options instanceof Embed) options = { embed: options }; + if (!options.content) options.content = content; - if (typeof options.content !== 'undefined') content = options.content; - - if (typeof content !== 'undefined') content = Util.resolveString(content); - - let { embed, code, reply } = options; - - if (embed) embed = new Embed(embed)._apiTransform(); - - // Wrap everything in a code block - if (typeof code !== 'undefined' && (typeof code !== 'boolean' || code === true)) { - content = Util.escapeMarkdown(Util.resolveString(content), true); - content = `\`\`\`${typeof code !== 'boolean' ? code || '' : ''}\n${content}\n\`\`\``; - } - - // Add the reply prefix - if (reply && this.channel.type !== 'dm') { - const id = this.client.users.resolveID(reply); - const mention = `<@${reply instanceof GuildMember && reply.nickname ? '!' : ''}${id}>`; - content = `${mention}${content ? `, ${content}` : ''}`; - } + const { data, files } = await createMessage(this, options); return this.client.api.channels[this.channel.id].messages[this.id] - .patch({ data: { content, embed } }) - .then(data => { + .patch({ data, files }) + .then(d => { const clone = this._clone(); - clone._patch(data); + clone._patch(d); return clone; }); } diff --git a/src/structures/Webhook.js b/src/structures/Webhook.js index f1a5c6a11..3601b9ca4 100644 --- a/src/structures/Webhook.js +++ b/src/structures/Webhook.js @@ -1,9 +1,5 @@ -const Util = require('../util/Util'); const DataResolver = require('../util/DataResolver'); -const Embed = require('./MessageEmbed'); -const MessageAttachment = require('./MessageAttachment'); -const MessageEmbed = require('./MessageEmbed'); -const { browser } = require('../util/Constants'); +const { createMessage } = require('./shared'); /** * Represents a webhook. @@ -98,115 +94,24 @@ class Webhook { * .catch(console.error); */ /* eslint-enable max-len */ - send(content, options) { // eslint-disable-line complexity + async send(content, options) { // eslint-disable-line complexity if (!options && typeof content === 'object' && !(content instanceof Array)) { options = content; - content = ''; + content = null; } else if (!options) { options = {}; } + if (!options.content) options.content = content; - if (options instanceof MessageAttachment) options = { files: [options.file] }; - if (options instanceof MessageEmbed) options = { embeds: [options] }; - if (options.embed) options = { embeds: [options.embed] }; - - 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 = ''; - } - - if (!options.username) options.username = this.name; - if (options.avatarURL) { - options.avatar_url = options.avatarURL; - options.avatarURL = null; - } - - if (content) { - content = Util.resolveString(content); - let { split, code, disableEveryone } = options; - if (split && typeof split !== 'object') split = {}; - if (typeof code !== 'undefined' && (typeof code !== 'boolean' || code === true)) { - content = Util.escapeMarkdown(content, true); - content = `\`\`\`${typeof code !== 'boolean' ? code || '' : ''}\n${content}\n\`\`\``; - if (split) { - split.prepend = `\`\`\`${typeof code !== 'boolean' ? code || '' : ''}\n`; - split.append = '\n```'; - } - } - if (disableEveryone || (typeof disableEveryone === 'undefined' && this.client.options.disableEveryone)) { - content = content.replace(/@(everyone|here)/g, '@\u200b$1'); - } - - if (split) content = Util.splitMessage(content, split); - } - options.content = content; - - if (options.embeds) options.embeds = options.embeds.map(embed => new Embed(embed)._apiTransform()); - - 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; - } - - return Promise.all(options.files.map(file => - DataResolver.resolveFile(file.attachment).then(resource => { - file.file = resource; - return file; - }) - )).then(files => this.client.api.webhooks(this.id, this.token).post({ - data: options, - query: { wait: true }, - files, - auth: false, - })); - } - - if (content instanceof Array) { - return new Promise((resolve, reject) => { - const messages = []; - (function sendChunk() { - const opt = content.length ? null : { embeds: options.embeds, files: options.files }; - this.client.api.webhooks(this.id, this.token).post({ - data: { content: content.shift(), opt }, - query: { wait: true }, - auth: false, - }) - .then(message => { - messages.push(message); - if (content.length === 0) return resolve(messages); - return sendChunk.call(this); - }) - .catch(reject); - }.call(this)); - }); - } + const { data, files } = await createMessage(this, options); return this.client.api.webhooks(this.id, this.token).post({ - data: options, + data, files, query: { wait: true }, auth: false, - }).then(data => { - if (!this.client.channels) return data; - return this.client.channels.get(data.channel_id).messages.create(data, false); + }).then(d => { + if (!this.client.channels) return d; + return this.client.channels.get(d.channel_id).messages.create(d, false); }); } diff --git a/src/structures/interfaces/TextBasedChannel.js b/src/structures/interfaces/TextBasedChannel.js index 77bf65935..39aac0937 100644 --- a/src/structures/interfaces/TextBasedChannel.js +++ b/src/structures/interfaces/TextBasedChannel.js @@ -1,12 +1,7 @@ const MessageCollector = require('../MessageCollector'); const Shared = require('../shared'); -const Util = require('../../util/Util'); -const { browser } = require('../../util/Constants'); const Snowflake = require('../../util/Snowflake'); const Collection = require('../../util/Collection'); -const DataResolver = require('../../util/DataResolver'); -const MessageAttachment = require('../../structures/MessageAttachment'); -const MessageEmbed = require('../../structures/MessageEmbed'); const { RangeError, TypeError } = require('../../errors'); /** @@ -80,61 +75,12 @@ class TextBasedChannel { send(content, options) { // eslint-disable-line complexity if (!options && typeof content === 'object' && !(content instanceof Array)) { options = content; - content = ''; + content = null; } else if (!options) { options = {}; } - - if (options instanceof MessageEmbed) 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); - if (attachments.length) { - options = { files: attachments }; - if (content instanceof Array) content = ''; - } - } - if (!options.content) options.content = content; - 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.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; - } - - return Promise.all(options.files.map(file => - DataResolver.resolveFile(file.attachment).then(resource => { - file.file = resource; - return file; - }) - )).then(files => { - options.files = files; - return Shared.sendMessage(this, options); - }); - } - return Shared.sendMessage(this, options); } diff --git a/src/structures/shared/CreateMessage.js b/src/structures/shared/CreateMessage.js new file mode 100644 index 000000000..65c7c2ca3 --- /dev/null +++ b/src/structures/shared/CreateMessage.js @@ -0,0 +1,111 @@ +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'); + +// 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 webhook = channel instanceof Webhook; + + if (typeof options.nonce !== 'undefined') { + options.nonce = parseInt(options.nonce); + if (isNaN(options.nonce) || options.nonce < 0) throw new RangeError('MESSAGE_NONCE_TYPE'); + } + + if (options instanceof MessageEmbed) options = webhook ? { embeds: [options] } : { embed: options }; + if (options instanceof MessageAttachment) options = { files: [options.file] }; + + if (options.reply && !(channel instanceof User || channel instanceof GuildMember) && channel.type !== 'dm') { + const id = channel.client.users.resolveID(options.reply); + const mention = `<@${options.reply instanceof GuildMember && options.reply.nickname ? '!' : ''}${id}>`; + if (options.split) options.split.prepend = `${mention}, ${options.split.prepend || ''}`; + options.content = `${mention}${typeof options.content !== 'undefined' ? `, ${options.content}` : ''}`; + } + + if (options.content) { + options.content = Util.resolveString(options.content); + if (options.split && typeof options.split !== 'object') options.split = {}; + // 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 = + `\`\`\`${typeof options.code !== 'boolean' ? options.code || '' : ''}\n${options.content}\n\`\`\``; + if (options.split) { + options.split.prepend = `\`\`\`${typeof options.code !== 'boolean' ? options.code || '' : ''}\n`; + options.split.append = '\n```'; + } + } + + // 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; + }) + )); + delete options.files; + } + + if (webhook) { + if (!options.username) options.username = this.name; + if (options.avatarURL) { + options.avatar_url = options.avatarURL; + options.avatarURL = null; + } + } + + return { data: { + content: options.content, + tts: options.tts, + nonce: options.nonce, + embed: options.embed, + embeds: options.embeds, + username: options.username, + avatar_url: options.avatarURL, + }, files }; +}; diff --git a/src/structures/shared/SendMessage.js b/src/structures/shared/SendMessage.js index 560ece728..5007a0af5 100644 --- a/src/structures/shared/SendMessage.js +++ b/src/structures/shared/SendMessage.js @@ -1,65 +1,12 @@ -const Util = require('../../util/Util'); -const Embed = require('../MessageEmbed'); -const { RangeError } = require('../../errors'); +const createMessage = require('./CreateMessage'); -module.exports = function sendMessage(channel, options) { // eslint-disable-line complexity +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)); - let { content, nonce, reply, code, disableEveryone, tts, embed, files, split } = options; - if (embed) embed = new Embed(embed)._apiTransform(); + const { data, files } = await createMessage(channel, options); - if (typeof nonce !== 'undefined') { - nonce = parseInt(nonce); - if (isNaN(nonce) || nonce < 0) throw new RangeError('MESSAGE_NONCE_TYPE'); - } - - // Add the reply prefix - if (reply && !(channel instanceof User || channel instanceof GuildMember) && channel.type !== 'dm') { - const id = channel.client.users.resolveID(reply); - const mention = `<@${reply instanceof GuildMember && reply.nickname ? '!' : ''}${id}>`; - if (split) split.prepend = `${mention}, ${split.prepend || ''}`; - content = `${mention}${typeof content !== 'undefined' ? `, ${content}` : ''}`; - } - - if (content) { - content = Util.resolveString(content); - if (split && typeof split !== 'object') split = {}; - // Wrap everything in a code block - if (typeof code !== 'undefined' && (typeof code !== 'boolean' || code === true)) { - content = Util.escapeMarkdown(content, true); - content = `\`\`\`${typeof code !== 'boolean' ? code || '' : ''}\n${content}\n\`\`\``; - if (split) { - split.prepend = `\`\`\`${typeof code !== 'boolean' ? code || '' : ''}\n`; - split.append = '\n```'; - } - } - - // Add zero-width spaces to @everyone/@here - if (disableEveryone || (typeof disableEveryone === 'undefined' && channel.client.options.disableEveryone)) { - content = content.replace(/@(everyone|here)/g, '@\u200b$1'); - } - - if (split) content = Util.splitMessage(content, split); - } - - if (content instanceof Array) { - return new Promise((resolve, reject) => { - const messages = []; - (function sendChunk() { - const opt = content.length ? { tts } : { tts, embed, files }; - channel.send(content.shift(), opt).then(message => { - messages.push(message); - if (content.length === 0) return resolve(messages); - return sendChunk(); - }).catch(reject); - }()); - }); - } - - return channel.client.api.channels[channel.id].messages.post({ - data: { content, tts, nonce, embed }, - files, - }).then(data => channel.client.actions.MessageCreate.handle(data).message); + return channel.client.api.channels[channel.id].messages.post({ data, files }) + .then(d => channel.client.actions.MessageCreate.handle(d).message); }; diff --git a/src/structures/shared/index.js b/src/structures/shared/index.js index 67eed7f83..67a09646b 100644 --- a/src/structures/shared/index.js +++ b/src/structures/shared/index.js @@ -1,4 +1,5 @@ module.exports = { search: require('./Search'), sendMessage: require('./SendMessage'), + createMessage: require('./CreateMessage'), };