Centralise message sending logic in one method, remove sendTTSMessage, add client shortcut in RESTMethods (#1031)

* start wip rewrite of sending/editing messages

* pass the build, modify the edit method to fit the new system

* simplify the applyToClass method

* change handling of file options

* add back message splitting

* i couldn't help myself

* add some smart message options

* clean up, add sexy options

* fix indentation

* fix up splitting

* add \b

* add back old methods for hydar happiness

* clean up more

* move code handling to the rest method

* clean up this.rest.client

* Update TextBasedChannel.js

* add docs back for the bad methods

* fix reply in group dms

* srsly gawdl3y

* make code better

* fix changes for gawdl3y

* fix checking

* remove getter

* make code handling more robust

* k

* fix up sendEmbed docs

* stupid

* fix up more docs because aaaaa

* no more pls
This commit is contained in:
Gus Caplan
2016-12-28 22:58:30 -06:00
committed by Schuyler Cebulskie
parent a014b59790
commit ed8fcf651a
6 changed files with 201 additions and 198 deletions

View File

@@ -430,8 +430,8 @@ class GuildMember {
}
// These are here only for documentation purposes - they are implemented by TextBasedChannel
send() { return; }
sendMessage() { return; }
sendTTSMessage() { return; }
sendEmbed() { return; }
sendFile() { return; }
sendCode() { return; }

View File

@@ -369,12 +369,13 @@ class Message {
* Options that can be passed into editMessage
* @typedef {Object} MessageEditOptions
* @property {Object} [embed] An embed to be added/edited
* @property {string} [code] Language for optional codeblock formatting to apply
*/
/**
* Edit the content of the message
* @param {StringResolvable} content The new content for the message
* @param {MessageEditOptions} [options={}] The options to provide
* @param {StringResolvable} [content] The new content for the message
* @param {MessageEditOptions} [options] The options to provide
* @returns {Promise<Message>}
* @example
* // update the content of a message
@@ -382,7 +383,13 @@ class Message {
* .then(msg => console.log(`Updated the content of a message from ${msg.author}`))
* .catch(console.error);
*/
edit(content, options = {}) {
edit(content, options) {
if (!options && typeof content === 'object') {
options = content;
content = '';
} else if (!options) {
options = {};
}
return this.client.rest.methods.updateMessage(this, content, options);
}
@@ -467,16 +474,8 @@ class Message {
* .catch(console.error);
*/
reply(content, options = {}) {
content = this.client.resolver.resolveString(content);
const prepend = this.guild ? `${this.author}, ` : '';
content = `${prepend}${content}`;
if (options.split) {
if (typeof options.split !== 'object') options.split = {};
if (!options.split.prepend) options.split.prepend = prepend;
}
return this.client.rest.methods.sendMessage(this.channel, content, options);
content = `${this.guild || this.channel.type === 'group' ? `${this.author}, ` : ''}${content}`;
return this.channel.send(content, options);
}
/**

View File

@@ -73,8 +73,8 @@ class TextChannel extends GuildChannel {
}
// These are here only for documentation purposes - they are implemented by TextBasedChannel
send() { return; }
sendMessage() { return; }
sendTTSMessage() { return; }
sendEmbed() { return; }
sendFile() { return; }
sendCode() { return; }

View File

@@ -265,8 +265,8 @@ class User {
}
// These are here only for documentation purposes - they are implemented by TextBasedChannel
send() { return; }
sendMessage() { return; }
sendTTSMessage() { return; }
sendEmbed() { return; }
sendFile() { return; }
sendCode() { return; }

View File

@@ -2,8 +2,7 @@ const path = require('path');
const Message = require('../Message');
const MessageCollector = require('../MessageCollector');
const Collection = require('../../util/Collection');
const RichEmbed = require('../RichEmbed');
const escapeMarkdown = require('../../util/EscapeMarkdown');
/**
* Interface for classes that have text-channel-like features
@@ -25,7 +24,7 @@ class TextBasedChannel {
}
/**
* Options that can be passed into sendMessage, sendTTSMessage, sendFile, sendCode, or Message.reply
* Options that can be passed into send, sendMessage, sendFile, sendEmbed, sendCode, and Message#reply
* @typedef {Object} MessageOptions
* @property {boolean} [tts=false] Whether or not the message should be spoken aloud
* @property {string} [nonce=''] The nonce for the message
@@ -33,10 +32,18 @@ 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} [file] A file to send with the message
* @property {string} [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.
*/
/**
* @typedef {Object} FileOptions
* @property {BufferResolvable} attachment
* @property {string} [name='file.jpg']
*/
/**
* Options for splitting a message
* @typedef {Object} SplitOptions
@@ -46,6 +53,45 @@ class TextBasedChannel {
* @property {string} [append=''] Text to append to every piece except the last
*/
/**
* Send a message to this channel
* @param {StringResolvable} [content] The content to send
* @param {MessageOptions} [options={}] The options to provide
* @returns {Promise<Message|Message[]>}
* @example
* // send a message
* channel.send('hello!')
* .then(message => console.log(`Sent message: ${message.content}`))
* .catch(console.error);
*/
send(content, options) {
if (!options && typeof content === 'object') {
options = content;
content = '';
} else if (!options) {
options = {};
}
if (options.file) {
if (typeof options.file === 'string') options.file = { attachment: options.file };
if (!options.file.name) {
if (typeof options.file.attachment === 'string') {
options.file.name = path.basename(options.file.attachment);
} else if (options.file.attachment && options.file.attachment.path) {
options.file.name = path.basename(options.file.attachment.path);
} else {
options.file.name = 'file.jpg';
}
}
return this.client.resolver.resolveBuffer(options.file.attachment).then(file =>
this.client.rest.methods.sendMessage(this, content, options, {
file,
name: options.file.name,
})
);
}
return this.client.rest.methods.sendMessage(this, content, options);
}
/**
* Send a message to this channel
* @param {StringResolvable} content The content to send
@@ -57,88 +103,48 @@ class TextBasedChannel {
* .then(message => console.log(`Sent message: ${message.content}`))
* .catch(console.error);
*/
sendMessage(content, options = {}) {
return this.client.rest.methods.sendMessage(this, content, options);
}
/**
* Send a text-to-speech message to this channel
* @param {StringResolvable} content The content to send
* @param {MessageOptions} [options={}] The options to provide
* @returns {Promise<Message|Message[]>}
* @example
* // send a TTS message
* channel.sendTTSMessage('hello!')
* .then(message => console.log(`Sent tts message: ${message.content}`))
* .catch(console.error);
*/
sendTTSMessage(content, options = {}) {
Object.assign(options, { tts: true });
return this.client.rest.methods.sendMessage(this, content, options);
sendMessage(content, options) {
return this.send(content, options);
}
/**
* Send an embed to this channel
* @param {RichEmbed|Object} embed The embed to send
* @param {string|MessageOptions} contentOrOptions Content to send or message options
* @param {MessageOptions} options If contentOrOptions is content, this will be options
* @param {string} [content] Content to send
* @param {MessageOptions} [options] The options to provide
* @returns {Promise<Message>}
*/
sendEmbed(embed, contentOrOptions, options = {}) {
if (!(embed instanceof RichEmbed)) embed = new RichEmbed(embed);
let content;
if (contentOrOptions) {
if (typeof contentOrOptions === 'string') {
content = contentOrOptions;
} else {
options = contentOrOptions;
}
sendEmbed(embed, content, options) {
if (!options && typeof content === 'object') {
options = content;
content = '';
} else if (!options) {
options = {};
}
options.embed = embed;
return this.sendMessage(content, options);
return this.send(content, Object.assign(options, { embed }));
}
/**
* Send a file to this channel
* @param {BufferResolvable} attachment The file to send
* @param {string} [fileName="file.jpg"] The name and extension of the file
* @param {string} [name='file.jpg'] The name and extension of the file
* @param {StringResolvable} [content] Text message to send with the attachment
* @param {MessageOptions} [options] The options to provide
* @returns {Promise<Message>}
*/
sendFile(attachment, fileName, content, options = {}) {
if (!fileName) {
if (typeof attachment === 'string') {
fileName = path.basename(attachment);
} else if (attachment && attachment.path) {
fileName = path.basename(attachment.path);
} else {
fileName = 'file.jpg';
}
}
return this.client.resolver.resolveBuffer(attachment).then(file =>
this.client.rest.methods.sendMessage(this, content, options, {
file,
name: fileName,
})
);
sendFile(attachment, name, content, options = {}) {
return this.send(content, Object.assign(options, { file: { attachment, name } }));
}
/**
* Send a code block to this channel
* @param {string} lang Language for the code block
* @param {StringResolvable} content Content of the code block
* @param {MessageOptions} options The options to provide
* @param {MessageOptions} [options] The options to provide
* @returns {Promise<Message|Message[]>}
*/
sendCode(lang, content, options = {}) {
if (options.split) {
if (typeof options.split !== 'object') options.split = {};
if (!options.split.prepend) options.split.prepend = `\`\`\`${lang || ''}\n`;
if (!options.split.append) options.split.append = '\n```';
}
content = escapeMarkdown(this.client.resolver.resolveString(content), true);
return this.sendMessage(`\`\`\`${lang || ''}\n${content}\n\`\`\``, options);
return this.send(content, Object.assign(options, { code: lang }));
}
/**
@@ -349,19 +355,21 @@ class TextBasedChannel {
}
exports.applyToClass = (structure, full = false) => {
const props = ['sendMessage', 'sendTTSMessage', 'sendEmbed', 'sendFile', 'sendCode'];
const props = ['send', 'sendMessage', 'sendEmbed', 'sendFile', 'sendCode'];
if (full) {
props.push('_cacheMessage');
props.push('fetchMessages');
props.push('fetchMessage');
props.push('bulkDelete');
props.push('startTyping');
props.push('stopTyping');
props.push('typing');
props.push('typingCount');
props.push('fetchPinnedMessages');
props.push('createCollector');
props.push('awaitMessages');
props.push(
'_cacheMessage',
'fetchMessages',
'fetchMessage',
'bulkDelete',
'startTyping',
'stopTyping',
'typing',
'typingCount',
'fetchPinnedMessages',
'createCollector',
'awaitMessages'
);
}
for (const prop of props) {
Object.defineProperty(structure.prototype, prop, Object.getOwnPropertyDescriptor(TextBasedChannel.prototype, prop));