Apprise HTML/MARKDOWN/TEXT Translation Handling Refactoring (#575)

This commit is contained in:
Chris Caron 2022-04-28 19:14:49 -04:00 committed by GitHub
parent 344b2153e0
commit 4a87d45879
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 378 additions and 224 deletions

View File

@ -43,6 +43,7 @@ from .AppriseLocale import AppriseLocale
from .config.ConfigBase import ConfigBase
from .plugins.NotifyBase import NotifyBase
from . import plugins
from . import __version__
@ -542,11 +543,23 @@ class Apprise(object):
# determined we need to notify the service it's associated with
if server.notify_format not in conversion_body_map:
# Perform Conversion
(conversion_title_map[server.notify_format],
conversion_body_map[server.notify_format]) = \
conversion_body_map[server.notify_format] = \
convert_between(
body_format, server.notify_format, body=body,
title=title, title_format=server.title_format)
body_format, server.notify_format, content=body)
# Prepare our title
conversion_title_map[server.notify_format] = \
'' if not title else title
# Tidy Title IF required (hence it will become part of the
# body)
if server.title_maxlen <= 0 and \
conversion_title_map[server.notify_format]:
conversion_title_map[server.notify_format] = \
convert_between(
body_format, server.notify_format,
content=conversion_title_map[server.notify_format])
if interpret_escapes:
#
@ -587,14 +600,6 @@ class Apprise(object):
if six.PY2:
# Python 2.7 strings must be encoded as utf-8 for
# consistency across all platforms
if conversion_title_map[server.notify_format] and \
isinstance(
conversion_title_map[server.notify_format],
unicode): # noqa: F821
conversion_title_map[server.notify_format] = \
conversion_title_map[server.notify_format]\
.encode('utf-8')
if conversion_body_map[server.notify_format] and \
isinstance(
conversion_body_map[server.notify_format],
@ -603,12 +608,21 @@ class Apprise(object):
conversion_body_map[server.notify_format]\
.encode('utf-8')
if conversion_title_map[server.notify_format] and \
isinstance(
conversion_title_map[server.notify_format],
unicode): # noqa: F821
conversion_title_map[server.notify_format] = \
conversion_title_map[server.notify_format]\
.encode('utf-8')
yield handler(
server,
body=conversion_body_map[server.notify_format],
title=conversion_title_map[server.notify_format],
notify_type=notify_type,
attach=attach
attach=attach,
body_format=body_format,
)
def details(self, lang=None, show_requirements=False, show_disabled=False):

View File

@ -28,6 +28,7 @@ import re
import six
from markdown import markdown
from .common import NotifyFormat
from .URLBase import URLBase
if six.PY2:
from HTMLParser import HTMLParser
@ -36,13 +37,12 @@ else:
from html.parser import HTMLParser
def convert_between(from_format, to_format, body, title=None,
title_format=NotifyFormat.TEXT):
def convert_between(from_format, to_format, content):
"""
Converts between different notification formats. If no conversion exists,
Converts between different suported formats. If no conversion exists,
or the selected one fails, the original text will be returned.
This function returns a tuple as (title, body)
This function returns the content translated (if required)
"""
converters = {
@ -53,106 +53,39 @@ def convert_between(from_format, to_format, body, title=None,
(NotifyFormat.HTML, NotifyFormat.MARKDOWN): html_to_text,
}
if NotifyFormat.MARKDOWN in (from_format, to_format):
# Tidy any exising pre-formating configuration
title = '' if not title else title.lstrip('\r\n \t\v\b*#-')
else:
title = '' if not title else title
convert = converters.get((from_format, to_format))
title, body = convert(title=title, body=body, title_format=title_format) \
if convert is not None else (title, body)
return (title, body)
return convert(content) if convert else content
def markdown_to_html(body, title=None, title_format=None):
def markdown_to_html(content):
"""
Handle Markdown conversions
Converts specified content from markdown to HTML.
"""
if title_format == NotifyFormat.HTML and title:
# perform conversion if otherwise told to do so
title = markdown(title)
return (
# Title
'' if not title else title,
# Body
markdown(body),
)
return markdown(content)
def text_to_html(body, title=None, title_format=None):
def text_to_html(content):
"""
Converts a notification body from plain text to HTML.
Converts specified content from plain text to HTML.
"""
# Basic TEXT to HTML format map; supports keys only
re_map = {
# Support Ampersand
r'&': '&amp;',
# Spaces to &nbsp; for formatting purposes since
# multiple spaces are treated as one an this may
# not be the callers intention
r' ': '&nbsp;',
# Tab support
r'\t': '&nbsp;&nbsp;&nbsp;',
# Greater than and Less than Characters
r'>': '&gt;',
r'<': '&lt;',
}
# Compile our map
re_table = re.compile(
r'(' + '|'.join(
map(re.escape, re_map.keys())) + r')',
re.IGNORECASE,
)
# Execute our map against our body in addition to
# swapping out new lines and replacing them with <br/>
return (
# Title; swap whitespace with space
'' if not title else re.sub(
r'[\r\n]+', ' ', re_table.sub(
lambda x: re_map[x.group()], title)),
# Body Formatting
re.sub(
r'\r*\n', '<br/>\n', re_table.sub(
lambda x: re_map[x.group()], body)))
return URLBase.escape_html(content)
def html_to_text(body, title=None, title_format=None):
def html_to_text(content):
"""
Converts a notification body from HTML to plain text.
Converts a content from HTML to plain text.
"""
parser = HTMLConverter()
if six.PY2:
# Python 2.7 requires an additional parsing to un-escape characters
body = parser.unescape(body)
content = parser.unescape(content)
if title:
if six.PY2:
# Python 2.7 requires an additional parsing to un-escape characters
title = parser.unescape(title)
parser.feed(title)
parser.close()
title = parser.converted
parser.feed(body)
parser.feed(content)
parser.close()
body = parser.converted
return ('' if not title else title, body)
return parser.converted
class HTMLConverter(HTMLParser, object):

View File

@ -121,13 +121,6 @@ class NotifyBase(BASE_OBJECT):
# automatically placed into the body
title_maxlen = 250
# Set this to HTML for services that support the conversion of HTML in
# the title. For example; services like Telegram support HTML in the
# title, however services like Email (where this goes in the Subject line)
# do not (but the body does). By default we do not convert titles but
# allow those who wish to over-ride this to do so.
title_format = NotifyFormat.TEXT
# Set the maximum line count; if this is set to anything larger then zero
# the message (prior to it being sent) will be truncated to this number
# of lines. Setting this to zero disables this feature.
@ -272,7 +265,7 @@ class NotifyBase(BASE_OBJECT):
)
def notify(self, body, title=None, notify_type=NotifyType.INFO,
overflow=None, attach=None, **kwargs):
overflow=None, attach=None, body_format=None, **kwargs):
"""
Performs notification
@ -298,18 +291,22 @@ class NotifyBase(BASE_OBJECT):
title = '' if not title else title
# Apply our overflow (if defined)
for chunk in self._apply_overflow(body=body, title=title,
overflow=overflow):
for chunk in self._apply_overflow(
body=body, title=title, overflow=overflow,
body_format=body_format):
# Send notification
if not self.send(body=chunk['body'], title=chunk['title'],
notify_type=notify_type, attach=attach):
notify_type=notify_type, attach=attach,
body_format=body_format):
# Toggle our return status flag
return False
return True
def _apply_overflow(self, body, title=None, overflow=None):
def _apply_overflow(self, body, title=None, overflow=None,
body_format=None):
"""
Takes the message body and title as input. This function then
applies any defined overflow restrictions associated with the
@ -341,18 +338,24 @@ class NotifyBase(BASE_OBJECT):
overflow = self.overflow_mode
if self.title_maxlen <= 0 and len(title) > 0:
if self.notify_format == NotifyFormat.MARKDOWN:
# Content is appended to body as markdown
body = '**{}**\r\n{}'.format(title, body)
elif self.notify_format == NotifyFormat.HTML:
if self.notify_format == NotifyFormat.HTML:
# Content is appended to body as html
body = '<{open_tag}>{title}</{close_tag}>' \
'<br />\r\n{body}'.format(
open_tag=self.default_html_tag_id,
title=self.escape_html(title),
title=title,
close_tag=self.default_html_tag_id,
body=body)
elif self.notify_format == NotifyFormat.MARKDOWN and \
body_format == NotifyFormat.TEXT:
# Content is appended to body as markdown
title = title.lstrip('\r\n \t\v\f#-')
if title:
# Content is appended to body as text
body = '# {}\r\n{}'.format(title, body)
else:
# Content is appended to body as text
body = '{}\r\n{}'.format(title, body)

