Merge pull request #29 from ThomasRubini/server_doc
This commit is contained in:
commit
e3f779abeb
@ -5,6 +5,14 @@ import os
|
|||||||
from truthseeker import discord_bot
|
from truthseeker import discord_bot
|
||||||
|
|
||||||
class TruthSeekerApp(flask.Flask):
|
class TruthSeekerApp(flask.Flask):
|
||||||
|
"""
|
||||||
|
Main class of the app
|
||||||
|
A single instance 'APP' of this class will be created and shared across the files
|
||||||
|
The class itself is a child class of flask.Flask and has property representing other services
|
||||||
|
|
||||||
|
:attr SocketIO socketio_app: the SocketIO service
|
||||||
|
:attr DiscordBot discord_bot: the Discord Bot service
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__("truthseeker")
|
super().__init__("truthseeker")
|
||||||
@ -26,7 +34,10 @@ class TruthSeekerApp(flask.Flask):
|
|||||||
def run_app(self):
|
def run_app(self):
|
||||||
self.socketio_app.run(self)
|
self.socketio_app.run(self)
|
||||||
|
|
||||||
def set_app_secret(self):
|
def set_app_secret(self) -> None:
|
||||||
|
"""
|
||||||
|
Set the secret used by flask
|
||||||
|
"""
|
||||||
if os.path.isfile("instance/secret.txt"):
|
if os.path.isfile("instance/secret.txt"):
|
||||||
f = open("instance/secret.txt", "r")
|
f = open("instance/secret.txt", "r")
|
||||||
self.config["SECRET_KEY"] = f.read()
|
self.config["SECRET_KEY"] = f.read()
|
||||||
@ -41,7 +52,10 @@ class TruthSeekerApp(flask.Flask):
|
|||||||
f.close()
|
f.close()
|
||||||
print("Generated secret and wrote to secret.txt !")
|
print("Generated secret and wrote to secret.txt !")
|
||||||
|
|
||||||
def get_discord_bot_token(self):
|
def get_discord_bot_token(self) -> str:
|
||||||
|
"""
|
||||||
|
Get the token used by the discord bot
|
||||||
|
"""
|
||||||
if os.path.isfile("instance/discord_bot_token.txt"):
|
if os.path.isfile("instance/discord_bot_token.txt"):
|
||||||
f = open("instance/discord_bot_token.txt", "r")
|
f = open("instance/discord_bot_token.txt", "r")
|
||||||
token = f.read()
|
token = f.read()
|
||||||
|
@ -3,10 +3,14 @@ import threading
|
|||||||
import truthseeker
|
import truthseeker
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
async def empty_coro():
|
|
||||||
return
|
|
||||||
|
|
||||||
class DiscordBot:
|
class DiscordBot:
|
||||||
|
"""
|
||||||
|
Wrapper around a discord bot, providing utility methods to interact with it
|
||||||
|
|
||||||
|
:attr Client bot: the underlying discord bot from discord.py
|
||||||
|
:attr TextChannel __channel__: the channel used by the bot to send messages
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.bot = discord.Client(intents=discord.Intents.default())
|
self.bot = discord.Client(intents=discord.Intents.default())
|
||||||
self.__channel__ = None
|
self.__channel__ = None
|
||||||
@ -19,7 +23,10 @@ class DiscordBot:
|
|||||||
self.__setup__channel__()
|
self.__setup__channel__()
|
||||||
self.update_games_presence()
|
self.update_games_presence()
|
||||||
|
|
||||||
def __setup__channel__(self):
|
def __setup__channel__(self) -> None:
|
||||||
|
"""
|
||||||
|
Setup the channel that the bot will send message in
|
||||||
|
"""
|
||||||
if len(self.bot.guilds) == 1:
|
if len(self.bot.guilds) == 1:
|
||||||
self.__channel__ = discord.utils.get(self.bot.guilds[0].channels, name="bot")
|
self.__channel__ = discord.utils.get(self.bot.guilds[0].channels, name="bot")
|
||||||
else:
|
else:
|
||||||
@ -31,6 +38,9 @@ class DiscordBot:
|
|||||||
return thr
|
return thr
|
||||||
|
|
||||||
def API(func):
|
def API(func):
|
||||||
|
"""
|
||||||
|
Decorator used to wrap APIs methods, to handle thread context change, and ready check
|
||||||
|
"""
|
||||||
def decorator(self, *args, **kwargs):
|
def decorator(self, *args, **kwargs):
|
||||||
if self.bot and self.bot.is_ready():
|
if self.bot and self.bot.is_ready():
|
||||||
self.event_loop.create_task(func(self, *args, **kwargs))
|
self.event_loop.create_task(func(self, *args, **kwargs))
|
||||||
@ -39,7 +49,10 @@ class DiscordBot:
|
|||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
@API
|
@API
|
||||||
async def update_games_presence(self):
|
async def update_games_presence(self) -> None:
|
||||||
|
"""
|
||||||
|
Update the bot's status using the app's current context
|
||||||
|
"""
|
||||||
games_n = len(truthseeker.APP.games_list)
|
games_n = len(truthseeker.APP.games_list)
|
||||||
activity_name = f"Handling {games_n} game{'' if games_n==1 else 's'} !"
|
activity_name = f"Handling {games_n} game{'' if games_n==1 else 's'} !"
|
||||||
activity = discord.Activity(name=activity_name, type=discord.ActivityType.watching)
|
activity = discord.Activity(name=activity_name, type=discord.ActivityType.watching)
|
||||||
@ -47,7 +60,10 @@ class DiscordBot:
|
|||||||
|
|
||||||
@API
|
@API
|
||||||
async def send_message(self, text):
|
async def send_message(self, text):
|
||||||
|
"""
|
||||||
|
Send a message to the channel used by the bot
|
||||||
|
"""
|
||||||
if self.__channel__:
|
if self.__channel__:
|
||||||
await self.__channel__.send(text)
|
await self.__channel__.send(text)
|
||||||
else:
|
else:
|
||||||
print("channel member not defined, not sending discord message")
|
print("channel not defined, not sending discord message")
|
||||||
|
@ -3,20 +3,15 @@ import random
|
|||||||
from truthseeker.logic.data_persistance.data_access import *
|
from truthseeker.logic.data_persistance.data_access import *
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from truthseeker import APP
|
from truthseeker import APP
|
||||||
|
from typing import Union, Optional
|
||||||
|
|
||||||
# Map of all actively running games
|
|
||||||
# games_list["game.game_id"]-> game info linked to that id
|
|
||||||
|
|
||||||
def random_string(length: int) ->str:
|
def random_string(length: int) ->str:
|
||||||
"""
|
"""
|
||||||
This function create a random string as long as the lint passed as
|
This function create a random string as long as the lint passed as
|
||||||
parameter
|
parameter
|
||||||
|
|
||||||
: param length: the lenght of the random string
|
:param length: the length of the random string to create
|
||||||
: type length : int
|
:return: a random string
|
||||||
: return : a random string
|
|
||||||
: return type : string
|
|
||||||
"""
|
"""
|
||||||
return "".join(random.choice(string.ascii_letters) for _ in range(length))
|
return "".join(random.choice(string.ascii_letters) for _ in range(length))
|
||||||
|
|
||||||
@ -24,13 +19,13 @@ class Member:
|
|||||||
"""
|
"""
|
||||||
stores information related to the member of a given game
|
stores information related to the member of a given game
|
||||||
|
|
||||||
Member.username : The username of this member
|
:attr str username: The username of this member
|
||||||
Member.socker : The reference to the socket to talk to this member
|
:attr TODO progress: TODO
|
||||||
|
:attr TODO results: TODO
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, username):
|
def __init__(self, username):
|
||||||
self.username = username
|
self.username = username
|
||||||
self.socket = None
|
|
||||||
self.progress = 0
|
self.progress = 0
|
||||||
self.results = None
|
self.results = None
|
||||||
|
|
||||||
@ -44,10 +39,14 @@ class Game:
|
|||||||
"""
|
"""
|
||||||
The game info class stores all information linked to a active game
|
The game info class stores all information linked to a active game
|
||||||
|
|
||||||
Game.game_id : str, the game identifier of the game
|
:attr str game_id: str, the game identifier of the game
|
||||||
Game.owner : Member, the game identifier of the game
|
:attr owner Member: the player start created the game. It is also stored in self.members
|
||||||
Game.members : Member[], the members of the game
|
:attr Member[] members: the members of the game
|
||||||
|
:attr bool has_started: TODO
|
||||||
|
:attr TODO gamedata: TODO
|
||||||
|
:attr TODO reaction_table: TODO
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.game_id = None
|
self.game_id = None
|
||||||
self.owner = None
|
self.owner = None
|
||||||
@ -56,12 +55,21 @@ class Game:
|
|||||||
self.gamedata = {}
|
self.gamedata = {}
|
||||||
self.reaction_table = {}
|
self.reaction_table = {}
|
||||||
|
|
||||||
def set_owner(self, username):
|
def set_owner(self, username: str) -> Member:
|
||||||
|
"""
|
||||||
|
Set the owner of the game
|
||||||
|
|
||||||
|
:param username: the username of the owner.
|
||||||
|
:return: the Member object created by this method
|
||||||
|
"""
|
||||||
self.owner = Member(username)
|
self.owner = Member(username)
|
||||||
self.members.append(self.owner)
|
self.members.append(self.owner)
|
||||||
return self.owner
|
return self.owner
|
||||||
|
|
||||||
def generateGameResults(self):
|
def generateGameResults(self) -> None:
|
||||||
|
"""
|
||||||
|
TODO + TODO RET TYPE
|
||||||
|
"""
|
||||||
data = {}
|
data = {}
|
||||||
npcs = data["npcs"] = {}
|
npcs = data["npcs"] = {}
|
||||||
for npc_id in self.gamedata["npcs"]:
|
for npc_id in self.gamedata["npcs"]:
|
||||||
@ -75,29 +83,50 @@ class Game:
|
|||||||
player_results[member.username] = member.results
|
player_results[member.username] = member.results
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def generate_data(self):
|
def generate_data(self) -> None:
|
||||||
|
"""
|
||||||
|
TODO
|
||||||
|
"""
|
||||||
#TODO Get language from player
|
#TODO Get language from player
|
||||||
self.gamedata, self.reaction_table = generateGameData("FR")
|
self.gamedata, self.reaction_table = generateGameData("FR")
|
||||||
|
|
||||||
def get_member(self, username):
|
def get_member(self, username: str) -> Union[Member, None]:
|
||||||
|
"""
|
||||||
|
Get a Member object from a username
|
||||||
|
|
||||||
|
:param username: the username of the member to search for
|
||||||
|
:return the member corresponding to the username, or None if none if found:
|
||||||
|
"""
|
||||||
for member in self.members:
|
for member in self.members:
|
||||||
if member.username == username:
|
if member.username == username:
|
||||||
return member
|
return member
|
||||||
|
|
||||||
def add_member(self, username):
|
def add_member(self, username: str) -> Union[Member, None]:
|
||||||
|
"""
|
||||||
|
Add a Member to the game
|
||||||
|
|
||||||
|
:param username: the username of the member to add
|
||||||
|
:return: the Member created, or None if a Member with this username already exists in the game
|
||||||
|
"""
|
||||||
if self.get_member(username):
|
if self.get_member(username):
|
||||||
return None
|
return None
|
||||||
member = Member(username)
|
member = Member(username)
|
||||||
self.members.append(member)
|
self.members.append(member)
|
||||||
return member
|
return member
|
||||||
|
|
||||||
def get_npc_reaction(self,npc_id,reaction):
|
def get_npc_reaction(self, npc_id, reaction) -> None:
|
||||||
|
"""
|
||||||
|
TODO + TODO TYPES
|
||||||
|
"""
|
||||||
if npc_id not in self.reaction_table.keys():
|
if npc_id not in self.reaction_table.keys():
|
||||||
return 0
|
return 0
|
||||||
reaction_id = self.reaction_table[npc_id][int(reaction)]
|
reaction_id = self.reaction_table[npc_id][int(reaction)]
|
||||||
return read_image(f"./truthseeker/static/images/npc/{npc_id}/{reaction_id}.png")
|
return read_image(f"./truthseeker/static/images/npc/{npc_id}/{reaction_id}.png")
|
||||||
|
|
||||||
def getPlayerResults(self,responses: dict):
|
def getPlayerResults(self, responses: dict) -> None:
|
||||||
|
"""
|
||||||
|
TODO + TODO RETTYPE
|
||||||
|
"""
|
||||||
results = {}
|
results = {}
|
||||||
try:
|
try:
|
||||||
for npc_id in responses:
|
for npc_id in responses:
|
||||||
@ -108,7 +137,12 @@ class Game:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def has_finished(self):
|
def has_finished(self) -> bool:
|
||||||
|
"""
|
||||||
|
Checks if the game has finished by checking if every Member has submitted answers
|
||||||
|
|
||||||
|
:return: True if the game has finished, else False
|
||||||
|
"""
|
||||||
for member in self.members:
|
for member in self.members:
|
||||||
if member.results == None : return False
|
if member.results == None : return False
|
||||||
return True
|
return True
|
||||||
@ -119,13 +153,12 @@ class Game:
|
|||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return self.__str__()
|
return self.__str__()
|
||||||
|
|
||||||
def create_game(owner):
|
def create_game(owner: str) -> Game:
|
||||||
"""
|
"""
|
||||||
This function creates a new game by creating a Game object and stores
|
This function creates a new game by creating a Game object and stores
|
||||||
it into the games_list dictionnary
|
it into the games_list dictionnary
|
||||||
|
|
||||||
: return : a new Game
|
:return: a new Game
|
||||||
: return type : Game
|
|
||||||
"""
|
"""
|
||||||
game = Game()
|
game = Game()
|
||||||
game.owner = owner
|
game.owner = owner
|
||||||
@ -134,32 +167,26 @@ def create_game(owner):
|
|||||||
APP.games_list[game.game_id] = game
|
APP.games_list[game.game_id] = game
|
||||||
return game
|
return game
|
||||||
|
|
||||||
def get_game(game_id):
|
def get_game(game_id: str) -> Union[Game, None]:
|
||||||
if game_id in APP.games_list:
|
"""
|
||||||
return APP.games_list[game_id]
|
Get a game from its ID
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_game_info(game_id):
|
:param game_id: the id of the game to search
|
||||||
""" if not flask.session:
|
:return: the Game object or None if not found
|
||||||
return {"error": 1, "msg": "No session"}
|
|
||||||
game = game_logic.get_game(flask.session["game_id"])
|
|
||||||
if game == None:
|
|
||||||
return {"error": 1, "msg": "this game doesn't exist"}
|
|
||||||
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 APP.games_list:
|
if game_id in APP.games_list:
|
||||||
return APP.games_list[game_id]
|
return APP.games_list[game_id]
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def check_username(username):
|
def check_username(username: str) -> bool:
|
||||||
|
"""
|
||||||
|
Check if a username is valid using a set of rules
|
||||||
|
|
||||||
|
:param username: the username to check
|
||||||
|
:return: True or False depending on if the rules are respected
|
||||||
|
"""
|
||||||
|
|
||||||
if not username:
|
if not username:
|
||||||
return False
|
return False
|
||||||
if not username.isalnum():
|
if not username.isalnum():
|
||||||
@ -178,10 +205,10 @@ def generateNpcText(npc: tables.Npc, lang: str) -> dict:
|
|||||||
data["QA_1"] = getTextFromLid(lang, getNpcRandomAnswer(npc,1).TEXT_LID)
|
data["QA_1"] = getTextFromLid(lang, getNpcRandomAnswer(npc,1).TEXT_LID)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def generateNpcReactions(npc : tables.Npc) ->list:
|
def generateNpcReactions(npc: tables.Npc) ->list:
|
||||||
return getNpcRandomTraitId(npc)
|
return getNpcRandomTraitId(npc)
|
||||||
|
|
||||||
def generatePlaceData(npcs :list, places: list, lang : str) -> dict:
|
def generatePlaceData(npcs: list, places: list, lang: str) -> dict:
|
||||||
data = {}
|
data = {}
|
||||||
random.shuffle(npcs)
|
random.shuffle(npcs)
|
||||||
for place in places:
|
for place in places:
|
||||||
@ -230,4 +257,4 @@ def getTraitIdFromString(trait):
|
|||||||
return getTraitFromText(trait)
|
return getTraitFromText(trait)
|
||||||
|
|
||||||
def get_npc_image(npc_id):
|
def get_npc_image(npc_id):
|
||||||
return read_image(f"./truthseeker/static/images/npc/{npc_id}/0.png")
|
return read_image(f"./truthseeker/static/images/npc/{npc_id}/0.png")
|
||||||
|
Loading…
Reference in New Issue
Block a user