Commit Graph

419 Commits

Author SHA1 Message Date
Cal Corum
89801e1d42 Fix circular import by moving play lock functions to separate module
HOTFIX: Production bot failed to start due to circular import.

Root cause: utilities/dropdown.py importing from command_logic/logic_gameplay.py
while logic_gameplay.py imports from utilities/dropdown.py.

Solution: Created play_lock.py as standalone module containing:
- release_play_lock()
- safe_play_lock()

Both modules now import from play_lock.py instead of each other.

Error message:
  ImportError: cannot import name 'release_play_lock' from partially
  initialized module 'command_logic.logic_gameplay' (most likely due
  to a circular import)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 09:33:37 -06:00
Cal Corum
48c86e54fd Merge remote-tracking branch 'homelab/main'
Some checks failed
Build Docker Image / build (pull_request) Failing after 24s
2026-02-04 09:24:15 -06:00
Cal Corum
ebf006e5c6 Fix play lock system to prevent permanent user lockouts
CRITICAL BUG FIX: Play locks were never released on exceptions, causing
permanent user lockouts. Found 13 stuck plays in production.

Changes:
1. Added lock_play parameter to checks_log_interaction() (default True)
2. Removed unnecessary locks from read-only commands:
   - /settings-ingame (game settings, not play state)
   - /show-card defense (read-only display)
   - /substitute commands (just show UI, lock in callback)
3. Added safe_play_lock() context manager for automatic lock release
4. Added play locking to substitution callbacks:
   - SelectBatterSub.callback()
   - SelectReliefPitcher.callback()
5. Global error handler now releases stuck locks automatically

Architecture:
- Commands that display UI or read data: No lock
- Commands that modify play state: Lock at last possible moment
- 3-layer defense: manual release, context manager, global handler

