Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Message|TextChannel): Inline replies #4874

Merged
merged 25 commits into from
Dec 8, 2020
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
bc4dc22
feat(Message): remove reply functionality
almostSouji Oct 1, 2020
0ed2818
feat(InlineReplies): add INLINE_REPLY constant/typing
monbrey Oct 2, 2020
ab5ee83
feat(InlineReplies): add Message#replyReference property
monbrey Oct 2, 2020
a5ce6cf
feat(InlineReplies): add typings for sending inline replies
monbrey Oct 2, 2020
9f31080
feat(InlineReplies): provide support for inline-replying to messages
monbrey Oct 2, 2020
b8f50a0
feat(Message): add referencedMessage getter
monbrey Oct 5, 2020
ab0d6fc
fix: check that Message#reference is defined in referencedMessage
monbrey Oct 5, 2020
67c2e56
refactor(InlineReplies): rename property, rework Message resolution
monbrey Oct 7, 2020
975b6cb
docs: update jsdoc for inline replies
monbrey Oct 7, 2020
4f7c207
feat(Message): inline reply method
monbrey Oct 7, 2020
ef1856b
Merge remote-tracking branch 'souji/maj/remove-msg-reply' into inline…
monbrey Oct 7, 2020
2e96b9a
fix(ApiMessage): finish renaming replyTo
monbrey Oct 7, 2020
0828645
fix: jsdocs for Message#referencedMessage
monbrey Oct 7, 2020
2eafeec
fix: restore reply typings
monbrey Nov 16, 2020
274ae99
fix: dont pass channel_id to API when replying
monbrey Nov 16, 2020
3463aca
chore: update jsdocs
monbrey Nov 16, 2020
ff2dbfa
chore: more jsdoc updates
monbrey Nov 16, 2020
b61a367
feat(AllowedMentions): add typings for replied_user
monbrey Nov 16, 2020
1028183
Merge remote-tracking branch 'upstream/master' into inline-replies
monbrey Nov 16, 2020
065e893
fix: naming conventions
monbrey Nov 17, 2020
939c495
fix(Message): referenced_message is null, not undefined
monbrey Nov 17, 2020
eadeeed
fix(MessageMentionOptions): repliedUser should be optional
monbrey Nov 19, 2020
e43ad1e
chore: get this back to the right state
monbrey Nov 23, 2020
88625a5
fix(ApiMessage): pass allowed_mentions when replying without content
monbrey Nov 23, 2020
fbb1c93
fix(ApiMessage): prevent mutation of client options
monbrey Nov 27, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ client.on('ready', () => {

client.on('message', msg => {
if (msg.content === 'ping') {
msg.reply('pong');
msg.channel.send('pong');
monbrey marked this conversation as resolved.
Show resolved Hide resolved
}
});

Expand Down
2 changes: 1 addition & 1 deletion docs/examples/avatars.js
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
});

