mirror of https://github.com/caronc/apprise
Refactored Slack plugin to allow users to switch between payload types (#482)
parent
a9ce6b3556
commit
76700bfa1d
|
@ -73,6 +73,7 @@ import re
|
||||||
import requests
|
import requests
|
||||||
from json import dumps
|
from json import dumps
|
||||||
from json import loads
|
from json import loads
|
||||||
|
from time import time
|
||||||
|
|
||||||
from .NotifyBase import NotifyBase
|
from .NotifyBase import NotifyBase
|
||||||
from ..common import NotifyImageSize
|
from ..common import NotifyImageSize
|
||||||
|
@ -254,6 +255,14 @@ class NotifySlack(NotifyBase):
|
||||||
'default': True,
|
'default': True,
|
||||||
'map_to': 'include_footer',
|
'map_to': 'include_footer',
|
||||||
},
|
},
|
||||||
|
# Use Payload in Blocks (vs legacy way):
|
||||||
|
# See: https://api.slack.com/reference/messaging/payload
|
||||||
|
'blocks': {
|
||||||
|
'name': _('Use Blocks'),
|
||||||
|
'type': 'bool',
|
||||||
|
'default': False,
|
||||||
|
'map_to': 'use_blocks',
|
||||||
|
},
|
||||||
'to': {
|
'to': {
|
||||||
'alias_of': 'targets',
|
'alias_of': 'targets',
|
||||||
},
|
},
|
||||||
|
@ -265,7 +274,7 @@ class NotifySlack(NotifyBase):
|
||||||
|
|
||||||
def __init__(self, access_token=None, token_a=None, token_b=None,
|
def __init__(self, access_token=None, token_a=None, token_b=None,
|
||||||
token_c=None, targets=None, include_image=True,
|
token_c=None, targets=None, include_image=True,
|
||||||
include_footer=True, **kwargs):
|
include_footer=True, use_blocks=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Initialize Slack Object
|
Initialize Slack Object
|
||||||
"""
|
"""
|
||||||
|
@ -316,6 +325,11 @@ class NotifySlack(NotifyBase):
|
||||||
# specify a full email as a recipient via slack
|
# specify a full email as a recipient via slack
|
||||||
self._lookup_users = {}
|
self._lookup_users = {}
|
||||||
|
|
||||||
|
self.use_blocks = parse_bool(
|
||||||
|
use_blocks, self.template_args['blocks']['default']) \
|
||||||
|
if use_blocks is not None \
|
||||||
|
else self.template_args['blocks']['default']
|
||||||
|
|
||||||
# Build list of channels
|
# Build list of channels
|
||||||
self.channels = parse_list(targets)
|
self.channels = parse_list(targets)
|
||||||
if len(self.channels) == 0:
|
if len(self.channels) == 0:
|
||||||
|
@ -359,45 +373,117 @@ class NotifySlack(NotifyBase):
|
||||||
# error tracking (used for function return)
|
# error tracking (used for function return)
|
||||||
has_error = False
|
has_error = False
|
||||||
|
|
||||||
# Perform Formatting
|
#
|
||||||
title = self._re_formatting_rules.sub( # pragma: no branch
|
# Prepare JSON Object (applicable to both WEBHOOK and BOT mode)
|
||||||
lambda x: self._re_formatting_map[x.group()], title,
|
#
|
||||||
)
|
if self.use_blocks:
|
||||||
# Only for NONE markdown, otherwise eg links wont work
|
# Our slack format
|
||||||
if self.notify_format != NotifyFormat.MARKDOWN:
|
_slack_format = 'mrkdwn' \
|
||||||
body = self._re_formatting_rules.sub( # pragma: no branch
|
if self.notify_format == NotifyFormat.MARKDOWN \
|
||||||
lambda x: self._re_formatting_map[x.group()], body,
|
else 'plain_text'
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
'username': self.user if self.user else self.app_id,
|
||||||
|
'attachments': [{
|
||||||
|
'blocks': [{
|
||||||
|
'type': 'section',
|
||||||
|
'text': {
|
||||||
|
'type': _slack_format,
|
||||||
|
'text': body
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
'color': self.color(notify_type),
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Slack only accepts non-empty header sections
|
||||||
|
if title:
|
||||||
|
payload['attachments'][0]['blocks'].insert(0, {
|
||||||
|
'type': 'header',
|
||||||
|
'text': {
|
||||||
|
'type': 'plain_text',
|
||||||
|
'text': title,
|
||||||
|
'emoji': True
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
# Include the footer only if specified to do so
|
||||||
|
if self.include_footer:
|
||||||
|
|
||||||
|
# Acquire our to-be footer icon if configured to do so
|
||||||
|
image_url = None if not self.include_image \
|
||||||
|
else self.image_url(notify_type)
|
||||||
|
|
||||||
|
# Prepare our footer based on the block structure
|
||||||
|
_footer = {
|
||||||
|
'type': 'context',
|
||||||
|
'elements': [{
|
||||||
|
'type': _slack_format,
|
||||||
|
'text': self.app_id
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
if image_url:
|
||||||
|
payload['icon_url'] = image_url
|
||||||
|
|
||||||
|
_footer['elements'].insert(0, {
|
||||||
|
'type': 'image',
|
||||||
|
'image_url': image_url,
|
||||||
|
'alt_text': notify_type
|
||||||
|
})
|
||||||
|
|
||||||
|
payload['attachments'][0]['blocks'].append(_footer)
|
||||||
|
|
||||||
|
else:
|
||||||
|
#
|
||||||
|
# Legacy API Formatting
|
||||||
|
#
|
||||||
|
if self.notify_format == NotifyFormat.MARKDOWN:
|
||||||
|
body = self._re_formatting_rules.sub( # pragma: no branch
|
||||||
|
lambda x: self._re_formatting_map[x.group()], body,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Perform Formatting on title here; this is not needed for block
|
||||||
|
# mode above
|
||||||
|
title = self._re_formatting_rules.sub( # pragma: no branch
|
||||||
|
lambda x: self._re_formatting_map[x.group()], title,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Prepare JSON Object (applicable to both WEBHOOK and BOT mode)
|
# Prepare JSON Object (applicable to both WEBHOOK and BOT mode)
|
||||||
_slack_format = 'mrkdwn' \
|
payload = {
|
||||||
if self.notify_format == NotifyFormat.MARKDOWN else 'plain_text'
|
'username': self.user if self.user else self.app_id,
|
||||||
payload = {
|
# Use Markdown language
|
||||||
'username': self.user if self.user else self.app_id,
|
'mrkdwn': (self.notify_format == NotifyFormat.MARKDOWN),
|
||||||
'attachments': [{
|
'attachments': [{
|
||||||
'blocks': [{
|
'title': title,
|
||||||
'type': 'section',
|
'text': body,
|
||||||
'text': {
|
'color': self.color(notify_type),
|
||||||
'type': _slack_format,
|
# Time
|
||||||
'text': body
|
'ts': time(),
|
||||||
}
|
|
||||||
}],
|
}],
|
||||||
'color': self.color(notify_type),
|
}
|
||||||
}]
|
# Acquire our to-be footer icon if configured to do so
|
||||||
}
|
image_url = None if not self.include_image \
|
||||||
|
else self.image_url(notify_type)
|
||||||
|
|
||||||
# Slack only accepts non-empty header sections
|
if image_url:
|
||||||
if title:
|
payload['icon_url'] = image_url
|
||||||
payload['attachments'][0]['blocks'].insert(0, {
|
|
||||||
'type': 'header',
|
|
||||||
'text': {
|
|
||||||
'type': 'plain_text',
|
|
||||||
'text': title,
|
|
||||||
'emoji': True
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
# Prepare our URL (depends on mode)
|
# Include the footer only if specified to do so
|
||||||
|
if self.include_footer:
|
||||||
|
if image_url:
|
||||||
|
payload['attachments'][0]['footer_icon'] = image_url
|
||||||
|
|
||||||
|
# Include the footer only if specified to do so
|
||||||
|
payload['attachments'][0]['footer'] = self.app_id
|
||||||
|
|
||||||
|
if attach and self.mode is SlackMode.WEBHOOK:
|
||||||
|
# Be friendly; let the user know why they can't send their
|
||||||
|
# attachments if using the Webhook mode
|
||||||
|
self.logger.warning(
|
||||||
|
'Slack Webhooks do not support attachments.')
|
||||||
|
|
||||||
|
# Prepare our Slack URL (depends on mode)
|
||||||
if self.mode is SlackMode.WEBHOOK:
|
if self.mode is SlackMode.WEBHOOK:
|
||||||
url = '{}/{}/{}/{}'.format(
|
url = '{}/{}/{}/{}'.format(
|
||||||
self.webhook_url,
|
self.webhook_url,
|
||||||
|
@ -409,37 +495,6 @@ class NotifySlack(NotifyBase):
|
||||||
else: # SlackMode.BOT
|
else: # SlackMode.BOT
|
||||||
url = self.api_url.format('chat.postMessage')
|
url = self.api_url.format('chat.postMessage')
|
||||||
|
|
||||||
# Include the footer only if specified to do so
|
|
||||||
if self.include_footer:
|
|
||||||
_footer = {
|
|
||||||
'type': 'context',
|
|
||||||
'elements': [{
|
|
||||||
'type': _slack_format,
|
|
||||||
'text': self.app_id
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
|
|
||||||
# Acquire our to-be footer icon if configured to do so
|
|
||||||
image_url = None if not self.include_image \
|
|
||||||
else self.image_url(notify_type)
|
|
||||||
|
|
||||||
if image_url:
|
|
||||||
payload['icon_url'] = image_url
|
|
||||||
|
|
||||||
_footer['elements'].insert(0, {
|
|
||||||
'type': 'image',
|
|
||||||
'image_url': image_url,
|
|
||||||
'alt_text': notify_type
|
|
||||||
})
|
|
||||||
|
|
||||||
payload['attachments'][0]['blocks'].append(_footer)
|
|
||||||
|
|
||||||
if attach and self.mode is SlackMode.WEBHOOK:
|
|
||||||
# Be friendly; let the user know why they can't send their
|
|
||||||
# attachments if using the Webhook mode
|
|
||||||
self.logger.warning(
|
|
||||||
'Slack Webhooks do not support attachments.')
|
|
||||||
|
|
||||||
# Create a copy of the channel list
|
# Create a copy of the channel list
|
||||||
channels = list(self.channels)
|
channels = list(self.channels)
|
||||||
|
|
||||||
|
@ -875,6 +930,7 @@ class NotifySlack(NotifyBase):
|
||||||
params = {
|
params = {
|
||||||
'image': 'yes' if self.include_image else 'no',
|
'image': 'yes' if self.include_image else 'no',
|
||||||
'footer': 'yes' if self.include_footer else 'no',
|
'footer': 'yes' if self.include_footer else 'no',
|
||||||
|
'blocks': 'yes' if self.use_blocks else 'no',
|
||||||
}
|
}
|
||||||
|
|
||||||
# Extend our parameters
|
# Extend our parameters
|
||||||
|
@ -978,6 +1034,10 @@ class NotifySlack(NotifyBase):
|
||||||
results['include_image'] = \
|
results['include_image'] = \
|
||||||
parse_bool(results['qsd'].get('image', True))
|
parse_bool(results['qsd'].get('image', True))
|
||||||
|
|
||||||
|
# Get Payload structure (use blocks?)
|
||||||
|
if 'blocks' in results['qsd'] and len(results['qsd']['blocks']):
|
||||||
|
results['use_blocks'] = parse_bool(results['qsd']['blocks'])
|
||||||
|
|
||||||
# Get Footer Flag
|
# Get Footer Flag
|
||||||
results['include_footer'] = \
|
results['include_footer'] = \
|
||||||
parse_bool(results['qsd'].get('footer', True))
|
parse_bool(results['qsd'].get('footer', True))
|
||||||
|
|
|
@ -694,10 +694,7 @@ def parse_url(url, default_schema='http', verify_host=True):
|
||||||
|
|
||||||
def parse_bool(arg, default=False):
|
def parse_bool(arg, default=False):
|
||||||
"""
|
"""
|
||||||
NZBGet uses 'yes' and 'no' as well as other strings such as 'on' or
|
Support string based boolean settings.
|
||||||
'off' etch to handle boolean operations from it's control interface.
|
|
||||||
|
|
||||||
This method can just simplify checks to these variables.
|
|
||||||
|
|
||||||
If the content could not be parsed, then the default is returned.
|
If the content could not be parsed, then the default is returned.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -4859,6 +4859,33 @@ TEST_URLS = (
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
# Test blocks mode
|
||||||
|
('slack://?token=T1JJ3T3L2/A1BRTD4JD/TIiajkdnlazkcOXrIdevi7FQ/'
|
||||||
|
'&to=#chan&blocks=yes&footer=yes',
|
||||||
|
{
|
||||||
|
'instance': plugins.NotifySlack,
|
||||||
|
'requests_response_text': 'ok'}),
|
||||||
|
('slack://?token=T1JJ3T3L2/A1BRTD4JD/TIiajkdnlazkcOXrIdevi7FQ/'
|
||||||
|
'&to=#chan&blocks=yes&footer=no',
|
||||||
|
{
|
||||||
|
'instance': plugins.NotifySlack,
|
||||||
|
'requests_response_text': 'ok'}),
|
||||||
|
('slack://?token=T1JJ3T3L2/A1BRTD4JD/TIiajkdnlazkcOXrIdevi7FQ/'
|
||||||
|
'&to=#chan&blocks=yes&footer=yes&image=no',
|
||||||
|
{
|
||||||
|
'instance': plugins.NotifySlack,
|
||||||
|
'requests_response_text': 'ok'}),
|
||||||
|
('slack://?token=T1JJ3T3L2/A1BRTD4JD/TIiajkdnlazkcOXrIdevi7FQ/'
|
||||||
|
'&to=#chan&blocks=yes&format=text',
|
||||||
|
{
|
||||||
|
'instance': plugins.NotifySlack,
|
||||||
|
'requests_response_text': 'ok'}),
|
||||||
|
('slack://?token=T1JJ3T3L2/A1BRTD4JD/TIiajkdnlazkcOXrIdevi7FQ/'
|
||||||
|
'&to=#chan&blocks=no&format=text',
|
||||||
|
{
|
||||||
|
'instance': plugins.NotifySlack,
|
||||||
|
'requests_response_text': 'ok'}),
|
||||||
|
|
||||||
# Test using a bot-token as argument
|
# Test using a bot-token as argument
|
||||||
('slack://?token=xoxb-1234-1234-abc124&to=#nuxref&footer=no&user=test', {
|
('slack://?token=xoxb-1234-1234-abc124&to=#nuxref&footer=no&user=test', {
|
||||||
'instance': plugins.NotifySlack,
|
'instance': plugins.NotifySlack,
|
||||||
|
|
Loading…
Reference in New Issue