mirror of
https://github.com/caronc/apprise.git
synced 2024-11-25 01:24:03 +01:00
Apprise HTML/MARKDOWN/TEXT Translation Handling Refactoring (#575)
This commit is contained in:
parent
344b2153e0
commit
4a87d45879
@ -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):
|
||||
|
@ -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 <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):
|
||||
|
@ -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)
|
||||
|
@ -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 ( ) 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[^>]*>': '<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'>': '>',
|
||||
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(
|
||||
'<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)
|
||||
|
@ -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:
|
||||
|
@ -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 == \
|
||||
'<title>Test Message</title><body>Body<'\
|
||||
'/body>'
|
||||
|
@ -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 for <i>Apprise Test' \
|
||||
' Title</i></b>\r\n<a href="http://localhost">' \
|
||||
'<i>Apprise Body Title</i></a> had' \
|
||||
' <a href="http://127.0.0.1">a change</a>'
|
||||
'<b>🚨 Change detected for <i>Apprise ' \
|
||||
'Test Title</i></b>\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'] == \
|
||||
'<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 for ' \
|
||||
'<i>Apprise Test Title</i></b>\r\n' \
|
||||
'<a href="http://localhost"><i>Apprise Body ' \
|
||||
'Title</i></a> had <a ' \
|
||||
'href="http://127.0.0.1">a change</a>'
|
||||
'<b>\xf0\x9f\x9a\xa8 Change detected for <i>' \
|
||||
'Apprise Test Title</i></b>\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') == \
|
||||
'<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><title>&apos;information&apos</title></b>' \
|
||||
'\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>'
|
||||
|
@ -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') == ""
|
||||
|
Loading…
Reference in New Issue
Block a user