- /card player lookup was a leading-wildcard substring match with a Python
ulower() UDF over 6.16M player_games_hist rows — a full scan measured at
27s live before the ~2s render. Add a prefix fast-path
(nick LIKE 'name%' COLLATE NOCASE) that uses the existing NOCASE index
(~1ms), falling back to the ulower substring scan only when the prefix
finds nothing. Same fix applied to _resolve_player_uids_batch (compare/stats).
Lookup: 27,205ms -> 1ms.
- Completed-season recap cache served ANY cached PNG forever, including files
rendered mid-season (frozen at whatever point they were last viewed). Only
serve from cache when the file's mtime is after season end; otherwise
re-render. Fixed in both BOT/utils.py and web/server.js (shared cache).
- Replace squadron card "Rating change" with "Place finished" (#rank / total),
derived by ranking clans by final total_score among those active in-season.
- Add (CARD)/(RECAP) timing logs for prod observability.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
obtain_clan_new_points treated a missing `astat` or `member_ratings` block as
"squadron unavailable or deleted". After a season reset the game API omits those
blocks for inactive/unranked squadrons even though the clan and its roster still
exist, so /sq-info wrongly reported real squadrons (e.g. DSPL, 513th) as
nonexistent. Require a `members` roster to consider the payload valid (genuinely
deleted clans return CLAN_IS_NOT_EXISTS JSON handled upstream) and default both
stat blocks to zero, so the squadron renders at 0 points instead of erroring.
permission_fail was invoked twice per error because discord.py dispatches to
both the command-local `@cmd.error` handler and the global `@tree.error`
handler. Combined with a public defer, this produced a duplicated error message
(one public, one ephemeral). Guard on interaction.extras so the embed is sent
at most once per interaction.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Fix 1: UV_THREADPOOL_SIZE=24 via start_server.sh wrapper (libuv reads OS
environ; process.env and PM2 env blocks don't propagate on this system)
- Fix 2: Stale-while-revalidate for leaderboards — serve cached/stale data
instantly, refresh in background; dedicated aggregateCache isolated from
the 100-entry responseCache; single-flight dedup for concurrent computes
- Fix 3: Background warmer precomputes current + last-completed season
leaderboards at +20s boot and every 4 min
- Fix 5: Adaptive TTL (5 min live, 24 h completed) via aggregateCacheTtl
- Fixes 1+2 combined: player page stall 95s -> 3.6s under concurrent heavy
leaderboard load; warm hits served in 1-4ms (was 13-53s)
* feat(gateway): hashed key store with grant + hot reload
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(gateway): channel registry + aiohttp app (keyed auth, whoami, per-channel ws/proxy)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(gateway): manage_keys CLI (add/list/revoke)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(gateway): retire srebot_external, run relay-gateway under PM2
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(gateway): point ecosystem + README at relay-gateway
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(tss): replay outbox producer for relay gateway
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(tss): forward processed games to relay outbox
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(tss-api): db helpers, app skeleton, info endpoint, fixtures
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(tss-api): player, games, history, search endpoints
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(tss-api): live, match, scoreboard, matches-search, maps
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(tss-api): filter-required leaderboards (players/vehicles/stats)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(tss-api): tournament list/detail/standings/matches
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat: wire tss upstream through gateway + tssbot-api PM2 app
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
- Move tally hook from process_session (per-guild, gated by Logs subs)
to process_ws_replays (once per game, all guilds) via on_game_finished
- Add set_voice_channel_status permission check at /tally-claim time so
failures are immediate and visible rather than silent on every game
- Remove entitlement gate from tally_claim and tally_transfer
- Add VC tally permission section to /diagnose-perms when run in a VC
- Add 5 new locale keys to en.json for the permission messages
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
A player who changed squadrons could end up with two rows in
squadron_members (old + new clan). Without ORDER BY, LIMIT 1
returned whichever row the primary-key B-tree (clan_id, uid) put
first — i.e. the lowest clan_id, which was the stale entry. This
caused the player page to show the old squadron while the homepage
search (which only reads player_games_hist) showed the correct one.
Fix: add ORDER BY updated_at DESC to both the API roster lookup in
server.js and the web fallback lookup in web/server.js, so the most
recently synced membership always wins.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add a 'Voice channel tally' group to /help with the three commands.
- Add a Voice Channel Tally section to the website docs (docs.ejs).
- Translate the commands.tally bot strings + help_group_tally into all 10
other bot locales, and the new docs.* web strings into all 10 web locales.
- Fix stale need_one_input string (ign/squadron_short -> username/squadron).
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* chore(tally): remove /dev-tally testing command
Feature is verified working; drop the dev-only manual win/loss command and its now-unused apply_manual_result helper and test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* refactor(tally): rename /tally-wipe to /tally-clear
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Rename /tally-claim and /tally-transfer options to username/squadron with
clearer descriptions. Fix /dev-tally to only apply a win/loss when the
passed username/squadron actually matches what the VC is tracking, instead
of bumping any active tally.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* feat(tally): /tally-claim, /tally-transfer, /tally-wipe commands
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(tally): idle sweep, startup load, and empty-VC expiry
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* style(tally): parenthesize voice-state guard for clarity
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(tally): update live tallies when sessions finish
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(tally): robust winner matching + cleanup of deleted-VC tallies
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(tally): /dev-tally to manually attribute a win/loss in your VC
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>