View File

@ -105,8 +105,8 @@ class NotifyTelegram(NotifyBase):
# The maximum allowable characters allowed in the body per message
body_maxlen = 4096
# Allow the title to support HTML character sets
title_format = NotifyFormat.HTML
# Title is to be part of body
title_maxlen = 0
# Telegram is limited to sending a maximum of 100 requests per second.
request_rate_per_sec = 0.001
@ -173,6 +173,49 @@ class NotifyTelegram(NotifyBase):
},
)
# Telegram's HTML support doesn't like having HTML escaped
# characters passed into it. to handle this situation, we need to
# search the body for these sequences and convert them to the
# output the user expected
__telegram_escape_html_dict = {
# New Lines
re.compile(r'<\s*/?br\s*/?>\r*\n?', re.I): '\r\n',
re.compile(r'<\s*/(br|p|div|li)[^>]*>\r*\n?', re.I): '\r\n',
# The following characters can be altered to become supported
re.compile(r'<\s*pre[^>]*>', re.I): '<code>',
re.compile(r'<\s*/pre[^>]*>', re.I): '</code>',
# the following tags are not supported
re.compile(
r'<\s*(br|p|div|span|body|script|meta|html|font'
r'|label|iframe|li|ol|ul|source|script)[^>]*>', re.I): '',
re.compile(
r'<\s*/(span|body|script|meta|html|font'
r'|label|iframe|ol|ul|source|script)[^>]*>', re.I): '',
# Italic
re.compile(r'<\s*(caption|em)[^>]*>', re.I): '<i>',
re.compile(r'<\s*/(caption|em)[^>]*>', re.I): '</i>',
# Bold
re.compile(r'<\s*(h[1-6]|title|strong)[^>]*>', re.I): '<b>',
re.compile(r'<\s*/(h[1-6]|title|strong)[^>]*>', re.I): '</b>',
# HTML Spaces (&nbsp;) and tabs (&emsp;) aren't supported
# See https://core.telegram.org/bots/api#html-style
re.compile(r'\&nbsp;?', re.I): ' ',
# Tabs become 3 spaces
re.compile(r'\&emsp;?', re.I): ' ',
# Some characters get re-escaped by the Telegram upstream
# service so we need to convert these back,
re.compile(r'\&apos;?', re.I): '\'',
re.compile(r'\&quot;?', re.I): '"',
}
# Define our template tokens
template_tokens = dict(NotifyBase.template_tokens, **{
'bot_token': {
@ -505,7 +548,7 @@ class NotifyTelegram(NotifyBase):
return 0
def send(self, body, title='', notify_type=NotifyType.INFO, attach=None,
**kwargs):
body_format=None, **kwargs):
"""
Perform Telegram Notification
"""
@ -548,93 +591,43 @@ class NotifyTelegram(NotifyBase):
if self.notify_format == NotifyFormat.MARKDOWN:
payload['parse_mode'] = 'MARKDOWN'
payload['text'] = '{}{}'.format(
'# {}\r\n'.format(title) if title else '',
body,
)
payload['text'] = body
else: # HTML
elif self.notify_format == NotifyFormat.HTML:
# Use Telegram's HTML mode
payload['parse_mode'] = 'HTML'
for r, v in self.__telegram_escape_html_dict.items():
body = r.sub(v, body, re.I)
# Telegram's HTML support doesn't like having HTML escaped
# characters passed into it. to handle this situation, we need to
# search the body for these sequences and convert them to the
# output the user expected
telegram_escape_html_dict = {
# HTML Spaces (&nbsp;) and tabs (&emsp;) aren't supported
# See https://core.telegram.org/bots/api#html-style
r'\&nbsp;?': ' ',
# Prepare our payload based on HTML or TEXT
payload['text'] = body
# Tabs become 3 spaces
r'\&emsp;?': ' ',
# else: # self.notify_format == NotifyFormat.TEXT:
# # Use Telegram's HTML mode
# payload['parse_mode'] = 'HTML'
# Some characters get re-escaped by the Telegram upstream
# service so we need to convert these back,
r'\&apos;?': '\'',
r'\&quot;?': '"',
# # Further html escaping required...
# telegram_escape_text_dict = {
# # We need to escape characters that conflict with html
# # entity blocks (< and >) when displaying text
# r'>': '&gt;',
# r'<': '&lt;',
# r'\&': '&amp;',
# }
# the following tags are not supported
r'<[ \t]*/?(br|p|div|span|body|script|meta|html|font'
r'|label|iframe|li|ol|ul)[^>]*>': '',
# # Create a regular expression from the dictionary keys
# text_regex = re.compile("(%s)" % "|".join(
# map(re.escape, telegram_escape_text_dict.keys())).lower(),
# re.I)
# The following characters can be altered to become supported
r'<[ \t]*pre[^>]*>': '<code>',
r'<[ \t]*/pre[^>]*>': '</code>',
# # For each match, look-up corresponding value in dictionary
# body = text_regex.sub( # pragma: no branch
# lambda mo: telegram_escape_text_dict[
# mo.string[mo.start():mo.end()]], body)
# Bold
r'<[ \t]*(h[0-9]+|title|strong)[^>]*>': '<b>',
r'<[ \t]*/(h[0-9]+|title|strong)[^>]*>': '</b>',
# Italic
r'<[ \t]*(caption|em)[^>]*>': '<i>',
r'<[ \t]*/(caption|em)[^>]*>': '</i>',
}
for k, v in telegram_escape_html_dict.items():
body = re.sub(k, v, body, re.I)
if title:
title = re.sub(k, v, title, re.I)
# prepare our payload based on HTML or TEXT
payload['text'] = '{}{}'.format(
'<b>{}</b>\r\n'.format(title) if title else '',
body,
)
else: # self.notify_format == NotifyFormat.TEXT:
# Use Telegram's HTML mode
payload['parse_mode'] = 'HTML'
# Further html escaping required...
telegram_escape_text_dict = {
# We need to escape characters that conflict with html
# entity blocks (< and >) when displaying text
r'>': '&gt;',
r'<': '&lt;',
}
# Create a regular expression from the dictionary keys
text_regex = re.compile("(%s)" % "|".join(
map(re.escape, telegram_escape_text_dict.keys())).lower(),
re.I)
# For each match, look-up corresponding value in dictionary
body = text_regex.sub( # pragma: no branch
lambda mo: telegram_escape_text_dict[
mo.string[mo.start():mo.end()]], body)
if title:
# For each match, look-up corresponding value in dictionary
title = text_regex.sub( # pragma: no branch
lambda mo: telegram_escape_text_dict[
mo.string[mo.start():mo.end()]], title)
# prepare our payload based on HTML or TEXT
payload['text'] = '{}{}'.format(
'<b>{}</b>\r\n'.format(title) if title else '',
body,
)
# # prepare our payload based on HTML or TEXT
# payload['text'] = body
# Create a copy of the chat_ids list
targets = list(self.targets)

View File

@ -702,7 +702,7 @@ def parse_url(url, default_schema='http', verify_host=True, strict_port=False):
# Port Parsing
pmatch = re.search(
r'^(?P<host>([[0-9a-f:]+]|[^:]+)):(?P<port>[^:]*)$',
r'^(?P<host>(\[[0-9a-f:]+\]|[^:]+)):(?P<port>[^:]*)$',
result['host'])
if pmatch:

View File

@ -32,7 +32,7 @@ import logging
logging.disable(logging.CRITICAL)
def test_html_to_text():
def test_conversion_html_to_text():
"""conversion: Test HTML to plain text
"""
@ -40,7 +40,7 @@ def test_html_to_text():
"""
A function to simply html conversion tests
"""
return convert_between(NotifyFormat.HTML, NotifyFormat.TEXT, body)[1]
return convert_between(NotifyFormat.HTML, NotifyFormat.TEXT, body)
assert to_html("No HTML code here.") == "No HTML code here."
@ -134,3 +134,16 @@ def test_html_to_text():
with pytest.raises(TypeError):
# Invalid input
assert to_html(object)
def test_conversion_text_to():
"""conversion: Test Text to all types
"""
response = convert_between(
NotifyFormat.TEXT, NotifyFormat.HTML,
"<title>Test Message</title><body>Body</body>")
assert response == \
'&lt;title&gt;Test&nbsp;Message&lt;/title&gt;&lt;body&gt;Body&lt;'\
'/body&gt;'

View File

@ -311,10 +311,6 @@ def test_plugin_telegram_general(mock_post):
# ensures our plugin inheritance is working properly
assert obj.body_maxlen == plugins.NotifyTelegram.body_maxlen
# We don't override the title maxlen so we should be set to the same
# as our parent class in this case
assert obj.title_maxlen == plugins.NotifyBase.title_maxlen
# This tests erroneous messages involving multiple chat ids
assert obj.notify(
body='body', title='title', notify_type=NotifyType.INFO) is False
@ -407,7 +403,7 @@ def test_plugin_telegram_general(mock_post):
# Test our payload
assert payload['text'] == \
'<b>special characters</b>\r\n\'"This can\'t\t\r\nfail us"\''
'<b>special characters</b>\r\n\'"This can\'t\t\r\nfail us"\'\r\n'
# Test sending attachments
attach = AppriseAttachment(os.path.join(TEST_VAR_DIR, 'apprise-test.gif'))
@ -629,10 +625,11 @@ def test_plugin_telegram_formating_py3(mock_post):
# Test that everything is escaped properly in a TEXT mode
assert payload['text'] == \
'<b>🚨 Change detected&nbsp;for&nbsp;&lt;i&gt;Apprise&nbsp;Test' \
'&nbsp;Title&lt;/i&gt;</b>\r\n&lt;a href="http://localhost"&gt;' \
'&lt;i&gt;Apprise Body&nbsp;Title&lt;/i&gt;&lt;/a&gt;&nbsp;had' \
'&nbsp;&lt;a&nbsp;href="http://127.0.0.1"&gt;a&nbsp;change&lt;/a&gt;'
'<b>🚨 Change detected&nbsp;for&nbsp;&lt;i&gt;Apprise&nbsp;' \
'Test&nbsp;Title&lt;/i&gt;</b>\r\n&lt;a&nbsp;href=' \
'"http://localhost"&gt;&lt;i&gt;Apprise&nbsp;Body&nbsp;Title&lt;' \
'/i&gt;&lt;/a&gt;&nbsp;had&nbsp;&lt;a&nbsp;href=&quot;http://' \
'127.0.0.1&quot;&gt;a&nbsp;change&lt;/a&gt;'
# Reset our values
mock_post.reset_mock()
@ -699,7 +696,12 @@ def test_plugin_telegram_formating_py3(mock_post):
aobj.add('tgram://987654321:abcdefg_hijklmnop/?format=html')
assert len(aobj) == 1
# HTML forced by the command line, but MARKDOWN spacified as
# Now test our MARKDOWN Handling
title = '# 🚨 Another Change detected for _Apprise Test Title_'
body = '_[Apprise Body Title](http://localhost)_' \
' had [a change](http://127.0.0.2)'
# HTML forced by the command line, but MARKDOWN specified as
# upstream mode
assert aobj.notify(
title=title, body=body, body_format=NotifyFormat.MARKDOWN)
@ -716,9 +718,79 @@ def test_plugin_telegram_formating_py3(mock_post):
# Test that everything is escaped properly in a HTML mode
assert payload['text'] == \
'<b>🚨 Change detected for <i>Apprise Test Title</i></b>\r\n<i>' \
'<a href="http://localhost">Apprise Body Title</a></i> ' \
'had <a href="http://127.0.0.1">a change</a>'
'<b><b>🚨 Another Change detected for <i>Apprise Test Title</i>' \
'</b></b>\r\n<i><a href="http://localhost">Apprise Body Title</a>' \
'</i> had <a href="http://127.0.0.2">a change</a>\r\n'
# Now we'll test an edge case where a title was defined, but after
# processing it, it was determiend there really wasn't anything there
# at all at the end of the day.
# Reset our values
mock_post.reset_mock()
# Upstream to use HTML but input specified as Markdown
aobj = Apprise()
aobj.add('tgram://987654321:abcdefg_hijklmnop/?format=markdown')
assert len(aobj) == 1
# Now test our MARKDOWN Handling (no title defined... not really anyway)
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
# 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 HTML mode
assert payload['text'] == \
'_[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
aobj = Apprise()
aobj.add('tgram://987654321:abcdefg_hijklmnop/?format=markdown')
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
# 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 HTML mode
assert payload['text'] == \
'# A Great Title\r\n_[Apprise Body Title](http://localhost)_ had ' \
'[a change](http://127.0.0.2)'
@pytest.mark.skipif(sys.version_info.major >= 3, reason="Requires Python 2.x+")
@ -809,11 +881,11 @@ def test_plugin_telegram_formating_py2(mock_post):
# Test that everything is escaped properly in a TEXT mode
assert payload['text'].encode('utf-8') == \
'<b>\xf0\x9f\x9a\xa8 Change detected&nbsp;for&nbsp;' \
'&lt;i&gt;Apprise&nbsp;Test&nbsp;Title&lt;/i&gt;</b>\r\n' \
'&lt;a href="http://localhost"&gt;&lt;i&gt;Apprise Body&nbsp;' \
'Title&lt;/i&gt;&lt;/a&gt;&nbsp;had&nbsp;&lt;a&nbsp;' \
'href="http://127.0.0.1"&gt;a&nbsp;change&lt;/a&gt;'
'<b>\xf0\x9f\x9a\xa8 Change detected&nbsp;for&nbsp;&lt;i&gt;' \
'Apprise&nbsp;Test&nbsp;Title&lt;/i&gt;</b>\r\n&lt;a&nbsp;' \
'href="http://localhost"&gt;&lt;i&gt;Apprise&nbsp;Body&nbsp;' \
'Title&lt;/i&gt;&lt;/a&gt;&nbsp;had&nbsp;&lt;a&nbsp;href=&quot;' \
'http://127.0.0.1&quot;&gt;a&nbsp;change&lt;/a&gt;'
# Reset our values
mock_post.reset_mock()
@ -880,7 +952,7 @@ def test_plugin_telegram_formating_py2(mock_post):
aobj.add('tgram://987654321:abcdefg_hijklmnop/?format=html')
assert len(aobj) == 1
# HTML forced by the command line, but MARKDOWN spacified as
# HTML forced by the command line, but MARKDOWN specified as
# upstream mode
assert aobj.notify(
title=title, body=body, body_format=NotifyFormat.MARKDOWN)
@ -897,9 +969,10 @@ def test_plugin_telegram_formating_py2(mock_post):
# Test that everything is escaped properly in a HTML mode
assert payload['text'].encode('utf-8') == \
'<b>\xf0\x9f\x9a\xa8 Change detected for <i>Apprise Test Title</i>' \
'</b>\r\n<i><a href="http://localhost">Apprise Body Title</a></i> ' \
'had <a href="http://127.0.0.1">a change</a>'
'<b><b>\xf0\x9f\x9a\xa8 Change detected for ' \
'<i>Apprise Test Title</i></b></b>\r\n<i>' \
'<a href="http://localhost">Apprise Body Title</a>'\
'</i> had <a href="http://127.0.0.1">a change</a>\r\n'
# Reset our values
mock_post.reset_mock()
@ -951,6 +1024,76 @@ def test_plugin_telegram_formating_py2(mock_post):
'\xd7\xa0\xd7\xa4\xd7\x9c\xd7\x90\xd7\x94</b>\r\n[_[\xd7\x96\xd7\x95 '\
'\xd7\x94\xd7\x95\xd7\x93\xd7\xa2\xd7\x94](http://localhost)_'
# Now we'll test an edge case where a title was defined, but after
# processing it, it was determiend there really wasn't anything there
# at all at the end of the day.
# Reset our values
mock_post.reset_mock()
# Upstream to use HTML but input specified as Markdown
aobj = Apprise()
aobj.add('tgram://987654321:abcdefg_hijklmnop/?format=markdown')
assert len(aobj) == 1
# Now test our MARKDOWN Handling (no title defined... not really anyway)
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
# 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 HTML mode
assert payload['text'] == \
'_[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
aobj = Apprise()
aobj.add('tgram://987654321:abcdefg_hijklmnop/?format=markdown')
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
# 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 HTML mode
assert payload['text'] == \
'# A Great Title\r\n_[Apprise Body Title](http://localhost)_ had ' \
'[a change](http://127.0.0.2)'
@mock.patch('requests.post')
def test_plugin_telegram_html_formatting(mock_post):
@ -1020,8 +1163,8 @@ def test_plugin_telegram_html_formatting(mock_post):
# Test that everything is escaped properly in a HTML mode
assert payload['text'] == \
'<b><b>\'information\'</b></b>\r\n<i>"This is in Italic"</i>' \
'<b> Headings are dropped and converted to bold</b>'
'<b><b>\'information\'</b></b>\r\n<i>"This is in Italic"' \
'</i>\r\n<b> Headings are dropped and converted to bold</b>'
mock_post.reset_mock()
@ -1034,6 +1177,7 @@ def test_plugin_telegram_html_formatting(mock_post):
assert payload['text'] == \
'<b>&lt;title&gt;&amp;apos;information&amp;apos&lt;/title&gt;</b>' \
'\r\n&lt;em&gt;&amp;quot;This is in&nbsp;Italic&amp;quot&lt;/em&gt;' \
'&lt;br/&gt;&lt;h5&gt;&amp;emsp;&amp;emspHeadings&amp;nbsp;are' \
'&nbsp;dropped&nbsp;and&amp;nbspconverted&nbsp;to&nbsp;bold&lt;/h5&gt;'
'\r\n&lt;em&gt;&amp;quot;This is in&nbsp;Italic&amp;quot&lt;/em' \
'&gt;&lt;br/&gt;&lt;h5&gt;&amp;emsp;&amp;emspHeadings&amp;nbsp;' \
'are&nbsp;dropped&nbsp;and&amp;nbspconverted&nbsp;to&nbsp;bold&lt;' \
'/h5&gt;'

View File

@ -386,3 +386,57 @@ def test_notify_overflow_split():
_body = chunk.get('body')
assert bulk[offset: len(_body) + offset] == _body
offset += len(_body)
def test_notify_overflow_general():
"""
API: Overflow General Testing
"""
#
# A little preparation
#
# Disable Throttling to speed testing
plugins.NotifyBase.request_rate_per_sec = 0
#
# First Test: Truncated Title
#
class TestMarkdownNotification(NotifyBase):
# Force our title to wrap
title_maxlen = 0
# Default Notify Format
notify_format = NotifyFormat.MARKDOWN
def __init__(self, *args, **kwargs):
super(TestMarkdownNotification, self).__init__(**kwargs)
def notify(self, *args, **kwargs):
# Pretend everything is okay
return True
# Load our object
obj = TestMarkdownNotification()
assert obj is not None
# A bad header
title = " # "
body = "**Test Body**"
chunks = obj._apply_overflow(body=body, title=title)
assert len(chunks) == 1
# whitspace is trimmed
assert '#\r\n**Test Body**' == chunks[0].get('body')
assert chunks[0].get('title') == ""
# If we know our input is text however, we perform manipulation
chunks = obj._apply_overflow(
body=body, title=title, body_format=NotifyFormat.TEXT)
assert len(chunks) == 1
# Our title get's stripped off since it's not of valid markdown
assert body == chunks[0].get('body')
assert chunks[0].get('title') == ""