Created get_context_user() helper function to safely extract the user from either Context or Interaction objects. This prevents AttributeError issues when hybrid commands are invoked as slash commands. Hybrid commands receive commands.Context (with .author) when invoked with prefix commands, but discord.Interaction (with .user) when invoked as slash commands. The helper function handles both cases transparently. Updated all affected hybrid commands: - /branding-pd (cogs/players.py, cogs/players_new/team_management.py) - /pullroster (cogs/players.py, cogs/players_new/team_management.py) - /newsheet (cogs/economy_new/team_setup.py) - /lastpack (cogs/economy_new/packs.py) This follows the same pattern as the owner_only() fix and provides a consistent, maintainable solution for all hybrid commands. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
170 lines
5.5 KiB
Python
170 lines
5.5 KiB
Python
"""
|
|
General Utilities
|
|
|
|
This module contains standalone utility functions with minimal dependencies,
|
|
including timestamp conversion, position abbreviations, and simple helpers.
|
|
"""
|
|
import datetime
|
|
from typing import Optional
|
|
import discord
|
|
|
|
|
|
def int_timestamp(datetime_obj: Optional[datetime.datetime] = None):
|
|
"""Convert current datetime to integer timestamp."""
|
|
if datetime_obj:
|
|
return int(datetime.datetime.timestamp(datetime_obj) * 1000)
|
|
return int(datetime.datetime.now().timestamp())
|
|
|
|
|
|
def get_pos_abbrev(field_pos: str) -> str:
|
|
"""Convert position name to standard abbreviation."""
|
|
if field_pos.lower() == 'catcher':
|
|
return 'C'
|
|
elif field_pos.lower() == 'first baseman':
|
|
return '1B'
|
|
elif field_pos.lower() == 'second baseman':
|
|
return '2B'
|
|
elif field_pos.lower() == 'third baseman':
|
|
return '3B'
|
|
elif field_pos.lower() == 'shortstop':
|
|
return 'SS'
|
|
elif field_pos.lower() == 'left fielder':
|
|
return 'LF'
|
|
elif field_pos.lower() == 'center fielder':
|
|
return 'CF'
|
|
elif field_pos.lower() == 'right fielder':
|
|
return 'RF'
|
|
else:
|
|
return 'P'
|
|
|
|
|
|
def position_name_to_abbrev(position_name):
|
|
"""Convert position name to abbreviation (alternate format)."""
|
|
if position_name == 'Catcher':
|
|
return 'C'
|
|
elif position_name == 'First Base':
|
|
return '1B'
|
|
elif position_name == 'Second Base':
|
|
return '2B'
|
|
elif position_name == 'Third Base':
|
|
return '3B'
|
|
elif position_name == 'Shortstop':
|
|
return 'SS'
|
|
elif position_name == 'Left Field':
|
|
return 'LF'
|
|
elif position_name == 'Center Field':
|
|
return 'CF'
|
|
elif position_name == 'Right Field':
|
|
return 'RF'
|
|
elif position_name == 'Pitcher':
|
|
return 'P'
|
|
else:
|
|
return position_name
|
|
|
|
|
|
def user_has_role(user: discord.User | discord.Member, role_name: str) -> bool:
|
|
"""Check if a Discord user has a specific role."""
|
|
for x in user.roles:
|
|
if x.name == role_name:
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
def get_roster_sheet_legacy(team):
|
|
"""Get legacy roster sheet URL for a team."""
|
|
return f'https://docs.google.com/spreadsheets/d/{team.gsheet}/edit'
|
|
|
|
|
|
def get_roster_sheet(team):
|
|
"""Get roster sheet URL for a team."""
|
|
return f'https://docs.google.com/spreadsheets/d/{team["gsheet"]}/edit'
|
|
|
|
|
|
def get_player_url(team, player) -> str:
|
|
"""Generate player URL for SBA or Baseball Reference."""
|
|
if team.get('league') == 'SBA':
|
|
return f'https://statsplus.net/super-baseball-association/player/{player["player_id"]}'
|
|
else:
|
|
return f'https://www.baseball-reference.com/players/{player["bbref_id"][0]}/{player["bbref_id"]}.shtml'
|
|
|
|
|
|
def owner_only(ctx) -> bool:
|
|
"""Check if user is the bot owner."""
|
|
# ID for discord User Cal
|
|
owners = [287463767924137994, 1087936030899347516]
|
|
|
|
# Handle both Context (has .author) and Interaction (has .user) objects
|
|
user = getattr(ctx, 'user', None) or getattr(ctx, 'author', None)
|
|
|
|
if user and user.id in owners:
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
def get_context_user(ctx):
|
|
"""
|
|
Get the user from either a Context or Interaction object.
|
|
|
|
Hybrid commands can receive either commands.Context (from prefix commands)
|
|
or discord.Interaction (from slash commands). This helper safely extracts
|
|
the user from either type.
|
|
|
|
Returns:
|
|
discord.User or discord.Member: The user who invoked the command
|
|
"""
|
|
# Handle both Context (has .author) and Interaction (has .user) objects
|
|
return getattr(ctx, 'user', None) or getattr(ctx, 'author', None)
|
|
|
|
|
|
def get_cal_user(ctx):
|
|
"""Get the Cal user from context. Always returns an object with .mention attribute."""
|
|
import logging
|
|
logger = logging.getLogger('discord_app')
|
|
|
|
# Define placeholder user class first
|
|
class PlaceholderUser:
|
|
def __init__(self):
|
|
self.mention = "<@287463767924137994>"
|
|
self.id = 287463767924137994
|
|
|
|
# Handle both Context and Interaction objects
|
|
if hasattr(ctx, 'bot'): # Context object
|
|
bot = ctx.bot
|
|
logger.debug("get_cal_user: Using Context object")
|
|
elif hasattr(ctx, 'client'): # Interaction object
|
|
bot = ctx.client
|
|
logger.debug("get_cal_user: Using Interaction object")
|
|
else:
|
|
logger.error("get_cal_user: No bot or client found in context")
|
|
return PlaceholderUser()
|
|
|
|
if not bot:
|
|
logger.error("get_cal_user: bot is None")
|
|
return PlaceholderUser()
|
|
|
|
logger.debug(f"get_cal_user: Searching among members")
|
|
try:
|
|
for user in bot.get_all_members():
|
|
if user.id == 287463767924137994:
|
|
logger.debug("get_cal_user: Found user in get_all_members")
|
|
return user
|
|
except Exception as e:
|
|
logger.error(f"get_cal_user: Exception in get_all_members: {e}")
|
|
|
|
# Fallback: try to get user directly by ID
|
|
logger.debug("get_cal_user: User not found in get_all_members, trying get_user")
|
|
try:
|
|
user = bot.get_user(287463767924137994)
|
|
if user:
|
|
logger.debug("get_cal_user: Found user via get_user")
|
|
return user
|
|
else:
|
|
logger.debug("get_cal_user: get_user returned None")
|
|
except Exception as e:
|
|
logger.error(f"get_cal_user: Exception in get_user: {e}")
|
|
|
|
# Last resort: return a placeholder user object with mention
|
|
logger.debug("get_cal_user: Using placeholder user")
|
|
return PlaceholderUser() |