Google Chat Thread-Key support (#753)

This commit is contained in:
Chris Caron 2022-11-12 15:14:54 -05:00 committed by GitHub
parent 2912dfd1e3
commit fc16c7bf0b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 140 additions and 9 deletions

View File

@ -80,8 +80,7 @@ class NotifyGoogleChat(NotifyBase):
setup_url = 'https://github.com/caronc/apprise/wiki/Notify_googlechat'
# Google Chat Webhook
notify_url = 'https://chat.googleapis.com/v1/spaces/{workspace}/messages' \
'?key={key}&token={token}'
notify_url = 'https://chat.googleapis.com/v1/spaces/{workspace}/messages'
# Default Notify Format
notify_format = NotifyFormat.MARKDOWN
@ -96,6 +95,7 @@ class NotifyGoogleChat(NotifyBase):
# Define object templates
templates = (
'{schema}://{workspace}/{webhook_key}/{webhook_token}',
'{schema}://{workspace}/{webhook_key}/{webhook_token}/{thread_key}',
)
# Define our template tokens
@ -118,6 +118,11 @@ class NotifyGoogleChat(NotifyBase):
'private': True,
'required': True,
},
'thread_key': {
'name': _('Thread Key'),
'type': 'string',
'private': True,
},
})
# Define our template arguments
@ -131,9 +136,13 @@ class NotifyGoogleChat(NotifyBase):
'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
@ -164,6 +173,16 @@ class NotifyGoogleChat(NotifyBase):
self.logger.warning(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
def send(self, body, title='', notify_type=NotifyType.INFO, **kwargs):
@ -185,13 +204,21 @@ class NotifyGoogleChat(NotifyBase):
# Construct Notify URL
notify_url = self.notify_url.format(
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)' % (
notify_url, self.verify_certificate,
))
self.logger.debug('Google Chat Parameters: %s' % str(params))
self.logger.debug('Google Chat Payload: %s' % str(payload))
# Always call throttle before any remote server i/o is made
@ -199,6 +226,7 @@ class NotifyGoogleChat(NotifyBase):
try:
r = requests.post(
notify_url,
params=params,
data=dumps(payload),
headers=headers,
verify=self.verify_certificate,
@ -242,11 +270,13 @@ class NotifyGoogleChat(NotifyBase):
# Set our parameters
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,
workspace=self.pprint(self.workspace, privacy, safe=''),
key=self.pprint(self.webhook_key, 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),
)
@ -258,6 +288,7 @@ class NotifyGoogleChat(NotifyBase):
Syntax:
gchat://workspace/webhook_key/webhook_token
gchat://workspace/webhook_key/webhook_token/thread_key
"""
results = NotifyBase.parse_url(url, verify_host=False)
@ -277,6 +308,9 @@ class NotifyGoogleChat(NotifyBase):
# Store our Webhook Token
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)
if 'workspace' in results['qsd']:
results['workspace'] = \
@ -290,6 +324,17 @@ class NotifyGoogleChat(NotifyBase):
results['webhook_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
@staticmethod
@ -298,6 +343,8 @@ class NotifyGoogleChat(NotifyBase):
Support
https://chat.googleapis.com/v1/spaces/{workspace}/messages
'?key={key}&token={token}
https://chat.googleapis.com/v1/spaces/{workspace}/messages
'?key={key}&token={token}&threadKey={thread}
"""
result = re.match(

View File

@ -23,9 +23,13 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
import requests
import pytest
from apprise import Apprise
from apprise.plugins.NotifyGoogleChat import NotifyGoogleChat
from helpers import AppriseURLTester
from unittest import mock
from apprise import NotifyType
from json import loads
# Disable logging for a cleaner testing output
import logging
@ -57,12 +61,25 @@ apprise_url_tests = (
'instance': NotifyGoogleChat,
'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'
'?key=mykey&token=mytoken', {
'instance': NotifyGoogleChat,
'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', {
'instance': NotifyGoogleChat,
# force a failure
@ -92,3 +109,70 @@ def test_plugin_google_chat_urls():
# Run our general tests
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())