mirror of
https://github.com/caronc/apprise.git
synced 2024-11-27 02:23:25 +01:00
Escape unsupported Telegram MarkdownV2 characters if required (#1181)
This commit is contained in:
parent
1ee08d14fe
commit
5cee11ac84
@ -368,7 +368,7 @@ class NotifyTelegram(NotifyBase):
|
|||||||
'name': _('Markdown Version'),
|
'name': _('Markdown Version'),
|
||||||
'type': 'choice:string',
|
'type': 'choice:string',
|
||||||
'values': ('v1', 'v2'),
|
'values': ('v1', 'v2'),
|
||||||
'default': 'v2',
|
'default': 'v1',
|
||||||
},
|
},
|
||||||
'to': {
|
'to': {
|
||||||
'alias_of': 'targets',
|
'alias_of': 'targets',
|
||||||
@ -764,6 +764,16 @@ class NotifyTelegram(NotifyBase):
|
|||||||
|
|
||||||
# Prepare Message Body
|
# Prepare Message Body
|
||||||
if self.notify_format == NotifyFormat.MARKDOWN:
|
if self.notify_format == NotifyFormat.MARKDOWN:
|
||||||
|
|
||||||
|
if body_format not in (None, NotifyFormat.MARKDOWN) \
|
||||||
|
and self.markdown_ver == TelegramMarkdownVersion.TWO:
|
||||||
|
# Telegram Markdown v2 is not very accomodating to some
|
||||||
|
# characters such as the hashtag (#) which is fine in v1.
|
||||||
|
# To try and be accomodating we escape them in advance
|
||||||
|
# See: https://stackoverflow.com/a/69892704/355584
|
||||||
|
# Also: https://core.telegram.org/bots/api#markdownv2-style
|
||||||
|
body = re.sub(r'(?<!\\)([_*[\]()~`>#+=|{}.!-])', r'\\\1', body)
|
||||||
|
|
||||||
_payload['parse_mode'] = self.markdown_ver
|
_payload['parse_mode'] = self.markdown_ver
|
||||||
_payload['text'] = body
|
_payload['text'] = body
|
||||||
|
|
||||||
|
@ -695,7 +695,38 @@ def test_plugin_telegram_formatting(mock_post):
|
|||||||
' had [a change](http://127.0.0.1)'
|
' had [a change](http://127.0.0.1)'
|
||||||
|
|
||||||
aobj = Apprise()
|
aobj = Apprise()
|
||||||
aobj.add('tgram://123456789:abcdefg_hijklmnop/?format=markdown')
|
aobj.add('tgram://123456789:abcdefg_hijklmnop/?format=markdown&mdv=2')
|
||||||
|
assert len(aobj) == 1
|
||||||
|
|
||||||
|
assert aobj.notify(
|
||||||
|
title=title, body=body, body_format=NotifyFormat.MARKDOWN)
|
||||||
|
|
||||||
|
# Test our calls
|
||||||
|
assert mock_post.call_count == 2
|
||||||
|
|
||||||
|
assert mock_post.call_args_list[0][0][0] == \
|
||||||
|
'https://api.telegram.org/bot123456789:abcdefg_hijklmnop/getUpdates'
|
||||||
|
assert mock_post.call_args_list[1][0][0] == \
|
||||||
|
'https://api.telegram.org/bot123456789:abcdefg_hijklmnop/sendMessage'
|
||||||
|
|
||||||
|
payload = loads(mock_post.call_args_list[1][1]['data'])
|
||||||
|
|
||||||
|
# Test that everything is escaped properly in a HTML mode
|
||||||
|
assert payload['text'] == \
|
||||||
|
'# 🚨 Change detected for _Apprise Test Title_\r\n' \
|
||||||
|
'_[Apprise Body Title](http://localhost)_ had ' \
|
||||||
|
'[a change](http://127.0.0.1)'
|
||||||
|
|
||||||
|
# Reset our values
|
||||||
|
mock_post.reset_mock()
|
||||||
|
|
||||||
|
# Now test our MARKDOWN Handling
|
||||||
|
title = '# 🚨 Change detected for _Apprise Test Title_'
|
||||||
|
body = '_[Apprise Body Title](http://localhost)_' \
|
||||||
|
' had [a change](http://127.0.0.1)'
|
||||||
|
|
||||||
|
aobj = Apprise()
|
||||||
|
aobj.add('tgram://123456789:abcdefg_hijklmnop/?format=markdown&mdv=1')
|
||||||
assert len(aobj) == 1
|
assert len(aobj) == 1
|
||||||
|
|
||||||
assert aobj.notify(
|
assert aobj.notify(
|
||||||
@ -759,9 +790,9 @@ def test_plugin_telegram_formatting(mock_post):
|
|||||||
# Reset our values
|
# Reset our values
|
||||||
mock_post.reset_mock()
|
mock_post.reset_mock()
|
||||||
|
|
||||||
# Upstream to use HTML but input specified as Markdown
|
# Upstream to use HTML but input specified as Markdown v1
|
||||||
aobj = Apprise()
|
aobj = Apprise()
|
||||||
aobj.add('tgram://987654321:abcdefg_hijklmnop/?format=markdown')
|
aobj.add('tgram://987654321:abcdefg_hijklmnop/?format=markdown&mdv=1')
|
||||||
assert len(aobj) == 1
|
assert len(aobj) == 1
|
||||||
|
|
||||||
# Now test our MARKDOWN Handling (no title defined... not really anyway)
|
# Now test our MARKDOWN Handling (no title defined... not really anyway)
|
||||||
@ -784,24 +815,17 @@ def test_plugin_telegram_formatting(mock_post):
|
|||||||
|
|
||||||
payload = loads(mock_post.call_args_list[1][1]['data'])
|
payload = loads(mock_post.call_args_list[1][1]['data'])
|
||||||
|
|
||||||
# Test that everything is escaped properly in a HTML mode
|
# Test that everything is escaped properly in a MARKDOWN mode
|
||||||
assert payload['text'] == \
|
assert payload['text'] == body
|
||||||
'_[Apprise Body Title](http://localhost)_ had ' \
|
|
||||||
'[a change](http://127.0.0.2)'
|
|
||||||
|
|
||||||
# Reset our values
|
# Reset our values
|
||||||
mock_post.reset_mock()
|
mock_post.reset_mock()
|
||||||
|
|
||||||
# Upstream to use HTML but input specified as Markdown
|
# Upstream to use HTML but input specified as Markdown v2
|
||||||
aobj = Apprise()
|
aobj = Apprise()
|
||||||
aobj.add('tgram://987654321:abcdefg_hijklmnop/?format=markdown')
|
aobj.add('tgram://987654321:abcdefg_hijklmnop/?format=markdown&mdv=2')
|
||||||
assert len(aobj) == 1
|
assert len(aobj) == 1
|
||||||
|
|
||||||
# Set an actual title this time
|
|
||||||
title = '# A Great Title'
|
|
||||||
body = '_[Apprise Body Title](http://localhost)_' \
|
|
||||||
' had [a change](http://127.0.0.2)'
|
|
||||||
|
|
||||||
# MARKDOWN forced by the command line, but TEXT specified as
|
# MARKDOWN forced by the command line, but TEXT specified as
|
||||||
# upstream mode
|
# upstream mode
|
||||||
assert aobj.notify(
|
assert aobj.notify(
|
||||||
@ -817,9 +841,115 @@ def test_plugin_telegram_formatting(mock_post):
|
|||||||
|
|
||||||
payload = loads(mock_post.call_args_list[1][1]['data'])
|
payload = loads(mock_post.call_args_list[1][1]['data'])
|
||||||
|
|
||||||
# Test that everything is escaped properly in a HTML mode
|
# Test that everything is escaped properly in a MARKDOWN mode
|
||||||
assert payload['text'] == \
|
assert payload['text'] == \
|
||||||
'# A Great Title\r\n_[Apprise Body Title](http://localhost)_ had ' \
|
'\\_\\[Apprise Body Title\\]\\(http://localhost\\)\\_ had \\' \
|
||||||
|
'[a change\\]\\(http://127\\.0\\.0\\.2\\)'
|
||||||
|
|
||||||
|
# Reset our values
|
||||||
|
mock_post.reset_mock()
|
||||||
|
|
||||||
|
# Upstream to use HTML but input specified as Markdown v1
|
||||||
|
aobj = Apprise()
|
||||||
|
aobj.add('tgram://987654321:abcdefg_hijklmnop/?format=markdown&mdv=1')
|
||||||
|
assert len(aobj) == 1
|
||||||
|
|
||||||
|
# Set an actual title this time
|
||||||
|
title = '# A Great Title'
|
||||||
|
body = '_[Apprise Body Title](http://localhost)_' \
|
||||||
|
' had [a change](http://127.0.0.2)'
|
||||||
|
|
||||||
|
# TEXT forced by the command line, but MARKDOWN specified as
|
||||||
|
# upstream mode
|
||||||
|
assert aobj.notify(
|
||||||
|
title=title, body=body, body_format=NotifyFormat.TEXT)
|
||||||
|
|
||||||
|
# Test our calls
|
||||||
|
assert mock_post.call_count == 2
|
||||||
|
|
||||||
|
assert mock_post.call_args_list[0][0][0] == \
|
||||||
|
'https://api.telegram.org/bot987654321:abcdefg_hijklmnop/getUpdates'
|
||||||
|
assert mock_post.call_args_list[1][0][0] == \
|
||||||
|
'https://api.telegram.org/bot987654321:abcdefg_hijklmnop/sendMessage'
|
||||||
|
|
||||||
|
payload = loads(mock_post.call_args_list[1][1]['data'])
|
||||||
|
|
||||||
|
# Test that everything is escaped properly in a MARKDOWN mode
|
||||||
|
assert payload['text'] == \
|
||||||
|
'# A Great Title\r\n' \
|
||||||
|
'_[Apprise Body Title](http://localhost)_ had ' \
|
||||||
|
'[a change](http://127.0.0.2)'
|
||||||
|
|
||||||
|
# Reset our values
|
||||||
|
mock_post.reset_mock()
|
||||||
|
|
||||||
|
# Upstream to use HTML but input specified as Markdown v2
|
||||||
|
aobj = Apprise()
|
||||||
|
aobj.add('tgram://987654321:abcdefg_hijklmnop/?format=markdown&mdv=2')
|
||||||
|
assert len(aobj) == 1
|
||||||
|
|
||||||
|
# TEXT forced by the command line, but MARKDOWN specified as
|
||||||
|
# upstream mode
|
||||||
|
assert aobj.notify(
|
||||||
|
title=title, body=body, body_format=NotifyFormat.TEXT)
|
||||||
|
|
||||||
|
# Test our calls
|
||||||
|
assert mock_post.call_count == 2
|
||||||
|
|
||||||
|
assert mock_post.call_args_list[0][0][0] == \
|
||||||
|
'https://api.telegram.org/bot987654321:abcdefg_hijklmnop/getUpdates'
|
||||||
|
assert mock_post.call_args_list[1][0][0] == \
|
||||||
|
'https://api.telegram.org/bot987654321:abcdefg_hijklmnop/sendMessage'
|
||||||
|
|
||||||
|
payload = loads(mock_post.call_args_list[1][1]['data'])
|
||||||
|
|
||||||
|
# Test that everything is escaped properly in a MARKDOWN mode
|
||||||
|
assert payload['text'] == \
|
||||||
|
'\\# A Great Title\r\n' \
|
||||||
|
'\\_\\[Apprise Body Title\\]\\(http://localhost\\)\\_ had ' \
|
||||||
|
'\\[a change\\]\\(http://127\\.0\\.0\\.2\\)'
|
||||||
|
|
||||||
|
# Reset our values
|
||||||
|
mock_post.reset_mock()
|
||||||
|
|
||||||
|
# If input is markdown and output is v2, it is expected the user knows
|
||||||
|
# what he is doing... no esaping takes place
|
||||||
|
assert aobj.notify(
|
||||||
|
title=title, body=body, body_format=NotifyFormat.MARKDOWN)
|
||||||
|
|
||||||
|
# Test our calls
|
||||||
|
assert mock_post.call_count == 1
|
||||||
|
|
||||||
|
assert mock_post.call_args_list[0][0][0] == \
|
||||||
|
'https://api.telegram.org/bot987654321:abcdefg_hijklmnop/sendMessage'
|
||||||
|
|
||||||
|
payload = loads(mock_post.call_args_list[0][1]['data'])
|
||||||
|
|
||||||
|
# No escaping in this circumstance
|
||||||
|
assert payload['text'] == \
|
||||||
|
'# A Great Title\r\n' \
|
||||||
|
'_[Apprise Body Title](http://localhost)_ had ' \
|
||||||
|
'[a change](http://127.0.0.2)'
|
||||||
|
|
||||||
|
# Reset our values
|
||||||
|
mock_post.reset_mock()
|
||||||
|
|
||||||
|
# No body format specified at all... user definitely must know what
|
||||||
|
# they are doing... still no escaping in this circumstance
|
||||||
|
assert aobj.notify(title=title, body=body)
|
||||||
|
|
||||||
|
# Test our calls
|
||||||
|
assert mock_post.call_count == 1
|
||||||
|
|
||||||
|
assert mock_post.call_args_list[0][0][0] == \
|
||||||
|
'https://api.telegram.org/bot987654321:abcdefg_hijklmnop/sendMessage'
|
||||||
|
|
||||||
|
payload = loads(mock_post.call_args_list[0][1]['data'])
|
||||||
|
|
||||||
|
# No escaping in this circumstance
|
||||||
|
assert payload['text'] == \
|
||||||
|
'# A Great Title\r\n' \
|
||||||
|
'_[Apprise Body Title](http://localhost)_ had ' \
|
||||||
'[a change](http://127.0.0.2)'
|
'[a change](http://127.0.0.2)'
|
||||||
|
|
||||||
# Reset our values
|
# Reset our values
|
||||||
@ -1118,3 +1248,82 @@ def test_plugin_telegram_threads(mock_post):
|
|||||||
assert payload['message_thread_id'] == 1234
|
assert payload['message_thread_id'] == 1234
|
||||||
|
|
||||||
mock_post.reset_mock()
|
mock_post.reset_mock()
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch('requests.post')
|
||||||
|
def test_plugin_telegram_markdown_v2(mock_post):
|
||||||
|
"""
|
||||||
|
NotifyTelegram() MarkdownV2
|
||||||
|
|
||||||
|
"""
|
||||||
|
# Prepare Mock
|
||||||
|
mock_post.return_value = requests.Request()
|
||||||
|
mock_post.return_value.status_code = requests.codes.ok
|
||||||
|
|
||||||
|
# Simple success response
|
||||||
|
mock_post.return_value.content = dumps({
|
||||||
|
"ok": True,
|
||||||
|
"result": [{
|
||||||
|
"update_id": 645421321,
|
||||||
|
"message": {
|
||||||
|
"message_id": 2,
|
||||||
|
"from": {
|
||||||
|
"id": 532389719,
|
||||||
|
"is_bot": False,
|
||||||
|
"first_name": "Chris",
|
||||||
|
"language_code": "en-US"
|
||||||
|
},
|
||||||
|
"chat": {
|
||||||
|
"id": 532389719,
|
||||||
|
"first_name": "Chris",
|
||||||
|
"type": "private"
|
||||||
|
},
|
||||||
|
"date": 1519694394,
|
||||||
|
"text": "/start",
|
||||||
|
"entities": [{
|
||||||
|
"offset": 0,
|
||||||
|
"length": 6,
|
||||||
|
"type": "bot_command",
|
||||||
|
}],
|
||||||
|
}},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
aobj = Apprise()
|
||||||
|
aobj.add('tgram://123456789:abcdefg_hijklmnop/?mdv=2&format=markdown')
|
||||||
|
assert len(aobj) == 1
|
||||||
|
assert isinstance(aobj[0], NotifyTelegram)
|
||||||
|
|
||||||
|
body = '# my message\r\n## more content\r\n\\# already escaped hashtag'
|
||||||
|
|
||||||
|
# Test with body format set to markdown
|
||||||
|
assert aobj.notify(body=body, body_format=NotifyFormat.TEXT)
|
||||||
|
|
||||||
|
# 1 call to look up bot owner, and second for notification
|
||||||
|
assert mock_post.call_count == 2
|
||||||
|
payload = loads(mock_post.call_args_list[1][1]['data'])
|
||||||
|
|
||||||
|
# Our content is escapped properly
|
||||||
|
assert payload['text'] == '\\# my message\r\n' \
|
||||||
|
'\\#\\# more content\r\n\\# already escaped hashtag'
|
||||||
|
|
||||||
|
mock_post.reset_mock()
|
||||||
|
|
||||||
|
# We'll iterate over all of the bad unsupported characters
|
||||||
|
mdv2_unsupported = (
|
||||||
|
'_', '*', '[', ']', '(', ')', '~', '`', '>', '#', '+', '=', '|', '{',
|
||||||
|
'}', '.', '!', '-')
|
||||||
|
|
||||||
|
for c in mdv2_unsupported:
|
||||||
|
body = f'bad character: {c}, and already escapped \\{c}'
|
||||||
|
|
||||||
|
# Test with body format set to markdown
|
||||||
|
assert aobj.notify(body=body, body_format=NotifyFormat.TEXT)
|
||||||
|
assert mock_post.call_count == 1
|
||||||
|
payload = loads(mock_post.call_args_list[0][1]['data'])
|
||||||
|
|
||||||
|
# Our content is escapped properly
|
||||||
|
assert payload['text'] == \
|
||||||
|
f'bad character: \\{c}, and already escapped \\{c}'
|
||||||
|
|
||||||
|
mock_post.reset_mock()
|
||||||
|
Loading…
Reference in New Issue
Block a user