async def error_handler(update: types.Update, error: TelegramAPIError) -> None: for game in GAMES.values(): asyncio.create_task(game.scan_for_stale_timer()) if isinstance(error, MigrateToChat): if update.message.chat.id in GAMES: GAMES[error.migrate_to_chat_id] = GAMES.pop(update.message.chat.id) GAMES[error.migrate_to_chat_id].group_id = error.migrate_to_chat_id async with pool.acquire() as conn: await conn.execute( "UPDATE game SET group_id = $1 WHERE group_id = $2;\n" "UPDATE gameplayer SET group_id = $1 WHERE group_id = $2;\n" "DELETE FROM game WHERE group_id = $2;\n" "DELETE FROM gameplayer WHERE group_id = $2;", error.migrate_to_chat_id, update.message.chat.id) return await bot.send_message( ADMIN_GROUP_ID, f"`{error.__class__.__name__} @ " f"{update.message.chat.id if update.message and update.message.chat else 'idk'}`:\n" f"`{str(error)}`") if not update.message or not update.message.chat: return try: await update.message.reply( "Error occurred. My owner has been notified.") except TelegramAPIError: pass if update.message.chat.id in GAMES: GAMES[update.message.chat.id].state = GameState.KILLGAME await asyncio.sleep(2) try: del GAMES[update.message.chat.id] await update.message.reply("Game ended forcibly.") except: pass
async def main_loop(self, message: types.Message) -> None: # Attempt to fix issue of stuck game with negative timer. negative_timer = 0 try: await self.send_message( f"A{'n' if self.name[0] in 'aeiou' else ''} {self.name} is starting.\n" f"{self.min_players}-{self.max_players} players are needed.\n" f"{self.time_left}s to /join.") await self.join(message) while True: await asyncio.sleep(1) if self.state == GameState.JOINING: if self.time_left > 0: self.time_left -= 1 if self.time_left in (15, 30, 60): await self.send_message( f"{self.time_left}s left to /join.") else: if len(self.players) < self.min_players: await self.send_message( "Not enough players. Game terminated.") del GAMES[self.group_id] return else: self.state = GameState.RUNNING await self.send_message("Game is starting...") random.shuffle(self.players) self.players_in_game = self.players[:] await self.running_initialization() await self.send_turn_message() elif self.state == GameState.RUNNING: # Check for prolonged negative timer if self.time_left < 0: negative_timer += 1 if negative_timer >= 5: raise ValueError("Prolonged negative timer.") if await self.running_phase_tick(): # True: Game ended await self.update_db() return elif self.state == GameState.KILLGAME: await self.send_message("Game ended forcibly.") del GAMES[self.group_id] return except Exception as e: GAMES.pop(self.group_id, None) try: await self.send_message( f"Game ended due to the following error:\n`{e.__class__.__name__}: {e}`.\n" "My owner will be notified.") except: pass raise
async def error_handler(update: types.Update, error: TelegramAPIError) -> None: for game in GAMES.values( ): # TODO: Do this for group in which error occurs only asyncio.create_task(game.scan_for_stale_timer()) if isinstance(error, MigrateToChat): if update.message.chat.id in GAMES: # TODO: Test old_gid = GAMES[update.message.chat.id].group_id GAMES[error.migrate_to_chat_id] = GAMES.pop(update.message.chat.id) GAMES[error.migrate_to_chat_id].group_id = error.migrate_to_chat_id asyncio.create_task( send_admin_group( f"Game moved from {old_gid} to {error.migrate_to_chat_id}." )) async with pool.acquire() as conn: await conn.execute( """\ UPDATE game SET group_id = $1 WHERE group_id = $2; UPDATE gameplayer SET group_id = $1 WHERE group_id = $2; DELETE FROM game WHERE group_id = $2; DELETE FROM gameplayer WHERE group_id = $2;""", error.migrate_to_chat_id, update.message.chat.id, ) await send_admin_group(f"Group migrated to {error.migrate_to_chat_id}." ) return send_admin_msg = await send_admin_group( f"`{error.__class__.__name__} @ " f"{update.message.chat.id if update.message and update.message.chat else 'idk'}`:\n" f"`{str(error)}`", ) if not update.message or not update.message.chat: return try: await update.message.reply( "Error occurred. My owner has been notified.") except TelegramAPIError: pass if update.message.chat.id in GAMES: asyncio.create_task( send_admin_msg.reply( f"Killing game in {update.message.chat.id} consequently.")) GAMES[update.message.chat.id].state = GameState.KILLGAME await asyncio.sleep(2) try: del GAMES[update.message.chat.id] await update.message.reply("Game ended forcibly.") except: pass
async def scan_for_stale_timer(self) -> None: # Check if game timer is stuck timer = self.time_left for _ in range(5): await asyncio.sleep(1) if timer != self.time_left and timer >= 0: return # Timer not stuck await send_admin_group( f"Prolonged stale/negative timer detected in group `{self.group_id}`. Game Terminated." ) try: await self.send_message( "Game timer is malfunctioning. Game terminated.") except: pass GAMES.pop(self.group_id, None)