Implements a comprehensive health check system using aiohttp to support
container orchestration and external monitoring systems.
Features:
- /health endpoint: Basic liveness check (is process running?)
- /ready endpoint: Readiness check (is bot connected to Discord?)
- /metrics endpoint: Detailed bot metrics (guilds, users, cogs, latency)
Changes:
- Add aiohttp to requirements.txt
- Create health_server.py module with HTTP server
- Update paperdynasty.py to run health server alongside bot
- Update docker-compose.yml with HTTP-based healthcheck
- Fix deploy.sh Docker image name
Benefits:
- Auto-restart on bot hangs/deadlocks
- Foundation for external monitoring (Prometheus, Grafana, etc.)
- Detailed diagnostics for troubleshooting
- Industry-standard health check pattern
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
When a gauntlet game ended for Event ID 9, the post_result function would crash with:
"UnboundLocalError: cannot access local variable 'team_id' where it is not associated with a value"
Event ID 9 was missing team_id initialization, while all other events (3,4,5,6,7,8)
properly set team_id. Added team_id = None to match the pattern used by Events 5, 6, and 8.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Event ID 9 (Live 25) gauntlet rewards were being given without cardset_id
assigned, causing Team Choice packs to be unconfigured and unusable.
Root cause: Line 1923 checked if event ID was in [3, 4, 5, 6, 8], but
Event ID 9 was missing from this list. This caused the entire reward
assignment block (lines 1923-1957) including Event 9's cardset logic
(lines 1945-1948) to be skipped.
Event 9 should assign:
- cardset_id 27 for standard packs (2005 Live)
- cardset_id 26 for Promo Choice packs (pack_type_id 9)
Added 9 to the event ID list at line 1923 to enable proper cardset
assignment for Event 9 gauntlet rewards.
Fixes: Team Choice packs from Event 9 gauntlet missing cardset_id
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Users were receiving "This interaction failed" when trying to open Team
Choice packs that had neither pack_team nor pack_cardset assigned in the
database.
The previous fix only handled packs WITH cardset but WITHOUT team. This
adds handling for completely unconfigured Team Choice packs.
Now shows a helpful error message: "This Team Choice pack needs to be
assigned a team and cardset. Please contact an admin to configure this pack."
This prevents the KeyError exception that was being thrown at
helpers.py:1692 when open_choice_pack() received a pack with pack_team=None.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
When opening a Team Choice pack without a pre-assigned team, the bot would:
1. Edit the interaction to remove the pack selection view
2. Send team selection (AL/NL) via interaction.channel.send()
3. Return without sending a followup to the interaction
4. Discord left waiting for a response that never came
Changed interaction.channel.send() to interaction.followup.send() with
a helpful message explaining what's happening. This properly responds to
the interaction and allows the team selection dropdown to function.
The issue occurred at discord_ui/selectors.py:188 in SelectOpenPack callback.
Fixes: /open-packs command hangs when selecting 'Team Choice' pack type
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Created comprehensive documentation distinguishing between:
- Production cogs currently loaded (cogs.players, etc.)
- Work-in-progress refactored cogs (cogs/players_new)
- Which bug fixes apply to production vs future releases
- Migration checklist for when players_new goes live
This prevents confusion when making fixes - developers need to know
whether to update cogs/players.py (production) or cogs/players_new/
(WIP) or both.
Key insights:
- gauntlet commands are in cogs/players.py (production)
- cogs/players_new is NOT loaded in paperdynasty.py yet
- Recent fixes applied to both for consistency
- Migration requires updating COGS list in paperdynasty.py
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Users reported that end-of-run messages to #pd-news-ticker were no longer
appearing when gauntlet teams lost their second game. The news-ticker
announcement was only happening for 10-win completions, not 2-loss endings.
Changes:
- Updated end_run() to accept bot and main_team parameters
- Added send_to_channel() call when losses == 2 (natural run end)
- Skips news-ticker for manual resets (force_end=True)
- Updated post_result() to pass bot and main_team to end_run()
- Updated manual reset calls to explicitly pass force_end=True
Now when a gauntlet team loses their second game, #pd-news-ticker will
show: "The **[Team]** have completed their **[Event]** Gauntlet run
with a final record of [wins]-[losses]."
The draft completion message to news-ticker was already working correctly
at cogs/players_new/gauntlet.py:178-183.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed TypeError: 'Team' object is not subscriptable error occurring at the
end of /gauntlet start command when sending completion messages.
The get_roster_sheet() function was using dict syntax (team["gsheet"]) but
was receiving Team objects from gauntlet commands. Updated the function to
handle both dict and Team object formats using isinstance() check.
This follows the same pattern as get_context_user() and owner_only() for
handling multiple input types gracefully.
Fixes: Command 'start' raised an exception: TypeError: 'Team' object is
not subscriptable (reported at end of gauntlet draft completion)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
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>
The owner_only() function was accessing ctx.author.id, which caused an
AttributeError when called with Interaction objects from slash commands
(which use .user instead of .author).
Updated both utils.py and helpers/utils.py to handle both Context and
Interaction objects by checking for .user first, then falling back to
.author for backward compatibility with traditional commands.
Fixes: Command 'reset-image' raised an exception: AttributeError:
'Interaction' object has no attribute 'author'
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Multiple fixes to resolve PlayNotFoundException and lineup initialization errors:
1. gauntlets.py:
- Fixed Team object subscriptable errors (use .id instead of ['id'])
- Added fallback cardsets (24, 25, 26) for Event 9 RP shortage
- Fixed draft_team type handling (can be Team object or dict)
2. cogs/gameplay.py:
- Fixed gauntlet game creation flow to read field player lineup from sheets
- Catches LineupsMissingException when SP not yet selected
- Instructs user to run /gamestate after SP selection
3. utilities/dropdown.py:
- Fixed SelectStartingPitcher to create own session instead of using closed session
- Store game/team IDs instead of objects to avoid detached session issues
- Added exception handling for failed legality check API calls
These changes fix the issue where gauntlet games would fail to initialize
because the SP lineup entry wasn't being committed to the database.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed missing draft configuration for Event ID 9 "Live 25" gauntlet that was causing crashes when users tried to start a gauntlet run.
Changes:
- Added cardset configuration for Event 9 (cardsets 27, 28, 29)
- Added Event 9 to max_counts progressive difficulty logic
- Added Event 9 to draft_loop() events list
- Opponent configuration for Event 9 was already present
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implements defensive error handling for WPA (Win Probability Added) calculations when rare game states are missing from the static lookup table. The safe_wpa_lookup() function uses a three-tier fallback strategy: exact lookup, bases-empty fallback, and 0.0 default value.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>