Add Markdown to HTML conversion for Telegram messages#32
Add Markdown to HTML conversion for Telegram messages#32Mohammedamaan5714 wants to merge 2 commits intojobrunr:mainfrom
Conversation
… and test coverage
|
We require contributors to sign our Contributor License Agreement, and we don't have @Mohammedamaan5714 on file. In order for us to review and merge your code, please create a PR where you add yourself to the contributors of JobRunr. This only needs to be done once. As soon as that is done, we can review your PR. Thanks a lot! |
|
@cla-bot check |
|
The cla-bot has been summoned, and re-checked this pull request! |
auloin
left a comment
There was a problem hiding this comment.
Hi @Mohammedamaan5714, thanks for the PR!
I've one global request to reduce the amount of comments. Your descriptive variable names already already makes the code readable.
Two other requests:
- Should we add a fallback and send the original raw markdown in case sending the html fails?
- Can we handle all the non supported tags? I think headings, lists (numbered or bulleted) and tables are very common in the agent reply.
| // Initialize Parser and Renderer statically for maximum performance | ||
| private static final Parser MARKDOWN_PARSER = Parser.builder().build(); | ||
| private static final HtmlRenderer HTML_RENDERER = HtmlRenderer.builder() | ||
| .softbreak("<br/>") // Preserves newlines generated by the AI |
There was a problem hiding this comment.
I'd also escapeHtml in case the agent returns html in the markdown
There was a problem hiding this comment.
okay I will add .escapeHtml(true) to the HtmlRenderer.builder(). So, if the AI accidentally generates raw, unformatted HTML tags (like <script>), commonmark will safely escape them to <script> so it doesn't break the Telegram API payload.
| .messageThreadId(messageThreadId) | ||
| .text(message) | ||
| .text(formattedHtmlMessage) // Use the formatted string | ||
| .parseMode("HTML") // Instruct Telegram to parse as HTML |
There was a problem hiding this comment.
(will need an import)
| .parseMode("HTML") // Instruct Telegram to parse as HTML | |
| .parseMode(ParseMode.HTML) |
|
|
||
| // Telegram only accepts specific HTML tags. Strip wrapping <p> tags | ||
| // and map standard strong/em tags to b/i tags for strict compatibility. | ||
| return html.replace("<p>", "").replace("</p>", "\n") |
There was a problem hiding this comment.
I think you meant
| return html.replace("<p>", "").replace("</p>", "\n") | |
| return html.replace("<p>", "").replace("</p>", "<br />") |
| .replace("<strong>", "<b>").replace("</strong>", "</b>") | ||
| .replace("<em>", "<i>").replace("</em>", "</i>") |
There was a problem hiding this comment.
I don't think we need to replace these, already supported by Telegram: https://core.telegram.org/bots/api#html-style
There was a problem hiding this comment.
You’re right, thanks for pointing that out those replacements are not necessary.
My initial intention was to ensure compatibility with Telegram’s limited HTML support, especially for cases where the renderer might produce unsupported tags (like tables or other complex structures from AI responses).
I’m also considering adding a fallback mechanism to handle cases where Telegram rejects the formatted HTML ( due to unsupported tags), by sending the raw text instead.
I’ll update the implementation accordingly and remove the unnecessary replacements.
|
I've cleaned my commit and add fallback mechanism. please review this and tell me if there is anything else i need to change. |
|
I just tested the PR but I can't see any differences in rendering in Telegram. It seems like it still outputting markdown. Do I need to change something on my end to make it work? Attached you see the output where you should be able to see bold, titles and a table. I also added an anonymized version of the chat (with other names, amounts etc) so you can see how the yaml looks like on my side |


Summary
This PR adds support for converting Markdown responses into Telegram-compatible HTML format.
Changes
Testing
formatsMarkdownToHtmlAndSetsParseMode