mirror of
https://github.com/caronc/apprise.git
synced 2025-02-06 05:19:15 +01:00
Google Chat Thread-Key support (#753)
This commit is contained in:
parent
2912dfd1e3
commit
fc16c7bf0b
@ -80,8 +80,7 @@ class NotifyGoogleChat(NotifyBase):
|
|||||||
setup_url = 'https://github.com/caronc/apprise/wiki/Notify_googlechat'
|
setup_url = 'https://github.com/caronc/apprise/wiki/Notify_googlechat'
|
||||||
|
|
||||||
# Google Chat Webhook
|
# Google Chat Webhook
|
||||||
notify_url = 'https://chat.googleapis.com/v1/spaces/{workspace}/messages' \
|
notify_url = 'https://chat.googleapis.com/v1/spaces/{workspace}/messages'
|
||||||
'?key={key}&token={token}'
|
|
||||||
|
|
||||||
# Default Notify Format
|
# Default Notify Format
|
||||||
notify_format = NotifyFormat.MARKDOWN
|
notify_format = NotifyFormat.MARKDOWN
|
||||||
@ -96,6 +95,7 @@ class NotifyGoogleChat(NotifyBase):
|
|||||||
# Define object templates
|
# Define object templates
|
||||||
templates = (
|
templates = (
|
||||||
'{schema}://{workspace}/{webhook_key}/{webhook_token}',
|
'{schema}://{workspace}/{webhook_key}/{webhook_token}',
|
||||||
|
'{schema}://{workspace}/{webhook_key}/{webhook_token}/{thread_key}',
|
||||||
)
|
)
|
||||||
|
|
||||||
# Define our template tokens
|
# Define our template tokens
|
||||||
@ -118,6 +118,11 @@ class NotifyGoogleChat(NotifyBase):
|
|||||||
'private': True,
|
'private': True,
|
||||||
'required': True,
|
'required': True,
|
||||||
},
|
},
|
||||||
|
'thread_key': {
|
||||||
|
'name': _('Thread Key'),
|
||||||
|
'type': 'string',
|
||||||
|
'private': True,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
# Define our template arguments
|
# Define our template arguments
|
||||||
@ -131,9 +136,13 @@ class NotifyGoogleChat(NotifyBase):
|
|||||||
'token': {
|
'token': {
|
||||||
'alias_of': 'webhook_token',
|
'alias_of': 'webhook_token',
|
||||||
},
|
},
|
||||||
|
'thread': {
|
||||||
|
'alias_of': 'thread_key',
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
def __init__(self, workspace, webhook_key, webhook_token, **kwargs):
|
def __init__(self, workspace, webhook_key, webhook_token,
|
||||||
|
thread_key=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Initialize Google Chat Object
|
Initialize Google Chat Object
|
||||||
|
|
||||||
@ -164,6 +173,16 @@ class NotifyGoogleChat(NotifyBase):
|
|||||||
self.logger.warning(msg)
|
self.logger.warning(msg)
|
||||||
raise TypeError(msg)
|
raise TypeError(msg)
|
||||||
|
|
||||||
|
if thread_key:
|
||||||
|
self.thread_key = validate_regex(thread_key)
|
||||||
|
if not self.thread_key:
|
||||||
|
msg = 'An invalid Google Chat Thread Key ' \
|
||||||
|
'({}) was specified.'.format(thread_key)
|
||||||
|
self.logger.warning(msg)
|
||||||
|
raise TypeError(msg)
|
||||||
|
else:
|
||||||
|
self.thread_key = None
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def send(self, body, title='', notify_type=NotifyType.INFO, **kwargs):
|
def send(self, body, title='', notify_type=NotifyType.INFO, **kwargs):
|
||||||
@ -185,13 +204,21 @@ class NotifyGoogleChat(NotifyBase):
|
|||||||
# Construct Notify URL
|
# Construct Notify URL
|
||||||
notify_url = self.notify_url.format(
|
notify_url = self.notify_url.format(
|
||||||
workspace=self.workspace,
|
workspace=self.workspace,
|
||||||
key=self.webhook_key,
|
|
||||||
token=self.webhook_token,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
params = {
|
||||||
|
# Prepare our URL Parameters
|
||||||
|
'token': self.webhook_token,
|
||||||
|
'key': self.webhook_key,
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.thread_key:
|
||||||
|
params['threadKey'] = self.thread_key
|
||||||
|
|
||||||
self.logger.debug('Google Chat POST URL: %s (cert_verify=%r)' % (
|
self.logger.debug('Google Chat POST URL: %s (cert_verify=%r)' % (
|
||||||
notify_url, self.verify_certificate,
|
notify_url, self.verify_certificate,
|
||||||
))
|
))
|
||||||
|
self.logger.debug('Google Chat Parameters: %s' % str(params))
|
||||||
self.logger.debug('Google Chat Payload: %s' % str(payload))
|
self.logger.debug('Google Chat Payload: %s' % str(payload))
|
||||||
|
|
||||||
# Always call throttle before any remote server i/o is made
|
# Always call throttle before any remote server i/o is made
|
||||||
@ -199,6 +226,7 @@ class NotifyGoogleChat(NotifyBase):
|
|||||||
try:
|
try:
|
||||||
r = requests.post(
|
r = requests.post(
|
||||||
notify_url,
|
notify_url,
|
||||||
|
params=params,
|
||||||
data=dumps(payload),
|
data=dumps(payload),
|
||||||
headers=headers,
|
headers=headers,
|
||||||
verify=self.verify_certificate,
|
verify=self.verify_certificate,
|
||||||
@ -242,11 +270,13 @@ class NotifyGoogleChat(NotifyBase):
|
|||||||
# Set our parameters
|
# Set our parameters
|
||||||
params = self.url_parameters(privacy=privacy, *args, **kwargs)
|
params = self.url_parameters(privacy=privacy, *args, **kwargs)
|
||||||
|
|
||||||
return '{schema}://{workspace}/{key}/{token}/?{params}'.format(
|
return '{schema}://{workspace}/{key}/{token}/{thread}?{params}'.format(
|
||||||
schema=self.secure_protocol,
|
schema=self.secure_protocol,
|
||||||
workspace=self.pprint(self.workspace, privacy, safe=''),
|
workspace=self.pprint(self.workspace, privacy, safe=''),
|
||||||
key=self.pprint(self.webhook_key, privacy, safe=''),
|
key=self.pprint(self.webhook_key, privacy, safe=''),
|
||||||
token=self.pprint(self.webhook_token, privacy, safe=''),
|
token=self.pprint(self.webhook_token, privacy, safe=''),
|
||||||
|
thread='' if not self.thread_key
|
||||||
|
else self.pprint(self.thread_key, privacy, safe=''),
|
||||||
params=NotifyGoogleChat.urlencode(params),
|
params=NotifyGoogleChat.urlencode(params),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -258,6 +288,7 @@ class NotifyGoogleChat(NotifyBase):
|
|||||||
|
|
||||||
Syntax:
|
Syntax:
|
||||||
gchat://workspace/webhook_key/webhook_token
|
gchat://workspace/webhook_key/webhook_token
|
||||||
|
gchat://workspace/webhook_key/webhook_token/thread_key
|
||||||
|
|
||||||
"""
|
"""
|
||||||
results = NotifyBase.parse_url(url, verify_host=False)
|
results = NotifyBase.parse_url(url, verify_host=False)
|
||||||
@ -277,6 +308,9 @@ class NotifyGoogleChat(NotifyBase):
|
|||||||
# Store our Webhook Token
|
# Store our Webhook Token
|
||||||
results['webhook_token'] = tokens.pop(0) if tokens else None
|
results['webhook_token'] = tokens.pop(0) if tokens else None
|
||||||
|
|
||||||
|
# Store our Thread Key
|
||||||
|
results['thread_key'] = tokens.pop(0) if tokens else None
|
||||||
|
|
||||||
# Support arguments as overrides (if specified)
|
# Support arguments as overrides (if specified)
|
||||||
if 'workspace' in results['qsd']:
|
if 'workspace' in results['qsd']:
|
||||||
results['workspace'] = \
|
results['workspace'] = \
|
||||||
@ -290,6 +324,17 @@ class NotifyGoogleChat(NotifyBase):
|
|||||||
results['webhook_token'] = \
|
results['webhook_token'] = \
|
||||||
NotifyGoogleChat.unquote(results['qsd']['token'])
|
NotifyGoogleChat.unquote(results['qsd']['token'])
|
||||||
|
|
||||||
|
if 'thread' in results['qsd']:
|
||||||
|
results['thread_key'] = \
|
||||||
|
NotifyGoogleChat.unquote(results['qsd']['thread'])
|
||||||
|
|
||||||
|
elif 'threadkey' in results['qsd']:
|
||||||
|
# Support Google Chat's Thread Key (if set)
|
||||||
|
# keys are always made lowercase; so check above is attually
|
||||||
|
# testing threadKey successfully as well
|
||||||
|
results['thread_key'] = \
|
||||||
|
NotifyGoogleChat.unquote(results['qsd']['threadkey'])
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -298,6 +343,8 @@ class NotifyGoogleChat(NotifyBase):
|
|||||||
Support
|
Support
|
||||||
https://chat.googleapis.com/v1/spaces/{workspace}/messages
|
https://chat.googleapis.com/v1/spaces/{workspace}/messages
|
||||||
'?key={key}&token={token}
|
'?key={key}&token={token}
|
||||||
|
https://chat.googleapis.com/v1/spaces/{workspace}/messages
|
||||||
|
'?key={key}&token={token}&threadKey={thread}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
result = re.match(
|
result = re.match(
|
||||||
|
@ -23,9 +23,13 @@
|
|||||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
# THE SOFTWARE.
|
# THE SOFTWARE.
|
||||||
import requests
|
import requests
|
||||||
|
import pytest
|
||||||
|
from apprise import Apprise
|
||||||
from apprise.plugins.NotifyGoogleChat import NotifyGoogleChat
|
from apprise.plugins.NotifyGoogleChat import NotifyGoogleChat
|
||||||
from helpers import AppriseURLTester
|
from helpers import AppriseURLTester
|
||||||
|
from unittest import mock
|
||||||
|
from apprise import NotifyType
|
||||||
|
from json import loads
|
||||||
|
|
||||||
# Disable logging for a cleaner testing output
|
# Disable logging for a cleaner testing output
|
||||||
import logging
|
import logging
|
||||||
@ -57,12 +61,25 @@ apprise_url_tests = (
|
|||||||
'instance': NotifyGoogleChat,
|
'instance': NotifyGoogleChat,
|
||||||
'privacy_url': 'gchat://w...s/m...y/m...n',
|
'privacy_url': 'gchat://w...s/m...y/m...n',
|
||||||
}),
|
}),
|
||||||
# Google Native Webhohok URL
|
('gchat://?workspace=ws&key=mykey&token=mytoken&thread=abc123', {
|
||||||
|
# Test our thread key
|
||||||
|
'instance': NotifyGoogleChat,
|
||||||
|
'privacy_url': 'gchat://w...s/m...y/m...n/a...3',
|
||||||
|
}),
|
||||||
|
('gchat://?workspace=ws&key=mykey&token=mytoken&threadKey=abc345', {
|
||||||
|
# Test our thread key
|
||||||
|
'instance': NotifyGoogleChat,
|
||||||
|
'privacy_url': 'gchat://w...s/m...y/m...n/a...5',
|
||||||
|
}),
|
||||||
|
# Google Native Webhook URL
|
||||||
('https://chat.googleapis.com/v1/spaces/myworkspace/messages'
|
('https://chat.googleapis.com/v1/spaces/myworkspace/messages'
|
||||||
'?key=mykey&token=mytoken', {
|
'?key=mykey&token=mytoken', {
|
||||||
'instance': NotifyGoogleChat,
|
'instance': NotifyGoogleChat,
|
||||||
'privacy_url': 'gchat://m...e/m...y/m...n'}),
|
'privacy_url': 'gchat://m...e/m...y/m...n'}),
|
||||||
|
('https://chat.googleapis.com/v1/spaces/myworkspace/messages'
|
||||||
|
'?key=mykey&token=mytoken&threadKey=mythreadkey', {
|
||||||
|
'instance': NotifyGoogleChat,
|
||||||
|
'privacy_url': 'gchat://m...e/m...y/m...n/m...y'}),
|
||||||
('gchat://workspace/key/token', {
|
('gchat://workspace/key/token', {
|
||||||
'instance': NotifyGoogleChat,
|
'instance': NotifyGoogleChat,
|
||||||
# force a failure
|
# force a failure
|
||||||
@ -92,3 +109,70 @@ def test_plugin_google_chat_urls():
|
|||||||
|
|
||||||
# Run our general tests
|
# Run our general tests
|
||||||
AppriseURLTester(tests=apprise_url_tests).run_all()
|
AppriseURLTester(tests=apprise_url_tests).run_all()
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch('requests.post')
|
||||||
|
def test_plugin_google_chat_general(mock_post):
|
||||||
|
"""
|
||||||
|
NotifyGoogleChat() General Checks
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Initialize some generic (but valid) tokens
|
||||||
|
workspace = 'ws'
|
||||||
|
key = 'key'
|
||||||
|
threadkey = 'threadkey'
|
||||||
|
token = 'token'
|
||||||
|
|
||||||
|
# Prepare Mock
|
||||||
|
mock_post.return_value = requests.Request()
|
||||||
|
mock_post.return_value.status_code = requests.codes.ok
|
||||||
|
|
||||||
|
# Test our messaging
|
||||||
|
obj = Apprise.instantiate(
|
||||||
|
'gchat://{}/{}/{}'.format(workspace, key, token))
|
||||||
|
assert isinstance(obj, NotifyGoogleChat)
|
||||||
|
assert obj.notify(
|
||||||
|
body="test body", title='title',
|
||||||
|
notify_type=NotifyType.INFO) is True
|
||||||
|
|
||||||
|
# Test our call count
|
||||||
|
assert mock_post.call_count == 1
|
||||||
|
assert mock_post.call_args_list[0][0][0] == \
|
||||||
|
'https://chat.googleapis.com/v1/spaces/ws/messages'
|
||||||
|
params = mock_post.call_args_list[0][1]['params']
|
||||||
|
assert params.get('token') == token
|
||||||
|
assert params.get('key') == key
|
||||||
|
assert 'threadKey' not in params
|
||||||
|
payload = loads(mock_post.call_args_list[0][1]['data'])
|
||||||
|
assert payload['text'] == "title\r\ntest body"
|
||||||
|
|
||||||
|
mock_post.reset_mock()
|
||||||
|
|
||||||
|
# Test our messaging with the threadKey
|
||||||
|
obj = Apprise.instantiate(
|
||||||
|
'gchat://{}/{}/{}/{}'.format(workspace, key, token, threadkey))
|
||||||
|
assert isinstance(obj, NotifyGoogleChat)
|
||||||
|
assert obj.notify(
|
||||||
|
body="test body", title='title',
|
||||||
|
notify_type=NotifyType.INFO) is True
|
||||||
|
|
||||||
|
# Test our call count
|
||||||
|
assert mock_post.call_count == 1
|
||||||
|
assert mock_post.call_args_list[0][0][0] == \
|
||||||
|
'https://chat.googleapis.com/v1/spaces/ws/messages'
|
||||||
|
params = mock_post.call_args_list[0][1]['params']
|
||||||
|
assert params.get('token') == token
|
||||||
|
assert params.get('key') == key
|
||||||
|
assert params.get('threadKey') == threadkey
|
||||||
|
payload = loads(mock_post.call_args_list[0][1]['data'])
|
||||||
|
assert payload['text'] == "title\r\ntest body"
|
||||||
|
|
||||||
|
|
||||||
|
def test_plugin_google_chat_edge_case():
|
||||||
|
"""
|
||||||
|
NotifyGoogleChat() Edge Cases
|
||||||
|
|
||||||
|
"""
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
NotifyGoogleChat('workspace', 'webhook', 'token', thread_key=object())
|
||||||
|
Loading…
Reference in New Issue
Block a user