# Players Lookup Module - Fixed Functions # Contains corrected functions that don't meet business requirements from the original players.py from discord.ext import commands from discord import app_commands import discord from typing import Optional # Import specific utilities needed by this module import logging from discord.ext import tasks from api_calls import db_get from helpers.constants import ALL_CARDSET_NAMES from helpers import ( PD_PLAYERS_ROLE_NAME, IMAGES, PD_SEASON, get_card_embeds, get_blank_team_card, get_team_by_owner, legal_channel, embed_pagination, Confirm, player_desc, is_ephemeral_channel, is_restricted_channel, can_send_message ) from helpers.search_utils import fuzzy_search, cardset_search from discord_ui import SelectUpdatePlayerTeam, SelectView logger = logging.getLogger('discord_app') class PlayerLookup(commands.Cog): """Player card display and lookup functionality for Paper Dynasty.""" def __init__(self, bot): self.bot = bot self.player_list = [] self.cardset_list = [] @tasks.loop(hours=18) # Match old frequency async def build_player_list(self): """Background task to build fuzzy player search list.""" logger.debug('Rebuilding player list for fuzzy searching') # Get players with flat=True parameter like original all_players = await db_get('players', params=[('flat', True), ('inc_dex', False)], timeout=25) all_cardsets = await db_get('cardsets', params=[('flat', True)]) if not all_players: logger.error('Failed to get players for fuzzy list') return self.player_list = [] # Build list using p_name.lower() like original, avoiding duplicates [self.player_list.append(x['p_name'].lower()) for x in all_players['players'] if x['p_name'] and x['p_name'].lower() not in self.player_list] logger.info(f'There are now {len(self.player_list)} player names in the fuzzy search list.') # Build cardset list if all_cardsets: self.cardset_list = [x['name'].lower() for x in all_cardsets['cardsets']] logger.info(f'There are now {len(self.cardset_list)} cardsets in the fuzzy search list.') @build_player_list.before_loop async def before_build_player_list(self): """Wait for bot to be ready before starting task.""" await self.bot.wait_until_ready() async def cog_load(self): """Start background tasks when cog loads.""" logger.info(f'Building player list') self.build_player_list.start() async def cog_unload(self): """Stop background tasks when cog unloads.""" self.build_player_list.cancel() @commands.command(name='player', help='For specific cardset, run /player', aliases=['show', 'card']) @commands.has_any_role(PD_PLAYERS_ROLE_NAME) @commands.check(legal_channel) async def player_command(self, ctx, *, _name_or_id): """Legacy player lookup command.""" await ctx.send('This command has been replaced by the `/player` slash command. Please use that instead!') @app_commands.command(name='player', description='Display one or more of the player\'s cards') @app_commands.checks.has_any_role(PD_PLAYERS_ROLE_NAME) async def player_slash_command( self, interaction: discord.Interaction, player_name: str, # Changed from name_or_id to match original cardset: ALL_CARDSET_NAMES = 'All', player_team: Optional[str] = None): """Display player cards with filtering options.""" ephemeral = is_ephemeral_channel(interaction.channel) await interaction.response.defer(ephemeral=ephemeral) # Use fuzzy_search like the original instead of get_close_matches this_player = fuzzy_search(player_name, self.player_list) if not this_player: await interaction.edit_original_response(content=f'No clue who that is.') return # Build params like original if cardset and cardset != 'All': this_cardset = await cardset_search(cardset, self.cardset_list) if this_cardset: all_params = [('name', this_player), ('cardset_id', this_cardset['id'])] else: await interaction.edit_original_response(content=f'I couldn\'t find {cardset} cardset.') return else: all_params = [('name', this_player)] all_players = await db_get('players', params=all_params) if not all_players or all_players.get('count', 0) == 0: await interaction.edit_original_response(content='No players found') return # Apply player_team filter if provided if player_team and all_players: filtered_players = [p for p in all_players.get('players', []) if p.get('franchise', '').upper() == player_team.upper()] all_players['players'] = filtered_players if not filtered_players: await interaction.edit_original_response(content='No players found matching your filters.') return # Create cards with blank team like original if not all_players: await interaction.edit_original_response(content='No players found') return all_cards = [get_blank_team_card(x) for x in all_players.get('players', [])] all_cards.sort(key=lambda x: x['player']['rarity']['value'], reverse=True) all_embeds = [] for x in all_cards: all_embeds.extend(await get_card_embeds(x, include_stats=True)) logger.debug(f'embeds: {all_embeds}') if len(all_embeds) > 1 and all_players and all_players.get('players'): await interaction.edit_original_response(content=f'# {all_players["players"][0]["p_name"]}') # Handle User | Member type for embed_pagination if isinstance(interaction.user, discord.Member): await embed_pagination(all_embeds, interaction.channel, interaction.user, timeout=20, start_page=0) elif interaction.guild: member = interaction.guild.get_member(interaction.user.id) if member: await embed_pagination(all_embeds, interaction.channel, member, timeout=20, start_page=0) else: # Fallback: send embeds one by one if we can't get member for embed in all_embeds[:5]: # Limit to prevent spam await interaction.followup.send(embed=embed) else: # DM context - send embeds one by one for embed in all_embeds[:5]: # Limit to prevent spam await interaction.followup.send(embed=embed) else: await interaction.edit_original_response(content=None, embed=all_embeds[0]) @app_commands.command(name='update-player', description='Update a player\'s card to a specific MLB team') @app_commands.checks.has_any_role(PD_PLAYERS_ROLE_NAME) async def update_player_team(self, interaction: discord.Interaction, player_id: int): """Update a player's MLB team affiliation.""" owner_team = await get_team_by_owner(interaction.user.id) if not owner_team: await interaction.response.send_message( 'Thank you for offering to help - if you sign up for a team with /newteam I can let you post updates.', ephemeral=True ) return if is_restricted_channel(interaction.channel): await interaction.response.send_message( f'Slide on down to #pd-bot-hole to run updates - thanks!', ephemeral=True ) return # Missing return in original causes issue await interaction.response.defer() # Use object_id parameter like original this_player = await db_get('players', object_id=player_id) if not this_player: await interaction.edit_original_response(content=f'No clue who that is.') # Use edit instead of response return # Show player card for confirmation embeds = await get_card_embeds(get_blank_team_card(this_player)) await interaction.edit_original_response(content=None, embed=embeds[0]) # Confirm this is the right player - use channel.send like original view = Confirm(responders=[interaction.user]) if can_send_message(interaction.channel): question = await interaction.channel.send( content='Is this the player you want to update?', view=view ) else: question = await interaction.followup.send( content='Is this the player you want to update?', view=view ) await view.wait() if not view.value: if question: await question.edit(content='Okay, we\'ll leave it be.', view=None) return else: if question: await question.delete() # Show team selection dropdowns - use channel.send like original view = SelectView([ SelectUpdatePlayerTeam('AL', this_player, owner_team, self.bot), SelectUpdatePlayerTeam('NL', this_player, owner_team, self.bot) ]) if can_send_message(interaction.channel): await interaction.channel.send(content='Select the new team:', view=view) else: await interaction.followup.send(content='Select the new team:', view=view) group_lookup = app_commands.Group(name='lookup', description='Lookup commands for cards and players') @group_lookup.command(name='card-id', description='Look up individual card by ID') @app_commands.checks.has_any_role(PD_PLAYERS_ROLE_NAME) async def lookup_card_by_id(self, interaction: discord.Interaction, card_id: int): """Look up a specific card by its ID.""" await interaction.response.defer() # Use object_id parameter like original c_query = await db_get('cards', object_id=card_id) if c_query: # Include ownership and pack information like original c_string = f'Card ID {card_id} is a {player_desc(c_query["player"])}' if c_query['team'] is not None: c_string += f' owned by the {c_query["team"]["sname"]}' if c_query["pack"] is not None: c_string += f' pulled from a {c_query["pack"]["pack_type"]["name"]} pack.' else: c_query['team'] = c_query["pack"]["team"] c_string += f' used by the {c_query["pack"]["team"]["sname"]} in a gauntlet' await interaction.edit_original_response( content=c_string, embeds=await get_card_embeds(c_query) # Pass card directly, not wrapped ) return await interaction.edit_original_response(content=f'There is no card with ID {card_id}') @group_lookup.command(name='player-id', description='Look up an individual player by ID') @app_commands.checks.has_any_role(PD_PLAYERS_ROLE_NAME) async def lookup_player_by_id(self, interaction: discord.Interaction, player_id: int): """Look up a player by their ID.""" await interaction.response.defer() # Use object_id parameter like original p_query = await db_get('players', object_id=player_id) if p_query: p_card = get_blank_team_card(p_query) embeds = await get_card_embeds(p_card) if embeds: # Original doesn't handle multiple embeds for single player lookup await interaction.edit_original_response( content=None, embeds=embeds # Pass all embeds like original ) else: await interaction.edit_original_response(content='Could not generate card display for this player') return await interaction.edit_original_response(content=f'There is no player with ID {player_id}.') @commands.hybrid_command(name='random', help='Check out a random card') # Use hybrid_command like original @commands.has_any_role(PD_PLAYERS_ROLE_NAME) @commands.check(legal_channel) async def random_card_command(self, ctx: commands.Context): # Use Context instead of Interaction """Display a random player card.""" p_query = await db_get('players/random', params=[('limit', 1)]) if not p_query or not p_query.get('count'): await ctx.send('Could not find any random players') return this_player = p_query['players'][0] # Use blank team card structure like original this_embed = await get_card_embeds( {'player': this_player, 'team': {'lname': 'Paper Dynasty', 'logo': IMAGES['logo'], 'season': PD_SEASON}} ) await ctx.send(content=None, embeds=this_embed) async def setup(bot): """Setup function for the PlayerLookup cog.""" await bot.add_cog(PlayerLookup(bot))