CLAUDE: Add get_context_user() helper for hybrid command compatibility
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>
This commit is contained in:
parent
c0f9466e54
commit
943dcc9b74
@ -13,7 +13,7 @@ from api_calls import db_get, db_post, db_patch
|
||||
from helpers.constants import PD_PLAYERS_ROLE_NAME, PD_PLAYERS, IMAGES
|
||||
from helpers import (
|
||||
get_team_by_owner, display_cards, give_packs, legal_channel, get_channel,
|
||||
get_cal_user, refresh_sheet, roll_for_cards, int_timestamp
|
||||
get_cal_user, refresh_sheet, roll_for_cards, int_timestamp, get_context_user
|
||||
)
|
||||
from helpers.discord_utils import get_team_embed, send_to_channel, get_emoji
|
||||
from discord_ui import SelectView, SelectOpenPack
|
||||
@ -68,7 +68,7 @@ class Packs(commands.Cog):
|
||||
@commands.check(legal_channel)
|
||||
@commands.has_any_role(PD_PLAYERS_ROLE_NAME)
|
||||
async def last_pack_command(self, ctx: commands.Context):
|
||||
team = await get_team_by_owner(ctx.author.id)
|
||||
team = await get_team_by_owner(get_context_user(ctx).id)
|
||||
if not team:
|
||||
await ctx.send(f'I don\'t see a team for you, yet. You can sign up with the `/newteam` command!')
|
||||
return
|
||||
|
||||
@ -18,7 +18,7 @@ from helpers import (
|
||||
get_team_by_owner, share_channel, get_role, get_cal_user, get_or_create_role,
|
||||
display_cards, give_packs, get_all_pos, get_sheets, refresh_sheet,
|
||||
post_ratings_guide, team_summary_embed, get_roster_sheet, Question, Confirm,
|
||||
ButtonOptions, legal_channel, get_channel, create_channel
|
||||
ButtonOptions, legal_channel, get_channel, create_channel, get_context_user
|
||||
)
|
||||
from api_calls import team_hash
|
||||
from helpers.discord_utils import get_team_embed, send_to_channel
|
||||
@ -395,14 +395,14 @@ class TeamSetup(commands.Cog):
|
||||
@commands.has_any_role(PD_PLAYERS)
|
||||
async def share_sheet_command(
|
||||
self, ctx, google_sheet_url: str, team_abbrev: Optional[str], copy_rosters: Optional[bool] = True):
|
||||
owner_team = await get_team_by_owner(ctx.author.id)
|
||||
owner_team = await get_team_by_owner(get_context_user(ctx).id)
|
||||
if not owner_team:
|
||||
await ctx.send(f'I don\'t see a team for you, yet. You can sign up with the `/newteam` command!')
|
||||
return
|
||||
team = owner_team
|
||||
|
||||
if team_abbrev and team_abbrev != owner_team['abbrev']:
|
||||
if ctx.author.id != 258104532423147520:
|
||||
if get_context_user(ctx).id != 258104532423147520:
|
||||
await ctx.send(f'You can only update the team sheet for your own team, you goober.')
|
||||
return
|
||||
else:
|
||||
|
||||
@ -31,7 +31,7 @@ from api_calls import db_get, db_post, db_patch, get_team_by_abbrev
|
||||
from helpers import ACTIVE_EVENT_LITERAL, PD_PLAYERS_ROLE_NAME, IMAGES, PD_SEASON, random_conf_gif, fuzzy_player_search, ALL_MLB_TEAMS, \
|
||||
fuzzy_search, get_channel, display_cards, get_card_embeds, get_team_embed, cardset_search, get_blank_team_card, \
|
||||
get_team_by_owner, get_rosters, get_roster_sheet, legal_channel, random_conf_word, embed_pagination, get_cal_user, \
|
||||
team_summary_embed, SelectView, SelectPaperdexCardset, SelectPaperdexTeam
|
||||
team_summary_embed, SelectView, SelectPaperdexCardset, SelectPaperdexTeam, get_context_user
|
||||
from utilities.buttons import ask_with_buttons
|
||||
from utilities.autocomplete import cardset_autocomplete, player_autocomplete
|
||||
|
||||
@ -660,7 +660,7 @@ class Players(commands.Cog):
|
||||
@commands.check(legal_channel)
|
||||
async def branding_command(
|
||||
self, ctx, team_logo_url: str = None, color: str = None, short_name: str = None, full_name: str = None):
|
||||
owner_team = await get_team_by_owner(ctx.author.id)
|
||||
owner_team = await get_team_by_owner(get_context_user(ctx).id)
|
||||
if not owner_team:
|
||||
await ctx.send(f'Hmm...I don\'t see a team for you, yet. You can create one with `/newteam`!')
|
||||
return
|
||||
@ -869,7 +869,7 @@ class Players(commands.Cog):
|
||||
@commands.has_any_role(PD_PLAYERS_ROLE_NAME)
|
||||
@commands.check(legal_channel)
|
||||
async def pull_roster_command(self, ctx: commands.Context, specific_roster_num: Optional[int] = None):
|
||||
team = await get_team_by_owner(ctx.author.id)
|
||||
team = await get_team_by_owner(get_context_user(ctx).id)
|
||||
if not team:
|
||||
await ctx.send(f'Do you even have a team? I don\'t know you.')
|
||||
return
|
||||
|
||||
@ -13,9 +13,10 @@ import requests
|
||||
from api_calls import db_get, db_post, db_patch, get_team_by_abbrev
|
||||
from helpers import (
|
||||
PD_PLAYERS_ROLE_NAME, IMAGES, get_channel, get_team_embed,
|
||||
get_blank_team_card, get_team_by_owner, get_rosters,
|
||||
get_blank_team_card, get_team_by_owner, get_rosters,
|
||||
legal_channel, team_summary_embed, SelectView,
|
||||
is_ephemeral_channel, is_restricted_channel, can_send_message
|
||||
is_ephemeral_channel, is_restricted_channel, can_send_message,
|
||||
get_context_user
|
||||
)
|
||||
import helpers
|
||||
from helpers.utils import get_roster_sheet
|
||||
@ -136,11 +137,11 @@ class TeamManagement(commands.Cog):
|
||||
@commands.hybrid_command(name='branding-pd', help='Update your team branding')
|
||||
@commands.has_any_role(PD_PLAYERS_ROLE_NAME)
|
||||
@commands.check(legal_channel)
|
||||
async def branding_command(self, ctx, team_logo_url: str = None, color: str = None,
|
||||
async def branding_command(self, ctx, team_logo_url: str = None, color: str = None,
|
||||
short_name: str = None, full_name: str = None, *, branding_updates: Optional[str] = None):
|
||||
"""Update team branding (logo, color, etc.)."""
|
||||
|
||||
team = await get_team_by_owner(ctx.author.id)
|
||||
|
||||
team = await get_team_by_owner(get_context_user(ctx).id)
|
||||
if not team:
|
||||
await ctx.send('You don\'t have a team yet! Use `/newteam` to create one.')
|
||||
return
|
||||
@ -242,11 +243,11 @@ class TeamManagement(commands.Cog):
|
||||
)
|
||||
@commands.has_any_role(PD_PLAYERS_ROLE_NAME)
|
||||
@commands.check(legal_channel)
|
||||
async def pull_roster_command(self, ctx, specific_roster_num: Optional[int] = None,
|
||||
async def pull_roster_command(self, ctx, specific_roster_num: Optional[int] = None,
|
||||
roster_name: Optional[str] = None):
|
||||
"""Pull and display saved rosters from Google Sheets."""
|
||||
|
||||
team = await get_team_by_owner(ctx.author.id)
|
||||
|
||||
team = await get_team_by_owner(get_context_user(ctx).id)
|
||||
if not team:
|
||||
await ctx.send('You don\'t have a team yet! Use `/newteam` to create one.')
|
||||
return
|
||||
|
||||
@ -22,7 +22,7 @@ from in_game.gameplay_models import Team
|
||||
from constants import *
|
||||
from discord_ui import *
|
||||
from random_content import *
|
||||
from utils import position_name_to_abbrev, user_has_role, get_roster_sheet_legacy, get_roster_sheet, get_player_url, owner_only, get_cal_user
|
||||
from utils import position_name_to_abbrev, user_has_role, get_roster_sheet_legacy, get_roster_sheet, get_player_url, owner_only, get_cal_user, get_context_user
|
||||
from search_utils import *
|
||||
from discord_utils import *
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@ from in_game.gameplay_models import Team
|
||||
from constants import *
|
||||
from discord_ui import *
|
||||
from random_content import *
|
||||
from utils import position_name_to_abbrev, user_has_role, get_roster_sheet_legacy, get_roster_sheet, get_player_url, owner_only, get_cal_user
|
||||
from utils import position_name_to_abbrev, user_has_role, get_roster_sheet_legacy, get_roster_sheet, get_player_url, owner_only, get_cal_user, get_context_user
|
||||
from search_utils import *
|
||||
from discord_utils import *
|
||||
|
||||
|
||||
@ -103,6 +103,21 @@ def owner_only(ctx) -> bool:
|
||||
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
|
||||
|
||||
15
utils.py
15
utils.py
@ -100,6 +100,21 @@ def owner_only(ctx) -> bool:
|
||||
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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user