Commit Graph

100 Commits

Author SHA1 Message Date
Cal Corum
3c453c89ce fix: update chart_service path from data/ to storage/
PR #86 moved state files to storage/ but missed chart_service.py,
which still pointed to data/charts.json. The file exists at
/app/storage/charts.json in the container but the code looked
in /app/data/charts.json, causing empty autocomplete results.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 20:30:41 -05:00
Cal Corum
6f3339a42e perf: parallelize independent API calls (#90)
Closes #90

Replace sequential awaits with asyncio.gather() in all locations identified
in the issue:

- commands/gameplay/scorebug.py: parallel team lookups in publish_scorecard
  and scorebug commands; also fix missing await on async scorecard_tracker calls
- commands/league/submit_scorecard.py: parallel away/home team lookups
- tasks/live_scorebug_tracker.py: parallel team lookups inside per-scorecard
  loop (compounds across multiple active games); fix missing await on
  get_all_scorecards
- commands/injuries/management.py: parallel get_current_state() +
  search_players() in injury_roll, injury_set_new, and injury_clear
- services/trade_builder.py: parallel per-participant roster validation in
  validate_trade()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 17:48:10 +00:00
cal
52fa56cb69 Merge pull request 'perf: parallelize schedule_service API calls with asyncio.gather' (#103) from ai/major-domo-v2-88 into next-release
All checks were successful
Build Docker Image / build (push) Successful in 1m16s
Reviewed-on: #103
2026-03-20 15:16:40 +00:00
Cal Corum
d4e7246166 cleanup: remove unused weeks_ahead parameter from get_upcoming_games
The parameter was already ignored (body hardcodes range(1, 19)).
Remove from signature and the one caller that passed it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 10:11:18 -05:00
Cal Corum
b480120731 perf: parallelize schedule_service week fetches with asyncio.gather (#88)
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m20s
Closes #88

Replaced sequential for-loops in get_team_schedule(), get_recent_games(),
and get_upcoming_games() with asyncio.gather() to fire all per-week HTTP
requests concurrently. Also adds import asyncio which was missing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 10:02:11 -05:00
Cal Corum
9df8d77fa0 perf: replace sequential awaits with asyncio.gather() for true parallelism
Fixes #87

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 09:14:14 -05:00
Cal Corum
910a27e356 Merge main into next-release
All checks were successful
Build Docker Image / build (push) Successful in 3m26s
Build Docker Image / build (pull_request) Successful in 58s
Resolve conflict in views/trade_embed.py: keep main's hotfix
(emoji-stripped UI, inline validation errors) and apply next-release's
import refactor (lazy imports hoisted to top-level).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 11:28:05 -05:00
cal
541380acdf Merge pull request 'fix: re-fetch existing transactions on each validation' (#72) from fix/refresh-existing-transactions-on-validation into next-release
All checks were successful
Build Docker Image / build (push) Successful in 1m24s
Build Docker Image / build (pull_request) Successful in 57s
Reviewed-on: #72
2026-03-11 16:27:55 +00:00
Cal Corum
461112b2f8 fix: prevent partial DB writes and show detailed errors on scorecard submission failure
Read all spreadsheet data (plays, box score, pitching decisions) before any
database writes so formula errors like #N/A don't leave the DB in a partial
state. Also preserve SheetsException detail through the error chain and show
users the specific cell/error instead of a generic failure message.

Closes #78

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 20:55:10 -05:00
cal
bebe25b9dd Merge pull request 'fix: prefix trade validation errors with team abbreviation' (#71) from fix/trade-errors-show-team-abbrev into next-release
All checks were successful
Build Docker Image / build (push) Successful in 3m5s
Reviewed-on: #71
2026-03-10 15:46:42 +00:00
Cal Corum
ba55ed3109 fix: add trailing slashes to all collection POST calls
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m16s
Ensures all client.post() calls to collection endpoints include
trailing slashes, matching the standardized database API routes.
Covers BaseService.create(), TransactionService, InjuryService,
and DraftListService POST calls.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 19:34:36 -05:00
Cal Corum
f6a25aa16d fix: use targeted trailing slashes instead of universal (hotfix)
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m7s
Reverts universal trailing slash in _build_url which broke custom_commands
endpoints (401 on /execute/). Instead, add trailing slashes only to the
two batch POST endpoints (plays/, decisions/) that need them to avoid
307 redirects dropping request bodies.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 17:50:58 -05:00
Cal Corum
556a30c397 fix: re-fetch existing transactions on each validation
The TransactionBuilder cached pre-existing transactions on first load
and never refreshed them. This meant transactions submitted by other
sessions (or newly visible after API fixes) were invisible for the
lifetime of the builder session, causing incorrect roster projections.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 08:10:22 -05:00
Cal Corum
db08d0eaa4 fix: auto-fetch next_week for trade validation against projected roster
Trade validation now automatically fetches the current week from
league_service and validates against next week's projected roster
(including pending transactions), matching /dropadd behavior.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 11:48:15 -05:00
Cal Corum
8ee35e564c fix: prefix trade validation errors with team abbreviation
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m5s
Roster-level errors, warnings, and suggestions now display as
"[BSG] Too many ML players" so users know which team each issue
applies to.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 11:37:39 -05:00
Cal Corum
58fe9f22de fix: trade validation now checks against next week's projected roster
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m5s
validate_trade() was passing next_week=None to each team's
validate_transaction(), which skipped load_existing_transactions()
entirely. Trades were validated against the current roster only,
ignoring pending /dropadd transactions for next week.

Now auto-fetches current week from league_service and passes
next_week=current_week+1, matching /dropadd validation behavior.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 16:27:03 +00:00
cal
b6b997c15e Merge pull request 'fix: reuse persistent aiohttp.ClientSession in GiphyService (#26)' (#66) from ai/major-domo-v2-26 into next-release
Reviewed-on: #66
2026-03-07 03:31:02 +00:00
cal
f048a3c04b Merge pull request 'fix: update roster labels to use Minor League and Injured List (#59)' (#63) from ai/major-domo-v2-59 into next-release
Reviewed-on: #63
2026-03-07 03:26:15 +00:00
Cal Corum
18ae65a8e2 fix: replace create_item_in_table placeholder with direct endpoint call (#30)
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m10s
Remove the generic placeholder method from BaseService and replace the
single call site in CustomCommandsService.get_or_create_creator with a
direct client.post("custom_commands/creators", ...) call, consistent
with how _update_creator_stats and _update_creator_info already work.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 14:04:05 -06:00
Cal Corum
e9dd318e5a perf: reuse persistent aiohttp.ClientSession in GiphyService (#26)
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m5s
Both get_disappointment_gif and get_gif previously created a new
ClientSession per call. Replace with a lazily-initialised shared
session stored on the instance, eliminating per-call TCP handshake
and DNS overhead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 01:32:52 -06:00
Cal Corum
346e36bfc6 fix: update roster labels to use Minor League and Injured List (#59)
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m10s
- Add section headers (Active Roster, Minor League, Injured List) to the
  main summary embed in _create_roster_embeds so each roster section is
  clearly labeled for users
- Fix incorrect docstring in team_service.get_team_roster that had the
  shortil/longil mapping backwards

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 22:04:57 -06:00
Cal Corum
ffa0366441 refactor: invalidate roster cache after submission instead of force-refreshing
Simplify review found that force_refresh=True on every validate_transaction()
call caused redundant API fetches on every embed render and button press.

Instead, invalidate the roster cache after successful submit_transaction() so
the next operation fetches fresh data. This preserves the cache for normal
interaction flows while still preventing stale data after submissions.

Also adds type annotation for roster_svc DI parameter.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 21:06:00 +00:00
Cal Corum
c70669d474 fix: refresh roster data before validation to prevent stale cache
TransactionBuilder cached roster data indefinitely via _roster_loaded flag,
causing validation to use stale counts when builders persisted across multiple
/ilmove invocations. Now validate_transaction() always fetches fresh roster
data. Also adds dependency injection for roster_service to improve testability.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 21:06:00 +00:00
Cal Corum
858663cd27 refactor: move 42 unnecessary lazy imports to top-level across codebase
Codebase audit identified ~50 lazy imports. Moved 42 unnecessary ones to
top-level imports — only keeping those justified by circular imports,
init-order dependencies, or optional dependency guards. Updated test mock
patch targets where needed. See #57 for remaining DI candidates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 13:35:23 -06:00
Cal Corum
aa27769ed6 fix: roster validation now includes org affiliate transactions (closes #49)
load_existing_transactions only queried for the base team abbreviation
(e.g. "POR"), missing trades involving MiL/IL affiliates ("PORMIL",
"PORIL"). This caused false "too many players" errors when a pending
trade would have cleared a roster spot.

- get_team_transactions now accepts Union[str, List[str]] for team_abbrev
- load_existing_transactions queries all org affiliates [BASE, BASEMiL, BASEIL]
- Added 5 tests covering the fix and backwards compatibility

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 10:45:21 -06:00
Cal Corum
d1a6b57ccd fix: scorebug stale data, win probability parsing, and read-failure tolerance (closes #39, #40)
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m14s
#40: ScorecardTracker cached data in memory at startup — background task never
saw newly published scorecards. Fixed by reloading from disk on every read.

#39: Win percentage defaulted to 50% when unavailable, showing a misleading
50/50 bar. Now defaults to None with "unavailable" message in embed. Parsing
handles decimal (0.75), percentage string, and empty values. Also fixed
orientation bug where win% was always shown as home team's even when the
sheet reports the away team as the leader.

Additionally: live scorebug tracker now distinguishes between "all games
confirmed final" and "sheet read failures" — transient Google Sheets errors
no longer hide the live scores channel.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 14:14:23 -06:00
Cal Corum
9cd577cba1 fix: batch quick-wins — 4 issues resolved (closes #37, #27, #25, #38)
- #37: Fix stale comment in transaction_freeze.py referencing wrong moveid format
- #27: Change config.testing default from True to False (was masking prod behavior)
- #25: Replace deprecated asyncio.get_event_loop() with get_running_loop()
- #38: Replace naive datetime.now() with timezone-aware datetime.now(UTC) across
  7 source files and 4 test files to prevent subtle timezone bugs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 11:48:16 -06:00
Cal Corum
f64fee8d2e fix: remove 226 unused imports across the codebase (closes #33)
Ran `ruff check --select F401 --fix` to auto-remove 221 unused imports,
manually removed 4 unused `import discord` from package __init__.py files,
and fixed test import for DISAPPOINTMENT_TIERS to reference canonical location.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 11:35:04 -06:00
Cal Corum
f4be20afb3 fix: address 7 security issues across the codebase
- Remove hardcoded Giphy API key from config.py, load from env var (#19)
- URL-encode query parameters in APIClient._add_params (#20)
- URL-encode Giphy search phrases before building request URLs (#21)
- Replace internal exception details with generic messages to users (#22)
- Replace all bare except: with except Exception: (#23)
- Guard interaction.guild access in has_player_role (#24)
- Replace MD5 with SHA-256 for command change detection hash (#32)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 09:54:53 -06:00
Cal Corum
f29cccd3ab fix: roster validation now includes pending trades and fixes sWAR field name
Some checks failed
Build Docker Image / build (pull_request) Failing after 15s
RosterValidation used total_wara instead of total_sWAR, causing /legal
to silently fail. Transaction embed and submit validation now pass
next_week to validate_transaction() so pending trades are included in
roster count projections. Moved lazy imports to top-level in
transaction_embed.py. Fixed dropadd integration test fixtures that
exceeded sWAR cap.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 22:57:06 -06:00
Cal Corum
1d08dc1755 Raise exception on spreadsheet errors instead of silently skipping
**Problem:**
The _is_spreadsheet_error() check was logging a warning and silently skipping
rows with formula errors (#REF!, #N/A, etc.). This could lead to incomplete
game data being submitted without the user knowing.

**Solution:**
Raise SheetsException immediately when spreadsheet errors are detected,
providing:
- Exact row number and field name
- Actual error value found in the cell
- Common error type explanations
- Clear action required to fix

**Impact:**
- Users get immediate feedback about spreadsheet errors
- No partial/incomplete data submitted to API
- Clear instructions on what needs to be fixed
- Better data integrity

**Example Error Message:**
```
 Spreadsheet Error Detected

**Location:** Row 7, Column 'pitcher_id'
**Value Found:** `#REF!`

This cell contains a formula error that must be fixed before submission.

**Action Required:** Fix cell pitcher_id in row 7 and resubmit.
```

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-06 07:54:33 -06:00
Cal Corum
92eb9055f1 fix: Validate and sanitize pitching decision data from Google Sheets
Some checks failed
Build Docker Image / build (pull_request) Failing after 19s
Added robust validation to handle spreadsheet errors and invalid data
when reading pitching decisions from scorecards.

Problem:
- POST /api/v3/decisions was failing with 422 errors
- Google Sheets cells containing "#N/A" were passed directly to API
- API correctly rejected invalid team_id values like "#N/A" string
- No validation of integer fields or required fields

Root Cause:
- sheets_service.py:read_pitching_decisions() read values without
  validation or type checking
- Spreadsheet formula errors (#N/A, #REF!, etc.) passed through
- Invalid data types not caught until API validation failed

Solution:
1. Added _is_spreadsheet_error() to detect formula errors
2. Added _sanitize_int_field() to validate and convert integers
3. Enhanced read_pitching_decisions() to:
   - Detect and skip rows with spreadsheet errors
   - Validate integer fields (pitcher_id, team_id, etc.)
   - Ensure required fields (pitcher_id, team_id) are present
   - Log warnings for invalid data with row numbers
   - Only return valid, sanitized decision data

Impact:
- Prevents 422 errors from bad spreadsheet data
- Provides clear warnings in logs when data is invalid
- Gracefully skips invalid rows instead of crashing
- Helps identify scorecard data entry errors

Testing:
- Handles #N/A, #REF!, #VALUE!, #DIV/0! and other errors
- Converts "123.0" strings to integers correctly
- Validates required fields before sending to API
- Logs row numbers for debugging bad data

Production logs showed:
  "Input should be a valid integer, unable to parse string as
   an integer", input: "#N/A" for team_id field

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-05 21:44:06 -06:00
Cal Corum
060287b7ca Fix API parameter name: use 'demotion_week' instead of 'dem_week'
The API expects 'demotion_week' as the query parameter name, not 'dem_week'.
Updated service to send correct parameter name and tests to verify.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-01 21:27:52 -06:00
Cal Corum
be3dbf1f8d Add dem_week parameter to player team updates
- Add optional dem_week parameter to PlayerService.update_player_team()
- Transaction freeze sets dem_week to current.week + 2
- /ilmove command sets dem_week to current.week
- Draft picks (manual and auto) set dem_week to current.week + 2
- Backwards compatible - admin commands don't set dem_week
- Add 4 unit tests for dem_week scenarios
- Enhanced logging shows dem_week values

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

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-01 21:12:06 -06:00
Cal Corum
b5365f01f9 Fix custom command delete permission check using wrong ID field
Changed from command.creator_id (database ID) to command.creator.discord_id
to properly compare against the deleter's Discord user ID.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 16:10:52 -06:00
Cal Corum
2881207ca4 Update CLAUDE.md docs for all-season player search feature
- Document new autocomplete behavior (deduplicated, all-season search)
- Add PlayerService.search_players() method signature with all_seasons parameter
- Update search logic flow documentation

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

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-23 14:36:03 -06:00
Cal Corum
bf079eab83 Add all-season player search with deduplicated autocomplete
- PlayerService.search_players() now supports all_seasons=True to search across all 13 seasons
- Autocomplete shows unique player names (most recent season's team) instead of duplicates
- Command defaults to most recent season when no season parameter specified
- Users can specify season parameter for historical data

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

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-23 14:23:16 -06:00
Cal Corum
ee434c98f1 Fix weekly freeze/thaw automation - API params not being sent
Root cause: league_service.update_current_state() was calling self.patch()
without use_query_params=True. The API expected query params but received
JSON body, so database updates for week/freeze silently failed.

Changes:
- Add use_query_params=True to league_service.py:99
- Fix service layer violation in transaction_freeze.py - now uses
  player_service.update_player_team() instead of direct API client
- Bump version to 2.25.8

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 14:00:54 -06:00
Cal Corum
889b3a4e2d Fix sWAR cap validation for /ilmove and /dropadd commands
- Add sWAR cap validation to TransactionBuilder.validate_transaction()
- Use team-specific salary_cap from Team.salary_cap field
- Fall back to config.swar_cap_limit (32.0) if team has no custom cap
- Add major_league_swar_cap field to RosterValidationResult
- Update major_league_swar_status to show / with cap limit
- Add 4 new tests for sWAR cap validation

This fixes a bug where IL moves could put a team over their sWAR cap
because the validation only checked roster counts, not sWAR limits.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-06 13:17:05 -06:00
Cal Corum
37bf797254 Fix critical week rollover bugs causing 60x freeze message spam
Three bugs identified and fixed:

1. Deduplication logic tracked wrong week (transaction_freeze.py:216-219)
   - Saved freeze_from_week BEFORE _begin_freeze() modifies current.week
   - Prevents re-execution when API returns stale data

2. _run_transactions() bypassed service layer (transaction_freeze.py:350-394)
   - Added get_regular_transactions_by_week() to transaction_service.py
   - Now properly filters frozen=false and cancelled=false
   - Uses Transaction model objects instead of raw dict access

3. CRITICAL: Hardcoded current_id=1 (league_service.py:88-106)
   - Current table has one row PER SEASON, not a single row
   - Was patching Season 3 (id=1) instead of Season 13 (id=11)
   - Now fetches actual current state ID before patching

Root cause: The hardcoded ID caused every PATCH to update the wrong
season's record, so freeze was never actually set to True on the
current season. This caused the dedup check to pass 60 times (once
per minute during hour 0).

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 14:15:26 -06:00
Cal Corum
a942475950 Fix pending transaction check to use week_start parameter
The API was returning keepers transactions (week=0) when querying for
week 2 transactions. Changed from 'week' to 'week_start' parameter to
properly filter out earlier weeks.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-21 22:03:19 -06:00
Cal Corum
3cbc904478 Add pending transaction validation for /dropadd command
Prevents players who are already claimed in another team's pending
transaction (frozen=false, cancelled=false) from being added to a new
transaction for the same week.

Changes:
- Add is_player_in_pending_transaction() to TransactionService
- Make TransactionBuilder.add_move() async with validation
- Add check_pending_transactions flag (default True for /dropadd)
- Skip validation for /ilmove and trades (check_pending_transactions=False)
- Add tests/conftest.py for proper test isolation
- Add 4 new tests for pending transaction validation

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-21 17:13:43 -06:00
Cal Corum
62541ac750 Add injury log posting and fix view interaction permissions
Features:
- Post injury announcements to #sba-network-news when injuries are logged
- Update #injury-log channel with two embeds:
  - All injuries grouped by Major League team with return dates
  - All injuries grouped by return week, sorted ascending
- Auto-purge old messages before posting updated injury log

Bug Fixes:
- Fix BaseView interaction_check logic that incorrectly rejected command users
  - Old: Rejected if (not user_id match) OR (not in responders)
  - New: Allow if (user_id match) OR (in responders)
- Filter None values from responders list (handles missing gmid2)

Changes:
- services/injury_service.py: Add get_all_active_injuries_raw() method
- utils/injury_log.py: New utility for injury channel posting
- views/modals.py: Call injury posting after successful injury logging
- views/base.py: Fix interaction authorization logic
- config.py: Update to Season 13 Players role

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-19 00:08:11 -06:00
Cal Corum
1af7c82353 Fix off-by-one error in draft recent picks display
The get_recent_picks service method was subtracting 1 from overall_end,
but callers were already passing currentpick - 1. This caused the "Last 5
picks" list on the OnTheClock embed to skip the most recently completed
pick (showing picks 2-6 before current instead of 1-5).

Removed the extra subtraction in the service method since callers already
handle the exclusivity of the overall_end parameter.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-12 19:14:53 -06:00
Cal Corum
e3122fa23a Fix sWAR display precision and draft team role pings
- Change sWAR formatting from 1 decimal to 2 decimal places across all displays
- Draft on-clock announcements now ping team role (via team.lname) instead of GM

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-12 18:41:46 -06:00
Cal Corum
a814aadd61 Optimize draft sheet batch writes to single API call
- Changed write_picks_batch() from 105 individual API calls to 1 batch call
- Builds 2D array covering full pick range and writes in single update_values()
- Eliminates Google Sheets 429 rate limiting during resync operations
- Reduces resync time from ~2 minutes to seconds

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-12 15:01:48 -06:00
Cal Corum
ff62529ee3 Add draft pause/resume functionality
- Add paused field to DraftData model
- Add pause_draft() and resume_draft() methods to DraftService
- Add /draft-admin pause and /draft-admin resume commands
- Block picks in /draft command when draft is paused
- Skip auto-draft in draft_monitor when draft is paused
- Update status embeds to show paused state
- Add comprehensive tests for pause/resume

When paused:
- Timer is stopped (set to False)
- Deadline is set far in future
- All /draft picks are blocked
- Auto-draft monitor skips processing

When resumed:
- Timer is restarted with fresh deadline
- Picks are allowed again

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-11 19:58:37 -06:00
Cal Corum
9093055bb5 Add Google Sheets integration for draft pick tracking
- Add DraftSheetService with write_pick(), write_picks_batch(),
  clear_picks_range(), and get_sheet_url() methods
- Integrate sheet writes in /draft command (fire-and-forget pattern)
- Integrate sheet writes in draft_monitor.py for auto-draft picks
- Add /draft-admin resync-sheet command for bulk recovery
- Add sheet link to /draft-status embed
- Add draft_sheet_keys config with env var overrides per season
- Add get_picks_with_players() to draft_pick_service for resync
- Add 13 unit tests for DraftSheetService (all passing)
- Update CLAUDE.md documentation files

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-11 11:18:27 -06:00
Cal Corum
8f486a8424 Add skipped pick support to /draft command
Teams can now make up missed draft picks when not on the clock:
- Added get_skipped_picks_for_team() to draft_pick_service
- /draft checks for skipped picks when user isn't on the clock
- Uses earliest (lowest overall) skipped pick first
- Shows footer noting "Making up skipped pick" on success
- Does NOT advance draft when using skipped pick
- Fixed duplicate embed when command run in ping channel

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-10 23:16:10 -06:00
Cal Corum
ada4feef3e Add draft monitor auto-start and on-clock announcement embed
- Start draft monitor when timer enabled via /draft-admin timer
- Auto-start monitor when /draft-admin set-pick is used with active timer
- Add _ensure_monitor_running() helper for consistent monitor management
- Create on-clock announcement embed with:
  - Team name, pick info, and deadline
  - Team sWAR and cap space
  - Last 5 picks
  - Top 5 roster players by sWAR
- Implement smart polling intervals:
  - 30s when >60s remaining
  - 15s when 30-60s remaining
  - 5s when <30s remaining
- Add get_top_free_agents() to player service
- Fix DraftAdminGroup to accept bot parameter

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-10 23:04:39 -06:00