From bc4dc22c1f20519090b3fef0d96635b04ac8a1f5 Mon Sep 17 00:00:00 2001 From: almostSouji Date: Thu, 1 Oct 2020 13:52:02 +0200 Subject: [PATCH 01/23] feat(Message): remove reply functionality --- README.md | 2 +- docs/examples/avatars.js | 2 +- docs/examples/moderation.md | 16 +++++----- docs/general/welcome.md | 2 +- docs/topics/voice.md | 2 +- src/structures/APIMessage.js | 32 ++----------------- src/structures/Emoji.js | 2 +- src/structures/Message.js | 24 +++----------- src/structures/interfaces/TextBasedChannel.js | 6 ++-- test/disableMentions.js | 6 ++-- test/random.js | 4 +-- test/sendtest.js | 3 -- test/tester1000.js | 2 +- test/voice.js | 4 +-- test/webhooktest.js | 3 -- typings/index.d.ts | 15 --------- 16 files changed, 31 insertions(+), 94 deletions(-) diff --git a/README.md b/README.md index cbc2bb0e..10a10461 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ client.on('ready', () => { client.on('message', msg => { if (msg.content === 'ping') { - msg.reply('pong'); + msg.channel.send('pong'); } }); diff --git a/docs/examples/avatars.js b/docs/examples/avatars.js index acb13d3a..6687033d 100644 --- a/docs/examples/avatars.js +++ b/docs/examples/avatars.js @@ -23,7 +23,7 @@ client.on('message', message => { // If the message is "what is my avatar" if (message.content === 'what is my avatar') { // Send the user's avatar URL - message.reply(message.author.displayAvatarURL()); + message.channel.send(message.author.displayAvatarURL()); } }); diff --git a/docs/examples/moderation.md b/docs/examples/moderation.md index 63c665c4..9d2ea032 100644 --- a/docs/examples/moderation.md +++ b/docs/examples/moderation.md @@ -45,23 +45,23 @@ client.on('message', message => { .kick('Optional reason that will display in the audit logs') .then(() => { // We let the message author know we were able to kick the person - message.reply(`Successfully kicked ${user.tag}`); + message.channel.send(`Successfully kicked ${user.tag}`); }) .catch(err => { // An error happened // This is generally due to the bot not being able to kick the member, // either due to missing permissions or role hierarchy - message.reply('I was unable to kick the member'); + message.channel.send('I was unable to kick the member'); // Log the error console.error(err); }); } else { // The mentioned user isn't in this guild - message.reply("That user isn't in this guild!"); + message.channel.send("That user isn't in this guild!"); } // Otherwise, if no user was mentioned } else { - message.reply("You didn't mention the user to kick!"); + message.channel.send("You didn't mention the user to kick!"); } } }); @@ -121,23 +121,23 @@ client.on('message', message => { }) .then(() => { // We let the message author know we were able to ban the person - message.reply(`Successfully banned ${user.tag}`); + message.channel.send(`Successfully banned ${user.tag}`); }) .catch(err => { // An error happened // This is generally due to the bot not being able to ban the member, // either due to missing permissions or role hierarchy - message.reply('I was unable to ban the member'); + message.channel.send('I was unable to ban the member'); // Log the error console.error(err); }); } else { // The mentioned user isn't in this guild - message.reply("That user isn't in this guild!"); + message.channel.send("That user isn't in this guild!"); } } else { // Otherwise, if no user was mentioned - message.reply("You didn't mention the user to ban!"); + message.channel.send("You didn't mention the user to ban!"); } } }); diff --git a/docs/general/welcome.md b/docs/general/welcome.md index 2d639c6a..58d36129 100644 --- a/docs/general/welcome.md +++ b/docs/general/welcome.md @@ -68,7 +68,7 @@ client.on('ready', () => { client.on('message', msg => { if (msg.content === 'ping') { - msg.reply('pong'); + msg.channel.send('pong'); } }); diff --git a/docs/topics/voice.md b/docs/topics/voice.md index cb20dd5c..8da93676 100644 --- a/docs/topics/voice.md +++ b/docs/topics/voice.md @@ -37,7 +37,7 @@ client.on('message', async message => { if (message.member.voice.channel) { const connection = await message.member.voice.channel.join(); } else { - message.reply('You need to join a voice channel first!'); + message.channel.send('You need to join a voice channel first!'); } } }); diff --git a/src/structures/APIMessage.js b/src/structures/APIMessage.js index 3451bdb9..b692ab93 100644 --- a/src/structures/APIMessage.js +++ b/src/structures/APIMessage.js @@ -79,8 +79,6 @@ class APIMessage { * @returns {?(string|string[])} */ makeContent() { - const GuildMember = require('./GuildMember'); - let content; if (this.options.content === null) { content = ''; @@ -110,25 +108,14 @@ class APIMessage { const isCode = typeof this.options.code !== 'undefined' && this.options.code !== false; const splitOptions = isSplit ? { ...this.options.split } : undefined; - let mentionPart = ''; - if (this.options.reply && !this.isUser && this.target.type !== 'dm') { - const id = this.target.client.users.resolveID(this.options.reply); - mentionPart = `<@${this.options.reply instanceof GuildMember && this.options.reply.nickname ? '!' : ''}${id}>, `; - if (isSplit) { - splitOptions.prepend = `${mentionPart}${splitOptions.prepend || ''}`; - } - } - - if (content || mentionPart) { + if (content) { if (isCode) { const codeName = typeof this.options.code === 'string' ? this.options.code : ''; - content = `${mentionPart}\`\`\`${codeName}\n${Util.cleanCodeBlockContent(content)}\n\`\`\``; + content = `\`\`\`${codeName}\n${Util.cleanCodeBlockContent(content)}\n\`\`\``; if (isSplit) { splitOptions.prepend = `${splitOptions.prepend || ''}\`\`\`${codeName}\n`; splitOptions.append = `\n\`\`\`${splitOptions.append || ''}`; } - } else if (mentionPart) { - content = `${mentionPart}${content}`; } if (isSplit) { @@ -182,21 +169,6 @@ class APIMessage { typeof this.options.allowedMentions === 'undefined' ? this.target.client.options.allowedMentions : this.options.allowedMentions; - if (this.options.reply) { - const id = this.target.client.users.resolveID(this.options.reply); - if (allowedMentions) { - // Clone the object as not to alter the ClientOptions object - allowedMentions = Util.cloneObject(allowedMentions); - const parsed = allowedMentions.parse && allowedMentions.parse.includes('users'); - // Check if the mention won't be parsed, and isn't supplied in `users` - if (!parsed && !(allowedMentions.users && allowedMentions.users.includes(id))) { - if (!allowedMentions.users) allowedMentions.users = []; - allowedMentions.users.push(id); - } - } else { - allowedMentions = { users: [id] }; - } - } this.data = { content, diff --git a/src/structures/Emoji.js b/src/structures/Emoji.js index 0214ea80..86119d30 100644 --- a/src/structures/Emoji.js +++ b/src/structures/Emoji.js @@ -82,7 +82,7 @@ class Emoji extends Base { * @example * // Send a custom emoji from a guild: * const emoji = guild.emojis.cache.first(); - * msg.reply(`Hello! ${emoji}`); + * msg.channel.send(`Hello! ${emoji}`); * @example * // Send the emoji used in a reaction to the channel the reaction is part of * reaction.message.channel.send(`The emoji used was: ${reaction.emoji}`); diff --git a/src/structures/Message.js b/src/structures/Message.js index 439eb4aa..cde7f114 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -460,7 +460,10 @@ class Message extends Base { * } */ async crosspost() { - await this.client.api.channels(this.channel.id).messages(this.id).crosspost.post(); + await this.client.api + .channels(this.channel.id) + .messages(this.id) + .crosspost.post(); return this; } @@ -563,25 +566,6 @@ class Message extends Base { } } - /** - * Replies to the message. - * @param {StringResolvable|APIMessage} [content=''] The content for the message - * @param {MessageOptions|MessageAdditions} [options={}] The options to provide - * @returns {Promise} - * @example - * // Reply to a message - * message.reply('Hey, I\'m a reply!') - * .then(() => console.log(`Sent a reply to ${message.author.username}`)) - * .catch(console.error); - */ - reply(content, options) { - return this.channel.send( - content instanceof APIMessage - ? content - : APIMessage.transformOptions(content, options, { reply: this.member || this.author }), - ); - } - /** * Fetch this message. * @param {boolean} [force=false] Whether to skip the cache check and request the API diff --git a/src/structures/interfaces/TextBasedChannel.js b/src/structures/interfaces/TextBasedChannel.js index f5269e97..e6ffb85e 100644 --- a/src/structures/interfaces/TextBasedChannel.js +++ b/src/structures/interfaces/TextBasedChannel.js @@ -65,7 +65,6 @@ class TextBasedChannel { * @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 - * @property {UserResolvable} [reply] User to reply to (prefixes the message with a mention, except in DMs) */ /** @@ -330,7 +329,10 @@ class TextBasedChannel { } if (messageIDs.length === 0) return new Collection(); if (messageIDs.length === 1) { - await this.client.api.channels(this.id).messages(messageIDs[0]).delete(); + await this.client.api + .channels(this.id) + .messages(messageIDs[0]) + .delete(); const message = this.client.actions.MessageDelete.getMessage( { message_id: messageIDs[0], diff --git a/test/disableMentions.js b/test/disableMentions.js index 97077e20..0521d34c 100644 --- a/test/disableMentions.js +++ b/test/disableMentions.js @@ -36,9 +36,9 @@ client.on('message', message => { // Clean content and log each character console.log(Util.cleanContent(args.join(' '), message).split('')); - if (command === 'test1') message.reply(tests[0]); - else if (command === 'test2') message.reply(tests[1]); - else if (command === 'test3') message.reply(tests[2]); + if (command === 'test1') message.channel.send(tests[0]); + else if (command === 'test2') message.channel.send(tests[1]); + else if (command === 'test3') message.channel.send(tests[2]); }); client.login(token).catch(console.error); diff --git a/test/random.js b/test/random.js index 77112883..11d869ed 100644 --- a/test/random.js +++ b/test/random.js @@ -134,7 +134,7 @@ client.on('message', message => { } message.channel.send('last one...').then(m => { const diff = Date.now() - start; - m.reply(`Each message took ${diff / 21}ms to send`); + m.channel.send(`Each message took ${diff / 21}ms to send`); }); } @@ -205,7 +205,7 @@ client.on('message', msg => { .join() .then(conn => { con = conn; - msg.reply('done'); + msg.channel.send('done'); const s = ytdl(song, { filter: 'audioonly' }, { passes: 3 }); s.on('error', e => console.log(`e w stream 2 ${e}`)); disp = conn.playStream(s); diff --git a/test/sendtest.js b/test/sendtest.js index 186e1c23..691c4e24 100644 --- a/test/sendtest.js +++ b/test/sendtest.js @@ -32,7 +32,6 @@ const tests = [ m => m.channel.send(fill('x'), { split: true }), m => m.channel.send(fill('1'), { code: 'js', split: true }), - m => m.channel.send(fill('x'), { reply: m.author, code: 'js', split: true }), m => m.channel.send(fill('xyz '), { split: { char: ' ' } }), m => m.channel.send('x', { embed: { description: 'a' } }), @@ -99,7 +98,6 @@ const tests = [ async m => m.channel.send({ files: [await read(fileA)] }), async m => m.channel.send(fill('x'), { - reply: m.author, code: 'js', split: true, embed: embed().setImage('attachment://zero.png'), @@ -111,7 +109,6 @@ const tests = [ m => m.channel.send({ files: [{ attachment: readStream(fileA) }] }), async m => m.channel.send(fill('xyz '), { - reply: m.author, code: 'js', split: { char: ' ', prepend: 'hello! ', append: '!!!' }, embed: embed().setImage('attachment://zero.png'), diff --git a/test/tester1000.js b/test/tester1000.js index 471860b3..0421d76f 100644 --- a/test/tester1000.js +++ b/test/tester1000.js @@ -31,7 +31,7 @@ const commands = { } message.channel.send(res, { code: 'js' }); }, - ping: message => message.reply('pong'), + ping: message => message.channel.send('pong'), }; client.on('message', message => { diff --git a/test/voice.js b/test/voice.js index 37d2669a..8ef20cd4 100644 --- a/test/voice.js +++ b/test/voice.js @@ -43,11 +43,11 @@ client.on('message', m => { conn.receiver.createStream(m.author, true).on('data', b => console.log(b.toString())); conn.player.on('error', (...e) => console.log('player', ...e)); if (!connections.has(m.guild.id)) connections.set(m.guild.id, { conn, queue: [] }); - m.reply('ok!'); + m.channel.send('ok!'); conn.play(ytdl('https://www.youtube.com/watch?v=_XXOSf0s2nk', { filter: 'audioonly' }, { passes: 3 })); }); } else { - m.reply('Specify a voice channel!'); + m.channel.send('Specify a voice channel!'); } } else if (m.content.startsWith('#eval') && m.author.id === '66564597481480192') { try { diff --git a/test/webhooktest.js b/test/webhooktest.js index f73c693f..3691dd8d 100644 --- a/test/webhooktest.js +++ b/test/webhooktest.js @@ -32,7 +32,6 @@ const tests = [ (m, hook) => hook.send(fill('x'), { split: true }), (m, hook) => hook.send(fill('1'), { code: 'js', split: true }), - (m, hook) => hook.send(fill('x'), { reply: m.author, code: 'js', split: true }), (m, hook) => hook.send(fill('xyz '), { split: { char: ' ' } }), (m, hook) => hook.send({ embeds: [{ description: 'a' }] }), @@ -96,7 +95,6 @@ const tests = [ async (m, hook) => hook.send({ files: [await read(fileA)] }), async (m, hook) => hook.send(fill('x'), { - reply: m.author, code: 'js', split: true, embeds: [embed().setImage('attachment://zero.png')], @@ -108,7 +106,6 @@ const tests = [ (m, hook) => hook.send({ files: [{ attachment: readStream(fileA) }] }), async (m, hook) => hook.send(fill('xyz '), { - reply: m.author, code: 'js', split: { char: ' ', prepend: 'hello! ', append: '!!!' }, embeds: [embed().setImage('attachment://zero.png')], diff --git a/typings/index.d.ts b/typings/index.d.ts index 381686cc..bbba068c 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -1027,20 +1027,6 @@ declare module 'discord.js' { public fetch(force?: boolean): Promise; public pin(options?: { reason?: string }): Promise; public react(emoji: EmojiIdentifierResolvable): Promise; - public reply( - content: APIMessageContentResolvable | (MessageOptions & { split?: false }) | MessageAdditions, - ): Promise; - public reply(options: MessageOptions & { split: true | SplitOptions }): Promise; - public reply(options: MessageOptions | APIMessage): Promise; - public reply( - content: StringResolvable, - options: (MessageOptions & { split?: false }) | MessageAdditions, - ): Promise; - public reply( - content: StringResolvable, - options: MessageOptions & { split: true | SplitOptions }, - ): Promise; - public reply(content: StringResolvable, options: MessageOptions): Promise; public suppressEmbeds(suppress?: boolean): Promise; public toJSON(): object; public toString(): string; @@ -2829,7 +2815,6 @@ declare module 'discord.js' { files?: (FileOptions | BufferResolvable | Stream | MessageAttachment)[]; code?: string | boolean; split?: boolean | SplitOptions; - reply?: UserResolvable; } type MessageReactionResolvable = MessageReaction | Snowflake; From 0ed281888d609f46e6fba0dd60fe1ce91dcc48c9 Mon Sep 17 00:00:00 2001 From: monbrey Date: Fri, 2 Oct 2020 14:57:40 +1000 Subject: [PATCH 02/23] feat(InlineReplies): add INLINE_REPLY constant/typing --- src/util/Constants.js | 4 ++++ typings/index.d.ts | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/util/Constants.js b/src/util/Constants.js index 251267b9..366770af 100644 --- a/src/util/Constants.js +++ b/src/util/Constants.js @@ -417,6 +417,10 @@ exports.MessageTypes = [ null, 'GUILD_DISCOVERY_DISQUALIFIED', 'GUILD_DISCOVERY_REQUALIFIED', + null, + null, + null, + 'INLINE_REPLY', ]; /** diff --git a/typings/index.d.ts b/typings/index.d.ts index 381686cc..fa273e64 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -2859,7 +2859,8 @@ declare module 'discord.js' { | 'USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_3' | 'CHANNEL_FOLLOW_ADD' | 'GUILD_DISCOVERY_DISQUALIFIED' - | 'GUILD_DISCOVERY_REQUALIFIED'; + | 'GUILD_DISCOVERY_REQUALIFIED' + | 'INLINE_REPLY'; interface OverwriteData { allow?: PermissionResolvable; From ab5ee838a318879f7b61560ce2f3742c22adb239 Mon Sep 17 00:00:00 2001 From: monbrey Date: Fri, 2 Oct 2020 15:37:40 +1000 Subject: [PATCH 03/23] feat(InlineReplies): add Message#replyReference property --- src/structures/Message.js | 11 +++++++++++ typings/index.d.ts | 1 + 2 files changed, 12 insertions(+) diff --git a/src/structures/Message.js b/src/structures/Message.js index 439eb4aa..1d3a9d6b 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -223,6 +223,17 @@ class Message extends Base { messageID: data.message_reference.message_id, } : null; + + /** + * The message this message replies to + * @type {?Message} + */ + if ('referenced_message' in data) { + this.replyReference = + this.channel.messages.get(data.referenced_message.id) || this.channel.messages.add(data.referenced_message); + } else { + this.replyReference = null; + } } /** diff --git a/typings/index.d.ts b/typings/index.d.ts index fa273e64..3c6461fa 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -1011,6 +1011,7 @@ declare module 'discord.js' { public webhookID: Snowflake | null; public flags: Readonly; public reference: MessageReference | null; + public replyReference: Message | null; public awaitReactions( filter: CollectorFilter, options?: AwaitReactionsOptions, From a5ce6cfa9a54645e4b549f9d5d656d40ea053fa1 Mon Sep 17 00:00:00 2001 From: monbrey Date: Fri, 2 Oct 2020 15:41:08 +1000 Subject: [PATCH 04/23] feat(InlineReplies): add typings for sending inline replies --- typings/index.d.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/typings/index.d.ts b/typings/index.d.ts index 3c6461fa..f2bbd2b7 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -2831,6 +2831,7 @@ declare module 'discord.js' { code?: string | boolean; split?: boolean | SplitOptions; reply?: UserResolvable; + messageReference?: MessageResolvable | MessageReplyReference; } type MessageReactionResolvable = MessageReaction | Snowflake; @@ -2841,6 +2842,11 @@ declare module 'discord.js' { messageID: string | null; } + interface MessageReplyReference { + channelID: string; + messageID: string; + } + type MessageResolvable = Message | Snowflake; type MessageTarget = TextChannel | NewsChannel | DMChannel | User | GuildMember | Webhook | WebhookClient; From 9f3108052ca937133fc5565dd86adb830498b8ed Mon Sep 17 00:00:00 2001 From: monbrey Date: Fri, 2 Oct 2020 16:00:14 +1000 Subject: [PATCH 05/23] feat(InlineReplies): provide support for inline-replying to messages --- src/structures/APIMessage.js | 9 +++++++++ src/structures/interfaces/TextBasedChannel.js | 1 + typings/index.d.ts | 7 +------ 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/structures/APIMessage.js b/src/structures/APIMessage.js index 3451bdb9..46a93063 100644 --- a/src/structures/APIMessage.js +++ b/src/structures/APIMessage.js @@ -198,6 +198,14 @@ class APIMessage { } } + let message_reference; + if (this.options.messageReference) { + message_reference = { + message_id: this.target.messages.resolveID(this.options.messageReference), + channel_id: this.target.id, + }; + } + this.data = { content, tts, @@ -208,6 +216,7 @@ class APIMessage { avatar_url: avatarURL, allowed_mentions: typeof content === 'undefined' ? undefined : allowedMentions, flags, + message_reference, }; return this; } diff --git a/src/structures/interfaces/TextBasedChannel.js b/src/structures/interfaces/TextBasedChannel.js index f5269e97..c0a73379 100644 --- a/src/structures/interfaces/TextBasedChannel.js +++ b/src/structures/interfaces/TextBasedChannel.js @@ -66,6 +66,7 @@ class TextBasedChannel { * @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 * @property {UserResolvable} [reply] User to reply to (prefixes the message with a mention, except in DMs) + * @property {MessageResolvable} [messageReference] The message to inline-reply to (must be in the same channel) */ /** diff --git a/typings/index.d.ts b/typings/index.d.ts index f2bbd2b7..777305a4 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -2831,7 +2831,7 @@ declare module 'discord.js' { code?: string | boolean; split?: boolean | SplitOptions; reply?: UserResolvable; - messageReference?: MessageResolvable | MessageReplyReference; + messageReference?: MessageResolvable; } type MessageReactionResolvable = MessageReaction | Snowflake; @@ -2842,11 +2842,6 @@ declare module 'discord.js' { messageID: string | null; } - interface MessageReplyReference { - channelID: string; - messageID: string; - } - type MessageResolvable = Message | Snowflake; type MessageTarget = TextChannel | NewsChannel | DMChannel | User | GuildMember | Webhook | WebhookClient; From b8f50a09d20a68fb0c0e0b3b03eb57e43ffc711b Mon Sep 17 00:00:00 2001 From: monbrey Date: Mon, 5 Oct 2020 17:40:05 +1100 Subject: [PATCH 06/23] feat(Message): add referencedMessage getter --- src/structures/Message.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/structures/Message.js b/src/structures/Message.js index 1d3a9d6b..f1ac639f 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -224,15 +224,8 @@ class Message extends Base { } : null; - /** - * The message this message replies to - * @type {?Message} - */ if ('referenced_message' in data) { - this.replyReference = - this.channel.messages.get(data.referenced_message.id) || this.channel.messages.add(data.referenced_message); - } else { - this.replyReference = null; + this.channel.messages.add(data.referenced_message); } } @@ -429,6 +422,12 @@ class Message extends Base { ); } + get referencedMessage() { + return this.reference.messageID && this.client.channels.cache.has(this.reference.channelID) + ? this.client.channels.resolve(this.reference.channelID).messages.resolve(this.reference.messageID) + : null; + } + /** * Options that can be passed into editMessage. * @typedef {Object} MessageEditOptions From ab0d6fc5c99efb6265ffae65faea572555d4e418 Mon Sep 17 00:00:00 2001 From: monbrey Date: Mon, 5 Oct 2020 18:19:39 +1100 Subject: [PATCH 07/23] fix: check that Message#reference is defined in referencedMessage --- src/structures/Message.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/structures/Message.js b/src/structures/Message.js index f1ac639f..70de928d 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -423,7 +423,7 @@ class Message extends Base { } get referencedMessage() { - return this.reference.messageID && this.client.channels.cache.has(this.reference.channelID) + return this.reference && this.reference.messageID && this.client.channels.cache.has(this.reference.channelID) ? this.client.channels.resolve(this.reference.channelID).messages.resolve(this.reference.messageID) : null; } From 67c2e566478aa7bbce5ba0ccaa0a8bc2060d6b95 Mon Sep 17 00:00:00 2001 From: monbrey Date: Wed, 7 Oct 2020 14:18:17 +1100 Subject: [PATCH 08/23] refactor(InlineReplies): rename property, rework Message resolution --- src/structures/APIMessage.js | 15 ++++++++++----- typings/index.d.ts | 7 ++++++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/structures/APIMessage.js b/src/structures/APIMessage.js index 46a93063..bb884ccd 100644 --- a/src/structures/APIMessage.js +++ b/src/structures/APIMessage.js @@ -199,11 +199,16 @@ class APIMessage { } let message_reference; - if (this.options.messageReference) { - message_reference = { - message_id: this.target.messages.resolveID(this.options.messageReference), - channel_id: this.target.id, - }; + if (this.options.inlineReplyTo) { + const message = this.target.messages.resolve(this.options.inlineReplyTo); + if (message) { + message_reference = { message_id: message.id, channel_id: message.channel.id }; + } else { + message_reference = { + message_id: this.options.inlineReplyTo.messageID, + channel_id: this.options.inlineReplyTo.channelID, + }; + } } this.data = { diff --git a/typings/index.d.ts b/typings/index.d.ts index 777305a4..63e6d66d 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -2831,7 +2831,7 @@ declare module 'discord.js' { code?: string | boolean; split?: boolean | SplitOptions; reply?: UserResolvable; - messageReference?: MessageResolvable; + inlineReplyTo?: MessageResolvable | MessageReplyReference; } type MessageReactionResolvable = MessageReaction | Snowflake; @@ -2842,6 +2842,11 @@ declare module 'discord.js' { messageID: string | null; } + interface MessageReplyReference { + channelID: Snowflake; + messageID: Snowflake; + } + type MessageResolvable = Message | Snowflake; type MessageTarget = TextChannel | NewsChannel | DMChannel | User | GuildMember | Webhook | WebhookClient; From 975b6cbd94289c47c7a510aa8c157c3bd0be5378 Mon Sep 17 00:00:00 2001 From: monbrey Date: Wed, 7 Oct 2020 14:42:20 +1100 Subject: [PATCH 09/23] docs: update jsdoc for inline replies --- src/structures/interfaces/TextBasedChannel.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/structures/interfaces/TextBasedChannel.js b/src/structures/interfaces/TextBasedChannel.js index c0a73379..df8fe67b 100644 --- a/src/structures/interfaces/TextBasedChannel.js +++ b/src/structures/interfaces/TextBasedChannel.js @@ -66,7 +66,8 @@ class TextBasedChannel { * @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 * @property {UserResolvable} [reply] User to reply to (prefixes the message with a mention, except in DMs) - * @property {MessageResolvable} [messageReference] The message to inline-reply to (must be in the same channel) + * @property {MessageResolvable|MessageReplyReference} [inlineReplyTo] The message to reply to + * (must be in the same channel) */ /** @@ -77,6 +78,13 @@ class TextBasedChannel { * @property {Snowflake[]} [roles] Snowflakes of Roles to be parsed as mentions */ + /** + * Options provided to control parsing of mentions by Discord + * @typedef {Object} MessageReplyReference + * @property {Snowflake} [messageID] Snowflakes of Users to be parsed as mentions + * @property {Snowflake} [channelID] Snowflakes of Roles to be parsed as mentions + */ + /** * Types of mentions to enable in MessageMentionOptions. * - `roles` From 4f7c207c99bad27d0581c5a6d7cc9dbf1c980043 Mon Sep 17 00:00:00 2001 From: monbrey Date: Wed, 7 Oct 2020 20:58:50 +1100 Subject: [PATCH 10/23] feat(Message): inline reply method --- src/structures/Message.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/structures/Message.js b/src/structures/Message.js index 70de928d..2d28d944 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -592,6 +592,23 @@ class Message extends Base { ); } + /** + * Send an inline reply to this message. + * @param {StringResolvable|APIMessage} [content=''] The content for the message + * @param {MessageOptions|MessageAdditions} [options={inlineReplyTo:this}] + * The additional options to provide + * @returns {Promise} + */ + inlineReply(content, options) { + return this.channel.send( + content instanceof APIMessage + ? content + : APIMessage.transformOptions(content, options, { + inlineReplyTo: this, + }), + ); + } + /** * Fetch this message. * @param {boolean} [force=false] Whether to skip the cache check and request the API From 2e96b9a606e0788cef8238a4642aa1595ac5491d Mon Sep 17 00:00:00 2001 From: monbrey Date: Wed, 7 Oct 2020 21:39:01 +1100 Subject: [PATCH 11/23] fix(ApiMessage): finish renaming replyTo --- src/structures/APIMessage.js | 14 +++++--------- src/structures/Message.js | 12 +++++++++--- src/util/Constants.js | 1 + typings/index.d.ts | 4 +--- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/structures/APIMessage.js b/src/structures/APIMessage.js index 04bc7210..db257bdb 100644 --- a/src/structures/APIMessage.js +++ b/src/structures/APIMessage.js @@ -171,15 +171,11 @@ class APIMessage { : this.options.allowedMentions; let message_reference; - if (this.options.inlineReplyTo) { - const message = this.target.messages.resolve(this.options.inlineReplyTo); - if (message) { - message_reference = { message_id: message.id, channel_id: message.channel.id }; - } else { - message_reference = { - message_id: this.options.inlineReplyTo.messageID, - channel_id: this.options.inlineReplyTo.channelID, - }; + if (typeof this.options.replyTo !== 'undefined') { + const message_id = this.target.messages.resolveID(this.options.replyTo); + if (message_id) { + const channel_id = typeof this.options.replyTo === 'string' ? this.target.id : this.options.replyTo.channel.id; + message_reference = { message_id, channel_id }; } } diff --git a/src/structures/Message.js b/src/structures/Message.js index c2fe1b1a..07d6e5ff 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -422,10 +422,16 @@ class Message extends Base { ); } + /** + * The Message this crosspost/reply/pin add references, if cached + * @type {Message} + * @readonly + */ get referencedMessage() { - return this.reference && this.reference.messageID && this.client.channels.cache.has(this.reference.channelID) - ? this.client.channels.resolve(this.reference.channelID).messages.resolve(this.reference.messageID) - : null; + if (!this.reference) return null; + const referenceChannel = this.client.channels.resolve(this.reference.channelID); + if (!referenceChannel) return null; + return referenceChannel.messages.resolve(this.reference.messageID); } /** diff --git a/src/util/Constants.js b/src/util/Constants.js index 366770af..1386c9fa 100644 --- a/src/util/Constants.js +++ b/src/util/Constants.js @@ -398,6 +398,7 @@ exports.WSEvents = keyMirror([ * * CHANNEL_FOLLOW_ADD * * GUILD_DISCOVERY_DISQUALIFIED * * GUILD_DISCOVERY_REQUALIFIED + * * INLINE_REPLY * @typedef {string} MessageType */ exports.MessageTypes = [ diff --git a/typings/index.d.ts b/typings/index.d.ts index 001a0871..f8eb52e8 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -1518,8 +1518,6 @@ declare module 'discord.js' { public fetchWebhooks(): Promise>; } - type TextBasedChannelResolvable = TextChannel | NewsChannel | DMChannel | Snowflake; - export class User extends PartialTextBasedChannel(Base) { constructor(client: Client, data: object); public avatar: string | null; @@ -2829,7 +2827,7 @@ declare module 'discord.js' { messageID: string | null; } - type MessageResolvable = Message | { id: Snowflake; channel: TextBasedChannelResolvable }; + type MessageResolvable = Message | Snowflake; type MessageTarget = TextChannel | NewsChannel | DMChannel | User | GuildMember | Webhook | WebhookClient; From 08286459cb109fef61e4ce823496cb4a1b8a65f1 Mon Sep 17 00:00:00 2001 From: monbrey Date: Thu, 8 Oct 2020 08:15:08 +1100 Subject: [PATCH 12/23] fix: jsdocs for Message#referencedMessage Co-authored-by: Tristan Guichaoua <33934311+tguichaoua@users.noreply.github.com> --- src/structures/Message.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/structures/Message.js b/src/structures/Message.js index 07d6e5ff..e747a048 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -424,7 +424,7 @@ class Message extends Base { /** * The Message this crosspost/reply/pin add references, if cached - * @type {Message} + * @type {?Message} * @readonly */ get referencedMessage() { From 2eafeeca55c5425f93b339a4b2e08a8a21366913 Mon Sep 17 00:00:00 2001 From: monbrey Date: Tue, 17 Nov 2020 08:24:16 +1100 Subject: [PATCH 13/23] fix: restore reply typings --- typings/index.d.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/typings/index.d.ts b/typings/index.d.ts index f8eb52e8..269a8596 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -1028,6 +1028,20 @@ declare module 'discord.js' { public fetch(force?: boolean): Promise; public pin(options?: { reason?: string }): Promise; public react(emoji: EmojiIdentifierResolvable): Promise; + public reply( + content: APIMessageContentResolvable | (MessageOptions & { split?: false }) | MessageAdditions, + ): Promise; + public reply(options: MessageOptions & { split: true | SplitOptions }): Promise; + public reply(options: MessageOptions | APIMessage): Promise; + public reply( + content: StringResolvable, + options: (MessageOptions & { split?: false }) | MessageAdditions, + ): Promise; + public reply( + content: StringResolvable, + options: MessageOptions & { split: true | SplitOptions }, + ): Promise; + public reply(content: StringResolvable, options: MessageOptions): Promise; public suppressEmbeds(suppress?: boolean): Promise; public toJSON(): object; public toString(): string; From 274ae9935e80cfc66f6b4ee9f4c900134bcd0435 Mon Sep 17 00:00:00 2001 From: monbrey Date: Tue, 17 Nov 2020 08:29:18 +1100 Subject: [PATCH 14/23] fix: dont pass channel_id to API when replying --- src/structures/APIMessage.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/structures/APIMessage.js b/src/structures/APIMessage.js index db257bdb..ee2fe36e 100644 --- a/src/structures/APIMessage.js +++ b/src/structures/APIMessage.js @@ -174,8 +174,7 @@ class APIMessage { if (typeof this.options.replyTo !== 'undefined') { const message_id = this.target.messages.resolveID(this.options.replyTo); if (message_id) { - const channel_id = typeof this.options.replyTo === 'string' ? this.target.id : this.options.replyTo.channel.id; - message_reference = { message_id, channel_id }; + message_reference = { message_id }; } } From 3463acafcab5cb8c3797627dc4664c49defbce97 Mon Sep 17 00:00:00 2001 From: monbrey Date: Tue, 17 Nov 2020 08:29:50 +1100 Subject: [PATCH 15/23] chore: update jsdocs --- src/structures/Message.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/structures/Message.js b/src/structures/Message.js index e747a048..c1a97384 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -205,11 +205,11 @@ class Message extends Base { this.flags = new MessageFlags(data.flags).freeze(); /** - * Reference data sent in a crossposted message. + * Reference data sent in a crossposted message or inline reply. * @typedef {Object} MessageReference - * @property {string} channelID ID of the channel the message was crossposted from - * @property {?string} guildID ID of the guild the message was crossposted from - * @property {?string} messageID ID of the message that was crossposted + * @property {string} channelID ID of the channel the message was referenced + * @property {?string} guildID ID of the guild the message was referenced + * @property {?string} messageID ID of the message that was referenced */ /** @@ -423,7 +423,7 @@ class Message extends Base { } /** - * The Message this crosspost/reply/pin add references, if cached + * The Message this crosspost/reply/pin-add references, if cached * @type {?Message} * @readonly */ From ff2dbfa52d4477bf70c611d3f59c869b37eddcb7 Mon Sep 17 00:00:00 2001 From: monbrey Date: Tue, 17 Nov 2020 08:30:10 +1100 Subject: [PATCH 16/23] chore: more jsdoc updates --- src/structures/interfaces/TextBasedChannel.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/structures/interfaces/TextBasedChannel.js b/src/structures/interfaces/TextBasedChannel.js index ec9dffe7..828fadbe 100644 --- a/src/structures/interfaces/TextBasedChannel.js +++ b/src/structures/interfaces/TextBasedChannel.js @@ -65,8 +65,7 @@ class TextBasedChannel { * @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 - * @property {MessageResolvable} [replyTo] The message to reply to - * (must be in the same channel) + * @property {MessageResolvable} [replyTo] The message to reply to (must be in the same channel) */ /** From b61a3673922def3b3d8e0f01364ce5fc9bde22c6 Mon Sep 17 00:00:00 2001 From: monbrey Date: Tue, 17 Nov 2020 10:39:37 +1100 Subject: [PATCH 17/23] feat(AllowedMentions): add typings for replied_user --- src/structures/interfaces/TextBasedChannel.js | 1 + typings/index.d.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/structures/interfaces/TextBasedChannel.js b/src/structures/interfaces/TextBasedChannel.js index 828fadbe..b47409e6 100644 --- a/src/structures/interfaces/TextBasedChannel.js +++ b/src/structures/interfaces/TextBasedChannel.js @@ -74,6 +74,7 @@ class TextBasedChannel { * @property {MessageMentionTypes[]} [parse] Types of mentions to be parsed * @property {Snowflake[]} [users] Snowflakes of Users to be parsed as mentions * @property {Snowflake[]} [roles] Snowflakes of Roles to be parsed as mentions + * @property {boolean} [repliedUser] Whether the author of the Message being replied to should be pinged */ /** diff --git a/typings/index.d.ts b/typings/index.d.ts index 269a8596..329bcd0b 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -2816,6 +2816,7 @@ declare module 'discord.js' { parse?: MessageMentionTypes[]; roles?: Snowflake[]; users?: Snowflake[]; + repliedUser: boolean; } type MessageMentionTypes = 'roles' | 'users' | 'everyone'; From 065e89337e7e923c1e78321a6db67152362f4925 Mon Sep 17 00:00:00 2001 From: monbrey Date: Tue, 17 Nov 2020 13:33:59 +1100 Subject: [PATCH 18/23] fix: naming conventions --- src/util/Constants.js | 4 ++-- typings/index.d.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/util/Constants.js b/src/util/Constants.js index 7ef8f41b..fe0bc300 100644 --- a/src/util/Constants.js +++ b/src/util/Constants.js @@ -401,7 +401,7 @@ exports.WSEvents = keyMirror([ * * CHANNEL_FOLLOW_ADD * * GUILD_DISCOVERY_DISQUALIFIED * * GUILD_DISCOVERY_REQUALIFIED - * * INLINE_REPLY + * * REPLY * @typedef {string} MessageType */ exports.MessageTypes = [ @@ -424,7 +424,7 @@ exports.MessageTypes = [ null, null, null, - 'INLINE_REPLY', + 'REPLY', ]; /** diff --git a/typings/index.d.ts b/typings/index.d.ts index 9b791584..b1273007 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -2863,7 +2863,7 @@ declare module 'discord.js' { | 'CHANNEL_FOLLOW_ADD' | 'GUILD_DISCOVERY_DISQUALIFIED' | 'GUILD_DISCOVERY_REQUALIFIED' - | 'INLINE_REPLY'; + | 'REPLY'; interface OverwriteData { allow?: PermissionResolvable; From 939c495ebb29c5678227ed952a96fbaeef2dc2ca Mon Sep 17 00:00:00 2001 From: monbrey Date: Wed, 18 Nov 2020 08:16:20 +1100 Subject: [PATCH 19/23] fix(Message): referenced_message is null, not undefined --- src/structures/Message.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/structures/Message.js b/src/structures/Message.js index 962bd09f..b99d2627 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -224,7 +224,7 @@ class Message extends Base { } : null; - if ('referenced_message' in data) { + if (data.referenced_message) { this.channel.messages.add(data.referenced_message); } } From eadeeed72e3f980f358b0d565112bc179f338fc1 Mon Sep 17 00:00:00 2001 From: monbrey Date: Thu, 19 Nov 2020 18:56:09 +1100 Subject: [PATCH 20/23] fix(MessageMentionOptions): repliedUser should be optional --- typings/index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typings/index.d.ts b/typings/index.d.ts index b1273007..2ae7c27f 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -2817,7 +2817,7 @@ declare module 'discord.js' { parse?: MessageMentionTypes[]; roles?: Snowflake[]; users?: Snowflake[]; - repliedUser: boolean; + repliedUser?: boolean; } type MessageMentionTypes = 'roles' | 'users' | 'everyone'; From e43ad1eea92d602cd0e64796c508972864f859d6 Mon Sep 17 00:00:00 2001 From: monbrey Date: Tue, 24 Nov 2020 08:29:40 +1100 Subject: [PATCH 21/23] chore: get this back to the right state --- src/structures/APIMessage.js | 4 +++- typings/index.d.ts | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/structures/APIMessage.js b/src/structures/APIMessage.js index ee2fe36e..c44ef5ba 100644 --- a/src/structures/APIMessage.js +++ b/src/structures/APIMessage.js @@ -172,7 +172,9 @@ class APIMessage { let message_reference; if (typeof this.options.replyTo !== 'undefined') { - const message_id = this.target.messages.resolveID(this.options.replyTo); + const message_id = this.isMessage + ? this.target.channel.messages.resolveID(this.options.replyTo) + : this.target.messages.resolveID(this.options.replyTo); if (message_id) { message_reference = { message_id }; } diff --git a/typings/index.d.ts b/typings/index.d.ts index 2ae7c27f..0c4590fe 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -1012,7 +1012,7 @@ declare module 'discord.js' { public webhookID: Snowflake | null; public flags: Readonly; public reference: MessageReference | null; - public replyReference: Message | null; + public readonly referencedMessage: Message | null; public awaitReactions( filter: CollectorFilter, options?: AwaitReactionsOptions, From 88625a5b7db9d16e251840a091ba740a9bb82c80 Mon Sep 17 00:00:00 2001 From: monbrey Date: Tue, 24 Nov 2020 09:01:24 +1100 Subject: [PATCH 22/23] fix(ApiMessage): pass allowed_mentions when replying without content --- src/structures/APIMessage.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/structures/APIMessage.js b/src/structures/APIMessage.js index c44ef5ba..ebe82282 100644 --- a/src/structures/APIMessage.js +++ b/src/structures/APIMessage.js @@ -170,6 +170,11 @@ class APIMessage { ? this.target.client.options.allowedMentions : this.options.allowedMentions; + if (allowedMentions) { + allowedMentions.replied_user = allowedMentions.repliedUser; + delete allowedMentions.repliedUser; + } + let message_reference; if (typeof this.options.replyTo !== 'undefined') { const message_id = this.isMessage @@ -188,7 +193,8 @@ class APIMessage { embeds, username, avatar_url: avatarURL, - allowed_mentions: typeof content === 'undefined' ? undefined : allowedMentions, + allowed_mentions: + typeof content === 'undefined' && typeof message_reference === 'undefined' ? undefined : allowedMentions, flags, message_reference, }; From fbb1c93454b4e1b468f89817d30e2a8ad2a1f787 Mon Sep 17 00:00:00 2001 From: monbrey Date: Fri, 27 Nov 2020 12:43:26 +1100 Subject: [PATCH 23/23] fix(ApiMessage): prevent mutation of client options --- src/structures/APIMessage.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/structures/APIMessage.js b/src/structures/APIMessage.js index ebe82282..d475df72 100644 --- a/src/structures/APIMessage.js +++ b/src/structures/APIMessage.js @@ -171,6 +171,7 @@ class APIMessage { : this.options.allowedMentions; if (allowedMentions) { + allowedMentions = Util.cloneObject(allowedMentions); allowedMentions.replied_user = allowedMentions.repliedUser; delete allowedMentions.repliedUser; }