mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-10 00:23:30 +01:00
Add Attachment structure (#1731)
* Add Attachment structure * Fix linter issues + @private * Fixed array sends, also added embed sends * fixed proving path to attachment * fixed incorrect name assumption from path * linting fix * ;) * im really good at this * changes as requested by gus and computer from #1459 * am a dum * update webhook#send * readonly addition to getters * i... uh... oops * farming deez commits * fix webhook split * removed some ugly * removed .every checks
This commit is contained in:
committed by
Schuyler Cebulskie
parent
317a352337
commit
62fc9fce6d
73
src/structures/Attachment.js
Normal file
73
src/structures/Attachment.js
Normal file
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Represents an attachment in a message
|
||||
*/
|
||||
class Attachment {
|
||||
constructor(file, name) {
|
||||
this.file = null;
|
||||
this._attach(file, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the file of this attachment.
|
||||
* @param {BufferResolvable|Stream} file The file
|
||||
* @param {string} name The name of the file
|
||||
* @returns {Attachment} This attachment
|
||||
*/
|
||||
setAttachment(file, name) {
|
||||
this.file = { attachment: file, name };
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the file of this attachment.
|
||||
* @param {BufferResolvable|Stream} attachment The file
|
||||
* @returns {Attachment} This attachment
|
||||
*/
|
||||
setFile(attachment) {
|
||||
this.file.attachment = attachment;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of this attachment.
|
||||
* @param {string} name The name of the image
|
||||
* @returns {Attachment} This attachment
|
||||
*/
|
||||
setName(name) {
|
||||
this.file.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the file of this attachment.
|
||||
* @param {BufferResolvable|Stream} file The file
|
||||
* @param {string} name The name of the file
|
||||
* @private
|
||||
*/
|
||||
_attach(file, name) {
|
||||
if (file) {
|
||||
if (typeof file === 'string') this.file = file;
|
||||
else this.setAttachment(file, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Attachment;
|
||||
@@ -1,3 +1,4 @@
|
||||
const Attachment = require('./Attachment');
|
||||
const Util = require('../util/Util');
|
||||
const { RangeError } = require('../errors');
|
||||
|
||||
@@ -129,6 +130,17 @@ class MessageEmbed {
|
||||
iconURL: data.footer.iconURL || data.footer.icon_url,
|
||||
proxyIconURL: data.footer.proxyIconURL || data.footer.proxy_icon_url,
|
||||
} : null;
|
||||
|
||||
/**
|
||||
* The files of this embed
|
||||
* @type {?Object}
|
||||
* @property {Array<FileOptions|string|Attachment>} files Files to attach
|
||||
*/
|
||||
if (data.files) {
|
||||
for (let file of data.files) {
|
||||
if (file instanceof Attachment) file = file.file;
|
||||
}
|
||||
} else { data.files = null; }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,12 +190,15 @@ class MessageEmbed {
|
||||
/**
|
||||
* Sets the file to upload alongside the embed. This file can be accessed via `attachment://fileName.extension` when
|
||||
* setting an embed image or author/footer icons. Only one file may be attached.
|
||||
* @param {Array<FileOptions|string>} files Files to attach
|
||||
* @param {Array<FileOptions|string|Attachment>} files Files to attach
|
||||
* @returns {MessageEmbed} This embed
|
||||
*/
|
||||
attachFiles(files) {
|
||||
if (this.files) this.files = this.files.concat(files);
|
||||
else this.files = files;
|
||||
for (let file of files) {
|
||||
if (file instanceof Attachment) file = file.file;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -286,6 +301,11 @@ class MessageEmbed {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the embed object to be processed.
|
||||
* @returns {Object} The raw data of this embed
|
||||
* @private
|
||||
*/
|
||||
_apiTransform() {
|
||||
return {
|
||||
title: this.title,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
const path = require('path');
|
||||
const Util = require('../util/Util');
|
||||
const Embed = require('./MessageEmbed');
|
||||
const Attachment = require('./Attachment');
|
||||
const MessageEmbed = require('./MessageEmbed');
|
||||
|
||||
/**
|
||||
* Represents a webhook.
|
||||
@@ -82,7 +84,7 @@ class Webhook {
|
||||
* (see [here](https://discordapp.com/developers/docs/resources/channel#embed-object) for more details)
|
||||
* @property {boolean} [disableEveryone=this.client.options.disableEveryone] Whether or not @everyone and @here
|
||||
* should be replaced with plain-text
|
||||
* @property {FileOptions|string} [file] A file to send with the message
|
||||
* @property {FileOptions|BufferResolvable} [file] A file to send with the message
|
||||
* @property {FileOptions[]|string[]} [files] Files to send with the message
|
||||
* @property {string|boolean} [code] Language for optional codeblock formatting to apply
|
||||
* @property {boolean|SplitOptions} [split=false] Whether or not the message should be split into multiple messages if
|
||||
@@ -100,7 +102,7 @@ class Webhook {
|
||||
* .then(message => console.log(`Sent message: ${message.content}`))
|
||||
* .catch(console.error);
|
||||
*/
|
||||
send(content, options) {
|
||||
send(content, options) { // eslint-disable-line complexity
|
||||
if (!options && typeof content === 'object' && !(content instanceof Array)) {
|
||||
options = content;
|
||||
content = '';
|
||||
@@ -108,49 +110,70 @@ class Webhook {
|
||||
options = {};
|
||||
}
|
||||
|
||||
if (!options.username) options.username = this.name;
|
||||
if (options instanceof Attachment) 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 Attachment);
|
||||
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 (typeof content !== 'undefined') content = Util.resolveString(content);
|
||||
if (content) {
|
||||
if (options.disableEveryone ||
|
||||
(typeof options.disableEveryone === 'undefined' && this.client.options.disableEveryone)
|
||||
) {
|
||||
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.file) {
|
||||
if (options.files) options.files.push(options.file);
|
||||
else options.files = [options.file];
|
||||
}
|
||||
|
||||
if (options.files) {
|
||||
for (let i = 0; i < options.files.length; i++) {
|
||||
let file = options.files[i];
|
||||
if (typeof file === 'string') file = { attachment: file };
|
||||
if (typeof file === 'string' || Buffer.isBuffer(file)) file = { attachment: file };
|
||||
if (!file.name) {
|
||||
if (typeof file.attachment === 'string') {
|
||||
file.name = path.basename(file.attachment);
|
||||
} else if (file.attachment && file.attachment.path) {
|
||||
file.name = path.basename(file.attachment.path);
|
||||
} else if (file instanceof Attachment) {
|
||||
file = { attachment: file.file, name: path.basename(file.file) || 'file.jpg' };
|
||||
} else {
|
||||
file.name = 'file.jpg';
|
||||
}
|
||||
} else if (file instanceof Attachment) {
|
||||
file = file.file;
|
||||
}
|
||||
options.files[i] = file;
|
||||
}
|
||||
|
||||
return Promise.all(options.files.map(file =>
|
||||
this.client.resolver.resolveBuffer(file.attachment).then(buffer => {
|
||||
file.file = buffer;
|
||||
this.client.resolver.resolveFile(file.attachment).then(resource => {
|
||||
file.file = resource;
|
||||
return file;
|
||||
})
|
||||
)).then(files => this.client.api.webhooks(this.id, this.token).post({
|
||||
@@ -161,6 +184,26 @@ class Webhook {
|
||||
}));
|
||||
}
|
||||
|
||||
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));
|
||||
});
|
||||
}
|
||||
|
||||
return this.client.api.webhooks(this.id, this.token).post({
|
||||
data: options,
|
||||
query: { wait: true },
|
||||
|
||||
@@ -3,6 +3,8 @@ const MessageCollector = require('../MessageCollector');
|
||||
const Shared = require('../shared');
|
||||
const Collection = require('../../util/Collection');
|
||||
const Snowflake = require('../../util/Snowflake');
|
||||
const Attachment = require('../../structures/Attachment');
|
||||
const MessageEmbed = require('../../structures/MessageEmbed');
|
||||
const { Error, RangeError, TypeError } = require('../../errors');
|
||||
|
||||
/**
|
||||
@@ -39,7 +41,7 @@ class TextBasedChannel {
|
||||
* (see [here](https://discordapp.com/developers/docs/resources/channel#embed-object) for more details)
|
||||
* @property {boolean} [disableEveryone=this.client.options.disableEveryone] Whether or not @everyone and @here
|
||||
* should be replaced with plain-text
|
||||
* @property {FileOptions[]|string[]} [files] Files to send with the message
|
||||
* @property {FileOptions[]|BufferResolvable[]} [files] Files to send with the message
|
||||
* @property {string|boolean} [code] Language for optional codeblock formatting to apply
|
||||
* @property {boolean|SplitOptions} [split=false] Whether or not the message should be split into multiple messages if
|
||||
* it exceeds the character limit. If an object is provided, these are the options for splitting the message
|
||||
@@ -72,7 +74,7 @@ class TextBasedChannel {
|
||||
* .then(message => console.log(`Sent message: ${message.content}`))
|
||||
* .catch(console.error);
|
||||
*/
|
||||
send(content, options) {
|
||||
send(content, options) { // eslint-disable-line complexity
|
||||
if (!options && typeof content === 'object' && !(content instanceof Array)) {
|
||||
options = content;
|
||||
content = '';
|
||||
@@ -80,6 +82,18 @@ class TextBasedChannel {
|
||||
options = {};
|
||||
}
|
||||
|
||||
if (options instanceof MessageEmbed) options = { embed: options };
|
||||
if (options instanceof Attachment) 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 Attachment);
|
||||
if (attachments.length) {
|
||||
options = { files: attachments };
|
||||
if (content instanceof Array) content = '';
|
||||
}
|
||||
}
|
||||
|
||||
if (!options.content) options.content = content;
|
||||
|
||||
if (options.embed && options.embed.files) {
|
||||
@@ -90,22 +104,26 @@ class TextBasedChannel {
|
||||
if (options.files) {
|
||||
for (let i = 0; i < options.files.length; i++) {
|
||||
let file = options.files[i];
|
||||
if (typeof file === 'string') file = { attachment: file };
|
||||
if (typeof file === 'string' || Buffer.isBuffer(file)) file = { attachment: file };
|
||||
if (!file.name) {
|
||||
if (typeof file.attachment === 'string') {
|
||||
file.name = path.basename(file.attachment);
|
||||
} else if (file.attachment && file.attachment.path) {
|
||||
file.name = path.basename(file.attachment.path);
|
||||
} else if (file instanceof Attachment) {
|
||||
file = { attachment: file.file, name: path.basename(file.file) || 'file.jpg' };
|
||||
} else {
|
||||
file.name = 'file.jpg';
|
||||
}
|
||||
} else if (file instanceof Attachment) {
|
||||
file = file.file;
|
||||
}
|
||||
options.files[i] = file;
|
||||
}
|
||||
|
||||
return Promise.all(options.files.map(file =>
|
||||
this.client.resolver.resolveBuffer(file.attachment).then(buffer => {
|
||||
file.file = buffer;
|
||||
this.client.resolver.resolveFile(file.attachment).then(resource => {
|
||||
file.file = resource;
|
||||
return file;
|
||||
})
|
||||
)).then(files => {
|
||||
|
||||
Reference in New Issue
Block a user