Skip to content

Commit

Permalink
feat: Use Telegram HTML Parsemode (#8731)
Browse files Browse the repository at this point in the history
- this ensures that the markdown formatted messages from the Chatwoot dashboard will render consistently in telegram UI for the supported types like bold, italics, links etc
  • Loading branch information
sojan-official committed Jan 18, 2024
1 parent 4bf23ad commit 5f6e17f
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 15 deletions.
28 changes: 18 additions & 10 deletions app/models/channel/telegram.rb
Expand Up @@ -149,24 +149,32 @@ def attachments_request(chat_id, attachments, reply_to_message_id)
})
end

def convert_markdown_to_telegram(text)
## supported characters : https://core.telegram.org/bots/api#markdown-style
## To implement MarkdownV2, we will need to do a lot of escaping
def convert_markdown_to_telegram_html(text)
# ref: https://core.telegram.org/bots/api#html-style

# Convert bold - double asterisks to single asterisk in Telegram
# Chatwoot uses double asterisks for bold, while telegram used single asterisk
text.gsub!(/\*\*(.*?)\*\*/, '*\1*')
text
# escape html tags in text. We are subbing \n to <br> since commonmark will strip exta '\n'
text = CGI.escapeHTML(text.gsub("\n", '<br>'))

# convert markdown to html
html = CommonMarker.render_html(text).strip

# remove all html tags except b, strong, i, em, u, ins, s, strike, del, a, code, pre, blockquote
stripped_html = Rails::HTML5::SafeListSanitizer.new.sanitize(html, tags: %w[b strong i em u ins s strike del a code pre blockquote],
attributes: %w[href])

# converted escaped br tags to \n
stripped_html.gsub('&lt;br&gt;', "\n")
end

def message_request(chat_id, text, reply_markup = nil, reply_to_message_id = nil)
text_to_md = convert_markdown_to_telegram(text)
text_payload = convert_markdown_to_telegram_html(text)

HTTParty.post("#{telegram_api_url}/sendMessage",
body: {
chat_id: chat_id,
text: text_to_md,
text: text_payload,
reply_markup: reply_markup,
parse_mode: 'Markdown',
parse_mode: 'HTML',
reply_to_message_id: reply_to_message_id
})
end
Expand Down
64 changes: 59 additions & 5 deletions spec/models/channel/telegram_spec.rb
Expand Up @@ -3,14 +3,66 @@
RSpec.describe Channel::Telegram do
let(:telegram_channel) { create(:channel_telegram) }

describe '#convert_markdown_to_telegram_html' do
subject { telegram_channel.send(:convert_markdown_to_telegram_html, text) }

context 'when text contains multiple newline characters' do
let(:text) { "Line one\nLine two\n\nLine four" }

it 'preserves multiple newline characters' do
expect(subject).to eq("Line one\nLine two\n\nLine four")
end
end

context 'when text contains broken markdown' do
let(:text) { 'This is a **broken markdown with <b>HTML</b> tags.' }

it 'does not break and properly converts to Telegram HTML format and escapes html tags' do
expect(subject).to eq('This is a **broken markdown with &lt;b&gt;HTML&lt;/b&gt; tags.')
end
end

context 'when text contains markdown and HTML elements' do
let(:text) { "Hello *world*! This is <b>bold</b> and this is <i>italic</i>.\nThis is a new line." }

it 'converts markdown to Telegram HTML format and escapes other html' do
expect(subject).to eq("Hello <em>world</em>! This is &lt;b&gt;bold&lt;/b&gt; and this is &lt;i&gt;italic&lt;/i&gt;.\nThis is a new line.")
end
end

context 'when text contains unsupported HTML tags' do
let(:text) { 'This is a <span>test</span> with unsupported tags.' }

it 'removes unsupported HTML tags' do
expect(subject).to eq('This is a &lt;span&gt;test&lt;/span&gt; with unsupported tags.')
end
end

context 'when text contains special characters' do
let(:text) { 'Special characters: & < >' }

it 'escapes special characters' do
expect(subject).to eq('Special characters: &amp; &lt; &gt;')
end
end

context 'when text contains markdown links' do
let(:text) { 'Check this [link](http://example.com) out!' }

it 'converts markdown links to Telegram HTML format' do
expect(subject).to eq('Check this <a href="http://example.com">link</a> out!')
end
end
end

context 'when a valid message and empty attachments' do
it 'send message' do
message = create(:message, message_type: :outgoing, content: 'test',
conversation: create(:conversation, inbox: telegram_channel.inbox, additional_attributes: { 'chat_id' => '123' }))

stub_request(:post, "https://api.telegram.org/bot#{telegram_channel.bot_token}/sendMessage")
.with(
body: 'chat_id=123&text=test&reply_markup=&parse_mode=Markdown&reply_to_message_id='
body: 'chat_id=123&text=test&reply_markup=&parse_mode=HTML&reply_to_message_id='
)
.to_return(
status: 200,
Expand All @@ -21,13 +73,15 @@
expect(telegram_channel.send_message_on_telegram(message)).to eq('telegram_123')
end

it 'send message with markdown converted to telegram markdown' do
it 'send message with markdown converted to telegram HTML' do
message = create(:message, message_type: :outgoing, content: '**test** *test* ~test~',
conversation: create(:conversation, inbox: telegram_channel.inbox, additional_attributes: { 'chat_id' => '123' }))

stub_request(:post, "https://api.telegram.org/bot#{telegram_channel.bot_token}/sendMessage")
.with(
body: "chat_id=123&text=#{ERB::Util.url_encode('*test* *test* ~test~')}&reply_markup=&parse_mode=Markdown&reply_to_message_id="
body: "chat_id=123&text=#{
ERB::Util.url_encode('<strong>test</strong> <em>test</em> ~test~')
}&reply_markup=&parse_mode=HTML&reply_to_message_id="
)
.to_return(
status: 200,
Expand All @@ -49,7 +103,7 @@
.with(
body: 'chat_id=123&text=test' \
'&reply_markup=%7B%22one_time_keyboard%22%3Atrue%2C%22inline_keyboard%22%3A%5B%5B%7B%22text%22%3A%22test%22%2C%22' \
'callback_data%22%3A%22test%22%7D%5D%5D%7D&parse_mode=Markdown&reply_to_message_id='
'callback_data%22%3A%22test%22%7D%5D%5D%7D&parse_mode=HTML&reply_to_message_id='
)
.to_return(
status: 200,
Expand All @@ -66,7 +120,7 @@

stub_request(:post, "https://api.telegram.org/bot#{telegram_channel.bot_token}/sendMessage")
.with(
body: 'chat_id=123&text=test&reply_markup=&parse_mode=Markdown&reply_to_message_id='
body: 'chat_id=123&text=test&reply_markup=&parse_mode=HTML&reply_to_message_id='
)
.to_return(
status: 403,
Expand Down

0 comments on commit 5f6e17f

Please sign in to comment.