2021-08-08 02:41:37 -04:00
|
|
|
"""Matrix Webhook main request handler."""
|
|
|
|
|
|
|
|
import json
|
|
|
|
import logging
|
|
|
|
from http import HTTPStatus
|
2021-08-27 17:47:07 -04:00
|
|
|
from hmac import HMAC
|
2021-11-03 17:02:22 -04:00
|
|
|
import importlib
|
2021-08-08 02:41:37 -04:00
|
|
|
|
|
|
|
from markdown import markdown
|
|
|
|
|
2021-11-03 17:02:22 -04:00
|
|
|
from . import conf, utils
|
2021-08-08 02:41:37 -04:00
|
|
|
|
|
|
|
LOGGER = logging.getLogger("matrix_webhook.handler")
|
|
|
|
|
|
|
|
|
|
|
|
async def matrix_webhook(request):
|
|
|
|
"""
|
|
|
|
Coroutine given to the server, st. it knows what to do with an HTTP request.
|
|
|
|
|
|
|
|
This one handles a POST, checks its content, and forwards it to the matrix room.
|
|
|
|
"""
|
|
|
|
LOGGER.debug(f"Handling {request=}")
|
2021-08-27 17:47:07 -04:00
|
|
|
data_b = await request.read()
|
2021-08-08 02:41:37 -04:00
|
|
|
|
|
|
|
try:
|
2021-08-27 17:47:07 -04:00
|
|
|
data = json.loads(data_b.decode())
|
2021-08-08 02:41:37 -04:00
|
|
|
except json.decoder.JSONDecodeError:
|
|
|
|
return utils.create_json_response(HTTPStatus.BAD_REQUEST, "Invalid JSON")
|
|
|
|
|
|
|
|
# legacy naming
|
|
|
|
if "text" in data and "body" not in data:
|
|
|
|
data["body"] = data["text"]
|
|
|
|
|
|
|
|
# allow key to be passed as a parameter
|
|
|
|
if "key" in request.rel_url.query and "key" not in data:
|
|
|
|
data["key"] = request.rel_url.query["key"]
|
|
|
|
|
|
|
|
if "formatter" in request.rel_url.query:
|
|
|
|
try:
|
2022-12-02 18:03:25 -05:00
|
|
|
format = request.rel_url.query["formatter"]
|
|
|
|
plugin = importlib.import_module(f"matrix_webhook.formatters.{format}", "formatter")
|
2021-11-03 17:02:22 -04:00
|
|
|
data = plugin.formatter(data, request.headers)
|
2021-11-03 17:05:45 -04:00
|
|
|
except ModuleNotFoundError:
|
2021-08-08 02:41:37 -04:00
|
|
|
return utils.create_json_response(
|
|
|
|
HTTPStatus.BAD_REQUEST, "Unknown formatter"
|
|
|
|
)
|
|
|
|
|
|
|
|
if "room_id" in request.rel_url.query and "room_id" not in data:
|
|
|
|
data["room_id"] = request.rel_url.query["room_id"]
|
|
|
|
if "room_id" not in data:
|
|
|
|
data["room_id"] = request.path.lstrip("/")
|
|
|
|
|
2021-08-27 17:47:07 -04:00
|
|
|
# If we get a good SHA-256 HMAC digest,
|
|
|
|
# we can consider that the sender has the right API key
|
|
|
|
if "digest" in data:
|
|
|
|
if data["digest"] == HMAC(conf.API_KEY.encode(), data_b, "sha256").hexdigest():
|
|
|
|
data["key"] = conf.API_KEY
|
|
|
|
else: # but if there is a wrong digest, an informative error should be provided
|
|
|
|
return utils.create_json_response(
|
|
|
|
HTTPStatus.UNAUTHORIZED, "Invalid SHA-256 HMAC digest"
|
|
|
|
)
|
|
|
|
|
2021-08-08 02:41:37 -04:00
|
|
|
missing = []
|
|
|
|
for key in ["body", "key", "room_id"]:
|
|
|
|
if key not in data or not data[key]:
|
|
|
|
missing.append(key)
|
|
|
|
if missing:
|
|
|
|
return utils.create_json_response(
|
|
|
|
HTTPStatus.BAD_REQUEST, f"Missing {', '.join(missing)}"
|
|
|
|
)
|
|
|
|
|
2021-11-04 13:26:54 -04:00
|
|
|
if data["key"] not in conf.API_KEYS:
|
2021-08-08 02:41:37 -04:00
|
|
|
return utils.create_json_response(HTTPStatus.UNAUTHORIZED, "Invalid API key")
|
|
|
|
|
|
|
|
if "formatted_body" in data:
|
|
|
|
formatted_body = data["formatted_body"]
|
|
|
|
else:
|
|
|
|
formatted_body = markdown(str(data["body"]), extensions=["extra"])
|
|
|
|
|
2021-09-18 06:12:54 -04:00
|
|
|
# try to join room first -> non none response means error
|
|
|
|
resp = await utils.join_room(data["room_id"])
|
|
|
|
if resp is not None:
|
|
|
|
return resp
|
|
|
|
|
2021-08-08 02:41:37 -04:00
|
|
|
content = {
|
|
|
|
"msgtype": "m.text",
|
|
|
|
"body": data["body"],
|
|
|
|
"format": "org.matrix.custom.html",
|
|
|
|
"formatted_body": formatted_body,
|
|
|
|
}
|
|
|
|
return await utils.send_room_message(data["room_id"], content)
|