Update TG Transient error code and unclosed tag handler

This commit is contained in:
Timothy Carambat
2026-04-10 08:49:12 -07:00
parent 67d871bbe4
commit 0325598197
2 changed files with 66 additions and 2 deletions

View File

@@ -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} */

View File

@@ -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("</")) {
const lastOpenIdx = openTags.lastIndexOf(tagName);
if (lastOpenIdx !== -1) {
openTags.splice(lastOpenIdx, 1);
}
} else {
openTags.push(tagName);
}
}
// Close any remaining open tags in reverse order
let result = html;
for (let i = openTags.length - 1; i >= 0; i--) {
result += `</${openTags[i]}>`;
}
return result;
}
/**
* Convert a markdown table to aligned preformatted text.
* @param {string} tableMarkdown