This commit addresses critical bugs in the injury command system and establishes best practices for Discord command groups. ## Critical Fixes ### 1. GroupCog → app_commands.Group Migration - **Problem**: `commands.GroupCog` has a duplicate interaction processing bug causing "404 Unknown interaction" errors when deferring responses - **Root Cause**: GroupCog triggers command handler twice, consuming the interaction token before the second execution can respond - **Solution**: Migrated InjuryCog to InjuryGroup using `app_commands.Group` pattern (same as ChartManageGroup and ChartCategoryGroup) - **Result**: Reliable command execution, no more 404 errors ### 2. GiphyService GIF URL Fix - **Problem**: Giphy service returned web page URLs (https://giphy.com/gifs/...) instead of direct image URLs, preventing Discord embed display - **Root Cause**: Code accessed `data.url` instead of `data.images.original.url` - **Solution**: Updated both `get_disappointment_gif()` and `get_gif()` methods to use correct API response path for embeddable GIF URLs - **Result**: GIFs now display correctly in Discord embeds ## Documentation ### Command Groups Best Practices (commands/README.md) Added comprehensive section documenting: - **Critical Warning**: Never use `commands.GroupCog` - use `app_commands.Group` - **Technical Explanation**: Why GroupCog fails (duplicate execution bug) - **Migration Guide**: Step-by-step conversion from GroupCog to Group - **Comparison Table**: Key differences between the two approaches - **Working Examples**: References to ChartManageGroup, InjuryGroup patterns ## Architecture Changes ### Injury Commands (`commands/injuries/`) - Converted from `commands.GroupCog` to `app_commands.Group` - Registration via `bot.tree.add_command()` instead of `bot.add_cog()` - Removed workarounds for GroupCog duplicate interaction issues - Clean defer/response pattern with `@logged_command` decorator ### GiphyService (`services/giphy_service.py`) - Centralized from `commands/soak/giphy_service.py` - Now returns direct GIF image URLs for Discord embeds - Maintains Trump GIF filtering (legacy behavior) - Added gif_url to log output for debugging ### Configuration (`config.py`) - Added `giphy_api_key` and `giphy_translate_url` settings - Environment variable support via `GIPHY_API_KEY` - Default values provided for out-of-box functionality ## Files Changed - commands/injuries/: New InjuryGroup with app_commands.Group pattern - services/giphy_service.py: Centralized service with GIF URL fix - commands/soak/giphy_service.py: Backwards compatibility wrapper - commands/README.md: Command groups best practices documentation - config.py: Giphy configuration settings - services/__init__.py: GiphyService exports 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
85 lines
2.5 KiB
Python
85 lines
2.5 KiB
Python
"""
|
|
Giphy Service Wrapper for Soak Commands
|
|
|
|
This module provides backwards compatibility for existing soak commands
|
|
by re-exporting functions from the centralized GiphyService in services/.
|
|
|
|
All new code should import from services.giphy_service instead.
|
|
"""
|
|
from services import giphy_service
|
|
|
|
# Re-export tier configuration for backwards compatibility
|
|
from services.giphy_service import DISAPPOINTMENT_TIERS
|
|
|
|
|
|
def get_tier_for_seconds(seconds_elapsed):
|
|
"""
|
|
Determine disappointment tier based on elapsed time.
|
|
|
|
This is a wrapper function for backwards compatibility.
|
|
Use services.giphy_service.GiphyService.get_tier_for_seconds() directly in new code.
|
|
|
|
Args:
|
|
seconds_elapsed: Seconds since last soak, or None for first ever
|
|
|
|
Returns:
|
|
Tier key string (e.g., 'tier_1', 'first_ever')
|
|
"""
|
|
return giphy_service.get_tier_for_seconds(seconds_elapsed)
|
|
|
|
|
|
def get_random_phrase_for_tier(tier_key):
|
|
"""
|
|
Get a random search phrase from the specified tier.
|
|
|
|
This is a wrapper function for backwards compatibility.
|
|
Use services.giphy_service.GiphyService.get_random_phrase_for_tier() directly in new code.
|
|
|
|
Args:
|
|
tier_key: Tier identifier (e.g., 'tier_1', 'first_ever')
|
|
|
|
Returns:
|
|
Random search phrase from that tier
|
|
"""
|
|
return giphy_service.get_random_phrase_for_tier(tier_key)
|
|
|
|
|
|
def get_tier_description(tier_key):
|
|
"""
|
|
Get the human-readable description for a tier.
|
|
|
|
This is a wrapper function for backwards compatibility.
|
|
Use services.giphy_service.GiphyService.get_tier_description() directly in new code.
|
|
|
|
Args:
|
|
tier_key: Tier identifier
|
|
|
|
Returns:
|
|
Description string
|
|
"""
|
|
return giphy_service.get_tier_description(tier_key)
|
|
|
|
|
|
async def get_disappointment_gif(tier_key):
|
|
"""
|
|
Fetch a GIF from Giphy based on disappointment tier.
|
|
|
|
This is a wrapper function for backwards compatibility.
|
|
Use services.giphy_service.GiphyService.get_disappointment_gif() directly in new code.
|
|
|
|
Randomly selects a search phrase from the tier and queries Giphy.
|
|
Filters out Trump GIFs (legacy behavior).
|
|
Falls back to trying other phrases if first fails.
|
|
|
|
Args:
|
|
tier_key: Tier identifier (e.g., 'tier_1', 'first_ever')
|
|
|
|
Returns:
|
|
GIF URL string, or None if all attempts fail
|
|
"""
|
|
try:
|
|
return await giphy_service.get_disappointment_gif(tier_key)
|
|
except Exception:
|
|
# Return None for backwards compatibility with old error handling
|
|
return None
|