diff --git a/apprise/Apprise.py b/apprise/Apprise.py index 45a2ff66..42e5e624 100644 --- a/apprise/Apprise.py +++ b/apprise/Apprise.py @@ -21,8 +21,10 @@ import re import logging +from markdown import markdown from .common import NotifyType +from .common import NotifyFormat from .utils import parse_list from .utils import compat_is_basestring @@ -217,9 +219,14 @@ class Apprise(object): """ self.servers[:] = [] - def notify(self, title, body, notify_type=NotifyType.INFO): + def notify(self, title, body, notify_type=NotifyType.INFO, + body_format=None): """ - Send a notification to all of the plugins previously loaded + Send a notification to all of the plugins previously loaded. + + If the body_format specified is NotifyFormat.MARKDOWN, it will + be converted to HTML if the Notification type expects this. + """ # Initialize our return result @@ -228,12 +235,28 @@ class Apprise(object): if not (title or body): return False + # Tracks conversions + conversion_map = dict() + # Iterate over our loaded plugins for server in self.servers: + if server.notify_format not in conversion_map: + if body_format == NotifyFormat.MARKDOWN and \ + server.notify_format == NotifyFormat.HTML: + + # Apply Markdown + conversion_map[server.notify_format] = markdown(body) + + else: + # Store entry directly + conversion_map[server.notify_format] = body + try: # Send notification if not server.notify( - title=title, body=body, notify_type=notify_type): + title=title, + body=conversion_map[server.notify_format], + notify_type=notify_type): # Toggle our return status flag status = False diff --git a/apprise/__init__.py b/apprise/__init__.py index 3f08cf82..e8ac0545 100644 --- a/apprise/__init__.py +++ b/apprise/__init__.py @@ -24,9 +24,10 @@ __copywrite__ = 'Copyright 2017 Chris Caron ' from .common import NotifyType from .common import NOTIFY_TYPES -from .common import NOTIFY_IMAGE_SIZES from .common import NotifyImageSize -from .plugins.NotifyBase import NotifyFormat +from .common import NOTIFY_IMAGE_SIZES +from .common import NotifyFormat +from .common import NOTIFY_FORMATS from .plugins.NotifyBase import NotifyBase from .Apprise import Apprise @@ -43,5 +44,5 @@ __all__ = [ # Reference 'NotifyType', 'NotifyImageSize', 'NotifyFormat', 'NOTIFY_TYPES', - 'NOTIFY_IMAGE_SIZES', + 'NOTIFY_IMAGE_SIZES', 'NOTIFY_FORMATS', ] diff --git a/apprise/common.py b/apprise/common.py index 26da1982..be7a389c 100644 --- a/apprise/common.py +++ b/apprise/common.py @@ -53,3 +53,20 @@ NOTIFY_IMAGE_SIZES = ( NotifyImageSize.XY_128, NotifyImageSize.XY_256, ) + + +class NotifyFormat(object): + """ + A list of pre-defined text message formats that can be passed via the + apprise library. + """ + TEXT = 'text' + HTML = 'html' + MARKDOWN = 'markdown' + + +NOTIFY_FORMATS = ( + NotifyFormat.TEXT, + NotifyFormat.HTML, + NotifyFormat.MARKDOWN, +) diff --git a/apprise/plugins/NotifyBase.py b/apprise/plugins/NotifyBase.py index 724f826e..cb62093a 100644 --- a/apprise/plugins/NotifyBase.py +++ b/apprise/plugins/NotifyBase.py @@ -36,6 +36,8 @@ from ..utils import parse_bool from ..utils import is_hostname from ..common import NOTIFY_IMAGE_SIZES from ..common import NOTIFY_TYPES +from ..common import NotifyFormat +from ..common import NOTIFY_FORMATS from ..AppriseAsset import AppriseAsset @@ -60,24 +62,11 @@ HTTP_ERROR_MAP = { } # HTML New Line Delimiter -NOTIFY_NEWLINE = '\n' +NOTIFY_NEWLINE = '\r\n' # Used to break a path list into parts PATHSPLIT_LIST_DELIM = re.compile(r'[ \t\r\n,\\/]+') - -class NotifyFormat(object): - TEXT = 'text' - HTML = 'html' - MARKDOWN = 'markdown' - - -NOTIFY_FORMATS = ( - NotifyFormat.TEXT, - NotifyFormat.HTML, - NotifyFormat.MARKDOWN, -) - # Regular expression retrieved from: # http://www.regular-expressions.info/email.html IS_EMAIL_RE = re.compile( @@ -327,7 +316,7 @@ class NotifyBase(object): return is_hostname(hostname) @staticmethod - def parse_url(url, verify_host=True): + def parse_url(url, verify_host=True, default_format=NotifyFormat.TEXT): """ Parses the URL and returns it broken apart into a dictionary. @@ -343,10 +332,10 @@ class NotifyBase(object): results['secure'] = (results['schema'][-1] == 's') # Our default notification format - results['notify_format'] = NotifyFormat.TEXT + results['notify_format'] = default_format # Support SSL Certificate 'verify' keyword. Default to being enabled - results['verify'] = True + results['verify'] = verify_host if 'verify' in results['qsd']: results['verify'] = parse_bool( diff --git a/apprise/plugins/NotifyEmail.py b/apprise/plugins/NotifyEmail.py index 42e7b5ab..ffc9068d 100644 --- a/apprise/plugins/NotifyEmail.py +++ b/apprise/plugins/NotifyEmail.py @@ -25,7 +25,7 @@ from socket import error as SocketError from email.mime.text import MIMEText from .NotifyBase import NotifyBase -from .NotifyBase import NotifyFormat +from ..common import NotifyFormat class WebBaseLogin(object): diff --git a/apprise/plugins/NotifyMyAndroid.py b/apprise/plugins/NotifyMyAndroid.py index ebc1d546..ff725f33 100644 --- a/apprise/plugins/NotifyMyAndroid.py +++ b/apprise/plugins/NotifyMyAndroid.py @@ -20,8 +20,8 @@ import re import requests from .NotifyBase import NotifyBase -from .NotifyBase import NotifyFormat from .NotifyBase import HTTP_ERROR_MAP +from ..common import NotifyFormat # Extend HTTP Error Messages NMA_HTTP_ERROR_MAP = HTTP_ERROR_MAP.copy() diff --git a/apprise/plugins/NotifyTelegram.py b/apprise/plugins/NotifyTelegram.py index c11cdba8..17fe9e68 100644 --- a/apprise/plugins/NotifyTelegram.py +++ b/apprise/plugins/NotifyTelegram.py @@ -47,8 +47,8 @@ from json import loads from json import dumps from .NotifyBase import NotifyBase -from .NotifyBase import NotifyFormat from .NotifyBase import HTTP_ERROR_MAP +from ..common import NotifyFormat from ..common import NotifyImageSize from ..utils import compat_is_basestring diff --git a/requirements.txt b/requirements.txt index 8fca4c2f..f6ef661f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,3 +5,4 @@ oauthlib urllib3 six click >= 5.0 +markdown diff --git a/test/test_api.py b/test/test_api.py index 74272366..0238b654 100644 --- a/test/test_api.py +++ b/test/test_api.py @@ -25,6 +25,7 @@ from apprise import AppriseAsset from apprise.Apprise import SCHEMA_MAP from apprise import NotifyBase from apprise import NotifyType +from apprise import NotifyFormat from apprise import NotifyImageSize from apprise.Apprise import __load_matrix @@ -107,7 +108,8 @@ def test_apprise(): class GoodNotification(NotifyBase): def __init__(self, **kwargs): - super(GoodNotification, self).__init__() + super(GoodNotification, self).__init__( + notify_format=NotifyFormat.HTML) def notify(self, **kwargs): # Pretend everything is okay @@ -205,6 +207,79 @@ def test_apprise(): assert(len(a) == 0) +def test_apprise_notify_formats(tmpdir): + """ + API: Apprise() TextFormat tests + + """ + # Caling load matix a second time which is an internal function causes it + # to skip over content already loaded into our matrix and thefore accesses + # other if/else parts of the code that aren't otherwise called + __load_matrix() + + a = Apprise() + + # no items + assert(len(a) == 0) + + class TextNotification(NotifyBase): + def __init__(self, **kwargs): + super(TextNotification, self).__init__( + notify_format=NotifyFormat.TEXT) + + def notify(self, **kwargs): + # Pretend everything is okay + return True + + class HtmlNotification(NotifyBase): + def __init__(self, **kwargs): + super(HtmlNotification, self).__init__( + notify_format=NotifyFormat.HTML) + + def notify(self, **kwargs): + # Pretend everything is okay + return True + + class MarkDownNotification(NotifyBase): + def __init__(self, **kwargs): + super(MarkDownNotification, self).__init__( + notify_format=NotifyFormat.MARKDOWN) + + def notify(self, **kwargs): + # Pretend everything is okay + return True + + # Store our notifications into our schema map + SCHEMA_MAP['text'] = TextNotification + SCHEMA_MAP['html'] = HtmlNotification + SCHEMA_MAP['markdown'] = MarkDownNotification + + # Test Markdown; the above calls the markdown because our good:// + # defined plugin above was defined to default to HTML which triggers + # a markdown to take place if the body_format specified on the notify + # call + assert(a.add('html://localhost') is True) + assert(a.add('html://another.server') is True) + assert(a.add('html://and.another') is True) + assert(a.add('text://localhost') is True) + assert(a.add('text://another.server') is True) + assert(a.add('text://and.another') is True) + assert(a.add('markdown://localhost') is True) + assert(a.add('markdown://another.server') is True) + assert(a.add('markdown://and.another') is True) + + assert(len(a) == 9) + + assert(a.notify(title="markdown", body="## Testing Markdown", + body_format=NotifyFormat.MARKDOWN) is True) + + assert(a.notify(title="text", body="Testing Text", + body_format=NotifyFormat.TEXT) is True) + + assert(a.notify(title="html", body="HTML", + body_format=NotifyFormat.HTML) is True) + + def test_apprise_asset(tmpdir): """ API: AppriseAsset() object