From 9ca4c913f38d1cd92dab5753a42d0e6a6b1790ea Mon Sep 17 00:00:00 2001 From: Kierre Date: Fri, 17 Oct 2025 17:44:41 -0400 Subject: [PATCH] Make sending Matrix requests easier --- pyproject.toml | 2 +- vona/__main__.py | 8 ++-- vona/custom/hammerhead.py | 2 +- vona/globals/__init__.py | 80 ++++++++++++++++++++++++++++++++++++++- vona/utils/joinroom.py | 47 ++++++++--------------- 5 files changed, 99 insertions(+), 40 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 1978d63..18a8c66 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ dependencies = [ "httpx (>=0.28.1,<0.29.0)", "pynacl (>=1.6.0,<2.0.0)", "flask[async] (>=3.1.2,<4.0.0)", - "resolvematrix @ git+https://foundry.fsky.io/vel/resolvematrix.git@dev", + "resolvematrix @ git+https://codeberg.org/timedout/resolvematrix.git", ] authors = [ diff --git a/vona/__main__.py b/vona/__main__.py index 970f520..82e0f31 100644 --- a/vona/__main__.py +++ b/vona/__main__.py @@ -132,11 +132,9 @@ async def client(): def federation_self_test(): try: - auth = globals.make_auth_header(config.server_name, "GET", "/.well-known/matrix/server") - - resp = globals.http_client.get( - f"https://{config.server_name}/.well-known/matrix/server", - headers={"Authorization": auth} + resp = globals.http_client().get( + path="/", + destination=config.server_name, ) resp.raise_for_status() diff --git a/vona/custom/hammerhead.py b/vona/custom/hammerhead.py index 38331bb..442f613 100644 --- a/vona/custom/hammerhead.py +++ b/vona/custom/hammerhead.py @@ -6,12 +6,12 @@ import vona.globals as globals import vona.config as config import time - hammerhead = Blueprint("hammerhead", __name__) # Hammerhead endpoints. Not documented, but code is at: # https://codeberg.org/timedout/hammerhead/src/branch/dev/hammerhead/router/routes/hammerhead + @hammerhead.route("/_hammerhead/uptime") async def uptime(): return jsonify({"started_at": config.the_funny_number}) diff --git a/vona/globals/__init__.py b/vona/globals/__init__.py index ef8a777..f89742e 100644 --- a/vona/globals/__init__.py +++ b/vona/globals/__init__.py @@ -1,3 +1,5 @@ +from resolvematrix import ServerResolver +from types import SimpleNamespace from collections import Counter import vona.config as config import nacl.signing @@ -10,7 +12,6 @@ import json import re version = "1.4.3" -http_client = httpx.Client(headers={"User-Agent": f"Vona/{version}"}) def canonical_json(value): @@ -131,7 +132,12 @@ def pubkey() -> str: ) -def make_auth_header(destination, method, path, content=None) -> str: +def make_auth_header( + destination: str, + method: str, + path: str, + content = None +) -> str: request_json = { "method": method, "uri": path, @@ -255,6 +261,7 @@ def room_version_from_id(room_id): return most_common_character(nums)[0] + room_dir = { "chunk": [{ "avatar_url": f"mxc://{config.server_name}/cat", @@ -270,3 +277,72 @@ room_dir = { }], "total_room_count_estimate": 1 } + + +class http_client: + http = httpx.Client(headers={"User-Agent": f"Vona/{version}"}) + resolver = ServerResolver(client=http) + + def _resolve(self, target) -> SimpleNamespace: + r = self.resolver.resolve(target) + + if r.sni: + sni = r.sni + else: + sni = r.host_header + + return SimpleNamespace( + base_url=r.base_url, + host_header=r.host_header, + sni=sni + ) + + def put( + self, + path: str, + destination: str, + headers: dict = {}, + authorize: bool = True, + json: dict = {}, + ): + resolved = self._resolve(destination) + + if authorize: + headers["Authorization"] = make_auth_header( + method="PUT", + destination=destination, + path=path, + ) + + headers["Host"] = resolved.host_header + + return self.http.put( + f"{resolved.base_url}{path}", + headers=headers, + extensions={"sni_hostname": resolved.sni}, + json=json + ) + + def get( + self, + path: str, + destination: str, + headers: dict = {}, + authorize: bool = True, + ): + resolved = self._resolve(destination) + + if authorize: + headers["Authorization"] = make_auth_header( + method="GET", + destination=destination, + path=path, + ) + + headers["Host"] = resolved.host_header + + return self.http.get( + f"{resolved.base_url}{path}", + headers=headers, + extensions={"sni_hostname": resolved.sni} + ) diff --git a/vona/utils/joinroom.py b/vona/utils/joinroom.py index ce5e4dc..2b2ac34 100644 --- a/vona/utils/joinroom.py +++ b/vona/utils/joinroom.py @@ -1,16 +1,18 @@ +from resolvematrix import ServerResolver +import urllib.parse, time, json, httpx import vona.globals as globals import vona.config as config -import urllib.parse -import httpx -import json -import time -http_client = globals.http_client +http_client = globals.http_client() def get_user_input(prompt): try: - return urllib.parse.quote(input(prompt).replace("\\n", "\n", count=32)) + answer = input(prompt) + while "\\n" in answer: + answer = answer.replace("\\n", "\n") + + return urllib.parse.quote(answer) except Exception as e: print(f"Error reading input: {e}") return None @@ -21,27 +23,19 @@ room_id = get_user_input("Room ID:\n\t") try: - unresolved_server_name = input("\nServer name before resolve:\n\t") - resolved_server_name = input("Server name after resolve:\n\t") + server_name = input("\nServer name to join via:\n\t") except Exception as e: print(f"Error reading server names: {e}") exit(1) +resolver = ServerResolver(client=http_client) try: - make_join_auth = globals.make_auth_header( - unresolved_server_name, - "GET", - f"/_matrix/federation/v1/make_join/{room_id}/%40{username}%3A{config.server_name}?ver=1&ver=2&ver=3&ver=4&ver=5&ver=6&ver=7&ver=8&ver=9&ver=10&ver=11&ver=12", - ) - print("\nSending make_join request..") make_join_response = http_client.get( - f"https://{resolved_server_name}/_matrix/federation/v1/make_join/{room_id}/%40{username}%3A{config.server_name}?ver=1&ver=2&ver=3&ver=4&ver=5&ver=6&ver=7&ver=8&ver=9&ver=10&ver=11&ver=12", - headers={ - "Authorization": make_join_auth - }, + path=f"/_matrix/federation/v1/make_join/{room_id}/%40{username}%3A{config.server_name}?ver=1&ver=2&ver=3&ver=4&ver=5&ver=6&ver=7&ver=8&ver=9&ver=10&ver=11&ver=12", + destination=server_name, ) make_join_response.raise_for_status() @@ -51,7 +45,7 @@ except httpx.HTTPStatusError as e: print(f"HTTP error occurred: {e.response.status_code} - {e.response.text}") exit(1) except json.JSONDecodeError: - print("Failed to decode JSON response.") + print("Failed to decode response.") exit(1) except Exception as e: print(f"An error occurred: {e}") @@ -59,7 +53,7 @@ except Exception as e: join_event = make_join.get("event", {}) -if make_join.get("room_version") in ["1", "2"]: +if make_join.get("room_version", "1") in ["1", "2"]: # NOTE: if we always make it opaque than Synapse will 500 lmao join_event["event_id"] = globals.make_event_id() @@ -72,19 +66,10 @@ except ValueError: signed_join = globals.hash_and_sign_event(join_event) try: - send_join_auth = globals.make_auth_header( - unresolved_server_name, - "PUT", - f"/_matrix/federation/v2/send_join/{room_id}/%24doesntmatter?omit_members=true", - signed_join, - ) - send_join_response = http_client.put( - f"https://{resolved_server_name}/_matrix/federation/v2/send_join/{room_id}/%24doesntmatter?omit_members=true", - headers={ - "Authorization": send_join_auth - }, + path=f"/_matrix/federation/v2/send_join/{room_id}/%24doesntmatter?omit_members=true", json=signed_join, + destination=server_name, ) send_join_response.raise_for_status()