From aea8668d0a6961042cb2485ec25f6a43b28efe06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Christian=20Gr=C3=BCnhage?= Date: Mon, 10 Feb 2020 18:23:03 +0100 Subject: [PATCH] migrate modules matrix-nio and add tests --- matrix-login.py | 32 +++++++++----- matrix-logout.py | 25 +++++------ matrix.py => matrix-notification.py | 65 +++++++++++++++++++++-------- test-login-logout.sh | 11 +++++ test-notification.sh | 7 ++++ test-settings.sample.sh | 7 ++++ 6 files changed, 106 insertions(+), 41 deletions(-) rename matrix.py => matrix-notification.py (66%) create mode 100755 test-login-logout.sh create mode 100755 test-notification.sh create mode 100644 test-settings.sample.sh diff --git a/matrix-login.py b/matrix-login.py index a9b96cc..5bf254d 100644 --- a/matrix-login.py +++ b/matrix-login.py @@ -3,7 +3,7 @@ # (c) 2018, Jan Christian Grünhage # (c) 2020, Famedly GmbH -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# GNU Affero General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/agpl-3.0.txt) from __future__ import (absolute_import, division, print_function) __metaclass__ = type @@ -35,7 +35,7 @@ options: - The password to log in with required: true requirements: - - matrix-client (Python library) + - matrix-nio (Python library) ''' EXAMPLES = ''' @@ -51,22 +51,26 @@ token: description: The access token aquired by logging in returned: When login was successful type: str +device_id: + description: The device ID assigned by the server + returned: When login was successful + type: str ''' import traceback +import asyncio from ansible.module_utils.basic import AnsibleModule, missing_required_lib MATRIX_IMP_ERR = None try: - from matrix_client.client import MatrixClient + from nio import AsyncClient except ImportError: MATRIX_IMP_ERR = traceback.format_exc() matrix_found = False else: matrix_found = True - -def run_module(): +async def run_module(): module_args = dict( hs_url=dict(type='str', required=True), user_id=dict(type='str', required=True), @@ -83,22 +87,28 @@ def run_module(): ) if not matrix_found: - module.fail_json(msg=missing_required_lib('matrix-client'), exception=MATRIX_IMP_ERR) + module.fail_json(msg=missing_required_lib('matrix-nio'), exception=MATRIX_IMP_ERR) if module.check_mode: return result - # create a client object - client = MatrixClient(module.params['hs_url']) - token = client.login(module.params['user_id'], module.params['password'], sync=False) + # Create client object + client = AsyncClient(module.params['hs_url'], module.params['user_id']) + # Log in + login_response = await client.login(module.params['password']) - result['token'] = token + # Store results + result['token'] = login_response.access_token + result['device_id'] = login_response.device_id + + # Close client sessions + await client.close() module.exit_json(**result) def main(): - run_module() + asyncio.run(run_module()) if __name__ == '__main__': diff --git a/matrix-logout.py b/matrix-logout.py index 241458d..d4ac3fe 100644 --- a/matrix-logout.py +++ b/matrix-logout.py @@ -3,7 +3,7 @@ # (c) 2018, Jan Christian Grünhage # (c) 2020, Famedly GmbH -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# GNU Affero General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/agpl-3.0.txt) from __future__ import (absolute_import, division, print_function) __metaclass__ = type @@ -31,7 +31,7 @@ options: - Authentication token for the API call required: true requirements: - - matrix-client (Python library) + - matrix-nio (Python library) ''' EXAMPLES = ''' @@ -44,20 +44,20 @@ EXAMPLES = ''' RETURN = ''' ''' import traceback +import asyncio from ansible.module_utils.basic import AnsibleModule, missing_required_lib MATRIX_IMP_ERR = None try: - from matrix_client.client import MatrixClient + from nio import AsyncClient except ImportError: MATRIX_IMP_ERR = traceback.format_exc() matrix_found = False else: matrix_found = True - -def run_module(): +async def run_module(): module_args = dict( hs_url=dict(type='str', required=True), token=dict(type='str', required=True, no_log=True), @@ -73,23 +73,24 @@ def run_module(): ) if not matrix_found: - module.fail_json(msg=missing_required_lib('matrix-client'), exception=MATRIX_IMP_ERR) + module.fail_json(msg=missing_required_lib('matrix-nio'), exception=MATRIX_IMP_ERR) if module.check_mode: return result # create a client object - client = MatrixClient(module.params['hs_url']) - client.api.token = module.params['token'] - - client.logout() + client = AsyncClient(module.params['hs_url']) + client.access_token = module.params['access_token'] + # log out + await client.logout() + # close client sessions + await client.close() module.exit_json(**result) def main(): - run_module() - + asyncio.run(run_module()) if __name__ == '__main__': main() diff --git a/matrix.py b/matrix-notification.py similarity index 66% rename from matrix.py rename to matrix-notification.py index 658b9a6..edd3852 100644 --- a/matrix.py +++ b/matrix-notification.py @@ -2,7 +2,8 @@ # coding: utf-8 # (c) 2018, Jan Christian Grünhage -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# (c) 2020, Famedly GmbH +# GNU Affero General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/agpl-3.0.txt) from __future__ import (absolute_import, division, print_function) __metaclass__ = type @@ -16,7 +17,7 @@ ANSIBLE_METADATA = { DOCUMENTATION = ''' --- author: "Jan Christian Grünhage (@jcgruenhage)" -module: matrix +module: matrix-notification short_description: Send notifications to matrix description: - This module sends html formatted notifications to matrix rooms. @@ -48,12 +49,12 @@ options: description: - The password to log in with requirements: - - matrix-client (Python library) + - matrix-nio (Python library) ''' EXAMPLES = ''' - name: Send matrix notification with token - matrix: + matrix-notification: msg_plain: "**hello world**" msg_html: "hello world" room_id: "!12345678:server.tld" @@ -61,7 +62,7 @@ EXAMPLES = ''' token: "{{ matrix_auth_token }}" - name: Send matrix notification with user_id and password - matrix: + matrix-notification: msg_plain: "**hello world**" msg_html: "hello world" room_id: "!12345678:server.tld" @@ -73,20 +74,30 @@ EXAMPLES = ''' RETURN = ''' ''' import traceback +import asyncio from ansible.module_utils.basic import AnsibleModule, missing_required_lib MATRIX_IMP_ERR = None try: - from matrix_client.client import MatrixClient + from nio import AsyncClient except ImportError: MATRIX_IMP_ERR = traceback.format_exc() matrix_found = False else: matrix_found = True +async def get_client_with_token(hs_url, token): + client = AsyncClient(hs_url) + client.access_token = token + return client -def run_module(): +async def get_client_with_password(hs_url, user, password): + client = AsyncClient(hs_url, user) + await client.login(password) + return client + +async def run_module(): module_args = dict( msg_plain=dict(type='str', required=True), msg_html=dict(type='str', required=True), @@ -98,7 +109,7 @@ def run_module(): ) result = dict( - changed=False, + changed=True, message='' ) @@ -111,28 +122,46 @@ def run_module(): ) if not matrix_found: - module.fail_json(msg=missing_required_lib('matrix-client'), exception=MATRIX_IMP_ERR) + module.fail_json(msg=missing_required_lib('matrix-nio'), exception=MATRIX_IMP_ERR) if module.check_mode: return result # create a client object - client = MatrixClient(module.params['hs_url']) if module.params['token'] is not None: - client.api.token = module.params['token'] + client = await get_client_with_token( + module.params['hs_url'], + module.params['token'] + ) else: - client.login(module.params['user_id'], module.params['password'], sync=False) + client = await get_client_with_password( + module.params['hs_url'], + module.params['user_id'], + module.params['password'] + ) - # make sure we are in a given room and return a room object for it - room = client.join_room(module.params['room_id']) - # send an html formatted messages - room.send_html(module.params['msg_html'], module.params['msg_plain']) + # send message + await client.room_send( + room_id=module.params['room_id'], + message_type="m.room.message", + content={ + "msgtype": "m.text", + "body": module.params['msg_plain'], + "format": "org.matrix.custom.html", + "formatted_body": module.params['msg_html'], + } + ) + + # when we did the login ourselves, invalidate the access token + if module.params['token'] is None: + await client.logout() + + await client.close() module.exit_json(**result) - def main(): - run_module() + asyncio.run(run_module()) if __name__ == '__main__': diff --git a/test-login-logout.sh b/test-login-logout.sh new file mode 100755 index 0000000..a4debf7 --- /dev/null +++ b/test-login-logout.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +source test-settings.sh + +login_resp=` echo "{\"ANSIBLE_MODULE_ARGS\": {\"hs_url\": \"${HS_URL}\",\"user_id\": \"${USER_ID}\",\"password\": \"${PASSWORD}\"}}" | python matrix-login.py` + +echo $login_resp + +local_token=`echo $login_resp | jq --raw-output '.token'` + +echo "{\"ANSIBLE_MODULE_ARGS\": {\"hs_url\": \"${HS_URL}\",\"token\": \"${local_token}\"}}" | python matrix-logout.py diff --git a/test-notification.sh b/test-notification.sh new file mode 100755 index 0000000..3dc6c53 --- /dev/null +++ b/test-notification.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +source test-settings.sh + +notification_resp=` echo "{\"ANSIBLE_MODULE_ARGS\": {\"hs_url\": \"${HS_URL}\", \"token\": \"${TOKEN}\", \"room_id\": \"${ROOM_ID}\", \"msg_plain\": \"**Hello, World!**\", \"msg_html\": \"Hello, World!\"}}" | python matrix-notification.py` + +echo $notification_resp diff --git a/test-settings.sample.sh b/test-settings.sample.sh new file mode 100644 index 0000000..566a015 --- /dev/null +++ b/test-settings.sample.sh @@ -0,0 +1,7 @@ +HS_URL= +USER_ID= +PASSWORD= +TOKEN= + +ROOM_ID= +ROOM_ALIAS=