soumyadeepdas/TbhRichBotPublic · Community Store Listing
Rich Message Demo [TBH]
17 commands0 envUpdated 5d agoCreated Jul 16, 2026
commands/_handle_guest_message.js
javascript · 329 lines · click line # to share
1/**#command2name: /handle_guest_message3answer: 4keyboard: 5parse_mode: 6aliases: 7allow_only_group: false8need_reply: false9is_web: 010#command**/11 12// Command: *13 14// Handle only guest messages15if (!update.guest_message) {16 return17}18 19let msg = update.guest_message20let userText = msg.text || ""21 22// Skip non-text messages23if (!msg.text) {24 return25}26 27// =======================28// REMOVE BOT USERNAME FROM TEXT29// =======================30// Try multiple ways to get bot username31let botUsername = ""32 33// Method 1: Try from global bot object34if (typeof bot !== 'undefined' && bot.username) {35 botUsername = bot.username36}37// Method 2: Try from update (if available)38else if (update.bot && update.bot.username) {39 botUsername = update.bot.username40}41// Method 3: Hardcode your bot's username (REPLACE WITH YOUR ACTUAL BOT USERNAME)42else {43 botUsername = "pyralbot" // Change this to your bot's actual username without @44}45 46let botMention = "@" + botUsername47 48// Remove bot mention from text49if (userText.startsWith(botMention)) {50 userText = userText.substring(botMention.length).trim()51} else {52 // Also handle cases where there's a space after mention53 let regex = new RegExp('^@' + botUsername + '\\s*', 'i')54 userText = userText.replace(regex, '').trim()55}56 57// If text is empty after removing mention, return58if (!userText) {59 await Api.call("answerGuestQuery", {60 guest_query_id: msg.guest_query_id,61 result: JSON.stringify({62 type: "article",63 id: String(Date.now()),64 title: "Rich Echo",65 input_message_content: {66 message_text: "Please type something after @bot",67 parse_mode: "HTML"68 }69 })70 })71 return72}73 74// =======================75// MARKDOWN TO HTML CONVERTER (IMPROVED)76// =======================77function markdownToHtml(text) {78 let html = text79 80 // ===== HEADINGS =====81 html = html.replace(/^###### (.*$)/gm, '<h6>$1</h6>')82 html = html.replace(/^##### (.*$)/gm, '<h5>$1</h5>')83 html = html.replace(/^#### (.*$)/gm, '<h4>$1</h4>')84 html = html.replace(/^### (.*$)/gm, '<h3>$1</h3>')85 html = html.replace(/^## (.*$)/gm, '<h2>$1</h2>')86 html = html.replace(/^# (.*$)/gm, '<h1>$1</h1>')87 88 // ===== TEXT FORMATTING =====89 // Bold90 html = html.replace(/\*\*(.*?)\*\*/g, '<b>$1</b>')91 html = html.replace(/__(.*?)__/g, '<b>$1</b>')92 93 // Italic94 html = html.replace(/\*(.*?)\*/g, '<i>$1</i>')95 html = html.replace(/_(.*?)_/g, '<i>$1</i>')96 97 // Strikethrough98 html = html.replace(/~~(.*?)~~/g, '<s>$1</s>')99 100 // Highlight101 html = html.replace(/==(.*?)==/g, '<mark>$1</mark>')102 103 // Spoiler104 html = html.replace(/\|\|(.*?)\|\|/g, '<tg-spoiler>$1</tg-spoiler>')105 106 // Inline Code107 html = html.replace(/`(.*?)`/g, '<code>$1</code>')108 109 // ===== CODE BLOCKS =====110 html = html.replace(/```(\w+)?\n([\s\S]*?)```/g, function(match, lang, code) {111 if (lang) {112 return `<pre><code class="language-${lang}">${code.trim()}</code></pre>`113 }114 return `<pre><code>${code.trim()}</code></pre>`115 })116 117 // ===== LINKS & IMAGES =====118 // Images119 html = html.replace(/!\[(.*?)\]\((.*?)\)/g, '<img src="$2" alt="$1"/>')120 121 // Links122 html = html.replace(/\[(.*?)\]\((.*?)\)/g, '<a href="$2">$1</a>')123 124 // ===== BLOCKQUOTES =====125 html = html.replace(/^> (.*$)/gm, '<blockquote>$1</blockquote>')126 html = html.replace(/<\/blockquote>\n<blockquote>/g, '<br>')127 128 // Fix: Properly wrap blockquotes129 let blockquoteRegex = /(<blockquote>.*?<\/blockquote>)/gs130 let matches = html.match(blockquoteRegex)131 if (matches) {132 for (let match of matches) {133 let content = match.replace(/<blockquote>/g, '').replace(/<\/blockquote>/g, '')134 html = html.replace(match, `<blockquote>${content}</blockquote>`)135 }136 }137 138 // ===== LISTS =====139 // Unordered lists140 html = html.replace(/^- (.*$)/gm, '<li>$1</li>')141 html = html.replace(/^\* (.*$)/gm, '<li>$1</li>')142 html = html.replace(/^\+ (.*$)/gm, '<li>$1</li>')143 144 // Wrap consecutive list items145 html = html.replace(/(<li>.*<\/li>\n?)+/g, function(match) {146 return `<ul>${match.trim()}</ul>`147 })148 149 // Ordered lists150 html = html.replace(/^\d+\. (.*$)/gm, '<li>$1</li>')151 html = html.replace(/(<li>.*<\/li>\n?)+/g, function(match) {152 return `<ol>${match.trim()}</ol>`153 })154 155 // Task lists156 html = html.replace(/- \[x\] (.*$)/g, '<li><input type="checkbox" checked disabled> $1</li>')157 html = html.replace(/- \[ \] (.*$)/g, '<li><input type="checkbox" disabled> $1</li>')158 159 // ===== DIVIDERS =====160 html = html.replace(/^---$/gm, '<hr/>')161 html = html.replace(/^\*\*\*$/gm, '<hr/>')162 163 // ===== MATH =====164 // Inline math165 html = html.replace(/\$(.*?)\$/g, '<tg-math>$1</tg-math>')166 167 // Block math168 html = html.replace(/\$\$([\s\S]*?)\$\$/g, '<tg-math-block>$1</tg-math-block>')169 170 // ===== TABLES =====171 let tableRows = html.match(/\|.*\|/g)172 if (tableRows && tableRows.length > 1) {173 let tableHtml = '<table>'174 let isHeader = true175 176 for (let row of tableRows) {177 let cells = row.split('|').filter(cell => cell.trim())178 let isSeparator = cells.every(cell => /^[\s\-:]+$/.test(cell))179 180 if (isSeparator) continue181 182 tableHtml += '<tr>'183 for (let cell of cells) {184 let align = ''185 let cleanCell = cell.trim()186 187 if (cleanCell.startsWith(':') && cleanCell.endsWith(':')) {188 align = ' align="center"'189 } else if (cleanCell.startsWith(':')) {190 align = ' align="left"'191 } else if (cleanCell.endsWith(':')) {192 align = ' align="right"'193 }194 195 let tag = isHeader ? 'th' : 'td'196 let content = cleanCell.replace(/^:|:$/g, '').trim()197 tableHtml += `<${tag}${align}>${content}</${tag}>`198 }199 tableHtml += '</tr>'200 isHeader = false201 }202 203 tableHtml += '</table>'204 205 // Remove original table markdown206 html = html.replace(/\|.*\|\n?/g, '')207 html = html.replace(/\|---\|/g, '')208 html = html + tableHtml209 }210 211 // ===== COLLAPSIBLE DETAILS =====212 html = html.replace(/<details>/g, '<details>')213 html = html.replace(/<\/details>/g, '</details>')214 html = html.replace(/<summary>(.*?)<\/summary>/g, '<summary>$1</summary>')215 216 // ===== CLEANUP =====217 // Convert double newlines to paragraphs218 html = html.replace(/\n\n/g, '</p><p>')219 html = '<p>' + html + '</p>'220 221 // Remove empty paragraphs222 html = html.replace(/<p><\/p>/g, '')223 224 // Clean up nested block elements225 html = html.replace(/<p>\s*<h/g, '<h')226 html = html.replace(/<\/h\d>\s*<\/p>/g, '</h>')227 html = html.replace(/<p>\s*<ul/g, '<ul')228 html = html.replace(/<\/ul>\s*<\/p>/g, '</ul>')229 html = html.replace(/<p>\s*<ol/g, '<ol')230 html = html.replace(/<\/ol>\s*<\/p>/g, '</ol>')231 html = html.replace(/<p>\s*<blockquote/g, '<blockquote')232 html = html.replace(/<\/blockquote>\s*<\/p>/g, '</blockquote>')233 html = html.replace(/<p>\s*<table/g, '<table')234 html = html.replace(/<\/table>\s*<\/p>/g, '</table>')235 html = html.replace(/<p>\s*<pre/g, '<pre')236 html = html.replace(/<\/pre>\s*<\/p>/g, '</pre>')237 html = html.replace(/<p>\s*<hr/g, '<hr')238 html = html.replace(/<hr\/>\s*<\/p>/g, '<hr/>')239 html = html.replace(/<p>\s*<tg-math/g, '<tg-math')240 html = html.replace(/<\/tg-math>\s*<\/p>/g, '</tg-math>')241 html = html.replace(/<p>\s*<tg-math-block/g, '<tg-math-block')242 html = html.replace(/<\/tg-math-block>\s*<\/p>/g, '</tg-math-block>')243 244 return html245}246 247// =======================248// VALIDATE AND FIX HTML249// =======================250function validateAndFixHtml(html) {251 // Remove empty tags that cause errors252 html = html.replace(/<tg-math>\s*<\/tg-math>/g, '')253 html = html.replace(/<tg-math-block>\s*<\/tg-math-block>/g, '')254 html = html.replace(/<tg-emoji>\s*<\/tg-emoji>/g, '')255 html = html.replace(/<tg-time>\s*<\/tg-time>/g, '')256 html = html.replace(/<tg-reference>\s*<\/tg-reference>/g, '')257 258 // Fix: tg-time needs valid attributes259 html = html.replace(/<tg-time[^>]*>/g, function(match) {260 if (match.includes('unix=') && match.includes('format=')) {261 return match262 }263 return '' // Remove invalid tg-time264 })265 266 // Fix: tg-emoji needs emoji-id267 html = html.replace(/<tg-emoji[^>]*>/g, function(match) {268 if (match.includes('emoji-id=')) {269 return match270 }271 return '' // Remove invalid tg-emoji272 })273 274 // Fix: tg-reference needs name275 html = html.replace(/<tg-reference[^>]*>/g, function(match) {276 if (match.includes('name=')) {277 return match278 }279 return '' // Remove invalid tg-reference280 })281 282 // Fix: math tags need content283 html = html.replace(/<tg-math>\s*<\/tg-math>/g, '')284 html = html.replace(/<tg-math-block>\s*<\/tg-math-block>/g, '')285 286 return html287}288 289// =======================290// PROCESS USER INPUT291// =======================292let finalHtml = userText293 294// Check if it's pure HTML295let isHTML = userText.includes("<") && userText.includes(">") && 296 (userText.includes("</") || userText.includes("<br>") || 297 userText.includes("<b>") || userText.includes("<i>") ||298 userText.includes("<u>") || userText.includes("<code>") ||299 userText.includes("<h1>") || userText.includes("<p>"))300 301// If not HTML, convert markdown to HTML302if (!isHTML) {303 finalHtml = markdownToHtml(userText)304}305 306// Validate and fix any issues307finalHtml = validateAndFixHtml(finalHtml)308 309// =======================310// BUILD RICH MESSAGE311// =======================312let richMessage = {313 html: finalHtml314}315 316// =======================317// SEND RICH MESSAGE318// =======================319await Api.call("answerGuestQuery", {320 guest_query_id: msg.guest_query_id,321 result: JSON.stringify({322 type: "article",323 id: String(Date.now()),324 title: "Rich Echo",325 input_message_content: {326 rich_message: richMessage327 }328 })329})