summary refs log tree commit diff
path: root/main.py
blob: 9446b719b4f7fec7501ce5ee96104d081c5eea64 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import asyncio
import json
import logging
from websockets.asyncio.server import serve
from websockets.asyncio.server import broadcast
import re
from passlib.hash import scrypt
import db
import uuid
import secrets

addr = "localhost"
port = 3636

logging.basicConfig(level=logging.INFO)

error_contexts = {
    "malformedJson": "The JSON data sent to the server could not be parsed.",
    "lengthInvalid": "A value in the JSON data is longer or shorter than expected.",
    "invalidUsername": "Username is invalid. It may contain characters that are not permitted in usernames.",
    "invalidInvite": "The invite code you are trying to use is invalid or has expired.",
    "usernameTaken": "This username has been taken.",
    "notExists": "The requested value does not exist."
}

ulist = {}
user_clients = {}
clients = []

invite_codes = ["STANLEYYELNATSAB"]
locked = False

class util:
    def error(code, listener):
        if code in error_contexts:
            context = error_contexts[code]
        else:
            context = ""
        return json.dumps({
            "error": True,
            "code": code,
            "form": "helium-util",
            "context": context,
            "listener": listener
        })
    
    def field_check(expects, gets):
        for i in expects:
            if i not in gets:
                return "malformedJson"
            if type(gets[i]) in [str, dict, list]:
                if len(gets[i]) not in expects[i]:
                    return "lengthInvalid"
        return True

async def handler(websocket):
    clients.append(websocket)
    async for message in websocket:
        try:
            r = json.loads(message)
        except:
            await websocket.send(util.error("malformedJson", None))
            continue
        if "listener" not in r:
            r["listener"] = None
        listener = r["listener"]
        if "command" not in r:
            await websocket.send(util.error("malformedJson", listener))
            continue
        if r["command"] == "register":
            fc = util.field_check({"username": range(1,21), "password": range(8,256), "invite_code": range(16,17)}, r)
            if fc != True:
                await websocket.send(util.error(fc, listener))
                continue
            r["username"] = r["username"].lower()
            r["invite_code"] = r["invite_code"].upper()
            if not re.fullmatch("[a-z0-9-_]{1,20}", r["username"]):
                await websocket.send(util.error("invalidUsername", listener))
                continue
            if r["invite_code"] not in invite_codes:
                await websocket.send(util.error("invalidInvite", listener))
                continue
            if db.acc.get(r["username"]) != "notExists":
                await websocket.send(util.error("usernameTaken", listener))
                continue
            data = {
                "_id": str(uuid.uuid4()),
                "username": r["username"],
                "display_name": r["username"],
                "avatar": None,
                "bot": False,
                "verified": False,
                "banned_until": 0,
                "profile": {
                    "bio": "",
                    "lastfm": "",
                    "banner": None,
                    "links": {}
                },
                "secure": {
                    "password": scrypt.hash(r["password"]),
                    "token": secrets.token_urlsafe(64),
                    "ban_reason": "",
                    "invite_code": r["invite_code"],
                    "support_code": secrets.token_hex(16)
                }
            }
            result = db.acc.add(data)
            if result != True:
                await websocket.send(util.error(result, listener))
                continue
            invite_codes.remove(r["invite_code"])
            await websocket.send(json.dumps({"error": False, "token": data["secure"]["token"], "listener": listener}))
        elif r["command"] == "login_pswd":
            fc = util.field_check({"username": range(1,21), "password": range(8,256)}, r)
            if fc != True:
                await websocket.send(util.error(fc, listener))
                continue
            r["username"] = r["username"].lower()

        else:
            await websocket.send(util.error("malformedJson", listener))
    if websocket in clients:
        clients.append(websocket)

async def main():
    async with serve(handler, addr, port) as server:
        await server.serve_forever()

asyncio.run(main())