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 a59b1cc22f63..e046f43b2614 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 { MessageTypes, SystemMessageTypes } = require('../util/Constants'); @@ -447,7 +447,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 @@ -468,13 +468,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 }); } /** @@ -488,47 +486,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); } /** @@ -546,24 +531,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 c217855c8f26..3ba3770f902c 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -1048,7 +1048,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 reply( content: APIMessageContentResolvable | (MessageOptions & { split?: false }) | MessageAdditions, @@ -1067,7 +1067,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 { @@ -1378,7 +1378,7 @@ declare module 'discord.js' { public eval(fn: (client: Client) => T): Promise; public fetchClientValue(prop: string): Promise; public kill(): void; - public respawn(options?: { delay?: number, timeout?: number }): Promise; + public respawn(options?: { delay?: number; timeout?: number }): Promise; public send(message: any): Promise; public spawn(timeout?: number): Promise; @@ -1411,7 +1411,7 @@ declare module 'discord.js' { public broadcastEval(fn: (client: Client) => T, shard: number): Promise; public fetchClientValues(prop: string): Promise; public fetchClientValues(prop: string, shard: number): Promise; - public respawnAll(options?: { shardDelay?: number, respawnDelay?: number, timeout?: number }): Promise; + public respawnAll(options?: { shardDelay?: number; respawnDelay?: number; timeout?: number }): Promise; public send(message: any): Promise; public static singleton(client: Client, mode: ShardingManagerMode): ShardClientUtil; @@ -1448,11 +1448,15 @@ declare module 'discord.js' { public fetchClientValues(prop: string): Promise; public fetchClientValues(prop: string, shard: number): Promise; public respawnAll(options?: { - shardDelay?: number, - respawnDelay?: number, - timeout?: number, + shardDelay?: number; + respawnDelay?: number; + timeout?: number; + }): Promise>; + public spawn(options?: { + amount?: number | 'auto'; + delay?: number; + timeout?: number; }): Promise>; - public spawn(options?: { amount?: number | 'auto', delay?: number, timeout?: number }): Promise>; public on(event: 'shardCreate', listener: (shard: Shard) => void): this; @@ -2061,6 +2065,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, @@ -2068,7 +2075,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 {