From a2c44cf90bd7f6f2ccf2203bc5637a793427f37c Mon Sep 17 00:00:00 2001 From: Chris Caron Date: Tue, 5 Feb 2019 22:02:42 -0500 Subject: [PATCH] telegram markdown support added; refs #52 --- apprise/plugins/NotifyBase.py | 6 +-- apprise/plugins/NotifyEmail.py | 13 ++----- apprise/plugins/NotifyTelegram.py | 61 ++++++++++++++++++++++--------- test/test_api.py | 23 ++++++++---- test/test_email_plugin.py | 20 ++++++---- test/test_notify_base.py | 2 +- test/test_rest_plugins.py | 60 ++++++++++++++++++++++++++++++ 7 files changed, 139 insertions(+), 46 deletions(-) diff --git a/apprise/plugins/NotifyBase.py b/apprise/plugins/NotifyBase.py index db4d3345..87b794ff 100644 --- a/apprise/plugins/NotifyBase.py +++ b/apprise/plugins/NotifyBase.py @@ -159,9 +159,9 @@ class NotifyBase(object): self.user = kwargs.get('user') self.password = kwargs.get('password') - if 'notify_format' in kwargs: - # Store the specified notify_format if specified - notify_format = kwargs.get('notify_format') + if 'format' in kwargs: + # Store the specified format if specified + notify_format = kwargs.get('format', '') if notify_format.lower() not in NOTIFY_FORMATS: self.logger.error( 'Invalid notification format %s' % notify_format, diff --git a/apprise/plugins/NotifyEmail.py b/apprise/plugins/NotifyEmail.py index 6d036796..69d836c8 100644 --- a/apprise/plugins/NotifyEmail.py +++ b/apprise/plugins/NotifyEmail.py @@ -227,6 +227,9 @@ class NotifyEmail(NotifyBase): # A URL that takes you to the setup/help of the specific protocol setup_url = 'https://github.com/caronc/apprise/wiki/Notify_email' + # Default Notify Format + notify_format = NotifyFormat.HTML + # Default Non-Encryption Port default_port = 25 @@ -433,20 +436,10 @@ class NotifyEmail(NotifyBase): # Apply our settings now - # Default Format is HTML - results['notify_format'] = NotifyFormat.HTML - results['secure_mode'] = NotifyEmail.default_secure_mode - to_addr = '' from_addr = '' smtp_host = '' - if 'format' in results['qsd'] and len(results['qsd']['format']): - # Extract email format (Text/Html) - format = NotifyBase.unquote(results['qsd']['format']).lower() - if len(format) > 0 and format[0] == 't': - results['notify_format'] = NotifyFormat.TEXT - # Attempt to detect 'from' email address if 'from' in results['qsd'] and len(results['qsd']['from']): from_addr = NotifyBase.unquote(results['qsd']['from']) diff --git a/apprise/plugins/NotifyTelegram.py b/apprise/plugins/NotifyTelegram.py index 8a653559..4ad54cc8 100644 --- a/apprise/plugins/NotifyTelegram.py +++ b/apprise/plugins/NotifyTelegram.py @@ -62,6 +62,7 @@ from .NotifyBase import HTTP_ERROR_MAP from ..common import NotifyImageSize from ..utils import compat_is_basestring from ..utils import parse_bool +from ..common import NotifyFormat TELEGRAM_IMAGE_XY = NotifyImageSize.XY_256 @@ -262,7 +263,7 @@ class NotifyTelegram(NotifyBase): # Try to get the error message if we can: error_msg = loads(r.content)['description'] - except: + except Exception: error_msg = None try: @@ -356,24 +357,50 @@ class NotifyTelegram(NotifyBase): payload = {} - # HTML Spaces ( ) and tabs ( ) aren't supported - # See https://core.telegram.org/bots/api#html-style - title = re.sub(' ?', ' ', title, re.I) - body = re.sub(' ?', ' ', body, re.I) - # Tabs become 3 spaces - title = re.sub(' ?', ' ', title, re.I) - body = re.sub(' ?', ' ', body, re.I) + # Prepare Email Message + if self.notify_format == NotifyFormat.MARKDOWN: + payload['parse_mode'] = 'MARKDOWN' - # HTML - title = NotifyBase.escape_html(title, whitespace=False) - body = NotifyBase.escape_html(body, whitespace=False) + else: + # Either TEXT or HTML; if TEXT we'll make it HTML + payload['parse_mode'] = 'HTML' - payload['parse_mode'] = 'HTML' + # HTML Spaces ( ) and tabs ( ) aren't supported + # See https://core.telegram.org/bots/api#html-style + body = re.sub(' ?', ' ', body, re.I) - payload['text'] = '%s\r\n%s' % ( - title, - body, - ) + # Tabs become 3 spaces + body = re.sub(' ?', ' ', body, re.I) + + if title: + # HTML Spaces ( ) and tabs ( ) aren't supported + # See https://core.telegram.org/bots/api#html-style + title = re.sub(' ?', ' ', title, re.I) + + # Tabs become 3 spaces + title = re.sub(' ?', ' ', title, re.I) + + # HTML + title = NotifyBase.escape_html(title, whitespace=False) + body = NotifyBase.escape_html(body, whitespace=False) + + # Assign the body + payload['text'] = body + + if title and self.notify_format == NotifyFormat.TEXT: + # Text HTML Formatting + payload['text'] = '%s\r\n%s' % ( + title, + body, + ) + + elif title: + # Already HTML; trust developer has wrapped + # the title appropriately + payload['text'] = '%s\r\n%s' % ( + title, + body, + ) # Create a copy of the chat_ids list chat_ids = list(self.chat_ids) @@ -426,7 +453,7 @@ class NotifyTelegram(NotifyBase): # Try to get the error message if we can: error_msg = loads(r.content)['description'] - except: + except Exception: error_msg = None try: diff --git a/test/test_api.py b/test/test_api.py index 2c94148a..c16bdc03 100644 --- a/test/test_api.py +++ b/test/test_api.py @@ -35,7 +35,6 @@ from apprise import NotifyBase from apprise import NotifyType from apprise import NotifyFormat from apprise import NotifyImageSize -from apprise import plugins from apprise import __version__ from apprise.Apprise import __load_matrix import pytest @@ -243,27 +242,35 @@ def test_apprise_notify_formats(tmpdir): assert(len(a) == 0) class TextNotification(NotifyBase): + # set our default notification format + notify_format = NotifyFormat.TEXT + def __init__(self, **kwargs): - super(TextNotification, self).__init__( - notify_format=NotifyFormat.TEXT) + super(TextNotification, self).__init__() def notify(self, **kwargs): # Pretend everything is okay return True class HtmlNotification(NotifyBase): + + # set our default notification format + notify_format = NotifyFormat.HTML + def __init__(self, **kwargs): - super(HtmlNotification, self).__init__( - notify_format=NotifyFormat.HTML) + super(HtmlNotification, self).__init__() def notify(self, **kwargs): # Pretend everything is okay return True class MarkDownNotification(NotifyBase): + + # set our default notification format + notify_format = NotifyFormat.MARKDOWN + def __init__(self, **kwargs): - super(MarkDownNotification, self).__init__( - notify_format=NotifyFormat.MARKDOWN) + super(MarkDownNotification, self).__init__() def notify(self, **kwargs): # Pretend everything is okay @@ -339,7 +346,7 @@ def test_apprise_asset(tmpdir): # The exception we expect since dict is not supported assert(True) - except: + except Exception: # Any other exception is not good assert(False) diff --git a/test/test_email_plugin.py b/test/test_email_plugin.py index 6859a795..bbd89c8d 100644 --- a/test/test_email_plugin.py +++ b/test/test_email_plugin.py @@ -26,6 +26,8 @@ from apprise import plugins from apprise import NotifyType from apprise import Apprise +from apprise.plugins import NotifyEmailBase + import smtplib import mock import re @@ -287,8 +289,6 @@ def test_webbase_lookup(mock_smtp, mock_smtpssl): """ - from apprise.plugins import NotifyEmailBase - # Insert a test email at the head of our table NotifyEmailBase.WEBBASE_LOOKUP_TABLE = ( ( @@ -324,8 +324,6 @@ def test_smtplib_init_fail(mock_smtplib): """ - from apprise.plugins import NotifyEmailBase - obj = Apprise.instantiate( 'mailto://user:pass@gmail.com', suppress_exceptions=False) assert(isinstance(obj, plugins.NotifyEmail)) @@ -362,8 +360,7 @@ def test_smtplib_send_okay(mock_smtplib): """ - from apprise.plugins import NotifyEmailBase - + # Defaults to HTML obj = Apprise.instantiate( 'mailto://user:pass@gmail.com', suppress_exceptions=False) assert(isinstance(obj, plugins.NotifyEmail)) @@ -374,4 +371,13 @@ def test_smtplib_send_okay(mock_smtplib): mock_smtplib.sendmail.return_value = True mock_smtplib.quit.return_value = True - obj.notify(title='test', body='body', notify_type=NotifyType.INFO) + assert(obj.notify( + title='test', body='body', notify_type=NotifyType.INFO) is True) + + # Set Text + obj = Apprise.instantiate( + 'mailto://user:pass@gmail.com?format=text', suppress_exceptions=False) + assert(isinstance(obj, plugins.NotifyEmail)) + + assert(obj.notify( + title='test', body='body', notify_type=NotifyType.INFO) is True) diff --git a/test/test_notify_base.py b/test/test_notify_base.py index db602bd4..01d809b9 100644 --- a/test/test_notify_base.py +++ b/test/test_notify_base.py @@ -38,7 +38,7 @@ def test_notify_base(): # invalid types throw exceptions try: - nb = NotifyBase(notify_format='invalid') + nb = NotifyBase(**{'format': 'invalid'}) # We should never reach here as an exception should be thrown assert(False) diff --git a/test/test_rest_plugins.py b/test/test_rest_plugins.py index 0390c4e8..63b41df8 100644 --- a/test/test_rest_plugins.py +++ b/test/test_rest_plugins.py @@ -140,6 +140,15 @@ TEST_URLS = ( 'instance': plugins.NotifyDiscord, 'requests_response_code': requests.codes.no_content, }), + # different format support + ('discord://%s/%s?format=markdown' % ('i' * 24, 't' * 64), { + 'instance': plugins.NotifyDiscord, + 'requests_response_code': requests.codes.no_content, + }), + ('discord://%s/%s?format=text' % ('i' * 24, 't' * 64), { + 'instance': plugins.NotifyDiscord, + 'requests_response_code': requests.codes.no_content, + }), # Test without image set ('discord://%s/%s' % ('i' * 24, 't' * 64), { 'instance': plugins.NotifyDiscord, @@ -1155,6 +1164,16 @@ TEST_URLS = ( ('tgram://123456789:abcdefg_hijklmnop/lead2gold/?format=', { 'instance': plugins.NotifyTelegram, }), + # Testing valid formats + ('tgram://123456789:abcdefg_hijklmnop/lead2gold/?format=markdown', { + 'instance': plugins.NotifyTelegram, + }), + ('tgram://123456789:abcdefg_hijklmnop/lead2gold/?format=html', { + 'instance': plugins.NotifyTelegram, + }), + ('tgram://123456789:abcdefg_hijklmnop/lead2gold/?format=text', { + 'instance': plugins.NotifyTelegram, + }), # Simple Message without image ('tgram://123456789:abcdefg_hijklmnop/lead2gold/', { 'instance': plugins.NotifyTelegram, @@ -1486,6 +1505,9 @@ def test_rest_plugins(mock_post, mock_get): assert(hasattr(key, obj)) assert(getattr(key, obj) == val) + # + # Stage 1: with title defined + # try: if test_requests_exceptions is False: # check that we're as expected @@ -1521,6 +1543,44 @@ def test_rest_plugins(mock_post, mock_get): # Check that we were expecting this exception to happen assert isinstance(e, response) + # + # Stage 1: without title defined + # + try: + if test_requests_exceptions is False: + # check that we're as expected + assert obj.notify( + title='', body='body', + notify_type=notify_type) == response + + else: + for _exception in REQUEST_EXCEPTIONS: + mock_post.side_effect = _exception + mock_get.side_effect = _exception + + try: + assert obj.notify( + title='', body='body', + notify_type=NotifyType.INFO) is False + + except AssertionError: + # Don't mess with these entries + raise + + except Exception as e: + # We can't handle this exception type + print('%s / %s' % (url, str(e))) + assert False + + except AssertionError: + # Don't mess with these entries + print('%s AssertionError' % url) + raise + + except Exception as e: + # Check that we were expecting this exception to happen + assert isinstance(e, response) + except AssertionError: # Don't mess with these entries print('%s AssertionError' % url)