Resolves race condition from concurrent play modifications.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 09:21:18 -06:00
cal
c055276dd9 Merge pull request 'Update .gitea/workflows/docker-build.yml' (#6) from fix-docker-push into main
All checks were successful
Build Docker Image / build (push) Successful in 1m17s
Reviewed-on: #6
2026-02-04 07:11:33 +00:00
cal
735d51ab2e Update VERSION
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m5s
2026-02-04 07:09:05 +00:00
cal
8183549b65 Update .gitea/workflows/docker-build.yml
Some checks failed
Build Docker Image / build (pull_request) Failing after 11s
2026-02-04 07:07:58 +00:00
cal
0fb980aec1 Merge pull request 'debug-discord-notis' (#5) from debug-discord-notis into main
All checks were successful
Build Docker Image / build (push) Successful in 54s
Reviewed-on: #5
2026-02-04 07:00:12 +00:00
cal
1f4fad4a66 Update .gitea/workflows/docker-build.yml
All checks were successful
Build Docker Image / build (pull_request) Successful in 57s
2026-02-04 06:58:18 +00:00
cal
56746a8f6a Update .gitea/workflows/docker-build.yml
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m3s
2026-02-04 06:53:48 +00:00
cal
f36bcaccd2 Update .gitea/workflows/docker-build.yml
Some checks failed
Build Docker Image / build (pull_request) Failing after 1m4s
2026-02-04 06:49:54 +00:00
cal
ab0f42d615 Update VERSION
Some checks failed
Build Docker Image / build (pull_request) Failing after 1m9s
2026-02-04 06:47:22 +00:00
cal
b90006cbab Update .gitea/workflows/docker-build.yml
Some checks failed
Build Docker Image / build (pull_request) Failing after 13s
2026-02-04 06:45:52 +00:00
cal
6eece9f1db Update .gitea/workflows/docker-build.yml 2026-02-04 06:43:59 +00:00
cal
cd1b485946 Merge pull request 'Update VERSION' (#4) from cal-patch-1 into main
All checks were successful
Build Docker Image / build (push) Successful in 46s
Reviewed-on: #4
2026-02-04 06:37:15 +00:00
cal
f0c124b00f Update .gitea/workflows/docker-build.yml
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m8s
2026-02-04 06:35:09 +00:00
cal
a360e8812d Update .gitea/workflows/docker-build.yml
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m2s
2026-02-04 06:28:55 +00:00
cal
8bd30211e0 Update VERSION
Some checks failed
/ build (pull_request) Has been cancelled
2026-02-04 06:22:40 +00:00
cal
61a0ad60ba Merge pull request 'Pulling master over to main' (#3) from master into main
All checks were successful
/ build (push) Successful in 1m17s
Reviewed-on: #3
2026-02-04 06:16:29 +00:00
cal
63ea86f35a Update .gitea/workflows/docker-build.yml
All checks were successful
/ build (pull_request) Successful in 2m9s
2026-02-04 06:13:37 +00:00
cal
8ff9cc02e7 Merge branch 'main' into master
All checks were successful
/ build (pull_request) Successful in 1m1s
2026-02-04 06:09:45 +00:00
cal
c252ab3f1a Merge pull request 'Update .gitea/workflows/docker-build.yml' (#2) from cal-patch-2 into main
All checks were successful
/ build (push) Successful in 34s
Reviewed-on: #2
2026-02-04 06:02:36 +00:00
cal
175a19b372 Update .gitea/workflows/docker-build.yml
All checks were successful
/ build (pull_request) Successful in 57s
2026-02-04 06:01:01 +00:00
cal
a1c7aec7af Merge pull request 'Add automated docker build' (#1) from cicd/docker-build into main
All checks were successful
/ build (push) Successful in 32s
Reviewed-on: #1
2026-02-04 05:54:35 +00:00
cal
7ad8d03f98 Update .gitea/workflows/docker-build.yml
All checks were successful
/ build (pull_request) Successful in 57s
2026-02-04 05:52:17 +00:00
cal
91e96a2876 Update .gitea/workflows/docker-build.yml
Some checks failed
/ build (pull_request) Failing after 1m4s
2026-02-04 05:48:40 +00:00
cal
d094809838 Add automated docker build
Some checks failed
Build Docker Image / build (pull_request) Failing after 6m57s
2026-02-04 05:41:18 +00:00
Cal Corum
eb2a511507 Update restart notification message to be more accurate
Changed from 'Auto-Restarted' to 'Restarted' and made the message
generic since the bot restarts for multiple reasons (manual, deployment,
healthcheck) - not just healthcheck failures.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-03 23:29:51 -06:00
Cal Corum
d31ca7d7e5 Fix webhook notification by adding User-Agent header
Discord webhooks require a User-Agent header or they return 403 Forbidden.
Added 'Paper-Dynasty-Discord-Bot/1.0' as User-Agent.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-03 23:28:41 -06:00
Cal Corum
4c39e9c0ce Add Discord webhook notifications for bot restarts
Sends instant notifications to Discord when the bot restarts, helping
track stability issues and auto-recovery events.

Changes:
- Add notify_restart.py script to send webhook notifications
- Integrate notification into bot startup (on_ready event)

The notification includes:
- Timestamp of restart (CST)
- Reason (healthcheck failure detection)
- Reference to diagnostics logs

Configuration:
Set RESTART_WEBHOOK_URL environment variable in docker-compose.yml
to enable notifications.

This provides immediate visibility when Docker auto-restarts the bot
due to crashes or healthcheck failures.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-03 23:19:24 -06:00
Cal Corum
90d7345850 Implement play locking to prevent concurrent command processing
Adds idempotency guard to prevent race conditions when multiple users
submit commands for the same play simultaneously.

Changes:
- Add PlayLockedException for locked play detection
- Implement lock check in checks_log_interaction()
- Acquire lock (play.locked = True) before processing commands
- Release lock (play.locked = False) after play completion
- Add warning logs for rejected duplicate submissions
- Add /diagnostics endpoint to health server for debugging

This prevents database corruption and duplicate processing when users
spam commands like "log xcheck" while the first is still processing.

Tested successfully in Discord - duplicate commands now properly return
PlayLockedException with instructions to wait.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-03 23:13:40 -06:00
Cal Corum
00ed42befd Add tests for play lock release and cross-command racing
Adds two new test cases to test_play_locking.py to improve coverage:

1. test_lock_released_after_successful_completion
   - Verifies play.locked is set to False after complete_play()
   - Confirms play.complete is set to True
   - Validates database commit is called

2. test_different_commands_racing_on_locked_play
   - Tests that ANY command type is blocked on locked plays
   - Prevents race conditions between different command types
   - Tests multiple commands: walk, strikeout, single, xcheck

These tests ensure the play locking idempotency guard works correctly
for both lock acquisition and release, and prevents all command types
from racing (not just duplicate commands).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-03 17:13:57 -06:00
Cal Corum
38a411fd3e Bump version to 1.7.13 2026-01-31 15:53:07 -06:00
Cal Corum
4be6afb541 Add API timeout/retry logic and fix get_team_by_owner for PostgreSQL
- Add APITimeoutError exception and retry logic to db_get
- Add timeout handling to db_post, db_put, db_patch, db_delete
- Fix get_team_by_owner to prefer non-gauntlet team (PostgreSQL migration fix)
- Code formatting cleanup (black)
2026-01-31 15:52:14 -06:00
Cal Corum
0e70e94644 Bump version to 1.7.12 2026-01-30 14:10:42 -06:00
Cal Corum
22d15490dd Add pitcher validation with rank retry in get_starting_pitcher
When API returns a pitcher without pitching data (e.g., Ohtani with
pos_1=DH), explicitly fetch pitcherscouting and validate before use.
If validation fails, retry with different sp_rank values.

Retry strategy: increment rank first, if > 5 then decrement from
original rank, ensuring all 5 ranks are tried before giving up.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 14:04:03 -06:00
Cal Corum
329658ce8d Fix team_lineup crash when pitcher lacks pitcherscouting
Card 6605 (Ohtani) placed at position P had pitcherscouting_id=NULL,
causing AttributeError when accessing pitcherscouting.pitchingcard.hand.

Added null check with fallback to batterscouting hand or '?' if neither
scouting record exists.

Also fixed set notation bug on line 240 where {value} created a Python
set instead of a string.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 13:17:26 -06:00
Cal Corum
541c5bbc1e Fix pack type grouping logic in packs display
Previously p_group was only set if pack type already existed in p_data,
which would silently skip new pack types. Now properly initializes the
pack type list when encountering a new type.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 14:22:06 -06:00
Cal Corum
08fd7bec75 Fix numpy CPU compatibility issue (X86_V2)
Add numpy<2 constraint to requirements.txt to fix RuntimeError on
sba-bots where CPU doesn't support X86_V2 instructions required by
numpy 2.x. This was causing cogs.admins and cogs.gameplay to fail
loading on bot startup.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 14:04:18 -06:00
Cal Corum
565afd0183 Normalize Player.franchise queries to use Team.sname
- Add FRANCHISE_NORMALIZE dict and helper to constants.py
- Update economy.py to normalize team_choice and use sname
- Update helpers/main.py franchise queries to use sname
- Update selectors.py to normalize franchise on player updates

Part of cross-era player matching fix for AI rosters

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 12:01:00 -06:00
Cal Corum
5aa88e4e3d Fix Athletics Team Choice pack KeyError
Add 'Athletics' alias to ALL_MLB_TEAMS, IMAGES['mvp'], and AL_TEAM_IDS
to support both old franchise name ("Oakland Athletics") and new mlbclub
name ("Athletics") after the team's relocation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-04 16:20:05 -06:00
Cal Corum
c3054971eb Fix /player stats bug - sync helpers/constants.py with root
PD_SEASON was set to 9 in helpers/constants.py while games are
recorded in season 10. This caused /player command to return
no stats for cardset 27 cards since they only have season 10 data.

Changes:
- PD_SEASON: 9 → 10
- SBA_SEASON: 11 → 12
- ranked_cardsets: updated to current cardsets
- Added gauntlet-8 and gauntlet-9 configs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 21:48:30 -06:00
Cal Corum
06ff92df6c Update live cardset IDs to 27 and 28
- LIVE_CARDSET_ID: 24 → 27
- LIVE_PROMO_CARDSET_ID: 25 → 28

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-24 14:41:18 -06:00
Cal Corum
f95afd8d5d Fix unawaited coroutine in singles() command (SPD hit result)
- Added missing await keyword on line 2981 in logic_gameplay.py
- SPD hit result was calling singles() without await, causing RuntimeWarning
- All groundball tests passing (24/24)
- Bump version to 1.7.7

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-17 22:37:51 -06:00
Cal Corum
7bb191dbf4 Add app_legal_channel to helpers package (fixes import error)
Bug: Version 1.7.5 added app_legal_channel to helpers.py but production uses
the helpers/ package which imports from helpers/main.py. This caused:
- NameError: name 'app_legal_channel' is not defined
- ImportError: cannot import name 'app_legal_channel' from 'helpers'

Result: cogs.economy and cogs.players failed to load, causing all slash
commands (including /team, /selldupes, /comeonmanineedthis) to be unavailable.

Fix: Add app_legal_channel() function to helpers/main.py so it's exported
via the helpers package __init__.py.

Bumps version to 1.7.6

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-17 09:23:27 -06:00
Cal Corum
584a8a95ab Fix slash command decorators using wrong prefix command checks
Bug: Several slash commands (@app_commands.command) were using prefix command
decorators (@commands.has_any_role, @commands.check) which don't work with
app_commands. This caused errors caught by the global error handler, resulting
in "Unknown interaction" (404) errors being displayed before the command executed.

Affected commands:
- /comeonmanineedthis: Both role and channel checks were wrong
- /selldupes: Channel check was wrong
- /team: Channel check was wrong

Fix:
- Created app_legal_channel() decorator in helpers.py for slash commands
- Changed @commands.has_any_role to @app_commands.checks.has_any_role
- Changed @commands.check(legal_channel) to @app_legal_channel()

Bumps version to 1.7.5

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-17 07:29:08 -06:00
Cal Corum
563820fe93 Remove debug print from is_game_over() causing stdout spam
Bug: The is_game_over() function contained a debug print statement that was
printing "1: " to stdout on every call. This was causing massive log spam
in Docker container output (thousands of lines) and making it difficult to
diagnose actual issues.

Fix: Remove the print(f'1: ') statement from line 3251.

Bumps version to 1.7.4

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-17 07:11:10 -06:00
Cal Corum
ae52e9aad7 Bump version to 1.7.3
Bugfix release includes:
- Fix null card_id in RosterLink causing IntegrityError

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-15 09:25:58 -06:00
Cal Corum
0d8942bf96 Add VERSION file for docker build tracking
Initial version: 1.7.2

This file tracks the current version for Docker builds. When building
and pushing new versions, this file will be updated and the commit
will be tagged with the version number.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-15 09:24:16 -06:00
Cal Corum
0163f24000 Fix null card_id in RosterLink causing IntegrityError
Previously, if get_card_or_none returned None (when a card ID from the
Google Sheet doesn't exist in the database), the code would create a
RosterLink with card=None, causing card_id to be null which violates
the NOT NULL constraint on the primary key.

Now we check if this_card is None before creating the RosterLink and
raise a CardNotFoundException with a helpful error message to guide
the user to fix their roster sheet.

Fixes the error: null value in column "card_id" of relation "rosterlink"
violates not-null constraint

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-15 09:15:07 -06:00
Cal Corum
51004ea143 Fix bot crash when substituting player without eligible position
Bug: When a user attempted to substitute a player who didn't have the required
position rating, the bot would display an error message but leave the database
session in an inconsistent state. The old player was marked inactive and flushed
to the session, but when the position check failed, the function returned early
without rolling back the session. This left the session dirty, causing crashes
on subsequent operations.

Fix: Added session.rollback() before returning when PositionNotFoundException
is caught, ensuring the database session is cleanly reset.

Location: utilities/dropdown.py:479-480 in SelectBatterSub.callback()

Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-12 16:02:35 -06:00