Matrix login URL updated to accommodate newer API (#970)

pull/977/head
Chris Caron 2023-10-15 15:03:08 -04:00 committed by GitHub
parent f6b53ac556
commit 97c7af4c4a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 167 additions and 76 deletions

View File

@ -586,7 +586,7 @@ class NotifyMatrix(NotifyBase):
attachments = None attachments = None
if attach and self.attachment_support: if attach and self.attachment_support:
attachments = self._send_attachments(attach) attachments = self._send_attachments(attach)
if not attachments: if attachments is False:
# take an early exit # take an early exit
return False return False
@ -611,19 +611,30 @@ class NotifyMatrix(NotifyBase):
self.image_url(notify_type) self.image_url(notify_type)
# Build our path # Build our path
if self.version == MatrixVersion.V3:
path = '/rooms/{}/send/m.room.message/0'.format(
NotifyMatrix.quote(room_id))
else:
path = '/rooms/{}/send/m.room.message'.format( path = '/rooms/{}/send/m.room.message'.format(
NotifyMatrix.quote(room_id)) NotifyMatrix.quote(room_id))
if self.version == MatrixVersion.V2:
#
# Attachments don't work beyond V2 at this time
#
if image_url: if image_url:
# Define our payload # Define our payload
image_payload = { image_payload = {
'msgtype': 'm.image', 'msgtype': 'm.image',
'url': image_url, 'url': image_url,
'body': '{}'.format(notify_type if not title else title), 'body': '{}'.format(
notify_type if not title else title),
} }
# Post our content # Post our content
postokay, response = self._fetch(path, payload=image_payload) postokay, response = self._fetch(
path, payload=image_payload)
if not postokay: if not postokay:
# Mark our failure # Mark our failure
has_error = True has_error = True
@ -631,7 +642,11 @@ class NotifyMatrix(NotifyBase):
if attachments: if attachments:
for attachment in attachments: for attachment in attachments:
postokay, response = self._fetch(path, payload=attachment) attachment['room_id'] = room_id
attachment['type'] = 'm.room.message'
postokay, response = self._fetch(
path, payload=attachment)
if not postokay: if not postokay:
# Mark our failure # Mark our failure
has_error = True has_error = True
@ -667,7 +682,9 @@ class NotifyMatrix(NotifyBase):
}) })
# Post our content # Post our content
postokay, response = self._fetch(path, payload=payload) method = 'PUT' if self.version == MatrixVersion.V3 else 'POST'
postokay, response = self._fetch(
path, payload=payload, method=method)
if not postokay: if not postokay:
# Notify our user # Notify our user
self.logger.warning( self.logger.warning(
@ -685,6 +702,11 @@ class NotifyMatrix(NotifyBase):
""" """
payloads = [] payloads = []
if self.version != MatrixVersion.V2:
self.logger.warning(
'Add ?v=2 to Apprise URL to support Attachments')
return next((False for a in attach if not a), [])
for attachment in attach: for attachment in attach:
if not attachment: if not attachment:
# invalid attachment (bad file) # invalid attachment (bad file)
@ -705,6 +727,19 @@ class NotifyMatrix(NotifyBase):
# "content_uri": "mxc://example.com/a-unique-key" # "content_uri": "mxc://example.com/a-unique-key"
# } # }
if self.version == MatrixVersion.V3:
# Prepare our payload
payloads.append({
"body": attachment.name,
"info": {
"mimetype": attachment.mimetype,
"size": len(attachment),
},
"msgtype": "m.image",
"url": response.get('content_uri'),
})
else:
# Prepare our payload # Prepare our payload
payloads.append({ payloads.append({
"info": { "info": {
@ -780,7 +815,18 @@ class NotifyMatrix(NotifyBase):
'user/pass combo is missing.') 'user/pass combo is missing.')
return False return False
# Prepare our Registration Payload # Prepare our Authentication Payload
if self.version == MatrixVersion.V3:
payload = {
'type': 'm.login.password',
'identifier': {
'type': 'm.id.user',
'user': self.user,
},
'password': self.password,
}
else:
payload = { payload = {
'type': 'm.login.password', 'type': 'm.login.password',
'user': self.user, 'user': self.user,
@ -1109,7 +1155,8 @@ class NotifyMatrix(NotifyBase):
response = {} response = {}
# fetch function # fetch function
fn = requests.post if method == 'POST' else requests.get fn = requests.post if method == 'POST' else (
requests.put if method == 'PUT' else requests.get)
# Define how many attempts we'll make if we get caught in a throttle # Define how many attempts we'll make if we get caught in a throttle
# event # event
@ -1137,7 +1184,9 @@ class NotifyMatrix(NotifyBase):
timeout=self.request_timeout, timeout=self.request_timeout,
) )
self.logger.debug('Matrix Response: %s' % str(r.content)) self.logger.debug(
'Matrix Response: code=%d, %s' % (
r.status_code, str(r.content)))
response = loads(r.content) response = loads(r.content)
if r.status_code == 429: if r.status_code == 429:

View File

@ -27,7 +27,6 @@
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from unittest import mock from unittest import mock
import os import os
import requests import requests
import pytest import pytest
@ -228,9 +227,10 @@ def test_plugin_matrix_urls():
AppriseURLTester(tests=apprise_url_tests).run_all() AppriseURLTester(tests=apprise_url_tests).run_all()
@mock.patch('requests.put')
@mock.patch('requests.get') @mock.patch('requests.get')
@mock.patch('requests.post') @mock.patch('requests.post')
def test_plugin_matrix_general(mock_post, mock_get): def test_plugin_matrix_general(mock_post, mock_get, mock_put):
""" """
NotifyMatrix() General Tests NotifyMatrix() General Tests
@ -250,6 +250,7 @@ def test_plugin_matrix_general(mock_post, mock_get):
# Prepare Mock # Prepare Mock
mock_get.return_value = request mock_get.return_value = request
mock_post.return_value = request mock_post.return_value = request
mock_put.return_value = request
# Variation Initializations # Variation Initializations
obj = NotifyMatrix(host='host', targets='#abcd') obj = NotifyMatrix(host='host', targets='#abcd')
@ -383,9 +384,10 @@ def test_plugin_matrix_general(mock_post, mock_get):
assert obj.send(user='test', password='passwd', body="test") is True assert obj.send(user='test', password='passwd', body="test") is True
@mock.patch('requests.put')
@mock.patch('requests.get') @mock.patch('requests.get')
@mock.patch('requests.post') @mock.patch('requests.post')
def test_plugin_matrix_fetch(mock_post, mock_get): def test_plugin_matrix_fetch(mock_post, mock_get, mock_put):
""" """
NotifyMatrix() Server Fetch/API Tests NotifyMatrix() Server Fetch/API Tests
@ -419,6 +421,7 @@ def test_plugin_matrix_fetch(mock_post, mock_get):
return request return request
mock_put.side_effect = fetch_failed
mock_get.side_effect = fetch_failed mock_get.side_effect = fetch_failed
mock_post.side_effect = fetch_failed mock_post.side_effect = fetch_failed
@ -449,12 +452,14 @@ def test_plugin_matrix_fetch(mock_post, mock_get):
# Default configuration # Default configuration
mock_get.side_effect = None mock_get.side_effect = None
mock_post.side_effect = None mock_post.side_effect = None
mock_put.side_effect = None
request = mock.Mock() request = mock.Mock()
request.status_code = requests.codes.ok request.status_code = requests.codes.ok
request.content = dumps(response_obj) request.content = dumps(response_obj)
mock_post.return_value = request mock_post.return_value = request
mock_get.return_value = request mock_get.return_value = request
mock_put.return_value = request
obj = NotifyMatrix(host='host', include_image=True) obj = NotifyMatrix(host='host', include_image=True)
assert isinstance(obj, NotifyMatrix) is True assert isinstance(obj, NotifyMatrix) is True
@ -467,6 +472,7 @@ def test_plugin_matrix_fetch(mock_post, mock_get):
request.content = dumps({ request.content = dumps({
'retry_after_ms': 1, 'retry_after_ms': 1,
}) })
code, response = obj._fetch('/retry/apprise/unit/test') code, response = obj._fetch('/retry/apprise/unit/test')
assert code is False assert code is False
@ -485,9 +491,10 @@ def test_plugin_matrix_fetch(mock_post, mock_get):
assert code is False assert code is False
@mock.patch('requests.put')
@mock.patch('requests.get') @mock.patch('requests.get')
@mock.patch('requests.post') @mock.patch('requests.post')
def test_plugin_matrix_auth(mock_post, mock_get): def test_plugin_matrix_auth(mock_post, mock_get, mock_put):
""" """
NotifyMatrix() Server Authentication NotifyMatrix() Server Authentication
@ -506,6 +513,7 @@ def test_plugin_matrix_auth(mock_post, mock_get):
request.content = dumps(response_obj) request.content = dumps(response_obj)
mock_post.return_value = request mock_post.return_value = request
mock_get.return_value = request mock_get.return_value = request
mock_put.return_value = request
obj = NotifyMatrix(host='localhost') obj = NotifyMatrix(host='localhost')
assert isinstance(obj, NotifyMatrix) is True assert isinstance(obj, NotifyMatrix) is True
@ -579,9 +587,10 @@ def test_plugin_matrix_auth(mock_post, mock_get):
assert obj.access_token is None assert obj.access_token is None
@mock.patch('requests.put')
@mock.patch('requests.get') @mock.patch('requests.get')
@mock.patch('requests.post') @mock.patch('requests.post')
def test_plugin_matrix_rooms(mock_post, mock_get): def test_plugin_matrix_rooms(mock_post, mock_get, mock_put):
""" """
NotifyMatrix() Room Testing NotifyMatrix() Room Testing
@ -606,6 +615,7 @@ def test_plugin_matrix_rooms(mock_post, mock_get):
request.content = dumps(response_obj) request.content = dumps(response_obj)
mock_post.return_value = request mock_post.return_value = request
mock_get.return_value = request mock_get.return_value = request
mock_put.return_value = request
obj = NotifyMatrix(host='host') obj = NotifyMatrix(host='host')
assert isinstance(obj, NotifyMatrix) is True assert isinstance(obj, NotifyMatrix) is True
@ -789,9 +799,10 @@ def test_plugin_matrix_url_parsing():
assert '#room3' in result['targets'] assert '#room3' in result['targets']
@mock.patch('requests.put')
@mock.patch('requests.get') @mock.patch('requests.get')
@mock.patch('requests.post') @mock.patch('requests.post')
def test_plugin_matrix_image_errors(mock_post, mock_get): def test_plugin_matrix_image_errors(mock_post, mock_get, mock_put):
""" """
NotifyMatrix() Image Error Handling NotifyMatrix() Image Error Handling
@ -822,8 +833,9 @@ def test_plugin_matrix_image_errors(mock_post, mock_get):
# Prepare Mock # Prepare Mock
mock_get.side_effect = mock_function_handing mock_get.side_effect = mock_function_handing
mock_post.side_effect = mock_function_handing mock_post.side_effect = mock_function_handing
mock_put.side_effect = mock_function_handing
obj = NotifyMatrix(host='host', include_image=True) obj = NotifyMatrix(host='host', include_image=True, version='2')
assert isinstance(obj, NotifyMatrix) is True assert isinstance(obj, NotifyMatrix) is True
assert obj.access_token is None assert obj.access_token is None
@ -831,7 +843,7 @@ def test_plugin_matrix_image_errors(mock_post, mock_get):
# we had post errors (of any kind) we still report a failure. # we had post errors (of any kind) we still report a failure.
assert obj.notify('test', 'test') is False assert obj.notify('test', 'test') is False
obj = NotifyMatrix(host='host', include_image=False) obj = NotifyMatrix(host='host', include_image=False, version='2')
assert isinstance(obj, NotifyMatrix) is True assert isinstance(obj, NotifyMatrix) is True
assert obj.access_token is None assert obj.access_token is None
@ -862,6 +874,7 @@ def test_plugin_matrix_image_errors(mock_post, mock_get):
# Prepare Mock # Prepare Mock
mock_get.side_effect = mock_function_handing mock_get.side_effect = mock_function_handing
mock_put.side_effect = mock_function_handing
mock_post.side_effect = mock_function_handing mock_post.side_effect = mock_function_handing
obj = NotifyMatrix(host='host', include_image=True) obj = NotifyMatrix(host='host', include_image=True)
assert isinstance(obj, NotifyMatrix) is True assert isinstance(obj, NotifyMatrix) is True
@ -879,9 +892,10 @@ def test_plugin_matrix_image_errors(mock_post, mock_get):
del obj del obj
@mock.patch('requests.put')
@mock.patch('requests.get') @mock.patch('requests.get')
@mock.patch('requests.post') @mock.patch('requests.post')
def test_plugin_matrix_attachments_api_v3(mock_post, mock_get): def test_plugin_matrix_attachments_api_v3(mock_post, mock_get, mock_put):
""" """
NotifyMatrix() Attachment Checks (v3) NotifyMatrix() Attachment Checks (v3)
@ -899,6 +913,7 @@ def test_plugin_matrix_attachments_api_v3(mock_post, mock_get):
# Prepare Mock return object # Prepare Mock return object
mock_post.return_value = response mock_post.return_value = response
mock_get.return_value = response mock_get.return_value = response
mock_put.return_value = response
# Instantiate our object # Instantiate our object
obj = Apprise.instantiate('matrix://user:pass@localhost/#general?v=3') obj = Apprise.instantiate('matrix://user:pass@localhost/#general?v=3')
@ -913,26 +928,22 @@ def test_plugin_matrix_attachments_api_v3(mock_post, mock_get):
attach = AppriseAttachment(os.path.join(TEST_VAR_DIR, 'apprise-test.gif')) attach = AppriseAttachment(os.path.join(TEST_VAR_DIR, 'apprise-test.gif'))
# Test our call count # Test our call count
assert mock_post.call_count == 5 assert mock_put.call_count == 1
assert mock_post.call_count == 2
assert mock_post.call_args_list[0][0][0] == \ assert mock_post.call_args_list[0][0][0] == \
'http://localhost/_matrix/client/v3/login' 'http://localhost/_matrix/client/v3/login'
assert mock_post.call_args_list[1][0][0] == \ assert mock_post.call_args_list[1][0][0] == \
'http://localhost/_matrix/media/v3/upload'
assert mock_post.call_args_list[2][0][0] == \
'http://localhost/_matrix/client/v3/join/%23general%3Alocalhost' 'http://localhost/_matrix/client/v3/join/%23general%3Alocalhost'
assert mock_post.call_args_list[3][0][0] == \ assert mock_put.call_args_list[0][0][0] == \
'http://localhost/_matrix/client/v3/rooms/%21abc123%3Alocalhost/' \ 'http://localhost/_matrix/client/v3/rooms/%21abc123%3Alocalhost/' \
'send/m.room.message' 'send/m.room.message/0'
assert mock_post.call_args_list[4][0][0] == \
'http://localhost/_matrix/client/v3/rooms/%21abc123%3Alocalhost/' \
'send/m.room.message'
# Attach an unsupported file type # Attach an unsupported file type (it's just skipped)
attach = AppriseAttachment( attach = AppriseAttachment(
os.path.join(TEST_VAR_DIR, 'apprise-archive.zip')) os.path.join(TEST_VAR_DIR, 'apprise-archive.zip'))
assert obj.notify( assert obj.notify(
body='body', title='title', notify_type=NotifyType.INFO, body='body', title='title', notify_type=NotifyType.INFO,
attach=attach) is False attach=attach) is True
# An invalid attachment will cause a failure # An invalid attachment will cause a failure
path = os.path.join(TEST_VAR_DIR, '/invalid/path/to/an/invalid/file.jpg') path = os.path.join(TEST_VAR_DIR, '/invalid/path/to/an/invalid/file.jpg')
@ -949,23 +960,23 @@ def test_plugin_matrix_attachments_api_v3(mock_post, mock_get):
for side_effect in (requests.RequestException(), OSError(), bad_response): for side_effect in (requests.RequestException(), OSError(), bad_response):
mock_post.side_effect = [side_effect] mock_post.side_effect = [side_effect]
# We'll fail now because of our error handling # We'll never fail because files are not attached
assert obj.send(body="test", attach=attach) is False assert obj.send(body="test", attach=attach) is True
# Throw an exception on the second call to requests.post() # Throw an exception on the second call to requests.post()
for side_effect in (requests.RequestException(), OSError(), bad_response): for side_effect in (requests.RequestException(), OSError(), bad_response):
mock_post.side_effect = [response, side_effect] mock_post.side_effect = [response, side_effect]
# We'll fail now because of our error handling # Attachment support does not exist vor v3 at time, so this will
assert obj.send(body="test", attach=attach) is False # work nicely
assert obj.send(body="test", attach=attach) is True
# handle a bad response # handle a bad response
bad_response = mock.Mock()
bad_response.status_code = requests.codes.internal_server_error
mock_post.side_effect = [response, bad_response, response] mock_post.side_effect = [response, bad_response, response]
# We'll fail now because of an internal exception # Attachment support does not exist vor v3 at time, so this will
assert obj.send(body="test", attach=attach) is False # work nicely
assert obj.send(body="test", attach=attach) is True
# Force a object removal (thus a logout call) # Force a object removal (thus a logout call)
del obj del obj
@ -993,7 +1004,7 @@ def test_plugin_matrix_attachments_api_v2(mock_post, mock_get):
mock_get.return_value = response mock_get.return_value = response
# Instantiate our object # Instantiate our object
obj = Apprise.instantiate('matrix://user:pass@localhost/#general?v=3') obj = Apprise.instantiate('matrix://user:pass@localhost/#general?v=2')
# attach our content # attach our content
attach = AppriseAttachment(os.path.join(TEST_VAR_DIR, 'apprise-test.gif')) attach = AppriseAttachment(os.path.join(TEST_VAR_DIR, 'apprise-test.gif'))
@ -1013,13 +1024,13 @@ def test_plugin_matrix_attachments_api_v2(mock_post, mock_get):
# Force a object removal (thus a logout call) # Force a object removal (thus a logout call)
del obj del obj
# Instantiate our object
obj = Apprise.instantiate('matrixs://user:pass@localhost/#general?v=2')
# Reset our object # Reset our object
mock_post.reset_mock() mock_post.reset_mock()
mock_get.reset_mock() mock_get.reset_mock()
# Instantiate our object
obj = Apprise.instantiate('matrixs://user:pass@localhost/#general?v=2')
assert obj.notify( assert obj.notify(
body='body', title='title', notify_type=NotifyType.INFO, body='body', title='title', notify_type=NotifyType.INFO,
attach=attach) is True attach=attach) is True
@ -1039,12 +1050,12 @@ def test_plugin_matrix_attachments_api_v2(mock_post, mock_get):
'https://localhost/_matrix/client/r0/rooms/%21abc123%3Alocalhost/' \ 'https://localhost/_matrix/client/r0/rooms/%21abc123%3Alocalhost/' \
'send/m.room.message' 'send/m.room.message'
# Attach an unsupported file type # Attach an unsupported file type; these are skipped
attach = AppriseAttachment( attach = AppriseAttachment(
os.path.join(TEST_VAR_DIR, 'apprise-archive.zip')) os.path.join(TEST_VAR_DIR, 'apprise-archive.zip'))
assert obj.notify( assert obj.notify(
body='body', title='title', notify_type=NotifyType.INFO, body='body', title='title', notify_type=NotifyType.INFO,
attach=attach) is False attach=attach) is True
# An invalid attachment will cause a failure # An invalid attachment will cause a failure
path = os.path.join(TEST_VAR_DIR, '/invalid/path/to/an/invalid/file.jpg') path = os.path.join(TEST_VAR_DIR, '/invalid/path/to/an/invalid/file.jpg')
@ -1083,8 +1094,6 @@ def test_plugin_matrix_attachments_api_v2(mock_post, mock_get):
assert obj.send(body="test", attach=attach) is False assert obj.send(body="test", attach=attach) is False
# handle a bad response # handle a bad response
bad_response = mock.Mock()
bad_response.status_code = requests.codes.internal_server_error
mock_post.side_effect = \ mock_post.side_effect = \
[response, bad_response, response, response, response, response] [response, bad_response, response, response, response, response]
mock_get.side_effect = \ mock_get.side_effect = \
@ -1093,5 +1102,38 @@ def test_plugin_matrix_attachments_api_v2(mock_post, mock_get):
# We'll fail now because of an internal exception # We'll fail now because of an internal exception
assert obj.send(body="test", attach=attach) is False assert obj.send(body="test", attach=attach) is False
# Force a object removal (thus a logout call)
del obj
# Instantiate our object
obj = Apprise.instantiate(
'matrixs://user:pass@localhost/#general?v=2&image=y')
# Reset our object
mock_post.reset_mock()
mock_get.reset_mock()
mock_post.return_value = None
mock_get.return_value = None
mock_post.side_effect = \
[response, response, bad_response, response, response, response,
response]
mock_get.side_effect = \
[response, response, bad_response, response, response, response,
response]
# image attachment didn't succeed
assert obj.notify(
body='body', title='title', notify_type=NotifyType.INFO) is False
# Error during image post
mock_post.return_value = response
mock_get.return_value = response
mock_post.side_effect = None
mock_get.side_effect = None
# We'll fail now because of an internal exception
assert obj.send(body="test", attach=attach) is True
# Force __del__() call # Force __del__() call
del obj del obj