guild_squadron is the clean short name from SQUADRONS.json (e.g. DSPLA)
but winning_team_squadron in the replay is the raw tagged value (-DSPLA-).
They never matched for dash/underscore-tagged squads so bar_color was
always not_involved instead of win/loss on the guild's own scoreboard.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Squads with dash or underscore tags (e.g. -DSPLA-, _APS_) had their raw
replay team.squadron value written directly to COMPS filenames, producing
-DSPLA-.json / _APS_.json. The /comp autocomplete returns the clean DB
short_name (DSPLA) so the file lookup never matched.
Fix: strip leading/trailing non-alphanumeric characters and uppercase in
both the writer and the /comp command lookup. Also renamed the 8 existing
decorated COMPS files to their clean equivalents on disk.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
My earlier change to use squadron_short (resolved clean name, e.g. DSPLA)
for display broke the win/loss header colour: winning_team is the raw
replay value (e.g. -DSPLA-) so the comparison never matched, rendering
both teams as losers. Split into squadron_raw (comparison) vs
squadron_short (display text) so each uses the right value.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Same order-inversion bug as build_scoreboard_context existed in
process_session's resolve_clans call. The inverted squadron_long
caused the scoreboard renderer to find the opponent's diff entry
for a dash-tagged squad's players, producing ??? for every player
since their UIDs weren't in the wrong squad's points_diff dict.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When one team has a dash-tag (e.g. -DSPLA-) and the opponent has a normal
tag (e.g. ALUN2), batching both into resolve_clans caused the short-name
pass to place the normal team first in results and the tag pass to append
the dash-tagged team second — inverting the mapping vs. the teams array.
Each team's players were then looked up against the wrong squadron's API,
yielding curr=0 for everyone and diffs=0 on the scoreboard.
Fix: resolve each team concurrently and independently so results are always
index-aligned with the teams list regardless of which resolution path fires.
Also propagates squadron_short to the scoreboard renderer so display names
are clean (DSPLA not -DSPLA-).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Squads with dash-wrapped tags (e.g. -DSPLA-) store the full tag in the
replay's team.squadron field rather than the bare short name. The short
lookup fails and returns a placeholder whose short_name blocks the tag
lookup, leaving squadron_long as "<unresolved>" and producing no point
diffs on scoreboards. Fix: only include successfully resolved clans
(clan_id is not None) in results and resolved_shorts so the tag pass
still runs for any failed short lookup. Affects all 186 dash-tagged squads.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Raise websockets max_size to 32MB so zstd-compressed frames from Spectra
that exceed the 1MB default aren't rejected before decompression.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Per-squadron WeeklyBR reports are distinct from the global wildcard
report and should always send even when both point at the same channel.
Removed the dedup block that was silently dropping squadron-specific
embeds whenever the channel matched the wildcard channel.
Adds /resend-weekly-br (dev-only) to force-resend the most recently
ended BR window to all configured channels, clearing the idempotency
marker first.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Make .env the single source of truth for runtime config. Remove all
`env:` blocks from SREBOT/ecosystem.config.js and TSSBOT/ecosystem.config.js
so values can't silently shadow .env. Both ecosystem files load .env via
`require('dotenv').config()` and PM2 inherits the resolved environment.
- Rename SREBOT_STORAGE_VOL_PATH → STORAGE_VOL_PATH across all readers
(BOT/utils.py, BOT/receiver_bridge.py, BOT/render_recap.py, server.js,
web/server.js, dateindex.js, scripts/*, srebot.service, tests/, README,
and both .env files). STORAGE is shared between SREBOT and TSSBOT, so the
variable shouldn't carry one bot's prefix.
- Rename per-process PORT env vars to disambiguated names so .env can be
the source of truth without collisions:
PORT (api) → SREBOT_API_PORT (server.js)
PORT (web) → SREBOT_WEB_PORT (web/server.js)
WEBHOOK_PORT → SREBOT_WEBHOOK_PORT (github_webhook_updater.py)
SREBOT_EXTERNAL_HOST/PORT/UPSTREAM_URL were already uniquely named;
they just move from ecosystem env to .env.
- TSSBOT/.env: drop GITHUB_WEBHOOK_SECRET (only srebot-webhook consumes it)
and the stale SREBOT_DEPLOY_PATH. SREBOT/.env: also drop the obsolete
SREBOT_DEPLOY_PATH (ecosystem now hardcodes __dirname).
- ecosystem.config.js no longer references SREBOT_DEPLOY_PATH; deploy path
is always __dirname of the ecosystem file.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix BOT.data_parser import in render_recap.py
render_recap.py runs as a subprocess (Path(__file__).parent.parent on
sys.path) and used `from BOT.data_parser import ...`. After the SHARED
move, data_parser is no longer in the BOT package. Add BOTS/SHARED to
sys.path and switch to the absolute `from data_parser import ...`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* centralize SHARED sys.path bootstrap in BOT/__init__.py
Drop per-file `sys.path.insert(SHARED)` bandaids from BOT/scoreboard.py,
gob.py, utils.py, and game_api.py. The bootstrap now happens exactly
once when the BOT package is imported (via BOT/__init__.py), which is
implicit for any `from BOT.X import …` / `import BOT.X` and any
`python -m BOT.x` invocation.
`SHARED_DIR` is exposed as a public name on the BOT package; siblings
import it via `from . import SHARED_DIR` for building asset paths
(MAPS, ICONS, FONTS, vromfs) instead of recomputing the location.
render_recap.py is the one subprocess entry point that runs as
__main__, so it keeps a minimal bootstrap: add SREBOT to sys.path
then `import BOT` to fire the package init once.
Also move pyrightconfig.json to the BOTS monorepo root so pyright
resolves data_parser and third-party imports regardless of which
subproject the editor opens from.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR #1223 + fixup moved data_parser into BOTS/SHARED, but five BOT modules
(analytics, autologging, botscript, lux_apis, meta_manager) still used
`from .data_parser import ...`. That relative form looks inside the BOT
package, which no longer contains data_parser, so the bot crashed at
startup with ModuleNotFoundError.
Add BOT/__init__.py to put BOTS/SHARED on sys.path at package import,
then switch all five files to absolute `from data_parser import ...`.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR #1223 only staged the deletions of the old paths because the new
top-level directories were still untracked when the commit was authored.
This commit adds the actual restructured tree: SREBOT/ (existing bot),
SHARED/ (vromfs, data_parser, ICONS/MAPS/FONTS, DAGOR_FILES,
update_game_files), and TSSBOT/ (skeleton).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>