mirror of
https://github.com/caronc/apprise.git
synced 2025-01-20 21:08:38 +01:00
Slack Integration Support thread_timestamp (#1033)
This commit is contained in:
parent
c240f8ba48
commit
bb4acd0bdf
@ -96,6 +96,10 @@ SLACK_HTTP_ERROR_MAP = {
|
||||
# Used to break path apart into list of channels
|
||||
CHANNEL_LIST_DELIM = re.compile(r'[ \t\r\n,#\\/]+')
|
||||
|
||||
# Channel Regular Expression Parsing
|
||||
CHANNEL_RE = re.compile(
|
||||
r'^(?P<channel>[+#@]?[A-Z0-9_-]{1,32})(:(?P<thread_ts>[0-9.]+))?$', re.I)
|
||||
|
||||
|
||||
class SlackMode:
|
||||
"""
|
||||
@ -547,39 +551,52 @@ class NotifySlack(NotifyBase):
|
||||
attach_channel_list = []
|
||||
while len(channels):
|
||||
channel = channels.pop(0)
|
||||
|
||||
if channel is not None:
|
||||
channel = validate_regex(channel, r'[+#@]?[A-Z0-9_]{1,32}')
|
||||
if not channel:
|
||||
# Channel over-ride was specified
|
||||
self.logger.warning(
|
||||
"The specified target {} is invalid;"
|
||||
"skipping.".format(channel))
|
||||
# We'll perform a user lookup if we detect an email
|
||||
email = is_email(channel)
|
||||
if email:
|
||||
payload['channel'] = \
|
||||
self.lookup_userid(email['full_email'])
|
||||
|
||||
# Mark our failure
|
||||
has_error = True
|
||||
continue
|
||||
if not payload['channel']:
|
||||
# Move along; any notifications/logging would have
|
||||
# come from lookup_userid()
|
||||
has_error = True
|
||||
continue
|
||||
|
||||
if channel[0] == '+':
|
||||
# Treat as encoded id if prefixed with a +
|
||||
payload['channel'] = channel[1:]
|
||||
else: # Channel
|
||||
result = CHANNEL_RE.match(channel)
|
||||
|
||||
elif channel[0] == '@':
|
||||
# Treat @ value 'as is'
|
||||
payload['channel'] = channel
|
||||
if not result:
|
||||
# Channel over-ride was specified
|
||||
self.logger.warning(
|
||||
"The specified Slack target {} is invalid;"
|
||||
"skipping.".format(channel))
|
||||
|
||||
else:
|
||||
# We'll perform a user lookup if we detect an email
|
||||
email = is_email(channel)
|
||||
if email:
|
||||
payload['channel'] = \
|
||||
self.lookup_userid(email['full_email'])
|
||||
# Mark our failure
|
||||
has_error = True
|
||||
continue
|
||||
|
||||
# Store oure content
|
||||
channel, thread_ts = \
|
||||
result.group('channel'), result.group('thread_ts')
|
||||
if thread_ts:
|
||||
payload['thread_ts'] = thread_ts
|
||||
|
||||
elif 'thread_ts' in payload:
|
||||
# Handle situations where one channel has a thread_id
|
||||
# specified, and the next does not. We do not want to
|
||||
# cary forward the last value specified
|
||||
del payload['thread_ts']
|
||||
|
||||
if channel[0] == '+':
|
||||
# Treat as encoded id if prefixed with a +
|
||||
payload['channel'] = channel[1:]
|
||||
|
||||
elif channel[0] == '@':
|
||||
# Treat @ value 'as is'
|
||||
payload['channel'] = channel
|
||||
|
||||
if not payload['channel']:
|
||||
# Move along; any notifications/logging would have
|
||||
# come from lookup_userid()
|
||||
has_error = True
|
||||
continue
|
||||
else:
|
||||
# Prefix with channel hash tag (if not already)
|
||||
payload['channel'] = \
|
||||
@ -795,7 +812,6 @@ class NotifySlack(NotifyBase):
|
||||
"""
|
||||
Wrapper to the requests (post) object
|
||||
"""
|
||||
|
||||
self.logger.debug('Slack POST URL: %s (cert_verify=%r)' % (
|
||||
url, self.verify_certificate,
|
||||
))
|
||||
|
@ -253,6 +253,29 @@ apprise_url_tests = (
|
||||
'test_requests_exceptions': True,
|
||||
'requests_response_text': 'ok',
|
||||
}),
|
||||
('slack://notify@T1JJ3T3L2/A1BRTD4JD/TIiajkdnlazkcOXrIdevi7FQ/#b:100', {
|
||||
'instance': NotifySlack,
|
||||
'requests_response_text': 'ok',
|
||||
}),
|
||||
('slack://notify@T1JJ3T3L2/A1BRTD4JD/TIiajkdnlazkcOXrIdevi7FQ/+124:100', {
|
||||
'instance': NotifySlack,
|
||||
'requests_response_text': 'ok',
|
||||
}),
|
||||
# test a case where we have a channel defined alone (without a thread_ts)
|
||||
# that exists after a definition where a thread_ts does exist. this
|
||||
# tests the branch of code that ensures we do not pass the same thread_ts
|
||||
# twice
|
||||
('slack://notify@T1JJ3T3L2/A1BRTD4JD/'
|
||||
'TIiajkdnlazkcOXrIdevi7FQ/+124:100/@chan', {
|
||||
'instance': NotifySlack,
|
||||
'requests_response_text': 'ok',
|
||||
}),
|
||||
('slack://notify@T1JJ3T3L2/A1BRTD4JD/TIiajkdnlazkcOXrIdevi7FQ/#b:bad', {
|
||||
'instance': NotifySlack,
|
||||
'requests_response_text': 'ok',
|
||||
# we'll fail because our thread_ts is bad
|
||||
'response': False,
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
@ -702,3 +725,94 @@ def test_plugin_slack_markdown(mock_get, mock_post):
|
||||
"We also want to be able to support <https://slack.com> "\
|
||||
"links without the\ndescription."\
|
||||
"\n\nChannel Testing\n<!channelA>\n<!channelA|Description>"
|
||||
|
||||
|
||||
@mock.patch('requests.post')
|
||||
def test_plugin_slack_single_thread_reply(mock_post):
|
||||
"""
|
||||
NotifySlack() Send Notification as a Reply
|
||||
|
||||
"""
|
||||
|
||||
# Generate a (valid) bot token
|
||||
token = 'xoxb-1234-1234-abc124'
|
||||
thread_id = 100
|
||||
request = mock.Mock()
|
||||
request.content = dumps({
|
||||
'ok': True,
|
||||
'message': '',
|
||||
'user': {
|
||||
'id': 'ABCD1234'
|
||||
}
|
||||
})
|
||||
request.status_code = requests.codes.ok
|
||||
|
||||
# Prepare Mock
|
||||
mock_post.return_value = request
|
||||
|
||||
# Variation Initializations
|
||||
obj = NotifySlack(access_token=token, targets=[f'#general:{thread_id}'])
|
||||
assert isinstance(obj, NotifySlack) is True
|
||||
assert isinstance(obj.url(), str) is True
|
||||
|
||||
# No calls made yet
|
||||
assert mock_post.call_count == 0
|
||||
|
||||
# Send our notification
|
||||
assert obj.notify(
|
||||
body='body', title='title', notify_type=NotifyType.INFO) is True
|
||||
|
||||
# Post was made
|
||||
assert mock_post.call_count == 1
|
||||
assert mock_post.call_args_list[0][0][0] == \
|
||||
'https://slack.com/api/chat.postMessage'
|
||||
assert loads(mock_post.call_args_list[0][1]['data']).get("thread_ts") \
|
||||
== str(thread_id)
|
||||
|
||||
|
||||
@mock.patch('requests.post')
|
||||
def test_plugin_slack_multiple_thread_reply(mock_post):
|
||||
"""
|
||||
NotifySlack() Send Notification to multiple channels as Reply
|
||||
|
||||
"""
|
||||
|
||||
# Generate a (valid) bot token
|
||||
token = 'xoxb-1234-1234-abc124'
|
||||
thread_id_1, thread_id_2 = 100, 200
|
||||
request = mock.Mock()
|
||||
request.content = dumps({
|
||||
'ok': True,
|
||||
'message': '',
|
||||
'user': {
|
||||
'id': 'ABCD1234'
|
||||
}
|
||||
})
|
||||
request.status_code = requests.codes.ok
|
||||
|
||||
# Prepare Mock
|
||||
mock_post.return_value = request
|
||||
|
||||
# Variation Initializations
|
||||
obj = NotifySlack(access_token=token,
|
||||
targets=[
|
||||
f'#general:{thread_id_1}',
|
||||
f'#other:{thread_id_2}'])
|
||||
assert isinstance(obj, NotifySlack) is True
|
||||
assert isinstance(obj.url(), str) is True
|
||||
|
||||
# No calls made yet
|
||||
assert mock_post.call_count == 0
|
||||
|
||||
# Send our notification
|
||||
assert obj.notify(
|
||||
body='body', title='title', notify_type=NotifyType.INFO) is True
|
||||
|
||||
# Post was made
|
||||
assert mock_post.call_count == 2
|
||||
assert mock_post.call_args_list[0][0][0] == \
|
||||
'https://slack.com/api/chat.postMessage'
|
||||
assert loads(mock_post.call_args_list[0][1]['data']).get("thread_ts") \
|
||||
== str(thread_id_1)
|
||||
assert loads(mock_post.call_args_list[1][1]['data']).get("thread_ts") \
|
||||
== str(thread_id_2)
|
||||
|
Loading…
Reference in New Issue
Block a user