diff --git a/server/utils/telegramBot/index.js b/server/utils/telegramBot/index.js index adcd590a..503a71e4 100644 --- a/server/utils/telegramBot/index.js +++ b/server/utils/telegramBot/index.js @@ -33,6 +33,12 @@ class TelegramBotService { static #BASE_RETRY_DELAY_MS = 1000; static #MAX_RETRY_DELAY_MS = 5 * 60 * 1000; // 5 minutes static #NETWORK_ERROR_PATTERNS = [ + "EPIPE", + "EPROTO", + "ECONNABORTED", + "EHOSTDOWN", + "ENETDOWN", + "EADDRNOTAVAIL", "ETIMEDOUT", "ECONNRESET", "ECONNREFUSED", @@ -44,7 +50,20 @@ class TelegramBotService { "socket hang up", "network", "timeout", - "409 Conflict", + "bad gateway", + "flood", + "429", + "409", + "500", + "501", + "502", + "503", + "504", + "520", + "521", + "522", + "523", + "524", ]; /** @type {TelegramBot|null} */ diff --git a/server/utils/telegramBot/utils/format.js b/server/utils/telegramBot/utils/format.js index 1a907882..1ab01f36 100644 --- a/server/utils/telegramBot/utils/format.js +++ b/server/utils/telegramBot/utils/format.js @@ -5,9 +5,13 @@ * @param {string} text - The markdown text to convert * @param {object} [opts] * @param {boolean} [opts.escapeHtml=true] - Whether to escape HTML in non-code text + * @param {boolean} [opts.closeUnclosedTags=true] - Whether to close unclosed HTML tags * @returns {string} - HTML formatted text for Telegram */ -function markdownToTelegram(text, { escapeHtml = true } = {}) { +function markdownToTelegram( + text, + { escapeHtml = true, closeUnclosedTags = true } = {} +) { if (!text) return ""; let result = text; @@ -113,6 +117,9 @@ function markdownToTelegram(text, { escapeHtml = true } = {}) { result = result.replace(`\x00INLINECODE${i}\x00`, code); }); + // Close any unclosed HTML tags to prevent Telegram API errors during streaming + // since if you try to update a message with an unclosed tag, the API will return an 400 error + if (closeUnclosedTags) result = closeUnclosedHtmlTags(result); return result; } @@ -128,6 +135,44 @@ function escapeHTML(text) { .replace(/>/g, ">"); } +/** + * Close any unclosed HTML tags to prevent Telegram API errors. + * This is important during streaming when messages may be split mid-markdown. + * @param {string} html - The HTML text to fix + * @returns {string} - HTML with all tags properly closed + */ +function closeUnclosedHtmlTags(html) { + const tags = ["b", "i", "u", "s", "code", "pre", "a", "blockquote"]; + const openTags = []; + const tagRegex = /<\/?([a-z]+)(?:\s[^>]*)?\s*\/?>/gi; + let match; + + while ((match = tagRegex.exec(html)) !== null) { + const fullMatch = match[0]; + const tagName = match[1].toLowerCase(); + + if (!tags.includes(tagName)) continue; + if (fullMatch.endsWith("/>")) continue; + + if (fullMatch.startsWith("= 0; i--) { + result += ``; + } + + return result; +} + /** * Convert a markdown table to aligned preformatted text. * @param {string} tableMarkdown