From a0a78bbad097a36101d205bf7588758ab00a9832 Mon Sep 17 00:00:00 2001 From: Guilhem Saurel Date: Wed, 14 Jul 2021 20:00:52 +0200 Subject: [PATCH 1/8] improve logging --- matrix_webhook.py | 22 ++++++++++++---------- tests/Dockerfile | 2 +- tests/start.py | 20 +++++++++++++++++++- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/matrix_webhook.py b/matrix_webhook.py index f754680..c52fe43 100755 --- a/matrix_webhook.py +++ b/matrix_webhook.py @@ -73,7 +73,6 @@ parser.add_argument( ) args = parser.parse_args() -logging.basicConfig(level=50 - 10 * args.verbose) SERVER_ADDRESS = (args.host, args.port) MATRIX_URL = args.matrix_url @@ -81,6 +80,7 @@ MATRIX_ID = args.matrix_id MATRIX_PW = args.matrix_pw API_KEY = args.api_key CLIENT = AsyncClient(args.matrix_url, args.matrix_id) +LOGGER = logging.getLogger("matrix-webhook") async def handler(request): @@ -89,7 +89,7 @@ async def handler(request): This one handles a POST, checks its content, and forwards it to the matrix room. """ - logging.debug(f"Handling {request=}") + LOGGER.debug(f"Handling {request=}") data = await request.read() try: @@ -115,8 +115,8 @@ async def handler(request): try: await send_room_message(room_id, content) except LocalProtocolError as e: # Connection lost, try another login - logging.error(f"Send error: {e}") - logging.warning("Reconnecting and trying again") + LOGGER.error(f"Send error: {e}") + LOGGER.warning("Reconnecting and trying again") await CLIENT.login(MATRIX_PW) await send_room_message(room_id, content) @@ -125,14 +125,14 @@ async def handler(request): def create_json_response(status, ret): """Create a JSON response.""" - logging.debug(f"Creating json response: {status=}, {ret=}") + LOGGER.debug(f"Creating json response: {status=}, {ret=}") response_data = {"status": status, "ret": ret} return web.json_response(response_data, status=status) async def send_room_message(room_id, content): """Send a message to a room.""" - logging.debug(f"Sending room message in {room_id=}: {content=}") + LOGGER.debug(f"Sending room message in {room_id=}: {content=}") return await CLIENT.room_send( room_id=room_id, message_type="m.room.message", content=content ) @@ -144,13 +144,13 @@ async def main(event): matrix client login & start web server """ - logging.info(f"Log in {MATRIX_ID=} on {MATRIX_URL=}") + LOGGER.info(f"Log in {MATRIX_ID=} on {MATRIX_URL=}") await CLIENT.login(MATRIX_PW) server = web.Server(handler) runner = web.ServerRunner(server) await runner.setup() - logging.info(f"Binding on {SERVER_ADDRESS=}") + LOGGER.info(f"Binding on {SERVER_ADDRESS=}") site = web.TCPSite(runner, *SERVER_ADDRESS) await site.start() @@ -170,7 +170,7 @@ def terminate(event, signal): def run(): """Launch everything.""" - logging.info("Matrix Webhook starting...") + LOGGER.info("Starting...") loop = asyncio.get_event_loop() event = asyncio.Event() @@ -179,9 +179,11 @@ def run(): loop.run_until_complete(main(event)) - logging.info("Matrix Webhook closing...") + LOGGER.info("Closing...") loop.close() if __name__ == "__main__": + log_format = "%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(message)s" + logging.basicConfig(level=50 - 10 * args.verbose, format=log_format) run() diff --git a/tests/Dockerfile b/tests/Dockerfile index 0ff4cb2..42b60b9 100644 --- a/tests/Dockerfile +++ b/tests/Dockerfile @@ -17,4 +17,4 @@ RUN pip install --no-cache-dir markdown matrix-nio httpx coverage WORKDIR /app -CMD ./tests/start.py +CMD ./tests/start.py -vvv diff --git a/tests/start.py b/tests/start.py index 27c184c..3124927 100755 --- a/tests/start.py +++ b/tests/start.py @@ -1,6 +1,8 @@ #!/usr/bin/env python """Entry point to start an instrumentalized bot for coverage and run tests.""" +import argparse +import logging from os import environ from subprocess import Popen, run from time import time @@ -15,6 +17,12 @@ KEY, MATRIX_URL, MATRIX_ID, MATRIX_PW = ( environ[v] for v in ["API_KEY", "MATRIX_URL", "MATRIX_ID", "MATRIX_PW"] ) FULL_ID = f'@{MATRIX_ID}:{MATRIX_URL.split("/")[2]}' +LOGGER = logging.getLogger("matrix-webhook.tests.start") + +parser = argparse.ArgumentParser(description=__doc__) +parser.add_argument( + "-v", "--verbose", action="count", default=0, help="increment verbosity level" +) def bot_req(req=None, key=None, room_id=None): @@ -47,6 +55,7 @@ def wait_available(url: str, key: str, timeout: int = 10) -> bool: def run_and_test(): """Launch the bot and its tests.""" # Start the server, and wait for it + LOGGER.info("Spawning synapse") srv = Popen( [ "python", @@ -60,29 +69,38 @@ def run_and_test(): return False # Register a user for the bot. + LOGGER.info("Registering the bot") with open("/srv/homeserver.yaml") as f: secret = yaml.safe_load(f.read()).get("registration_shared_secret", None) request_registration(MATRIX_ID, MATRIX_PW, MATRIX_URL, secret, admin=True) # Start the bot, and wait for it - bot = Popen(["coverage", "run", "matrix_webhook.py"]) + LOGGER.info("Spawning the bot") + bot = Popen(["coverage", "run", "matrix_webhook.py", "-vvvvv"]) if not wait_available(BOT_URL, "status"): return False # Run the main unittest module + LOGGER.info("Runnig unittests") ret = main(module=None, exit=False).result.wasSuccessful() + LOGGER.info("Stopping synapse") srv.terminate() # TODO Check what the bot says when the server is offline # print(bot_req({'text': 'bye'}, KEY), {'status': 200, 'ret': 'OK'}) + LOGGER.info("Stopping the bot") bot.terminate() + LOGGER.info("Processing coverage") for cmd in ["report", "html", "xml"]: run(["coverage", cmd]) return ret if __name__ == "__main__": + args = parser.parse_args() + log_format = "%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(message)s" + logging.basicConfig(level=50 - 10 * args.verbose, format=log_format) exit(not run_and_test()) From 0c0a42a4c91e69252523c854197d9a75298b9e29 Mon Sep 17 00:00:00 2001 From: Guilhem Saurel Date: Wed, 14 Jul 2021 23:25:24 +0200 Subject: [PATCH 2/8] improve error management and tests --- matrix_webhook.py | 29 ++++++++++++++++++++++------- tests/tests.py | 20 +++++++++++++++++--- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/matrix_webhook.py b/matrix_webhook.py index c52fe43..70d3234 100755 --- a/matrix_webhook.py +++ b/matrix_webhook.py @@ -17,6 +17,7 @@ from aiohttp import web from markdown import markdown from nio import AsyncClient from nio.exceptions import LocalProtocolError +from nio.responses import RoomSendError parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( @@ -81,6 +82,7 @@ MATRIX_PW = args.matrix_pw API_KEY = args.api_key CLIENT = AsyncClient(args.matrix_url, args.matrix_id) LOGGER = logging.getLogger("matrix-webhook") +ERROR_MAP = {"M_FORBIDDEN": HTTPStatus.FORBIDDEN} async def handler(request): @@ -112,13 +114,26 @@ async def handler(request): "format": "org.matrix.custom.html", "formatted_body": markdown(str(data["text"]), extensions=["extra"]), } - try: - await send_room_message(room_id, content) - except LocalProtocolError as e: # Connection lost, try another login - LOGGER.error(f"Send error: {e}") - LOGGER.warning("Reconnecting and trying again") - await CLIENT.login(MATRIX_PW) - await send_room_message(room_id, content) + for _ in range(10): + try: + resp = await send_room_message(room_id, content) + if isinstance(resp, RoomSendError): + if resp.status_code == "M_UNKNOWN_TOKEN": + LOGGER.warning("Reconnecting") + await CLIENT.login(MATRIX_PW) + else: + return create_json_response( + ERROR_MAP[resp.status_code], resp.message + ) + else: + break + except LocalProtocolError as e: + LOGGER.error(f"Send error: {e}") + LOGGER.warning("Trying again") + else: + return create_json_response( + HTTPStatus.GATEWAY_TIMEOUT, "Homeserver not responding" + ) return create_json_response(HTTPStatus.OK, "OK") diff --git a/tests/tests.py b/tests/tests.py index f6ec6f9..c92fb95 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -20,9 +20,11 @@ class BotTest(unittest.IsolatedAsyncioTestCase): self.assertEqual( bot_req({"text": 3, "key": None}), {"status": 401, "ret": "Invalid API key"} ) - - # TODO: we are not sending to a real room, so this should not be "OK" - self.assertEqual(bot_req({"text": 3}, KEY), {"status": 200, "ret": "OK"}) + # TODO: if the client from matrix_webhook has olm support, this won't be a 403 from synapse, + # but a LocalProtocolError from matrix_webhook + self.assertEqual( + bot_req({"text": 3}, KEY), {"status": 403, "ret": "Unknown room"} + ) async def test_message(self): """Send a markdown message, and check the result.""" @@ -45,3 +47,15 @@ class BotTest(unittest.IsolatedAsyncioTestCase): self.assertEqual(message.sender, FULL_ID) self.assertEqual(message.body, text) self.assertEqual(message.formatted_body, "

