diff --git a/apprise/Apprise.py b/apprise/Apprise.py index 1c0f1922..30c93653 100644 --- a/apprise/Apprise.py +++ b/apprise/Apprise.py @@ -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): diff --git a/apprise/conversion.py b/apprise/conversion.py index 827b65a7..bfd9a644 100644 --- a/apprise/conversion.py +++ b/apprise/conversion.py @@ -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'&': '&', - - # Spaces to   for formatting purposes since - # multiple spaces are treated as one an this may - # not be the callers intention - r' ': ' ', - - # Tab support - r'\t': '   ', - - # Greater than and Less than Characters - r'>': '>', - r'<': '<', - } - - # 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
- 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', '
\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): diff --git a/apprise/plugins/NotifyBase.py b/apprise/plugins/NotifyBase.py index ea5b7c56..54e89790 100644 --- a/apprise/plugins/NotifyBase.py +++ b/apprise/plugins/NotifyBase.py @@ -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}' \ '
\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) diff --git a/apprise/plugins/NotifyTelegram.py b/apprise/plugins/NotifyTelegram.py index fea3180f..23552eb6 100644 --- a/apprise/plugins/NotifyTelegram.py +++ b/apprise/plugins/NotifyTelegram.py @@ -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): '', + re.compile(r'<\s*/pre[^>]*>', re.I): '', + + # 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): '', + re.compile(r'<\s*/(caption|em)[^>]*>', re.I): '', + + # Bold + re.compile(r'<\s*(h[1-6]|title|strong)[^>]*>', re.I): '', + re.compile(r'<\s*/(h[1-6]|title|strong)[^>]*>', re.I): '', + + # HTML Spaces ( ) and tabs ( ) aren't supported + # See https://core.telegram.org/bots/api#html-style + re.compile(r'\ ?', re.I): ' ', + + # Tabs become 3 spaces + re.compile(r'\ ?', re.I): ' ', + + # Some characters get re-escaped by the Telegram upstream + # service so we need to convert these back, + re.compile(r'\'?', re.I): '\'', + re.compile(r'\"?', 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 ( ) and tabs ( ) aren't supported - # See https://core.telegram.org/bots/api#html-style - r'\ ?': ' ', + # Prepare our payload based on HTML or TEXT + payload['text'] = body - # Tabs become 3 spaces - r'\ ?': ' ', + # 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'\'?': '\'', - r'\"?': '"', + # # Further html escaping required... + # telegram_escape_text_dict = { + # # We need to escape characters that conflict with html + # # entity blocks (< and >) when displaying text + # r'>': '>', + # r'<': '<', + # r'\&': '&', + # } - # 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[^>]*>': '', - r'<[ \t]*/pre[^>]*>': '', + # # 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)[^>]*>': '', - r'<[ \t]*/(h[0-9]+|title|strong)[^>]*>': '', - - # Italic - r'<[ \t]*(caption|em)[^>]*>': '', - r'<[ \t]*/(caption|em)[^>]*>': '', - } - - 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( - '{}\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'>': '>', - r'<': '<', - } - - # 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( - '{}\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) diff --git a/apprise/utils.py b/apprise/utils.py index e7847f6e..6bf6df23 100644 --- a/apprise/utils.py +++ b/apprise/utils.py @@ -702,7 +702,7 @@ def parse_url(url, default_schema='http', verify_host=True, strict_port=False): # Port Parsing pmatch = re.search( - r'^(?P([[0-9a-f:]+]|[^:]+)):(?P[^:]*)$', + r'^(?P(\[[0-9a-f:]+\]|[^:]+)):(?P[^:]*)$', result['host']) if pmatch: diff --git a/test/test_conversion.py b/test/test_conversion.py index a67de5b1..c6ab6d8a 100644 --- a/test/test_conversion.py +++ b/test/test_conversion.py @@ -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, + "Test MessageBody") + + assert response == \ + '<title>Test Message</title><body>Body<'\ + '/body>' diff --git a/test/test_plugin_telegram.py b/test/test_plugin_telegram.py index b65cffc4..a9f191b6 100644 --- a/test/test_plugin_telegram.py +++ b/test/test_plugin_telegram.py @@ -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'] == \ - 'special characters\r\n\'"This can\'t\t\r\nfail us"\'' + 'special characters\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'] == \ - '🚨 Change detected for <i>Apprise Test' \ - ' Title</i>\r\n<a href="http://localhost">' \ - '<i>Apprise Body Title</i></a> had' \ - ' <a href="http://127.0.0.1">a change</a>' + '🚨 Change detected for <i>Apprise ' \ + 'Test Title</i>\r\n<a href=' \ + '"http://localhost"><i>Apprise Body Title<' \ + '/i></a> had <a href="http://' \ + '127.0.0.1">a change</a>' # 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'] == \ - '🚨 Change detected for Apprise Test Title\r\n' \ - 'Apprise Body Title ' \ - 'had a change' + '🚨 Another Change detected for Apprise Test Title' \ + '\r\nApprise Body Title' \ + ' had a change\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') == \ - '\xf0\x9f\x9a\xa8 Change detected for ' \ - '<i>Apprise Test Title</i>\r\n' \ - '<a href="http://localhost"><i>Apprise Body ' \ - 'Title</i></a> had <a ' \ - 'href="http://127.0.0.1">a change</a>' + '\xf0\x9f\x9a\xa8 Change detected for <i>' \ + 'Apprise Test Title</i>\r\n<a ' \ + 'href="http://localhost"><i>Apprise Body ' \ + 'Title</i></a> had <a href="' \ + 'http://127.0.0.1">a change</a>' # 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') == \ - '\xf0\x9f\x9a\xa8 Change detected for Apprise Test Title' \ - '\r\nApprise Body Title ' \ - 'had a change' + '\xf0\x9f\x9a\xa8 Change detected for ' \ + 'Apprise Test Title\r\n' \ + 'Apprise Body Title'\ + ' had a change\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\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'] == \ - '\'information\'\r\n"This is in Italic"' \ - ' Headings are dropped and converted to bold' + '\'information\'\r\n"This is in Italic"' \ + '\r\n Headings are dropped and converted to bold' mock_post.reset_mock() @@ -1034,6 +1177,7 @@ def test_plugin_telegram_html_formatting(mock_post): assert payload['text'] == \ '<title>&apos;information&apos</title>' \ - '\r\n<em>&quot;This is in Italic&quot</em>' \ - '<br/><h5>&emsp;&emspHeadings&nbsp;are' \ - ' dropped and&nbspconverted to bold</h5>' + '\r\n<em>&quot;This is in Italic&quot</em' \ + '><br/><h5>&emsp;&emspHeadings&nbsp;' \ + 'are dropped and&nbspconverted to bold<' \ + '/h5>' diff --git a/test/test_rest_plugins.py b/test/test_rest_plugins.py index f6a52dfe..c709c2dc 100644 --- a/test/test_rest_plugins.py +++ b/test/test_rest_plugins.py @@ -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') == ""