diff --git a/matrix_webhook.py b/matrix_webhook.py index 5540f59..f754680 100755 --- a/matrix_webhook.py +++ b/matrix_webhook.py @@ -3,12 +3,12 @@ Matrix Webhook. Post a message to a matrix room with a simple HTTP POST -v1: matrix-client & http.server -v2: matrix-nio & aiohttp & markdown """ +import argparse import asyncio import json +import logging import os from http import HTTPStatus from signal import SIGINT, SIGTERM @@ -18,12 +18,69 @@ from markdown import markdown from nio import AsyncClient from nio.exceptions import LocalProtocolError -SERVER_ADDRESS = (os.environ.get("HOST", ""), int(os.environ.get("PORT", 4785))) -MATRIX_URL = os.environ.get("MATRIX_URL", "https://matrix.org") -MATRIX_ID = os.environ.get("MATRIX_ID", "@wwm:matrix.org") -MATRIX_PW = os.environ["MATRIX_PW"] -API_KEY = os.environ["API_KEY"] -CLIENT = AsyncClient(MATRIX_URL, MATRIX_ID) +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() +logging.basicConfig(level=50 - 10 * args.verbose) + +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) async def handler(request): @@ -32,6 +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=}") data = await request.read() try: @@ -56,7 +114,9 @@ async def handler(request): } try: await send_room_message(room_id, content) - except LocalProtocolError: # Connection lost, try another login + except LocalProtocolError as e: # Connection lost, try another login + logging.error(f"Send error: {e}") + logging.warning("Reconnecting and trying again") await CLIENT.login(MATRIX_PW) await send_room_message(room_id, content) @@ -65,12 +125,14 @@ async def handler(request): def create_json_response(status, ret): """Create a JSON response.""" + logging.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=}") return await CLIENT.room_send( room_id=room_id, message_type="m.room.message", content=content ) @@ -82,11 +144,13 @@ async def main(event): matrix client login & start web server """ + logging.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=}") site = web.TCPSite(runner, *SERVER_ADDRESS) await site.start() @@ -106,6 +170,7 @@ def terminate(event, signal): def run(): """Launch everything.""" + logging.info("Matrix Webhook starting...") loop = asyncio.get_event_loop() event = asyncio.Event() @@ -114,6 +179,7 @@ def run(): loop.run_until_complete(main(event)) + logging.info("Matrix Webhook closing...") loop.close()