commit
8f2a4cea04
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
**/__pycache__
|
||||||
|
instance/
|
||||||
4
app.py
Normal file
4
app.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
from truthseeker import app # the variable 'app' is detected by `flask run`
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.run()
|
||||||
27
truthseeker/__init__.py
Normal file
27
truthseeker/__init__.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import flask
|
||||||
|
import os
|
||||||
|
|
||||||
|
from truthseeker.routes import routes_api, routes_ui
|
||||||
|
|
||||||
|
|
||||||
|
app = flask.Flask("truthseeker")
|
||||||
|
|
||||||
|
def set_secret(app):
|
||||||
|
if os.path.isfile("instance/secret.txt"):
|
||||||
|
f = open("instance/secret.txt", "r")
|
||||||
|
app.config["SECRET_KEY"] = f.read()
|
||||||
|
f.close()
|
||||||
|
print("Read secret from secret.txt !")
|
||||||
|
else:
|
||||||
|
import secrets
|
||||||
|
app.config["SECRET_KEY"] = secrets.token_hex()
|
||||||
|
f = open("instance/secret.txt", "w")
|
||||||
|
f.write(app.config["SECRET_KEY"])
|
||||||
|
f.close()
|
||||||
|
print("Generated secret and wrote to secret.txt !")
|
||||||
|
|
||||||
|
set_secret(app)
|
||||||
|
|
||||||
|
|
||||||
|
app.register_blueprint(routes_api.routes_api, url_prefix="/api/v1")
|
||||||
|
app.register_blueprint(routes_ui.routes_ui, url_prefix="/")
|
||||||
118
truthseeker/logic/game_logic.py
Normal file
118
truthseeker/logic/game_logic.py
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
import string
|
||||||
|
import random
|
||||||
|
import jwt
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
import truthseeker
|
||||||
|
|
||||||
|
|
||||||
|
# Map of all actively running games
|
||||||
|
# games_list["game.game_id"]-> game info linked to that id
|
||||||
|
games_list = {}
|
||||||
|
|
||||||
|
def random_string(length: int) ->str:
|
||||||
|
"""
|
||||||
|
This function create a random string as long as the lint passed as
|
||||||
|
parameter
|
||||||
|
|
||||||
|
: param length: the lenght of the random string
|
||||||
|
: type length : int
|
||||||
|
: return : a random string
|
||||||
|
: return type : string
|
||||||
|
"""
|
||||||
|
return "".join(random.choice(string.ascii_letters) for _ in range(length))
|
||||||
|
|
||||||
|
class Member:
|
||||||
|
"""
|
||||||
|
stores information related to the member of a given game
|
||||||
|
|
||||||
|
Member.username : The username of this member
|
||||||
|
Member.socker : The reference to the socket to talk to this member
|
||||||
|
|
||||||
|
"""
|
||||||
|
def __init__(self, username):
|
||||||
|
self.username = username
|
||||||
|
self.socket = None
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return "Member[username={}]".format(self.username)
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return self.__str__()
|
||||||
|
|
||||||
|
class Game:
|
||||||
|
"""
|
||||||
|
The game info class stores all information linked to a active game
|
||||||
|
|
||||||
|
Game.game_id : str, the game identifier of the game
|
||||||
|
Game.owner : Member, the game identifier of the game
|
||||||
|
Game.members : Member[], the members of the game
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
self.game_id = None
|
||||||
|
self.owner = None
|
||||||
|
self.members = []
|
||||||
|
|
||||||
|
def _gen_jwt(self, username, owner):
|
||||||
|
return jwt.encode(
|
||||||
|
payload={
|
||||||
|
"game_type": "multi",
|
||||||
|
"game_id": self.game_id,
|
||||||
|
"username": username,
|
||||||
|
"owner": owner,
|
||||||
|
"exp": datetime.utcnow() + timedelta(hours = 1) # handled automatically on jwt.decode
|
||||||
|
},
|
||||||
|
key=truthseeker.app.config["SECRET_KEY"],
|
||||||
|
algorithm="HS256"
|
||||||
|
)
|
||||||
|
|
||||||
|
def set_owner(self, username):
|
||||||
|
self.owner = Member(username)
|
||||||
|
self.members.append(self.owner)
|
||||||
|
return self.owner, self._gen_jwt(username, owner=True)
|
||||||
|
|
||||||
|
def add_member(self, username):
|
||||||
|
member = Member(username)
|
||||||
|
self.members.append(member)
|
||||||
|
return member, self._gen_jwt(username, owner=False)
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return "Game[game_id={}, owner={}, members={}]".format(self.game_id, self.owner, self.members)
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return self.__str__()
|
||||||
|
|
||||||
|
def create_game():
|
||||||
|
"""
|
||||||
|
This function creates a new game by creating a Game object and stores
|
||||||
|
it into the games_list dictionnary
|
||||||
|
|
||||||
|
: return : a new Game
|
||||||
|
: return type : Game
|
||||||
|
"""
|
||||||
|
game = Game()
|
||||||
|
game.game_id = random_string(6)
|
||||||
|
game.start_token = random_string(64)
|
||||||
|
games_list[game.game_id] = game
|
||||||
|
#TODO ADD A WEBSOCKET IF THE GAME IS KNOWN TO BE MULTIPLAYER
|
||||||
|
return game
|
||||||
|
|
||||||
|
def get_game(game_id):
|
||||||
|
if game_id in games_list:
|
||||||
|
return games_list[game_id]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_game_info(game_id):
|
||||||
|
"""
|
||||||
|
This function retrieve a the Game object linked to the game_id
|
||||||
|
passed as parametter
|
||||||
|
|
||||||
|
: param game_id : the lenght of the random string
|
||||||
|
: type game_id : str
|
||||||
|
: return : The Game Object linked to the game_id
|
||||||
|
: return type : Game
|
||||||
|
"""
|
||||||
|
if game_id in games_list:
|
||||||
|
return games_list[game_id]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
91
truthseeker/routes/routes_api.py
Normal file
91
truthseeker/routes/routes_api.py
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import flask
|
||||||
|
import jwt
|
||||||
|
|
||||||
|
import truthseeker
|
||||||
|
from truthseeker.logic import game_logic
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
|
||||||
|
routes_api = flask.Blueprint("api", __name__)
|
||||||
|
|
||||||
|
# Auth decorator
|
||||||
|
def jwt_required(f):
|
||||||
|
@wraps(f)
|
||||||
|
def decorator(*args, **kwargs):
|
||||||
|
jwt_str = flask.request.args.get("jwt")
|
||||||
|
if not jwt_str:
|
||||||
|
return {"status": "Error, JWT token missing"}, 401
|
||||||
|
|
||||||
|
try:
|
||||||
|
claims = jwt.decode(jwt_str, truthseeker.app.config['SECRET_KEY'], algorithms=['HS256'])
|
||||||
|
except jwt.exceptions.InvalidTokenError as e:
|
||||||
|
print("Caught exception while decoding JWT token :", e)
|
||||||
|
return {"status": "Error, invalid JWT"}, 401
|
||||||
|
|
||||||
|
return f(claims, *args, **kwargs)
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@routes_api.route("/createGame")
|
||||||
|
def create_game():
|
||||||
|
username = flask.request.args.get("username")
|
||||||
|
if username==None:
|
||||||
|
return {"status": "error, username not set"}
|
||||||
|
|
||||||
|
|
||||||
|
response = {}
|
||||||
|
response["status"] = "ok"
|
||||||
|
game = game_logic.create_game()
|
||||||
|
response["game_id"] = game.game_id
|
||||||
|
owner, owner_jwt = game.set_owner(username=username)
|
||||||
|
response["jwt"] = owner_jwt
|
||||||
|
return response
|
||||||
|
|
||||||
|
@routes_api.route("/joinGame")
|
||||||
|
def join_game():
|
||||||
|
game_id = flask.request.args.get("game_id")
|
||||||
|
username = flask.request.args.get("username")
|
||||||
|
if game_id==None or username==None:
|
||||||
|
return {"status": "error, username or game id not set"}
|
||||||
|
|
||||||
|
game = game_logic.get_game(game_id)
|
||||||
|
if game == None:
|
||||||
|
return {"status": "error, game does not exist"}
|
||||||
|
|
||||||
|
member, member_jwt = game.add_member(username)
|
||||||
|
|
||||||
|
response = {}
|
||||||
|
response["status"] = "ok"
|
||||||
|
response["jwt"] = member_jwt
|
||||||
|
return response
|
||||||
|
|
||||||
|
@routes_api.route("/getGameInfo")
|
||||||
|
def get_game_info(): # DEPRECATED, SHOULD BE REMOVED
|
||||||
|
response = {}
|
||||||
|
game_id = flask.request.args.get("game_id")
|
||||||
|
if game_id == None:
|
||||||
|
response["status"] = "No 'game_id' argument"
|
||||||
|
return response
|
||||||
|
game = game_logic.get_game_info(game_id)
|
||||||
|
if game == None:
|
||||||
|
response["status"] = "Game {} does not exist".format(game_id)
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
response["status"] = "ok"
|
||||||
|
response["game_id"] = game_id
|
||||||
|
response["token"] = game.start_token
|
||||||
|
return response
|
||||||
|
|
||||||
|
@routes_api.route("/startGame")
|
||||||
|
@jwt_required
|
||||||
|
def start_game(claims):
|
||||||
|
if not claims["owner"]:
|
||||||
|
return {"status": "Error, you are not the owner of this game"}
|
||||||
|
|
||||||
|
if game_logic.get_game(claims["game_id"]) == None:
|
||||||
|
return {"status": "Error, this game doesn't exist"}
|
||||||
|
|
||||||
|
return {"status": "ok"}
|
||||||
10
truthseeker/routes/routes_ui.py
Normal file
10
truthseeker/routes/routes_ui.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import flask
|
||||||
|
|
||||||
|
from truthseeker.logic import game_logic
|
||||||
|
|
||||||
|
|
||||||
|
routes_ui = flask.Blueprint("ui", __name__)
|
||||||
|
|
||||||
|
@routes_ui.route("/")
|
||||||
|
def hello():
|
||||||
|
return "Hello World!"
|
||||||
Loading…
Reference in New Issue
Block a user