Hello

") + + async def test_reconnect(self): + """Check the reconnecting path.""" + client = nio.AsyncClient(MATRIX_URL, MATRIX_ID) + await client.login(MATRIX_PW) + room = await client.room_create() + await client.logout(all_devices=True) + await client.close() + self.assertEqual( + bot_req({"text": "Re"}, KEY, room.room_id), + {"status": 200, "ret": "OK"}, + ) From a974f073c96f645f79afee543a3b2aefe1bac74b Mon Sep 17 00:00:00 2001 From: Guilhem Saurel Date: Sun, 18 Jul 2021 16:52:39 +0200 Subject: [PATCH 3/8] split code --- Dockerfile | 4 +- .../__main__.py | 82 +++---------------- matrix_webhook/conf.py | 66 +++++++++++++++ tests/start.py | 2 +- 4 files changed, 80 insertions(+), 74 deletions(-) rename matrix_webhook.py => matrix_webhook/__main__.py (63%) create mode 100644 matrix_webhook/conf.py diff --git a/Dockerfile b/Dockerfile index da6ffa2..688b216 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,6 +4,6 @@ EXPOSE 4785 RUN pip install --no-cache-dir markdown matrix-nio -ADD matrix_webhook.py / +ADD matrix_webhook . -CMD /matrix_webhook.py +CMD python -m matrix_webhook diff --git a/matrix_webhook.py b/matrix_webhook/__main__.py similarity index 63% rename from matrix_webhook.py rename to matrix_webhook/__main__.py index 70d3234..7b7db33 100755 --- a/matrix_webhook.py +++ b/matrix_webhook/__main__.py @@ -5,11 +5,9 @@ Matrix Webhook. Post a message to a matrix room with a simple HTTP POST """ -import argparse import asyncio import json import logging -import os from http import HTTPStatus from signal import SIGINT, SIGTERM @@ -19,71 +17,13 @@ from nio import AsyncClient from nio.exceptions import LocalProtocolError from nio.responses import RoomSendError -parser = argparse.ArgumentParser(description=__doc__) -parser.add_argument( - "-H", - "--host", - default=os.environ.get("HOST", ""), - help="host to listen to. Default: `''`. Environment variable: `HOST`", -) -parser.add_argument( - "-P", - "--port", - type=int, - default=os.environ.get("PORT", 4785), - help="port to listed to. Default: 4785. Environment variable: `PORT`", -) -parser.add_argument( - "-u", - "--matrix-url", - default=os.environ.get("MATRIX_URL", "https://matrix.org"), - help="matrix homeserver url. Default: `https://matrix.org`. Environment variable: `MATRIX_URL`", -) -parser.add_argument( - "-i", - "--matrix-id", - help="matrix user-id. Required. Environment variable: `MATRIX_ID`", - **( - {"default": os.environ["MATRIX_ID"]} - if "MATRIX_ID" in os.environ - else {"required": True} - ), -) -parser.add_argument( - "-p", - "--matrix-pw", - help="matrix password. Required. Environment variable: `MATRIX_PW`", - **( - {"default": os.environ["MATRIX_PW"]} - if "MATRIX_PW" in os.environ - else {"required": True} - ), -) -parser.add_argument( - "-k", - "--api-key", - help="shared secret to use this service. Required. Environment variable: `API_KEY`", - **( - {"default": os.environ["API_KEY"]} - if "API_KEY" in os.environ - else {"required": True} - ), -) -parser.add_argument( - "-v", "--verbose", action="count", default=0, help="increment verbosity level" -) +from . import conf -args = parser.parse_args() - -SERVER_ADDRESS = (args.host, args.port) -MATRIX_URL = args.matrix_url -MATRIX_ID = args.matrix_id -MATRIX_PW = args.matrix_pw -API_KEY = args.api_key -CLIENT = AsyncClient(args.matrix_url, args.matrix_id) -LOGGER = logging.getLogger("matrix-webhook") ERROR_MAP = {"M_FORBIDDEN": HTTPStatus.FORBIDDEN} +CLIENT = AsyncClient(conf.MATRIX_URL, conf.MATRIX_ID) +LOGGER = logging.getLogger("matrix-webhook") + async def handler(request): """ @@ -104,7 +44,7 @@ async def handler(request): HTTPStatus.BAD_REQUEST, "Missing text and/or API key property" ) - if data["key"] != API_KEY: + if data["key"] != conf.API_KEY: return create_json_response(HTTPStatus.UNAUTHORIZED, "Invalid API key") room_id = request.path[1:] @@ -120,7 +60,7 @@ async def handler(request): if isinstance(resp, RoomSendError): if resp.status_code == "M_UNKNOWN_TOKEN": LOGGER.warning("Reconnecting") - await CLIENT.login(MATRIX_PW) + await CLIENT.login(conf.MATRIX_PW) else: return create_json_response( ERROR_MAP[resp.status_code], resp.message @@ -159,14 +99,14 @@ async def main(event): matrix client login & start web server """ - LOGGER.info(f"Log in {MATRIX_ID=} on {MATRIX_URL=}") - await CLIENT.login(MATRIX_PW) + LOGGER.info(f"Log in {conf.MATRIX_ID=} on {conf.MATRIX_URL=}") + await CLIENT.login(conf.MATRIX_PW) server = web.Server(handler) runner = web.ServerRunner(server) await runner.setup() - LOGGER.info(f"Binding on {SERVER_ADDRESS=}") - site = web.TCPSite(runner, *SERVER_ADDRESS) + LOGGER.info(f"Binding on {conf.SERVER_ADDRESS=}") + site = web.TCPSite(runner, *conf.SERVER_ADDRESS) await site.start() # Run until we get a shutdown request @@ -200,5 +140,5 @@ def run(): if __name__ == "__main__": log_format = "%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(message)s" - logging.basicConfig(level=50 - 10 * args.verbose, format=log_format) + logging.basicConfig(level=50 - 10 * conf.VERBOSE, format=log_format) run() diff --git a/matrix_webhook/conf.py b/matrix_webhook/conf.py new file mode 100644 index 0000000..ae81672 --- /dev/null +++ b/matrix_webhook/conf.py @@ -0,0 +1,66 @@ +"""Configuration for Matrix Webhook.""" +import argparse +import os + +parser = argparse.ArgumentParser(description=__doc__) +parser.add_argument( + "-H", + "--host", + default=os.environ.get("HOST", ""), + help="host to listen to. Default: `''`. Environment variable: `HOST`", +) +parser.add_argument( + "-P", + "--port", + type=int, + default=os.environ.get("PORT", 4785), + help="port to listed to. Default: 4785. Environment variable: `PORT`", +) +parser.add_argument( + "-u", + "--matrix-url", + default=os.environ.get("MATRIX_URL", "https://matrix.org"), + help="matrix homeserver url. Default: `https://matrix.org`. Environment variable: `MATRIX_URL`", +) +parser.add_argument( + "-i", + "--matrix-id", + help="matrix user-id. Required. Environment variable: `MATRIX_ID`", + **( + {"default": os.environ["MATRIX_ID"]} + if "MATRIX_ID" in os.environ + else {"required": True} + ), +) +parser.add_argument( + "-p", + "--matrix-pw", + help="matrix password. Required. Environment variable: `MATRIX_PW`", + **( + {"default": os.environ["MATRIX_PW"]} + if "MATRIX_PW" in os.environ + else {"required": True} + ), +) +parser.add_argument( + "-k", + "--api-key", + help="shared secret to use this service. Required. Environment variable: `API_KEY`", + **( + {"default": os.environ["API_KEY"]} + if "API_KEY" in os.environ + else {"required": True} + ), +) +parser.add_argument( + "-v", "--verbose", action="count", default=0, help="increment verbosity level" +) + +args = parser.parse_args() + +SERVER_ADDRESS = (args.host, args.port) +MATRIX_URL = args.matrix_url +MATRIX_ID = args.matrix_id +MATRIX_PW = args.matrix_pw +API_KEY = args.api_key +VERBOSE = args.verbose diff --git a/tests/start.py b/tests/start.py index 3124927..3df3418 100755 --- a/tests/start.py +++ b/tests/start.py @@ -76,7 +76,7 @@ def run_and_test(): # Start the bot, and wait for it LOGGER.info("Spawning the bot") - bot = Popen(["coverage", "run", "matrix_webhook.py", "-vvvvv"]) + bot = Popen(["coverage", "run", "-m", "matrix_webhook", "-vvvvv"]) if not wait_available(BOT_URL, "status"): return False From bc646b9f4a3311311a7637aa7389a88ee0fd64f9 Mon Sep 17 00:00:00 2001 From: Guilhem Saurel Date: Sun, 18 Jul 2021 16:55:23 +0200 Subject: [PATCH 4/8] update year --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 939f05c..8960340 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2020 tetaneutral.net All rights reserved. +Copyright (c) 2019-2021 tetaneutral.net All rights reserved. BSD 2 Clause License From 00997360ebf8b43161ab541d6e0e6e2b69960fce Mon Sep 17 00:00:00 2001 From: Guilhem Saurel Date: Sun, 18 Jul 2021 16:55:54 +0200 Subject: [PATCH 5/8] fix permissions --- matrix_webhook/__main__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 matrix_webhook/__main__.py diff --git a/matrix_webhook/__main__.py b/matrix_webhook/__main__.py old mode 100755 new mode 100644 From 922ebf5c7807569b6141e4fc731d65e87d92cbea Mon Sep 17 00:00:00 2001 From: Guilhem Saurel Date: Sun, 18 Jul 2021 16:56:59 +0200 Subject: [PATCH 6/8] poetry update --- poetry.lock | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/poetry.lock b/poetry.lock index fcf852f..c600d93 100644 --- a/poetry.lock +++ b/poetry.lock @@ -87,7 +87,7 @@ tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (> [[package]] name = "black" -version = "21.6b0" +version = "21.7b0" description = "The uncompromising code formatter." category = "dev" optional = false @@ -99,7 +99,7 @@ click = ">=7.1.2" mypy-extensions = ">=0.4.3" pathspec = ">=0.8.1,<1" regex = ">=2020.1.8" -toml = ">=0.10.1" +tomli = ">=0.2.6,<2.0.0" [package.extras] colorama = ["colorama (>=0.4.3)"] @@ -301,7 +301,7 @@ testing = ["coverage", "pyyaml"] [[package]] name = "matrix-nio" -version = "0.18.3" +version = "0.18.4" description = "A Python Matrix client library, designed according to sans I/O principles." category = "main" optional = false @@ -320,7 +320,7 @@ pycryptodome = ">=3.10.1,<4.0.0" unpaddedbase64 = ">=2.1.0,<3.0.0" [package.extras] -e2e = ["python-olm (>=3.1.3,<4.0.0)", "peewee (>=3.14.4,<4.0.0)", "cachetools (>=4.2.1,<5.0.0)", "atomicwrites (>=1.4.0,<2.0.0)"] +e2e = ["atomicwrites (>=1.4.0,<2.0.0)", "cachetools (>=4.2.1,<5.0.0)", "peewee (>=3.14.4,<4.0.0)", "python-olm (>=3.1.3,<4.0.0)"] [[package]] name = "mccabe" @@ -348,11 +348,11 @@ python-versions = "*" [[package]] name = "pathspec" -version = "0.8.1" +version = "0.9.0" description = "Utility library for gitignore style pattern matching of file paths." category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [[package]] name = "pycodestyle" @@ -463,12 +463,12 @@ optional = false python-versions = "*" [[package]] -name = "toml" -version = "0.10.2" -description = "Python Library for Tom's Obvious, Minimal Language" +name = "tomli" +version = "1.0.4" +description = "A lil' TOML parser" category = "dev" optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +python-versions = ">=3.6" [[package]] name = "typing-extensions" @@ -568,8 +568,8 @@ attrs = [ {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, ] black = [ - {file = "black-21.6b0-py3-none-any.whl", hash = "sha256:dfb8c5a069012b2ab1e972e7b908f5fb42b6bbabcba0a788b86dc05067c7d9c7"}, - {file = "black-21.6b0.tar.gz", hash = "sha256:dc132348a88d103016726fe360cb9ede02cecf99b76e3660ce6c596be132ce04"}, + {file = "black-21.7b0-py3-none-any.whl", hash = "sha256:1c7aa6ada8ee864db745b22790a32f94b2795c253a75d6d9b5e439ff10d23116"}, + {file = "black-21.7b0.tar.gz", hash = "sha256:c8373c6491de9362e39271630b65b964607bc5c79c83783547d76c839b3aa219"}, ] certifi = [ {file = "certifi-2021.5.30-py2.py3-none-any.whl", hash = "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"}, @@ -696,8 +696,8 @@ markdown = [ {file = "Markdown-3.3.4.tar.gz", hash = "sha256:31b5b491868dcc87d6c24b7e3d19a0d730d59d3e46f4eea6430a321bed387a49"}, ] matrix-nio = [ - {file = "matrix-nio-0.18.3.tar.gz", hash = "sha256:7f2e92f5b219367e47824bfe8bd2b1a06ce83ae28956f112dd3c2112a4d27085"}, - {file = "matrix_nio-0.18.3-py3-none-any.whl", hash = "sha256:a28653f96760b045c7edc53b645872cf2facc1639dc8cf56d748cd5e54ed2d3d"}, + {file = "matrix-nio-0.18.4.tar.gz", hash = "sha256:e5f0a62ff66474f5c56dc40c3eb3c74a29943800589ae6947ea224c288f3ab41"}, + {file = "matrix_nio-0.18.4-py3-none-any.whl", hash = "sha256:7ea00ae362a3621624b8ff463a2b06cb945ffa12e2f3919cae5321d06285a361"}, ] mccabe = [ {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, @@ -747,8 +747,8 @@ mypy-extensions = [ {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, ] pathspec = [ - {file = "pathspec-0.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"}, - {file = "pathspec-0.8.1.tar.gz", hash = "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd"}, + {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, + {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, ] pycodestyle = [ {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, @@ -880,9 +880,9 @@ snowballstemmer = [ {file = "snowballstemmer-2.1.0-py2.py3-none-any.whl", hash = "sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2"}, {file = "snowballstemmer-2.1.0.tar.gz", hash = "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914"}, ] -toml = [ - {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, - {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +tomli = [ + {file = "tomli-1.0.4-py3-none-any.whl", hash = "sha256:0713b16ff91df8638a6a694e295c8159ab35ba93e3424a626dd5226d386057be"}, + {file = "tomli-1.0.4.tar.gz", hash = "sha256:be670d0d8d7570fd0ea0113bd7bb1ba3ac6706b4de062cc4c952769355c9c268"}, ] typing-extensions = [ {file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"}, From c9045d407d89776ebc49b7c98e928833179e115b Mon Sep 17 00:00:00 2001 From: Guilhem Saurel Date: Sun, 18 Jul 2021 18:12:08 +0200 Subject: [PATCH 7/8] update README --- Dockerfile | 4 +-- README.md | 62 ++++++++++++++++++++++++++++++-------- matrix_webhook/__main__.py | 1 - matrix_webhook/conf.py | 2 +- 4 files changed, 53 insertions(+), 16 deletions(-) diff --git a/Dockerfile b/Dockerfile index 688b216..2eb8198 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,6 +4,6 @@ EXPOSE 4785 RUN pip install --no-cache-dir markdown matrix-nio -ADD matrix_webhook . +ADD matrix_webhook matrix_webhook -CMD python -m matrix_webhook +ENTRYPOINT ["python", "-m", "matrix_webhook"] diff --git a/README.md b/README.md index 02ebc4d..d9517ed 100644 --- a/README.md +++ b/README.md @@ -7,27 +7,59 @@ Post a message to a matrix room with a simple HTTP POST -## Configuration +## Install -Create a matrix user for the bot, make it join the rooms you want it to talk into, and then set the following -environment variables: +``` +python3 -m pip install matrix-webhook +# OR +docker pull nim65s/matrix-webhook +``` + +## Start + +Create a matrix user for the bot, make it join the rooms you want it to talk into, and launch it with the following +arguments or environment variables: + +``` +python -m matrix_webhook -h +# OR +docker run --rm -it nim65s/matrix-webhook -h +``` + +``` +usage: python -m matrix_webhook [-h] [-H HOST] [-P PORT] [-u MATRIX_URL] -i MATRIX_ID -p MATRIX_PW -k API_KEY [-v] + +Configuration for Matrix Webhook. + + +optional arguments: + -h, --help show this help message and exit + -H HOST, --host HOST host to listen to. Default: `''`. Environment variable: `HOST` + -P PORT, --port PORT port to listed to. Default: 4785. Environment variable: `PORT` + -u MATRIX_URL, --matrix-url MATRIX_URL + matrix homeserver url. Default: `https://matrix.org`. Environment variable: `MATRIX_URL` + -i MATRIX_ID, --matrix-id MATRIX_ID + matrix user-id. Required. Environment variable: `MATRIX_ID` + -p MATRIX_PW, --matrix-pw MATRIX_PW + matrix password. Required. Environment variable: `MATRIX_PW` + -k API_KEY, --api-key API_KEY + shared secret to use this service. Required. Environment variable: `API_KEY` + -v, --verbose increment verbosity level +``` -- `MATRIX_URL`: the url of the matrix homeserver -- `MATRIX_ID`: the user id of the bot on this server -- `MATRIX_PW`: the password for this user -- `API_KEY`: a secret to share with the users of the service -- `HOST`: HOST to listen on, all interfaces if `''` (default). -- `PORT`: PORT to listed on, default to 4785. ## Dev ``` -pip3 install --user markdown matrix-nio -./matrix_webhook.py +poetry install +# or python3 -m pip install --user markdown matrix-nio +python3 -m matrix_webhook ``` ## Prod +A `docker-compose.yml` is provided: + - Use [Traefik](https://traefik.io/) on the `web` docker network, eg. with [proxyta.net](https://framagit.org/oxyta.net/proxyta.net) - Put the configuration into a `.env` file @@ -47,4 +79,10 @@ curl -d '{"text":"new contrib from toto: [44](http://radio.localhost/map/#44)", ## Test room -[#matrix-webhook:tetaneutral.net](https://matrix.to/#/!DPrUlnwOhBEfYwsDLh:matrix.org?via=laas.fr&via=tetaneutral.net&via=aen.im) +#matrix-webhook:tetaneutral.net](https://matrix.to/#/!DPrUlnwOhBEfYwsDLh:matrix.org?via=laas.fr&via=tetaneutral.net&via=aen.im) + +## Unit tests + +``` +docker-compose -f test.yml up --exit-code-from tests --force-recreate --build +``` diff --git a/matrix_webhook/__main__.py b/matrix_webhook/__main__.py index 7b7db33..2b4a7d7 100644 --- a/matrix_webhook/__main__.py +++ b/matrix_webhook/__main__.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 """ Matrix Webhook. diff --git a/matrix_webhook/conf.py b/matrix_webhook/conf.py index ae81672..fd39e02 100644 --- a/matrix_webhook/conf.py +++ b/matrix_webhook/conf.py @@ -2,7 +2,7 @@ import argparse import os -parser = argparse.ArgumentParser(description=__doc__) +parser = argparse.ArgumentParser(description=__doc__, prog="python -m matrix_webhook") parser.add_argument( "-H", "--host", From febf2f857c96c9569dd99516b4be04feaa7bc5c0 Mon Sep 17 00:00:00 2001 From: Guilhem Saurel Date: Sun, 18 Jul 2021 18:41:20 +0200 Subject: [PATCH 8/8] add changelog --- CHANGELOG.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..9dcb006 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,33 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +- Simplify code + in [#1](https://github.com/nim65s/matrix-webhook/pull/1) + by [@homeworkprod](https://github.com/homeworkprod) +- Update aiohttp use and docs + in [#5](https://github.com/nim65s/matrix-webhook/pull/5) + by [@svenseeberg](https://github.com/svenseeberg) +- Setup Tests, Coverage & CI ; update tooling + in [#7](https://github.com/nim65s/matrix-webhook/pull/7) + by [@nim65s](https://github.com/nim65s) +- Setup argparse & logging + in [#8](https://github.com/nim65s/matrix-webhook/pull/8) + by [@nim65s](https://github.com/nim65s) +- Setup packaging + in [#9](https://github.com/nim65s/matrix-webhook/pull/9) + by [@nim65s](https://github.com/nim65s) + +## [1.0.0] - 2020-03-14 +- Update to matrix-nio & aiohttp & markdown + +## [1.0.0] - 2020-02-14 +- First release with matrix-client & http.server + +[Unreleased]: https://github.com/nim65s/matrix-webhook/compare/v2.0.0...devel +[2.0.0]: https://github.com/nim65s/matrix-webhook/compare/v1.0.0...v2.0.0 +[1.0.0]: https://github.com/nim65s/matrix-webhook/releases/tag/v1.0.0