diff --git a/README.md b/README.md index e38c101..c753add 100644 --- a/README.md +++ b/README.md @@ -9,13 +9,8 @@ Features: ToDo: -- API Key section - - Have user enter their own API key, pass along to the functions that call APIs -- server side config storage - - have multiple managers logged in to make changes - basic auth - since control of the Discord bot would also be through there technically -- add status description to member cards +- Log output section on webpage, to see who is being assigned to who, when the hit is complete, if it is missed, how many hits a person has done +- Hit Leaderboard? - -For now let's pivot to the Discord Bot functionality. What the bot is going to do is for each battle group it needs to assign a friendly member to an enemy member. That friendly will then get a ping in Discord by pulling the list of Discord users and matching the player id to the user in the Discord server. It will ping them and say "New target for @user , attack (link to enemy profile) in the next 30 seconds!" If the enemies status does not change to "In Hospital" in the next 30 seconds that enemy will be assigned to the next player in the group that has not received a hit yet. We will also need to keep track of how many hits a friendly has completed. That way if a new friendly enters a pool they will get a chance to attack before the ones that have not had a chance. We also need a button on the webpage to start and stop the bot \ No newline at end of file diff --git a/__pycache__/config.cpython-311.pyc b/__pycache__/config.cpython-311.pyc index 79536c3..8c658f4 100644 Binary files a/__pycache__/config.cpython-311.pyc and b/__pycache__/config.cpython-311.pyc differ diff --git a/services/__pycache__/bot_assignment.cpython-311.pyc b/services/__pycache__/bot_assignment.cpython-311.pyc index c33ff03..afce625 100644 Binary files a/services/__pycache__/bot_assignment.cpython-311.pyc and b/services/__pycache__/bot_assignment.cpython-311.pyc differ diff --git a/services/bot_assignment.py b/services/bot_assignment.py index e6de6ab..130a973 100644 --- a/services/bot_assignment.py +++ b/services/bot_assignment.py @@ -102,13 +102,21 @@ class BotAssignmentManager: def get_next_enemy_in_group(self, group_id: str, enemy_ids: list) -> Optional[int]: """ Get the next enemy in the group who needs to be assigned. - Returns None if all enemies are already assigned. + Returns None if all enemies are already assigned or not attackable. """ for eid in enemy_ids: key = f"{group_id}:{eid}" - # If enemy is not currently assigned, return it - if key not in self.active_targets: - return eid + # If enemy is already assigned, skip them + if key in self.active_targets: + continue + + # Check if enemy is attackable (status must be "Okay") + enemy = STATE.enemy.get(eid) + if not enemy or enemy.status.lower() != "okay": + continue + + # This enemy is available for assignment + return eid return None async def assignment_loop(self): @@ -176,18 +184,30 @@ class BotAssignmentManager: print(f"Cannot assign: friendly {friendly_id} or enemy {enemy_id} not found") return + # Only assign if enemy status is "Okay" (not traveling, hospitalized, etc.) + if enemy.status.lower() != "okay": + print(f"Skipping assignment: {enemy.name} status is '{enemy.status}' (must be 'Okay')") + return + # Get Discord user discord_id = self.get_discord_id(friendly_id) if not discord_id: - print(f"No Discord mapping for Torn ID {friendly_id}") + print(f"No Discord mapping for Torn ID {friendly_id} - skipping assignment") + # Record assignment to prevent infinite retries + key = f"{group_id}:{enemy_id}" + self.active_targets[key] = { + "group_id": group_id, + "friendly_id": friendly_id, + "enemy_id": enemy_id, + "discord_id": None, + "assigned_at": datetime.now(), + "reminded": False, + "failed": True + } return - discord_user = await self.bot.fetch_user(discord_id) - if not discord_user: - print(f"Discord user {discord_id} not found") - return - - # Record assignment + # Record assignment BEFORE attempting to send message + # This prevents infinite retries if message sending fails key = f"{group_id}:{enemy_id}" self.active_targets[key] = { "group_id": group_id, @@ -195,22 +215,32 @@ class BotAssignmentManager: "enemy_id": enemy_id, "discord_id": discord_id, "assigned_at": datetime.now(), - "reminded": False + "reminded": False, + "failed": False } - # Send Discord message to channel - attack_link = f"https://www.torn.com/loader.php?sid=attack&user2ID={enemy_id}" - message = f"**New target for {discord_user.mention}!**\n\n[**{enemy.name}** (Level {enemy.level})]({attack_link})\n\nYou have {ASSIGNMENT_TIMEOUT} seconds!" - + # Fetch Discord user and send message try: + discord_user = await self.bot.fetch_user(discord_id) + if not discord_user: + print(f"Discord user {discord_id} not found") + self.active_targets[key]["failed"] = True + return + + # Send Discord message to channel + attack_link = f"https://www.torn.com/loader.php?sid=attack&user2ID={enemy_id}" + message = f"**New target for {discord_user.mention}!**\n\n[**{enemy.name}** (Level {enemy.level})]({attack_link})\n\nYou have {ASSIGNMENT_TIMEOUT} seconds!" + channel = self.bot.get_channel(ALLOWED_CHANNEL_ID) if channel: await channel.send(message) print(f"Assigned {enemy.name} to {friendly.name} (Discord: {discord_user.name})") else: print(f"Assignment channel {ALLOWED_CHANNEL_ID} not found") + self.active_targets[key]["failed"] = True except Exception as e: print(f"Failed to send Discord message to channel: {e}") + self.active_targets[key]["failed"] = True async def monitor_active_targets(self): """Monitor active targets for status changes or timeouts""" @@ -220,12 +250,24 @@ class BotAssignmentManager: for key, data in list(self.active_targets.items()): elapsed = (now - data["assigned_at"]).total_seconds() + # Remove failed assignments after a short delay (don't reassign them) + if data.get("failed", False): + if elapsed >= 30: # Clean up after 30 seconds + print(f"Removing failed assignment: {key}") + del self.active_targets[key] + continue + # Check enemy status enemy_id = data["enemy_id"] enemy = STATE.enemy.get(enemy_id) - if enemy and "hospital" in enemy.status.lower(): - # Enemy is hospitalized - success! + if not enemy: + # Enemy no longer exists, remove assignment + del self.active_targets[key] + continue + + # Check if enemy is hospitalized (success!) + if "hospital" in enemy.status.lower(): friendly_id = data["friendly_id"] if friendly_id in STATE.friendly: # Increment hit count @@ -236,7 +278,13 @@ class BotAssignmentManager: del self.active_targets[key] continue - # Send reminder + # Check if enemy is no longer attackable (traveling, etc.) + if enemy.status.lower() != "okay": + print(f"Target {enemy.name} is now '{enemy.status}' - removing assignment") + del self.active_targets[key] + continue + + # Send reminder (only for successful assignments) if elapsed >= ASSIGNMENT_REMINDER and not data["reminded"]: discord_id = data["discord_id"] try: