From d71fbbdab062ec3b349560f3c46ad82bfd0b51d4 Mon Sep 17 00:00:00 2001 From: Mohammed Amaan Ahmad Date: Thu, 26 Mar 2026 13:44:46 +0530 Subject: [PATCH 1/4] Add Markdown to HTML conversion for Telegram messages with CommonMark and test coverage --- plugins/telegram/build.gradle | 5 ++- .../channels/telegram/TelegramChannel.java | 35 +++++++++++++++++-- .../telegram/TelegramChannelTest.java | 22 ++++++++++++ 3 files changed, 59 insertions(+), 3 deletions(-) diff --git a/plugins/telegram/build.gradle b/plugins/telegram/build.gradle index d04bc82..dc65603 100644 --- a/plugins/telegram/build.gradle +++ b/plugins/telegram/build.gradle @@ -8,6 +8,9 @@ dependencies { implementation 'org.telegram:telegrambots-springboot-longpolling-starter:9.4.0' implementation 'org.telegram:telegrambots-client:9.4.0' + // Add commonmark for Markdown to HTML conversion + implementation 'org.commonmark:commonmark:0.21.0' + testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' -} +} \ No newline at end of file diff --git a/plugins/telegram/src/main/java/ai/javaclaw/channels/telegram/TelegramChannel.java b/plugins/telegram/src/main/java/ai/javaclaw/channels/telegram/TelegramChannel.java index fbc2af3..445308e 100644 --- a/plugins/telegram/src/main/java/ai/javaclaw/channels/telegram/TelegramChannel.java +++ b/plugins/telegram/src/main/java/ai/javaclaw/channels/telegram/TelegramChannel.java @@ -16,11 +16,23 @@ import org.telegram.telegrambots.meta.exceptions.TelegramApiException; import org.telegram.telegrambots.meta.generics.TelegramClient; +// Add CommonMark imports +import org.commonmark.node.Node; +import org.commonmark.parser.Parser; +import org.commonmark.renderer.html.HtmlRenderer; + import static java.util.Optional.ofNullable; public class TelegramChannel implements Channel, SpringLongPollingBot, LongPollingSingleThreadUpdateConsumer { private static final Logger log = LoggerFactory.getLogger(TelegramChannel.class); + + // 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("
") // Preserves newlines generated by the AI + .build(); + private final String botToken; private final String allowedUsername; private final TelegramClient telegramClient; @@ -83,10 +95,14 @@ public void sendMessage(String message) { } public void sendMessage(long chatId, Integer messageThreadId, String message) { + // Convert the raw markdown to HTML before building the message + String formattedHtmlMessage = convertMarkdownToTelegramHtml(message); + SendMessage messageMessage = SendMessage.builder() .chatId(chatId) .messageThreadId(messageThreadId) - .text(message) + .text(formattedHtmlMessage) // Use the formatted string + .parseMode("HTML") // Instruct Telegram to parse as HTML .build(); try { telegramClient.execute(messageMessage); @@ -95,6 +111,21 @@ public void sendMessage(long chatId, Integer messageThreadId, String message) { } } + // Helper method to handle the string conversion cleanly + private String convertMarkdownToTelegramHtml(String markdown) { + if (markdown == null || markdown.isBlank()) return ""; + + Node document = MARKDOWN_PARSER.parse(markdown); + String html = HTML_RENDERER.render(document); + + // Telegram only accepts specific HTML tags. Strip wrapping

tags + // and map standard strong/em tags to b/i tags for strict compatibility. + return html.replace("

", "").replace("

", "\n") + .replace("", "").replace("", "") + .replace("", "").replace("", "") + .trim(); + } + private boolean isAllowedUser(String userName) { String normalizedUserName = normalizeUsername(userName); return normalizedUserName != null && normalizedUserName.equalsIgnoreCase(allowedUsername); @@ -136,4 +167,4 @@ public Integer getMessageThreadId() { return messageThreadId; } } -} +} \ No newline at end of file diff --git a/plugins/telegram/src/test/java/ai/javaclaw/channels/telegram/TelegramChannelTest.java b/plugins/telegram/src/test/java/ai/javaclaw/channels/telegram/TelegramChannelTest.java index adbcc52..f275c9c 100644 --- a/plugins/telegram/src/test/java/ai/javaclaw/channels/telegram/TelegramChannelTest.java +++ b/plugins/telegram/src/test/java/ai/javaclaw/channels/telegram/TelegramChannelTest.java @@ -226,4 +226,26 @@ private Update updateFrom(String username, String text, long chatId, Integer mes return update; } + // ----------------------------------------------------------------------- +// Message formatting (Markdown → HTML) +// ----------------------------------------------------------------------- + + @Test + void formatsMarkdownToHtmlAndSetsParseMode() throws TelegramApiException { + TelegramChannel channel = channel("allowed_user"); + + // Mock agent response with Markdown + when(agent.respondTo(anyString(), anyString())) + .thenReturn("Here is **bold** text and a [link](http://example.com)"); + + // Trigger message consumption + channel.consume(updateFrom("allowed_user", "hello", 42L, null)); + + // Verify transformation + parse mode + verify(telegramClient).execute(argThat((SendMessage msg) -> + "HTML".equals(msg.getParseMode()) && + "Here is bold text and a link" + .equals(msg.getText()) + )); + } } From f5f47701bf96387f3cc9bd93810b34325a774fd2 Mon Sep 17 00:00:00 2001 From: Mohammed Amaan Ahmad Date: Fri, 27 Mar 2026 15:57:19 +0530 Subject: [PATCH 2/4] Remove unnecessary tag replacements and comments and adding fallback machenism --- .../channels/telegram/TelegramChannel.java | 48 ++++++++++++------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/plugins/telegram/src/main/java/ai/javaclaw/channels/telegram/TelegramChannel.java b/plugins/telegram/src/main/java/ai/javaclaw/channels/telegram/TelegramChannel.java index 445308e..512a9c8 100644 --- a/plugins/telegram/src/main/java/ai/javaclaw/channels/telegram/TelegramChannel.java +++ b/plugins/telegram/src/main/java/ai/javaclaw/channels/telegram/TelegramChannel.java @@ -10,13 +10,12 @@ import org.telegram.telegrambots.longpolling.interfaces.LongPollingUpdateConsumer; import org.telegram.telegrambots.longpolling.starter.SpringLongPollingBot; import org.telegram.telegrambots.longpolling.util.LongPollingSingleThreadUpdateConsumer; +import org.telegram.telegrambots.meta.api.methods.ParseMode; import org.telegram.telegrambots.meta.api.methods.send.SendMessage; import org.telegram.telegrambots.meta.api.objects.Update; import org.telegram.telegrambots.meta.api.objects.message.Message; import org.telegram.telegrambots.meta.exceptions.TelegramApiException; import org.telegram.telegrambots.meta.generics.TelegramClient; - -// Add CommonMark imports import org.commonmark.node.Node; import org.commonmark.parser.Parser; import org.commonmark.renderer.html.HtmlRenderer; @@ -27,10 +26,10 @@ public class TelegramChannel implements Channel, SpringLongPollingBot, LongPolli private static final Logger log = LoggerFactory.getLogger(TelegramChannel.class); - // 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("
") // Preserves newlines generated by the AI + .softbreak("
") + .escapeHtml(true) .build(); private final String botToken; @@ -95,34 +94,51 @@ public void sendMessage(String message) { } public void sendMessage(long chatId, Integer messageThreadId, String message) { - // Convert the raw markdown to HTML before building the message String formattedHtmlMessage = convertMarkdownToTelegramHtml(message); - SendMessage messageMessage = SendMessage.builder() + SendMessage htmlMessage = SendMessage.builder() .chatId(chatId) .messageThreadId(messageThreadId) - .text(formattedHtmlMessage) // Use the formatted string - .parseMode("HTML") // Instruct Telegram to parse as HTML + .text(formattedHtmlMessage) + .parseMode(ParseMode.HTML) .build(); + try { - telegramClient.execute(messageMessage); + telegramClient.execute(htmlMessage); } catch (TelegramApiException e) { - throw new RuntimeException(e); + log.warn("Failed to send HTML parsed message, falling back to raw text. Error: {}", e.getMessage()); + + SendMessage fallbackMessage = SendMessage.builder() + .chatId(chatId) + .messageThreadId(messageThreadId) + .text(message) + .build(); + try { + telegramClient.execute(fallbackMessage); + } catch (TelegramApiException fallbackEx) { + throw new RuntimeException("Failed to send both HTML and fallback messages", fallbackEx); + } } } - // Helper method to handle the string conversion cleanly private String convertMarkdownToTelegramHtml(String markdown) { if (markdown == null || markdown.isBlank()) return ""; Node document = MARKDOWN_PARSER.parse(markdown); String html = HTML_RENDERER.render(document); - // Telegram only accepts specific HTML tags. Strip wrapping

tags - // and map standard strong/em tags to b/i tags for strict compatibility. - return html.replace("

", "").replace("

", "\n") - .replace("", "").replace("", "") - .replace("", "").replace("", "") + // Minimalist replacement logic to handle unsupported structural tags + return html.replace("

", "").replace("

", "
") + .replaceAll("(?s)(.*?)", "$1
") + .replaceAll("(?s)
  • (.*?)
  • ", "- $1
    ") + .replace("", "") + .replace("
      ", "").replace("
    ", "") + .replaceAll("(?s)(.*?)", "$1 | ") + .replaceAll("(?s)(.*?)", "$1 | ") + .replaceAll("(?s)(.*?)", "$1
    ") + .replace("", "").replace("
    ", "") + .replace("", "").replace("", "") + .replace("", "").replace("", "") .trim(); } From 42e09c8808408797a03e0eaadd7f6f425e837e0b Mon Sep 17 00:00:00 2001 From: Ismaila Abdoulahi <36740618+auloin@users.noreply.github.com> Date: Thu, 2 Apr 2026 11:13:37 +0200 Subject: [PATCH 3/4] Fix tests and html conversion --- plugins/telegram/build.gradle | 2 +- .../channels/telegram/TelegramChannel.java | 25 +++---- .../telegram/TelegramChannelTest.java | 73 ++++++++++++------- 3 files changed, 58 insertions(+), 42 deletions(-) diff --git a/plugins/telegram/build.gradle b/plugins/telegram/build.gradle index dc65603..9692a49 100644 --- a/plugins/telegram/build.gradle +++ b/plugins/telegram/build.gradle @@ -8,8 +8,8 @@ dependencies { implementation 'org.telegram:telegrambots-springboot-longpolling-starter:9.4.0' implementation 'org.telegram:telegrambots-client:9.4.0' - // Add commonmark for Markdown to HTML conversion implementation 'org.commonmark:commonmark:0.21.0' + implementation 'org.commonmark:commonmark-ext-gfm-strikethrough:0.21.0' testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' diff --git a/plugins/telegram/src/main/java/ai/javaclaw/channels/telegram/TelegramChannel.java b/plugins/telegram/src/main/java/ai/javaclaw/channels/telegram/TelegramChannel.java index 512a9c8..0cced8c 100644 --- a/plugins/telegram/src/main/java/ai/javaclaw/channels/telegram/TelegramChannel.java +++ b/plugins/telegram/src/main/java/ai/javaclaw/channels/telegram/TelegramChannel.java @@ -4,6 +4,7 @@ import ai.javaclaw.channels.Channel; import ai.javaclaw.channels.ChannelMessageReceivedEvent; import ai.javaclaw.channels.ChannelRegistry; +import org.commonmark.ext.gfm.strikethrough.StrikethroughExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.telegram.telegrambots.client.okhttp.OkHttpTelegramClient; @@ -20,16 +21,19 @@ import org.commonmark.parser.Parser; import org.commonmark.renderer.html.HtmlRenderer; +import java.util.List; + import static java.util.Optional.ofNullable; public class TelegramChannel implements Channel, SpringLongPollingBot, LongPollingSingleThreadUpdateConsumer { - private static final Logger log = LoggerFactory.getLogger(TelegramChannel.class); + private static final Logger LOGGER = LoggerFactory.getLogger(TelegramChannel.class); private static final Parser MARKDOWN_PARSER = Parser.builder().build(); private static final HtmlRenderer HTML_RENDERER = HtmlRenderer.builder() .softbreak("
    ") .escapeHtml(true) + .extensions(List.of(StrikethroughExtension.create())) .build(); private final String botToken; @@ -38,7 +42,6 @@ public class TelegramChannel implements Channel, SpringLongPollingBot, LongPolli private final Agent agent; private final ChannelRegistry channelRegistry; private Long chatId; - private Integer messageThreadId; public TelegramChannel(String botToken, String allowedUsername, Agent agent, ChannelRegistry channelRegistry) { this(botToken, allowedUsername, new OkHttpTelegramClient(botToken), agent, channelRegistry); @@ -51,7 +54,7 @@ public TelegramChannel(String botToken, String allowedUsername, Agent agent, Cha this.agent = agent; this.channelRegistry = channelRegistry; channelRegistry.registerChannel(this); - log.info("Started Telegram integration"); + LOGGER.info("Started Telegram integration"); } @Override @@ -71,14 +74,14 @@ public void consume(Update update) { Message requestMessage = update.getMessage(); String userName = requestMessage.getFrom() == null ? null : requestMessage.getFrom().getUserName(); if (!isAllowedUser(userName)) { - log.warn("Ignoring Telegram message from unauthorized username '{}'", userName); + LOGGER.warn("Ignoring Telegram message from unauthorized username '{}'", userName); sendMessage("I'm sorry, I don't accept instructions from you."); return; } String messageText = requestMessage.getText(); this.chatId = requestMessage.getChatId(); - this.messageThreadId = requestMessage.getMessageThreadId(); + Integer messageThreadId = requestMessage.getMessageThreadId(); channelRegistry.publishMessageReceivedEvent(new TelegramChannelMessageReceivedEvent(getName(), messageText, chatId, messageThreadId)); String response = agent.respondTo(getConversationId(chatId, messageThreadId), messageText); sendMessage(chatId, messageThreadId, response); @@ -87,7 +90,7 @@ public void consume(Update update) { @Override public void sendMessage(String message) { if (chatId == null) { - log.error("No known chatId, cannot send message '{}'", message); + LOGGER.error("No known chatId, cannot send message '{}'", message); return; } sendMessage(chatId, null, message); @@ -106,13 +109,14 @@ public void sendMessage(long chatId, Integer messageThreadId, String message) { try { telegramClient.execute(htmlMessage); } catch (TelegramApiException e) { - log.warn("Failed to send HTML parsed message, falling back to raw text. Error: {}", e.getMessage()); + LOGGER.warn("Failed to send HTML parsed message, falling back to raw text.", e); SendMessage fallbackMessage = SendMessage.builder() .chatId(chatId) .messageThreadId(messageThreadId) .text(message) .build(); + try { telegramClient.execute(fallbackMessage); } catch (TelegramApiException fallbackEx) { @@ -133,12 +137,7 @@ private String convertMarkdownToTelegramHtml(String markdown) { .replaceAll("(?s)
  • (.*?)
  • ", "- $1
    ") .replace("", "") .replace("
      ", "").replace("
    ", "") - .replaceAll("(?s)(.*?)", "$1 | ") - .replaceAll("(?s)(.*?)", "$1 | ") - .replaceAll("(?s)(.*?)", "$1
    ") - .replace("", "").replace("
    ", "") - .replace("", "").replace("", "") - .replace("", "").replace("", "") + .replace("
    ", "
    ") .trim(); } diff --git a/plugins/telegram/src/test/java/ai/javaclaw/channels/telegram/TelegramChannelTest.java b/plugins/telegram/src/test/java/ai/javaclaw/channels/telegram/TelegramChannelTest.java index f275c9c..e89b616 100644 --- a/plugins/telegram/src/test/java/ai/javaclaw/channels/telegram/TelegramChannelTest.java +++ b/plugins/telegram/src/test/java/ai/javaclaw/channels/telegram/TelegramChannelTest.java @@ -6,6 +6,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.telegram.telegrambots.meta.api.methods.ParseMode; import org.telegram.telegrambots.meta.api.methods.send.SendMessage; import org.telegram.telegrambots.meta.api.objects.Update; import org.telegram.telegrambots.meta.api.objects.User; @@ -90,7 +91,7 @@ void ignoresMessagesFromUnauthorizedUser() { // ----------------------------------------------------------------------- @Test - void usernameMatchingIsCaseInsensitive() throws TelegramApiException { + void usernameMatchingIsCaseInsensitive() { TelegramChannel channel = channel("Allowed_User"); when(agent.respondTo(anyString(), anyString())).thenReturn("hi"); @@ -100,7 +101,7 @@ void usernameMatchingIsCaseInsensitive() throws TelegramApiException { } @Test - void stripsLeadingAtFromConfiguredUsername() throws TelegramApiException { + void stripsLeadingAtFromConfiguredUsername() { TelegramChannel channel = channel("@Allowed_User"); when(agent.respondTo(anyString(), anyString())).thenReturn("hi"); @@ -110,7 +111,7 @@ void stripsLeadingAtFromConfiguredUsername() throws TelegramApiException { } @Test - void stripsLeadingAtFromIncomingUsername() throws TelegramApiException { + void stripsLeadingAtFromIncomingUsername() { TelegramChannel channel = channel("allowed_user"); when(agent.respondTo(anyString(), anyString())).thenReturn("hi"); @@ -124,7 +125,7 @@ void stripsLeadingAtFromIncomingUsername() throws TelegramApiException { // ----------------------------------------------------------------------- @Test - void usesChannelChatIdAsConversationId() throws TelegramApiException { + void usesChannelChatIdAsConversationId() { TelegramChannel channel = channel("allowed_user"); when(agent.respondTo(anyString(), anyString())).thenReturn("hi"); @@ -134,7 +135,7 @@ void usesChannelChatIdAsConversationId() throws TelegramApiException { } @Test - void includesMessageThreadIdInConversationId() throws TelegramApiException { + void includesMessageThreadIdInConversationId() { TelegramChannel channel = channel("allowed_user"); when(agent.respondTo(anyString(), anyString())).thenReturn("hi"); @@ -151,7 +152,7 @@ void sendsAgentResponseToCorrectChatId() throws TelegramApiException { channel.consume(updateFrom("allowed_user", "hello", 42L, null)); verify(telegramClient).execute(argThat((SendMessage msg) -> - "42".equals(msg.getChatId()) && "hi".equals(msg.getText()))); + "42".equals(msg.getChatId()) && "hi
    ".equals(msg.getText()))); } @Test @@ -190,6 +191,44 @@ void sendMessageDoesNothingWhenNoChatIdKnown() { verifyNoInteractions(telegramClient); } + // ----------------------------------------------------------------------- + // Message formatting (Markdown → HTML) + // ----------------------------------------------------------------------- + + @Test + void sendMessageConvertsMarkdownToTelegramHtml() throws TelegramApiException { + TelegramChannel channel = channel("allowed_user"); + when(agent.respondTo(anyString(), anyString())) + .thenReturn("Here is **bold** text and a [link](https://example.com)"); + + channel.consume(updateFrom("allowed_user", "hello", 42L, 567)); + + verify(telegramClient).execute(argThat((SendMessage msg) -> + ParseMode.HTML.equals(msg.getParseMode()) && + "Here is bold text and a link
    " + .equals(msg.getText()) + )); + } + + + @Test + void sendMessageFallbacksToSendingRawTextWhenFailingToSendHtml() throws TelegramApiException { + TelegramChannel channel = channel("allowed_user"); + when(agent.respondTo(anyString(), anyString())) + .thenReturn("Here is **bold** text and an image: ![An example image](/assets/images/clawrunr.png)"); + + when(telegramClient.execute(argThat((SendMessage msg) -> + ParseMode.HTML.equals(msg.getParseMode()) + ))).thenThrow(new TelegramApiException("Invalid HTML")); + + channel.consume(updateFrom("allowed_user", "hello", 42L, 567)); + + verify(telegramClient).execute(argThat((SendMessage msg) -> + msg.getParseMode() == null && + msg.getText().equals("Here is **bold** text and an image: ![An example image](/assets/images/clawrunr.png)") + )); + } + // ----------------------------------------------------------------------- // helpers // ----------------------------------------------------------------------- @@ -226,26 +265,4 @@ private Update updateFrom(String username, String text, long chatId, Integer mes return update; } - // ----------------------------------------------------------------------- -// Message formatting (Markdown → HTML) -// ----------------------------------------------------------------------- - - @Test - void formatsMarkdownToHtmlAndSetsParseMode() throws TelegramApiException { - TelegramChannel channel = channel("allowed_user"); - - // Mock agent response with Markdown - when(agent.respondTo(anyString(), anyString())) - .thenReturn("Here is **bold** text and a [link](http://example.com)"); - - // Trigger message consumption - channel.consume(updateFrom("allowed_user", "hello", 42L, null)); - - // Verify transformation + parse mode - verify(telegramClient).execute(argThat((SendMessage msg) -> - "HTML".equals(msg.getParseMode()) && - "Here is bold text and a link" - .equals(msg.getText()) - )); - } } From 09485d7b663e6b67e125cb61f49047ddb525e766 Mon Sep 17 00:00:00 2001 From: Ismaila Abdoulahi <36740618+auloin@users.noreply.github.com> Date: Thu, 2 Apr 2026 13:31:52 +0200 Subject: [PATCH 4/4] Replace `br` by `\n` --- .../ai/javaclaw/channels/telegram/TelegramChannel.java | 9 ++++----- .../javaclaw/channels/telegram/TelegramChannelTest.java | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/plugins/telegram/src/main/java/ai/javaclaw/channels/telegram/TelegramChannel.java b/plugins/telegram/src/main/java/ai/javaclaw/channels/telegram/TelegramChannel.java index 0cced8c..018c427 100644 --- a/plugins/telegram/src/main/java/ai/javaclaw/channels/telegram/TelegramChannel.java +++ b/plugins/telegram/src/main/java/ai/javaclaw/channels/telegram/TelegramChannel.java @@ -31,7 +31,6 @@ public class TelegramChannel implements Channel, SpringLongPollingBot, LongPolli private static final Parser MARKDOWN_PARSER = Parser.builder().build(); private static final HtmlRenderer HTML_RENDERER = HtmlRenderer.builder() - .softbreak("
    ") .escapeHtml(true) .extensions(List.of(StrikethroughExtension.create())) .build(); @@ -132,12 +131,12 @@ private String convertMarkdownToTelegramHtml(String markdown) { String html = HTML_RENDERER.render(document); // Minimalist replacement logic to handle unsupported structural tags - return html.replace("

    ", "").replace("

    ", "
    ") - .replaceAll("(?s)(.*?)", "$1
    ") - .replaceAll("(?s)
  • (.*?)
  • ", "- $1
    ") + return html.replace("

    ", "").replace("

    ", "\n") + .replaceAll("(?s)(.*?)", "$1\n") + .replaceAll("(?s)
  • (.*?)
  • ", "- $1\n") .replace("", "") .replace("
      ", "").replace("
    ", "") - .replace("
    ", "
    ") + .replace("
    ", "\n") .trim(); } diff --git a/plugins/telegram/src/test/java/ai/javaclaw/channels/telegram/TelegramChannelTest.java b/plugins/telegram/src/test/java/ai/javaclaw/channels/telegram/TelegramChannelTest.java index e89b616..a8d0378 100644 --- a/plugins/telegram/src/test/java/ai/javaclaw/channels/telegram/TelegramChannelTest.java +++ b/plugins/telegram/src/test/java/ai/javaclaw/channels/telegram/TelegramChannelTest.java @@ -152,7 +152,7 @@ void sendsAgentResponseToCorrectChatId() throws TelegramApiException { channel.consume(updateFrom("allowed_user", "hello", 42L, null)); verify(telegramClient).execute(argThat((SendMessage msg) -> - "42".equals(msg.getChatId()) && "hi
    ".equals(msg.getText()))); + "42".equals(msg.getChatId()) && "hi".equals(msg.getText()))); } @Test @@ -205,7 +205,7 @@ void sendMessageConvertsMarkdownToTelegramHtml() throws TelegramApiException { verify(telegramClient).execute(argThat((SendMessage msg) -> ParseMode.HTML.equals(msg.getParseMode()) && - "Here is bold text and a link
    " + "Here is bold text and a link" .equals(msg.getText()) )); }