diff --git a/src/managers/MessageManager.js b/src/managers/MessageManager.js index 8df800159c81..d889695ea415 100644 --- a/src/managers/MessageManager.js +++ b/src/managers/MessageManager.js @@ -2,6 +2,7 @@ const BaseManager = require('./BaseManager'); const { TypeError } = require('../errors'); +const APIMessage = require('../structures/APIMessage'); const Message = require('../structures/Message'); const Collection = require('../util/Collection'); const LimitedCollection = require('../util/LimitedCollection'); @@ -113,6 +114,81 @@ class MessageManager extends BaseManager { * @returns {?Snowflake} */ + /** + * Edits a message, even if it's not cached. + * @param {MessageResolvable} message The message to edit + * @param {MessageEditOptions} [options] The options to provide + * @returns {Promise} + */ + async edit(message, options) { + message = this.resolveID(message); + if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable'); + + const { data } = APIMessage.create(this, options).resolveData(); + const d = await this.client.api.channels[this.channel.id].messages[message].patch({ data }); + + if (this.cache.has(message)) { + const clone = this.cache.get(message)._clone(); + clone._patch(d); + return clone; + } + return this.add(d); + } + + /** + * Publishes a message in an announcement channel to all channels following it, even if it's not cached. + * @param {MessageResolvable} message The message to publish + * @returns {Promise} + */ + async crosspost(message) { + message = this.resolveID(message); + if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable'); + + const data = await this.client.api.channels(this.channel.id).messages(message).crosspost.post(); + return this.cache.get(data.id) || this.add(data); + } + + /** + * Pins a message to the channel's pinned messages, even if it's not cached. + * @param {MessageResolvable} message The message to pin + * @returns {Promise} + */ + async pin(message) { + message = this.resolveID(message); + if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable'); + + await this.client.api.channels(this.channel.id).pins(message).put(); + } + + /** + * Unins a message from the channel's pinned messages, even if it's not cached. + * @param {MessageResolvable} message The message to unpin + * @returns {Promise} + */ + async unpin(message) { + message = this.resolveID(message); + if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable'); + + await this.client.api.channels(this.channel.id).pins(message).delete(); + } + + /** + * Adds a reaction to a message, even if it's not cached. + * @param {MessageResolvable} message The messag to react to + * @param {EmojiIdentifierResolvable} emoji The emoji to react with + * @returns {Promise} + */ + async react(message, emoji) { + message = this.resolveID(message); + if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable'); + + emoji = this.client.emojis.resolveIdentifier(emoji); + if (!emoji) throw new TypeError('EMOJI_TYPE', 'emoji', 'EmojiIdentifierResolvable'); + + // eslint-disable-next-line newline-per-chained-call + await this.client.api.channels(this.channel.id).messages(message).reactions(emoji, '@me').put(); + } + /** * Deletes a message, even if it's not cached. * @param {MessageResolvable} message The message to delete diff --git a/src/structures/Message.js b/src/structures/Message.js index 72f287411f84..8d34a2567b10 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -8,7 +8,7 @@ const Embed = require('./MessageEmbed'); const Mentions = require('./MessageMentions'); const ReactionCollector = require('./ReactionCollector'); const Sticker = require('./Sticker'); -const { Error, TypeError } = require('../errors'); +const { Error } = require('../errors'); const ReactionManager = require('../managers/ReactionManager'); const Collection = require('../util/Collection'); const { InteractionTypes, MessageTypes, SystemMessageTypes } = require('../util/Constants'); @@ -472,7 +472,7 @@ class Message extends Base { } /** - * Options that can be passed into editMessage. + * Options that can be passed into {@link Message#edit}. * @typedef {Object} MessageEditOptions * @property {string} [content] Content to be edited * @property {MessageEmbed|Object} [embed] An embed to be added/edited @@ -494,13 +494,11 @@ class Message extends Base { * .catch(console.error); */ edit(content, options) { - const { data } = - content instanceof APIMessage ? content.resolveData() : APIMessage.create(this, content, options).resolveData(); - return this.client.api.channels[this.channel.id].messages[this.id].patch({ data }).then(d => { - const clone = this._clone(); - clone._patch(d); - return clone; - }); + if (!options && typeof content === 'object' && !Array.isArray(content)) { + options = content; + content = undefined; + } + return this.channel.messages.edit(this.id, { content, ...options }); } /** @@ -514,47 +512,34 @@ class Message extends Base { * .catch(console.error); * } */ - async crosspost() { - await this.client.api.channels(this.channel.id).messages(this.id).crosspost.post(); - return this; + crosspost() { + return this.channel.messages.crosspost(this.id); } /** * Pins this message to the channel's pinned messages. - * @param {Object} [options] Options for pinning - * @param {string} [options.reason] Reason for pinning * @returns {Promise} * @example - * // Pin a message with a reason - * message.pin({ reason: 'important' }) + * // Pin a message + * message.pin() * .then(console.log) * .catch(console.error) */ - pin(options) { - return this.client.api - .channels(this.channel.id) - .pins(this.id) - .put(options) - .then(() => this); + pin() { + return this.channel.messages.pin(this.id).then(() => this); } /** * Unpins this message from the channel's pinned messages. - * @param {Object} [options] Options for unpinning - * @param {string} [options.reason] Reason for unpinning * @returns {Promise} * @example - * // Unpin a message with a reason - * message.unpin({ reason: 'no longer relevant' }) + * // Unpin a message + * message.unpin() * .then(console.log) * .catch(console.error) */ - unpin(options) { - return this.client.api - .channels(this.channel.id) - .pins(this.id) - .delete(options) - .then(() => this); + unpin() { + return this.channel.messages.unpin(this.id).then(() => this); } /** @@ -572,24 +557,14 @@ class Message extends Base { * .then(console.log) * .catch(console.error); */ - react(emoji) { - emoji = this.client.emojis.resolveIdentifier(emoji); - if (!emoji) throw new TypeError('EMOJI_TYPE'); - - return this.client.api - .channels(this.channel.id) - .messages(this.id) - .reactions(emoji, '@me') - .put() - .then( - () => - this.client.actions.MessageReactionAdd.handle({ - user: this.client.user, - channel: this.channel, - message: this, - emoji: Util.parseEmoji(emoji), - }).reaction, - ); + async react(emoji) { + await this.channel.messages.react(this.id, emoji); + return this.client.actions.MessageReactionAdd.handle({ + user: this.client.user, + channel: this.channel, + message: this, + emoji: Util.parseEmoji(emoji), + }).reaction; } /** diff --git a/typings/index.d.ts b/typings/index.d.ts index 473b5e4b5355..c0ad9fc31dba 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -1144,7 +1144,7 @@ declare module 'discord.js' { public fetchWebhook(): Promise; public crosspost(): Promise; public fetch(force?: boolean): Promise; - public pin(options?: { reason?: string }): Promise; + public pin(): Promise; public react(emoji: EmojiIdentifierResolvable): Promise; public removeAttachments(): Promise; public reply( @@ -1164,7 +1164,7 @@ declare module 'discord.js' { public suppressEmbeds(suppress?: boolean): Promise; public toJSON(): object; public toString(): string; - public unpin(options?: { reason?: string }): Promise; + public unpin(): Promise; } export class MessageAttachment { @@ -2191,6 +2191,9 @@ declare module 'discord.js' { constructor(channel: TextChannel | DMChannel, iterable?: Iterable); public channel: TextBasedChannelFields; public cache: Collection; + public crosspost(message: MessageResolvable): Promise; + public delete(message: MessageResolvable): Promise; + public edit(message: MessageResolvable, options: MessageEditOptions): Promise; public fetch(message: Snowflake, cache?: boolean, force?: boolean): Promise; public fetch( options?: ChannelLogsQueryOptions, @@ -2198,7 +2201,9 @@ declare module 'discord.js' { force?: boolean, ): Promise>; public fetchPinned(cache?: boolean): Promise>; - public delete(message: MessageResolvable): Promise; + public react(message: MessageResolvable, emoji: EmojiIdentifierResolvable): Promise; + public pin(message: MessageResolvable): Promise; + public unpin(message: MessageResolvable): Promise; } export class PresenceManager extends BaseManager {