Make Vona a Python module

This commit is contained in:
2025-10-01 11:06:58 -04:00
parent 0224909a50
commit eb3c2015c3
16 changed files with 52 additions and 27 deletions

657
vona/client/__init__.py Normal file
View File

@@ -0,0 +1,657 @@
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__)
@client.route("/_matrix/client/v3/account/password", methods=["POST"])
@client.route("/_matrix/client/v3/user/<user>/account_data/<type>", methods=["GET", "PUT"])
@client.route("/_matrix/client/r0/user/<user>/account_data/<type>", methods=["GET", "PUT"])
@client.route("/_matrix/client/v3/sendToDevice/<event>/<txnId>", methods=["PUT"])
@client.route("/_matrix/media/v3/upload/<server>/<media>", methods=["PUT"])
@client.route("/_matrix/client/v3/thirdparty/protocols")
@client.route("/_matrix/client/r0/thirdparty/protocols")
@client.route("/_matrix/client/v3/delete_devices", methods=["POST"])
@client.route("/_matrix/client/r0/delete_devices", methods=["POST"])
@client.route("/_matrix/client/v3/logout/all", methods=["POST"])
@client.route("/_matrix/client/v3/logout", methods=["POST"])
@client.route("/_matrix/client/r0/logout", methods=["POST"])
@client.route("/_matrix/client/v3/rooms/<room>/invite", methods=["POST"])
@client.route("/_matrix/client/v3/rooms/<roomId>/leave", methods=["POST"])
@client.route("/_matrix/client/r0/rooms/<roomId>/leave", methods=["POST"])
@client.route("/_matrix/client/v3/rooms/<roomId>/read_markers", methods=["POST"])
@client.route("/_matrix/client/r0/rooms/<roomId>/read_markers", methods=["POST"])
@client.route("/_matrix/client/v3/rooms/<room>/typing/<user>", methods=["PUT"])
@client.route("/_matrix/client/v3/keys/device_signing/upload", methods=["POST"])
@client.route("/_matrix/client/v3/rooms/<room>/receipt/<type>/<event>", methods=["POST"])
@client.route("/_matrix/client/v3/users/<user>/report", methods=["POST"])
@client.route("/_matrix/client/v3/voip/turnServer")
@client.route("/_matrix/client/r0/voip/turnServer")
@client.route("/_matrix/client/v3/rooms/<r>/report/<e>")
@client.route("/_matrix/client/v3/rooms/<r>/report")
@client.route("/_matrix/client/v3/users/<u>/report")
async def empty_response(**kwargs):
return jsonify({})
@client.route("/_matrix/client/versions")
async def spec_versions():
return jsonify({
"versions": (
["r0.0.0"] + [f"r0.{i}.0" for i in range(1, 7)] +
["r0.6.1"] + [f"v1.{i}" for i in range(1, 17)]
),
"unstable_features": {
"uk.half-shot.msc2666": True,
"uk.timedout.msc4323": True
}
})
@client.route("/_matrix/client/v3/admin/whois/<user>")
@client.route("/_matrix/client/r0/admin/whois/<user>")
async def whois(user):
if userId.startswith("@"):
return jsonify({
"devices": {
"": {
"sessions": [{
"connections": [{
"ip": "127.0.0.1",
"last_seen": config.the_funny_number,
"user_agent": f"Vona/{globals.vona_version}"
}]
}]
}
},
"user_id": user
})
return jsonify({
"errcode": "M_INVALID_PARAM",
"error": "Expected UserID string to start with '@'"
})
@client.route("/_matrix/client/unstable/uk.timedout.msc4323/admin/suspend/<user>", methods=["GET", "PUT"])
@client.route("/_matrix/client/v1/admin/suspend/<user>", methods=["GET", "PUT"])
async def suspend(user):
if request.method == "PUT":
req = request.get_json()
if req and "suspended" in req:
return jsonify({"suspended": req["suspended"]})
return jsonify({"suspended": True})
@client.route("/_matrix/client/unstable/uk.timedout.msc4323/admin/lock/<user>", methods=["GET", "PUT"])
@client.route("/_matrix/client/v1/admin/lock/<user>", methods=["GET", "PUT"])
async def lock(user):
if request.method == "PUT":
req = request.get_json()
if req and "locked" in req:
return jsonify({"locked": req["locked"]})
return jsonify({"locked": True})
@client.route("/_matrix/client/v3/rooms/<roomId>/members")
@client.route("/_matrix/client/r0/rooms/<roomId>/members")
async def room_member_count(roomId):
return jsonify({
"chunk": [{
"content": {
"avatar_url": f"mxc://{config.server_name}/cat",
"displayname": "Vona",
"membership": "join"
},
"event_id": globals.make_event_id(),
"origin_server_ts": config.the_funny_number,
"room_id": roomId,
"sender": f"@vona:{config.server_name}",
"state_key": f"@vona:{config.server_name}",
"type": "m.room.member",
"unsigned": {}
}]
})
@client.route("/_matrix/client/v3/account/whoami")
async def whoami():
return jsonify({
"device_id": "VVOONNAA",
"user_id": f"@vona:{config.server_name}"
})
@client.route("/_matrix/client/v3/register", methods=["POST"])
@client.route("/_matrix/client/v1/register", methods=["POST"])
@client.route("/_matrix/client/r0/register", methods=["POST"])
async def register():
if config.users_can_register:
try:
data = request.get_json()
if data and "auth" in data:
return jsonify({
"user_id": f"@vona:{config.server_name}",
"home_server": f"{config.server_name}",
"access_token": "vona",
"device_id": "VVOONNAA"
})
except:
pass
return jsonify({
"session": os.urandom(32).hex(),
"flows": [{"stages": ["m.login.dummy"]}],
"params": {}
}), 401
return jsonify({
"errcode": "M_FORBIDDEN",
"error": "M_FORBIDDEN: Registration has been disabled."
}), 403
@client.route("/_matrix/client/r0/login", methods=["GET", "POST"])
@client.route("/_matrix/client/v3/login", methods=["GET", "POST"])
async def login():
if request.method == "GET":
return jsonify({
"flows": [
{"type": "m.login.password"},
{"type": "m.login.application_service"},
{
"type": "m.login.token",
"get_login_token": True
}
]
})
return jsonify({
"access_token": "vona",
"device_id": "VVOONNAA",
"user_id": f"@vona:{config.server_name}"
})
@client.route("/_matrix/client/v3/account/password/email/requestToken", methods=["POST"])
async def pswd_reset():
return jsonify({"errcode":"M_THREEPID_NOT_FOUND","error":"Email not found"}), 400
@client.route("/_matrix/client/v3/keys/upload", methods=["POST"])
async def key_upload():
return jsonify({"one_time_key_counts":{"signed_curve25519":50}})
@client.route("/_matrix/client/v3/room_keys/version", methods=["POST", "GET"])
@client.route("/_matrix/client/unstable/room_keys/version", methods=["POST", "GET"])
async def room_keys():
if request.method == "POST":
return jsonify({"version": str(config.the_funny_number)})
return jsonify({
"algorithm": "m.megolm_backup.v1.curve25519-aes-sha2",
"auth_data": {
"public_key":"vonaisflazingbastandsemorymafe",
"signatures": {
f"@vona:{config.server_name}": {
# TODO: Make this actually valid
"ed25519:vonaa": "vona"
}
}
},
"count": config.the_funny_number,
"etag": "burgerkingfootlettuce",
"version": str(config.the_funny_number)
})
@client.route("/_matrix/client/v3/capabilities")
@client.route("/_matrix/client/r0/capabilities")
async def capabilities():
return jsonify({
"capabilities": {
"m.room_versions": {
"default": "2",
"available": {
"1": "stable",
"2": "stable",
"3": "stable",
"4": "stable",
"5": "stable",
"6": "stable",
"7": "stable",
"8": "stable",
"9": "stable",
"10": "stable",
"11": "stable",
"12": "stable"
},
"org.matrix.msc3244.room_capabilities": {
"knock": {
"preferred":"7",
"support": [
"7","8","9","10","11","12"
]
},
"restricted": {
"preferred": "9",
"support": ["8","9","10","11","12"]
}
}
},
"m.change_password": {"enabled": True},
"m.3pid_changes": {"enabled": True},
"m.get_login_token": {"enabled": False},
"m.profile_fields": {
"enabled": True,
"allowed":["*"]
},
"account_moderation": {
"suspend": True,
"lock": True
}
}
})
@client.route("/_matrix/client/r0/pushrules/")
@client.route("/_matrix/client/v3/pushrules/")
async def pushrules():
# TODO: Actually implement this
return jsonify({})
@client.route("/_matrix/client/v3/user/<user>/filter/<data>")
@client.route("/_matrix/client/r0/user/<user>/filter/<data>")
@client.route("/_matrix/client/v3/user/<user>/filter", methods=["POST"])
@client.route("/_matrix/client/r0/user/<user>/filter", methods=["POST"])
async def filter(**kwargs):
return jsonify({"filter_id": "vvvooonnnaaa"})
@client.route("/_matrix/client/v3/join/<room>", methods=["POST"])
@client.route("/_matrix/client/r0/join/<room>", methods=["POST"])
@client.route("/_matrix/client/v3/rooms/<room>/join", methods=["POST"])
@client.route("/_matrix/client/v3/knock/<room>", methods=["POST"])
async def join(room):
return jsonify({"room_id": room})
@client.route("/_matrix/client/v3/initialSync")
@client.route("/_matrix/client/v3/sync")
@client.route("/_matrix/client/r0/sync")
async def sync():
class bullshit:
def get_json():
return {}
async def remove_keys(l, keys_to_remove) -> dict:
if not isinstance(l, list):
return l
new_list = []
for d in l:
if not isinstance(d, dict):
new_list.append(d)
continue
new_dict = {}
for k, v in d.items():
if k in keys_to_remove:
continue
new_dict[k] = await remove_keys(v, keys_to_remove)
new_list.append(new_dict)
return new_list
room = globals.make_event_id().replace("$", "!")
old_room_state = send_join(
bullshit,
room
)["state"]
room_state = await remove_keys(
old_room_state,
[
"auth_events",
"prev_events",
"signatures",
"hashes",
"depth"
]
)
room_name = {
"content": {
"name": "Burger King Foot Lettuce cult"
},
"origin_server_ts": config.the_funny_number,
"sender": f"@vona:{config.server_name}",
"state_key": "",
"type": "m.room.name",
"event_id": globals.make_event_id(),
"room_id": room
}
room_state.append(room_name)
wait_time = 0
if "timeout" in request.args:
try:
wait_time = int(request.args.get("timeout")) / 1000
except:
pass
await asyncio.sleep(wait_time)
return jsonify({
"next_batch": f"{os.urandom(64).hex()}",
"presence": {},
"device_one_time_keys_count": {"signed_curve25519": 50},
"org.matrix.msc2732.device_unused_fallback_key_types": ["signed_curve25519"],
"device_unused_fallback_key_types": ["signed_curve25519"],
"rooms": {
"join": {
room: {
"timeline": {
"events": [],
"prev_batch": f"{random.randint(32095,309390)}",
"limited": False
},
"state": {"events": room_state},
"account_data": {"events": []},
"ephemeral": {"events": []},
"unread_notifications": {
"notification_count": 0,
"highlight_count": 0
},
"summary": {}
}
}
}
})
@client.route("/_matrix/client/v3/rooms/<room>/send/<eventType>/<txnId>", methods=["POST", "PUT"])
@client.route("/_matrix/client/r0/rooms/<room>/send/<eventType>/<txnId>", methods=["POST", "PUT"])
async def send_message(room, eventType, txnId):
return jsonify({"event_id": globals.make_event_id()}), 200
@client.route("/_matrix/client/v3/user_directory/search", methods=["POST"])
async def user_directory():
return jsonify({
"limited": False,
"results": [{
"avatar_url": f"mxc://{config.server_name}/cat",
"display_name": "Vona",
"user_id": f"@vona:{config.server_name}"
}]
})
@client.route("/_matrix/client/v3/devices")
@client.route("/_matrix/client/r0/devices")
async def devices():
return jsonify({
"devices": [{
"device_id": "VVOONNAA",
"display_name": "Vona",
"last_seen_ip": "127.0.0.1",
"last_seen_ts": config.the_funny_number
}]
})
@client.route("/_matrix/client/v3/devices/<device>", methods=["GET", "PUT", "DELETE"])
@client.route("/_matrix/client/r0/devices/<device>", methods=["GET", "PUT", "DELETE"])
async def get_device(device):
if request.method == "GET":
return jsonify({
"device_id": device,
"display_name": "Vona",
"last_seen_ip": "127.0.0.1",
"last_seen_ts": config.the_funny_number
})
return jsonify({})
@client.route("/_matrix/client/v3/refresh", methods=["POST"])
async def refresh():
return jsonify({
"access_token": "vona",
"expires_in_ms": config.the_funny_number * 1000,
"refresh_token": "vona"
})
@client.route("/_matrix/client/unstable/im.nheko.summary/rooms/<roomId>/summary")
@client.route("/_matrix/client/unstable/im.nheko.summary/summary/<roomId>")
@client.route("/_matrix/client/v1/room_summary/<roomId>")
async def room_summary(roomId):
return jsonify({
"room_id": globals.make_event_id().replace("$", "!"),
"avatar_url": f"mxc://{config.server_name}/cat",
"guest_can_join": False,
"name": "Vona",
"num_joined_members": config.the_funny_number,
"topic": None,
"world_readable": False,
"join_rule": "public",
"room_type": "m.room",
"membership": "join",
"room_version": 2
})
@client.route("/_matrix/client/v3/directory/room/<room>", methods=["GET", "PUT", "DELETE"])
@client.route("/_matrix/client/r0/directory/room/<room>")
async def room_query(room):
if request.method == "GET":
return jsonify({
"room_id": globals.make_event_id().replace("$", "!"),
"servers": [config.server_name]
})
return jsonify({})
@client.route("/_matrix/client/v3/rooms/<room>/aliases")
async def room_aliases(room):
return jsonify({
"aliases": [
f"#vona:{config.server_name}"
]
})
@client.route("/_matrix/client/v3/directory/list/room/<room>", methods=["GET", "PUT"])
@client.route("/_matrix/client/r0/directory/list/room/<room>", methods=["GET", "PUT"])
async def room_visibility(room):
return jsonify({"visibility": "public"})
@client.route("/_matrix/client/v3/search", methods=["POST"])
async def search():
room = globals.make_event_id().replace("$", "!")
event = globals.make_event_id()
return jsonify({
"search_categories": {
"room_events": {
"count": 1,
"groups": {
"room_id": {
room: {
"next_batch": "vona",
"order": 1,
"results": [event]
}
}
},
"highlights": [],
"next_batch": "vona",
"results": [{
"rank": config.the_funny_number,
"result": {
"content": {
"msgtype": "m.text",
"body": "Number 15: Burger King Foot Lettuce.\nThe last thing you'd want in your Burger King burger is someones foot fungus, but as it turns out, that might be what you get. A 4channer uploaded a photo, anonymously to the site showcasing his feet in a plastic bin of lettuce with the statement \"This is the lettuce you eat at Burger King.\". Admittedly, he had shoes on, but thats even worse. The post went live at 11:38 PM on July 16 and a mere 20 minutes later the Burger King in question was alerted to the rogue employee. At least, I hope hes rogue. How did it happen? Well, the BK employee hadn't removed the EXIF data from the uploaded photo, which suggested that the culprit was somewhere in Mayfield Heights, Ohio. This was at 11:47. 3 minutes later, at 11:50, the Burger King branch was posted with wishes of happy unemployment. 5 minutes later, the news station was contacted by another 4channer, and 3 minutes later at 11:58 a link was posted: BK's tell us about us online forum. The foot photo, otherwise known as Exhibit A, was attached. Cleveland Seen Magazine contacted the BK in question and the next day when questioned, the breakfast shift manager said \"Oh, I know who that is, hes getting fired\". Mystery solved, by 4chan. Now we can go back to eating our fast food in peace.",
"format": "org.matrix.custom.html",
"formatted_body": "Number 15: Burger King Foot Lettuce.<br />The last thing you'd want in your Burger King burger is someones foot fungus, but as it turns out, that might be what you get. A 4channer uploaded a photo, anonymously to the site showcasing his feet in a plastic bin of lettuce with the statement &quot;This is the lettuce you eat at Burger King.&quot;. Admittedly, he had shoes on, but thats even worse. The post went live at 11:38 PM on July 16 and a mere 20 minutes later the Burger King in question was alerted to the rogue employee. At least, I hope hes rogue. How did it happen? Well, the BK employee hadn't removed the EXIF data from the uploaded photo, which suggested that the culprit was somewhere in Mayfield Heights, Ohio. This was at 11:47. 3 minutes later, at 11:50, the Burger King branch was posted with wishes of happy unemployment. 5 minutes later, the news station was contacted by another 4channer, and 3 minutes later at 11:58 a link was posted: BK's tell us about us online forum. The foot photo, otherwise known as Exhibit A, was attached. Cleveland Seen Magazine contacted the BK in question and the next day when questioned, the breakfast shift manager said &quot;Oh, I know who that is, hes getting fired&quot;. Mystery solved, by 4chan. Now we can go back to eating our fast food in peace."
},
"event_id": event,
"origin_server_ts": config.the_funny_number,
"room_id": room,
"sender": f"@vona:{config.server_name}",
"type": "m.room.message"
}
}]
}
}
})
@client.route("/_matrix/media/v1/thumbnail/<server>/<file>")
@client.route("/_matrix/client/v1/media/thumbnail/<s>/<f>")
@client.route("/_matrix/media/r0/thumbnail/<server>/<file>")
@client.route("/_matrix/media/v3/thumbnail/<server>/<media>")
@client.route("/_matrix/media/v3/download/<server>/<media>/<file>")
@client.route("/_matrix/client/v1/media/download/<s>/<f>")
@client.route("/_matrix/media/v3/download/<server>/<media>")
@client.route("/_matrix/media/r0/download/<server>/<media>")
async def media(**kwargs):
return send_file(config.cat)
@client.route("/_matrix/client/v3/register/available")
@client.route("/_matrix/client/r0/register/available")
async def username_available():
return jsonify({"available": True})
@client.route("/_matrix/media/v3/preview_url")
async def url_preview():
return jsonify({
"matrix:image:size": 102400,
"og:description": "look at this cool cat",
"og:image": f"mxc://{config.server_name}/ascERGshawAWawugaAcauga",
"og:image:height": 48,
"og:image:type": "image/jpg",
"og:image:width": 48,
"og:title": "cool cat"
})
@client.route("/_matrix/client/v1/media/preview_url")
async def media_preview():
response = send_file(config.cat)
response.headers["Content-Disposition"] = f'inline; filename="cat.jpg"'
response.headers["Content-Type"] = "image/jpg"
return response
@client.route("/_matrix/media/v3/upload", methods=["POST"])
@client.route("/_matrix/media/r0/upload", methods=["POST"])
@client.route("/_matrix/media/v1/create", methods=["POST"])
async def upload_media():
return jsonify({"content_uri": f"mxc://{config.server_name}/cat"})
@client.route("/_matrix/media/v3/config")
async def media_config():
return jsonify({"m.upload.size": config.the_funny_number * 69420})
@client.route("/_matrix/client/v3/profile/<userId>/<key>", methods=["GET", "PUT", "DELETE"])
@client.route("/_matrix/client/r0/profile/<userId>/<key>", methods=["GET", "PUT", "DELETE"])
async def profile_keys(userId, key):
if request.method == "GET":
if key == "avatar_url":
return jsonify({"avatar_url": f"mxc://{config.server_name}/cat"})
elif key == "displayname":
return jsonify({"displayname": "Vona"})
return jsonify({
"errcode": "M_NOT_FOUND",
"error": "The requested profile key does not exist."
})
return jsonify({})
@client.route("/_matrix/client/v3/profile/<userId>")
@client.route("/_matrix/client/r0/profile/<userId>")
async def user_profile(userId):
return jsonify({
"avatar_url": f"mxc://{config.server_name}/cat",
"displayname": "Vona"
})
@client.route("/_matrix/client/v3/rooms/<roomId>/messages")
@client.route("/_matrix/client/r0/rooms/<roomId>/messages")
async def room_messages(roomId):
return jsonify({
"chunk": [{
"content": {
"msgtype": "m.text",
"body": "Number 15: Burger King Foot Lettuce.\nThe last thing you'd want in your Burger King burger is someones foot fungus, but as it turns out, that might be what you get. A 4channer uploaded a photo, anonymously to the site showcasing his feet in a plastic bin of lettuce with the statement \"This is the lettuce you eat at Burger King.\". Admittedly, he had shoes on, but thats even worse. The post went live at 11:38 PM on July 16 and a mere 20 minutes later the Burger King in question was alerted to the rogue employee. At least, I hope hes rogue. How did it happen? Well, the BK employee hadn't removed the EXIF data from the uploaded photo, which suggested that the culprit was somewhere in Mayfield Heights, Ohio. This was at 11:47. 3 minutes later, at 11:50, the Burger King branch was posted with wishes of happy unemployment. 5 minutes later, the news station was contacted by another 4channer, and 3 minutes later at 11:58 a link was posted: BK's tell us about us online forum. The foot photo, otherwise known as Exhibit A, was attached. Cleveland Seen Magazine contacted the BK in question and the next day when questioned, the breakfast shift manager said \"Oh, I know who that is, hes getting fired\". Mystery solved, by 4chan. Now we can go back to eating our fast food in peace.",
"format": "org.matrix.custom.html",
"formatted_body": "Number 15: Burger King Foot Lettuce.<br />The last thing you'd want in your Burger King burger is someones foot fungus, but as it turns out, that might be what you get. A 4channer uploaded a photo, anonymously to the site showcasing his feet in a plastic bin of lettuce with the statement &quot;This is the lettuce you eat at Burger King.&quot;. Admittedly, he had shoes on, but thats even worse. The post went live at 11:38 PM on July 16 and a mere 20 minutes later the Burger King in question was alerted to the rogue employee. At least, I hope hes rogue. How did it happen? Well, the BK employee hadn't removed the EXIF data from the uploaded photo, which suggested that the culprit was somewhere in Mayfield Heights, Ohio. This was at 11:47. 3 minutes later, at 11:50, the Burger King branch was posted with wishes of happy unemployment. 5 minutes later, the news station was contacted by another 4channer, and 3 minutes later at 11:58 a link was posted: BK's tell us about us online forum. The foot photo, otherwise known as Exhibit A, was attached. Cleveland Seen Magazine contacted the BK in question and the next day when questioned, the breakfast shift manager said &quot;Oh, I know who that is, hes getting fired&quot;. Mystery solved, by 4chan. Now we can go back to eating our fast food in peace."
},
"event_id": globals.make_event_id(),
"origin_server_ts": config.the_funny_number,
"room_id": roomId,
"sender": f"@vona:{config.server_name}",
"type": "m.room.message"
}],
"end": f"{os.urandom(16).hex()}",
"start": f"{os.urandom(16).hex()}"
})
@client.route("/_matrix/client/v3/keys/query", methods=["POST"])
@client.route("/_matrix/client/r0/keys/query", methods=["POST"])
async def query_keys():
user = request.get_json()["device_keys"]
return jsonify({
"device_keys": user,
"master_keys": user,
"self_signing_keys": user,
"user_signing_keys": user
})
@client.route("/_matrix/client/api/v1/createRoom", methods=["POST"])
@client.route("/_matrix/client/v3/createRoom", methods=["POST"])
@client.route("/_matrix/client/r0/createRoom", methods=["POST"])
async def create_room():
return jsonify({"room_id": globals.make_event_id().replace("$", "!")})
@client.route("/_matrix/client/unstable/uk.half-shot.msc2666/mutual_rooms")
@client.route("/_matrix/client/v1/user/mutual_rooms")
async def mutual_rooms():
return jsonify({
"joined": [
globals.make_event_id().replace("$", "!")
]
})
@client.route("/_matrix/client/r0/presence/<user>/status", methods=["GET", "PUT"])
async def presence(user):
if request.method == "PUT":
return jsonify({})
return jsonify({
"presence": "online"
})
@client.route("/_matrix/client/r0/publicRooms", methods=["GET", "POST"])
async def room_directory():
return jsonify({
"chunk": [],
"total_room_count_estimate": 0
})