From b040e232ae69822705083f1f05f137a065cb1dac Mon Sep 17 00:00:00 2001 From: Chris Caron Date: Sat, 24 Mar 2018 22:44:18 -0400 Subject: [PATCH] enhanced Discord formatting with markdown; refs #10 --- apprise/plugins/NotifyDiscord.py | 41 +++++++++++++++++++++++++++++++- test/test_rest_plugins.py | 35 +++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/apprise/plugins/NotifyDiscord.py b/apprise/plugins/NotifyDiscord.py index 6022a920..e500d930 100644 --- a/apprise/plugins/NotifyDiscord.py +++ b/apprise/plugins/NotifyDiscord.py @@ -33,12 +33,14 @@ # API Documentation on Webhooks: # - https://discordapp.com/developers/docs/resources/webhook # +import re import requests from json import dumps from .NotifyBase import NotifyBase from .NotifyBase import HTTP_ERROR_MAP from ..common import NotifyImageSize +from ..common import NotifyFormat from ..utils import parse_bool # Image Support (256x256) @@ -58,13 +60,15 @@ class NotifyDiscord(NotifyBase): notify_url = 'https://discordapp.com/api/webhooks' def __init__(self, webhook_id, webhook_token, tts=False, avatar=True, - footer=False, thumbnail=True, **kwargs): + footer=False, thumbnail=True, + notify_format=NotifyFormat.MARKDOWN, **kwargs): """ Initialize Discord Object """ super(NotifyDiscord, self).__init__( title_maxlen=250, body_maxlen=2000, + notify_format=notify_format, image_size=DISCORD_IMAGE_XY, **kwargs) if not webhook_id: @@ -128,6 +132,17 @@ class NotifyDiscord(NotifyBase): }] } + if self.notify_format == NotifyFormat.MARKDOWN: + fields = self.extract_markdown_sections(body) + + if len(fields) > 0: + # Apply our additional parsing for a better presentation + + # Swap first entry for description + payload['embeds'][0]['description'] = \ + fields[0].get('name') + fields[0].get('value') + payload['embeds'][0]['fields'] = fields[1:] + if self.footer: logo_url = self.image_url(notify_type, logo=True) payload['embeds'][0]['footer'] = { @@ -249,3 +264,27 @@ class NotifyDiscord(NotifyBase): parse_bool(results['qsd'].get('thumbnail', True)) return results + + @staticmethod + def extract_markdown_sections(markdown): + """ + Takes a string in a markdown type format and extracts + the headers and their corresponding sections into individual + fields that get passed as an embed entry to Discord. + + """ + regex = re.compile( + r'\s*#+\s*(?P[^#\n]+)([ \r\t\v#]*)' + r'(?P(.+?)(\n(?!\s#))|\s*$)', flags=re.S) + + common = regex.finditer(markdown) + fields = list() + for el in common: + d = el.groupdict() + + fields.append({ + 'name': d.get('name', '').strip(), + 'value': '```md\n' + d.get('value', '').strip() + '\n```' + }) + + return fields diff --git a/test/test_rest_plugins.py b/test/test_rest_plugins.py index e45d2c7a..ad85df95 100644 --- a/test/test_rest_plugins.py +++ b/test/test_rest_plugins.py @@ -21,6 +21,8 @@ from apprise import NotifyType from apprise import Apprise from apprise import AppriseAsset from apprise.utils import compat_is_basestring +from apprise.common import NotifyFormat + from json import dumps import uuid import requests @@ -1656,6 +1658,39 @@ def test_notify_discord_plugin(mock_post, mock_get): assert obj.notify(title='title', body='body', notify_type=NotifyType.INFO) is True + # Test our header parsing + test_markdown = "## Heading one\nbody body\n\n" + \ + "# Heading 2 ##\n\nTest\n\n" + \ + "more content\n" + \ + "even more content \t\r\n\n\n" + \ + "# Heading 3 ##\n\n\n" + \ + "normal content\n" + \ + "# heading 4\n" + \ + "#### Heading 5" + + results = obj.extract_markdown_sections(test_markdown) + assert(isinstance(results, list)) + # We should have 5 sections (since there are 5 headers identified above) + assert(len(results) == 5) + + # Use our test markdown string during a notification + assert obj.notify(title='title', body=test_markdown, + notify_type=NotifyType.INFO) is True + + # Our processing is slightly different when we aren't using markdown + # as we do not pre-parse content during our notifications + obj = plugins.NotifyDiscord( + webhook_id=webhook_id, + webhook_token=webhook_token, + notify_format=NotifyFormat.TEXT) + + # Disable throttling to speed up unit tests + obj.throttle_attempt = 0 + + # This call includes an image with it's payload: + assert obj.notify(title='title', body='body', + notify_type=NotifyType.INFO) is True + @mock.patch('requests.get') @mock.patch('requests.post')