Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
492ffc62e9
|
|||
|
9ca4c913f3
|
|||
|
6dd4fb04e3
|
|||
|
f771130a16
|
|||
|
9aa2e062e5
|
|||
|
c8d6f57e5b
|
|||
|
753625ad5c
|
|||
|
74a36e0b34
|
|||
|
b178011ad9
|
|||
|
76f89beb29
|
|||
|
7e8eb6de7f
|
|||
|
4e28507dea
|
|||
|
357d3b7429
|
|||
|
8a2bae706e
|
|||
|
4603ed86ac
|
|||
|
4a6e65226e
|
|||
|
fc9787b8ec
|
|||
|
2dbec63ff7
|
|||
|
2021fc027b
|
|||
|
e0115fe8db
|
3
SECURITY.md
Normal file
3
SECURITY.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Security
|
||||||
|
If you believe you have found a vulnerability in Vona, please email vel@riseup.net or DM `@vel:faelix.im` on Matrix.
|
||||||
|
Do not disclose the details of the vulnerability until a public fix has been made.
|
||||||
@@ -8,6 +8,7 @@ dependencies = [
|
|||||||
"httpx (>=0.28.1,<0.29.0)",
|
"httpx (>=0.28.1,<0.29.0)",
|
||||||
"pynacl (>=1.6.0,<2.0.0)",
|
"pynacl (>=1.6.0,<2.0.0)",
|
||||||
"flask[async] (>=3.1.2,<4.0.0)",
|
"flask[async] (>=3.1.2,<4.0.0)",
|
||||||
|
"resolvematrix @ git+https://codeberg.org/timedout/resolvematrix.git",
|
||||||
]
|
]
|
||||||
|
|
||||||
authors = [
|
authors = [
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ from flask import Flask, jsonify, request, redirect
|
|||||||
import vona.globals as globals
|
import vona.globals as globals
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import vona.config as config
|
import vona.config as config
|
||||||
|
import threading
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
from vona.federation import server
|
from vona.federation import server
|
||||||
from vona.custom import custom
|
from vona.custom import custom
|
||||||
@@ -24,9 +26,20 @@ app.register_blueprint(server)
|
|||||||
app.register_blueprint(apps)
|
app.register_blueprint(apps)
|
||||||
|
|
||||||
@app.before_request
|
@app.before_request
|
||||||
async def preflight():
|
async def validate_json():
|
||||||
if request.method == "OPTIONS":
|
if request.method == "OPTIONS":
|
||||||
return "", 204
|
return "", 200
|
||||||
|
|
||||||
|
elif request.method in ["PUT", "POST", "PATCH"]:
|
||||||
|
if "media" in request.path:
|
||||||
|
# Don't check media uploads
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
request.get_json(force=True)
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"error": "Content not JSON.", "errcode": "M_NOT_JSON"}), 400
|
||||||
|
|
||||||
|
|
||||||
@app.after_request
|
@app.after_request
|
||||||
async def handle_logging(response):
|
async def handle_logging(response):
|
||||||
@@ -36,7 +49,6 @@ async def handle_logging(response):
|
|||||||
response.headers["Access-Control-Allow-Methods"] = "GET, HEAD, POST, PUT, DELETE, OPTIONS"
|
response.headers["Access-Control-Allow-Methods"] = "GET, HEAD, POST, PUT, DELETE, OPTIONS"
|
||||||
|
|
||||||
if request.method == "OPTIONS":
|
if request.method == "OPTIONS":
|
||||||
# Discard logs for OPTIONS
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
origin = "unknown"
|
origin = "unknown"
|
||||||
@@ -44,13 +56,31 @@ async def handle_logging(response):
|
|||||||
try:
|
try:
|
||||||
if "Authorization" in request.headers:
|
if "Authorization" in request.headers:
|
||||||
if request.headers["Authorization"].split()[0] == "X-Matrix":
|
if request.headers["Authorization"].split()[0] == "X-Matrix":
|
||||||
origin = request.headers["Authorization"].split('origin="')[1].split('"')[0]
|
origin = (
|
||||||
|
request.headers["Authorization"]
|
||||||
|
.split("origin=")[1]
|
||||||
|
.split(",")[0]
|
||||||
|
)
|
||||||
|
|
||||||
|
while '"' in origin:
|
||||||
|
origin = origin.replace('"', "")
|
||||||
|
|
||||||
|
if origin == config.server_name:
|
||||||
|
return response
|
||||||
|
|
||||||
else:
|
else:
|
||||||
origin = "client"
|
origin = "client"
|
||||||
|
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
print(f'[{origin}] [{request.remote_addr}] [{datetime.now().strftime("%d/%b/%Y:%H:%M:%S")}] {request.method} {request.full_path} {response.status_code}')
|
print(
|
||||||
|
f"[{origin}] " +
|
||||||
|
f'[{datetime.now().strftime("%d/%b/%Y:%H:%M:%S")}] ' +
|
||||||
|
request.method + " " +
|
||||||
|
request.full_path.rstrip("?") + " " +
|
||||||
|
str(response.status_code)
|
||||||
|
)
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@@ -100,4 +130,20 @@ async def client():
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def federation_self_test():
|
||||||
|
try:
|
||||||
|
resp = globals.http_client().get(
|
||||||
|
path="/",
|
||||||
|
destination=config.server_name,
|
||||||
|
)
|
||||||
|
|
||||||
|
resp.raise_for_status()
|
||||||
|
print("[INFO] Federation self-test OK")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[FATL] Federation self-test failed: {e}")
|
||||||
|
os._exit(1)
|
||||||
|
|
||||||
|
threading.Thread(target=federation_self_test).start()
|
||||||
|
|
||||||
|
|
||||||
app.run(host=config.addr, port=config.port)
|
app.run(host=config.addr, port=config.port)
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ async def spec_versions():
|
|||||||
@client.route("/_matrix/client/v3/admin/whois/<user>")
|
@client.route("/_matrix/client/v3/admin/whois/<user>")
|
||||||
@client.route("/_matrix/client/r0/admin/whois/<user>")
|
@client.route("/_matrix/client/r0/admin/whois/<user>")
|
||||||
async def whois(user):
|
async def whois(user):
|
||||||
if userId.startswith("@"):
|
if user.startswith("@"):
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"devices": {
|
"devices": {
|
||||||
"": {
|
"": {
|
||||||
@@ -644,14 +644,10 @@ async def presence(user):
|
|||||||
if request.method == "PUT":
|
if request.method == "PUT":
|
||||||
return jsonify({})
|
return jsonify({})
|
||||||
|
|
||||||
return jsonify({
|
return jsonify({"presence": "online"})
|
||||||
"presence": "online"
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
|
@client.route("/_matrix/client/v3/publicRooms", methods=["GET", "POST"])
|
||||||
@client.route("/_matrix/client/r0/publicRooms", methods=["GET", "POST"])
|
@client.route("/_matrix/client/r0/publicRooms", methods=["GET", "POST"])
|
||||||
async def room_directory():
|
async def room_directory():
|
||||||
return jsonify({
|
return jsonify(globals.room_dir)
|
||||||
"chunk": [],
|
|
||||||
"total_room_count_estimate": 0
|
|
||||||
})
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ server_name: str = ""
|
|||||||
signing_key: str = ""
|
signing_key: str = ""
|
||||||
support: dict = {"contacts": []}
|
support: dict = {"contacts": []}
|
||||||
|
|
||||||
_CONFIG_PATH = Path("/etc/vona/config.toml")
|
_CONFIG_PATH = Path(os.getenv("VONA_CONFIG", "/etc/vona/config.toml"))
|
||||||
|
|
||||||
|
|
||||||
def _fatal(msg: str) -> None:
|
def _fatal(msg: str) -> None:
|
||||||
|
|||||||
@@ -2,16 +2,21 @@ from flask import Blueprint
|
|||||||
|
|
||||||
custom = Blueprint("custom", __name__)
|
custom = Blueprint("custom", __name__)
|
||||||
|
|
||||||
# This implements custom endpoints
|
# This implements non-standard
|
||||||
# used by other homeserver
|
# endpoints created by other
|
||||||
# implementations. They do not start
|
# homeserver implementations.
|
||||||
# with /_matrix/
|
|
||||||
|
|
||||||
|
|
||||||
|
from .hammerhead import hammerhead
|
||||||
from .conduwuit import conduwuit
|
from .conduwuit import conduwuit
|
||||||
from .dendrite import dendrite
|
from .dendrite import dendrite
|
||||||
|
from .telodendria import telo
|
||||||
from .synapse import synapse
|
from .synapse import synapse
|
||||||
|
from .citadel import citadel
|
||||||
|
|
||||||
|
custom.register_blueprint(hammerhead)
|
||||||
custom.register_blueprint(conduwuit)
|
custom.register_blueprint(conduwuit)
|
||||||
custom.register_blueprint(dendrite)
|
custom.register_blueprint(dendrite)
|
||||||
custom.register_blueprint(synapse)
|
custom.register_blueprint(synapse)
|
||||||
|
custom.register_blueprint(citadel)
|
||||||
|
custom.register_blueprint(telo)
|
||||||
|
|||||||
61
vona/custom/citadel.py
Normal file
61
vona/custom/citadel.py
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
from flask import Blueprint, jsonify, request
|
||||||
|
import vona.config as config
|
||||||
|
import base64
|
||||||
|
import os
|
||||||
|
|
||||||
|
citadel = Blueprint("citadel", __name__)
|
||||||
|
|
||||||
|
# These are endpoints made by Thales Citadel
|
||||||
|
|
||||||
|
# TODO: Add more endpoints, this likely
|
||||||
|
# isn't all of them
|
||||||
|
|
||||||
|
|
||||||
|
@citadel.route("/_matrix/client/r0/citadel/stats/m.news/<event>", methods=["GET", "PUT"])
|
||||||
|
async def news_stats(event):
|
||||||
|
if request.method == "PUT":
|
||||||
|
return jsonify({"success": True})
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
"total_clicks": config.the_funny_number,
|
||||||
|
"user_readings": config.the_funny_number
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@citadel.route("/_matrix/client/r0/citadel/rooms/<room>/closeRoom", methods=["POST"])
|
||||||
|
async def close_room(room):
|
||||||
|
store_response = request.json.get("store_response", True)
|
||||||
|
|
||||||
|
operation_id = base64.b64encode(
|
||||||
|
bytes(
|
||||||
|
os.urandom(8).hex(),
|
||||||
|
"utf-8"
|
||||||
|
)
|
||||||
|
).decode("utf-8")[:14]
|
||||||
|
|
||||||
|
|
||||||
|
if store_response:
|
||||||
|
resp = {"operation_id": operation_id}
|
||||||
|
else:
|
||||||
|
resp = {
|
||||||
|
"operation_id": operation_id,
|
||||||
|
"previous_state": {
|
||||||
|
"progress": {
|
||||||
|
"steps": {
|
||||||
|
"step_kick_users": {
|
||||||
|
"status": "complete"
|
||||||
|
},
|
||||||
|
"step_purge_history": {
|
||||||
|
"status": "complete"
|
||||||
|
},
|
||||||
|
"step_purge_media": {
|
||||||
|
"status": "complete"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"status": "complete"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return jsonify(resp)
|
||||||
77
vona/custom/hammerhead.py
Normal file
77
vona/custom/hammerhead.py
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
from flask import Blueprint, request, jsonify
|
||||||
|
from vona.config import the_funny_number
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
from vona.federation import send_join
|
||||||
|
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})
|
||||||
|
|
||||||
|
@hammerhead.route("/_hammerhead/version")
|
||||||
|
async def version():
|
||||||
|
return jsonify({"version": globals.version})
|
||||||
|
|
||||||
|
|
||||||
|
@hammerhead.route("/_hammerhead/admin/server-info/<server>")
|
||||||
|
async def server_info(server):
|
||||||
|
return jsonify({
|
||||||
|
"destination": {
|
||||||
|
"expires": datetime.now(timezone.utc).isoformat(),
|
||||||
|
"host_header": server,
|
||||||
|
"ip_port": [
|
||||||
|
f"{server}:443"
|
||||||
|
],
|
||||||
|
"server_name": server
|
||||||
|
},
|
||||||
|
"keys": globals.sign_json({
|
||||||
|
"old_verify_keys": {},
|
||||||
|
"server_name": server,
|
||||||
|
"valid_until_ts": int(time.time() * 1000 + 604800000),
|
||||||
|
"verify_keys": {
|
||||||
|
f"ed25519:{config.signing_key.split()[1]}": {
|
||||||
|
"key": globals.pubkey()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
"software": {
|
||||||
|
"server": {
|
||||||
|
"version": globals.version,
|
||||||
|
"name": "Vona"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@hammerhead.route("/_hammerhead/admin/rooms/<room>/state-resolver")
|
||||||
|
async def room_state(room):
|
||||||
|
class bullshit:
|
||||||
|
def get_json():
|
||||||
|
return {}
|
||||||
|
|
||||||
|
state = send_join(bullshit, room)["state"]
|
||||||
|
formatted_state = {}
|
||||||
|
event_cache = {}
|
||||||
|
|
||||||
|
for event in state:
|
||||||
|
key = f"({event["type"]},'{event["state_key"]}')"
|
||||||
|
formatted_state[key] = event
|
||||||
|
|
||||||
|
event_id = event["event_id"]
|
||||||
|
event_cache[event_id] = event
|
||||||
|
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
"current_state": formatted_state,
|
||||||
|
"event_cache": event_cache,
|
||||||
|
"room_id": room,
|
||||||
|
"room_version": globals.room_version_from_id(room)
|
||||||
|
})
|
||||||
@@ -24,9 +24,9 @@ synapse = Blueprint("synapse", __name__)
|
|||||||
@synapse.route("/_synapse/admin/v1/media/unquarantine/<s>/<media_id>", methods=["POST"])
|
@synapse.route("/_synapse/admin/v1/media/unquarantine/<s>/<media_id>", methods=["POST"])
|
||||||
@synapse.route("/_synapse/admin/v1/federation/destinations/<destination>/reset_connection", methods=["POST"])
|
@synapse.route("/_synapse/admin/v1/federation/destinations/<destination>/reset_connection", methods=["POST"])
|
||||||
@synapse.route("/_synapse/admin/v1/rooms/<room>")
|
@synapse.route("/_synapse/admin/v1/rooms/<room>")
|
||||||
@synapse.route("/_synapse/admin/v1/rooms/<room_id>/timestamp_to_event")
|
@synapse.route("/_synapse/admin/v1/rooms/<room>/timestamp_to_event")
|
||||||
@synapse.route("/_synapse/admin/v2/rooms/delete_status/<delete_id>")
|
@synapse.route("/_synapse/admin/v2/rooms/delete_status/<delete_id>")
|
||||||
@synapse.route("/_synapse/admin/v1/rooms/<room_id_or_alias>/make_room_admin", methods=["POST"])
|
@synapse.route("/_synapse/admin/v1/rooms/<room>/make_room_admin", methods=["POST"])
|
||||||
async def response(**kwargs):
|
async def response(**kwargs):
|
||||||
return jsonify({})
|
return jsonify({})
|
||||||
|
|
||||||
@@ -118,9 +118,24 @@ async def account_data(user_id):
|
|||||||
@synapse.route("/_synapse/admin/v1/users/<user_id>/media", methods=["GET", "DELETE"])
|
@synapse.route("/_synapse/admin/v1/users/<user_id>/media", methods=["GET", "DELETE"])
|
||||||
async def account_media(user_id):
|
async def account_media(user_id):
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
return jsonify({"media": [{"created_ts":config.the_funny_number,"last_access_ts":config.the_funny_number,"media_id":"cat","media_length":config.the_funny_number,"media_type":"image/jpeg","quarantined_by":"null","safe_from_quarantine":False,"upload_name":"cat.jpg"}], "total": config.the_funny_number})
|
return jsonify({
|
||||||
|
"media": [{
|
||||||
|
"created_ts": config.the_funny_number,
|
||||||
|
"last_access_ts": config.the_funny_number,
|
||||||
|
"media_id": "cat",
|
||||||
|
"media_length": config.the_funny_number,
|
||||||
|
"media_type": "image/jpeg",
|
||||||
|
"quarantined_by": None,
|
||||||
|
"safe_from_quarantine": False,
|
||||||
|
"upload_name": "cat.jpg"
|
||||||
|
}],
|
||||||
|
"total": config.the_funny_number
|
||||||
|
})
|
||||||
|
|
||||||
return jsonify({"deleted_media": ["cat"], "total": config.the_funny_number})
|
return jsonify({
|
||||||
|
"deleted_media": ["cat"],
|
||||||
|
"total": config.the_funny_number
|
||||||
|
})
|
||||||
|
|
||||||
@synapse.route("/_synapse/admin/v1/users/<user_id>/login", methods=["POST"])
|
@synapse.route("/_synapse/admin/v1/users/<user_id>/login", methods=["POST"])
|
||||||
async def account_login(user_id):
|
async def account_login(user_id):
|
||||||
@@ -137,7 +152,7 @@ async def device_list(user_id):
|
|||||||
"devices": [{
|
"devices": [{
|
||||||
"device_id": "VVOONNAA",
|
"device_id": "VVOONNAA",
|
||||||
"display_name": "Vona",
|
"display_name": "Vona",
|
||||||
"last_seen_ip": "127.0.0.1",
|
"last_seen_ip": config.addr,
|
||||||
"last_seen_ts": config.the_funny_number,
|
"last_seen_ts": config.the_funny_number,
|
||||||
"last_seen_user_agent": f"Vona/{globals.version}"
|
"last_seen_user_agent": f"Vona/{globals.version}"
|
||||||
}],
|
}],
|
||||||
@@ -152,7 +167,7 @@ async def device_info(user_id, device_id):
|
|||||||
return jsonify({
|
return jsonify({
|
||||||
"device_id": "VVOONNAA",
|
"device_id": "VVOONNAA",
|
||||||
"display_name": "Vona",
|
"display_name": "Vona",
|
||||||
"last_seen_ip": "127.0.0.1",
|
"last_seen_ip": config.addr,
|
||||||
"last_seen_ts": config.the_funny_number,
|
"last_seen_ts": config.the_funny_number,
|
||||||
"last_seen_user_agent": f"Vona/{globals.version}"
|
"last_seen_user_agent": f"Vona/{globals.version}"
|
||||||
})
|
})
|
||||||
@@ -169,11 +184,11 @@ async def username_available():
|
|||||||
|
|
||||||
@synapse.route("/_synapse/admin/v1/threepid/<medium>/users/<addr>")
|
@synapse.route("/_synapse/admin/v1/threepid/<medium>/users/<addr>")
|
||||||
@synapse.route("/_synapse/admin/v1/auth_providers/<provider>/users/<ext>")
|
@synapse.route("/_synapse/admin/v1/auth_providers/<provider>/users/<ext>")
|
||||||
async def threepid(p, a):
|
async def threepid(**kwargs):
|
||||||
return jsonify({"user_id": f"@vona:{config.server_name}"})
|
return jsonify({"user_id": f"@vona:{config.server_name}"})
|
||||||
|
|
||||||
@synapse.route("/_synapse/admin/v1/<user_id>/redact")
|
@synapse.route("/_synapse/admin/v1/<user_id>/redact")
|
||||||
def redact(user_id):
|
async def redact(user_id):
|
||||||
return jsonify({"redact_id": os.urandom(16).hex()})
|
return jsonify({"redact_id": os.urandom(16).hex()})
|
||||||
|
|
||||||
@synapse.route("/_synapse/admin/v1/user/redact_status/<redact_id>")
|
@synapse.route("/_synapse/admin/v1/user/redact_status/<redact_id>")
|
||||||
@@ -191,8 +206,8 @@ async def register():
|
|||||||
|
|
||||||
return jsonify({"access_token": "vona"})
|
return jsonify({"access_token": "vona"})
|
||||||
|
|
||||||
@synapse.route("/_synapse/admin/v1/join/<roomId>", methods=["POST"])
|
@synapse.route("/_synapse/admin/v1/join/<room>", methods=["POST"])
|
||||||
async def membership_manipulation(roomId):
|
async def membership_manipulation(room):
|
||||||
return jsonify({"room_id": globals.make_event_id().replace("$", "!")})
|
return jsonify({"room_id": globals.make_event_id().replace("$", "!")})
|
||||||
|
|
||||||
@synapse.route("/_synapse/admin/v1/account_validity/validity", methods=["POST"])
|
@synapse.route("/_synapse/admin/v1/account_validity/validity", methods=["POST"])
|
||||||
@@ -204,8 +219,8 @@ async def account_validity():
|
|||||||
async def server_notice(**kwargs):
|
async def server_notice(**kwargs):
|
||||||
return jsonify({"event_id": globals.make_event_id()})
|
return jsonify({"event_id": globals.make_event_id()})
|
||||||
|
|
||||||
@synapse.route("/_synapse/admin/v1/purge_history/<room_id>/<event_id>", methods=["POST"])
|
@synapse.route("/_synapse/admin/v1/purge_history/<room>/<event_id>", methods=["POST"])
|
||||||
@synapse.route("/_synapse/admin/v1/purge_history/<room_id>", methods=["POST"])
|
@synapse.route("/_synapse/admin/v1/purge_history/<room>", methods=["POST"])
|
||||||
async def purge_event(**kwargs):
|
async def purge_event(**kwargs):
|
||||||
return jsonify({"purge_id": os.urandom(16).hex()})
|
return jsonify({"purge_id": os.urandom(16).hex()})
|
||||||
|
|
||||||
@@ -213,12 +228,12 @@ async def purge_event(**kwargs):
|
|||||||
async def purge_status(purge_id):
|
async def purge_status(purge_id):
|
||||||
return jsonify({"status":"active"})
|
return jsonify({"status":"active"})
|
||||||
|
|
||||||
@synapse.route("/_synapse/admin/v1/room/<room_id>/media")
|
@synapse.route("/_synapse/admin/v1/room/<room>/media")
|
||||||
async def room_media(room_id):
|
async def room_media(room):
|
||||||
return jsonify({"local": [f"mxc://{config.server_name}/cat"], "remote": []})
|
return jsonify({"local": [f"mxc://{config.server_name}/cat"], "remote": []})
|
||||||
|
|
||||||
@synapse.route("/_synapse/admin/v1/room/<room_id>/media/quarantine", methods=["POST"])
|
@synapse.route("/_synapse/admin/v1/room/<room>/media/quarantine", methods=["POST"])
|
||||||
async def quarantine_room_media(room_id):
|
async def quarantine_room_media(room):
|
||||||
return jsonify({"num_quarantined": config.the_funny_number})
|
return jsonify({"num_quarantined": config.the_funny_number})
|
||||||
|
|
||||||
@synapse.route("/_synapse/admin/v1/media/<s>/delete", methods=["POST"])
|
@synapse.route("/_synapse/admin/v1/media/<s>/delete", methods=["POST"])
|
||||||
@@ -233,7 +248,15 @@ async def delete_remote_media():
|
|||||||
|
|
||||||
@synapse.route("/_synapse/admin/v1/statistics/users/media")
|
@synapse.route("/_synapse/admin/v1/statistics/users/media")
|
||||||
async def media_stats():
|
async def media_stats():
|
||||||
return jsonify({"users":[{"displayname":"Vona","media_count":config.the_funny_number,"media_length":config.the_funny_number,"user_id":f"@vona:{config.server_name}"}],"total":config.the_funny_number})
|
return jsonify({
|
||||||
|
"users": [{
|
||||||
|
"displayname": "Vona",
|
||||||
|
"media_count": config.the_funny_number,
|
||||||
|
"media_length": config.the_funny_number,
|
||||||
|
"user_id": f"@vona:{config.server_name}"
|
||||||
|
}],
|
||||||
|
"total": config.the_funny_number
|
||||||
|
})
|
||||||
|
|
||||||
@synapse.route("/_synapse/admin/v1/statistics/database/rooms")
|
@synapse.route("/_synapse/admin/v1/statistics/database/rooms")
|
||||||
async def room_stats():
|
async def room_stats():
|
||||||
@@ -302,7 +325,7 @@ async def interact_with_reported_event(report_id):
|
|||||||
@synapse.route("/_synapse/admin/v1/federation/destinations")
|
@synapse.route("/_synapse/admin/v1/federation/destinations")
|
||||||
async def federation_destinations():
|
async def federation_destinations():
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"destinations": [{}],
|
"destinations": [],
|
||||||
"total": 0
|
"total": 0
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -366,7 +389,7 @@ async def rooms():
|
|||||||
"total_rooms": 0
|
"total_rooms": 0
|
||||||
})
|
})
|
||||||
|
|
||||||
@synapse.route("/_synapse/admin/v1/rooms/<room_id>/members")
|
@synapse.route("/_synapse/admin/v1/rooms/<room>/members")
|
||||||
async def room_members(room):
|
async def room_members(room):
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"members": [
|
"members": [
|
||||||
@@ -375,11 +398,11 @@ async def room_members(room):
|
|||||||
"total": 1
|
"total": 1
|
||||||
})
|
})
|
||||||
|
|
||||||
@synapse.route("/_synapse/admin/v1/rooms/<room_id>/state")
|
@synapse.route("/_synapse/admin/v1/rooms/<room>/state")
|
||||||
async def room_state(room):
|
async def room_state(room):
|
||||||
return jsonify({"state": []})
|
return jsonify({"state": []})
|
||||||
|
|
||||||
@synapse.route("/_synapse/admin/v1/rooms/<room_id>/state")
|
@synapse.route("/_synapse/admin/v1/rooms/<room>/state")
|
||||||
async def room_messages(room):
|
async def room_messages(room):
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"chunk": [],
|
"chunk": [],
|
||||||
@@ -387,11 +410,11 @@ async def room_messages(room):
|
|||||||
"start": "vona"
|
"start": "vona"
|
||||||
})
|
})
|
||||||
|
|
||||||
@synapse.route("/_synapse/admin/v1/rooms/<room_id>/block", methods=["GET", "PUT"])
|
@synapse.route("/_synapse/admin/v1/rooms/<room>/block", methods=["GET", "PUT"])
|
||||||
async def block_room(room):
|
async def block_room(room):
|
||||||
return jsonify({"block": False})
|
return jsonify({"block": False})
|
||||||
|
|
||||||
@synapse.route("/_synapse/admin/v1/rooms/<room_id>", methods=["DELETE"])
|
@synapse.route("/_synapse/admin/v1/rooms/<room>", methods=["DELETE"])
|
||||||
async def room_delete(room):
|
async def room_delete(room):
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"kicked_users": [
|
"kicked_users": [
|
||||||
@@ -399,18 +422,18 @@ async def room_delete(room):
|
|||||||
],
|
],
|
||||||
"failed_to_kick_users": [],
|
"failed_to_kick_users": [],
|
||||||
"local_aliases": [],
|
"local_aliases": [],
|
||||||
"new_room_id": f"!vona:{config.server_name}"
|
"new_room_id": globals.make_event_id(seed=room)
|
||||||
})
|
})
|
||||||
|
|
||||||
@synapse.route("/_synapse/admin/v2/rooms/<room_id>", methods=["DELETE"])
|
@synapse.route("/_synapse/admin/v2/rooms/<room>", methods=["DELETE"])
|
||||||
async def room_delete_v2(room):
|
async def room_delete_v2(room):
|
||||||
return jsonify({"delete_id": "vona"})
|
return jsonify({"delete_id": "vona"})
|
||||||
|
|
||||||
@synapse.route("/_synapse/admin/v2/rooms/<room_id>/delete_status")
|
@synapse.route("/_synapse/admin/v2/rooms/<room>/delete_status")
|
||||||
async def room_delete_status(room):
|
async def room_delete_status(room):
|
||||||
return jsonify({"results": []})
|
return jsonify({"results": []})
|
||||||
|
|
||||||
@synapse.route("/_synapse/admin/v1/rooms/<room_id_or_alias>/forward_extremities", methods=["GET"])
|
@synapse.route("/_synapse/admin/v1/rooms/<room>/forward_extremities", methods=["GET"])
|
||||||
async def forward_extremities(room):
|
async def forward_extremities(room):
|
||||||
if request.method == "DELETE":
|
if request.method == "DELETE":
|
||||||
return jsonify({"deleted": 0})
|
return jsonify({"deleted": 0})
|
||||||
|
|||||||
85
vona/custom/telodendria.py
Normal file
85
vona/custom/telodendria.py
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
from flask import Blueprint, request, jsonify
|
||||||
|
from vona.globals import version
|
||||||
|
import vona.config as config
|
||||||
|
|
||||||
|
telo = Blueprint("telodendria", __name__)
|
||||||
|
|
||||||
|
# The telodendria admin API as specified by
|
||||||
|
# https://git.telodendria.org/Telodendria/Telodendria/src/branch/master/docs/user/admin/README.md
|
||||||
|
|
||||||
|
|
||||||
|
@telo.route("/_telodendria/admin/v1/restart", methods=["POST"])
|
||||||
|
@telo.route("/_telodendria/admin/v1/shutdown", methods=["POST"])
|
||||||
|
async def process_management(**kwargs):
|
||||||
|
return jsonify({})
|
||||||
|
|
||||||
|
|
||||||
|
@telo.route("/_telodendria/admin/v1/privileges/<lp>", methods=["GET", "PUT", "POST", "DELETE"])
|
||||||
|
@telo.route("/_telodendria/admin/v1/privileges/", methods=["GET", "POST"])
|
||||||
|
async def privileges(lp=None):
|
||||||
|
return jsonify({
|
||||||
|
"privileges": [
|
||||||
|
"GRANT_PRIVILEGES",
|
||||||
|
"PROC_CONTROL",
|
||||||
|
"ISSUE_TOKENS",
|
||||||
|
"DEACTIVATE",
|
||||||
|
"CONFIG",
|
||||||
|
"ALIAS",
|
||||||
|
"ALL"
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@telo.route("/_telodendria/admin/v1/config", methods=["GET", "PUT", "POST"])
|
||||||
|
async def configuration():
|
||||||
|
if request.method == "GET":
|
||||||
|
return jsonify({
|
||||||
|
"listen": [
|
||||||
|
{
|
||||||
|
"tls": None,
|
||||||
|
"port": config.port,
|
||||||
|
"threads": 1,
|
||||||
|
"maxConnections": 32
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"serverName": config.server_name,
|
||||||
|
"pid": "/dev/null",
|
||||||
|
"baseUrl": f"https://{config.server_name}/",
|
||||||
|
"identityServer": f"https://{config.server_name}/",
|
||||||
|
"runAs": None,
|
||||||
|
"federation": True,
|
||||||
|
"registration": config.users_can_register,
|
||||||
|
"log": {
|
||||||
|
"output": "stdout",
|
||||||
|
"level": "message",
|
||||||
|
"timestampFormat": "default",
|
||||||
|
"color": True
|
||||||
|
},
|
||||||
|
"maxCache": config.the_funny_number
|
||||||
|
})
|
||||||
|
|
||||||
|
return jsonify({"restart_required": True})
|
||||||
|
|
||||||
|
|
||||||
|
@telo.route("/_telodendria/admin/v1/stats")
|
||||||
|
async def stats():
|
||||||
|
return jsonify({
|
||||||
|
"memory_allocated": config.the_funny_number,
|
||||||
|
"version": version
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@telo.route("/_telodendria/admin/v1/tokens/<name>", methods=["GET", "DELETE"])
|
||||||
|
@telo.route("/_telodendria/admin/v1/tokens", methods=["GET", "POST"])
|
||||||
|
async def tokens(name=None):
|
||||||
|
if request.method == "DELETE":
|
||||||
|
return jsonify({})
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
"name": "vona",
|
||||||
|
"created_by": "vona",
|
||||||
|
"created_on": config.the_funny_number,
|
||||||
|
"expires_on": config.the_funny_number,
|
||||||
|
"used": config.the_funny_number,
|
||||||
|
"uses": config.the_funny_number
|
||||||
|
})
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
from flask import jsonify, Response, request, send_file, abort, Blueprint
|
from flask import jsonify, Response, request, send_file, abort, Blueprint
|
||||||
from vona.config import *
|
|
||||||
import vona.globals as globals
|
import vona.globals as globals
|
||||||
import httpx
|
import vona.config as config
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
import os
|
import os
|
||||||
@@ -25,13 +24,13 @@ def send_join(request, roomId) -> dict:
|
|||||||
create_event = {
|
create_event = {
|
||||||
"content": {
|
"content": {
|
||||||
"m.federate": True,
|
"m.federate": True,
|
||||||
"creator": f"@vona:{server_name}",
|
"creator": f"@vona:{config.server_name}",
|
||||||
"room_version": globals.room_version_from_id(roomId)
|
"room_version": globals.room_version_from_id(roomId)
|
||||||
},
|
},
|
||||||
"event_id": event_ids[0],
|
"event_id": event_ids[0],
|
||||||
"origin_server_ts": 1,
|
"origin_server_ts": 1,
|
||||||
"room_id": roomId,
|
"room_id": roomId,
|
||||||
"sender": f"@vona:{server_name}",
|
"sender": f"@vona:{config.server_name}",
|
||||||
"state_key": "",
|
"state_key": "",
|
||||||
"depth": 1,
|
"depth": 1,
|
||||||
"type": "m.room.create",
|
"type": "m.room.create",
|
||||||
@@ -45,12 +44,12 @@ def send_join(request, roomId) -> dict:
|
|||||||
our_join = {
|
our_join = {
|
||||||
"content": {
|
"content": {
|
||||||
"displayname": "Vona",
|
"displayname": "Vona",
|
||||||
"avatar_url": f"mxc://{server_name}/cat",
|
"avatar_url": f"mxc://{config.server_name}/cat",
|
||||||
"membership": "join"
|
"membership": "join"
|
||||||
},
|
},
|
||||||
"origin_server_ts": 2,
|
"origin_server_ts": 2,
|
||||||
"sender": f"@vona:{server_name}",
|
"sender": f"@vona:{config.server_name}",
|
||||||
"state_key": f"@vona:{server_name}",
|
"state_key": f"@vona:{config.server_name}",
|
||||||
"type": "m.room.member",
|
"type": "m.room.member",
|
||||||
"event_id": event_ids[1],
|
"event_id": event_ids[1],
|
||||||
"room_id": roomId,
|
"room_id": roomId,
|
||||||
@@ -71,17 +70,17 @@ def send_join(request, roomId) -> dict:
|
|||||||
pls = {
|
pls = {
|
||||||
"content": {
|
"content": {
|
||||||
"users": {
|
"users": {
|
||||||
f"@vona:{server_name}": "100"
|
f"@vona:{config.server_name}": "100"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"origin_server_ts": 3,
|
"origin_server_ts": 3,
|
||||||
"room_id": roomId,
|
"room_id": roomId,
|
||||||
"sender": f"@vona:{server_name}",
|
"sender": f"@vona:{config.server_name}",
|
||||||
"state_key": "",
|
"state_key": "",
|
||||||
"type": "m.room.power_levels",
|
"type": "m.room.power_levels",
|
||||||
"event_id": event_ids[2],
|
"event_id": event_ids[2],
|
||||||
"depth": 3,
|
"depth": 3,
|
||||||
"user_id": f"@vona:{server_name}",
|
"user_id": f"@vona:{config.server_name}",
|
||||||
"auth_events": [
|
"auth_events": [
|
||||||
[
|
[
|
||||||
screate_event["event_id"],
|
screate_event["event_id"],
|
||||||
@@ -106,7 +105,7 @@ def send_join(request, roomId) -> dict:
|
|||||||
"join_rule": "public"
|
"join_rule": "public"
|
||||||
},
|
},
|
||||||
"origin_server_ts": 4,
|
"origin_server_ts": 4,
|
||||||
"sender": f"@vona:{server_name}",
|
"sender": f"@vona:{config.server_name}",
|
||||||
"state_key": "",
|
"state_key": "",
|
||||||
"type": "m.room.join_rules",
|
"type": "m.room.join_rules",
|
||||||
"event_id": event_ids[3],
|
"event_id": event_ids[3],
|
||||||
@@ -141,7 +140,7 @@ def send_join(request, roomId) -> dict:
|
|||||||
},
|
},
|
||||||
"origin_server_ts": 5,
|
"origin_server_ts": 5,
|
||||||
"depth": 5,
|
"depth": 5,
|
||||||
"sender": f"@vona:{server_name}",
|
"sender": f"@vona:{config.server_name}",
|
||||||
"state_key": "",
|
"state_key": "",
|
||||||
"type": "m.room.guest_access",
|
"type": "m.room.guest_access",
|
||||||
"event_id": event_ids[4],
|
"event_id": event_ids[4],
|
||||||
@@ -158,10 +157,6 @@ def send_join(request, roomId) -> dict:
|
|||||||
[
|
[
|
||||||
spls["event_id"],
|
spls["event_id"],
|
||||||
spls["hashes"]
|
spls["hashes"]
|
||||||
],
|
|
||||||
[
|
|
||||||
sjoin_rule["event_id"],
|
|
||||||
sjoin_rule["hashes"]
|
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
"prev_events": [[
|
"prev_events": [[
|
||||||
@@ -178,7 +173,7 @@ def send_join(request, roomId) -> dict:
|
|||||||
"history_visibility": "shared"
|
"history_visibility": "shared"
|
||||||
},
|
},
|
||||||
"type": "m.room.history_visibility",
|
"type": "m.room.history_visibility",
|
||||||
"sender": f"@vona:{server_name}",
|
"sender": f"@vona:{config.server_name}",
|
||||||
"state_key": "",
|
"state_key": "",
|
||||||
"origin_server_ts": 6,
|
"origin_server_ts": 6,
|
||||||
"depth": 6,
|
"depth": 6,
|
||||||
@@ -196,10 +191,6 @@ def send_join(request, roomId) -> dict:
|
|||||||
[
|
[
|
||||||
spls["event_id"],
|
spls["event_id"],
|
||||||
spls["hashes"]
|
spls["hashes"]
|
||||||
],
|
|
||||||
[
|
|
||||||
sjoin_rule["event_id"],
|
|
||||||
sjoin_rule["hashes"]
|
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
"prev_events": [[
|
"prev_events": [[
|
||||||
@@ -217,7 +208,7 @@ def send_join(request, roomId) -> dict:
|
|||||||
"auth_chain": event_chain,
|
"auth_chain": event_chain,
|
||||||
"event": remote_join,
|
"event": remote_join,
|
||||||
"members_omitted": False,
|
"members_omitted": False,
|
||||||
"servers_in_room": [server_name],
|
"servers_in_room": [config.server_name],
|
||||||
"state": event_chain
|
"state": event_chain
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,10 +228,10 @@ async def version():
|
|||||||
async def keys():
|
async def keys():
|
||||||
return jsonify(globals.sign_json({
|
return jsonify(globals.sign_json({
|
||||||
"old_verify_keys": {},
|
"old_verify_keys": {},
|
||||||
"server_name": server_name,
|
"server_name": config.server_name,
|
||||||
"valid_until_ts": int(time.time() * 1000 + 604800000),
|
"valid_until_ts": int(time.time() * 1000 + 604800000),
|
||||||
"verify_keys": {
|
"verify_keys": {
|
||||||
f"ed25519:{signing_key.split()[1]}": {
|
f"ed25519:{config.signing_key.split()[1]}": {
|
||||||
"key": globals.pubkey()
|
"key": globals.pubkey()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -250,7 +241,7 @@ async def keys():
|
|||||||
async def room_query():
|
async def room_query():
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"room_id": globals.make_event_id().replace("$", "!"),
|
"room_id": globals.make_event_id().replace("$", "!"),
|
||||||
"servers": [server_name]
|
"servers": [config.server_name]
|
||||||
})
|
})
|
||||||
|
|
||||||
@server.route("/_matrix/federation/v1/media/download/<media_id>")
|
@server.route("/_matrix/federation/v1/media/download/<media_id>")
|
||||||
@@ -259,7 +250,7 @@ async def download_media(media_id):
|
|||||||
# multipart despite not even using
|
# multipart despite not even using
|
||||||
# it for anything. Minor annoyance.
|
# it for anything. Minor annoyance.
|
||||||
|
|
||||||
with open(cat, "rb") as img_file:
|
with open(config.cat, "rb") as img_file:
|
||||||
image_data = img_file.read()
|
image_data = img_file.read()
|
||||||
boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW"
|
boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW"
|
||||||
response_body = (
|
response_body = (
|
||||||
@@ -300,7 +291,7 @@ async def make_join(roomId, userId):
|
|||||||
}), 403
|
}), 403
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if roomId.split(":")[1] != server_name:
|
if roomId.split(":")[1] != config.server_name:
|
||||||
return not_invited()
|
return not_invited()
|
||||||
except:
|
except:
|
||||||
return not_invited()
|
return not_invited()
|
||||||
@@ -316,10 +307,10 @@ async def make_join(roomId, userId):
|
|||||||
|
|
||||||
join = {
|
join = {
|
||||||
"content": {
|
"content": {
|
||||||
"join_authorised_via_users_server": f"@vona:{server_name}",
|
"join_authorised_via_users_server": f"@vona:{config.server_name}",
|
||||||
"membership": "join"
|
"membership": "join"
|
||||||
},
|
},
|
||||||
"origin": server_name,
|
"origin": config.server_name,
|
||||||
"origin_server_ts": 7,
|
"origin_server_ts": 7,
|
||||||
"room_id": roomId,
|
"room_id": roomId,
|
||||||
"sender": userId,
|
"sender": userId,
|
||||||
@@ -353,12 +344,10 @@ async def make_join(roomId, userId):
|
|||||||
"room_version": globals.room_version_from_id(roomId)
|
"room_version": globals.room_version_from_id(roomId)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@server.route("/_matrix/federation/v1/publicRooms", methods=["POST", "GET"])
|
@server.route("/_matrix/federation/v1/publicRooms", methods=["POST", "GET"])
|
||||||
async def room_directory():
|
async def room_directory():
|
||||||
return jsonify({
|
return jsonify(globals.room_dir)
|
||||||
"chunk": [],
|
|
||||||
"total_room_count_estimate": 0
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
# https://spec.matrix.org/latest/server-server-api/#transactions
|
# https://spec.matrix.org/latest/server-server-api/#transactions
|
||||||
@@ -395,7 +384,7 @@ async def user_profile():
|
|||||||
field = request.args.get("field")
|
field = request.args.get("field")
|
||||||
if field:
|
if field:
|
||||||
if field == "avatar_url":
|
if field == "avatar_url":
|
||||||
return jsonify({"avatar_url":f"mxc://{server_name}/cat"})
|
return jsonify({"avatar_url":f"mxc://{config.server_name}/cat"})
|
||||||
elif field == "displayname":
|
elif field == "displayname":
|
||||||
return jsonify({"displayname":"Vona"})
|
return jsonify({"displayname":"Vona"})
|
||||||
|
|
||||||
@@ -404,31 +393,24 @@ async def user_profile():
|
|||||||
"error": "The requested profile key does not exist."
|
"error": "The requested profile key does not exist."
|
||||||
}), 404
|
}), 404
|
||||||
|
|
||||||
return jsonify({"avatar_url": f"mxc://{server_name}/cat","displayname": "Vona"})
|
return jsonify({"avatar_url": f"mxc://{config.server_name}/cat","displayname": "Vona"})
|
||||||
|
|
||||||
|
|
||||||
# https://spec.matrix.org/latest/server-server-api/#device-management
|
@server.route(f"/_matrix/federation/v1/user/devices/@/:{config.server_name}")
|
||||||
@server.route(f"/_matrix/federation/v1/user/devices/@/:{server_name}")
|
|
||||||
@server.route("/_matrix/federation/v1/user/devices/<user>")
|
@server.route("/_matrix/federation/v1/user/devices/<user>")
|
||||||
async def user_devices(user):
|
async def user_devices(user):
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"devices": [],
|
"devices": [],
|
||||||
"stream_id": the_funny_number,
|
"stream_id": config.the_funny_number,
|
||||||
"user_id": f"@vona:{server_name}"
|
"user_id": f"@vona:{config.server_name}"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@server.route("/_matrix/federation/v1/user/keys/query", methods=["POST"])
|
@server.route("/_matrix/federation/v1/user/keys/query", methods=["POST"])
|
||||||
async def user_keys():
|
async def user_keys():
|
||||||
try:
|
return jsonify({
|
||||||
users = request.json["device_keys"]
|
"device_keys": request.json.get("device_keys", {})
|
||||||
except:
|
})
|
||||||
return jsonify({
|
|
||||||
"errcode": "M_NOT_FOUND",
|
|
||||||
"error": "User does not exist"
|
|
||||||
}), 404
|
|
||||||
|
|
||||||
return jsonify({"device_keys": users})
|
|
||||||
|
|
||||||
|
|
||||||
@server.route("/_matrix/federation/v2/invite/<room>/<txnId>", methods=["PUT"])
|
@server.route("/_matrix/federation/v2/invite/<room>/<txnId>", methods=["PUT"])
|
||||||
@@ -438,21 +420,16 @@ async def invite_user_v2(room, txnId):
|
|||||||
|
|
||||||
@server.route("/_matrix/federation/v1/invite/<room>/<txnId>", methods=["PUT"])
|
@server.route("/_matrix/federation/v1/invite/<room>/<txnId>", methods=["PUT"])
|
||||||
async def invite_user_v1(room, txnId):
|
async def invite_user_v1(room, txnId):
|
||||||
return [200, invite_user(request.data)]
|
return [200, invite_user(request.json)]
|
||||||
|
|
||||||
|
|
||||||
def invite_user(data):
|
def invite_user(invite_data):
|
||||||
try:
|
|
||||||
invite_data = json.loads(data)
|
|
||||||
except:
|
|
||||||
return jsonify({"errcode":"M_NOT_JSON","error":"Content not JSON."}),
|
|
||||||
|
|
||||||
if "event" in invite_data:
|
if "event" in invite_data:
|
||||||
if "room_version" in invite_data:
|
if "room_version" in invite_data:
|
||||||
if invite_data["room_version"] != "2":
|
if invite_data["room_version"] not in ["1", "2"]:
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"errcode": "M_INCOMPATIBLE_ROOM_VERSION",
|
"errcode": "M_INCOMPATIBLE_ROOM_VERSION",
|
||||||
"error": "Vona only supports room version 2.",
|
"error": "Unsupported room version",
|
||||||
"room_version": invite_data["room_version"]
|
"room_version": invite_data["room_version"]
|
||||||
}), 400
|
}), 400
|
||||||
|
|
||||||
@@ -466,9 +443,12 @@ def invite_user(data):
|
|||||||
and "state_key" in event
|
and "state_key" in event
|
||||||
and "room_id" in event
|
and "room_id" in event
|
||||||
and content["membership"] == "invite"
|
and content["membership"] == "invite"
|
||||||
and event["state_key"] == f"@vona:{server_name}"
|
and event["state_key"] == f"@vona:{config.server_name}"
|
||||||
):
|
):
|
||||||
return jsonify({"event": globals.sign_json_without_discard(invite_data["event"]), "room_version": "2"})
|
return jsonify({
|
||||||
|
"event": globals.sign_json_without_discard(invite_data["event"]),
|
||||||
|
"room_version": invite_data["room_version"]
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
@@ -482,4 +462,26 @@ async def space_hierachy(roomId):
|
|||||||
return jsonify({
|
return jsonify({
|
||||||
"errcode": "M_NOT_FOUND",
|
"errcode": "M_NOT_FOUND",
|
||||||
"error": "Room does not exist."
|
"error": "Room does not exist."
|
||||||
|
}), 404
|
||||||
|
|
||||||
|
|
||||||
|
@server.route("/_matrix/federation/v1/org.matrix.msc4358/discover_common_rooms", methods=["POST"])
|
||||||
|
@server.route("/_matrix/federation/v1/discover_common_rooms", methods=["POST"])
|
||||||
|
async def discover_common_rooms():
|
||||||
|
tags = request.json.get("room_participation_tags", [])
|
||||||
|
return jsonify({"recognised_tags": tags})
|
||||||
|
|
||||||
|
|
||||||
|
@server.route("/_matrix/federation/v1/backfill/<room>")
|
||||||
|
async def backfill(room):
|
||||||
|
# TODO: burger king foot lettuce
|
||||||
|
|
||||||
|
class bullshit:
|
||||||
|
def get_json():
|
||||||
|
return {}
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
"origin": config.server_name,
|
||||||
|
"origin_server_ts": int(str(time.time() * 1000).split(".")[0]),
|
||||||
|
"pdus": send_join(bullshit, room)["state"]
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
|
from resolvematrix import ServerResolver
|
||||||
|
from types import SimpleNamespace
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
import vona.config as config
|
import vona.config as config
|
||||||
import nacl.signing
|
import nacl.signing
|
||||||
import hashlib
|
import hashlib
|
||||||
import base64
|
import base64
|
||||||
import random
|
import random
|
||||||
|
import httpx
|
||||||
import copy
|
import copy
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
|
||||||
version = "1.4.2"
|
version = "1.4.4"
|
||||||
|
|
||||||
|
|
||||||
def canonical_json(value):
|
def canonical_json(value):
|
||||||
@@ -109,7 +112,9 @@ def event_hash(event_object):
|
|||||||
|
|
||||||
event_json_bytes = canonical_json(event_object)
|
event_json_bytes = canonical_json(event_object)
|
||||||
|
|
||||||
return base64.b64encode(hashlib.sha256(event_json_bytes).digest()).decode("utf-8")
|
return base64.b64encode(
|
||||||
|
hashlib.sha256(event_json_bytes).digest()
|
||||||
|
).decode("utf-8").rstrip("=")
|
||||||
|
|
||||||
|
|
||||||
def pubkey() -> str:
|
def pubkey() -> str:
|
||||||
@@ -127,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 = {
|
request_json = {
|
||||||
"method": method,
|
"method": method,
|
||||||
"uri": path,
|
"uri": path,
|
||||||
@@ -250,3 +260,89 @@ def room_version_from_id(room_id):
|
|||||||
return most_common[0] if most_common else None
|
return most_common[0] if most_common else None
|
||||||
|
|
||||||
return most_common_character(nums)[0]
|
return most_common_character(nums)[0]
|
||||||
|
|
||||||
|
|
||||||
|
room_dir = {
|
||||||
|
"chunk": [{
|
||||||
|
"avatar_url": f"mxc://{config.server_name}/cat",
|
||||||
|
"guest_can_join": False,
|
||||||
|
"join_rule": "public",
|
||||||
|
"name": "Vona",
|
||||||
|
"num_joined_members": 1,
|
||||||
|
"room_id": make_event_id().replace("$", "!"),
|
||||||
|
"room_type": "m.room",
|
||||||
|
"topic": "",
|
||||||
|
"world_readable": False,
|
||||||
|
"via": [config.server_name]
|
||||||
|
}],
|
||||||
|
"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}
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
print("Available utils:")
|
print("Available utils:")
|
||||||
|
|
||||||
a = [
|
a = [
|
||||||
"makekey"
|
"makekey",
|
||||||
|
"joinroom"
|
||||||
]
|
]
|
||||||
|
|
||||||
for t in a:
|
for t in a:
|
||||||
|
|||||||
83
vona/utils/joinroom.py
Normal file
83
vona/utils/joinroom.py
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
from resolvematrix import ServerResolver
|
||||||
|
import urllib.parse, time, json, httpx
|
||||||
|
import vona.globals as globals
|
||||||
|
import vona.config as config
|
||||||
|
|
||||||
|
http_client = globals.http_client()
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_input(prompt):
|
||||||
|
try:
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
username = get_user_input("Username:\n\t")
|
||||||
|
room_id = get_user_input("Room ID:\n\t")
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
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:
|
||||||
|
print("\nSending make_join request..")
|
||||||
|
|
||||||
|
make_join_response = http_client.get(
|
||||||
|
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()
|
||||||
|
make_join = make_join_response.json()
|
||||||
|
|
||||||
|
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 response.")
|
||||||
|
exit(1)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"An error occurred: {e}")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
join_event = make_join.get("event", {})
|
||||||
|
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()
|
||||||
|
|
||||||
|
timestamp = input("\nTimestamp (leave blank for now):\n\t")
|
||||||
|
try:
|
||||||
|
join_event["origin_server_ts"] = int(timestamp)
|
||||||
|
except ValueError:
|
||||||
|
join_event["origin_server_ts"] = int(f"{time.time() * 1000}".split(".")[0])
|
||||||
|
|
||||||
|
signed_join = globals.hash_and_sign_event(join_event)
|
||||||
|
|
||||||
|
try:
|
||||||
|
send_join_response = http_client.put(
|
||||||
|
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()
|
||||||
|
|
||||||
|
print("\nSuccess :)")
|
||||||
|
except httpx.HTTPStatusError as e:
|
||||||
|
print(
|
||||||
|
f"HTTP error occurred during send_join: {e.response.status_code} - {e.response.text}"
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"An error occurred during send_join: {e}")
|
||||||
Reference in New Issue
Block a user