aboutsummaryrefslogtreecommitdiff
path: root/api
diff options
context:
space:
mode:
authorlonkaars <l.leblansch@gmail.com>2021-04-16 16:57:26 +0200
committerlonkaars <l.leblansch@gmail.com>2021-04-16 16:57:26 +0200
commit07c2b124e4348b15f1e5ec18c6cdfd77248c6bc8 (patch)
treee4a29123d3ebedc1d25500390c904c66b3b02489 /api
parentaa2c999702dadba2afbcf2be9f597f890aafcc87 (diff)
spaces > tabs in python :(
Diffstat (limited to 'api')
-rw-r--r--api/auth/login.py80
-rw-r--r--api/auth/login_token.py20
-rw-r--r--api/auth/signup.py126
-rw-r--r--api/auth/token.py66
-rw-r--r--api/dynamic_import.py28
-rw-r--r--api/events.py2
-rw-r--r--api/game/accept.py12
-rw-r--r--api/game/cleanup.py30
-rw-r--r--api/game/info.py90
-rw-r--r--api/game/new.py76
-rw-r--r--api/game/random.py50
-rw-r--r--api/game/socket.py174
-rw-r--r--api/game/voerbak_connector.py132
-rw-r--r--api/hierarchy.py150
-rw-r--r--api/main.py2
-rw-r--r--api/passwords.py6
-rw-r--r--api/randid.py20
-rw-r--r--api/rating.py54
-rw-r--r--api/readme.md51
-rw-r--r--api/ruleset.py52
-rw-r--r--api/social/create_relation.py44
-rw-r--r--api/social/destroy_relation.py20
-rw-r--r--api/social/friend_accept.py16
-rw-r--r--api/social/request_list.py20
-rw-r--r--api/social/search.py40
-rw-r--r--api/status.py18
-rw-r--r--api/user/avatar.py24
-rw-r--r--api/user/games.py86
-rw-r--r--api/user/info.py82
-rw-r--r--api/user/password.py10
-rw-r--r--api/user/preferences.py48
-rw-r--r--api/user/status.py18
-rw-r--r--api/util.py4
-rw-r--r--api/valid.py22
34 files changed, 844 insertions, 829 deletions
diff --git a/api/auth/login.py b/api/auth/login.py
index 1d5a4b2..e3d5fde 100644
--- a/api/auth/login.py
+++ b/api/auth/login.py
@@ -8,46 +8,46 @@ login = Blueprint('login', __name__)
@login.route('/login', methods=['POST'])
def index():
- data = request.get_json()
-
- # get form data
- email = data.get("email") or ""
- password = data.get("password") or ""
-
- # return malformed request if email or password is missing
- if not email or not password:
- return "", 400
-
- # resolve user_id from username or email
- user_id = None
- user_id = user_id or cursor.execute(
- "select user_id from users where email = ?", [email]
- ).fetchone()
- user_id = user_id or cursor.execute(
- "select user_id from users where lower(username) = lower(?)", [email]
- ).fetchone()
- if user_id == None: return "", 401
-
- # check the password
- passwd = cursor.execute(
- "select password_hash from users where user_id = ?", [user_id[0]]
- ).fetchone()
- check = passwords.check_password(password, passwd[0])
- if not check: return "", 401
-
- # generate a new authentication token and add it to the users valid token list
- new_token = token.generate_token()
- token.add_token(user_id[0], token.hash_token(new_token))
-
- # make response with the set_cookie header
- res = make_response("", 200)
- res.set_cookie(
- "token",
- new_token["token"],
- expires=int(new_token["expirationDate"] / 1000)
- )
-
- return res
+ data = request.get_json()
+
+ # get form data
+ email = data.get("email") or ""
+ password = data.get("password") or ""
+
+ # return malformed request if email or password is missing
+ if not email or not password:
+ return "", 400
+
+ # resolve user_id from username or email
+ user_id = None
+ user_id = user_id or cursor.execute(
+ "select user_id from users where email = ?", [email]
+ ).fetchone()
+ user_id = user_id or cursor.execute(
+ "select user_id from users where lower(username) = lower(?)", [email]
+ ).fetchone()
+ if user_id == None: return "", 401
+
+ # check the password
+ passwd = cursor.execute(
+ "select password_hash from users where user_id = ?", [user_id[0]]
+ ).fetchone()
+ check = passwords.check_password(password, passwd[0])
+ if not check: return "", 401
+
+ # generate a new authentication token and add it to the users valid token list
+ new_token = token.generate_token()
+ token.add_token(user_id[0], token.hash_token(new_token))
+
+ # make response with the set_cookie header
+ res = make_response("", 200)
+ res.set_cookie(
+ "token",
+ new_token["token"],
+ expires=int(new_token["expirationDate"] / 1000)
+ )
+
+ return res
dynamic_route = ["/auth", login]
diff --git a/api/auth/login_token.py b/api/auth/login_token.py
index b5b1579..de770b3 100644
--- a/api/auth/login_token.py
+++ b/api/auth/login_token.py
@@ -5,12 +5,12 @@ from auth.token import validate_token, hash_token
# get user_id from authentication token
def token_login(token):
- hashed = hash_token({"token": token, "expirationDate": 0})
- user_id = cursor.execute(
- "select user_id from users where valid_tokens like ?",
- [f"%{hashed['token']}%"]
- ).fetchone()
- return None if not user_id else user_id[0]
+ hashed = hash_token({"token": token, "expirationDate": 0})
+ user_id = cursor.execute(
+ "select user_id from users where valid_tokens like ?",
+ [f"%{hashed['token']}%"]
+ ).fetchone()
+ return None if not user_id else user_id[0]
token = Blueprint('token', __name__)
@@ -19,12 +19,12 @@ token = Blueprint('token', __name__)
# this endpoint is currently unused, but verifies that a token is valid
@token.route('/token', methods=['POST'])
def index():
- data = request.get_json()
+ data = request.get_json()
- auth_token = data.get("token") or ""
- if not auth_token: return "", 400
+ auth_token = data.get("token") or ""
+ if not auth_token: return "", 400
- return "", 200 if token_login(auth_token) else 401
+ return "", 200 if token_login(auth_token) else 401
dynamic_route = ["/auth", token]
diff --git a/api/auth/signup.py b/api/auth/signup.py
index 98c07a7..a18796b 100644
--- a/api/auth/signup.py
+++ b/api/auth/signup.py
@@ -9,20 +9,20 @@ import re
# checks if the usename is between 3 and 35 charachters
def validate_username(username):
- return len(username) in range(3, 35 + 1)
+ return len(username) in range(3, 35 + 1)
# garbage email validation (see todo)
def validate_email(email):
- #TODO: use node_modules/email-validator/index.js
- return len(email) > 1 and \
- "@" in email
+ #TODO: use node_modules/email-validator/index.js
+ return len(email) > 1 and \
+ "@" in email
# checks if the password is safe (regex explanation in pages/register.tsx)
def validate_password(password):
- passwordRegex = r"^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9]).{8,}$" # r"" = raw string
- return re.match(passwordRegex, password)
+ passwordRegex = r"^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9]).{8,}$" # r"" = raw string
+ return re.match(passwordRegex, password)
signup = Blueprint('signup', __name__)
@@ -30,63 +30,63 @@ signup = Blueprint('signup', __name__)
@signup.route('/signup', methods=['POST'])
def index():
- # parse request data if the content-type header is set to application/json
- data = request.get_json()
-
- # force string if {}.get(...) returns None
- username = data.get("username") or ""
- email = data.get("email") or ""
- password = data.get("password") or ""
-
- # return 400 (malformed request) if any of the required data is missing
- if not username or \
- not email or \
- not password:
- return "", 400
-
- # return 403 (forbidden) if any of the required data is invalid
- if not validate_username(username) or \
- not validate_email(email) or \
- not validate_password(password):
- return {"error": "form_data_invalid"}, 403
-
- # check if username is taken
- if cursor.execute(
- "select username from users where lower(username) = lower(?)",
- [username]
- ).fetchone():
- return {"error": "username_taken"}, 403
-
- # check if email is taken
- if cursor.execute("select email from users where email = ?",
- [email]).fetchone():
- return {"error": "email_taken"}, 403
-
- # create new user_id, hash password and note timestamp
- user_id = new_uuid("users")
- password_hash = passwords.password_hash(password)
- registered = int(time.time() * 1000)
-
- # write new user to database and commit
- cursor.execute(
- "insert into users values (?, ?, ?, NULL, NULL, ?, ?, \"[]\", FALSE, \"user\", \"{}\", \"online\") ",
- (user_id, username, email, password_hash, registered)
- )
- connection.commit()
-
- # create a new token for the user to use for authentication
- new_token = token.generate_token()
- token.add_token(user_id, token.hash_token(new_token))
-
- # create a flask response object to add the set-cookie header to
- res = make_response("", 200)
- res.set_cookie(
- "token",
- new_token["token"],
- expires=int(new_token["expirationDate"] / 1000)
- )
-
- return res
+ # parse request data if the content-type header is set to application/json
+ data = request.get_json()
+
+ # force string if {}.get(...) returns None
+ username = data.get("username") or ""
+ email = data.get("email") or ""
+ password = data.get("password") or ""
+
+ # return 400 (malformed request) if any of the required data is missing
+ if not username or \
+ not email or \
+ not password:
+ return "", 400
+
+ # return 403 (forbidden) if any of the required data is invalid
+ if not validate_username(username) or \
+ not validate_email(email) or \
+ not validate_password(password):
+ return {"error": "form_data_invalid"}, 403
+
+ # check if username is taken
+ if cursor.execute(
+ "select username from users where lower(username) = lower(?)",
+ [username]
+ ).fetchone():
+ return {"error": "username_taken"}, 403
+
+ # check if email is taken
+ if cursor.execute("select email from users where email = ?",
+ [email]).fetchone():
+ return {"error": "email_taken"}, 403
+
+ # create new user_id, hash password and note timestamp
+ user_id = new_uuid("users")
+ password_hash = passwords.password_hash(password)
+ registered = int(time.time() * 1000)
+
+ # write new user to database and commit
+ cursor.execute(
+ "insert into users values (?, ?, ?, NULL, NULL, ?, ?, \"[]\", FALSE, \"user\", \"{}\", \"online\") ",
+ (user_id, username, email, password_hash, registered)
+ )
+ connection.commit()
+
+ # create a new token for the user to use for authentication
+ new_token = token.generate_token()
+ token.add_token(user_id, token.hash_token(new_token))
+
+ # create a flask response object to add the set-cookie header to
+ res = make_response("", 200)
+ res.set_cookie(
+ "token",
+ new_token["token"],
+ expires=int(new_token["expirationDate"] / 1000)
+ )
+
+ return res
dynamic_route = ["/auth", signup]
diff --git a/api/auth/token.py b/api/auth/token.py
index e94b014..d439924 100644
--- a/api/auth/token.py
+++ b/api/auth/token.py
@@ -7,54 +7,54 @@ import time
# get valid token hashes for a given user_id
def valid_tokens(user_id):
- tokens = json.loads(
- cursor.execute(
- "select valid_tokens from users where user_id = ?", [user_id]
- ).fetchone()[0]
- )
- # return only tokens that aren't expired
- return [
- token for token in tokens
- if token["expirationDate"] > int(time.time() * 1000)
- ]
+ tokens = json.loads(
+ cursor.execute(
+ "select valid_tokens from users where user_id = ?", [user_id]
+ ).fetchone()[0]
+ )
+ # return only tokens that aren't expired
+ return [
+ token for token in tokens
+ if token["expirationDate"] > int(time.time() * 1000)
+ ]
def validate_token(user_id, token):
- tokens = valid_tokens(user_id)
- return hashlib.sha256(str(token).encode()).hexdigest() in [
- t["token"] for t in tokens
- if t["expirationDate"] > int(time.time() * 1000)
- ]
+ tokens = valid_tokens(user_id)
+ return hashlib.sha256(str(token).encode()).hexdigest() in [
+ t["token"] for t in tokens
+ if t["expirationDate"] > int(time.time() * 1000)
+ ]
def modify_tokens(user_id, formatted_token, remove):
- temp_tokens = valid_tokens(user_id)
- temp_tokens.remove(formatted_token
- ) if remove else temp_tokens.append(formatted_token)
- cursor.execute(
- "update users set valid_tokens = ? where user_id = ?",
- [json.dumps(temp_tokens), user_id]
- )
- connection.commit()
+ temp_tokens = valid_tokens(user_id)
+ temp_tokens.remove(formatted_token
+ ) if remove else temp_tokens.append(formatted_token)
+ cursor.execute(
+ "update users set valid_tokens = ? where user_id = ?",
+ [json.dumps(temp_tokens), user_id]
+ )
+ connection.commit()
def add_token(user_id, formatted_token):
- modify_tokens(user_id, formatted_token, False)
+ modify_tokens(user_id, formatted_token, False)
def revoke_token(user_id, formatted_token):
- modify_tokens(user_id, formatted_token, True)
+ modify_tokens(user_id, formatted_token, True)
def hash_token(token):
- return {
- "token": hashlib.sha256(str(token["token"]).encode()).hexdigest(),
- "expirationDate": token["expirationDate"]
- }
+ return {
+ "token": hashlib.sha256(str(token["token"]).encode()).hexdigest(),
+ "expirationDate": token["expirationDate"]
+ }
def generate_token():
- return {
- "token": secrets.token_hex(128),
- "expirationDate": int(time.time() * 1000) + (24 * 60 * 60 * 1000)
- }
+ return {
+ "token": secrets.token_hex(128),
+ "expirationDate": int(time.time() * 1000) + (24 * 60 * 60 * 1000)
+ }
diff --git a/api/dynamic_import.py b/api/dynamic_import.py
index ecf9ea1..542af92 100644
--- a/api/dynamic_import.py
+++ b/api/dynamic_import.py
@@ -9,24 +9,24 @@ import glob
files = glob.glob(os.path.dirname(__file__) + "/**/*.py", recursive=True)
files.remove(__file__)
files = [
- str(filename).replace(os.path.dirname(__file__) + "/",
- '').replace("/", ".").replace(".py", '')
- for filename in files
+ str(filename).replace(os.path.dirname(__file__) + "/",
+ '').replace("/", ".").replace(".py", '')
+ for filename in files
]
def route(dynamic_route):
- app.register_blueprint(dynamic_route[1], url_prefix=dynamic_route[0])
- path = (dynamic_route[0] + "/" + dynamic_route[1].name).replace('//', '/')
- log.info(f"dynamically routing {path}")
+ app.register_blueprint(dynamic_route[1], url_prefix=dynamic_route[0])
+ path = (dynamic_route[0] + "/" + dynamic_route[1].name).replace('//', '/')
+ log.info(f"dynamically routing {path}")
for file in files:
- mod = importlib.import_module(file)
- # check if module has `dynamic_route` defined (single route)
- if hasattr(mod, "dynamic_route"):
- route(mod.dynamic_route)
- # check if module has `dynamic_routes` defined (multiple routes as list)
- elif hasattr(mod, "dynamic_routes"):
- for dynamic_route in mod.dynamic_routes:
- route(dynamic_route)
+ mod = importlib.import_module(file)
+ # check if module has `dynamic_route` defined (single route)
+ if hasattr(mod, "dynamic_route"):
+ route(mod.dynamic_route)
+ # check if module has `dynamic_routes` defined (multiple routes as list)
+ elif hasattr(mod, "dynamic_routes"):
+ for dynamic_route in mod.dynamic_routes:
+ route(dynamic_route)
diff --git a/api/events.py b/api/events.py
index 695edb1..3097471 100644
--- a/api/events.py
+++ b/api/events.py
@@ -7,4 +7,4 @@ from hierarchy import io_auth_required
@io.on("connect")
@io_auth_required("none")
def connect(data, user_id):
- join_room("user-" + user_id)
+ join_room("user-" + user_id)
diff --git a/api/game/accept.py b/api/game/accept.py
index 3510ffd..e0681c8 100644
--- a/api/game/accept.py
+++ b/api/game/accept.py
@@ -16,14 +16,14 @@ join_game = Blueprint('game_accept', __name__)
@join_game.route('/accept', methods=['POST'])
@game_id_with_viewer
def index(game_id, user_id):
- if not user_id: return "", 400
- if cursor.execute("select status from games where game_id = ?",
- [game_id]).fetchone()[0] != "wait_for_opponent":
- return "", 403
+ if not user_id: return "", 400
+ if cursor.execute("select status from games where game_id = ?",
+ [game_id]).fetchone()[0] != "wait_for_opponent":
+ return "", 403
- start_game(game_id, user_id)
+ start_game(game_id, user_id)
- return {"id": game_id, "player_1": False, "game_started": True}, 200
+ return {"id": game_id, "player_1": False, "game_started": True}, 200
dynamic_route = ["/game", join_game]
diff --git a/api/game/cleanup.py b/api/game/cleanup.py
index cc0aab8..8f7266d 100644
--- a/api/game/cleanup.py
+++ b/api/game/cleanup.py
@@ -5,26 +5,26 @@ import time
# cleanup function that's ran every five minutes
def cleanup():
- now = int(time.time() * 1000)
- old_games = cursor.execute(
- "select game_id from games where (status = \"wait_for_opponent\" or status = \"in_progress\") and last_activity < ?",
- [now - 5 * 60 * 1e3]
- ).fetchall()
- for game_id in old_games:
- cursor.execute("delete from games where game_id = ?", [game_id[0]])
- connection.commit()
+ now = int(time.time() * 1000)
+ old_games = cursor.execute(
+ "select game_id from games where (status = \"wait_for_opponent\" or status = \"in_progress\") and last_activity < ?",
+ [now - 5 * 60 * 1e3]
+ ).fetchall()
+ for game_id in old_games:
+ cursor.execute("delete from games where game_id = ?", [game_id[0]])
+ connection.commit()
def set_interval(
- func, sec
+ func, sec
): # https://stackoverflow.com/questions/2697039/python-equivalent-of-setinterval
- def func_wrapper():
- set_interval(func, sec)
- func()
+ def func_wrapper():
+ set_interval(func, sec)
+ func()
- t = threading.Timer(sec, func_wrapper)
- t.start()
- return t
+ t = threading.Timer(sec, func_wrapper)
+ t.start()
+ return t
# run every five minutes
diff --git a/api/game/info.py b/api/game/info.py
index b150dbc..fa23616 100644
--- a/api/game/info.py
+++ b/api/game/info.py
@@ -9,54 +9,54 @@ import valid
def format_game(game_id, user_id=None):
- game = cursor.execute(
- "select " + ", ".join(
- [
- "game_id", # 0
- "parent_game", # 1
- "moves", # 2
- "player_1_id", # 3
- "player_2_id", # 4
- "outcome", # 5
- "created", # 6
- "started", # 7
- "duration", # 8
- "rating_delta_player_1", # 9
- "rating_delta_player_2", # 10
- "ruleset", # 11
- "status", # 12
- "private", # 13
- ]
- ) + " from games where game_id = ?",
- [game_id]
- ).fetchone()
+ game = cursor.execute(
+ "select " + ", ".join(
+ [
+ "game_id", # 0
+ "parent_game", # 1
+ "moves", # 2
+ "player_1_id", # 3
+ "player_2_id", # 4
+ "outcome", # 5
+ "created", # 6
+ "started", # 7
+ "duration", # 8
+ "rating_delta_player_1", # 9
+ "rating_delta_player_2", # 10
+ "ruleset", # 11
+ "status", # 12
+ "private", # 13
+ ]
+ ) + " from games where game_id = ?",
+ [game_id]
+ ).fetchone()
- is_player_1 = game[4] != user_id
+ is_player_1 = game[4] != user_id
- # get opponent from perspective of `user_id`
- #TODO: return .players as array of player_1 and player_2 but format_user()'d
- opponent = game[4] if is_player_1 else game[3]
+ # get opponent from perspective of `user_id`
+ #TODO: return .players as array of player_1 and player_2 but format_user()'d
+ opponent = game[4] if is_player_1 else game[3]
- # parse moves into list and return empty list if moves string is empty
- moves = [] if len(game[2]) == 0 else [
- int(move) for move in str(game[2] + "0").split(",")
- ]
+ # parse moves into list and return empty list if moves string is empty
+ moves = [] if len(game[2]) == 0 else [
+ int(move) for move in str(game[2] + "0").split(",")
+ ]
- return {
- "id": game[0],
- "parent": game[1],
- "moves": moves,
- "opponent": None if not opponent else format_user(opponent),
- "outcome": None if not game[5] else outcome(game[5], is_player_1),
- "created": game[6],
- "started": game[7],
- "duration": game[8],
- "rating": game[9] if is_player_1 else game[10],
- "rating_opponent": game[10] if is_player_1 else game[9],
- "ruleset": resolve_ruleset(game[11]),
- "status": game[12],
- "private": bool(game[13]),
- }
+ return {
+ "id": game[0],
+ "parent": game[1],
+ "moves": moves,
+ "opponent": None if not opponent else format_user(opponent),
+ "outcome": None if not game[5] else outcome(game[5], is_player_1),
+ "created": game[6],
+ "started": game[7],
+ "duration": game[8],
+ "rating": game[9] if is_player_1 else game[10],
+ "rating_opponent": game[10] if is_player_1 else game[9],
+ "ruleset": resolve_ruleset(game[11]),
+ "status": game[12],
+ "private": bool(game[13]),
+ }
game_info = Blueprint('game_info', __name__)
@@ -65,7 +65,7 @@ game_info = Blueprint('game_info', __name__)
@game_info.route('/info', methods=['POST'])
@game_id_with_viewer
def index(game_id, viewer):
- return format_game(game_id, viewer), 200
+ return format_game(game_id, viewer), 200
dynamic_route = ["/game", game_info]
diff --git a/api/game/new.py b/api/game/new.py
index 8c936de..edc7f52 100644
--- a/api/game/new.py
+++ b/api/game/new.py
@@ -8,46 +8,46 @@ from hierarchy import auth_required
def create_game(user_1_id, private=False, user_2_id=None):
- timestamp = int(time.time() * 1000)
+ timestamp = int(time.time() * 1000)
- game_id = new_uuid("games")
+ game_id = new_uuid("games")
- cursor.execute(
- "insert into games values (?, NULL, \"\", ?, ?, NULL, ?, NULL, ?, NULL, NULL, NULL, \"wait_for_opponent\", \"default\", ?, FALSE) ",
- (game_id, user_1_id, user_2_id, timestamp, timestamp, private)
- )
- connection.commit()
+ cursor.execute(
+ "insert into games values (?, NULL, \"\", ?, ?, NULL, ?, NULL, ?, NULL, NULL, NULL, \"wait_for_opponent\", \"default\", ?, FALSE) ",
+ (game_id, user_1_id, user_2_id, timestamp, timestamp, private)
+ )
+ connection.commit()
- return game_id
+ return game_id
def start_game(game_id, user_2_id):
- timestamp = int(time.time() * 1000)
-
- db_game = cursor.execute(
- "select player_2_id, status, private from games where game_id = ?",
- [game_id]
- ).fetchone()
- if db_game[1] != "wait_for_opponent": return False
-
- if db_game[0] == None:
- cursor.execute(
- "update games set player_2_id = ? where game_id = ?",
- (user_2_id, game_id)
- )
- cursor.execute(
- "update games set status = \"in_progress\", started = ?, last_activity = ? where game_id = ?",
- (timestamp, timestamp, game_id)
- )
- connection.commit()
-
- players = cursor.execute(
- "select player_1_id, player_2_id from games where game_id = ?",
- [game_id]
- ).fetchone()
- games[game_id] = game(game_id, io, players[0], players[1])
-
- io.emit("gameStart", room=games[game_id].room)
+ timestamp = int(time.time() * 1000)
+
+ db_game = cursor.execute(
+ "select player_2_id, status, private from games where game_id = ?",
+ [game_id]
+ ).fetchone()
+ if db_game[1] != "wait_for_opponent": return False
+
+ if db_game[0] == None:
+ cursor.execute(
+ "update games set player_2_id = ? where game_id = ?",
+ (user_2_id, game_id)
+ )
+ cursor.execute(
+ "update games set status = \"in_progress\", started = ?, last_activity = ? where game_id = ?",
+ (timestamp, timestamp, game_id)
+ )
+ connection.commit()
+
+ players = cursor.execute(
+ "select player_1_id, player_2_id from games where game_id = ?",
+ [game_id]
+ ).fetchone()
+ games[game_id] = game(game_id, io, players[0], players[1])
+
+ io.emit("gameStart", room=games[game_id].room)
new_game = Blueprint('new_game', __name__)
@@ -56,10 +56,10 @@ new_game = Blueprint('new_game', __name__)
@new_game.route('/new', methods=["GET", "POST"])
@auth_required("user")
def index(user_id):
- # create a new private game (join by link)
- #TODO: friend invites + notifications
- game_id = create_game(user_id, True)
- return {"id": game_id}, 200
+ # create a new private game (join by link)
+ #TODO: friend invites + notifications
+ game_id = create_game(user_id, True)
+ return {"id": game_id}, 200
dynamic_route = ["/game", new_game]
diff --git a/api/game/random.py b/api/game/random.py
index 559c9e5..aa5383c 100644
--- a/api/game/random.py
+++ b/api/game/random.py
@@ -15,31 +15,31 @@ random_game = Blueprint('random', __name__)
@random_game.route('/random')
@auth_required("user")
def index(user_id):
- # get public_games (random opponent queue)
- public_games = cursor.execute(
- "select game_id from games where private = FALSE and status = \"wait_for_opponent\""
- ).fetchall()
-
- game_started = False
-
- # create a new public game if the queue is empty
- if len(public_games) == 0:
- game_id = create_game(user_id)
- player_1 = True
- # otherwise join a random public game
- else:
- game_id = random.choice(public_games)[0]
-
- start_game(game_id, user_id)
-
- player_1 = False
- game_started = True
-
- return {
- "id": game_id,
- "player_1": player_1,
- "game_started": game_started
- }, 200
+ # get public_games (random opponent queue)
+ public_games = cursor.execute(
+ "select game_id from games where private = FALSE and status = \"wait_for_opponent\""
+ ).fetchall()
+
+ game_started = False
+
+ # create a new public game if the queue is empty
+ if len(public_games) == 0:
+ game_id = create_game(user_id)
+ player_1 = True
+ # otherwise join a random public game
+ else:
+ game_id = random.choice(public_games)[0]
+
+ start_game(game_id, user_id)
+
+ player_1 = False
+ game_started = True
+
+ return {
+ "id": game_id,
+ "player_1": player_1,
+ "game_started": game_started
+ }, 200
dynamic_route = ["/game", random_game]
diff --git a/api/game/socket.py b/api/game/socket.py
index ca28346..587ad7d 100644
--- a/api/game/socket.py
+++ b/api/game/socket.py
@@ -11,121 +11,121 @@ games = {}
def participants_only(func):
- '''
+ '''
listener should have two parameters:
listener(data: socket.io.data, user_id: str, game: game)
listener should only be executed if the request comes from one of
the game participants (player_1_id | player_2_id)
'''
- def wrapper(data, user_id):
- game_id = data["game_id"]
+ def wrapper(data, user_id):
+ game_id = data["game_id"]
- if not game_id or \
- not game_id in games:
- return
+ if not game_id or \
+ not game_id in games:
+ return
- game = games[game_id]
- if game.player_1_id != user_id and \
- game.player_2_id != user_id:
- return
+ game = games[game_id]
+ if game.player_1_id != user_id and \
+ game.player_2_id != user_id:
+ return
- return func(data, user_id, game)
+ return func(data, user_id, game)
- wrapper.__name__ = func.__name__
- return wrapper
+ wrapper.__name__ = func.__name__
+ return wrapper
class game:
- def __init__(self, game_id, io, player_1_id, player_2_id):
- self.game_id = game_id
- self.room = "game-" + game_id
- self.board = bord(7, 6)
- self.io = io
- self.player_1_id = player_1_id
- self.player_2_id = player_2_id
-
- # drop a disc in `column`
- def move(self, user_id, column):
- if len(self.board.win_positions) > 0: return
- if self.board.board_full: return
-
- move = self.player_1_id if self.board.player_1 else self.player_2_id
- if user_id != move: return
-
- self.board.drop_fisje(column)
-
- io.emit("fieldUpdate", {"field": self.board.board}, room=self.room)
-
- now = int(time.time() * 1000)
- cursor.execute(
- "update games set last_activity = ?, moves = moves || ? || ',' where game_id = ?",
- [now, column, self.game_id]
- )
- connection.commit()
-
- if len(self.board.win_positions) > 0 or self.board.board_full:
- outcome = "d"
- if not self.board.board_full:
- winner = self.board.board[int(self.board.win_positions[0][0])]
- outcome = "w" if winner == "2" else "l"
- io.emit(
- "finish", {
- "winPositions": self.board.win_positions,
- "boardFull": self.board.board_full
- },
- room=self.room
- )
- self.close("finished", outcome)
- return
-
- io.emit("turnUpdate", {"player1": self.board.player_1}, room=self.room)
-
- def resign(self):
- self.board.kill_voerbak()
- io.emit("resign", room=self.room)
- self.close("resign", "d")
-
- def close(self, new_status, outcome):
- cursor.execute(
- " ".join(
- [
- "update games set", "moves = moves || '0',",
- "duration = ?,", "status = ?,", "outcome = ?",
- "where game_id = ?"
- ]
- ), [
- int(time.time() * 1000) - cursor.execute(
- "select started from games where game_id = ?",
- [self.game_id]
- ).fetchone()[0], new_status, outcome, self.game_id
- ]
- )
- connection.commit()
-
- games.pop(self.game_id)
+ def __init__(self, game_id, io, player_1_id, player_2_id):
+ self.game_id = game_id
+ self.room = "game-" + game_id
+ self.board = bord(7, 6)
+ self.io = io
+ self.player_1_id = player_1_id
+ self.player_2_id = player_2_id
+
+ # drop a disc in `column`
+ def move(self, user_id, column):
+ if len(self.board.win_positions) > 0: return
+ if self.board.board_full: return
+
+ move = self.player_1_id if self.board.player_1 else self.player_2_id
+ if user_id != move: return
+
+ self.board.drop_fisje(column)
+
+ io.emit("fieldUpdate", {"field": self.board.board}, room=self.room)
+
+ now = int(time.time() * 1000)
+ cursor.execute(
+ "update games set last_activity = ?, moves = moves || ? || ',' where game_id = ?",
+ [now, column, self.game_id]
+ )
+ connection.commit()
+
+ if len(self.board.win_positions) > 0 or self.board.board_full:
+ outcome = "d"
+ if not self.board.board_full:
+ winner = self.board.board[int(self.board.win_positions[0][0])]
+ outcome = "w" if winner == "2" else "l"
+ io.emit(
+ "finish", {
+ "winPositions": self.board.win_positions,
+ "boardFull": self.board.board_full
+ },
+ room=self.room
+ )
+ self.close("finished", outcome)
+ return
+
+ io.emit("turnUpdate", {"player1": self.board.player_1}, room=self.room)
+
+ def resign(self):
+ self.board.kill_voerbak()
+ io.emit("resign", room=self.room)
+ self.close("resign", "d")
+
+ def close(self, new_status, outcome):
+ cursor.execute(
+ " ".join(
+ [
+ "update games set", "moves = moves || '0',",
+ "duration = ?,", "status = ?,", "outcome = ?",
+ "where game_id = ?"
+ ]
+ ), [
+ int(time.time() * 1000) - cursor.execute(
+ "select started from games where game_id = ?",
+ [self.game_id]
+ ).fetchone()[0], new_status, outcome, self.game_id
+ ]
+ )
+ connection.commit()
+
+ games.pop(self.game_id)
@io.on("newMove")
@io_auth_required("none")
@participants_only
def new_move(data, user_id, game):
- move = data.get("move")
- if not move: return
+ move = data.get("move")
+ if not move: return
- game.move(user_id, move)
+ game.move(user_id, move)
@io.on("resign")
@io_auth_required("none")
@participants_only
def resign(data, user_id, game):
- game.resign()
+ game.resign()
@io.on("registerGameListener")
def register_game_listener(data):
- game_id = data.get("game_id")
- if not game_id: return
+ game_id = data.get("game_id")
+ if not game_id: return
- join_room("game-" + game_id)
+ join_room("game-" + game_id)
diff --git a/api/game/voerbak_connector.py b/api/game/voerbak_connector.py
index 0b51bd5..54f5fa6 100644
--- a/api/game/voerbak_connector.py
+++ b/api/game/voerbak_connector.py
@@ -13,81 +13,81 @@ if os.name == "nt": VOERBAK_LOCATION += ".exe"
class bord:
- def __init__(self, w, h):
- self.width = w
- self.height = h
- self.player_1 = True
- self.board = "0" * (w * h)
- self.board_full = False
- self.win_positions = []
- self.process = subprocess.Popen(
- [VOERBAK_LOCATION, f"-w {w}", f"-h {h}"],
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=None
- )
- self.process.stdin.flush()
+ def __init__(self, w, h):
+ self.width = w
+ self.height = h
+ self.player_1 = True
+ self.board = "0" * (w * h)
+ self.board_full = False
+ self.win_positions = []
+ self.process = subprocess.Popen(
+ [VOERBAK_LOCATION, f"-w {w}", f"-h {h}"],
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=None
+ )
+ self.process.stdin.flush()
- # get output from voerbak without trailing newline character (this might break on windows because crlf)
- def get_output(self):
- return self.process.stdout.readline().decode()[:-1]
+ # get output from voerbak without trailing newline character (this might break on windows because crlf)
+ def get_output(self):
+ return self.process.stdout.readline().decode()[:-1]
- def kill_voerbak(self):
- self.process.stdin.write(bytearray("0", "utf-8"))
- self.process.stdin.flush()
+ def kill_voerbak(self):
+ self.process.stdin.write(bytearray("0", "utf-8"))
+ self.process.stdin.flush()
- # read messages from voerbak
- def update_board(self):
- buffer = self.get_output()
- while not buffer.isdigit():
- # win message
- if buffer.startswith("w:"):
- self.win_positions.append(buffer[2:].split("-"))
- log.info(f"won: {buffer[2:].split('-')}")
- self.kill_voerbak()
- # error message
- elif buffer.startswith("e:"):
- log.warning(buffer[2:])
- # turn update message
- elif buffer.startswith("m:"):
- substr = buffer[2:]
- self.player_1 = True if substr == "true" else False
- # draw game message
- elif buffer.startswith("d:"):
- self.board_full = True
- self.kill_voerbak()
- buffer = self.get_output()
- self.board = buffer
+ # read messages from voerbak
+ def update_board(self):
+ buffer = self.get_output()
+ while not buffer.isdigit():
+ # win message
+ if buffer.startswith("w:"):
+ self.win_positions.append(buffer[2:].split("-"))
+ log.info(f"won: {buffer[2:].split('-')}")
+ self.kill_voerbak()
+ # error message
+ elif buffer.startswith("e:"):
+ log.warning(buffer[2:])
+ # turn update message
+ elif buffer.startswith("m:"):
+ substr = buffer[2:]
+ self.player_1 = True if substr == "true" else False
+ # draw game message
+ elif buffer.startswith("d:"):
+ self.board_full = True
+ self.kill_voerbak()
+ buffer = self.get_output()
+ self.board = buffer
- # debug board print function
- def print(self):
- for y in range(self.height - 1, -1, -1):
- for x in range(self.width):
- state = self.board[x + y * self.width]
- char = [EMPTY, DISC_A, DISC_B]
- print(char[int(state)], end=" ")
- print("\n", end="")
+ # debug board print function
+ def print(self):
+ for y in range(self.height - 1, -1, -1):
+ for x in range(self.width):
+ state = self.board[x + y * self.width]
+ char = [EMPTY, DISC_A, DISC_B]
+ print(char[int(state)], end=" ")
+ print("\n", end="")
- def drop_fisje(self, column):
- self.process.stdin.write(bytearray(f"{column}\n", "utf-8"))
- self.process.stdin.flush()
- self.update_board()
+ def drop_fisje(self, column):
+ self.process.stdin.write(bytearray(f"{column}\n", "utf-8"))
+ self.process.stdin.flush()
+ self.update_board()
# debug game
def main():
- gert = bord(7, 6)
- while True:
- print(gert.player_1)
- if len(gert.win_positions) > 0:
- print(f"won: {gert.win_positions}")
- exit(0)
- gert.print()
- column = int(input("column?: ")) - 1
- if column not in range(gert.width):
- continue
- gert.drop_fisje(column + 1)
+ gert = bord(7, 6)
+ while True:
+ print(gert.player_1)
+ if len(gert.win_positions) > 0:
+ print(f"won: {gert.win_positions}")
+ exit(0)
+ gert.print()
+ column = int(input("column?: ")) - 1
+ if column not in range(gert.width):
+ continue
+ gert.drop_fisje(column + 1)
if __name__ == "__main__":
- main()
+ main()
diff --git a/api/hierarchy.py b/api/hierarchy.py
index c50aa81..87b9db9 100644
--- a/api/hierarchy.py
+++ b/api/hierarchy.py
@@ -8,7 +8,7 @@ ranks = ["none", "user", "moderator", "admin", "bot"]
def util_two_id(type="user"):
- '''
+ '''
type?: "user" | "game"
! only used internally !
func(token_id?: str, explicit_id?: str)
@@ -16,51 +16,51 @@ def util_two_id(type="user"):
This decorator doesn't check for hierarchy constraints, but does
make sure that token_id or explicit_id are valid user_id's
'''
- def decorator(func):
- def wrapper():
- token_id = None
- explicit_id = None
+ def decorator(func):
+ def wrapper():
+ token_id = None
+ explicit_id = None
- token = request.cookies.get("token") or ""
- if token: token_id = token_login(token)
+ token = request.cookies.get("token") or ""
+ if token: token_id = token_login(token)
- data = request.get_json()
- if data: explicit_id = data.get("id")
+ data = request.get_json()
+ if data: explicit_id = data.get("id")
- # if there's an explicit_id, validate it using `type`
- if explicit_id and \
- not valid.validate(explicit_id, type):
- explicit_id = None
+ # if there's an explicit_id, validate it using `type`
+ if explicit_id and \
+ not valid.validate(explicit_id, type):
+ explicit_id = None
- return func(token_id, explicit_id)
+ return func(token_id, explicit_id)
- wrapper.__name__ = func.__name__
- return wrapper
+ wrapper.__name__ = func.__name__
+ return wrapper
- return decorator
+ return decorator
def two_person(func):
- '''
+ '''
endpoint should have two parameters:
endpoint(user_1_id: str, user_2_id: str)
no authentication, just runs endpoint() if both token_id and
explicit_id are present from @util_two_id.
'''
- @util_two_id("user")
- def wrapper(token_id, explicit_id):
- if not all_def([token_id, explicit_id]):
- return "", 400
+ @util_two_id("user")
+ def wrapper(token_id, explicit_id):
+ if not all_def([token_id, explicit_id]):
+ return "", 400
- return func(token_id, explicit_id)
+ return func(token_id, explicit_id)
- wrapper.__name__ = func.__name__
- return wrapper
+ wrapper.__name__ = func.__name__
+ return wrapper
def one_person(func):
- '''
+ '''
endpoint should have two parameters:
endpoint(user_id: str, viewer?: str)
@@ -68,35 +68,35 @@ def one_person(func):
doesn't check for authentication
expects that func takes these arguments: (user_id, viewer?)
'''
- @util_two_id("user")
- def wrapper(token_id, explicit_id):
- if all_notdef([token_id, explicit_id]):
- return "", 400
+ @util_two_id("user")
+ def wrapper(token_id, explicit_id):
+ if all_notdef([token_id, explicit_id]):
+ return "", 400
- return func(explicit_id or token_id, token_id)
+ return func(explicit_id or token_id, token_id)
- wrapper.__name__ = func.__name__
- return wrapper
+ wrapper.__name__ = func.__name__
+ return wrapper
def game_id_with_viewer(func):
- '''
+ '''
endpoint should have two parameters:
endpoint(game_id: str, viewer?: str)
'''
- @util_two_id("game")
- def wrapper(token_id, game_id):
- if all_notdef([token_id, game_id]):
- return "", 400
+ @util_two_id("game")
+ def wrapper(token_id, game_id):
+ if all_notdef([token_id, game_id]):
+ return "", 400
- return func(game_id, token_id)
+ return func(game_id, token_id)
- wrapper.__name__ = func.__name__
- return wrapper
+ wrapper.__name__ = func.__name__
+ return wrapper
def auth_required(level):
- '''
+ '''
level = "none" | "user" | "moderator" | "admin" | "bot"
endpoint should have one parameter for the user_id of the request author:
endpoint(user_id: str) # `user_id` can only be `None` when `level == "none"`
@@ -105,33 +105,33 @@ def auth_required(level):
This decorator only runs endpoint() if token_id from
@util_two_id is not None and passes hierarchy constraints
'''
- def decorator(func):
- @util_two_id("user")
- def wrapper(token_id, explicit_id):
- if not token_id:
- if level == ranks[0]:
- return func(None)
- else:
- return "", 400
+ def decorator(func):
+ @util_two_id("user")
+ def wrapper(token_id, explicit_id):
+ if not token_id:
+ if level == ranks[0]:
+ return func(None)
+ else:
+ return "", 400
- user_rank_text = cursor.execute(
- "select type from users where user_id = ?", [token_id]
- ).fetchone()[0]
+ user_rank_text = cursor.execute(
+ "select type from users where user_id = ?", [token_id]
+ ).fetchone()[0]
- required_rank = ranks.index(level)
- user_rank = ranks.index(user_rank_text)
- if required_rank > user_rank: return "", 403
+ required_rank = ranks.index(level)
+ user_rank = ranks.index(user_rank_text)
+ if required_rank > user_rank: return "", 403
- return func(token_id)
+ return func(token_id)
- wrapper.__name__ = func.__name__
- return wrapper
+ wrapper.__name__ = func.__name__
+ return wrapper
- return decorator
+ return decorator
def io_auth_required(level):
- '''
+ '''
level = "none" | "user" | "moderator" | "admin" | "bot"
endpoint should have two parameters:
endpoint(data: socket.io.data, user_id: str) # `user_id` can only be `None` when `level == "none"`
@@ -139,22 +139,22 @@ def io_auth_required(level):
uses the @auth_required decorator, but is only for use with
@io.on decorators
'''
- def decorator(func):
- # data is the original @io.on data
- def wrapper(data={}):
+ def decorator(func):
+ # data is the original @io.on data
+ def wrapper(data={}):
- token = request.cookies.get("token") or ""
- user_id = token_login(token)
+ token = request.cookies.get("token") or ""
+ user_id = token_login(token)
- if not user_id:
- if level == ranks[0]:
- return func(data, None)
- else:
- return
+ if not user_id:
+ if level == ranks[0]:
+ return func(data, None)
+ else:
+ return
- return func(data, user_id)
+ return func(data, user_id)
- wrapper.__name__ = func.__name__
- return wrapper
+ wrapper.__name__ = func.__name__
+ return wrapper
- return decorator
+ return decorator
diff --git a/api/main.py b/api/main.py
index 8f4e087..717bb5e 100644
--- a/api/main.py
+++ b/api/main.py
@@ -6,4 +6,4 @@ import game.socket
# start the flask/socket.io server
if __name__ == "__main__":
- io.run(app, host="127.0.0.1", port=5000, debug=True)
+ io.run(app, host="127.0.0.1", port=5000, debug=True)
diff --git a/api/passwords.py b/api/passwords.py
index 9cd56da..761e6a3 100644
--- a/api/passwords.py
+++ b/api/passwords.py
@@ -3,14 +3,14 @@ import bcrypt
# encode string as utf-8
def enc(string):
- return string.encode('utf-8')
+ return string.encode('utf-8')
# check if password matches against hash in database
def check_password(password, password_hash):
- return bcrypt.checkpw(enc(password), password_hash)
+ return bcrypt.checkpw(enc(password), password_hash)
# hash a password for storing in the database
def password_hash(password):
- return bcrypt.hashpw(enc(password), bcrypt.gensalt())
+ return bcrypt.hashpw(enc(password), bcrypt.gensalt())
diff --git a/api/randid.py b/api/randid.py
index 6c1ca9d..e0baf43 100644
--- a/api/randid.py
+++ b/api/randid.py
@@ -6,13 +6,13 @@ tables = {"users": "user_id", "games": "game_id"}
# generate a new uuid and check for collisions (unlikely but still)
def new_uuid(table_name):
- temp_uuid = str(uuid.uuid4())
- column_name = tables[table_name]
- # check if id is already taken
- if cursor.execute(
- f"select {column_name} from {table_name} where {column_name} = ?",
- [temp_uuid]
- ).fetchone():
- return new_uuid(table_name)
- else:
- return temp_uuid
+ temp_uuid = str(uuid.uuid4())
+ column_name = tables[table_name]
+ # check if id is already taken
+ if cursor.execute(
+ f"select {column_name} from {table_name} where {column_name} = ?",
+ [temp_uuid]
+ ).fetchone():
+ return new_uuid(table_name)
+ else:
+ return temp_uuid
diff --git a/api/rating.py b/api/rating.py
index 406969b..cfdf5e4 100644
--- a/api/rating.py
+++ b/api/rating.py
@@ -2,40 +2,40 @@ from db import cursor
def outcome(outcome_str, player_1):
- outcome_int = {"w": 1, "l": -1, "d": 0}[outcome_str]
- if not player_1: outcome_int *= -1
- return {1: "w", -1: "l", 0: "d"}[outcome_int]
+ outcome_int = {"w": 1, "l": -1, "d": 0}[outcome_str]
+ if not player_1: outcome_int *= -1
+ return {1: "w", -1: "l", 0: "d"}[outcome_int]
def rating_v1(won_games): # python is a garbage language
- return abs(won_games)**(1 / 2.2) * 23 * (1, -1)[won_games < 0]
+ return abs(won_games)**(1 / 2.2) * 23 * (1, -1)[won_games < 0]
def get_all_games(user_id):
- return cursor.execute("select player_1_id, player_2_id, outcome " + \
- "from games " + \
- "where (player_1_id = ? or player_2_id = ?) " + \
- "and status = \"finished\" or status = \"resign\"", [user_id, user_id]).fetchall()
+ return cursor.execute("select player_1_id, player_2_id, outcome " + \
+ "from games " + \
+ "where (player_1_id = ? or player_2_id = ?) " + \
+ "and status = \"finished\" or status = \"resign\"", [user_id, user_id]).fetchall()
# simple rating function that doesn't use game analysis
def get_rating(user_id):
- score = 400
- games = get_all_games(user_id)
- # get all games for user_id and switch perspective in which user_id is player_2_id
- mapped_games = [
- game if game[0] == user_id else
- (game[1], game[0], outcome(game[2], False)) for game in games
- ]
- counted_opponents = {}
- for game in mapped_games:
- # calculate sum score against user (+1 for win, -1 for lose, 0 for draw game)
- counted_opponents[game[1]] = (counted_opponents.get(game[1]) or 0) + {
- "w": 1,
- "l": -1,
- "d": 0
- }[game[2]]
- for opponent in counted_opponents:
- # apply the cool curve to the sum score and add to the base score of 400
- score += rating_v1(counted_opponents.get(opponent))
- return int(score)
+ score = 400
+ games = get_all_games(user_id)
+ # get all games for user_id and switch perspective in which user_id is player_2_id
+ mapped_games = [
+ game if game[0] == user_id else
+ (game[1], game[0], outcome(game[2], False)) for game in games
+ ]
+ counted_opponents = {}
+ for game in mapped_games:
+ # calculate sum score against user (+1 for win, -1 for lose, 0 for draw game)
+ counted_opponents[game[1]] = (counted_opponents.get(game[1]) or 0) + {
+ "w": 1,
+ "l": -1,
+ "d": 0
+ }[game[2]]
+ for opponent in counted_opponents:
+ # apply the cool curve to the sum score and add to the base score of 400
+ score += rating_v1(counted_opponents.get(opponent))
+ return int(score)
diff --git a/api/readme.md b/api/readme.md
index 648171a..f5cd981 100644
--- a/api/readme.md
+++ b/api/readme.md
@@ -80,7 +80,8 @@ API return type classes are mostly defined in api/api.ts
```ts
{
- id: userID;
+ id:
+ userID;
}
```
@@ -90,7 +91,7 @@ API return type classes are mostly defined in api/api.ts
```ts
{
- userInfo;
+ userInfo;
}
```
@@ -107,7 +108,7 @@ API return type classes are mostly defined in api/api.ts
```ts
{
- userInfo;
+ userInfo;
}
```
@@ -122,7 +123,8 @@ API return type classes are mostly defined in api/api.ts
```ts
{
- id: userID;
+ id:
+ userID;
}
```
@@ -190,7 +192,8 @@ returns error when image is not .png or larger than 256x256
```ts
{
- image: base64PNG;
+ image:
+ base64PNG;
}
```
@@ -209,7 +212,8 @@ returns error when image is not .png or larger than 256x256
```ts
{
- preferences: userPreferences;
+ preferences:
+ userPreferences;
}
```
@@ -224,7 +228,8 @@ returns error when image is not .png or larger than 256x256
```ts
{
- newPreferences: userPreferences;
+ newPreferences:
+ userPreferences;
}
```
@@ -295,7 +300,8 @@ returns error when image is not .png or larger than 256x256
```ts
{
- status: string;
+ status:
+ string;
}
```
@@ -312,7 +318,8 @@ returns error when image is not .png or larger than 256x256
```ts
{
- id: userID;
+ id:
+ userID;
}
```
@@ -329,7 +336,8 @@ returns error when image is not .png or larger than 256x256
```ts
{
- id: userID;
+ id:
+ userID;
}
```
@@ -346,7 +354,8 @@ returns error when image is not .png or larger than 256x256
```ts
{
- id: userID;
+ id:
+ userID;
}
```
@@ -363,7 +372,8 @@ returns error when image is not .png or larger than 256x256
```ts
{
- query: string;
+ query:
+ string;
}
```
@@ -386,7 +396,8 @@ returns error when image is not .png or larger than 256x256
```ts
{
- id: userID;
+ id:
+ userID;
}
```
@@ -403,7 +414,8 @@ returns error when image is not .png or larger than 256x256
```ts
{
- id: userID;
+ id:
+ userID;
}
```
@@ -503,7 +515,8 @@ returns error when image is not .png or larger than 256x256
```ts
{
- id: gameID;
+ id:
+ gameID;
}
```
@@ -513,7 +526,7 @@ returns error when image is not .png or larger than 256x256
```ts
{
- gameInfo;
+ gameInfo;
}
```
@@ -528,7 +541,8 @@ returns error when image is not .png or larger than 256x256
```ts
{
- id: gameID;
+ id:
+ gameID;
}
```
@@ -555,7 +569,8 @@ returns error when image is not .png or larger than 256x256
```ts
{
- id: gameID;
+ id:
+ gameID;
}
```
diff --git a/api/ruleset.py b/api/ruleset.py
index 5f33e09..40458de 100644
--- a/api/ruleset.py
+++ b/api/ruleset.py
@@ -3,35 +3,35 @@ import json
# predefined rulesets
rulesets = {
- "default": {
- "timelimit": {
- "enabled": False,
- "minutes": 0,
- "seconds": 0,
- "addmove": 0,
- "shared": False,
- },
- "ranked": True,
- }
+ "default": {
+ "timelimit": {
+ "enabled": False,
+ "minutes": 0,
+ "seconds": 0,
+ "addmove": 0,
+ "shared": False,
+ },
+ "ranked": True,
+ }
}
# resolve ruleset from ruleset name or dict
def resolve_ruleset(ruleset):
- # create return variable
- export = {}
- try:
- # try to parse the ruleset as json
- export = json.loads(ruleset)
- merged = dict(rulesets["default"])
+ # create return variable
+ export = {}
+ try:
+ # try to parse the ruleset as json
+ export = json.loads(ruleset)
+ merged = dict(rulesets["default"])
- # fill missing keys in dict
- merge(merged, export)
- export = merged
- except ValueError as e:
- # if the ruleset is a name like 'default' or 'columns+2', read it from the predefined rulesets
- if ruleset in rulesets:
- export = rulesets[ruleset]
- if not export:
- export = rulesets["default"]
- return export
+ # fill missing keys in dict
+ merge(merged, export)
+ export = merged
+ except ValueError as e:
+ # if the ruleset is a name like 'default' or 'columns+2', read it from the predefined rulesets
+ if ruleset in rulesets:
+ export = rulesets[ruleset]
+ if not export:
+ export = rulesets["default"]
+ return export
diff --git a/api/social/create_relation.py b/api/social/create_relation.py
index 5367ac5..01ad0eb 100644
--- a/api/social/create_relation.py
+++ b/api/social/create_relation.py
@@ -6,46 +6,46 @@ import time
def create_relation(user_1_id, user_2_id, relation_type):
- remove_relation(user_1_id, user_2_id)
- remove_relation(user_2_id, user_1_id)
- timestamp = int(time.time() * 1000)
- cursor.execute(
- "insert into social values (?, ?, ?, ?)",
- [user_1_id, user_2_id, relation_type, timestamp]
- )
- connection.commit()
+ remove_relation(user_1_id, user_2_id)
+ remove_relation(user_2_id, user_1_id)
+ timestamp = int(time.time() * 1000)
+ cursor.execute(
+ "insert into social values (?, ?, ?, ?)",
+ [user_1_id, user_2_id, relation_type, timestamp]
+ )
+ connection.commit()
# remove relation between user_1_id and user_2_id (one-way)
def remove_relation(user_1_id, user_2_id):
- cursor.execute(
- "delete from social where user_1_id = ? and user_2_id = ?",
- [user_1_id, user_2_id]
- )
- connection.commit()
+ cursor.execute(
+ "delete from social where user_1_id = ? and user_2_id = ?",
+ [user_1_id, user_2_id]
+ )
+ connection.commit()
def create_relation_route(relation_type):
- @two_person
- def route(user_1_id, user_2_id):
- create_relation(user_1_id, user_2_id, relation_type)
+ @two_person
+ def route(user_1_id, user_2_id):
+ create_relation(user_1_id, user_2_id, relation_type)
- if relation_type == "outgoing":
- io.emit("incomingFriendRequest", room="user-" + user_2_id)
+ if relation_type == "outgoing":
+ io.emit("incomingFriendRequest", room="user-" + user_2_id)
- return "", 200
+ return "", 200
- return route
+ return route
friend_request = Blueprint('friend_request', __name__)
friend_request.add_url_rule(
- '/request', 'route', create_relation_route("outgoing"), methods=["POST"]
+ '/request', 'route', create_relation_route("outgoing"), methods=["POST"]
)
block = Blueprint('block', __name__)
block.add_url_rule(
- '/block', 'route', create_relation_route("block"), methods=["POST"]
+ '/block', 'route', create_relation_route("block"), methods=["POST"]
)
dynamic_routes = [["/social", friend_request], ["/social", block]]
diff --git a/api/social/destroy_relation.py b/api/social/destroy_relation.py
index 2aa793b..d2e4388 100644
--- a/api/social/destroy_relation.py
+++ b/api/social/destroy_relation.py
@@ -12,16 +12,16 @@ remove = Blueprint('remove', __name__)
@remove.route('/remove', methods=['POST'])
@two_person
def index(user_1_id, user_2_id):
- relation = get_relation_to(user_1_id, user_2_id)
- if relation == "none": return "", 403
+ relation = get_relation_to(user_1_id, user_2_id)
+ if relation == "none": return "", 403
- remove_relation(user_1_id, user_2_id)
- remove_relation(user_2_id, user_1_id)
+ remove_relation(user_1_id, user_2_id)
+ remove_relation(user_2_id, user_1_id)
- io.emit("changedRelation", {"id": user_2_id}, room="user-" + user_1_id)
- io.emit("changedRelation", {"id": user_1_id}, room="user-" + user_2_id)
+ io.emit("changedRelation", {"id": user_2_id}, room="user-" + user_1_id)
+ io.emit("changedRelation", {"id": user_1_id}, room="user-" + user_2_id)
- return "", 200
+ return "", 200
unblock = Blueprint('unblock', __name__)
@@ -30,10 +30,10 @@ unblock = Blueprint('unblock', __name__)
@unblock.route('/unblock', methods=['POST'])
@two_person
def index(user_1_id, user_2_id):
- if get_relation_to(user_1_id, user_2_id) != "blocked": return "", 403
+ if get_relation_to(user_1_id, user_2_id) != "blocked": return "", 403
- remove_relation(user_1_id, user_2_id)
- return "", 200
+ remove_relation(user_1_id, user_2_id)
+ return "", 200
dynamic_routes = [["/social", remove], ["/social", unblock]]
diff --git a/api/social/friend_accept.py b/api/social/friend_accept.py
index b434272..45edc72 100644
--- a/api/social/friend_accept.py
+++ b/api/social/friend_accept.py
@@ -10,16 +10,16 @@ accept = Blueprint('accept', __name__)
@accept.route("/accept", methods=['POST'])
@two_person
def route(user_1_id, user_2_id):
- cursor.execute(
- "update social set type = \"friendship\" where user_1_id = ? and user_2_id = ?",
- [user_2_id, user_1_id]
- )
- connection.commit()
+ cursor.execute(
+ "update social set type = \"friendship\" where user_1_id = ? and user_2_id = ?",
+ [user_2_id, user_1_id]
+ )
+ connection.commit()
- io.emit("changedRelation", {"id": user_2_id}, room="user-" + user_1_id)
- io.emit("changedRelation", {"id": user_1_id}, room="user-" + user_2_id)
+ io.emit("changedRelation", {"id": user_2_id}, room="user-" + user_1_id)
+ io.emit("changedRelation", {"id": user_1_id}, room="user-" + user_2_id)
- return "", 200
+ return "", 200
dynamic_route = ["/social", accept]
diff --git a/api/social/request_list.py b/api/social/request_list.py
index 9b79203..f991ce2 100644
--- a/api/social/request_list.py
+++ b/api/social/request_list.py
@@ -10,18 +10,18 @@ requests = Blueprint('requests', __name__)
@requests.route("/requests")
@auth_required("user")
def route(user_2_id):
- # get a list of friend requests
- request_list = cursor.execute(
- "select user_1_id from social where user_2_id = ? and type = \"outgoing\"",
- [user_2_id]
- ).fetchall()
+ # get a list of friend requests
+ request_list = cursor.execute(
+ "select user_1_id from social where user_2_id = ? and type = \"outgoing\"",
+ [user_2_id]
+ ).fetchall()
- # get user_id for each result to prevent repeat user/info requests
- formatted_request_list = []
- for user_1_id in [q[0] for q in request_list]:
- formatted_request_list.append(format_user(user_1_id))
+ # get user_id for each result to prevent repeat user/info requests
+ formatted_request_list = []
+ for user_1_id in [q[0] for q in request_list]:
+ formatted_request_list.append(format_user(user_1_id))
- return {"requests": formatted_request_list}, 200
+ return {"requests": formatted_request_list}, 200
dynamic_route = ["/social/list", requests]
diff --git a/api/social/search.py b/api/social/search.py
index f0ce8a2..c0ef312 100644
--- a/api/social/search.py
+++ b/api/social/search.py
@@ -8,26 +8,26 @@ search = Blueprint('search', __name__)
@search.route('/search', methods=['POST'])
def index():
- data_string = request.data or "{}"
- data = json.loads(data_string)
- query = data.get("query") or ""
- if not query: return "", 400
- if len(query) < 3: return "", 403
-
- # use levenshtein with max distance 3 to search for users
- #TODO: use mysql and sort by best match
- results = cursor.execute(
- "select user_id from users where levenshtein(lower(username), lower(?), 3)",
- [query]
- ).fetchmany(20)
-
- formatted = {"results": []}
-
- # get user_id for each result to prevent repeat user/info requests
- for user in results:
- formatted["results"].append(format_user(user[0]))
-
- return formatted, 200
+ data_string = request.data or "{}"
+ data = json.loads(data_string)
+ query = data.get("query") or ""
+ if not query: return "", 400
+ if len(query) < 3: return "", 403
+
+ # use levenshtein with max distance 3 to search for users
+ #TODO: use mysql and sort by best match
+ results = cursor.execute(
+ "select user_id from users where levenshtein(lower(username), lower(?), 3)",
+ [query]
+ ).fetchmany(20)
+
+ formatted = {"results": []}
+
+ # get user_id for each result to prevent repeat user/info requests
+ for user in results:
+ formatted["results"].append(format_user(user[0]))
+
+ return formatted, 200
dynamic_route = ["/social", search]
diff --git a/api/status.py b/api/status.py
index a47c6c4..32f96ee 100644
--- a/api/status.py
+++ b/api/status.py
@@ -6,15 +6,15 @@ status = Blueprint('server_status', __name__)
@status.route('/status')
def index():
- return {
- # "users": int,
- "games":
- len(
- cursor.execute(
- "select game_id from games where status = \"in_progress\""
- ).fetchall()
- )
- }
+ return {
+ # "users": int,
+ "games":
+ len(
+ cursor.execute(
+ "select game_id from games where status = \"in_progress\""
+ ).fetchall()
+ )
+ }
dynamic_route = ["/", status]
diff --git a/api/user/avatar.py b/api/user/avatar.py
index bfbc097..3589653 100644
--- a/api/user/avatar.py
+++ b/api/user/avatar.py
@@ -13,27 +13,27 @@ avatar = Blueprint('avatar', __name__)
@avatar.route('/avatar', methods=["GET"])
@auth_required("none")
def get_avatar(token_id):
- user_id = request.args.get("id") or token_id
- if not user_id: return "", 400
- if not valid.user_id(user_id): return "", 403
+ user_id = request.args.get("id") or token_id
+ if not user_id: return "", 400
+ if not valid.user_id(user_id): return "", 403
- avatar_path = f"database/avatars/{user_id}.png"
- avatar = ""
- if exists(avatar_path):
- avatar = open(avatar_path, "rb").read()
- return Response(avatar or default_avatar, 200, mimetype="image/png")
+ avatar_path = f"database/avatars/{user_id}.png"
+ avatar = ""
+ if exists(avatar_path):
+ avatar = open(avatar_path, "rb").read()
+ return Response(avatar or default_avatar, 200, mimetype="image/png")
#TODO: pillow image size validation (client side resize)
@avatar.route('/avatar', methods=["POST"])
@auth_required("user")
def update_avatar(user_id):
- if not request.data: return "", 400
+ if not request.data: return "", 400
- open(f"database/avatars/{user_id}.png", "wb") \
- .write(decode(request.data, "base64"))
+ open(f"database/avatars/{user_id}.png", "wb") \
+ .write(decode(request.data, "base64"))
- return "", 200
+ return "", 200
dynamic_route = ["/user", avatar]
diff --git a/api/user/games.py b/api/user/games.py
index da5f422..d3b650c 100644
--- a/api/user/games.py
+++ b/api/user/games.py
@@ -11,54 +11,54 @@ import json
# get total game outcome amount for user
def sum_games(user_id): #! SANITIZE USER_ID FIRST
- wld_querys = [
- ' '.join(
- [
- "select count(game_id)",
- "from games",
- "where",
- f"player_{x[0]}_id = \"{user_id}\" and",
- f"outcome = \"{x[1]}\"",
- ]
- ) for x in [(1, "w"), (1, "l"), (2, "w"), (2, "l")]
- ]
- wld_querys.insert(
- 0, ' '.join(
- [
- "select count(game_id)",
- "from games",
- "where",
- f"(player_1_id = \"{user_id}\" or player_2_id = \"{user_id}\") and",
- "outcome = \"d\"",
- ]
- )
- )
+ wld_querys = [
+ ' '.join(
+ [
+ "select count(game_id)",
+ "from games",
+ "where",
+ f"player_{x[0]}_id = \"{user_id}\" and",
+ f"outcome = \"{x[1]}\"",
+ ]
+ ) for x in [(1, "w"), (1, "l"), (2, "w"), (2, "l")]
+ ]
+ wld_querys.insert(
+ 0, ' '.join(
+ [
+ "select count(game_id)",
+ "from games",
+ "where",
+ f"(player_1_id = \"{user_id}\" or player_2_id = \"{user_id}\") and",
+ "outcome = \"d\"",
+ ]
+ )
+ )
- big_query = "select " + ", ".join([f"({query})" for query in wld_querys])
+ big_query = "select " + ", ".join([f"({query})" for query in wld_querys])
- results = cursor.execute(big_query).fetchone()
+ results = cursor.execute(big_query).fetchone()
- # win and lose are calculated from user_id's perspective (player_1_id, player_2_id in db)
- return {
- "draw": results[0],
- "win": results[1] + results[4],
- "lose": results[2] + results[3],
- "games": reduce(lambda a, b: a + b, results)
- }
+ # win and lose are calculated from user_id's perspective (player_1_id, player_2_id in db)
+ return {
+ "draw": results[0],
+ "win": results[1] + results[4],
+ "lose": results[2] + results[3],
+ "games": reduce(lambda a, b: a + b, results)
+ }
# get `count` games that `user_id` participated in, sorted by newest game
def fetch_games(user_id, count):
- game_ids = cursor.execute(
- "select game_id from games where player_1_id = ? or player_2_id = ? order by created desc",
- [user_id, user_id]
- ).fetchmany(count)
- export = []
+ game_ids = cursor.execute(
+ "select game_id from games where player_1_id = ? or player_2_id = ? order by created desc",
+ [user_id, user_id]
+ ).fetchmany(count)
+ export = []
- for game_id in game_ids:
- export.append(format_game(game_id[0], user_id))
+ for game_id in game_ids:
+ export.append(format_game(game_id[0], user_id))
- return export
+ return export
games = Blueprint('games', __name__)
@@ -67,10 +67,10 @@ games = Blueprint('games', __name__)
@games.route('/games', methods=['GET', 'POST'])
@one_person
def index(user_id, viewer):
- return {
- "totals": sum_games(user_id),
- "games": fetch_games(user_id, 20)
- }, 200
+ return {
+ "totals": sum_games(user_id),
+ "games": fetch_games(user_id, 20)
+ }, 200
dynamic_route = ["/user", games]
diff --git a/api/user/info.py b/api/user/info.py
index a5e2aac..fc303a6 100644
--- a/api/user/info.py
+++ b/api/user/info.py
@@ -7,53 +7,53 @@ import json
# get relation to user_2_id from user_1_id's perspective
def get_relation_to(user_1_id, user_2_id):
- relation = cursor.execute("select * from social where " + \
- "(user_1_id = ? and user_2_id = ?) or " + \
- "(user_1_id = ? and user_2_id = ?)", [user_1_id, user_2_id, user_2_id, user_1_id]).fetchone()
- if not relation: return "none"
- if relation[2] == "friendship": return "friends"
- if relation[2] == "outgoing" and relation[0] == user_1_id:
- return "outgoing"
- if relation[2] == "outgoing" and relation[1] == user_1_id:
- return "incoming"
- if relation[2] == "block" and relation[0] == user_1_id: return "blocked"
- return "none"
+ relation = cursor.execute("select * from social where " + \
+ "(user_1_id = ? and user_2_id = ?) or " + \
+ "(user_1_id = ? and user_2_id = ?)", [user_1_id, user_2_id, user_2_id, user_1_id]).fetchone()
+ if not relation: return "none"
+ if relation[2] == "friendship": return "friends"
+ if relation[2] == "outgoing" and relation[0] == user_1_id:
+ return "outgoing"
+ if relation[2] == "outgoing" and relation[1] == user_1_id:
+ return "incoming"
+ if relation[2] == "block" and relation[0] == user_1_id: return "blocked"
+ return "none"
# get users friend count
def count_friends(user_id):
- query = cursor.execute(
- "select type from social where (user_1_id = ? or user_2_id = ?) and type = \"friendship\"",
- [user_id, user_id]
- ).fetchall()
- return len(query) #FIXME: use SQL count() instead of python's len()
+ query = cursor.execute(
+ "select type from social where (user_1_id = ? or user_2_id = ?) and type = \"friendship\"",
+ [user_id, user_id]
+ ).fetchall()
+ return len(query) #FIXME: use SQL count() instead of python's len()
# get user/info of `user_id` as `viewer` (id)
def format_user(user_id, viewer=''):
- user = cursor.execute(
- "select " + ", ".join(
- [
- "username",
- "user_id",
- "country",
- "registered",
- "status",
- ]
- ) + " from users where user_id = ?", [user_id]
- ).fetchone()
- formatted_user = {
- "username": user[0],
- "id": user[1],
- "country": user[2],
- "registered": user[3],
- "status": user[4],
- "friends": count_friends(user_id),
- "rating":
- get_rating(user_id), #TODO: calculate rating based on game analysis
- }
- if viewer: formatted_user["relation"] = get_relation_to(viewer, user_id)
- return formatted_user
+ user = cursor.execute(
+ "select " + ", ".join(
+ [
+ "username",
+ "user_id",
+ "country",
+ "registered",
+ "status",
+ ]
+ ) + " from users where user_id = ?", [user_id]
+ ).fetchone()
+ formatted_user = {
+ "username": user[0],
+ "id": user[1],
+ "country": user[2],
+ "registered": user[3],
+ "status": user[4],
+ "friends": count_friends(user_id),
+ "rating":
+ get_rating(user_id), #TODO: calculate rating based on game analysis
+ }
+ if viewer: formatted_user["relation"] = get_relation_to(viewer, user_id)
+ return formatted_user
info = Blueprint('info', __name__)
@@ -64,8 +64,8 @@ info = Blueprint('info', __name__)
@info.route('/info', methods=['GET', 'POST'])
@one_person
def index(user_id, viewer):
- user = format_user(user_id, viewer)
- return user, 200
+ user = format_user(user_id, viewer)
+ return user, 200
dynamic_route = ["/user", info]
diff --git a/api/user/password.py b/api/user/password.py
index 51ab6db..731f7e4 100644
--- a/api/user/password.py
+++ b/api/user/password.py
@@ -7,13 +7,13 @@ password = Blueprint('password', __name__)
# this endpoint is unfinished
@password.route('/password')
def index():
- data = request.get_json()
+ data = request.get_json()
- if not data["password"] or \
- not data["newPassword"]:
- return "", 400
+ if not data["password"] or \
+ not data["newPassword"]:
+ return "", 400
- return {}, 200
+ return {}, 200
dynamic_route = ["/user", password]
diff --git a/api/user/preferences.py b/api/user/preferences.py
index 2ca4a05..8779eaf 100644
--- a/api/user/preferences.py
+++ b/api/user/preferences.py
@@ -7,17 +7,17 @@ import json
# fill missing dict keys in preferences object
def format_preferences(prefs):
- return {
- "darkMode":
- prefs.get("darkMode") or False,
- "ruleset":
- resolve_ruleset(json.dumps(prefs.get("ruleset") or {}) or "default"),
- "userColors": {
- "diskA": prefs.get("userColors", {}).get("diskA") or "",
- "diskB": prefs.get("userColors", {}).get("diskB") or "",
- "background": prefs.get("userColors", {}).get("background") or ""
- }
- }
+ return {
+ "darkMode":
+ prefs.get("darkMode") or False,
+ "ruleset":
+ resolve_ruleset(json.dumps(prefs.get("ruleset") or {}) or "default"),
+ "userColors": {
+ "diskA": prefs.get("userColors", {}).get("diskA") or "",
+ "diskB": prefs.get("userColors", {}).get("diskB") or "",
+ "background": prefs.get("userColors", {}).get("background") or ""
+ }
+ }
preferences = Blueprint('preferences', __name__)
@@ -26,27 +26,27 @@ preferences = Blueprint('preferences', __name__)
@preferences.route('/preferences', methods=["GET"])
@auth_required("user")
def get_preferences(login):
- user_prefs = cursor.execute(
- "select preferences from users where user_id = ?", [login]
- ).fetchone()
- return {"preferences": format_preferences(json.loads(user_prefs[0]))}, 200
+ user_prefs = cursor.execute(
+ "select preferences from users where user_id = ?", [login]
+ ).fetchone()
+ return {"preferences": format_preferences(json.loads(user_prefs[0]))}, 200
@preferences.route('/preferences', methods=["POST"])
@auth_required("user")
def index(login):
- data = request.get_json()
- new_preferences = data.get("newPreferences") or ""
+ data = request.get_json()
+ new_preferences = data.get("newPreferences") or ""
- formatted_json = format_preferences(new_preferences)
+ formatted_json = format_preferences(new_preferences)
- cursor.execute(
- "update users set preferences = ? where user_id = ?",
- [json.dumps(formatted_json), login]
- )
- connection.commit()
+ cursor.execute(
+ "update users set preferences = ? where user_id = ?",
+ [json.dumps(formatted_json), login]
+ )
+ connection.commit()
- return "", 200
+ return "", 200
dynamic_route = ["/user", preferences]
diff --git a/api/user/status.py b/api/user/status.py
index 219481a..050ed33 100644
--- a/api/user/status.py
+++ b/api/user/status.py
@@ -9,17 +9,17 @@ status = Blueprint('user_status', __name__)
@status.route('/status', methods=['POST'])
@auth_required("user")
def index(user_id):
- data = request.get_json()
- status = data.get("status") or ""
- if not status: return "", 400
+ data = request.get_json()
+ status = data.get("status") or ""
+ if not status: return "", 400
- cursor.execute(
- "update users set status = ? where user_id = ?",
- [status[0:200], user_id]
- )
- connection.commit()
+ cursor.execute(
+ "update users set status = ? where user_id = ?",
+ [status[0:200], user_id]
+ )
+ connection.commit()
- return "", 200
+ return "", 200
dynamic_route = ["/user", status]
diff --git a/api/util.py b/api/util.py
index 777820a..836c524 100644
--- a/api/util.py
+++ b/api/util.py
@@ -1,6 +1,6 @@
def all_def(props):
- return all(bool(v) for v in props)
+ return all(bool(v) for v in props)
def all_notdef(props):
- return all(not v for v in props)
+ return all(not v for v in props)
diff --git a/api/valid.py b/api/valid.py
index f407460..1c63703 100644
--- a/api/valid.py
+++ b/api/valid.py
@@ -2,20 +2,20 @@ from db import cursor
def validate(id, type):
- types = {
- "user": ["user_id", "users"],
- "game": ["game_id", "games"],
- }
- fields = types[type]
- query = cursor.execute(
- f"select {fields[0]} from {fields[1]} where {fields[0]} = ?", [id]
- ).fetchone()
- return bool(query)
+ types = {
+ "user": ["user_id", "users"],
+ "game": ["game_id", "games"],
+ }
+ fields = types[type]
+ query = cursor.execute(
+ f"select {fields[0]} from {fields[1]} where {fields[0]} = ?", [id]
+ ).fetchone()
+ return bool(query)
def user_id(user_id):
- return validate(user_id, "user")
+ return validate(user_id, "user")
def game_id(game_id):
- return validate(game_id, "game")
+ return validate(game_id, "game")