From d8fcffe1708deb6254424b159e9c45627c469d23 Mon Sep 17 00:00:00 2001 From: htilly Date: Sun, 15 Feb 2026 18:18:02 +0100 Subject: [PATCH 1/3] fix: Discord message chunking for long queue lists - Fix chunking logic to properly handle messages over 2000 chars - Add sendChunkSafe helper to ensure all chunks stay under limit - Convert Slack emoji codes to Unicode for Discord compatibility - Reduce maxLength to 1800 for extra Unicode buffer Fixes issue where 'list' command failed on Discord with 60+ tracks Co-authored-by: Cursor --- lib/discord.js | 87 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 64 insertions(+), 23 deletions(-) diff --git a/lib/discord.js b/lib/discord.js index c8625fe..bd98cac 100644 --- a/lib/discord.js +++ b/lib/discord.js @@ -257,10 +257,62 @@ 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 + 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:': '✨' + }; + for (const [slackEmoji, unicodeEmoji] of Object.entries(emojiMap)) { + discordText = discordText.split(slackEmoji).join(unicodeEmoji); + } + // 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)); + } + } + }; + if (discordText.length <= maxLength) { // Single message const message = await channel.send(discordText); @@ -274,33 +326,23 @@ 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 + // Handle oversized single lines 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 + await sendChunkSafe(line); + chunkCount++; + await new Promise(resolve => setTimeout(resolve, 300)); continue; } } @@ -308,10 +350,9 @@ async function sendDiscordMessage(channelId, text, options = {}) { 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++; } From ec3c59a1cd0503c2cec74f49bb1d5fec012ac4b4 Mon Sep 17 00:00:00 2001 From: htilly Date: Sun, 15 Feb 2026 18:28:08 +0100 Subject: [PATCH 2/3] chore: Update dependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - @slack/web-api: 7.13.0 → 7.14.1 - axios: 1.13.2 → 1.13.5 - posthog-node: 5.24.9 → 5.24.15 - openai: 6.16.0 → 6.22.0 Closes #252, #253, #255, #256 Co-authored-by: Cursor --- package-lock.json | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/package-lock.json b/package-lock.json index 29ed8d3..95d47d1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -460,9 +460,9 @@ } }, "node_modules/@posthog/core": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.19.0.tgz", - "integrity": "sha512-OMcdu5cJcvkle2hw0rpe+1mTOFRlerTHTtZKZFvB8z0hgzbN1WeaGZfGFY5wOq42LVTSxwdUgK1MYERyzG1Epw==", + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.22.0.tgz", + "integrity": "sha512-WkmOnq95aAOu6yk6r5LWr5cfXsQdpVbWDCwOxQwxSne8YV6GuZET1ziO5toSQXgrgbdcjrSz2/GopAfiL6iiAA==", "license": "MIT", "dependencies": { "cross-spawn": "^7.0.6" @@ -622,9 +622,9 @@ } }, "node_modules/@slack/types": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/@slack/types/-/types-2.19.0.tgz", - "integrity": "sha512-7+QZ38HGcNh/b/7MpvPG6jnw7mliV6UmrquJLqgdxkzJgQEYUcEztvFWRU49z0x4vthF0ixL5lTK601AXrS8IA==", + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/@slack/types/-/types-2.20.0.tgz", + "integrity": "sha512-PVF6P6nxzDMrzPC8fSCsnwaI+kF8YfEpxf3MqXmdyjyWTYsZQURpkK7WWUWvP5QpH55pB7zyYL9Qem/xSgc5VA==", "license": "MIT", "engines": { "node": ">= 12.13.0", @@ -632,16 +632,16 @@ } }, "node_modules/@slack/web-api": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@slack/web-api/-/web-api-7.13.0.tgz", - "integrity": "sha512-ERcExbWrnkDN8ovoWWe6Wgt/usanj1dWUd18dJLpctUI4mlPS0nKt81Joh8VI+OPbNnY1lIilVt9gdMBD9U2ig==", + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@slack/web-api/-/web-api-7.14.1.tgz", + "integrity": "sha512-RoygyteJeFswxDPJjUMESn9dldWVMD2xUcHHd9DenVavSfVC6FeVnSdDerOO7m8LLvw4Q132nQM4hX8JiF7dng==", "license": "MIT", "dependencies": { "@slack/logger": "^4.0.0", - "@slack/types": "^2.18.0", + "@slack/types": "^2.20.0", "@types/node": ">=18.0.0", "@types/retry": "0.12.0", - "axios": "^1.11.0", + "axios": "^1.13.5", "eventemitter3": "^5.0.1", "form-data": "^4.0.4", "is-electron": "2.2.2", @@ -789,13 +789,13 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", - "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz", + "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==", "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", "proxy-from-env": "^1.1.0" } }, @@ -2239,9 +2239,9 @@ } }, "node_modules/openai": { - "version": "6.16.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-6.16.0.tgz", - "integrity": "sha512-fZ1uBqjFUjXzbGc35fFtYKEOxd20kd9fDpFeqWtsOZWiubY8CZ1NAlXHW3iathaFvqmNtCWMIsosCuyeI7Joxg==", + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-6.22.0.tgz", + "integrity": "sha512-7Yvy17F33Bi9RutWbsaYt5hJEEJ/krRPOrwan+f9aCPuMat1WVsb2VNSII5W1EksKT6fF69TG/xj4XzodK3JZw==", "license": "Apache-2.0", "bin": { "openai": "bin/cli" @@ -2455,12 +2455,12 @@ } }, "node_modules/posthog-node": { - "version": "5.24.9", - "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-5.24.9.tgz", - "integrity": "sha512-afu4kYL+QTEPinnvTF/VimdsGbrpJztqbxIWhQ96C+m24yW/KenEodWH9em989t+MLwGWcnBGhw1vytgeZdySg==", + "version": "5.24.15", + "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-5.24.15.tgz", + "integrity": "sha512-0QnWVOZAPwEAlp+r3r0jIGfk2IaNYM/2YnEJJhBMJZXs4LpHcTu7mX42l+e95o9xX87YpVuZU0kOkmtQUxgnOA==", "license": "MIT", "dependencies": { - "@posthog/core": "1.19.0" + "@posthog/core": "1.22.0" }, "engines": { "node": "^20.20.0 || >=22.22.0" From 989d84070ff69d82646e4dd084afe6400189126d Mon Sep 17 00:00:00 2001 From: htilly Date: Sun, 15 Feb 2026 18:32:49 +0100 Subject: [PATCH 3/3] refactor: Improve Discord message chunking based on code review - Use single regex for emoji conversion (better performance) - Fix exact-length line bug that could cause blank messages - Change line length check from > to >= maxLength Co-authored-by: Cursor --- lib/discord.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/discord.js b/lib/discord.js index bd98cac..740e019 100644 --- a/lib/discord.js +++ b/lib/discord.js @@ -258,6 +258,7 @@ async function sendDiscordMessage(channelId, text, options = {}) { 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:': '🔒', @@ -284,9 +285,7 @@ async function sendDiscordMessage(channelId, text, options = {}) { ':exclamation:': '❗', ':sparkles:': '✨' }; - for (const [slackEmoji, unicodeEmoji] of Object.entries(emojiMap)) { - discordText = discordText.split(slackEmoji).join(unicodeEmoji); - } + discordText = discordText.replace(/:[a-z_]+:/g, (match) => emojiMap[match] || match); // Discord has a 2000 char limit, split into chunks if needed // Use 1800 as max to have buffer for edge cases with Unicode @@ -338,8 +337,8 @@ async function sendDiscordMessage(channelId, text, options = {}) { await new Promise(resolve => setTimeout(resolve, 300)); } - // Handle oversized single lines - if (line.length > maxLength) { + // 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));