Expand Down
16 changes: 8 additions & 8 deletions docs/examples/moderation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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!");
}
}
});
Expand Down Expand Up @@ -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!");
}
}
});
Expand Down
2 changes: 1 addition & 1 deletion docs/general/welcome.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ client.on('ready', () => {

client.on('message', msg => {
if (msg.content === 'ping') {
msg.reply('pong');
msg.channel.send('pong');
}
});

Expand Down
2 changes: 1 addition & 1 deletion docs/topics/voice.md
Original file line number Diff line number Diff line change
Expand Up @@ -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!');
}
}
});
Expand Down
37 changes: 9 additions & 28 deletions src/structures/APIMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,6 @@ class APIMessage {
* @returns {?(string|string[])}
*/
makeContent() {
const GuildMember = require('./GuildMember');

let content;
if (this.options.content === null) {
content = '';
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -182,19 +169,12 @@ 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] };

let message_reference;
if (typeof this.options.replyTo !== 'undefined') {
const message_id = this.target.messages.resolveID(this.options.replyTo);
if (message_id) {
message_reference = { message_id };
monbrey marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand All @@ -208,6 +188,7 @@ class APIMessage {
avatar_url: avatarURL,
allowed_mentions: typeof content === 'undefined' ? undefined : allowedMentions,
flags,
message_reference,
};
return this;
}
Expand Down
2 changes: 1 addition & 1 deletion src/structures/Emoji.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}`);
Expand Down
38 changes: 26 additions & 12 deletions src/structures/Message.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/

/**
Expand All @@ -223,6 +223,10 @@ class Message extends Base {
messageID: data.message_reference.message_id,
}
: null;

if ('referenced_message' in data) {
this.channel.messages.add(data.referenced_message);
}
}

/**
Expand Down Expand Up @@ -425,6 +429,18 @@ class Message extends Base {
);
}

/**
* The Message this crosspost/reply/pin-add references, if cached
* @type {?Message}
* @readonly
*/
get referencedMessage() {
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);
monbrey marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Whether the message is crosspostable by the client user
* @type {boolean}
Expand Down Expand Up @@ -588,21 +604,19 @@ class Message extends Base {
}

/**
* Replies to the message.
* Send an inline reply to this message.
* @param {StringResolvable|APIMessage} [content=''] The content for the message
* @param {MessageOptions|MessageAdditions} [options={}] The options to provide
* @param {MessageOptions|MessageAdditions} [options] The additional options to provide
* @param {MessageResolvable} [options.replyTo=this] The message to reply to
* @returns {Promise<Message|Message[]>}
* @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 }),
: APIMessage.transformOptions(content, options, {
replyTo: this,
}),
);
}

Expand Down
3 changes: 2 additions & 1 deletion src/structures/interfaces/TextBasedChannel.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +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 {UserResolvable} [reply] User to reply to (prefixes the message with a mention, except in DMs)
* @property {MessageResolvable} [replyTo] The message to reply to (must be in the same channel)
*/

/**
Expand All @@ -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
*/

/**
Expand Down
5 changes: 5 additions & 0 deletions src/util/Constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,7 @@ exports.WSEvents = keyMirror([
* * CHANNEL_FOLLOW_ADD
* * GUILD_DISCOVERY_DISQUALIFIED
* * GUILD_DISCOVERY_REQUALIFIED
* * REPLY
* @typedef {string} MessageType
*/
exports.MessageTypes = [
Expand All @@ -420,6 +421,10 @@ exports.MessageTypes = [
null,
'GUILD_DISCOVERY_DISQUALIFIED',
'GUILD_DISCOVERY_REQUALIFIED',
null,
null,
null,
'REPLY',
];

/**
Expand Down
6 changes: 3 additions & 3 deletions test/disableMentions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
4 changes: 2 additions & 2 deletions test/random.js
Original file line number Diff line number Diff line change
Expand Up @@ -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`);
});
}

Expand Down Expand Up @@ -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);
Expand Down
3 changes: 0 additions & 3 deletions test/sendtest.js
Original file line number Diff line number Diff line change
Expand Up @@ -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' } }),
Expand Down Expand Up @@ -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'),
Expand All @@ -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'),
Expand Down
7 changes: 2 additions & 5 deletions test/tester1000.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,13 @@ const commands = {
}
message.channel.send(res, { code: 'js' });
},
ping: message => message.reply('pong'),
ping: message => message.channel.send('pong'),
};

client.on('message', message => {
if (!message.content.startsWith(prefix) || message.author.bot) return;

message.content = message.content
.replace(prefix, '')
.trim()
.split(' ');
message.content = message.content.replace(prefix, '').trim().split(' ');
const command = message.content.shift();
message.content = message.content.join(' ');

Expand Down
11 changes: 3 additions & 8 deletions test/voice.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,15 @@ 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 {
const com = eval(
m.content
.split(' ')
.slice(1)
.join(' '),
);
const com = eval(m.content.split(' ').slice(1).join(' '));
m.channel.send(com, { code: true });
} catch (e) {
console.log(e);
Expand Down
Loading