paper-dynasty-discord/cogs/players/player_lookup.py
2025-08-17 08:46:55 -05:00

295 lines
13 KiB
Python

# 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))