Fix faulty chunking for received user messages
The chunks now properly handle code blocks and prefer to split at a newline rather than arbitrarily within the text.cshd
parent
0a98b5cb49
commit
012bd5b9b2
|
@ -89,13 +89,12 @@ class Thread {
|
||||||
if (typeof content === "string") {
|
if (typeof content === "string") {
|
||||||
// Content is a string, chunk it and send it as individual messages.
|
// Content is a string, chunk it and send it as individual messages.
|
||||||
// Files (attachments) are only sent with the last message.
|
// Files (attachments) are only sent with the last message.
|
||||||
const chunks = utils.chunk(content, 2000);
|
const chunks = utils.chunkMessageLines(content);
|
||||||
for (const [i, chunk] of chunks.entries()) {
|
for (const [i, chunk] of chunks.entries()) {
|
||||||
let msg;
|
// Only send embeds, files, etc. with the last message
|
||||||
if (i === chunks.length - 1) {
|
const msg = (i === chunks.length - 1)
|
||||||
// Only send embeds, files, etc. with the last message
|
? await bot.createMessage(this.channel_id, chunk, file)
|
||||||
msg = await bot.createMessage(this.channel_id, chunk, file);
|
: await bot.createMessage(this.channel_id, chunk);
|
||||||
}
|
|
||||||
|
|
||||||
firstMessage = firstMessage || msg;
|
firstMessage = firstMessage || msg;
|
||||||
}
|
}
|
||||||
|
|
72
src/utils.js
72
src/utils.js
|
@ -386,6 +386,77 @@ function messageContentIsWithinMaxLength(content) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits a string into chunks, preferring to split at a newline
|
||||||
|
* @param {string} str
|
||||||
|
* @param {number} [maxChunkLength=2000]
|
||||||
|
* @returns {string[]}
|
||||||
|
*/
|
||||||
|
function chunkByLines(str, maxChunkLength = 2000) {
|
||||||
|
if (str.length < maxChunkLength) {
|
||||||
|
return [str];
|
||||||
|
}
|
||||||
|
|
||||||
|
const chunks = [];
|
||||||
|
|
||||||
|
while (str.length) {
|
||||||
|
if (str.length <= maxChunkLength) {
|
||||||
|
chunks.push(str);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const slice = str.slice(0, maxChunkLength);
|
||||||
|
|
||||||
|
const lastLineBreakIndex = slice.lastIndexOf("\n");
|
||||||
|
if (lastLineBreakIndex === -1) {
|
||||||
|
chunks.push(str.slice(0, maxChunkLength));
|
||||||
|
str = str.slice(maxChunkLength);
|
||||||
|
} else {
|
||||||
|
chunks.push(str.slice(0, lastLineBreakIndex));
|
||||||
|
str = str.slice(lastLineBreakIndex + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chunks a long message to multiple smaller messages, retaining leading and trailing line breaks, open code blocks, etc.
|
||||||
|
*
|
||||||
|
* Default maxChunkLength is 1990, a bit under the message length limit of 2000, so we have space to add code block
|
||||||
|
* shenanigans to the start/end when needed. Take this into account when choosing a custom maxChunkLength as well.
|
||||||
|
*/
|
||||||
|
function chunkMessageLines(str, maxChunkLength = 1990) {
|
||||||
|
const chunks = chunkByLines(str, maxChunkLength);
|
||||||
|
let openCodeBlock = false;
|
||||||
|
|
||||||
|
return chunks.map(_chunk => {
|
||||||
|
// If the chunk starts with a newline, add an invisible unicode char so Discord doesn't strip it away
|
||||||
|
if (_chunk[0] === "\n") _chunk = "\u200b" + _chunk;
|
||||||
|
// If the chunk ends with a newline, add an invisible unicode char so Discord doesn't strip it away
|
||||||
|
if (_chunk[_chunk.length - 1] === "\n") _chunk = _chunk + "\u200b";
|
||||||
|
// If the previous chunk had an open code block, open it here again
|
||||||
|
if (openCodeBlock) {
|
||||||
|
openCodeBlock = false;
|
||||||
|
if (_chunk.startsWith("```")) {
|
||||||
|
// Edge case: chunk starts with a code block delimiter, e.g. the previous chunk and this one were split right before the end of a code block
|
||||||
|
// Fix: just strip the code block delimiter away from here, we don't need it anymore
|
||||||
|
_chunk = _chunk.slice(3);
|
||||||
|
} else {
|
||||||
|
_chunk = "```" + _chunk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If the chunk has an open code block, close it and open it again in the next chunk
|
||||||
|
const codeBlockDelimiters = _chunk.match(/```/g);
|
||||||
|
if (codeBlockDelimiters && codeBlockDelimiters.length % 2 !== 0) {
|
||||||
|
_chunk += "```";
|
||||||
|
openCodeBlock = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _chunk;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
BotError,
|
BotError,
|
||||||
|
|
||||||
|
@ -428,6 +499,7 @@ module.exports = {
|
||||||
readMultilineConfigValue,
|
readMultilineConfigValue,
|
||||||
|
|
||||||
messageContentIsWithinMaxLength,
|
messageContentIsWithinMaxLength,
|
||||||
|
chunkMessageLines,
|
||||||
|
|
||||||
noop,
|
noop,
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue