Spread scout buttons across multiple rows (5 per row) instead of
all on row 0. Cap at 25 buttons (Discord max) using the last 25 cards.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The database API only has GET/POST/DELETE for scout_opportunities.
The expires_at update is non-critical — the view timeout controls
the actual scout window.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The API returns opener_team as a full nested object, not an ID.
No need to fetch it separately.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds /resend_scout slash command to manually re-post a scout opportunity
with a custom timeout window. Updates the scout_opportunity's expires_at
in the database before posting. Also adds ruff pre-commit hook for
staged Python files.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The rewards API requires a week field. The scout claim callback was
posting without it, causing a 422 validation error when a user
selected a card from a scout opportunity.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove master_debug = True and replace all conditional INFO/DEBUG log
calls with unconditional logger.debug(). Also switches log_return_value
to logger.debug and removes the associated dead commented-out code.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
DNS failures and refused connections raised raw aiohttp errors to Discord
users. Added except aiohttp.ClientError handlers to db_get, db_patch,
db_post, db_put, and db_delete — each logs the error and raises
DatabaseError for consistent handling upstream.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The previous test patched api_calls.db_get and pygsheets.authorize then
called those mocks directly—never invoking any cog method. The test
passed even when all cog code was deleted.
Replace with a test that retrieves the real pull_roster_command.callback
from the cog instance, patches dependencies at the correct module-level
names, calls the callback, and asserts ctx.send was called with the
expected error message. If the cog cannot be imported, the test skips
gracefully via pytest.skip.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backup file was checked in with unused imports (requests, pygsheets),
adding noise to git grep, IDEs, and code review.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add SCOUTABLE_PACK_TYPES env var (default: Standard,Premium) to control
which pack types offer scout opportunities
- Unify embed construction into build_scout_embed() — removes 3 near-duplicate
embed builders across scout_view.py and scouting.py
- Replace manual total_scouts counter with derived property from claims dict
- Remove redundant db_get("current") API call per scout click — use PD_SEASON
- Remove duplicate expiry computation in create_scout_opportunity
- Move send_to_channel to top-level import, remove redundant local import
- Update tests to match simplified code
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Include pack_id in db_post("cards") payload (API requires it)
- Player names now link to card image URLs in scout embed
- Display format: "🟡 All-Star — [2023 Mike Trout](card_image_url)"
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix int_timestamp() no-arg path returning seconds instead of
milliseconds, which would silently break the daily scout token cap
against the real API
- Acknowledge double-click interactions with ephemeral message instead
of silently returning (Discord requires all interactions to be acked)
- Reorder scout flow: create card copy before consuming token so a
failure doesn't cost the player a token for nothing
- Move build_scouted_card_list import to top of scout_view.py
- Remove unused asyncio import from helpers/scouting.py
- Fix footer text inconsistency ("One scout per player" everywhere)
- Update tests for new operation order and double-click behavior
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Consolidate SCOUT_TOKENS_PER_DAY and get_scout_tokens_used() into
helpers/scouting.py (was duplicated across 3 files)
- Add midnight_timestamp() utility to helpers/utils.py
- Remove _build_scouted_ids() wrapper, use self.claims directly
- Fix build_scout_embed return type annotation
- Use Discord <t:UNIX:R> relative timestamps for scout window countdown
- Add 66-test suite covering helpers, ScoutView, and cog
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a player opens a pack, a scout opportunity is posted to #pack-openings
with face-down card buttons. Other players can blind-pick one card using
daily scout tokens (2/day), receiving a copy. The opener keeps all cards.
New files:
- discord_ui/scout_view.py: ScoutView with dynamic buttons and claim logic
- helpers/scouting.py: create_scout_opportunity() and embed builder
- cogs/economy_new/scouting.py: /scout-tokens command and cleanup task
Modified:
- helpers/main.py: Hook into open_st_pr_packs() after display_cards()
- paperdynasty.py: Register scouting cog
Requires new API endpoints in paper-dynasty-database (scout_opportunities).
Tracks #44.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Verified via `git ls-files storage/` that no storage files are tracked.
The existing `storage*` pattern already covers the directory, but adding
an explicit entry for `storage/paper-dynasty-service-creds.json` makes
the intent clear for this sensitive Google Sheets service credential file.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
DEFAULT_ACTIONS_URL=self requires local actions use short form
(cal/gitea-actions/...) so the runner passes its auth token, and
GitHub actions use full URLs (https://github.com/...) to bypass
local resolution.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fixes tag step failing due to branch protection on main rejecting
the runner's git push. Creates tags via REST API which bypasses
branch protection. Also removes the unnecessary VERSION file commit.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove manual semver validation from PR checks. Versions are now
auto-generated on merge to main by counting existing monthly tags.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Align production environment section with Major Domo's format:
container name, remote log command, co-hosted services, tea PR workflow.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove generated architecture docs, vague data flow sections, and boilerplate.
Keep commands, key patterns, and development notes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The gha cache backend silently fails on Gitea Actions due to Docker
networking issues between the Buildx builder container and the
act_runner cache server. Registry-based caching stores layers on
Docker Hub, which is more reliable for self-hosted runners.
range() objects were used directly in list membership checks instead of
being unpacked with *, causing all pitcher error ratings in range values
(roughly e27+) to silently fail during x-checks. Also fixed two range
boundary mismatches on dice 12 and dice 6.
Closes#12
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a half-inning ended with a CS (or pickoff), the batter who was at
the plate was incorrectly skipped in the next inning. The side-switch
code unconditionally advanced the batting order by 1 without checking
whether the last play was a plate appearance. Now checks opponent_play.pa
before incrementing, matching the existing non-side-switch logic.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Early returns in log_chaos, log_sac_bunt, and log_stealing left play
locks permanently stuck because the lock was acquired but never released.
The new locked_play async context manager wraps checks_log_interaction()
and guarantees lock release on exception, early return, or normal exit.
Migrated all 18 locking commands in gameplay.py and removed redundant
double-locking in end_game_command.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>