Limit replies to fit within one message

This applies to both the DM to be sent to the user, and the message
created in the thread channel.

This is because edits (via !edit) could change the amount of messages
a reply takes (based on message formatting), leading to either being
unable to post the full edit if it goes over the message limits, or
having to edit a previous message to be 'empty' if the result of the
edit would take fewer messages to post than the original reply.

This also fixes an issue where !edit/!delete would not apply to more
than the first message created by a reply - whether in user DMs or in
the thread channel.
cshd
Dragory 2020-10-21 21:03:33 +03:00
parent 42f6e79df8
commit d5ea95d9e9
No known key found for this signature in database
GPG Key ID: 5F387BA66DF8AAC1
2 changed files with 68 additions and 2 deletions

View File

@ -223,8 +223,16 @@ class Thread {
attachments: attachmentLinks, attachments: attachmentLinks,
}); });
// Send the reply DM
const dmContent = formatters.formatStaffReplyDM(threadMessage); const dmContent = formatters.formatStaffReplyDM(threadMessage);
const inboxContent = formatters.formatStaffReplyThreadMessage(threadMessage);
// Because moderator replies have to be editable, we enforce them to fit within 1 message
if (! utils.messageContentIsWithinMaxLength(dmContent) || ! utils.messageContentIsWithinMaxLength(inboxContent)) {
await this.postSystemMessage("Reply is too long! Make sure your reply is under 2000 characters total, moderator name in the reply included.");
return false;
}
// Send the reply DM
let dmMessage; let dmMessage;
try { try {
dmMessage = await this._sendDMToUser(dmContent, files); dmMessage = await this._sendDMToUser(dmContent, files);
@ -240,7 +248,6 @@ class Thread {
}); });
// Show the reply in the inbox thread // Show the reply in the inbox thread
const inboxContent = formatters.formatStaffReplyThreadMessage(threadMessage);
const inboxMessage = await this._postToThreadChannel(inboxContent, files); const inboxMessage = await this._postToThreadChannel(inboxContent, files);
if (inboxMessage) { if (inboxMessage) {
await this._updateThreadMessage(threadMessage.id, { inbox_message_id: inboxMessage.id }); await this._updateThreadMessage(threadMessage.id, { inbox_message_id: inboxMessage.id });
@ -719,6 +726,13 @@ class Thread {
const formattedThreadMessage = formatters.formatStaffReplyThreadMessage(newThreadMessage); const formattedThreadMessage = formatters.formatStaffReplyThreadMessage(newThreadMessage);
const formattedDM = formatters.formatStaffReplyDM(newThreadMessage); const formattedDM = formatters.formatStaffReplyDM(newThreadMessage);
// Same restriction as in replies. Because edits could theoretically change the number of messages a reply takes, we enforce replies
// to fit within 1 message to avoid the headache and issues caused by that.
if (! utils.messageContentIsWithinMaxLength(formattedDM) || ! utils.messageContentIsWithinMaxLength(formattedThreadMessage)) {
await this.postSystemMessage("Edited reply is too long! Make sure the edit is under 2000 characters total, moderator name in the reply included.");
return false;
}
await bot.editMessage(threadMessage.dm_channel_id, threadMessage.dm_message_id, formattedDM); await bot.editMessage(threadMessage.dm_channel_id, threadMessage.dm_message_id, formattedDM);
await bot.editMessage(this.channel_id, threadMessage.inbox_message_id, formattedThreadMessage); await bot.editMessage(this.channel_id, threadMessage.inbox_message_id, formattedThreadMessage);

View File

@ -336,6 +336,56 @@ function readMultilineConfigValue(str) {
function noop() {} function noop() {}
// https://discord.com/developers/docs/resources/channel#create-message-params
const MAX_MESSAGE_CONTENT_LENGTH = 2000;
// https://discord.com/developers/docs/resources/channel#embed-limits
const MAX_EMBED_CONTENT_LENGTH = 6000;
/**
* Checks if the given message content is within Discord's message length limits.
*
* Based on testing, Discord appears to enforce length limits (at least in the client)
* the same way JavaScript does, using the UTF-16 byte count as the number of characters.
*
* @param {string|Eris.MessageContent} content
*/
function messageContentIsWithinMaxLength(content) {
if (typeof content === "string") {
content = { content };
}
if (content.content && content.content.length > MAX_MESSAGE_CONTENT_LENGTH) {
return false;
}
if (content.embed) {
let embedContentLength = 0;
if (content.embed.title) embedContentLength += content.embed.title.length;
if (content.embed.description) embedContentLength += content.embed.description.length;
if (content.embed.footer && content.embed.footer.text) {
embedContentLength += content.embed.footer.text.length;
}
if (content.embed.author && content.embed.author.name) {
embedContentLength += content.embed.author.name.length;
}
if (content.embed.fields) {
for (const field of content.embed.fields) {
if (field.title) embedContentLength += field.name.length;
if (field.description) embedContentLength += field.value.length;
}
}
if (embedContentLength > MAX_EMBED_CONTENT_LENGTH) {
return false;
}
}
return true;
}
module.exports = { module.exports = {
BotError, BotError,
@ -377,5 +427,7 @@ module.exports = {
readMultilineConfigValue, readMultilineConfigValue,
messageContentIsWithinMaxLength,
noop, noop,
}; };