Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
88 changes: 64 additions & 24 deletions lib/discord.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,10 +257,61 @@ async function sendDiscordMessage(channelId, text, options = {}) {
// Convert Slack markdown to Discord markdown
let discordText = text.replace(/<(https?:\/\/[^|>]+)\|([^>]+)>/g, '[$2]($1)');

// Convert common Slack emoji codes to Unicode emoji for Discord
// Use a single regex-based replacement for better performance with large messages
const emojiMap = {
':notes:': '🎵',
':lock:': '🔒',
':star:': '⭐',
':stopwatch:': '⏱️',
':cricket:': '🦗',
':musical_note:': '🎵',
':headphones:': '🎧',
':speaker:': '🔊',
':mute:': '🔇',
':loud_sound:': '🔊',
':sound:': '🔉',
':fire:': '🔥',
':thumbsup:': '👍',
':thumbsdown:': '👎',
':clap:': '👏',
':party_popper:': '🎉',
':tada:': '🎉',
':warning:': '⚠️',
':x:': '❌',
':white_check_mark:': '✅',
':checkmark:': '✅',
':question:': '❓',
':exclamation:': '❗',
':sparkles:': '✨'
};
discordText = discordText.replace(/:[a-z_]+:/g, (match) => emojiMap[match] || match);

// Discord has a 2000 char limit, split into chunks if needed
const maxLength = 1900; // Leave some margin
// Use 1800 as max to have buffer for edge cases with Unicode
const maxLength = 1800;
let messages = [];

// Helper function to send a chunk safely (splitting further if needed)
const sendChunkSafe = async (chunk) => {
if (chunk.length <= maxLength) {
const message = await channel.send(chunk);
messages.push(message);
return;
}
// Chunk is still too long, split it
let remaining = chunk;
while (remaining.length > 0) {
const piece = remaining.substring(0, maxLength);
const message = await channel.send(piece);
messages.push(message);
remaining = remaining.substring(maxLength);
if (remaining.length > 0) {
await new Promise(resolve => setTimeout(resolve, 300));
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The delay between message chunks was reduced from 500ms (old code) to 300ms. While this improves user experience with faster queue display, ensure this doesn't hit Discord's rate limits. Discord.js typically allows 5 messages per 5 seconds per channel (1 message per second average), so 300ms should be safe, but consider testing with very large queues (100+ tracks) to verify.

Copilot uses AI. Check for mistakes.
}
}
};
Comment on lines +296 to +313
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The message chunking logic introduced in sendChunkSafe lacks test coverage. Given that this fixes a critical bug where large queue lists failed on Discord, consider adding tests for: 1) messages under 1800 chars (single send), 2) messages between 1800-2000 chars, 3) messages over 2000 chars with line breaks, 4) oversized single lines, and 5) edge cases with Unicode emoji conversions. The test/discord.test.mjs file exists but doesn't have chunking tests.

Copilot uses AI. Check for mistakes.

if (discordText.length <= maxLength) {
// Single message
const message = await channel.send(discordText);
Expand All @@ -274,44 +325,33 @@ async function sendDiscordMessage(channelId, text, options = {}) {

for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const potentialLength = currentChunk.length + line.length + 1; // +1 for newline

if ((currentChunk + line + '\n').length > maxLength) {
// Send current chunk
if (potentialLength > maxLength) {
// Send current chunk if it has content
if (currentChunk.trim().length > 0) {
const message = await channel.send(currentChunk);
messages.push(message);
await sendChunkSafe(currentChunk);
chunkCount++;
currentChunk = '';
// Small delay between messages
await new Promise(resolve => setTimeout(resolve, 500));
await new Promise(resolve => setTimeout(resolve, 300));
}

// Handle oversized single lines by splitting them
if (line.length > maxLength) {
// Split the line into smaller chunks
let remainingLine = line;
while (remainingLine.length > 0) {
const chunk = remainingLine.substring(0, maxLength);
const message = await channel.send(chunk);
messages.push(message);
chunkCount++;
remainingLine = remainingLine.substring(maxLength);
if (remainingLine.length > 0) {
await new Promise(resolve => setTimeout(resolve, 500));
}
}
// Skip adding to currentChunk since we already sent it
// Handle oversized or exact-length lines (>= to avoid adding newline that exceeds limit)
if (line.length >= maxLength) {
await sendChunkSafe(line);
chunkCount++;
await new Promise(resolve => setTimeout(resolve, 300));
continue;
}
}

currentChunk += line + '\n';
}

// Send remaining chunk
// Send remaining chunk (with safety check)
if (currentChunk.trim().length > 0) {
const message = await channel.send(currentChunk);
messages.push(message);
await sendChunkSafe(currentChunk);
chunkCount++;
}

Expand Down
46 changes: 23 additions & 23 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading