33import json
44from contextlib import suppress
55from pathlib import Path
6- from typing import Literal , NamedTuple , NotRequired , TypedDict
6+ from typing import Literal , NotRequired , TypedDict
77
88from discord import Embed , HTTPException , Message , Reaction , User
99from discord .ext import commands
10- from discord .ext .commands import Cog as DiscordCog , Command , Context
10+ from discord .ext .commands import Cog as DiscordCog , Context
1111from pydis_core .utils .logging import get_logger
1212
1313from bot import constants
1414from bot .bot import Bot
1515
16-
17- class Cog (NamedTuple ):
18- """Show information about a Cog's name, description and commands."""
19-
20- name : str
21- description : str
22- commands : list [Command ]
23-
24-
2516log = get_logger (__name__ )
2617
27-
2818class GameInfo (TypedDict ):
2919 """A dictionary containing the game information. Used in `available_games.json`."""
3020
@@ -116,8 +106,8 @@ def __init__(
116106 self .game_info = None
117107 if game_code_or_index :
118108 self .game_code = self ._parse_game_code (game_code_or_index )
119- self .game_data = self ._get_game_data ()
120109 self .game_info = self ._get_game_info ()
110+ self .game_data = self ._get_game_data ()
121111
122112 # store relevant discord info
123113 self .author = ctx .author
@@ -142,10 +132,13 @@ def _parse_game_code(self, game_code_or_index: str) -> str:
142132 # sanitize the game code to prevent directory traversal attacks.
143133 game_code = Path (game_code_or_index ).name
144134
145- # convert index to game code if it's a number
135+ # convert index to game code if it's a valid number that is in range.
136+ # otherwise, return the game code as is, assuming it's a valid game code.
137+ # if game code is not valid, errors will be raised later when trying to load the game info.
146138 try :
147139 index = int (game_code_or_index )
148- game_code = AVAILABLE_GAMES [index - 1 ]["id" ]
140+ if 1 <= index <= len (AVAILABLE_GAMES ):
141+ game_code = AVAILABLE_GAMES [index - 1 ]["id" ]
149142 except (ValueError , IndexError ):
150143 pass
151144
@@ -162,7 +155,11 @@ def _get_game_data(self) -> GameData | None:
162155 )
163156 return game_data
164157 except FileNotFoundError :
165- raise GameCodeNotFoundError (f'Game code "{ game_code } " not found.' )
158+ log .error (
159+ "Game located in `available_games.json`, but game data not found. Game code: %s" ,
160+ game_code
161+ )
162+ raise GameCodeNotFoundError (f"Game code `{ game_code } ` not found." )
166163
167164 def _get_game_info (self ) -> GameInfo :
168165 """Returns the game info for the given game code."""
@@ -171,10 +168,7 @@ def _get_game_info(self) -> GameInfo:
171168 try :
172169 return AVAILABLE_GAMES_DICT [game_code ]
173170 except KeyError :
174- log .error (
175- "Game data retrieved, but game info not found. Did you forget to add it to `available_games.json`?"
176- )
177- raise GameCodeNotFoundError (f'Game code "{ game_code } " not found.' )
171+ raise GameCodeNotFoundError (f"Game code `{ game_code } ` not found." )
178172
179173 async def notify_timeout (self ) -> None :
180174 """Notifies the user that the session has timed out."""
@@ -260,15 +254,15 @@ async def prepare(self) -> None:
260254 await self .send_available_game_codes ()
261255
262256
263- def add_reactions (self ) -> None :
257+ async def add_reactions (self ) -> None :
264258 """Adds the relevant reactions to the message based on if options are available in the current room."""
265259 if self .is_in_ending_room :
266260 return
267261
268262 pickable_emojis = [option ["emoji" ] for option in self .available_options ]
269263
270264 for reaction in pickable_emojis :
271- self ._bot . loop . create_task ( self . message .add_reaction (reaction ) )
265+ await self .message .add_reaction (reaction )
272266
273267 def _format_room_data (self , room_data : RoomData ) -> str :
274268 """Formats the room data into a string for the embed description."""
@@ -315,7 +309,7 @@ async def update_message(self, room_id: str) -> None:
315309 if self .is_in_ending_room :
316310 await self .stop ()
317311 else :
318- self .add_reactions ()
312+ await self .add_reactions ()
319313
320314 @classmethod
321315 async def start (cls , ctx : Context , game_code_or_index : str | None = None ) -> "GameSession" :
0 commit comments