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