From 6cc08fcb02ec1e8339b7596b5158869d8f0d49d3 Mon Sep 17 00:00:00 2001 From: Kierre Date: Tue, 21 Oct 2025 23:47:28 -0400 Subject: [PATCH] The Codebase Consistency Update --- TODO.md | 1 + vona/__init__.py | 1 - vona/__main__.py | 9 ++- vona/appservice/__init__.py | 7 ++- vona/client/__init__.py | 69 ++++++++++++--------- vona/client/groups.py | 120 ++++++++++++++++++++++++++++++++++++ vona/config/__init__.py | 3 +- vona/custom/__init__.py | 4 +- vona/custom/citadel.py | 8 ++- vona/custom/conduwuit.py | 5 +- vona/custom/dendrite.py | 7 ++- vona/custom/hammerhead.py | 7 ++- vona/custom/synapse.py | 51 ++++++++------- vona/custom/telodendria.py | 7 ++- vona/federation/__init__.py | 112 ++++++++++++++++++++------------- vona/globals/__init__.py | 10 +-- vona/identity/__init__.py | 7 ++- vona/policy/__init__.py | 6 +- vona/utils/__main__.py | 2 +- vona/utils/joinroom.py | 7 ++- vona/utils/makekey.py | 26 ++++---- 21 files changed, 339 insertions(+), 130 deletions(-) create mode 100644 vona/client/groups.py diff --git a/TODO.md b/TODO.md index 2fc43b7..904a263 100644 --- a/TODO.md +++ b/TODO.md @@ -1 +1,2 @@ - use `resolvematrix` async instead of sync +- implement groups over federation diff --git a/vona/__init__.py b/vona/__init__.py index 7e98d8f..e69de29 100644 --- a/vona/__init__.py +++ b/vona/__init__.py @@ -1 +0,0 @@ -# read __main__.py diff --git a/vona/__main__.py b/vona/__main__.py index 77ee8dd..a25b0a6 100644 --- a/vona/__main__.py +++ b/vona/__main__.py @@ -1,4 +1,3 @@ -from flask import Flask, jsonify, request, redirect import vona.globals as globals from datetime import datetime import vona.config as config @@ -6,6 +5,14 @@ import threading import logging import os +from flask import ( + Flask, + jsonify, + request, + redirect, + abort, +) + from vona.federation import server from vona.custom import custom from vona.identity import identity diff --git a/vona/appservice/__init__.py b/vona/appservice/__init__.py index d90a2d2..4e6c227 100644 --- a/vona/appservice/__init__.py +++ b/vona/appservice/__init__.py @@ -1,7 +1,11 @@ -from flask import Blueprint, jsonify from vona.config import the_funny_number import asyncio +from flask import ( + Blueprint, + jsonify, +) + apps = Blueprint("appservice", __name__) # This implements both being a homeserver and @@ -10,6 +14,7 @@ apps = Blueprint("appservice", __name__) # Endpoints invoked by the homeserver are put # lower than ones invoked by the appservice. + @apps.route("/_matrix/client/v1/appservice//ping", methods=["POST"]) async def homeserver_ping(app): # Sleeping here makes it more realistic diff --git a/vona/client/__init__.py b/vona/client/__init__.py index eb69920..6b087f1 100644 --- a/vona/client/__init__.py +++ b/vona/client/__init__.py @@ -1,12 +1,23 @@ -from flask import Blueprint, jsonify, request, send_file from vona.federation import send_join import vona.globals as globals import vona.config as config + import asyncio import random import os -client = Blueprint("c2s", __name__) +from flask import ( + Blueprint, + jsonify, + request, + send_file, +) + +client = Blueprint("client", __name__) + +from .groups import groups + +client.register_blueprint(groups) @client.route("/_matrix/client/v3/account/password", methods=["POST"]) @@ -22,10 +33,10 @@ client = Blueprint("c2s", __name__) @client.route("/_matrix/client/v3/logout", methods=["POST"]) @client.route("/_matrix/client/r0/logout", methods=["POST"]) @client.route("/_matrix/client/v3/rooms//invite", methods=["POST"]) -@client.route("/_matrix/client/v3/rooms//leave", methods=["POST"]) -@client.route("/_matrix/client/r0/rooms//leave", methods=["POST"]) -@client.route("/_matrix/client/v3/rooms//read_markers", methods=["POST"]) -@client.route("/_matrix/client/r0/rooms//read_markers", methods=["POST"]) +@client.route("/_matrix/client/v3/rooms//leave", methods=["POST"]) +@client.route("/_matrix/client/r0/rooms//leave", methods=["POST"]) +@client.route("/_matrix/client/v3/rooms//read_markers", methods=["POST"]) +@client.route("/_matrix/client/r0/rooms//read_markers", methods=["POST"]) @client.route("/_matrix/client/v3/rooms//typing/", methods=["PUT"]) @client.route("/_matrix/client/api/v1/rooms//typing/", methods=["PUT"]) @client.route("/_matrix/client/v3/keys/device_signing/upload", methods=["POST"]) @@ -101,10 +112,10 @@ async def lock(user): return jsonify({"locked": True}) -@client.route("/_matrix/client/api/v1/rooms//members") -@client.route("/_matrix/client/v3/rooms//members") -@client.route("/_matrix/client/r0/rooms//members") -async def room_members(roomId): +@client.route("/_matrix/client/api/v1/rooms//members") +@client.route("/_matrix/client/v3/rooms//members") +@client.route("/_matrix/client/r0/rooms//members") +async def room_members(room): return jsonify({ "chunk": [{ "content": { @@ -112,9 +123,9 @@ async def room_members(roomId): "displayname": "Vona", "membership": "join" }, - "event_id": globals.make_event_id(seed=roomId), + "event_id": globals.make_event_id(seed=room), "origin_server_ts": config.the_funny_number, - "room_id": roomId, + "room_id": room, "sender": f"@vona:{config.server_name}", "state_key": f"@vona:{config.server_name}", "type": "m.room.member", @@ -489,10 +500,10 @@ async def refresh(): }) -@client.route("/_matrix/client/unstable/im.nheko.summary/rooms//summary") -@client.route("/_matrix/client/unstable/im.nheko.summary/summary/") -@client.route("/_matrix/client/v1/room_summary/") -async def room_summary(roomId): +@client.route("/_matrix/client/unstable/im.nheko.summary/rooms//summary") +@client.route("/_matrix/client/unstable/im.nheko.summary/summary/") +@client.route("/_matrix/client/v1/room_summary/") +async def room_summary(room): return jsonify({ "room_id": globals.make_event_id().replace("$", "!"), "avatar_url": f"mxc://{config.server_name}/cat", @@ -504,7 +515,7 @@ async def room_summary(roomId): "join_rule": "public", "room_type": "m.room", "membership": "join", - "room_version": globals.room_version_from_id(roomId) + "room_version": globals.room_version_from_id(room) }) @@ -627,10 +638,10 @@ async def media_config(): return jsonify({"m.upload.size": config.the_funny_number * 69420}) -@client.route("/_matrix/client/api/v1/profile//", methods=["GET", "PUT", "DELETE"]) -@client.route("/_matrix/client/v3/profile//", methods=["GET", "PUT", "DELETE"]) -@client.route("/_matrix/client/r0/profile//", methods=["GET", "PUT", "DELETE"]) -async def profile_keys(userId, key): +@client.route("/_matrix/client/api/v1/profile//", methods=["GET", "PUT", "DELETE"]) +@client.route("/_matrix/client/v3/profile//", methods=["GET", "PUT", "DELETE"]) +@client.route("/_matrix/client/r0/profile//", methods=["GET", "PUT", "DELETE"]) +async def profile_keys(user, key): if request.method == "GET": if key == "avatar_url": return jsonify({"avatar_url": f"mxc://{config.server_name}/cat"}) @@ -645,19 +656,19 @@ async def profile_keys(userId, key): return jsonify({}) -@client.route("/_matrix/client/v3/profile/") -@client.route("/_matrix/client/r0/profile/") -async def user_profile(userId): +@client.route("/_matrix/client/v3/profile/") +@client.route("/_matrix/client/r0/profile/") +async def user_profile(user): return jsonify({ "avatar_url": f"mxc://{config.server_name}/cat", "displayname": "Vona" }) -@client.route("/_matrix/client/api/v1/rooms//messages") -@client.route("/_matrix/client/v3/rooms//messages") -@client.route("/_matrix/client/r0/rooms//messages") -async def room_messages(roomId): +@client.route("/_matrix/client/api/v1/rooms//messages") +@client.route("/_matrix/client/v3/rooms//messages") +@client.route("/_matrix/client/r0/rooms//messages") +async def room_messages(room): return jsonify({ "chunk": [{ "content": { @@ -668,7 +679,7 @@ async def room_messages(roomId): }, "event_id": globals.make_event_id(), "origin_server_ts": config.the_funny_number, - "room_id": roomId, + "room_id": room, "sender": f"@vona:{config.server_name}", "type": "m.room.message" }], diff --git a/vona/client/groups.py b/vona/client/groups.py new file mode 100644 index 0000000..b431ad9 --- /dev/null +++ b/vona/client/groups.py @@ -0,0 +1,120 @@ +import vona.globals as globals +import vona.config as config +import time + +from flask import ( + Blueprint, + jsonify, + request, +) + +groups = Blueprint("groups", __name__) + +# This implements the C2S API for groups + + +@groups.route("/_matrix/client/r0/groups//self/update_publicity", methods=["PUT"]) +@groups.route("/_matrix/client/r0/groups//self/accept_invite", methods=["PUT"]) +@groups.route("/_matrix/client/r0/groups//self/leave", methods=["PUT"]) +@groups.route("/_matrix/client/r0/groups//admin/users/remove/", methods=["PUT"]) +@groups.route("/_matrix/client/r0/groups//admin/rooms/", methods=["PUT", "DELETE"]) +async def empty(**k): + return jsonify({}) + + +@groups.route("/_matrix/client/r0/publicised_groups/") +@groups.route("/_matrix/client/r0/joined_groups") +async def joined_groups(user=None): + return jsonify({ + "groups": [f"+vona:{config.server_name}"] + }) + + +@groups.route("/_matrix/client/r0/groups//summary") +async def summary(group): + return jsonify({ + "profile": { + "name": "Vona", + "short_description": "", + "long_description": "", + "avatar_url": "", + "is_public": 1, + "is_openly_joinable": True + }, + "users_section": { + "users": [], + "roles": {}, + "total_user_count_estimate": 0 + }, + "rooms_section": { + "rooms": [], + "categories": {}, + "total_room_count_estimate": 0 + }, + "user": { + "membership": "join", + "is_public": 1, + "is_privileged": 1, + "is_publicised": True + } + }) + + +@groups.route("/_matrix/client/r0/groups//rooms") +async def rooms(group): + return jsonify({ + "chunk": [], + "total_room_count_estimate": 0 + }) + + +@groups.route("/_matrix/client/r0/groups//invited_users") +async def invited_users(group): + return jsonify({ + "chunk": [], + "total_user_count_estimate": 0 + }) + + +@groups.route("/_matrix/client/r0/groups//users") +async def users(group): + return jsonify({ + "chunk": [ + { + "user_id": f"@vona:{config.server_name}", + "displayname": "Vona", + "avatar_url": None, + "is_public": True, + "is_privileged": True, + "attestation": globals.sign_json({ + "group_id": group, + "user_id": f"@vona:{config.server_name}", + "valid_until_ms": int(str(time.time() * 1000).split(".")[0]) + }) + } + ], + "total_user_count_estimate": 1 + }) + + +@groups.route("/_matrix/client/r0/groups//profile", methods=["GET", "POST"]) +async def profile(group): + if request.method == "POST": + return jsonify({}) + + return jsonify({ + "name": "Vona", + "short_description": "", + "long_description": "", + "avatar_url": "", + "is_public": 1, + "is_openly_joinable": True + }) + + +@groups.route("/_matrix/client/r0/create_group", methods=["POST"]) +async def create_group(): + return jsonify({ + "group_id": f"+vona:{config.server_name}" + }) + diff --git a/vona/config/__init__.py b/vona/config/__init__.py index 92bb539..5ec5267 100644 --- a/vona/config/__init__.py +++ b/vona/config/__init__.py @@ -1,6 +1,7 @@ -import os from pathlib import Path import tomllib +import os + addr: str = "127.0.0.1" port: int = 5000 diff --git a/vona/custom/__init__.py b/vona/custom/__init__.py index 84fec9b..4db3e0e 100644 --- a/vona/custom/__init__.py +++ b/vona/custom/__init__.py @@ -1,4 +1,6 @@ -from flask import Blueprint +from flask import ( + Blueprint, +) custom = Blueprint("custom", __name__) diff --git a/vona/custom/citadel.py b/vona/custom/citadel.py index 0aa392e..c636e9d 100644 --- a/vona/custom/citadel.py +++ b/vona/custom/citadel.py @@ -1,8 +1,14 @@ -from flask import Blueprint, jsonify, request import vona.config as config + import base64 import os +from flask import ( + Blueprint, + jsonify, + request, +) + citadel = Blueprint("citadel", __name__) # These are endpoints made by Thales Citadel diff --git a/vona/custom/conduwuit.py b/vona/custom/conduwuit.py index eeb7124..0daee8d 100644 --- a/vona/custom/conduwuit.py +++ b/vona/custom/conduwuit.py @@ -1,5 +1,8 @@ -from flask import Blueprint, jsonify import vona.globals as globals +from flask import ( + Blueprint, + jsonify, +) conduwuit = Blueprint("conduwuit", __name__) diff --git a/vona/custom/dendrite.py b/vona/custom/dendrite.py index 0e7947b..ead4871 100644 --- a/vona/custom/dendrite.py +++ b/vona/custom/dendrite.py @@ -1,10 +1,13 @@ -from flask import Blueprint, jsonify import vona.globals as globals import vona.config as config +from flask import ( + Blueprint, + jsonify, +) dendrite = Blueprint("dendrite", __name__) -# https://element-hq.github.io/dendrite/administration/adminapi +# This implements the Dendrite admin API @dendrite.route("/_dendrite/admin/purgeRoom/", methods=["POST"]) diff --git a/vona/custom/hammerhead.py b/vona/custom/hammerhead.py index 98524ac..a4e4089 100644 --- a/vona/custom/hammerhead.py +++ b/vona/custom/hammerhead.py @@ -1,4 +1,3 @@ -from flask import Blueprint, request, jsonify from vona.config import the_funny_number from datetime import datetime, timezone from vona.federation import send_join @@ -6,6 +5,12 @@ import vona.globals as globals import vona.config as config import time +from flask import ( + Blueprint, + request, + jsonify, +) + hammerhead = Blueprint("hammerhead", __name__) # Hammerhead endpoints. Not documented, but code is at: diff --git a/vona/custom/synapse.py b/vona/custom/synapse.py index b34b3dc..002f37f 100644 --- a/vona/custom/synapse.py +++ b/vona/custom/synapse.py @@ -1,24 +1,31 @@ -from flask import Blueprint, jsonify, request, Response from vona.federation import send_join import vona.globals as globals import vona.config as config + import base64 import re import os +from flask import ( + Blueprint, + jsonify, + request, + Response, +) + synapse = Blueprint("synapse", __name__) # The absolute giant known as the Synapse admin API. -# Very messy, needs cleaning +# TODO: Very messy, needs cleaning -@synapse.route("/_synapse/admin/v1/suspend/", methods=["PUT"]) -@synapse.route("/_synapse/admin/v1/deactivate/", methods=["POST"]) -@synapse.route("/_synapse/admin/v1/reset_password/", methods=["POST"]) -@synapse.route("/_synapse/admin/v1/users//admin", methods=["PUT"]) -@synapse.route("/_synapse/admin/v2/users//delete_devices", methods=["POST"]) -@synapse.route("/_synapse/admin/v1/users//shadow_ban", methods=["DELETE", "POST"]) -@synapse.route("/_synapse/admin/v1/users//override_ratelimit", methods=["GET", "POST", "DELETE"]) +@synapse.route("/_synapse/admin/v1/suspend/", methods=["PUT"]) +@synapse.route("/_synapse/admin/v1/deactivate/", methods=["POST"]) +@synapse.route("/_synapse/admin/v1/reset_password/", methods=["POST"]) +@synapse.route("/_synapse/admin/v1/users//admin", methods=["PUT"]) +@synapse.route("/_synapse/admin/v2/users//delete_devices", methods=["POST"]) +@synapse.route("/_synapse/admin/v1/users//shadow_ban", methods=["DELETE", "POST"]) +@synapse.route("/_synapse/admin/v1/users//override_ratelimit", methods=["GET", "POST", "DELETE"]) @synapse.route("/_synapse/admin/v1/media/protect/", methods=["POST"]) @synapse.route("/_synapse/admin/v1/media/unprotect/", methods=["POST"]) @synapse.route("/_synapse/admin/v1/media/quarantine//", methods=["POST"]) @@ -57,7 +64,7 @@ async def user_list(): "total": 1 }) -@synapse.route("/_synapse/admin/v2/users/", methods=["GET", "PUT"]) +@synapse.route("/_synapse/admin/v2/users/", methods=["GET", "PUT"]) async def user_info(user_id): if request.method == "GET": return jsonify({ @@ -84,7 +91,7 @@ async def user_info(user_id): return jsonify({}), 201 -@synapse.route("/_synapse/admin/v1/whois/") +@synapse.route("/_synapse/admin/v1/whois/") async def whois(user_id): return jsonify({ "user_id": f"@vona:{config.server_name}", @@ -101,22 +108,22 @@ async def whois(user_id): } }) -@synapse.route("/_synapse/admin/v1/users//joined_rooms") +@synapse.route("/_synapse/admin/v1/users//joined_rooms") async def user_joined_rooms(user_id): return jsonify({ "joined_rooms": [globals.make_event_id().replace("$", "!")], "total": 1 }) -@synapse.route("/_synapse/admin/v1/users//sent_invite_count") +@synapse.route("/_synapse/admin/v1/users//sent_invite_count") async def invite_count(user_id): return jsonify({"invite_count": config.the_funny_number}) -@synapse.route("/_synapse/admin/v1/users//accountdata") +@synapse.route("/_synapse/admin/v1/users//accountdata") async def account_data(user_id): return jsonify({"account_data":{"global":{}}}) -@synapse.route("/_synapse/admin/v1/users//media", methods=["GET", "DELETE"]) +@synapse.route("/_synapse/admin/v1/users//media", methods=["GET", "DELETE"]) async def account_media(user_id): if request.method == "GET": return jsonify({ @@ -138,15 +145,15 @@ async def account_media(user_id): "total": config.the_funny_number }) -@synapse.route("/_synapse/admin/v1/users//login", methods=["POST"]) +@synapse.route("/_synapse/admin/v1/users//login", methods=["POST"]) async def account_login(user_id): return jsonify({"access_token": "vona"}) -@synapse.route("/_synapse/admin/v1/users//_allow_cross_signing_replacement_without_uia", methods=["POST"]) +@synapse.route("/_synapse/admin/v1/users//_allow_cross_signing_replacement_without_uia", methods=["POST"]) async def stupid_mas_bullshit(user_id): return jsonify({"updatable_without_uia_before_ms": config.the_funny_number}) -@synapse.route("/_synapse/admin/v2/users//devices", methods=["GET", "POST"]) +@synapse.route("/_synapse/admin/v2/users//devices", methods=["GET", "POST"]) async def device_list(user_id): if request.method == "GET": return jsonify({ @@ -162,7 +169,7 @@ async def device_list(user_id): return jsonify({}) -@synapse.route("/_synapse/admin/v2/users//devices/", methods=["GET", "PUT", "DELETE"]) +@synapse.route("/_synapse/admin/v2/users//devices/", methods=["GET", "PUT", "DELETE"]) async def device_info(user_id, device_id): if request.method == "GET": return jsonify({ @@ -175,7 +182,7 @@ async def device_info(user_id, device_id): return jsonify({}) -@synapse.route("/_synapse/admin/v1/users//pushers") +@synapse.route("/_synapse/admin/v1/users//pushers") async def pushers(user_id): return jsonify({"pushers": [], "total": config.the_funny_number}) @@ -188,7 +195,7 @@ async def username_available(): async def threepid(**kwargs): return jsonify({"user_id": f"@vona:{config.server_name}"}) -@synapse.route("/_synapse/admin/v1//redact") +@synapse.route("/_synapse/admin/v1//redact") async def redact(user_id): return jsonify({"redact_id": os.urandom(16).hex()}) @@ -196,7 +203,7 @@ async def redact(user_id): async def redact_status(redact_id): return jsonify({"status":"active","failed_redactions":[]}) -@synapse.route("/_synapse/admin/v1/experimental_features/", methods=["GET", "PUT"]) +@synapse.route("/_synapse/admin/v1/experimental_features/", methods=["GET", "PUT"]) async def experimental_features(user_id): return jsonify({"features": {}}) diff --git a/vona/custom/telodendria.py b/vona/custom/telodendria.py index 6a99f85..929ece1 100644 --- a/vona/custom/telodendria.py +++ b/vona/custom/telodendria.py @@ -1,7 +1,12 @@ -from flask import Blueprint, request, jsonify from vona.globals import version import vona.config as config +from flask import ( + Blueprint, + request, + jsonify, +) + telo = Blueprint("telodendria", __name__) # The telodendria admin API as specified by diff --git a/vona/federation/__init__.py b/vona/federation/__init__.py index cf4b8a0..6a71b7a 100644 --- a/vona/federation/__init__.py +++ b/vona/federation/__init__.py @@ -1,8 +1,18 @@ -from flask import jsonify, Response, request, send_file, abort, Blueprint -import json, time, os, threading import vona.globals as globals import vona.config as config +import threading +import json +import time +import os + +from flask import ( + jsonify, + Response, + request, + send_file, + Blueprint, +) server = Blueprint("federation", __name__) http = globals.http_client() @@ -12,28 +22,28 @@ class bullshit: def get_json(): return {} -def send_join(request, roomId) -> dict: +def send_join(request, room) -> dict: event_chain = [] event_hashes = [] event_ids = [ - globals.make_event_id(seed=f"1_{roomId}"), - globals.make_event_id(seed=f"2_{roomId}"), - globals.make_event_id(seed=f"3_{roomId}"), - globals.make_event_id(seed=f"4_{roomId}"), - globals.make_event_id(seed=f"5_{roomId}"), - globals.make_event_id(seed=f"6_{roomId}"), + globals.make_event_id(seed=f"1_{room}"), + globals.make_event_id(seed=f"2_{room}"), + globals.make_event_id(seed=f"3_{room}"), + globals.make_event_id(seed=f"4_{room}"), + globals.make_event_id(seed=f"5_{room}"), + globals.make_event_id(seed=f"6_{room}"), ] create_event = { "content": { "m.federate": True, "creator": f"@vona:{config.server_name}", - "room_version": globals.room_version_from_id(roomId) + "room_version": globals.room_version_from_id(room) }, "event_id": event_ids[0], "origin_server_ts": 1, - "room_id": roomId, + "room_id": room, "sender": f"@vona:{config.server_name}", "state_key": "", "depth": 1, @@ -56,7 +66,7 @@ def send_join(request, roomId) -> dict: "state_key": f"@vona:{config.server_name}", "type": "m.room.member", "event_id": event_ids[1], - "room_id": roomId, + "room_id": room, "depth": 2, "auth_events": [[ screate_event["event_id"], @@ -78,7 +88,7 @@ def send_join(request, roomId) -> dict: } }, "origin_server_ts": 3, - "room_id": roomId, + "room_id": room, "sender": f"@vona:{config.server_name}", "state_key": "", "type": "m.room.power_levels", @@ -113,7 +123,7 @@ def send_join(request, roomId) -> dict: "state_key": "", "type": "m.room.join_rules", "event_id": event_ids[3], - "room_id": roomId, + "room_id": room, "depth": 4, "auth_events": [ [ @@ -148,7 +158,7 @@ def send_join(request, roomId) -> dict: "state_key": "", "type": "m.room.guest_access", "event_id": event_ids[4], - "room_id": roomId, + "room_id": room, "auth_events": [ [ screate_event["event_id"], @@ -182,7 +192,7 @@ def send_join(request, roomId) -> dict: "origin_server_ts": 6, "depth": 6, "event_id": event_ids[5], - "room_id": roomId, + "room_id": room, "auth_events": [ [ screate_event["event_id"], @@ -278,16 +288,16 @@ async def thumbnail_media(media_id): "error": "Cat is too cute to thumbnail" }), 418 -@server.route("/_matrix/federation/v1/send_join//", methods=["PUT"]) -async def send_join_v1(roomId, eventId): - return jsonify([200, send_join(request, roomId)]) +@server.route("/_matrix/federation/v1/send_join//", methods=["PUT"]) +async def send_join_v1(room, eventId): + return jsonify([200, send_join(request, room)]) -@server.route("/_matrix/federation/v2/send_join//", methods=["PUT"]) -async def send_join_v2(roomId, eventId): - return jsonify(send_join(request, roomId)) +@server.route("/_matrix/federation/v2/send_join//", methods=["PUT"]) +async def send_join_v2(room, eventId): + return jsonify(send_join(request, room)) -@server.route("/_matrix/federation/v1/make_join//") -async def make_join(roomId, userId): +@server.route("/_matrix/federation/v1/make_join//") +async def make_join(room, user): def not_invited(): return jsonify({ "errcode": "M_FORBIDDEN", @@ -295,14 +305,14 @@ async def make_join(roomId, userId): }), 403 try: - if roomId.split(":")[1] != config.server_name: + if room.split(":")[1] != config.server_name: return not_invited() except: return not_invited() state = send_join( request=bullshit, - roomId=roomId + room=room )["state"] join = { @@ -312,9 +322,9 @@ async def make_join(roomId, userId): }, "origin": config.server_name, "origin_server_ts": 7, - "room_id": roomId, - "sender": userId, - "state_key": userId, + "room_id": room, + "sender": user, + "state_key": user, "type": "m.room.member", "depth": 7 } @@ -341,7 +351,7 @@ async def make_join(roomId, userId): return jsonify({ "event": globals.hash_and_sign_event(join), - "room_version": globals.room_version_from_id(roomId) + "room_version": globals.room_version_from_id(room) }) @@ -415,15 +425,6 @@ async def user_keys(): @server.route("/_matrix/federation/v2/invite//", methods=["PUT"]) async def invite_user_v2(room, txnId): - return invite_user(request.data) - - -@server.route("/_matrix/federation/v1/invite//", methods=["PUT"]) -async def invite_user_v1(room, txnId): - return [200, invite_user(request.json)] - - -def invite_user(invite_data): if "event" in invite_data: if "room_version" in invite_data: if invite_data["room_version"] not in ["1", "2"]: @@ -436,7 +437,6 @@ def invite_user(invite_data): event = invite_data.get("event", {}) content = event.get("content", {}) - # NOTE to crispycat: I know you loooooove this syntax if ( "content" in event and "membership" in content @@ -446,7 +446,7 @@ def invite_user(invite_data): and event["state_key"] == f"@vona:{config.server_name}" ): return jsonify({ - "event": globals.sign_json_without_discard(invite_data["event"]), + "event": globals.sign_json_without_discard(event), "room_version": invite_data["room_version"] }) @@ -457,8 +457,34 @@ def invite_user(invite_data): }), 403 -@server.route("/_matrix/federation/v1/hierarchy/") -async def space_hierachy(roomId): +@server.route("/_matrix/federation/v1/invite//", methods=["PUT"]) +async def invite_user_v1(room, txnId): + event = request.json + content = event.get("content", {}) + + if ( + "content" in event + and "membership" in content + and "state_key" in event + and "room_id" in event + and content["membership"] == "invite" + and event["state_key"] == f"@vona:{config.server_name}" + and "event_id" in event + and ":" in event["event_id"] + ): + return jsonify({ + "event": globals.sign_json_without_discard(event), + "room_version": invite_data["room_version"] + }) + + return jsonify({ + "errcode": "M_FORBIDDEN", + "error": "Invalid invitation PDU" + }), 403 + + +@server.route("/_matrix/federation/v1/hierarchy/") +async def space_hierachy(room): return jsonify({ "errcode": "M_NOT_FOUND", "error": "Room does not exist." diff --git a/vona/globals/__init__.py b/vona/globals/__init__.py index dab5d3e..3de09d3 100644 --- a/vona/globals/__init__.py +++ b/vona/globals/__init__.py @@ -236,14 +236,13 @@ def hash_and_sign_event(event_object): return event_object -def room_version_from_id(room_id): - room_id_no_sigil = room_id.replace("!", "") +def room_version_from_id(room): + room_id_no_sigil = room.replace("!", "") hexadecimal_room_id = bytes(room_id_no_sigil, "utf-8").hex() if "1" not in hexadecimal_room_id and "2" not in hexadecimal_room_id: - # NOTE: v2 if impossible from room ID alone - hexadecimal_room_id = "2" + hexadecimal_room_id[1:] + hexadecimal_room_id = "1" + hexadecimal_room_id[1:] def remove_chars(s): return re.sub("[^12]", "", s) @@ -252,11 +251,8 @@ def room_version_from_id(room_id): def most_common_character(s): s = s.replace(" ", "").lower() - counts = Counter(s) - most_common = counts.most_common(1) - return most_common[0] if most_common else None return most_common_character(nums)[0] diff --git a/vona/identity/__init__.py b/vona/identity/__init__.py index e252f48..66ffb5d 100644 --- a/vona/identity/__init__.py +++ b/vona/identity/__init__.py @@ -1,7 +1,12 @@ -from flask import Blueprint, jsonify, request from vona.config import server_name, the_funny_number import time +from flask import ( + Blueprint, + jsonify, + request, +) + identity = Blueprint("identity", __name__) # This implements being an identity server. diff --git a/vona/policy/__init__.py b/vona/policy/__init__.py index 047bda2..abf6967 100644 --- a/vona/policy/__init__.py +++ b/vona/policy/__init__.py @@ -1,4 +1,8 @@ -from flask import jsonify, Blueprint, request +from flask import ( + jsonify, + Blueprint, + request, +) policy = Blueprint("policy", __name__) diff --git a/vona/utils/__main__.py b/vona/utils/__main__.py index 1c11e48..d348e52 100644 --- a/vona/utils/__main__.py +++ b/vona/utils/__main__.py @@ -8,4 +8,4 @@ a = [ for t in a: print(f"\t{t}") -print("") \ No newline at end of file +print("") diff --git a/vona/utils/joinroom.py b/vona/utils/joinroom.py index 995194c..0f7dd00 100644 --- a/vona/utils/joinroom.py +++ b/vona/utils/joinroom.py @@ -1,7 +1,12 @@ -import urllib.parse, time, json, httpx import vona.globals as globals import vona.config as config +import urllib.parse +import time +import json +import httpx + + http_client = globals.http_client() versions = [ diff --git a/vona/utils/makekey.py b/vona/utils/makekey.py index 13a13d0..2fdb375 100644 --- a/vona/utils/makekey.py +++ b/vona/utils/makekey.py @@ -1,25 +1,23 @@ -# Generates a key in the format compatible with Synapse and Vona. - import base64 import os +# Generates a key in the format compatible with Synapse and Vona. + def mkchar() -> str: return os.urandom(4).hex()[:1] -key = ( - base64.b64encode(os.urandom(32)) - .decode("utf-8")[:43] - .replace("/", mkchar()) - .replace("+", mkchar()) -) +def random(length): + return ( + base64.b64encode(os.urandom(length)) + .decode("utf-8")[:length] + .replace("/", mkchar()) + .replace("+", mkchar()) + ) -key_id = ( - base64.b64encode(os.urandom(32)) - .decode("utf-8")[:6] - .replace("/", mkchar()) - .replace("+", mkchar()) -) + +key = random(43) +key_id = random(6) print(f"ed25519 {key_id} {key}")