Support Slack URLs missing channel references (#108)

This commit is contained in:
Chris Caron 2019-05-11 15:02:56 -04:00 committed by GitHub
parent 333926c359
commit 51a5ce344a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 54 additions and 68 deletions

View File

@ -57,7 +57,7 @@ The table below identifies the services this tool supports and some example serv
| [Pushover](https://github.com/caronc/apprise/wiki/Notify_pushover) | pover:// | (TCP) 443 | pover://user@token<br />pover://user@token/DEVICE<br />pover://user@token/DEVICE1/DEVICE2/DEVICEN<br />**Note**: you must specify both your user_id and token
| [Rocket.Chat](https://github.com/caronc/apprise/wiki/Notify_rocketchat) | rocket:// or rockets:// | (TCP) 80 or 443 | rocket://user:password@hostname/RoomID/Channel<br />rockets://user:password@hostname:443/Channel1/Channel1/RoomID<br />rocket://user:password@hostname/Channel
| [Ryver](https://github.com/caronc/apprise/wiki/Notify_ryver) | ryver:// | (TCP) 443 | ryver://Organization/Token<br />ryver://botname@Organization/Token
| [Slack](https://github.com/caronc/apprise/wiki/Notify_slack) | slack:// | (TCP) 443 | slack://TokenA/TokenB/TokenC/Channel<br />slack://botname@TokenA/TokenB/TokenC/Channel<br />slack://user@TokenA/TokenB/TokenC/Channel1/Channel2/ChannelN
| [Slack](https://github.com/caronc/apprise/wiki/Notify_slack) | slack:// | (TCP) 443 | slack://TokenA/TokenB/TokenC/<br />slack://TokenA/TokenB/TokenC/Channel<br />slack://botname@TokenA/TokenB/TokenC/Channel<br />slack://user@TokenA/TokenB/TokenC/Channel1/Channel2/ChannelN
| [Telegram](https://github.com/caronc/apprise/wiki/Notify_telegram) | tgram:// | (TCP) 443 | tgram://bottoken/ChatID<br />tgram://bottoken/ChatID1/ChatID2/ChatIDN
| [Twilio](https://github.com/caronc/apprise/wiki/Notify_twilio) | twilio:// | (TCP) 443 | twilio://AccountSid:AuthToken@FromPhoneNo<br/>twilio://AccountSid:AuthToken@FromPhoneNo/ToPhoneNo<br/>twilio://AccountSid:AuthToken@FromPhoneNo/ToPhoneNo1/ToPhoneNo2/ToPhoneNoN/<br/>twilio://AccountSid:AuthToken@ShortCode/ToPhoneNo<br/>twilio://AccountSid:AuthToken@ShortCode/ToPhoneNo1/ToPhoneNo2/ToPhoneNoN/
| [Twitter](https://github.com/caronc/apprise/wiki/Notify_twitter) | tweet:// | (TCP) 443 | tweet://user@CKey/CSecret/AKey/ASecret

View File

@ -36,7 +36,6 @@
#
#
import re
import six
import requests
from json import dumps
from time import time
@ -46,6 +45,7 @@ from ..common import NotifyImageSize
from ..common import NotifyType
from ..common import NotifyFormat
from ..utils import parse_bool
from ..utils import parse_list
# Token required as part of the API request
# /AAAAAAAAA/........./........................
@ -155,21 +155,13 @@ class NotifySlack(NotifyBase):
self.logger.warning(
'No user was specified; using %s.' % SLACK_DEFAULT_USER)
if isinstance(targets, six.string_types):
self.channels = [x for x in filter(bool, CHANNEL_LIST_DELIM.split(
targets,
))]
elif isinstance(targets, (set, tuple, list)):
self.channels = targets
else:
self.channels = list()
# Build list of channels
self.channels = parse_list(targets)
if len(self.channels) == 0:
msg = 'No channel(s) were specified.'
self.logger.warning(msg)
raise TypeError(msg)
# No problem; the webhook is smart enough to just notify the
# channel it was created for; adding 'None' is just used as
# a flag lower to not set the channels
self.channels.append(None)
# Formatting requirements are defined here:
# https://api.slack.com/docs/message-formatting
@ -218,47 +210,48 @@ class NotifySlack(NotifyBase):
self.token_c,
)
# prepare JSON Object
payload = {
'username': self.user if self.user else SLACK_DEFAULT_USER,
# Use Markdown language
'mrkdwn': (self.notify_format == NotifyFormat.MARKDOWN),
'attachments': [{
'title': title,
'text': body,
'color': self.color(notify_type),
# Time
'ts': time(),
'footer': self.app_id,
}],
}
# Create a copy of the channel list
channels = list(self.channels)
while len(channels):
channel = channels.pop(0)
if not IS_CHANNEL_RE.match(channel):
self.logger.warning(
"The specified channel '%s' is invalid; skipping." % (
channel,
)
)
# Mark our failure
has_error = True
continue
if len(channel) > 1 and channel[0] == '+':
# Treat as encoded id if prefixed with a +
_channel = channel[1:]
if channel is not None:
# Channel over-ride was specified
if not IS_CHANNEL_RE.match(channel):
self.logger.warning(
"The specified target {} is invalid;"
"skipping.".format(channel))
elif len(channel) > 1 and channel[0] == '@':
# Treat @ value 'as is'
_channel = channel
# Mark our failure
has_error = True
continue
else:
# Prefix with channel hash tag
_channel = '#%s' % channel
if len(channel) > 1 and channel[0] == '+':
# Treat as encoded id if prefixed with a +
payload['channel'] = channel[1:]
# prepare JSON Object
payload = {
'channel': _channel,
'username': self.user if self.user else SLACK_DEFAULT_USER,
# Use Markdown language
'mrkdwn': (self.notify_format == NotifyFormat.MARKDOWN),
'attachments': [{
'title': title,
'text': body,
'color': self.color(notify_type),
# Time
'ts': time(),
'footer': self.app_id,
}],
}
elif len(channel) > 1 and channel[0] == '@':
# Treat @ value 'as is'
payload['channel'] = channel
else:
# Prefix with channel hash tag
payload['channel'] = '#%s' % channel
# Acquire our to-be footer icon if configured to do so
image_url = None if not self.include_image \
@ -288,9 +281,10 @@ class NotifySlack(NotifyBase):
r.status_code, SLACK_HTTP_ERROR_MAP)
self.logger.warning(
'Failed to send Slack notification to {}: '
'Failed to send Slack notification{}: '
'{}{}error={}.'.format(
channel,
' to {}'.format(channel)
if channel is not None else '',
status_str,
', ' if status_str else '',
r.status_code))
@ -304,13 +298,16 @@ class NotifySlack(NotifyBase):
else:
self.logger.info(
'Sent Slack notification to {}.'.format(channel))
'Sent Slack notification{}.'.format(
' to {}'.format(channel)
if channel is not None else ''))
except requests.RequestException as e:
self.logger.warning(
'A Connection error occured sending Slack:%s ' % (
channel) + 'notification.'
)
'A Connection error occured sending Slack '
'notification{}.'.format(
' to {}'.format(channel)
if channel is not None else ''))
self.logger.debug('Socket Exception: %s' % str(e))
# Mark our failure

View File

@ -1671,8 +1671,8 @@ TEST_URLS = (
'instance': plugins.NotifySlack,
}),
('slack://username@T1JJ3T3L2/A1BRTD4JD/TIiajkdnlazkcOXrIdevi7FQ', {
# Missing a channel
'instance': TypeError,
# Missing a channel, falls back to webhook channel bindings
'instance': plugins.NotifySlack,
}),
('slack://username@INVALID/A1BRTD4JD/TIiajkdnlazkcOXrIdevi7FQ/#cool', {
# invalid 1st Token
@ -3157,17 +3157,6 @@ def test_notify_slack_plugin(mock_post, mock_get):
mock_post.return_value.status_code = requests.codes.ok
mock_get.return_value.status_code = requests.codes.ok
# Empty Channel list
try:
plugins.NotifySlack(
token_a=token_a, token_b=token_b, token_c=token_c,
targets=None)
assert False
except TypeError:
# we'll thrown because an empty list of channels was provided
assert True
# Missing first Token
try:
plugins.NotifySlack(