mirror of https://github.com/caronc/apprise
enhanced Discord formatting with markdown; refs #10
parent
cc6151909e
commit
b040e232ae
|
@ -33,12 +33,14 @@
|
||||||
# API Documentation on Webhooks:
|
# API Documentation on Webhooks:
|
||||||
# - https://discordapp.com/developers/docs/resources/webhook
|
# - https://discordapp.com/developers/docs/resources/webhook
|
||||||
#
|
#
|
||||||
|
import re
|
||||||
import requests
|
import requests
|
||||||
from json import dumps
|
from json import dumps
|
||||||
|
|
||||||
from .NotifyBase import NotifyBase
|
from .NotifyBase import NotifyBase
|
||||||
from .NotifyBase import HTTP_ERROR_MAP
|
from .NotifyBase import HTTP_ERROR_MAP
|
||||||
from ..common import NotifyImageSize
|
from ..common import NotifyImageSize
|
||||||
|
from ..common import NotifyFormat
|
||||||
from ..utils import parse_bool
|
from ..utils import parse_bool
|
||||||
|
|
||||||
# Image Support (256x256)
|
# Image Support (256x256)
|
||||||
|
@ -58,13 +60,15 @@ class NotifyDiscord(NotifyBase):
|
||||||
notify_url = 'https://discordapp.com/api/webhooks'
|
notify_url = 'https://discordapp.com/api/webhooks'
|
||||||
|
|
||||||
def __init__(self, webhook_id, webhook_token, tts=False, avatar=True,
|
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
|
Initialize Discord Object
|
||||||
|
|
||||||
"""
|
"""
|
||||||
super(NotifyDiscord, self).__init__(
|
super(NotifyDiscord, self).__init__(
|
||||||
title_maxlen=250, body_maxlen=2000,
|
title_maxlen=250, body_maxlen=2000,
|
||||||
|
notify_format=notify_format,
|
||||||
image_size=DISCORD_IMAGE_XY, **kwargs)
|
image_size=DISCORD_IMAGE_XY, **kwargs)
|
||||||
|
|
||||||
if not webhook_id:
|
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:
|
if self.footer:
|
||||||
logo_url = self.image_url(notify_type, logo=True)
|
logo_url = self.image_url(notify_type, logo=True)
|
||||||
payload['embeds'][0]['footer'] = {
|
payload['embeds'][0]['footer'] = {
|
||||||
|
@ -249,3 +264,27 @@ class NotifyDiscord(NotifyBase):
|
||||||
parse_bool(results['qsd'].get('thumbnail', True))
|
parse_bool(results['qsd'].get('thumbnail', True))
|
||||||
|
|
||||||
return results
|
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<name>[^#\n]+)([ \r\t\v#]*)'
|
||||||
|
r'(?P<value>(.+?)(\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
|
||||||
|
|
|
@ -21,6 +21,8 @@ from apprise import NotifyType
|
||||||
from apprise import Apprise
|
from apprise import Apprise
|
||||||
from apprise import AppriseAsset
|
from apprise import AppriseAsset
|
||||||
from apprise.utils import compat_is_basestring
|
from apprise.utils import compat_is_basestring
|
||||||
|
from apprise.common import NotifyFormat
|
||||||
|
|
||||||
from json import dumps
|
from json import dumps
|
||||||
import uuid
|
import uuid
|
||||||
import requests
|
import requests
|
||||||
|
@ -1656,6 +1658,39 @@ def test_notify_discord_plugin(mock_post, mock_get):
|
||||||
assert obj.notify(title='title', body='body',
|
assert obj.notify(title='title', body='body',
|
||||||
notify_type=NotifyType.INFO) is True
|
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.get')
|
||||||
@mock.patch('requests.post')
|
@mock.patch('requests.post')
|
||||||
|
|
Loading…
Reference in New Issue