From 6b9251fe4d9f567c0c21f6b4d700e57f61dfa6b3 Mon Sep 17 00:00:00 2001 From: Buddhi Thilakshana Date: Tue, 14 Oct 2025 10:19:36 +0530 Subject: [PATCH] feat: enhance error logging with HTML formatting and add HTML escaping utility --- internal/telegram/error_logger.go | 61 +++++++++++++++++++++++++++---- internal/utils/html.go | 15 ++++++++ 2 files changed, 68 insertions(+), 8 deletions(-) create mode 100644 internal/utils/html.go diff --git a/internal/telegram/error_logger.go b/internal/telegram/error_logger.go index 89ea7aa9..f5a18316 100644 --- a/internal/telegram/error_logger.go +++ b/internal/telegram/error_logger.go @@ -302,11 +302,34 @@ func (el *ErrorLogger) LogPaymentError(err error, amount int64, memo, invoice st timestamp := time.Now().Format("2006-01-02 15:04:05 UTC") - msg := fmt.Sprintf("🚫 Payment Error for %s (ID: %d)\n\n", el.getUserStrV2(user), user.ID) - msg += fmt.Sprintf("šŸ’° Amount: %s\n", el.escapeMarkdownV2(utils.FormatSats(amount))) - msg += fmt.Sprintf("šŸ“ Memo: %s\n", el.escapeMarkdownV2(memo)) - msg += fmt.Sprintf("šŸ“„ Invoice: %s\n", el.escapeMarkdownV2(invoice)) - msg += fmt.Sprintf("ā— Error: %s\n\n", el.escapeMarkdownV2(err.Error())) + // HTML escaping utility + htmlEscape := utils.EscapeHTML + + userStr := "Unknown" + if user != nil { + if user.Username != "" { + userStr = "@" + htmlEscape(user.Username) + "" + } else { + userStr = "" + htmlEscape(user.FirstName+" "+user.LastName) + "" + } + } + + amountStr := "" + htmlEscape(utils.FormatSats(amount)) + "" + memoStr := htmlEscape(memo) + invoiceStr := htmlEscape(invoice) + + // Compose HTML message + msg := fmt.Sprintf( + "🚫 Payment Error for %s (ID: %d)\n\n"+ + "šŸ’° Amount: %s\n"+ + "šŸ“„ Invoice: %s\n"+ + "šŸ“ Memo: %s\n"+ + "
", + userStr, user.ID, amountStr, invoiceStr, memoStr, + ) + + // Expandable details (Telegram does not support true expandable blocks, but blockquote visually separates) + detail := fmt.Sprintf("ā— Error: %s\n", htmlEscape(err.Error())) if _, file, line, ok := runtime.Caller(1); ok { if strings.Contains(file, "error_logger.go") { @@ -318,12 +341,34 @@ func (el *ErrorLogger) LogPaymentError(err error, amount int64, memo, invoice st if idx := strings.Index(file, "/internal/"); idx > -1 { file = file[idx:] } - msg += fmt.Sprintf("šŸ“ Logged at:\n%s:%d\n(from github.com/LightningTipBot/LightningTipBot)", el.escapeMarkdownV2(file), line) + detail += fmt.Sprintf("šŸ“ Logged at:\n%s:%d\n(from github.com/LightningTipBot/LightningTipBot)\n", htmlEscape(file), line) } + detail += fmt.Sprintf("šŸ•’ Time: %s", htmlEscape(timestamp)) + + msg += detail + "
" + + go el.sendToTelegramHTML(msg) +} - msg += fmt.Sprintf("\nšŸ•’ Time: %s", el.escapeMarkdownV2(timestamp)) +// sendToTelegramHTML sends the formatted HTML message to the Telegram group +func (el *ErrorLogger) sendToTelegramHTML(message string) { + if el.bot == nil || el.bot.Telegram == nil { + log.Warnf("[ErrorLogger] Cannot send error log - Telegram bot not initialized") + return + } - go el.sendToTelegram(msg) + recipient := &tb.Chat{ID: el.logGroupId} + sendOptions := &tb.SendOptions{ + ParseMode: "HTML", + DisableWebPagePreview: true, + } + if el.threadId > 0 { + sendOptions.ReplyTo = &tb.Message{ID: int(el.threadId)} + } + _, err := el.bot.Telegram.Send(recipient, message, sendOptions) + if err != nil { + log.Errorf("[ErrorLogger] Failed to send HTML error log to Telegram: %v", err) + } } // LogTransactionError logs transaction-related errors with sender/receiver info diff --git a/internal/utils/html.go b/internal/utils/html.go new file mode 100644 index 00000000..047b66e7 --- /dev/null +++ b/internal/utils/html.go @@ -0,0 +1,15 @@ +package utils + +import "strings" + +// EscapeHTML escapes special HTML characters for safe Telegram HTML message formatting. +func EscapeHTML(text string) string { + replacer := strings.NewReplacer( + "&", "&", + "<", "<", + ">", ">", + "\"", """, + "'", "'", + ) + return replacer.Replace(text) +}