mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-11-10 19:26:08 +01:00
Compare commits
353
Commits
907e71ce03
...
v1.29.11
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5dba69f30a | ||
|
|
023c348b0d | ||
|
|
8313dd811d | ||
|
|
3f7d10c2c6 | ||
|
|
98e83d120a | ||
|
|
dd845d2784 | ||
|
|
dc15df2a40 | ||
|
|
7c838aaf06 | ||
|
|
f181c96518 | ||
|
|
6fbfa2c70b | ||
|
|
17dd50b902 | ||
|
|
9061335292 | ||
|
|
c27d3a3b6a | ||
|
|
c37acf4a93 | ||
|
|
0c1cef87f6 | ||
|
|
e8b777be8a | ||
|
|
2941348412 | ||
|
|
68b2ecfb6d | ||
|
|
e058b09621 | ||
|
|
d5526da1fb | ||
|
|
091305a20f | ||
|
|
85cd70b2d5 | ||
|
|
7997f490fc | ||
|
|
5a0b309b0b | ||
|
|
448e4e0d7d | ||
|
|
43c14665de | ||
|
|
e249408360 | ||
|
|
24e57d7125 | ||
|
|
0ea7714578 | ||
|
|
636a2934a2 | ||
|
|
cbe22035cb | ||
|
|
4c8e5cc8ed | ||
|
|
ee4141838c | ||
|
|
24ff42a672 | ||
|
|
d8748c028e | ||
|
|
b55924ce2b | ||
|
|
66f7a8d369 | ||
|
|
8ed97a3173 | ||
|
|
b4f452b026 | ||
|
|
8d4d730b11 | ||
|
|
8f54bd5971 | ||
|
|
56a09536cd | ||
|
|
9df5efa422 | ||
|
|
ee5461a297 | ||
|
|
5ed5f45240 | ||
|
|
a8cdc96382 | ||
|
|
532d677491 | ||
|
|
610ea60ca2 | ||
|
|
718576d5a9 | ||
|
|
f885fba4bd | ||
|
|
408a14003c | ||
|
|
9416e25ee9 | ||
|
|
8a3bbb3647 | ||
|
|
e16cc63cfe | ||
|
|
b2856eab58 | ||
|
|
55ffe22fd6 | ||
|
|
98e0f28cd9 | ||
|
|
547381286e | ||
|
|
bdaf6e662c | ||
|
|
87438abaa3 | ||
|
|
f837aaf40a | ||
|
|
86e080aae9 | ||
|
|
57efde83df | ||
|
|
6b29584424 | ||
|
|
71e40a0113 | ||
|
|
b5e7170697 | ||
|
|
4122856024 | ||
|
|
5b4c85138e | ||
|
|
727ced967b | ||
|
|
4462ebe82c | ||
|
|
06ce263668 | ||
|
|
7e086cc639 | ||
|
|
540e56a12d | ||
|
|
faf802cf3d | ||
|
|
b6d6fa7212 | ||
|
|
dfa8be63b4 | ||
|
|
9eb6acc624 | ||
|
|
b6204b1c2c | ||
|
|
39f1389d77 | ||
|
|
53b0f04512 | ||
|
|
d2fa93ee71 | ||
|
|
7871eae7a3 | ||
|
|
0bd660f255 | ||
|
|
488857b7d8 | ||
|
|
5e4c5fc230 | ||
|
|
7e6925772e | ||
|
|
b479c09cf8 | ||
|
|
eddc8168ba | ||
|
|
80dfc59017 | ||
|
|
fdc0f34e08 | ||
|
|
c0fdf10eba | ||
|
|
2aba4da1af | ||
|
|
581d889e9b | ||
|
|
6a280982d0 | ||
|
|
45c7d74160 | ||
|
|
9e225d2093 | ||
|
|
d1001df3cf | ||
|
|
d28c56fe1c | ||
|
|
fe68ebf450 | ||
|
|
18cc885869 | ||
|
|
9d4e1a66a1 | ||
|
|
de8d3396b1 | ||
|
|
baaae612fd | ||
|
|
4b5a511216 | ||
|
|
57dacddcfd | ||
|
|
a56665f3a3 | ||
|
|
c27e903c31 | ||
|
|
476f180a1a | ||
|
|
b05e70faee | ||
|
|
5c30169eca | ||
|
|
1226ce89a7 | ||
|
|
dcc8207fb6 | ||
|
|
c50f40a887 | ||
|
|
85f7d420a0 | ||
|
|
1aec1b2885 | ||
|
|
51226c8503 | ||
|
|
407d00b25b | ||
|
|
fb4fb926e8 | ||
|
|
70b0df55b0 | ||
|
|
7e8fd40c61 | ||
|
|
9286104391 | ||
|
|
814192c0b0 | ||
|
|
8fa6f87540 | ||
|
|
d265f0b0a1 | ||
|
|
9ccaade1fc | ||
|
|
2bb5bb33c0 | ||
|
|
e456063c27 | ||
|
|
7bc25f0722 | ||
|
|
6ed991d281 | ||
|
|
2487e0eebf | ||
|
|
6cc44cdfee | ||
|
|
eca26e1e79 | ||
|
|
5484a15071 | ||
|
|
b58ca4f594 | ||
|
|
7708e2c1db | ||
|
|
de77e27d50 | ||
|
|
e159583333 | ||
|
|
bfcf183a6e | ||
|
|
eed9d23646 | ||
|
|
8c1bf450a8 | ||
|
|
9ffc040723 | ||
|
|
f33a73e1bb | ||
|
|
2101dba7c6 | ||
|
|
1b398a5f66 | ||
|
|
d8f9a2633d | ||
|
|
2e79ccf7eb | ||
|
|
be778acf39 | ||
|
|
8d10d7cffa | ||
|
|
5b9041a972 | ||
|
|
1e60b1f61f | ||
|
|
13a636c023 | ||
|
|
8bc0693f3b | ||
|
|
9b842c501c | ||
|
|
8aadda4fb8 | ||
|
|
58c70f98e1 | ||
|
|
54b73d11ce | ||
|
|
d1ac5644a4 | ||
|
|
ae35c1516c | ||
|
|
fd364d434f | ||
|
|
fd37f72f8c | ||
|
|
1427f61c52 | ||
|
|
cd4fa366a0 | ||
|
|
344e7caa81 | ||
|
|
fe7b2aea02 | ||
|
|
fced1e96ef | ||
|
|
778efb2372 | ||
|
|
b67fd5b58c | ||
|
|
2559782a28 | ||
|
|
efb479fece | ||
|
|
fb829d4b81 | ||
|
|
91f4f2edcf | ||
|
|
a692d1ee0b | ||
|
|
31d2ab4746 | ||
|
|
e4e2c30748 | ||
|
|
d633874d90 | ||
|
|
9cb1d0cb3f | ||
|
|
a0bccc7973 | ||
|
|
3efe807e35 | ||
|
|
1cc15f64b0 | ||
|
|
f70864a0db | ||
|
|
5de40b220f | ||
|
|
bd19c9aac9 | ||
|
|
6bc5b7dcb6 | ||
|
|
00795eecdd | ||
|
|
f4e7b2ed98 | ||
|
|
81612793e3 | ||
|
|
d3bdbc39d2 | ||
|
|
7cb67bd44b | ||
|
|
9df09c9a53 | ||
|
|
a9f1d9ebec | ||
|
|
1b834eb848 | ||
|
|
c5518ec9bc | ||
|
|
8c705d7e2e | ||
|
|
efe9f91a2d | ||
|
|
e84bf6b31c | ||
|
|
46234b6cbb | ||
|
|
0bda743a91 | ||
|
|
b19866a1f5 | ||
|
|
9ca329e53f | ||
|
|
47b1eb9ab0 | ||
|
|
6c8fbf7245 | ||
|
|
2ea22512d4 | ||
|
|
697d1fa1b4 | ||
|
|
e4429f15bd | ||
|
|
cd8083ae30 | ||
|
|
9dcf7441c9 | ||
|
|
d4e1f99ce2 | ||
|
|
98de246578 | ||
|
|
45097f198c | ||
|
|
e063719caa | ||
|
|
975d579d21 | ||
|
|
b963ef0981 | ||
|
|
49fab21113 | ||
|
|
cbde48d41b | ||
|
|
1210baa545 | ||
|
|
2c03b530d2 | ||
|
|
ec977899fb | ||
|
|
e5fc2faad7 | ||
|
|
541dfc186b | ||
|
|
182471c4d6 | ||
|
|
e5c66cf232 | ||
|
|
fbbc0b82b4 | ||
|
|
aecc2adb07 | ||
|
|
f8ee2b64a6 | ||
|
|
7c4e28bd09 | ||
|
|
80d61d9e04 | ||
|
|
0c22ac3a97 | ||
|
|
efad6e61f7 | ||
|
|
3563aaecd5 | ||
|
|
5294de848f | ||
|
|
e82aa56cb2 | ||
|
|
e1b2b130a5 | ||
|
|
28f22f1d40 | ||
|
|
a3bb023309 | ||
|
|
797307fb81 | ||
|
|
f9802b44ff | ||
|
|
e26b9c78a3 | ||
|
|
925ae1a0b7 | ||
|
|
9584ecf38b | ||
|
|
338168d7e4 | ||
|
|
711d92887e | ||
|
|
78160041e8 | ||
|
|
1b884a52f9 | ||
|
|
f13c658ef4 | ||
|
|
b72d4058be | ||
|
|
029a781c50 | ||
|
|
adc1791f56 | ||
|
|
15b6ff43c5 | ||
|
|
e2c02f3d86 | ||
|
|
3ce47f6ed0 | ||
|
|
fa91b37d92 | ||
|
|
108644bd44 | ||
|
|
413436376b | ||
|
|
7d118015d0 | ||
|
|
92e7390a5d | ||
|
|
475e25a0d8 | ||
|
|
17115af0d8 | ||
|
|
a9894ade78 | ||
|
|
f5b09873a9 | ||
|
|
ac768d0897 | ||
|
|
f802b997c0 | ||
|
|
db94edea69 | ||
|
|
0809c9ee0c | ||
|
|
0326e8d1d7 | ||
|
|
912c1fb125 | ||
|
|
d07383a722 | ||
|
|
582e608203 | ||
|
|
501a618d5f | ||
|
|
7c7f34d899 | ||
|
|
864d34f344 | ||
|
|
9201fc72ce | ||
|
|
50f82d68b4 | ||
|
|
b81f664379 | ||
|
|
389707d0c8 | ||
|
|
9fbd623d91 | ||
|
|
f36df9e902 | ||
|
|
437bbfdc89 | ||
|
|
7becb7b6ad | ||
|
|
94e8a07f90 | ||
|
|
4ae5283ac4 | ||
|
|
ade238966b | ||
|
|
ea048da7a2 | ||
|
|
263f292a59 | ||
|
|
ff4a87e5b8 | ||
|
|
222b690fba | ||
|
|
e1a6e4e38d | ||
|
|
37adc04cda | ||
|
|
3fc7279f0e | ||
|
|
bd852b9368 | ||
|
|
26304fc281 | ||
|
|
0d8cb5c1ae | ||
|
|
dc60cffdff | ||
|
|
a3e74de277 | ||
|
|
49b5d4f7dc | ||
|
|
2c55455cd7 | ||
|
|
648ac57424 | ||
|
|
5e3cf816ee | ||
|
|
98ea42c7b0 | ||
|
|
873c8a54d3 | ||
|
|
927495ca16 | ||
|
|
cc957466fc | ||
|
|
ef173758aa | ||
|
|
bfceacdf3c | ||
|
|
ed0dde37d2 | ||
|
|
5ba64f231a | ||
|
|
1f06b1fef4 | ||
|
|
7b0625fc8d | ||
|
|
5dce234447 | ||
|
|
0f609717d0 | ||
|
|
3958709348 | ||
|
|
2d869c9fac | ||
|
|
82afaf9494 | ||
|
|
b1d82e624a | ||
|
|
9b60d56a36 | ||
|
|
ee0d4f6996 | ||
|
|
2d43408822 | ||
|
|
868f132105 | ||
|
|
ce428e093b | ||
|
|
ab430dc0ca | ||
|
|
03a51c47bb | ||
|
|
1790443698 | ||
|
|
a60f95340c | ||
|
|
e407463ca7 | ||
|
|
61cc15aafc | ||
|
|
1a947a6300 | ||
|
|
75262647f7 | ||
|
|
9914371dd8 | ||
|
|
a4e48a2feb | ||
|
|
4ab03e56a2 | ||
|
|
06a3791ddd | ||
|
|
36cce4b0c1 | ||
|
|
158141fdc7 | ||
|
|
1d319551e7 | ||
|
|
0552ac790e | ||
|
|
5ad7566874 | ||
|
|
638f178fbf | ||
|
|
1e996cb970 | ||
|
|
8639b31b8f | ||
|
|
889a92e98b | ||
|
|
e7fe3a4baf | ||
|
|
ab10343704 | ||
|
|
9412be2d7c | ||
|
|
66cf590f1c | ||
|
|
7c18549c00 | ||
|
|
9b97d3e27d | ||
|
|
aadc252e47 | ||
|
|
320a764d45 | ||
|
|
5594c0ec3b | ||
|
|
3d69d464f5 | ||
|
|
dd0b2bade7 | ||
|
|
99c90cfa9e | ||
|
|
dd3ce21508 | ||
|
|
b512cb2292 |
No files matched your search
@@ -1,4 +1,4 @@
|
||||
name: Build Assets
|
||||
name: Assets
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -6,30 +6,31 @@ on:
|
||||
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
cdn_branch:
|
||||
asset_repo:
|
||||
type: choice
|
||||
required: false
|
||||
description: What asset repo to write to
|
||||
options:
|
||||
- blitzkit/assets
|
||||
- blitzkit/assets-preview
|
||||
- blitzkit/assets-dev
|
||||
default: blitzkit/assets
|
||||
|
||||
asset_source:
|
||||
type: string
|
||||
required: false
|
||||
default: main
|
||||
description: What CDN branch to write to
|
||||
zip_url:
|
||||
type: string
|
||||
required: false
|
||||
description: ZIP URL to download instead of Steam client
|
||||
zip_root:
|
||||
type: string
|
||||
required: false
|
||||
description: Root dir of ZIP which houses the executable and Data dir
|
||||
description: "Archive URL or Steam to download (example: steam:APP_ID/DEPOT_ID or zip:URL_TO_ZIP)"
|
||||
default: "steam:444200/444202"
|
||||
|
||||
schedule:
|
||||
- cron: '0 2 * * *'
|
||||
- cron: "0 2 * * *"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event.inputs.cdn_branch }}
|
||||
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event.inputs.asset_repo }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
DISCORD_CLIENT_ID: ${{ secrets.DISCORD_CLIENT_ID }}
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
|
||||
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
||||
VIDEOS_SHEET: ${{ secrets.VIDEOS_SHEET }}
|
||||
@@ -37,8 +38,8 @@ env:
|
||||
WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }}
|
||||
WOTB_DLC_CDN: ${{ secrets.WOTB_DLC_CDN }}
|
||||
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
|
||||
PUBLIC_ASSET_REPO: blitzkit/assets
|
||||
PUBLIC_ASSET_BRANCH: main
|
||||
PUBLIC_ASSET_REPO: ${{ github.event.inputs.asset_repo != '' && github.event.inputs.asset_repo || secrets.PUBLIC_ASSET_REPO }}
|
||||
PUBLIC_ASSET_BRANCH: ${{ secrets.PUBLIC_ASSET_BRANCH }}
|
||||
WOTB_GLOSSARY_DOMAIN: ${{ secrets.WOTB_GLOSSARY_DOMAIN }}
|
||||
WOTB_GLOSSARY_PATH: ${{ secrets.WOTB_GLOSSARY_PATH }}
|
||||
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
|
||||
@@ -54,100 +55,28 @@ env:
|
||||
PUBLIC_ASSET_FAST_USER: blitzkit
|
||||
PUBLIC_ASSET_FAST_REPO: assets
|
||||
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
STEAM_USERNAME: ${{ secrets.STEAM_USERNAME }}
|
||||
STEAM_PASSWORD: ${{ secrets.STEAM_PASSWORD }}
|
||||
|
||||
jobs:
|
||||
download_client:
|
||||
name: Download client
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: (ZIP) Maximize build space
|
||||
if: github.event.inputs.zip_url != ''
|
||||
uses: easimon/maximize-build-space@master
|
||||
with:
|
||||
root-reserve-mb: 512
|
||||
swap-size-mb: 1024
|
||||
remove-dotnet: 'true'
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
- name: (Steam) Setup bun
|
||||
uses: oven-sh/setup-bun@v1
|
||||
if: github.event.inputs.zip_url == ''
|
||||
|
||||
- name: (Steam) Install dependencies
|
||||
if: github.event.inputs.zip_url == ''
|
||||
run: bun install
|
||||
|
||||
- name: (Steam) Setup DepotDownloader
|
||||
if: github.event.inputs.zip_url == ''
|
||||
run: bun run setup:depotdownloader
|
||||
|
||||
- name: (Steam) Download client
|
||||
if: github.event.inputs.zip_url == ''
|
||||
run: |
|
||||
chmod +x ./temp/depotDownloader/DepotDownloader
|
||||
./temp/depotDownloader/DepotDownloader -q -os windows -app 444200 -username ${{ secrets.DEPOT_DOWNLOADER_USERNAME }} -password "${{ secrets.DEPOT_DOWNLOADER_PASSWORD }}"
|
||||
|
||||
- name: (Steam) Apply patches
|
||||
if: github.event.inputs.zip_url == ''
|
||||
run: bun setup:patches --depot
|
||||
|
||||
- name: (ZIP) Download client
|
||||
if: github.event.inputs.zip_url != ''
|
||||
env:
|
||||
ZIP_URL: ${{ github.event.inputs.zip_url }}
|
||||
ZIP_ROOT: ${{ github.event.inputs.zip_root }}
|
||||
run: |
|
||||
wget -q -O client.zip $ZIP_URL
|
||||
unzip -q client.zip -d client
|
||||
rm client.zip
|
||||
mkdir -p depots/444202/0
|
||||
mv client/$ZIP_ROOT/* depots/444202/0
|
||||
|
||||
- name: Archive depots
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: depots
|
||||
path: depots
|
||||
|
||||
build_assets:
|
||||
name: Build assets
|
||||
runs-on: ubuntu-latest
|
||||
needs: download_client
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
- name: Setup bun
|
||||
uses: oven-sh/setup-bun@v1
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install
|
||||
|
||||
- name: Lint
|
||||
run: bun run lint:website
|
||||
|
||||
- name: Download depots
|
||||
uses: actions/download-artifact@v4
|
||||
- uses: arduino/setup-protoc@v3
|
||||
with:
|
||||
name: depots
|
||||
path: depots
|
||||
repo-token: ${{ secrets.GH_TOKEN }}
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
- run: bun run setup
|
||||
|
||||
- name: AppArmor hack
|
||||
run: echo 0 | sudo tee /proc/sys/kernel/apparmor_restrict_unprivileged_userns
|
||||
|
||||
- name: Start server and build assets
|
||||
run: |
|
||||
bun dev:website --verbosepa &
|
||||
SERVER_PID=$!
|
||||
|
||||
bun run build:assets --all-targets --depot --production
|
||||
|
||||
kill $SERVER_PID || true
|
||||
- name: Build assets
|
||||
run: bun run build:assets --all-targets --source=${{ github.event.inputs.asset_source || 'steam:444200/444202' }}
|
||||
@@ -3,7 +3,7 @@ name: Averages
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 8 * * *'
|
||||
- cron: "0 8 * * *"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@@ -15,7 +15,7 @@ env:
|
||||
PUBLIC_ASSET_FAST_USER: ${{ secrets.PUBLIC_ASSET_FAST_USER }}
|
||||
PUBLIC_ASSET_FAST_REPO: ${{ secrets.PUBLIC_ASSET_FAST_REPO }}
|
||||
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
|
||||
ALLOW_ROBOTS: 'true'
|
||||
ALLOW_ROBOTS: "true"
|
||||
WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }}
|
||||
PUBLIC_GOOGLE_TAG_MANAGER_ID: ${{ secrets.PUBLIC_GOOGLE_TAG_MANAGER_ID }}
|
||||
PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ${{ secrets.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID }}
|
||||
@@ -25,23 +25,28 @@ env:
|
||||
PUBLIC_DISCORD_SERVER_ID: ${{ secrets.PUBLIC_DISCORD_SERVER_ID }}
|
||||
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
|
||||
PUBLIC_BRANCH: ${{ secrets.PUBLIC_BRANCH }}
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: oven-sh/setup-bun@v1
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
- uses: arduino/setup-protoc@v3
|
||||
with:
|
||||
repo-token: ${{ secrets.GH_TOKEN }}
|
||||
- run: bun run setup
|
||||
|
||||
- name: Build averages
|
||||
env:
|
||||
PUBLIC_ASSET_BRANCH: main
|
||||
DISCORD_CLIENT_ID: ${{ secrets.DISCORD_CLIENT_ID }}
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
|
||||
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
||||
VIDEOS_SHEET: ${{ secrets.VIDEOS_SHEET }}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Build Bot
|
||||
name: Bot
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -10,24 +10,25 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
name: Build bot
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: oven-sh/setup-bun@v1
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install
|
||||
|
||||
- name: Build Prisma
|
||||
run: bun run build:prisma
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
- uses: arduino/setup-protoc@v3
|
||||
with:
|
||||
repo-token: ${{ secrets.GH_TOKEN }}
|
||||
- run: bun run setup
|
||||
|
||||
- name: Build bot
|
||||
run: bun run build:bot
|
||||
|
||||
- name: Archive build
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: bot
|
||||
path: dist/bot
|
||||
@@ -1,9 +1,9 @@
|
||||
name: Discover IDs
|
||||
name: IDs
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 0 * * 1'
|
||||
- cron: "0 0 * * 1"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@@ -11,22 +11,26 @@ concurrency:
|
||||
|
||||
env:
|
||||
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
||||
PUBLIC_ASSET_BRANCH: ${{ secrets.PUBLIC_ASSET_BRANCH }}
|
||||
PUBLIC_ASSET_REPO: ${{ secrets.PUBLIC_ASSET_REPO }}
|
||||
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: oven-sh/setup-bun@v1
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
- uses: arduino/setup-protoc@v3
|
||||
with:
|
||||
repo-token: ${{ secrets.GH_TOKEN }}
|
||||
- run: bun run setup
|
||||
|
||||
- name: Discover IDs
|
||||
run: bun build:ids --production
|
||||
@@ -1,10 +1,10 @@
|
||||
name: Host Bot
|
||||
name: Host
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: main
|
||||
schedule:
|
||||
- cron: '0 */5 * * *'
|
||||
- cron: "0 */5 * * *"
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [created, published, released, edited]
|
||||
@@ -35,15 +35,17 @@ env:
|
||||
PUBLIC_BRANCH: ${{ secrets.PUBLIC_BRANCH }}
|
||||
PUBLIC_GUIDE_WEBSITE: ${{ secrets.PUBLIC_GUIDE_WEBSITE }}
|
||||
SERVICING: ${{ secrets.SERVICING }}
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
jobs:
|
||||
host:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: oven-sh/setup-bun@v1
|
||||
# with:
|
||||
# bun-version: 1.1.33
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
- uses: arduino/setup-protoc@v3
|
||||
with:
|
||||
repo-token: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
- name: Download release assets
|
||||
run: |
|
||||
|
||||
@@ -3,39 +3,44 @@ name: Rating Asia
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 17 * * *'
|
||||
- cron: "0 17 * * *"
|
||||
|
||||
env:
|
||||
PUBLIC_ASSET_BRANCH: main
|
||||
PUBLIC_ASSET_REPO: ${{ secrets.PUBLIC_ASSET_REPO }}
|
||||
DISCORD_CLIENT_ID: ${{ secrets.DISCORD_CLIENT_ID }}
|
||||
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
|
||||
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
||||
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
|
||||
PUBLIC_ASSET_FAST_USER: ${{ secrets.PUBLIC_ASSET_FAST_USER }}
|
||||
PUBLIC_ASSET_FAST_REPO: ${{ secrets.PUBLIC_ASSET_FAST_REPO }}
|
||||
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
|
||||
ALLOW_ROBOTS: "true"
|
||||
WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }}
|
||||
PUBLIC_GOOGLE_TAG_MANAGER_ID: ${{ secrets.PUBLIC_GOOGLE_TAG_MANAGER_ID }}
|
||||
PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ${{ secrets.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID }}
|
||||
GOOGLE_APPLICATION_CREDENTIALS: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }}
|
||||
WOTB_GLOSSARY_DOMAIN: ${{ secrets.WOTB_GLOSSARY_DOMAIN }}
|
||||
WOTB_GLOSSARY_PATH: ${{ secrets.WOTB_GLOSSARY_PATH }}
|
||||
PUBLIC_DISCORD_SERVER_ID: ${{ secrets.PUBLIC_DISCORD_SERVER_ID }}
|
||||
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
|
||||
PUBLIC_BRANCH: ${{ secrets.PUBLIC_BRANCH }}
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
jobs:
|
||||
host:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: oven-sh/setup-bun@v1
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
- uses: arduino/setup-protoc@v3
|
||||
with:
|
||||
repo-token: ${{ secrets.GH_TOKEN }}
|
||||
- run: bun run setup
|
||||
|
||||
- name: Generate leaderboard
|
||||
env:
|
||||
PUBLIC_ASSET_BRANCH: main
|
||||
PUBLIC_ASSET_REPO: ${{ secrets.PUBLIC_ASSET_REPO }}
|
||||
DISCORD_CLIENT_ID: ${{ secrets.DISCORD_CLIENT_ID }}
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
|
||||
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
||||
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
|
||||
PUBLIC_ASSET_FAST_USER: ${{ secrets.PUBLIC_ASSET_FAST_USER }}
|
||||
PUBLIC_ASSET_FAST_REPO: ${{ secrets.PUBLIC_ASSET_FAST_REPO }}
|
||||
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
|
||||
ALLOW_ROBOTS: 'true'
|
||||
WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }}
|
||||
PUBLIC_GOOGLE_TAG_MANAGER_ID: ${{ secrets.PUBLIC_GOOGLE_TAG_MANAGER_ID }}
|
||||
PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ${{ secrets.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID }}
|
||||
GOOGLE_APPLICATION_CREDENTIALS: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }}
|
||||
WOTB_GLOSSARY_DOMAIN: ${{ secrets.WOTB_GLOSSARY_DOMAIN }}
|
||||
WOTB_GLOSSARY_PATH: ${{ secrets.WOTB_GLOSSARY_PATH }}
|
||||
PUBLIC_DISCORD_SERVER_ID: ${{ secrets.PUBLIC_DISCORD_SERVER_ID }}
|
||||
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
|
||||
PUBLIC_BRANCH: ${{ secrets.PUBLIC_BRANCH }}
|
||||
run: bun build:ratingleaderboard --region=asia
|
||||
@@ -3,39 +3,44 @@ name: Rating Europe
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 23 * * *'
|
||||
- cron: "0 23 * * *"
|
||||
|
||||
env:
|
||||
PUBLIC_ASSET_BRANCH: main
|
||||
PUBLIC_ASSET_REPO: ${{ secrets.PUBLIC_ASSET_REPO }}
|
||||
DISCORD_CLIENT_ID: ${{ secrets.DISCORD_CLIENT_ID }}
|
||||
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
|
||||
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
||||
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
|
||||
PUBLIC_ASSET_FAST_USER: ${{ secrets.PUBLIC_ASSET_FAST_USER }}
|
||||
PUBLIC_ASSET_FAST_REPO: ${{ secrets.PUBLIC_ASSET_FAST_REPO }}
|
||||
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
|
||||
ALLOW_ROBOTS: "true"
|
||||
WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }}
|
||||
PUBLIC_GOOGLE_TAG_MANAGER_ID: ${{ secrets.PUBLIC_GOOGLE_TAG_MANAGER_ID }}
|
||||
PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ${{ secrets.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID }}
|
||||
GOOGLE_APPLICATION_CREDENTIALS: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }}
|
||||
WOTB_GLOSSARY_DOMAIN: ${{ secrets.WOTB_GLOSSARY_DOMAIN }}
|
||||
WOTB_GLOSSARY_PATH: ${{ secrets.WOTB_GLOSSARY_PATH }}
|
||||
PUBLIC_DISCORD_SERVER_ID: ${{ secrets.PUBLIC_DISCORD_SERVER_ID }}
|
||||
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
|
||||
PUBLIC_BRANCH: ${{ secrets.PUBLIC_BRANCH }}
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
jobs:
|
||||
host:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: oven-sh/setup-bun@v1
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
- uses: arduino/setup-protoc@v3
|
||||
with:
|
||||
repo-token: ${{ secrets.GH_TOKEN }}
|
||||
- run: bun run setup
|
||||
|
||||
- name: Generate leaderboard
|
||||
env:
|
||||
PUBLIC_ASSET_BRANCH: main
|
||||
PUBLIC_ASSET_REPO: ${{ secrets.PUBLIC_ASSET_REPO }}
|
||||
DISCORD_CLIENT_ID: ${{ secrets.DISCORD_CLIENT_ID }}
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
|
||||
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
||||
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
|
||||
PUBLIC_ASSET_FAST_USER: ${{ secrets.PUBLIC_ASSET_FAST_USER }}
|
||||
PUBLIC_ASSET_FAST_REPO: ${{ secrets.PUBLIC_ASSET_FAST_REPO }}
|
||||
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
|
||||
ALLOW_ROBOTS: 'true'
|
||||
WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }}
|
||||
PUBLIC_GOOGLE_TAG_MANAGER_ID: ${{ secrets.PUBLIC_GOOGLE_TAG_MANAGER_ID }}
|
||||
PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ${{ secrets.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID }}
|
||||
GOOGLE_APPLICATION_CREDENTIALS: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }}
|
||||
WOTB_GLOSSARY_DOMAIN: ${{ secrets.WOTB_GLOSSARY_DOMAIN }}
|
||||
WOTB_GLOSSARY_PATH: ${{ secrets.WOTB_GLOSSARY_PATH }}
|
||||
PUBLIC_DISCORD_SERVER_ID: ${{ secrets.PUBLIC_DISCORD_SERVER_ID }}
|
||||
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
|
||||
PUBLIC_BRANCH: ${{ secrets.PUBLIC_BRANCH }}
|
||||
run: bun build:ratingleaderboard --region=eu
|
||||
@@ -3,39 +3,44 @@ name: Rating North America
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 5 * * *'
|
||||
- cron: "0 5 * * *"
|
||||
|
||||
env:
|
||||
PUBLIC_ASSET_BRANCH: main
|
||||
PUBLIC_ASSET_REPO: ${{ secrets.PUBLIC_ASSET_REPO }}
|
||||
DISCORD_CLIENT_ID: ${{ secrets.DISCORD_CLIENT_ID }}
|
||||
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
|
||||
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
||||
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
|
||||
PUBLIC_ASSET_FAST_USER: ${{ secrets.PUBLIC_ASSET_FAST_USER }}
|
||||
PUBLIC_ASSET_FAST_REPO: ${{ secrets.PUBLIC_ASSET_FAST_REPO }}
|
||||
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
|
||||
ALLOW_ROBOTS: "true"
|
||||
WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }}
|
||||
PUBLIC_GOOGLE_TAG_MANAGER_ID: ${{ secrets.PUBLIC_GOOGLE_TAG_MANAGER_ID }}
|
||||
PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ${{ secrets.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID }}
|
||||
GOOGLE_APPLICATION_CREDENTIALS: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }}
|
||||
WOTB_GLOSSARY_DOMAIN: ${{ secrets.WOTB_GLOSSARY_DOMAIN }}
|
||||
WOTB_GLOSSARY_PATH: ${{ secrets.WOTB_GLOSSARY_PATH }}
|
||||
PUBLIC_DISCORD_SERVER_ID: ${{ secrets.PUBLIC_DISCORD_SERVER_ID }}
|
||||
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
|
||||
PUBLIC_BRANCH: ${{ secrets.PUBLIC_BRANCH }}
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
jobs:
|
||||
host:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: oven-sh/setup-bun@v1
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
- uses: arduino/setup-protoc@v3
|
||||
with:
|
||||
repo-token: ${{ secrets.GH_TOKEN }}
|
||||
- run: bun run setup
|
||||
|
||||
- name: Generate leaderboard
|
||||
env:
|
||||
PUBLIC_ASSET_BRANCH: main
|
||||
PUBLIC_ASSET_REPO: ${{ secrets.PUBLIC_ASSET_REPO }}
|
||||
DISCORD_CLIENT_ID: ${{ secrets.DISCORD_CLIENT_ID }}
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
|
||||
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
||||
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
|
||||
PUBLIC_ASSET_FAST_USER: ${{ secrets.PUBLIC_ASSET_FAST_USER }}
|
||||
PUBLIC_ASSET_FAST_REPO: ${{ secrets.PUBLIC_ASSET_FAST_REPO }}
|
||||
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
|
||||
ALLOW_ROBOTS: 'true'
|
||||
WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }}
|
||||
PUBLIC_GOOGLE_TAG_MANAGER_ID: ${{ secrets.PUBLIC_GOOGLE_TAG_MANAGER_ID }}
|
||||
PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ${{ secrets.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID }}
|
||||
GOOGLE_APPLICATION_CREDENTIALS: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }}
|
||||
WOTB_GLOSSARY_DOMAIN: ${{ secrets.WOTB_GLOSSARY_DOMAIN }}
|
||||
WOTB_GLOSSARY_PATH: ${{ secrets.WOTB_GLOSSARY_PATH }}
|
||||
PUBLIC_DISCORD_SERVER_ID: ${{ secrets.PUBLIC_DISCORD_SERVER_ID }}
|
||||
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
|
||||
PUBLIC_BRANCH: ${{ secrets.PUBLIC_BRANCH }}
|
||||
run: bun build:ratingleaderboard --region=com
|
||||
@@ -11,27 +11,28 @@ concurrency:
|
||||
|
||||
env:
|
||||
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
||||
PUBLIC_ASSET_BRANCH: ${{ secrets.PUBLIC_ASSET_BRANCH }}
|
||||
PUBLIC_ASSET_REPO: ${{ secrets.PUBLIC_ASSET_REPO }}
|
||||
GOOGLE_APPLICATION_CREDENTIALS: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }}
|
||||
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
- uses: oven-sh/setup-bun@v1
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
- uses: arduino/setup-protoc@v3
|
||||
with:
|
||||
repo-token: ${{ secrets.GH_TOKEN }}
|
||||
- run: bun run setup
|
||||
|
||||
- name: Find reviews
|
||||
run: bun packages/scripts/src/reviews.ts
|
||||
run: bun run build:reviews
|
||||
@@ -5,7 +5,7 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
schedule:
|
||||
- cron: '0 4 * * *'
|
||||
- cron: "0 4 * * *"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -31,28 +31,29 @@ env:
|
||||
PUBLIC_BRANCH: ${{ secrets.PUBLIC_BRANCH }}
|
||||
PUBLIC_GUIDE_WEBSITE: ${{ secrets.PUBLIC_GUIDE_WEBSITE }}
|
||||
SERVICING: ${{ secrets.SERVICING }}
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
name: Build website
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
- uses: oven-sh/setup-bun@v1
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
- uses: arduino/setup-protoc@v3
|
||||
with:
|
||||
repo-token: ${{ secrets.GH_TOKEN }}
|
||||
- uses: actions/configure-pages@v5
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install
|
||||
- run: bun run setup
|
||||
|
||||
- name: Build website
|
||||
run: bun run build:website
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
uses: actions/upload-pages-artifact@v4
|
||||
with:
|
||||
path: dist/website
|
||||
|
||||
|
||||
+3
-9
@@ -1,9 +1,3 @@
|
||||
[submodule "submodules/blitzkit-closed"]
|
||||
path = submodules/blitzkit-closed
|
||||
url = https://github.com/tresabhi/blitzkit-closed
|
||||
[submodule "submodules/CUE4Parse"]
|
||||
path = submodules/CUE4Parse
|
||||
url = https://github.com/FabianFG/CUE4Parse
|
||||
[submodule "submodules/blitzkit-internal"]
|
||||
path = submodules/blitzkit-internal
|
||||
url = https://github.com/blitzkit/internal
|
||||
[submodule "packages/closed"]
|
||||
path = packages/closed
|
||||
url = https://github.com/blitzkit/closed.git
|
||||
Vendored
+22
@@ -11,6 +11,7 @@
|
||||
"analyticsreporting",
|
||||
"APAC",
|
||||
"apcr",
|
||||
"appinfo",
|
||||
"applypatches",
|
||||
"artefacts",
|
||||
"astrojs",
|
||||
@@ -34,12 +35,14 @@
|
||||
"blitzkit",
|
||||
"blitzrinth",
|
||||
"blitzstars",
|
||||
"bufbuild",
|
||||
"cacheable",
|
||||
"Callouts",
|
||||
"Camo",
|
||||
"Carro",
|
||||
"CCPAOPTOUT",
|
||||
"CDON",
|
||||
"clanid",
|
||||
"classname",
|
||||
"cleandist",
|
||||
"closedkrieg",
|
||||
@@ -62,18 +65,23 @@
|
||||
"deburr",
|
||||
"decalmask",
|
||||
"decompressor",
|
||||
"décor",
|
||||
"dedup",
|
||||
"defence",
|
||||
"degressive",
|
||||
"depotdownloader",
|
||||
"devkrieg",
|
||||
"diacriticless",
|
||||
"DIDS",
|
||||
"donotremove",
|
||||
"drei",
|
||||
"Droodles",
|
||||
"dvpd",
|
||||
"dvpl",
|
||||
"DVPLCRC",
|
||||
"dvpm",
|
||||
"dxgi",
|
||||
"easimon",
|
||||
"echarts",
|
||||
"Everytime",
|
||||
"FASTNAME",
|
||||
@@ -86,11 +94,15 @@
|
||||
"Fullscreen",
|
||||
"fullstats",
|
||||
"fuzzysort",
|
||||
"gamemode",
|
||||
"gameplay",
|
||||
"glsl",
|
||||
"GLTF",
|
||||
"GSOR",
|
||||
"gtag",
|
||||
"haha",
|
||||
"Herdbreaker",
|
||||
"Hetz",
|
||||
"highp",
|
||||
"iife",
|
||||
"imgur",
|
||||
@@ -104,6 +116,7 @@
|
||||
"leaderboards",
|
||||
"lerp",
|
||||
"LINEARSIZE",
|
||||
"linktarget",
|
||||
"lmao",
|
||||
"loadouts",
|
||||
"loddist",
|
||||
@@ -126,6 +139,7 @@
|
||||
"NEGATIVEX",
|
||||
"NEGATIVEY",
|
||||
"NEGATIVEZ",
|
||||
"networkidle",
|
||||
"NITROPAY",
|
||||
"nivo",
|
||||
"nmatname",
|
||||
@@ -136,11 +150,13 @@
|
||||
"Oopsy",
|
||||
"opentest",
|
||||
"OPTML",
|
||||
"otemp",
|
||||
"outdir",
|
||||
"outfile",
|
||||
"ownedtanks",
|
||||
"padder",
|
||||
"partytown",
|
||||
"patchnotes",
|
||||
"PIXELFORMAT",
|
||||
"playerachievements",
|
||||
"playerinfo",
|
||||
@@ -153,11 +169,15 @@
|
||||
"Promisable",
|
||||
"proto",
|
||||
"protobuf",
|
||||
"protoc",
|
||||
"protos",
|
||||
"proxyclient",
|
||||
"prunestrings",
|
||||
"Pyogenics",
|
||||
"ratingleaderboard",
|
||||
"raycaster",
|
||||
"Redirector",
|
||||
"Relogin",
|
||||
"researchable",
|
||||
"Resvg",
|
||||
"rizz",
|
||||
@@ -206,12 +226,14 @@
|
||||
"Unregistering",
|
||||
"unrotate",
|
||||
"unwrapper",
|
||||
"userns",
|
||||
"vercel",
|
||||
"vickers",
|
||||
"vitepress",
|
||||
"Vovko",
|
||||
"wargaming",
|
||||
"webp",
|
||||
"wgpkg",
|
||||
"whitespaces",
|
||||
"WHSS",
|
||||
"winrate",
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
# BlitzKit
|
||||
|
||||
Tools for everything and anything World of Tanks Blitz.
|
||||
Tools for everything and anything, World of Tanks Blitz.
|
||||
|
||||
This GitHub repository contains the code for BlitzKit and all its services. You may need to checkout sensitive closed-source submodules in order to locally develop some aspects of BlitzKit. Communicate with `@tresabhi` on Discord to get started or talk about sensitive content like environment variables or submodules.
|
||||
This GitHub repository contains the code for BlitzKit and all its services. If you are here, looking to use BlitzKit, go to https://blitzkit.app/.
|
||||
|
||||
If you intend to contribute to the code, note that you may need to check out sensitive, closed-source submodules to locally develop certain aspects of BlitzKit. Communicate with `@tresabhi` on Discord to get started or discuss sensitive content, such as environment variables or submodules.
|
||||
|
||||
## Packages
|
||||
|
||||
@@ -13,23 +15,21 @@ This GitHub repository contains the code for BlitzKit and all its services. You
|
||||
| [packages/core](packages/core) | | | Mutual internal code shared between all other packages. |
|
||||
| [packages/i18n](packages/i18n) | | [Crowdin](https://crowdin.com/project/blitzkrieg) | Translations served as JSON shared across all other packages, including some utility code. |
|
||||
| [packages/scripts](packages/scripts) | | | CLI utilities for the development and deployment of BlitzKit services. |
|
||||
| [packages/varuna](packages/varuna) | Varuna | [NPM](https://www.npmjs.com/package/varuna) | React and Astro oriented state management library, awaiting separation from BlitzKit. Unavailable from NPM at this time. |
|
||||
| [packages/website](packages/website) | BlitzKit | https://blitzkit.app | The BlitzKit website providing a wide range of tools, APIs, and interconnecting all other services. |
|
||||
| [packages/varuna](packages/varuna) | Varuna | [NPM](https://www.npmjs.com/package/varuna) | React and Astro-oriented state management library, awaiting separation from BlitzKit. Unavailable from NPM at this time. |
|
||||
| [packages/website](packages/website) | BlitzKit | https://blitzkit.app | The BlitzKit website provides a wide range of tools, APIs, and interconnects all other services. |
|
||||
|
||||
## Submodules
|
||||
|
||||
| Submodule | Title | Source | Memo |
|
||||
| ------------------------------------------------------------ | ----------- | ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [submodules/blitzkit-closed](submodules/blitzkit-closed) | ClosedKit | [blitzkit/closed](https://github.com/blitzkit/closed) | Closed source, sensitive code hidden from the public to prevent bad actors from abusing the game. Discuss with `@tresabhi` on Discord for **special permission** to access. |
|
||||
| [submodules/blitzkit-internal](submodules/blitzkit-internal) | InternalKit | [blitzkit/internal](https://github.com/blitzkit/internal) | Closed source, sensitive internal documentation hidden from the public to prevent bad actors from abusing the game. Access to this submodule is **not negotiable**. |
|
||||
| [submodules/CUE4Parse](submodules/CUE4Parse) | CUE4Parse | [FabianFG/CUE4Parse](https://github.com/FabianFG/CUE4Parse) | An Unreal Engine archives and packages parsing library in C#. |
|
||||
| Submodule | Title | Source | Memo |
|
||||
| ------------------------------------------------------ | --------- | ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [submodules/blitzkit-closed](packages/blitzkit-closed) | ClosedKit | [blitzkit/closed](https://github.com/blitzkit/closed) | Closed source, sensitive code hidden from the public to prevent bad actors from abusing the game. Discuss with `@tresabhi` on Discord for **special permission** to access. |
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Name | Source | BlitzKit Default | Memo |
|
||||
| ------------------------------------------------------------- | -------------------------------------------------------------------------------- | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `PUBLIC_WARGAMING_APPLICATION_ID` | [Wargaming Developer's Room](https://developers.wargaming.net/) | | Application ID needed to communicate with Wargaming APIs. |
|
||||
| `PUBLIC_PROMOTE_OPENTEST` | | `false` | Optional boolean dictating wether the website advertises [the opentest subdomain of BlitzKit](https://opentest.blitzkit.app/). |
|
||||
| `PUBLIC_PROMOTE_OPENTEST` | | `false` | Optional boolean dictating whether the website advertises [the opentest subdomain of BlitzKit](https://opentest.blitzkit.app/). |
|
||||
| `ALLOW_ROBOTS` | | `true` | Toggles between `Allow: /` and `Disallow: /` in the website's `/robots.txt`. This is usually `false` for all BlitzKit's subdomains. |
|
||||
| `WOTB_GLOSSARY`, `WOTB_GLOSSARY_DOMAIN`, `WOTB_GLOSSARY_PATH` | | | |
|
||||
| `PUBLIC_GOOGLE_TAG_MANAGER_ID` | [Google Tag Manager](https://tagmanager.google.com/) | `G-GL2JVHCGPQ` | Google Tag Manager's unique identifier for analytical purposes. |
|
||||
|
||||
+44
-16
@@ -1,19 +1,47 @@
|
||||
# BlitzKit Credits
|
||||
|
||||
BlitzKit is a one man band **TrèsAbhi** but I am far from solo.
|
||||
BlitzKit is a one man band **TrèsAbhi** but I am far from solo. Helpers, in the order of contribution over time:
|
||||
|
||||
| Role | People |
|
||||
| ------------------------------ | ------------------------------ |
|
||||
| Bot verification | S0AP |
|
||||
| General testing | Prince_NA |
|
||||
| Bot advertisement platform | S0AP |
|
||||
| Bot rendering pipeline | Vovko |
|
||||
| Game asset decompression | Maddox |
|
||||
| Game 3D model parsing | Pyogenics |
|
||||
| Game file layout | Aeson |
|
||||
| 3-caliber rule edge cases | Minitelrose, ChickenMan7777 |
|
||||
| Embed UX | \_34, HisRoyalFatness, Synx |
|
||||
| Tankopedia pre-release testing | [OPTML], Aeson, \_34, xkdkw |
|
||||
| Website advertisement platform | Aeson |
|
||||
| Compare pre-release testing | [OPTML] |
|
||||
| Redacted | GonnaHetzMe |
|
||||
### S0AP
|
||||
|
||||
BlitzKit Discord bot verification, testing, advertisement, and the initial encouragement to create BlitzKit.
|
||||
|
||||
### Prince_NA
|
||||
|
||||
BlitzKit Discord bot and website testing and general advise.
|
||||
|
||||
### Vovko
|
||||
|
||||
BlitzKit Discord bot and website rendering pipeline.
|
||||
|
||||
### Maddox
|
||||
|
||||
Game file decompression.
|
||||
|
||||
### Pyogenics
|
||||
|
||||
Game 3D model parsing.
|
||||
|
||||
### Aeson
|
||||
|
||||
Game file layout, general advise, advertisement, and Tankopedia pre-release testing.
|
||||
|
||||
### Minitelrose, ChickenMan7777
|
||||
|
||||
3-caliber rule edge cases.
|
||||
|
||||
### HisRoyalFatness, Synx
|
||||
|
||||
Embed UX.
|
||||
|
||||
### [OPTML] clan, \_34, xkdkw, Droodles Blitz
|
||||
|
||||
Tankopedia pre-release testing.
|
||||
|
||||
### GonnaHetzMe
|
||||
|
||||
Redacted by request.
|
||||
|
||||
### Mark Greene
|
||||
|
||||
GitHub contributor on bug fixes and feature additions.
|
||||
@@ -0,0 +1,18 @@
|
||||
# BlitzKit 1.28.3
|
||||
|
||||
## Features
|
||||
|
||||
- Turrets and guns can now be rotated in Mixer without any pitch or yaw limits
|
||||
- Added nation icons to the search results for Guesser
|
||||
- The table now occupies the whole screen in Compare
|
||||
- Added the ability to log in with a Wargaming account in the navbar
|
||||
- Added owned/unowned filters in the Tankopedia filters
|
||||
- Revamped Tankopedia quick controls to be more accessible and obvious
|
||||
|
||||
## Fixes
|
||||
|
||||
- Missing tank icons across the website have been fixed
|
||||
- Events of starting and stopping of the transform tanks now use an asynchronous event instead of a reactive state, making them faster
|
||||
- Removed a hook call on root context, speeding up load times
|
||||
- Tankopedia's pitch input now follows the same sign convention as the pitch and yaw visualizer
|
||||
- Shadows are no longer disabled when the scene is unloaded when the user scrolls away from the canvas
|
||||
@@ -0,0 +1,10 @@
|
||||
# BlitzKit 1.28.4
|
||||
|
||||
## Fixes
|
||||
|
||||
- The tank names in the Tankopedia sandbox now have less margin from the navbar
|
||||
- Controls in the Tankopedia sandbox are now shown in all modes, except for the advanced highlighting button which only appears in the dynamic armor mode
|
||||
- The settings page now reacts to input again
|
||||
- The settings page now has a tab title and description for search engines
|
||||
- Added support for transparent textures, fixing tanks like the Herdbreaker and the WZ-132-1
|
||||
- The tank filters in the Performance and Tier-List tools work once again
|
||||
@@ -0,0 +1,12 @@
|
||||
# BlitzKit 1.29.0
|
||||
|
||||
## Features
|
||||
|
||||
- Added the Playlist tool
|
||||
- Don't know what to play today? Let BlitzKit do the thinking!
|
||||
- Navigate to [the tool on BlitzKit](https://blitzkit.app/playlist)
|
||||
- Make sure you login with your Wargaming account to unlock all features like live tracking and filtering by owned tanks
|
||||
- Use filters to pick tiers, nations, etc. then hit Generate playlist
|
||||
- BlitzKit will generate a checklist for you to go through as you play
|
||||
- Don't like the order of tanks? Hit Shuffle, Sort by Last played and Least played, or straight up remove tanks from the list by using the (-) button
|
||||
- If you're logged in, BlitzKit will automatically check off tanks as you play them
|
||||
@@ -0,0 +1,5 @@
|
||||
# BlitzKit 1.29.1
|
||||
|
||||
## Technical Changes
|
||||
|
||||
- Recreated the full dependency tree for BlitzKit
|
||||
@@ -0,0 +1,14 @@
|
||||
# BlitzKit 1.29.10
|
||||
|
||||
Thank you to [Mark Greene](https://github.com/Hitjack007) for your contributions this update.
|
||||
|
||||
## Features
|
||||
|
||||
- Combined game mode passive and active abilities into a single dropdown
|
||||
- [Mark Greene] Added tier filters to the Guess game
|
||||
- Changing tiers will break your streak
|
||||
|
||||
## Fixes
|
||||
|
||||
- [Mark Greene] module damage is now properly shown when sorting with it in the tank search
|
||||
- Duplicate abilities in the tank filters are now removed
|
||||
@@ -0,0 +1,5 @@
|
||||
# BlitzKit 1.29.11
|
||||
|
||||
## Fixes
|
||||
|
||||
- The Guesser game's toolbar now scales properly with screen size
|
||||
@@ -0,0 +1,5 @@
|
||||
# BlitzKit 1.29.2
|
||||
|
||||
## Technical Changes
|
||||
|
||||
- Implemented new workflow capabilities to deploy BlitzKit OpenTest and Preview remotely, from any device
|
||||
@@ -0,0 +1,5 @@
|
||||
# BlitzKit 1.29.3
|
||||
|
||||
## Technical Changes
|
||||
|
||||
- Added a significantly more memory and network efficient AssetUploader abstraction
|
||||
@@ -0,0 +1,5 @@
|
||||
# BlitzKit 1.29.4
|
||||
|
||||
## Technical Changes
|
||||
|
||||
- BlitzKit now partially downloads game files instead of the entire game, allowing for faster updates
|
||||
@@ -0,0 +1,11 @@
|
||||
# BlitzKit 1.29.5
|
||||
|
||||
## Features
|
||||
|
||||
- Added new lighting to be a bit softer on tanks
|
||||
- Added a button with the sun icon to toggle high graphics
|
||||
- Added a new reveal and transition animation between tabs
|
||||
|
||||
## Fixes
|
||||
|
||||
- Track wheels on some tanks no longer look translucent
|
||||
@@ -0,0 +1,18 @@
|
||||
# BlitzKit 1.29.6
|
||||
|
||||
## Features
|
||||
|
||||
- Simplified the ownership and test tank filters in the tank search
|
||||
- Renamed "Guess the tank" to simply "Guess"
|
||||
- The player count value in the Performance tool is now set to Total by default
|
||||
- Added Compare-like red/green highlighting to the Performance tool
|
||||
|
||||
## Changes
|
||||
|
||||
- Removed the last updated info bar in Performance for some more screen real estate
|
||||
|
||||
## Fixes
|
||||
|
||||
- The remove played tanks in the Playlist tool now shows even when logged out
|
||||
- The Guess game now has a page title
|
||||
- The compare table now occupies the minimum width possible instead of expanding all columns excessively
|
||||
@@ -0,0 +1,5 @@
|
||||
# BlitzKit 1.29.7
|
||||
|
||||
## Features
|
||||
|
||||
- Re-introduced the ability to deploy BlitzKit OpenTests
|
||||
@@ -0,0 +1,19 @@
|
||||
# BlitzKit 1.29.8
|
||||
|
||||
Thank you to [Mark Greene](https://github.com/Hitjack007) for your contributions this update.
|
||||
|
||||
## Features
|
||||
|
||||
- Added an option to clear provisions and consumables from all tanks in Compare
|
||||
- Added the ability to choose stock turrets and guns in Mixer if a tank has any
|
||||
- Added streaks to Guesser
|
||||
- Introduced an option to partially reveal tanks in Guesser
|
||||
- This removes the darkness effect but keeps the name hidden
|
||||
- This will reset your streak to 0
|
||||
- Regardless of getting the right guess on the tank's name, your response will be counted as incorrect if partially revealed
|
||||
|
||||
## Fixes
|
||||
|
||||
- The Tankopedia now smoothly resizes when enlarging on interaction
|
||||
- [Mark Greene] Stock turrets now correctly mount the corresponding stock gun
|
||||
- The consumable and provision buttons in Compare are no longer tiny
|
||||
@@ -0,0 +1,14 @@
|
||||
# BlitzKit 1.29.9
|
||||
|
||||
Thank you to [Mark Greene](https://github.com/Hitjack007) for your contributions this update.
|
||||
|
||||
## Features
|
||||
|
||||
- Reworked tank search filters to be far more compact to make way for new filters
|
||||
- [Mark Greene] Added the ability to filter tanks by consumables
|
||||
- Added the ability to filter tanks by provisions and game-mode abilities and powers
|
||||
|
||||
## Fixes
|
||||
|
||||
- Consumable and provision icons in Compare are now properly sized
|
||||
- [Mark Greene] Provisions now properly affect terrain resistances
|
||||
@@ -0,0 +1,57 @@
|
||||
# Christmas Garage Guide 2025
|
||||
|
||||
This content was pre-recorded on a test server and the version you may see may not be the same. Please see [the official WoTB portal](https://wotblitz.com/) for the most up-to-date information.
|
||||
|
||||
The holiday season has arrived, and this year the celebrations spill directly into your garage, transforming the familiar space into a festive hub filled with activities, rewards, and seasonal flair. Instead of just offering decorations or a visual makeover, this event brings a full set of mechanics that turn everyday battles into a steady stream of progress. Whether you’re aiming for bonuses, collectibles, or new tanks, the garage is designed to make every match feel like part of a larger holiday journey.
|
||||
|
||||

|
||||
|
||||
## The Music Box
|
||||
|
||||
The familiar Snow Globes are absent this year, replaced entirely by **Music Boxes**, which act as the new way to try your luck for seasonal rewards. They behave similarly to the Snow Globes and give you a chance to obtain various items, including a shot at the brand-new **[GSOR the TANK](https://blitzkit.app/tanks/gsor-the-tank/)**. But this time around, **they do not guarantee a tank every single time.** But as a bonus, the Music Box guarantees you will not receive duplicate tanks. Once you have a tank, the Music Box will try its best to give you a different one each time.
|
||||
|
||||

|
||||
|
||||
## Win Battles, Earn Rewards
|
||||
|
||||
Each victory during the event rewards you with **3 Hot Coffees**, a seasonal currency that serves as the backbone of the progression system. These coffees can be exchanged for a variety of Festive Town rewards, giving you reasons to keep pushing through battles even if you’re just casually playing.
|
||||
|
||||

|
||||
|
||||
With the coffees, you’ll also be able to claim **2 tank certificates**, one for **tier VII** vehicles and another covering **tier IX–X** options.
|
||||
|
||||
The tier VII pool includes the [E 25](https://blitzkit.app/tanks/e-25), [CS-52 LIS](https://blitzkit.app/tanks/cs-52-lis), [AMX 13 57](https://blitzkit.app/tanks/amx-13-57), [Steyr WT](https://blitzkit.app/tanks/steyr-wt), [Kunze Panzer](https://blitzkit.app/tanks/kunze-panzer), and [Super Hellcat](https://blitzkit.app/tanks/super-hellcat). Meanwhile, the tier IX–X certificate gives access to higher-tier picks like the [121B](https://blitzkit.app/tanks/121b), [AMX 30 B](https://blitzkit.app/tanks/amx-30-b), [Obj. 260](https://blitzkit.app/tanks/obj-260), [Char Futur 4](https://blitzkit.app/tanks/char-futur-4), [TL-7-120](https://blitzkit.app/tanks/tl-7-120), and [Object 452K](https://blitzkit.app/tanks/object-452k).
|
||||
|
||||

|
||||
|
||||
## Upgrade Your Christmas Tree
|
||||
|
||||
Golden tokens allow you to upgrade your Christmas Tree through **four distinct stages**, each bringing your garage closer to its fully decorated holiday form.
|
||||
|
||||

|
||||
|
||||
As the tree grows, additional ornament slots open up, eight in total, which appear on the left side of your screen. These slots give you more room to customize the look of your garage and make the tree feel like something you’ve built up piece by piece over the course of the event. Each new ornament adds to the atmosphere, making the progression feel both cosmetic and rewarding.
|
||||
|
||||
A fully decorated tree grants up to **+25% silver** per battle, turning your regular gameplay into a more profitable experience without requiring extra effort. Ensure all your ornaments come from the same set to unlock the full potential of the tree (all left, all middle, or all right).
|
||||
|
||||

|
||||
|
||||
## Upgrade Your Yuletide Stall
|
||||
|
||||
Your golden tokens can also be invested into upgrading the **Yuletide Stall**, another element of the garage that evolves as you level it up. Each upgrade unlocks new rewards, giving you incentives to distribute your tokens across different attractions instead of focusing on only one. The stall begins in a simple state, but after upgrades it transforms visually and functionally, becoming a more vibrant part of your garage.
|
||||
|
||||

|
||||
|
||||
Each upgrade unlocks a tier worth of rewards that you can claim in exchange for Free XP, Gold, Silver, and more Hot Coffees!
|
||||
|
||||

|
||||
|
||||
## Upgrade Your Shooting Gallery
|
||||
|
||||
Another upgradeable area is the **Shooting Gallery**, which contributes its own set of rewards as it levels up. Enhancing the gallery expands your options for exchanging mastery badges, making those badges more useful during the event than they usually are.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
Consider yourselves debriefed. Happy holidays!
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+15
-30
@@ -1,22 +1,16 @@
|
||||
{
|
||||
"name": "blitzkit",
|
||||
"version": "1.29.11",
|
||||
"license": "BlitzKit License",
|
||||
"private": true,
|
||||
"version": "1.28.2",
|
||||
"description": "🎉 Tools for everything World of Tanks Blitz",
|
||||
"type": "module",
|
||||
"types": "./types.d.ts",
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
"scripts": {
|
||||
"build:assets": "cd packages/scripts && bun run build:assets",
|
||||
"build:averages": "bun --filter=@blitzkit/scripts run build:averages",
|
||||
"build:bot": "cd packages/scripts && bun run build:bot",
|
||||
"build:website": "bun build:prisma --no-hints && cd packages/website && bun run build",
|
||||
"build:website": "cd packages/website && bun run build",
|
||||
"build:prisma": "prisma generate",
|
||||
"build:sitemap": "bun scripts/sitemap.ts",
|
||||
"build:docs": "vitepress build docs",
|
||||
"build:proxyclient": "cd packages/scripts && bun run build:proxyclient",
|
||||
"build:catalogitemaccesssor": "cd packages/scripts && bun run build:catalogitemaccesssor",
|
||||
"build:ratingleaderboard": "cd packages/scripts && bun run build:ratingleaderboard",
|
||||
"build:app": "cd packages/app && bun run build",
|
||||
"dev:bot": "bun run build:bot --dev && bun start:bot",
|
||||
@@ -36,30 +30,21 @@
|
||||
"update": "yarn upgrade --latest",
|
||||
"tunnel": "cloudflared tunnel --url http://localhost:3000",
|
||||
"discoverids": "scripts/discoverIds.ts",
|
||||
"build:protos": "bun packages/scripts/src/protos.ts",
|
||||
"build:protos": "cd packages/scripts && bun run build:protos",
|
||||
"preview:website": "cd packages/website && bun run preview",
|
||||
"build:reviews": "cd packages/scripts && bun build:reviews",
|
||||
"build:ids": "cd packages/scripts && bun build:ids",
|
||||
"prunestrings": "cd packages/scripts && bun prunestrings"
|
||||
"prunestrings": "cd packages/scripts && bun prunestrings",
|
||||
"setup": "bun install && bun run build:prisma && bun run build:protos && bun run build:proxyclient",
|
||||
"patchnotes": "cd packages/scripts && bun run patchnotes"
|
||||
},
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
"dependencies": {
|
||||
"@types/xmpp__client": "^0.13.3",
|
||||
"@xmpp/client": "^0.13.6",
|
||||
"astro-favicons": "^3.1.5",
|
||||
"cheerio": "^1.1.2",
|
||||
"googleapis": "^160.0.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"protobufjs": "^7.5.4",
|
||||
"quicklime": "^1.2.0",
|
||||
"sharp": "^0.34.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.2.22",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/node": "^24.5.2",
|
||||
"prettier": "^3.6.2",
|
||||
"prisma": "^6.16.2",
|
||||
"typescript": "^5.9.2"
|
||||
"@prisma/client": "^6.18.0",
|
||||
"prisma": "^6.18.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
+20
-12
@@ -1,26 +1,34 @@
|
||||
{
|
||||
"name": "@blitzkit/bot",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"types": "src/types.d.ts",
|
||||
"dependencies": {
|
||||
"@blitzkit/core": "workspace:*",
|
||||
"@blitzkit/i18n": "workspace:*",
|
||||
"satori": "^0.18.3",
|
||||
"@resvg/resvg-js": "^2.6.2",
|
||||
"discord.js": "^14.22.1",
|
||||
"react": "^19.1.1",
|
||||
"@radix-ui/colors": "^3.0.0",
|
||||
"fuzzysort": "^3.1.0",
|
||||
"@radix-ui/themes": "^3.2.1",
|
||||
"@resvg/resvg-js": "^2.6.2",
|
||||
"@types/adm-zip": "^0.5.7",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/markdown-escape": "^1.1.3",
|
||||
"@types/react": "^19.2.2",
|
||||
"adm-zip": "^0.5.16",
|
||||
"bepaint": "^0.1.0-beta.3",
|
||||
"discord.js": "^14.22.1",
|
||||
"dotenv": "^17.2.2",
|
||||
"markdown-escape": "^2.0.0"
|
||||
"fuzzysort": "^3.1.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"markdown-escape": "^2.0.0",
|
||||
"pickleparser": "^0.2.1",
|
||||
"prisma": "^6.17.1",
|
||||
"protobufjs": "^7.5.4",
|
||||
"react": "^19.2.0",
|
||||
"satori": "^0.18.3",
|
||||
"sharp": "^0.34.4"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "bun --watch src/main.ts",
|
||||
"start": "bun src/main.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/markdown-escape": "^1.1.3"
|
||||
}
|
||||
"type": "module",
|
||||
"types": "src/types.d.ts"
|
||||
}
|
||||
@@ -1,28 +1,31 @@
|
||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||
import { translator } from '../core/localization/translator';
|
||||
import { COMMANDS_RAW, CommandRegistry } from '../events/interactionCreate';
|
||||
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||
import { translator } from "../core/localization/translator";
|
||||
import {
|
||||
COMMANDS_RAW,
|
||||
type CommandRegistry,
|
||||
} from "../events/interactionCreate";
|
||||
|
||||
const subcommands = [
|
||||
'embeds',
|
||||
'introduction',
|
||||
'invite',
|
||||
'timezones',
|
||||
'credits',
|
||||
"embeds",
|
||||
"introduction",
|
||||
"invite",
|
||||
"timezones",
|
||||
"credits",
|
||||
] as const;
|
||||
type Subcommand = (typeof subcommands)[number];
|
||||
|
||||
const DOCS: Record<Subcommand, string> = {
|
||||
embeds: 'guide/embeds',
|
||||
introduction: 'guide/introduction',
|
||||
invite: 'guide/invite',
|
||||
timezones: 'guide/timezones',
|
||||
credits: 'about/credits',
|
||||
embeds: "guide/embeds",
|
||||
introduction: "guide/introduction",
|
||||
invite: "guide/invite",
|
||||
timezones: "guide/timezones",
|
||||
credits: "about/credits",
|
||||
};
|
||||
|
||||
export const aboutCommand = new Promise<CommandRegistry>(async (resolve) => {
|
||||
resolve({
|
||||
command: createLocalizedCommand('about', [
|
||||
{ subcommand: 'commands' },
|
||||
command: createLocalizedCommand("about", [
|
||||
{ subcommand: "commands" },
|
||||
...subcommands.map((subcommand) => ({ subcommand })),
|
||||
]),
|
||||
|
||||
@@ -30,7 +33,7 @@ export const aboutCommand = new Promise<CommandRegistry>(async (resolve) => {
|
||||
const subcommand = interaction.options.getSubcommand();
|
||||
const { strings } = translator(interaction.locale);
|
||||
|
||||
if (subcommand === 'commands') {
|
||||
if (subcommand === "commands") {
|
||||
return `${strings.bot.commands.about.subcommands.commands.body}${(
|
||||
await Promise.all(COMMANDS_RAW)
|
||||
)
|
||||
@@ -40,7 +43,7 @@ export const aboutCommand = new Promise<CommandRegistry>(async (resolve) => {
|
||||
(b.command.name_localizations?.[interaction.locale] ??
|
||||
b.command.name)
|
||||
? -1
|
||||
: 1,
|
||||
: 1
|
||||
)
|
||||
.map(
|
||||
(registry) =>
|
||||
@@ -51,9 +54,9 @@ export const aboutCommand = new Promise<CommandRegistry>(async (resolve) => {
|
||||
registry.command.description_localizations?.[
|
||||
interaction.locale
|
||||
] ?? registry.command.description
|
||||
}`,
|
||||
}`
|
||||
)
|
||||
.join('\n')}`;
|
||||
.join("\n")}`;
|
||||
}
|
||||
|
||||
const url = `https://raw.githubusercontent.com/tresabhi/blitzkit/main/docs/${DOCS[subcommand as Subcommand]}.md`;
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
import {
|
||||
BlitzTankClass,
|
||||
Region,
|
||||
regionToRegionSubdomain,
|
||||
} from '@blitzkit/core';
|
||||
import { literals } from '@blitzkit/i18n';
|
||||
import { tankDefinitions } from '../core/blitzkit/nonBlockingPromises';
|
||||
import { chunkLines } from '../core/discord/chunkLines';
|
||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||
import { translator } from '../core/localization/translator';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
|
||||
type Auction = null | {
|
||||
count: number;
|
||||
has_next: boolean;
|
||||
results: Result[];
|
||||
};
|
||||
|
||||
type Result =
|
||||
| {
|
||||
id?: number;
|
||||
type: 'vehicle';
|
||||
countable?: boolean;
|
||||
entity?: Entity;
|
||||
initial_count: number;
|
||||
current_count: number;
|
||||
saleable?: boolean;
|
||||
available_from?: Date;
|
||||
available_before?: Date;
|
||||
price?: Price;
|
||||
next_price: Price | null;
|
||||
available: true;
|
||||
display?: boolean;
|
||||
next_price_datetime: Date;
|
||||
next_price_timestamp: number;
|
||||
}
|
||||
| {
|
||||
available: false;
|
||||
type: 'vehicle';
|
||||
};
|
||||
|
||||
interface Entity {
|
||||
id: number;
|
||||
name: string;
|
||||
nation: string;
|
||||
subnation: string;
|
||||
use_subnation_flag: boolean;
|
||||
type_slug: BlitzTankClass;
|
||||
level: number;
|
||||
roman_level: string;
|
||||
user_string: string;
|
||||
image_url: string;
|
||||
preview_image_url: string;
|
||||
is_premium: boolean;
|
||||
is_collectible: boolean;
|
||||
}
|
||||
|
||||
interface Price {
|
||||
currency: Currency;
|
||||
value: number;
|
||||
}
|
||||
|
||||
interface Currency {
|
||||
name: 'gold';
|
||||
count: number;
|
||||
title: 'Gold';
|
||||
image_url: string;
|
||||
sizes: unknown;
|
||||
type: 'currency';
|
||||
}
|
||||
|
||||
const WARNING_COUNT = 500;
|
||||
|
||||
export const auctionCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
resolve({
|
||||
command: createLocalizedCommand('auction'),
|
||||
|
||||
async handler(interaction) {
|
||||
const { strings } = translator(interaction.locale);
|
||||
const awaitedTankDefinitions = await tankDefinitions;
|
||||
const region: Region = 'com';
|
||||
const pageSize = 80;
|
||||
const saleable = true;
|
||||
const data = await fetch(
|
||||
`https://${regionToRegionSubdomain(
|
||||
region,
|
||||
)}.wotblitz.com/en/api/events/items/auction/?page_size=${
|
||||
pageSize
|
||||
}&type[]=vehicle&saleable=${saleable}`,
|
||||
).then((response) => response.json() as Promise<Auction>);
|
||||
|
||||
if (data === null) {
|
||||
return strings.bot.commands.auction.errors.no_auction;
|
||||
}
|
||||
|
||||
const saleableTanks = data.results.filter((result) => result.available);
|
||||
const nextPricesTimestamp = saleableTanks[0].next_price_timestamp;
|
||||
|
||||
const title = `# ${strings.bot.commands.auction.body.title}`;
|
||||
const subtitle = `${literals(strings.bot.commands.auction.body.subtitle, [
|
||||
`${saleableTanks.length} / ${data.results.length}`,
|
||||
`<t:${nextPricesTimestamp}:R>`,
|
||||
'<https://wotblitz.com/auction/>',
|
||||
])}`;
|
||||
const body = saleableTanks.map((data, index) => {
|
||||
const tank = awaitedTankDefinitions.tanks[data.entity!.id];
|
||||
|
||||
if (!tank) return '';
|
||||
|
||||
const name = `[${tank.name}](<https://blitzkit.app/tanks/${
|
||||
tank.slug
|
||||
}>)`;
|
||||
const next =
|
||||
data.next_price === null
|
||||
? ''
|
||||
: `\n-# ${literals(strings.bot.commands.auction.body.next, [
|
||||
`<:gold:1317173197082333244> ${data.next_price.value.toLocaleString(
|
||||
interaction.locale,
|
||||
)}`,
|
||||
])}`;
|
||||
const available = literals(
|
||||
strings.bot.commands.auction.body.available,
|
||||
[
|
||||
data.current_count.toLocaleString(interaction.locale),
|
||||
data.initial_count.toLocaleString(interaction.locale),
|
||||
],
|
||||
);
|
||||
const isOut = data.current_count === 0;
|
||||
const outString = isOut ? '~~' : '';
|
||||
const isLow = data.current_count <= WARNING_COUNT;
|
||||
|
||||
return `${index + 1}. ${
|
||||
outString
|
||||
}${name} <:gold:1317173197082333244> ${data.price!.value.toLocaleString(
|
||||
interaction.locale,
|
||||
)}${outString}\n-#${isLow ? ' ‼️ ' : ' '}${available}${isOut ? '' : next}`;
|
||||
});
|
||||
const header = `${title}\n${subtitle}`;
|
||||
const lines = [header, ...body];
|
||||
|
||||
return chunkLines(lines, '\n\n');
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -13,7 +13,7 @@ import { resolvePeriodFromButton } from '../../core/discord/resolvePeriodFromBut
|
||||
import { resolvePeriodFromCommand } from '../../core/discord/resolvePeriodFromCommand';
|
||||
import { resolvePlayerFromButton } from '../../core/discord/resolvePlayerFromButton';
|
||||
import { resolvePlayerFromCommand } from '../../core/discord/resolvePlayerFromCommand';
|
||||
import { CommandRegistry } from '../../events/interactionCreate';
|
||||
import type { CommandRegistry } from '../../events/interactionCreate';
|
||||
import { renderBreakdown } from './render';
|
||||
|
||||
export const breakdownCommand = new Promise<CommandRegistry>(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {
|
||||
BlitzStats,
|
||||
type BlitzStats,
|
||||
calculateWN8,
|
||||
getAccountInfo,
|
||||
getClanAccountInfo,
|
||||
@@ -17,11 +17,11 @@ import { TitleBar } from "../../components/TitleBar";
|
||||
import { filtersToDescription } from "../../core/blitzkit/filtersToDescription";
|
||||
import { tankDefinitions } from "../../core/blitzkit/nonBlockingPromises";
|
||||
import { UserError } from "../../core/blitzkit/userError";
|
||||
import { filterStats, StatFilters } from "../../core/blitzstars/filterStats";
|
||||
import { filterStats, type StatFilters } from "../../core/blitzstars/filterStats";
|
||||
import { getStatsInPeriod } from "../../core/blitzstars/getStatsInPeriod";
|
||||
import { blitzStarsTankAverages } from "../../core/blitzstars/tankAverages";
|
||||
import { ResolvedPeriod } from "../../core/discord/resolvePeriodFromCommand";
|
||||
import { ResolvedPlayer } from "../../core/discord/resolvePlayerFromCommand";
|
||||
import type { ResolvedPeriod } from "../../core/discord/resolvePeriodFromCommand";
|
||||
import type { ResolvedPlayer } from "../../core/discord/resolvePlayerFromCommand";
|
||||
import { translator } from "../../core/localization/translator";
|
||||
|
||||
const ROWS_PER_PAGE = 8;
|
||||
@@ -193,9 +193,9 @@ export async function renderBreakdown(
|
||||
title={
|
||||
tankDefinition
|
||||
? unwrap(tankDefinition.name)
|
||||
: literals(strings.bot.commands.breakdown.body.unknown_tank, [
|
||||
`${id}`,
|
||||
])
|
||||
: literals(strings.bot.commands.breakdown.body.unknown_tank, {
|
||||
id,
|
||||
})
|
||||
}
|
||||
stats={[
|
||||
{
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import packageJSON from '../../../../package.json';
|
||||
import { client } from '../core/discord/client';
|
||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||
import { translator } from '../core/localization/translator';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
import packageJSON from "../../../../package.json";
|
||||
import { client } from "../core/discord/client";
|
||||
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||
import { translator } from "../core/localization/translator";
|
||||
import type { CommandRegistry } from "../events/interactionCreate";
|
||||
|
||||
const executionStart = new Date().getTime();
|
||||
|
||||
export const debugCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
resolve({
|
||||
command: createLocalizedCommand('debug'),
|
||||
command: createLocalizedCommand("debug"),
|
||||
|
||||
async handler(interaction) {
|
||||
const { strings } = translator(interaction.locale);
|
||||
@@ -23,15 +23,15 @@ export const debugCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
[
|
||||
strings.bot.commands.debug.body.uptime,
|
||||
`${Math.floor((uptime / 1000 / 60 / 60) % 24)}h ${Math.floor(
|
||||
(uptime / 1000 / 60) % 60,
|
||||
(uptime / 1000 / 60) % 60
|
||||
)}m ${Math.floor((uptime / 1000) % 60)}s ${Math.floor(
|
||||
uptime % 1000,
|
||||
uptime % 1000
|
||||
)}ms`,
|
||||
],
|
||||
];
|
||||
return `${strings.bot.commands.debug.body.title}${list
|
||||
.map(([key, value]) => `- ${key}: ${value}`)
|
||||
.join('\n')}`;
|
||||
.join("\n")}`;
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -1,48 +1,48 @@
|
||||
import { emblemURL, getAccountInfo, getClanAccountInfo } from '@blitzkit/core';
|
||||
import { Locale, SlashCommandSubcommandGroupBuilder } from 'discord.js';
|
||||
import { CommandWrapper } from '../components/CommandWrapper';
|
||||
import * as Graph from '../components/Graph';
|
||||
import { LineColor } from '../components/Graph/components/Line/constants';
|
||||
import { NoData } from '../components/NoData';
|
||||
import { TitleBar } from '../components/TitleBar';
|
||||
import { resolveTankId } from '../core/blitz/resolveTankId';
|
||||
import { getBlitzStarsLinkButton } from '../core/blitzstars/getBlitzStarsLinkButton';
|
||||
import { getPlayerHistories } from '../core/blitzstars/getPlayerHistories';
|
||||
import { getTankHistories } from '../core/blitzstars/getTankHistories';
|
||||
import { addPeriodSubCommands } from '../core/discord/addPeriodSubCommands';
|
||||
import { addTankChoices } from '../core/discord/addTankChoices';
|
||||
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
|
||||
import { autocompleteTanks } from '../core/discord/autocompleteTanks';
|
||||
import { autocompleteUsername } from '../core/discord/autocompleteUsername';
|
||||
import { buttonRefresh } from '../core/discord/buttonRefresh';
|
||||
import { commandToURL } from '../core/discord/commandToURL';
|
||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||
import { getCustomPeriodParams } from '../core/discord/getCustomPeriodParams';
|
||||
import { resolvePeriodFromButton } from '../core/discord/resolvePeriodFromButton';
|
||||
import { emblemURL, getAccountInfo, getClanAccountInfo } from "@blitzkit/core";
|
||||
import { Locale, SlashCommandSubcommandGroupBuilder } from "discord.js";
|
||||
import { CommandWrapper } from "../components/CommandWrapper";
|
||||
import * as Graph from "../components/Graph";
|
||||
import { LineColor } from "../components/Graph/components/Line/constants";
|
||||
import { NoData } from "../components/NoData";
|
||||
import { TitleBar } from "../components/TitleBar";
|
||||
import { resolveTankId } from "../core/blitz/resolveTankId";
|
||||
import { getBlitzStarsLinkButton } from "../core/blitzstars/getBlitzStarsLinkButton";
|
||||
import { getPlayerHistories } from "../core/blitzstars/getPlayerHistories";
|
||||
import { getTankHistories } from "../core/blitzstars/getTankHistories";
|
||||
import { addPeriodSubCommands } from "../core/discord/addPeriodSubCommands";
|
||||
import { addTankChoices } from "../core/discord/addTankChoices";
|
||||
import { addUsernameChoices } from "../core/discord/addUsernameChoices";
|
||||
import { autocompleteTanks } from "../core/discord/autocompleteTanks";
|
||||
import { autocompleteUsername } from "../core/discord/autocompleteUsername";
|
||||
import { buttonRefresh } from "../core/discord/buttonRefresh";
|
||||
import { commandToURL } from "../core/discord/commandToURL";
|
||||
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||
import { getCustomPeriodParams } from "../core/discord/getCustomPeriodParams";
|
||||
import { resolvePeriodFromButton } from "../core/discord/resolvePeriodFromButton";
|
||||
import {
|
||||
ResolvedPeriod,
|
||||
type ResolvedPeriod,
|
||||
resolvePeriodFromCommand,
|
||||
} from '../core/discord/resolvePeriodFromCommand';
|
||||
import { resolvePlayerFromButton } from '../core/discord/resolvePlayerFromButton';
|
||||
} from "../core/discord/resolvePeriodFromCommand";
|
||||
import { resolvePlayerFromButton } from "../core/discord/resolvePlayerFromButton";
|
||||
import {
|
||||
ResolvedPlayer,
|
||||
type ResolvedPlayer,
|
||||
resolvePlayerFromCommand,
|
||||
} from '../core/discord/resolvePlayerFromCommand';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
} from "../core/discord/resolvePlayerFromCommand";
|
||||
import type { CommandRegistry } from "../events/interactionCreate";
|
||||
|
||||
type EvolutionStatType = 'player' | 'tank';
|
||||
type EvolutionStatType = "player" | "tank";
|
||||
|
||||
async function render(
|
||||
{ region, id }: ResolvedPlayer,
|
||||
{ start, end, name }: ResolvedPeriod,
|
||||
type: 'player' | 'tank',
|
||||
type: "player" | "tank",
|
||||
tankId: number | null,
|
||||
locale: Locale,
|
||||
locale: Locale
|
||||
) {
|
||||
const accountInfo = await getAccountInfo(region, id);
|
||||
const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan;
|
||||
const clan = (await getClanAccountInfo(region, id, ["clan"]))?.clan;
|
||||
const logo = clan ? emblemURL(clan.emblem_set_id) : undefined;
|
||||
const histories = await (type === 'player'
|
||||
const histories = await (type === "player"
|
||||
? getPlayerHistories(region, id, {
|
||||
start,
|
||||
end,
|
||||
@@ -61,7 +61,7 @@ async function render(
|
||||
[
|
||||
history.all.battles,
|
||||
(history.all.wins / history.all.battles) * 100,
|
||||
] as Graph.PlotItem,
|
||||
] as Graph.PlotItem
|
||||
);
|
||||
const times = histories.map((history) => history.last_battle_time);
|
||||
const minTime = Math.min(...times);
|
||||
@@ -82,7 +82,7 @@ async function render(
|
||||
verticalMargin={{
|
||||
min: minY,
|
||||
max: maxY,
|
||||
suffix: '%',
|
||||
suffix: "%",
|
||||
precision: 2,
|
||||
}}
|
||||
horizontalMargin={{
|
||||
@@ -110,22 +110,22 @@ async function render(
|
||||
export const evolutionCommand = new Promise<CommandRegistry>(
|
||||
async (resolve) => {
|
||||
resolve({
|
||||
command: createLocalizedCommand('evolution', [
|
||||
command: createLocalizedCommand("evolution", [
|
||||
{
|
||||
group: 'player',
|
||||
group: "player",
|
||||
modify(option: SlashCommandSubcommandGroupBuilder) {
|
||||
addPeriodSubCommands(option, (option) =>
|
||||
option.addStringOption(addUsernameChoices),
|
||||
option.addStringOption(addUsernameChoices)
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
group: 'tank',
|
||||
group: "tank",
|
||||
modify(option: SlashCommandSubcommandGroupBuilder) {
|
||||
addPeriodSubCommands(option, (option) =>
|
||||
option
|
||||
.addStringOption(addTankChoices)
|
||||
.addStringOption(addUsernameChoices),
|
||||
.addStringOption(addUsernameChoices)
|
||||
);
|
||||
},
|
||||
},
|
||||
@@ -136,9 +136,9 @@ export const evolutionCommand = new Promise<CommandRegistry>(
|
||||
interaction.options.getSubcommandGroup() as EvolutionStatType;
|
||||
const player = await resolvePlayerFromCommand(interaction);
|
||||
const period = resolvePeriodFromCommand(player.region, interaction);
|
||||
const tankIdRaw = interaction.options.getString('tank')!;
|
||||
const tankIdRaw = interaction.options.getString("tank")!;
|
||||
const tankId =
|
||||
commandGroup === 'tank'
|
||||
commandGroup === "tank"
|
||||
? await resolveTankId(tankIdRaw, interaction.locale)
|
||||
: null;
|
||||
const path = commandToURL(interaction, {
|
||||
@@ -153,13 +153,13 @@ export const evolutionCommand = new Promise<CommandRegistry>(
|
||||
period,
|
||||
commandGroup,
|
||||
tankId,
|
||||
interaction.locale,
|
||||
interaction.locale
|
||||
),
|
||||
buttonRefresh(interaction, path),
|
||||
await getBlitzStarsLinkButton(
|
||||
player.region,
|
||||
player.id,
|
||||
tankId ?? undefined,
|
||||
tankId ?? undefined
|
||||
),
|
||||
];
|
||||
},
|
||||
@@ -171,7 +171,7 @@ export const evolutionCommand = new Promise<CommandRegistry>(
|
||||
|
||||
async button(interaction) {
|
||||
const url = new URL(`https://example.com/${interaction.customId}`);
|
||||
const path = url.pathname.split('/');
|
||||
const path = url.pathname.split("/");
|
||||
const commandGroup = path[2] as EvolutionStatType;
|
||||
const player = await resolvePlayerFromButton(interaction);
|
||||
const period = resolvePeriodFromButton(player.region, interaction);
|
||||
@@ -180,10 +180,10 @@ export const evolutionCommand = new Promise<CommandRegistry>(
|
||||
player,
|
||||
period,
|
||||
commandGroup,
|
||||
parseInt(url.searchParams.get('tankId') ?? '0') || null,
|
||||
interaction.locale,
|
||||
parseInt(url.searchParams.get("tankId") ?? "0") || null,
|
||||
interaction.locale
|
||||
);
|
||||
},
|
||||
});
|
||||
},
|
||||
}
|
||||
);
|
||||
@@ -11,7 +11,7 @@ import { NoData } from '../components/NoData';
|
||||
import { TierWeights } from '../components/TierWeights';
|
||||
import { TitleBar } from '../components/TitleBar';
|
||||
import { filtersToDescription } from '../core/blitzkit/filtersToDescription';
|
||||
import { filterStats, StatFilters } from '../core/blitzstars/filterStats';
|
||||
import { filterStats, type StatFilters } from '../core/blitzstars/filterStats';
|
||||
import { getBlitzStarsLinkButton } from '../core/blitzstars/getBlitzStarsLinkButton';
|
||||
import { getStatsInPeriod } from '../core/blitzstars/getStatsInPeriod';
|
||||
import { addPeriodicFilterOptions } from '../core/discord/addPeriodicFilterOptions';
|
||||
@@ -26,15 +26,15 @@ import { getFiltersFromButton } from '../core/discord/getFiltersFromButton';
|
||||
import { getFiltersFromCommand } from '../core/discord/getFiltersFromCommand';
|
||||
import { resolvePeriodFromButton } from '../core/discord/resolvePeriodFromButton';
|
||||
import {
|
||||
ResolvedPeriod,
|
||||
type ResolvedPeriod,
|
||||
resolvePeriodFromCommand,
|
||||
} from '../core/discord/resolvePeriodFromCommand';
|
||||
import { resolvePlayerFromButton } from '../core/discord/resolvePlayerFromButton';
|
||||
import {
|
||||
ResolvedPlayer,
|
||||
type ResolvedPlayer,
|
||||
resolvePlayerFromCommand,
|
||||
} from '../core/discord/resolvePlayerFromCommand';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
import type { CommandRegistry } from '../events/interactionCreate';
|
||||
|
||||
async function render(
|
||||
{ region, id }: ResolvedPlayer,
|
||||
|
||||
@@ -1,44 +1,44 @@
|
||||
import { getAccountInfo, getClanInfo } from '@blitzkit/core';
|
||||
import { literals } from '@blitzkit/i18n';
|
||||
import { Locale } from 'discord.js';
|
||||
import { CommandWrapper } from '../components/CommandWrapper';
|
||||
import { GenericStats } from '../components/GenericStats';
|
||||
import { NoData } from '../components/NoData';
|
||||
import { TitleBar } from '../components/TitleBar';
|
||||
import { addClanChoices } from '../core/discord/addClanChoices';
|
||||
import { autocompleteClan } from '../core/discord/autocompleteClan';
|
||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||
import { localizationObject } from '../core/discord/localizationObject';
|
||||
import { resolveClanFromCommand } from '../core/discord/resolveClanFromCommand';
|
||||
import { translator } from '../core/localization/translator';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
import { getAccountInfo, getClanInfo } from "@blitzkit/core";
|
||||
import { literals } from "@blitzkit/i18n";
|
||||
import { Locale } from "discord.js";
|
||||
import { CommandWrapper } from "../components/CommandWrapper";
|
||||
import { GenericStats } from "../components/GenericStats";
|
||||
import { NoData } from "../components/NoData";
|
||||
import { TitleBar } from "../components/TitleBar";
|
||||
import { addClanChoices } from "../core/discord/addClanChoices";
|
||||
import { autocompleteClan } from "../core/discord/autocompleteClan";
|
||||
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||
import { localizationObject } from "../core/discord/localizationObject";
|
||||
import { resolveClanFromCommand } from "../core/discord/resolveClanFromCommand";
|
||||
import { translator } from "../core/localization/translator";
|
||||
import type { CommandRegistry } from "../events/interactionCreate";
|
||||
|
||||
const DEFAULT_THRESHOLD = 7;
|
||||
|
||||
export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
resolve({
|
||||
command: createLocalizedCommand('inactive')
|
||||
command: createLocalizedCommand("inactive")
|
||||
.addStringOption(addClanChoices)
|
||||
.addNumberOption((option) => {
|
||||
const { strings } = translator(Locale.EnglishUS);
|
||||
|
||||
return option
|
||||
.setName('threshold')
|
||||
.setName("threshold")
|
||||
.setNameLocalizations(
|
||||
localizationObject(
|
||||
(strings) => strings.bot.commands.inactive.options.threshold.name,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
true
|
||||
)
|
||||
)
|
||||
.setDescription(
|
||||
strings.bot.commands.inactive.options.threshold.description,
|
||||
strings.bot.commands.inactive.options.threshold.description
|
||||
)
|
||||
.setDescriptionLocalizations(
|
||||
localizationObject(
|
||||
(strings) =>
|
||||
strings.bot.commands.inactive.options.threshold.description,
|
||||
),
|
||||
strings.bot.commands.inactive.options.threshold.description
|
||||
)
|
||||
)
|
||||
.setMinValue(0);
|
||||
}),
|
||||
@@ -47,7 +47,7 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
const { strings } = translator(interaction.locale);
|
||||
const { region, id } = await resolveClanFromCommand(interaction);
|
||||
const threshold =
|
||||
interaction.options.getNumber('threshold')! ?? DEFAULT_THRESHOLD;
|
||||
interaction.options.getNumber("threshold")! ?? DEFAULT_THRESHOLD;
|
||||
const time = new Date().getTime() / 1000;
|
||||
const clanInfo = await getClanInfo(region, id);
|
||||
const accountInfo = await getAccountInfo(region, clanInfo.members_ids);
|
||||
@@ -57,7 +57,7 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
[
|
||||
account.nickname,
|
||||
(time - account.last_battle_time) / 60 / 60 / 24,
|
||||
] as [string, number],
|
||||
] as [string, number]
|
||||
)
|
||||
.filter(([, inactiveDays]) => inactiveDays >= threshold)
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
@@ -65,10 +65,10 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
([name, days]) =>
|
||||
[
|
||||
name,
|
||||
literals(strings.bot.commands.inactive.body.listing, [
|
||||
days.toFixed(0),
|
||||
]),
|
||||
] as [string, string],
|
||||
literals(strings.bot.commands.inactive.body.listing, {
|
||||
days: days.toFixed(0),
|
||||
}),
|
||||
] as [string, string]
|
||||
);
|
||||
const hasInactiveMembers = inactive.length > 0;
|
||||
|
||||
@@ -79,7 +79,7 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
image={`https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clanInfo.emblem_set_id}.png`}
|
||||
description={`${literals(
|
||||
strings.bot.commands.inactive.body.subtitle,
|
||||
[`${threshold}`],
|
||||
{ days: threshold }
|
||||
)} • ${new Date().toLocaleDateString(interaction.locale)}`}
|
||||
/>
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||
import { embedInfo } from '../core/discord/embedInfo';
|
||||
import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand';
|
||||
import { translator } from '../core/localization/translator';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
import type { CommandRegistry } from '../events/interactionCreate';
|
||||
import { theme } from '../stitches.config';
|
||||
|
||||
export const verifyCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
|
||||
@@ -5,31 +5,31 @@ import {
|
||||
flags,
|
||||
getAccountInfo,
|
||||
getTankStats,
|
||||
} from '@blitzkit/core';
|
||||
import { literals } from '@blitzkit/i18n';
|
||||
import markdownEscape from 'markdown-escape';
|
||||
} from "@blitzkit/core";
|
||||
import { literals } from "@blitzkit/i18n";
|
||||
import markdownEscape from "markdown-escape";
|
||||
import {
|
||||
gameDefinitions,
|
||||
tankDefinitions,
|
||||
} from '../core/blitzkit/nonBlockingPromises';
|
||||
import { addTierChoices } from '../core/discord/addTierChoices';
|
||||
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
|
||||
import { autocompleteUsername } from '../core/discord/autocompleteUsername';
|
||||
import { chunkLines } from '../core/discord/chunkLines';
|
||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||
import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand';
|
||||
import { translator } from '../core/localization/translator';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
} from "../core/blitzkit/nonBlockingPromises";
|
||||
import { addTierChoices } from "../core/discord/addTierChoices";
|
||||
import { addUsernameChoices } from "../core/discord/addUsernameChoices";
|
||||
import { autocompleteUsername } from "../core/discord/autocompleteUsername";
|
||||
import { chunkLines } from "../core/discord/chunkLines";
|
||||
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||
import { resolvePlayerFromCommand } from "../core/discord/resolvePlayerFromCommand";
|
||||
import { translator } from "../core/localization/translator";
|
||||
import { type CommandRegistry } from "../events/interactionCreate";
|
||||
|
||||
export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
resolve({
|
||||
command: createLocalizedCommand('owned-tanks')
|
||||
command: createLocalizedCommand("owned-tanks")
|
||||
.addStringOption(addTierChoices)
|
||||
.addStringOption(addUsernameChoices),
|
||||
|
||||
async handler(interaction) {
|
||||
const { strings, unwrap } = translator(interaction.locale);
|
||||
const tier = Number(interaction.options.getString('tier'));
|
||||
const tier = Number(interaction.options.getString("tier"));
|
||||
const { id, region } = await resolvePlayerFromCommand(interaction);
|
||||
const accountInfo = await getAccountInfo(region, id);
|
||||
const tankStats = await getTankStats(region, id);
|
||||
@@ -43,7 +43,7 @@ export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
tankStats.map(async (tankData) => ({
|
||||
tankDefinitions: (await tankDefinitions).tanks[tankData.tank_id]!,
|
||||
id: tankData.tank_id,
|
||||
})),
|
||||
}))
|
||||
)
|
||||
).filter((tank) => tank.tankDefinitions?.tier === tier);
|
||||
const groupedTanks: Record<string, TankDefinition[]> = {};
|
||||
@@ -60,25 +60,25 @@ export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
|
||||
const awaitedTankDefinitions = await tankDefinitions;
|
||||
const awaitedGameDefinitions = await gameDefinitions;
|
||||
const title = literals(strings.bot.commands.owned_tanks.body.title, [
|
||||
markdownEscape(accountInfo.nickname),
|
||||
TIER_ROMAN_NUMERALS[tier],
|
||||
]);
|
||||
const title = literals(strings.bot.commands.owned_tanks.body.title, {
|
||||
name: markdownEscape(accountInfo.nickname),
|
||||
tier: TIER_ROMAN_NUMERALS[tier],
|
||||
});
|
||||
const tankList = tankStats
|
||||
.map(({ tank_id }) => awaitedTankDefinitions.tanks[tank_id])
|
||||
.filter((tank) => tank.tier === tier)
|
||||
.sort(
|
||||
(a, b) =>
|
||||
TANK_CLASSES.indexOf(a.class) - TANK_CLASSES.indexOf(b.class),
|
||||
TANK_CLASSES.indexOf(a.class) - TANK_CLASSES.indexOf(b.class)
|
||||
)
|
||||
.sort(
|
||||
(a, b) =>
|
||||
awaitedGameDefinitions.nations.indexOf(a.nation) -
|
||||
awaitedGameDefinitions.nations.indexOf(b.nation),
|
||||
awaitedGameDefinitions.nations.indexOf(b.nation)
|
||||
)
|
||||
.map(
|
||||
(tank) =>
|
||||
`[${flags[tank.nation]} ${markdownEscape(unwrap(tank.name))}](<https://blitzkit.app/tanks/${tank.slug}>)`,
|
||||
`[${flags[tank.nation]} ${markdownEscape(unwrap(tank.name))}](<https://blitzkit.app/tanks/${tank.slug}>)`
|
||||
);
|
||||
const lines = [title, ...tankList];
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { literals } from '@blitzkit/i18n';
|
||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||
import { translator } from '../core/localization/translator';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
import { literals } from "@blitzkit/i18n";
|
||||
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||
import { translator } from "../core/localization/translator";
|
||||
import { CommandRegistry } from "../events/interactionCreate";
|
||||
|
||||
export const permissionsCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
resolve({
|
||||
command: createLocalizedCommand('permissions'),
|
||||
command: createLocalizedCommand("permissions"),
|
||||
|
||||
async handler(interaction) {
|
||||
const { strings } = translator(interaction.locale);
|
||||
@@ -14,33 +14,33 @@ export const permissionsCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
new Date().getDay() === 5 && new Date().getDate() === 13;
|
||||
const permissions = [
|
||||
[
|
||||
interaction.appPermissions?.has('ViewChannel'),
|
||||
interaction.appPermissions?.has("ViewChannel"),
|
||||
strings.bot.commands.permissions.body.view_channels,
|
||||
],
|
||||
[
|
||||
interaction.appPermissions?.has('ReadMessageHistory'),
|
||||
interaction.appPermissions?.has("ReadMessageHistory"),
|
||||
strings.bot.commands.permissions.body.read_message_history,
|
||||
],
|
||||
[
|
||||
interaction.appPermissions?.has('AttachFiles'),
|
||||
interaction.appPermissions?.has("AttachFiles"),
|
||||
strings.bot.commands.permissions.body.attach_files,
|
||||
],
|
||||
[
|
||||
interaction.appPermissions?.has('UseExternalEmojis'),
|
||||
interaction.appPermissions?.has("UseExternalEmojis"),
|
||||
strings.bot.commands.permissions.body.external_emojis,
|
||||
],
|
||||
];
|
||||
const body = permissions
|
||||
.map(([hasPermission, description]) => {
|
||||
if (hasPermission) {
|
||||
return `${isFridayThe13th ? '😇' : '✅'} ${description}`;
|
||||
return `${isFridayThe13th ? "😇" : "✅"} ${description}`;
|
||||
} else {
|
||||
return `${isFridayThe13th ? '💀' : '❌'} ${description}`;
|
||||
return `${isFridayThe13th ? "💀" : "❌"} ${description}`;
|
||||
}
|
||||
})
|
||||
.join('\n');
|
||||
.join("\n");
|
||||
|
||||
return literals(strings.bot.commands.permissions.body.title, [body]);
|
||||
return literals(strings.bot.commands.permissions.body.title, { body });
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -1,15 +1,15 @@
|
||||
import { literals } from '@blitzkit/i18n';
|
||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||
import { translator } from '../core/localization/translator';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
import { literals } from "@blitzkit/i18n";
|
||||
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||
import { translator } from "../core/localization/translator";
|
||||
import type { CommandRegistry } from "../events/interactionCreate";
|
||||
|
||||
export const pingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
resolve({
|
||||
handlesInteraction: true,
|
||||
|
||||
command: createLocalizedCommand('ping', [
|
||||
{ subcommand: 'blitzkit' },
|
||||
{ subcommand: 'wotb' },
|
||||
command: createLocalizedCommand("ping", [
|
||||
{ subcommand: "blitzkit" },
|
||||
{ subcommand: "wotb" },
|
||||
]),
|
||||
|
||||
async handler(interaction) {
|
||||
@@ -17,17 +17,17 @@ export const pingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
const subcommand = interaction.options.getSubcommand();
|
||||
const executionStart = Date.now();
|
||||
|
||||
if (subcommand === 'blitzkit') {
|
||||
if (subcommand === "blitzkit") {
|
||||
await interaction.editReply(strings.bot.commands.ping.body.pong);
|
||||
} else {
|
||||
await fetch('https://api.wotblitz.com/');
|
||||
await fetch("https://api.wotblitz.com/");
|
||||
}
|
||||
|
||||
const executionTime = Date.now() - executionStart;
|
||||
interaction.editReply(
|
||||
literals(strings.bot.commands.ping.body.pong_with_time, [
|
||||
`${Math.round(executionTime)}`,
|
||||
]),
|
||||
literals(strings.bot.commands.ping.body.pong_with_time, {
|
||||
time: executionTime.toFixed(0),
|
||||
})
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { getAccountInfo } from '@blitzkit/core';
|
||||
import { literals } from '@blitzkit/i18n';
|
||||
import markdownEscape from 'markdown-escape';
|
||||
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
|
||||
import { autocompleteUsername } from '../core/discord/autocompleteUsername';
|
||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||
import { embedInfo } from '../core/discord/embedInfo';
|
||||
import { markdownTable } from '../core/discord/markdownTable';
|
||||
import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand';
|
||||
import { translator } from '../core/localization/translator';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
import { getAccountInfo } from "@blitzkit/core";
|
||||
import { literals } from "@blitzkit/i18n";
|
||||
import markdownEscape from "markdown-escape";
|
||||
import { addUsernameChoices } from "../core/discord/addUsernameChoices";
|
||||
import { autocompleteUsername } from "../core/discord/autocompleteUsername";
|
||||
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||
import { embedInfo } from "../core/discord/embedInfo";
|
||||
import { markdownTable } from "../core/discord/markdownTable";
|
||||
import { resolvePlayerFromCommand } from "../core/discord/resolvePlayerFromCommand";
|
||||
import { translator } from "../core/localization/translator";
|
||||
import { CommandRegistry } from "../events/interactionCreate";
|
||||
|
||||
export const playerInfoCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
resolve({
|
||||
command:
|
||||
createLocalizedCommand('player-info').addStringOption(addUsernameChoices),
|
||||
createLocalizedCommand("player-info").addStringOption(addUsernameChoices),
|
||||
|
||||
async handler(interaction) {
|
||||
const { strings } = translator(interaction.locale);
|
||||
@@ -22,9 +22,9 @@ export const playerInfoCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
const accountInfo = await getAccountInfo(region, id);
|
||||
|
||||
return embedInfo(
|
||||
literals(strings.bot.commands.player_info.body.title, [
|
||||
markdownEscape(accountInfo.nickname),
|
||||
]),
|
||||
literals(strings.bot.commands.player_info.body.title, {
|
||||
name: markdownEscape(accountInfo.nickname),
|
||||
}),
|
||||
|
||||
markdownTable([
|
||||
[
|
||||
@@ -51,16 +51,16 @@ export const playerInfoCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
[
|
||||
strings.bot.commands.player_info.body.created,
|
||||
new Date(accountInfo.created_at * 1000).toLocaleDateString(
|
||||
interaction.locale,
|
||||
interaction.locale
|
||||
),
|
||||
],
|
||||
[
|
||||
strings.bot.commands.player_info.body.last_battle,
|
||||
new Date(accountInfo.last_battle_time * 1000).toLocaleDateString(
|
||||
interaction.locale,
|
||||
interaction.locale
|
||||
),
|
||||
],
|
||||
]),
|
||||
])
|
||||
);
|
||||
},
|
||||
|
||||
|
||||
@@ -8,42 +8,42 @@ import {
|
||||
getRatingInfo,
|
||||
getRatingNeighbors,
|
||||
imgur,
|
||||
} from '@blitzkit/core';
|
||||
import { literals } from '@blitzkit/i18n';
|
||||
import { SlashCommandSubcommandBuilder } from 'discord.js';
|
||||
import markdownEscape from 'markdown-escape';
|
||||
import { Glow } from '../components/AllStatsOverview/components/HeroStat/components/Glow';
|
||||
import { CommandWrapper } from '../components/CommandWrapper';
|
||||
import { DeltaCaret } from '../components/DeltaCaret';
|
||||
import { TitleBar } from '../components/TitleBar';
|
||||
import { normalizeLeagueIcon } from '../core/blitz/normalizeLeagueIcon';
|
||||
import { iconPng } from '../core/blitzkit/iconPng';
|
||||
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
|
||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||
import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand';
|
||||
import { translator } from '../core/localization/translator';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
import { theme } from '../stitches.config';
|
||||
} from "@blitzkit/core";
|
||||
import { literals } from "@blitzkit/i18n";
|
||||
import { SlashCommandSubcommandBuilder } from "discord.js";
|
||||
import markdownEscape from "markdown-escape";
|
||||
import { Glow } from "../components/AllStatsOverview/components/HeroStat/components/Glow";
|
||||
import { CommandWrapper } from "../components/CommandWrapper";
|
||||
import { DeltaCaret } from "../components/DeltaCaret";
|
||||
import { TitleBar } from "../components/TitleBar";
|
||||
import { normalizeLeagueIcon } from "../core/blitz/normalizeLeagueIcon";
|
||||
import { iconPng } from "../core/blitzkit/iconPng";
|
||||
import { addUsernameChoices } from "../core/discord/addUsernameChoices";
|
||||
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||
import { resolvePlayerFromCommand } from "../core/discord/resolvePlayerFromCommand";
|
||||
import { translator } from "../core/localization/translator";
|
||||
import type { CommandRegistry } from "../events/interactionCreate";
|
||||
import { theme } from "../stitches.config";
|
||||
|
||||
const LEAGUE_ACCENT = [
|
||||
'_purple',
|
||||
'_sand',
|
||||
'_amber',
|
||||
'_mauve',
|
||||
'_brown',
|
||||
"_purple",
|
||||
"_sand",
|
||||
"_amber",
|
||||
"_mauve",
|
||||
"_brown",
|
||||
] as const;
|
||||
|
||||
export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
resolve({
|
||||
command: createLocalizedCommand('rating', [
|
||||
command: createLocalizedCommand("rating", [
|
||||
{
|
||||
subcommand: 'today',
|
||||
subcommand: "today",
|
||||
modify(option: SlashCommandSubcommandBuilder) {
|
||||
option.addStringOption(addUsernameChoices);
|
||||
},
|
||||
},
|
||||
{
|
||||
subcommand: 'season',
|
||||
subcommand: "season",
|
||||
modify(option: SlashCommandSubcommandBuilder) {
|
||||
option.addStringOption(addUsernameChoices);
|
||||
},
|
||||
@@ -52,11 +52,11 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
|
||||
async handler(interaction) {
|
||||
const subcommand = interaction.options.getSubcommand(true) as
|
||||
| 'season'
|
||||
| 'today';
|
||||
| "season"
|
||||
| "today";
|
||||
const { strings } = translator(interaction.locale);
|
||||
const { id, region } = await resolvePlayerFromCommand(interaction);
|
||||
const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan;
|
||||
const clan = (await getClanAccountInfo(region, id, ["clan"]))?.clan;
|
||||
const clanImage = clan ? emblemURL(clan.emblem_set_id) : undefined;
|
||||
|
||||
const ratingInfo = await getRatingInfo(region);
|
||||
@@ -67,20 +67,20 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
|
||||
const leaderboard = await getArchivedRatingLeaderboard(
|
||||
region,
|
||||
ratingInfo.current_season - (subcommand === 'today' ? 0 : 1),
|
||||
ratingInfo.current_season - (subcommand === "today" ? 0 : 1)
|
||||
);
|
||||
|
||||
if (leaderboard.version!.$case === 'v1') {
|
||||
if (leaderboard.version!.$case === "v1") {
|
||||
throw new Error(
|
||||
'Encountered bkrl minimal format in latest season. Wait till next cron?',
|
||||
"Encountered bkrl minimal format in latest season. Wait till next cron?"
|
||||
);
|
||||
}
|
||||
|
||||
const accountInfo = await getAccountInfo(region, id, [
|
||||
'statistics.rating',
|
||||
"statistics.rating",
|
||||
]);
|
||||
const statsIndex = leaderboard.version!.value.entries.findIndex(
|
||||
(entry) => entry.id === id,
|
||||
(entry) => entry.id === id
|
||||
);
|
||||
const statsA = leaderboard
|
||||
? leaderboard.version!.value.entries[statsIndex!]
|
||||
@@ -90,9 +90,9 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
const statsB2Array = (await getRatingNeighbors(region, id, 0)).neighbors;
|
||||
|
||||
if (statsB2Array === undefined) {
|
||||
return literals(strings.bot.commands.rating.errors.no_participation, [
|
||||
markdownEscape(accountInfo.nickname),
|
||||
]);
|
||||
return literals(strings.bot.commands.rating.errors.no_participation, {
|
||||
name: markdownEscape(accountInfo.nickname),
|
||||
});
|
||||
}
|
||||
|
||||
const [statsB2] = statsB2Array;
|
||||
@@ -102,15 +102,15 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
return strings.bot.commands.rating.errors.unrecorded_stats;
|
||||
}
|
||||
if (!statsB1) {
|
||||
return literals(strings.bot.commands.rating.errors.no_participation, [
|
||||
markdownEscape(accountInfo.nickname),
|
||||
]);
|
||||
return literals(strings.bot.commands.rating.errors.no_participation, {
|
||||
name: markdownEscape(accountInfo.nickname),
|
||||
});
|
||||
}
|
||||
|
||||
const reward = ratingInfo.rewards.find(
|
||||
(reward) =>
|
||||
statsB2.number >= reward.from_position &&
|
||||
statsB2.number <= reward.to_position,
|
||||
statsB2.number <= reward.to_position
|
||||
);
|
||||
const positionDelta = leaderboard
|
||||
? statsB2.number - positionA!
|
||||
@@ -118,11 +118,11 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
const delta = deltaBkrlBlitzStats(
|
||||
statsA,
|
||||
accountInfo.statistics.rating!,
|
||||
statsB2,
|
||||
statsB2
|
||||
);
|
||||
const leagueTheme = LEAGUE_ACCENT[league.index];
|
||||
let rewardImage = reward
|
||||
? reward.type === 'vehicle'
|
||||
? reward.type === "vehicle"
|
||||
? reward.vehicle.preview_image_url
|
||||
: reward.stuff.image_url
|
||||
: undefined;
|
||||
@@ -131,7 +131,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
const { ok } = await fetch(rewardImage);
|
||||
|
||||
if (!ok) {
|
||||
rewardImage = imgur('kP88vqr');
|
||||
rewardImage = imgur("kP88vqr");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,24 +147,24 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
|
||||
<div
|
||||
style={{
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
overflow: 'hidden',
|
||||
position: "relative",
|
||||
display: "flex",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
position: 'relative',
|
||||
position: "relative",
|
||||
padding: 16,
|
||||
backgroundColor: theme.colors[`appBackground2${leagueTheme}`],
|
||||
borderRadius: 4,
|
||||
border: `1px solid ${
|
||||
theme.colors[`componentInteractive${leagueTheme}`]
|
||||
}`,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
width: '100%',
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
{reward === undefined && (
|
||||
@@ -172,17 +172,17 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
endOpacity={3 / 4}
|
||||
color={theme.colors[`solidBackground${leagueTheme}`]}
|
||||
style={{
|
||||
transform: 'translateX(-16px) rotate(90deg)',
|
||||
transform: "translateX(-16px) rotate(90deg)",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
gap: 8,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
@@ -190,24 +190,24 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
style={{
|
||||
width: 64,
|
||||
height: 64,
|
||||
objectFit: 'contain',
|
||||
objectFit: "contain",
|
||||
}}
|
||||
src={normalizeLeagueIcon(
|
||||
ratingInfo.leagues[league.index].big_icon,
|
||||
ratingInfo.leagues[league.index].big_icon
|
||||
)}
|
||||
/>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-start",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
@@ -232,7 +232,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
}}
|
||||
>
|
||||
{Math.abs(delta.score).toLocaleString(
|
||||
interaction.locale,
|
||||
interaction.locale
|
||||
)}
|
||||
</span>
|
||||
</>
|
||||
@@ -241,9 +241,9 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-start",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
@@ -267,7 +267,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
}}
|
||||
>
|
||||
{Math.abs(positionDelta).toLocaleString(
|
||||
interaction.locale,
|
||||
interaction.locale
|
||||
)}
|
||||
</span>
|
||||
</>
|
||||
@@ -279,23 +279,23 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
{reward && (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
gap: 8,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-end',
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-end",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
@@ -303,13 +303,13 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
color: theme.colors[`textHighContrast${leagueTheme}`],
|
||||
fontSize: 16,
|
||||
maxWidth: 136,
|
||||
textAlign: 'right',
|
||||
whiteSpace: 'nowrap',
|
||||
textOverflow: 'ellipsis',
|
||||
overflow: 'hidden',
|
||||
textAlign: "right",
|
||||
whiteSpace: "nowrap",
|
||||
textOverflow: "ellipsis",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
{reward.type === 'vehicle'
|
||||
{reward.type === "vehicle"
|
||||
? reward.vehicle.user_string
|
||||
: reward.stuff.title}
|
||||
</span>
|
||||
@@ -317,9 +317,9 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-end',
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-end",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
@@ -329,7 +329,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
}}
|
||||
>
|
||||
Reward
|
||||
{reward.type === 'stuff' ? ` x${reward.count}` : ''}
|
||||
{reward.type === "stuff" ? ` x${reward.count}` : ""}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -338,7 +338,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
style={{
|
||||
width: 64,
|
||||
height: 64,
|
||||
objectFit: 'cover',
|
||||
objectFit: "cover",
|
||||
}}
|
||||
src={await iconPng(rewardImage!)}
|
||||
/>
|
||||
@@ -350,7 +350,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
endOpacity={3 / 4}
|
||||
color={theme.colors[`solidBackground${leagueTheme}`]}
|
||||
style={{
|
||||
transform: 'translateX(16px) rotate(-90deg)',
|
||||
transform: "translateX(16px) rotate(-90deg)",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -362,10 +362,10 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
direction="reverse"
|
||||
color={theme.colors[`solidBackground${leagueTheme}`]}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
position: "absolute",
|
||||
top: -40,
|
||||
left: '50%',
|
||||
transform: 'translateX(-50%)',
|
||||
left: "50%",
|
||||
transform: "translateX(-50%)",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -373,32 +373,32 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
<Glow
|
||||
color={theme.colors[`solidBackground${leagueTheme}`]}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
position: "absolute",
|
||||
bottom: -40,
|
||||
left: '50%',
|
||||
transform: 'translateX(-50%)',
|
||||
left: "50%",
|
||||
transform: "translateX(-50%)",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{delta.battles > 0 && (
|
||||
<div style={{ padding: '8px 0', display: 'flex' }}>
|
||||
<div style={{ padding: "8px 0", display: "flex" }}>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: 4,
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
fontSize: 24,
|
||||
color: theme.colors.textHighContrast,
|
||||
fontWeight: 'bold',
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
{delta.battles.toLocaleString(interaction.locale)}
|
||||
@@ -414,19 +414,19 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: 4,
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
fontSize: 24,
|
||||
color: theme.colors.textHighContrast,
|
||||
fontWeight: 'bold',
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
{(100 * (delta.wins / delta.battles)).toFixed(0)}%
|
||||
@@ -442,23 +442,23 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: 4,
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
fontSize: 24,
|
||||
color: theme.colors.textHighContrast,
|
||||
fontWeight: 'bold',
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
{Math.round(delta.damage / delta.battles).toLocaleString(
|
||||
interaction.locale,
|
||||
interaction.locale
|
||||
)}
|
||||
</span>
|
||||
<span
|
||||
@@ -472,19 +472,19 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: 4,
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
fontSize: 24,
|
||||
color: theme.colors.textHighContrast,
|
||||
fontWeight: 'bold',
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
{(100 * (delta.survived / delta.battles)).toFixed(0)}%
|
||||
@@ -500,19 +500,19 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: 4,
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
fontSize: 24,
|
||||
color: theme.colors.textHighContrast,
|
||||
fontWeight: 'bold',
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
{(delta.kills / delta.battles).toFixed(2)}
|
||||
|
||||
@@ -10,30 +10,30 @@ import {
|
||||
imgur,
|
||||
isOnGoingRatingSeason,
|
||||
LEAGUES,
|
||||
RatingInfo,
|
||||
type RatingInfo,
|
||||
RatingLeaderboard,
|
||||
Region,
|
||||
} from '@blitzkit/core';
|
||||
import { literals } from '@blitzkit/i18n';
|
||||
type Region,
|
||||
} from "@blitzkit/core";
|
||||
import { literals } from "@blitzkit/i18n";
|
||||
import {
|
||||
APIApplicationCommandOptionChoice,
|
||||
type APIApplicationCommandOptionChoice,
|
||||
Locale,
|
||||
SlashCommandSubcommandBuilder,
|
||||
} from 'discord.js';
|
||||
import { range } from 'lodash-es';
|
||||
import markdownEscape from 'markdown-escape';
|
||||
import { CommandWrapper } from '../components/CommandWrapper';
|
||||
import * as Leaderboard from '../components/Leaderboard';
|
||||
import { TitleBar } from '../components/TitleBar';
|
||||
import { UserError } from '../core/blitzkit/userError';
|
||||
import { addRegionChoices } from '../core/discord/addRegionChoices';
|
||||
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
|
||||
import { autocompleteUsername } from '../core/discord/autocompleteUsername';
|
||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||
import { localizationObject } from '../core/discord/localizationObject';
|
||||
import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand';
|
||||
import { translator } from '../core/localization/translator';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
} from "discord.js";
|
||||
import { range } from "lodash-es";
|
||||
import markdownEscape from "markdown-escape";
|
||||
import { CommandWrapper } from "../components/CommandWrapper";
|
||||
import * as Leaderboard from "../components/Leaderboard";
|
||||
import { TitleBar } from "../components/TitleBar";
|
||||
import { UserError } from "../core/blitzkit/userError";
|
||||
import { addRegionChoices } from "../core/discord/addRegionChoices";
|
||||
import { addUsernameChoices } from "../core/discord/addUsernameChoices";
|
||||
import { autocompleteUsername } from "../core/discord/autocompleteUsername";
|
||||
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||
import { localizationObject } from "../core/discord/localizationObject";
|
||||
import { resolvePlayerFromCommand } from "../core/discord/resolvePlayerFromCommand";
|
||||
import { translator } from "../core/localization/translator";
|
||||
import type { CommandRegistry } from "../events/interactionCreate";
|
||||
|
||||
interface SimplifiedPlayer {
|
||||
id: number;
|
||||
@@ -51,8 +51,8 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
const onGoingSeason = await isOnGoingRatingSeason();
|
||||
const seasonNumbers = range(
|
||||
FIRST_MINIMAL_ARCHIVED_RATING_SEASON,
|
||||
latestArchivedSeasonNumber + (onGoingSeason ? 1 : 0) + 1,
|
||||
);
|
||||
latestArchivedSeasonNumber + (onGoingSeason ? 1 : 0) + 1
|
||||
).slice(-25);
|
||||
|
||||
function addOptions(option: SlashCommandSubcommandBuilder) {
|
||||
const { strings } = translator(Locale.EnglishUS);
|
||||
@@ -66,51 +66,50 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
(strings) =>
|
||||
strings.bot.commands.rating_leaderboard.options.limit.name,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
true
|
||||
)
|
||||
)
|
||||
.setDescription(
|
||||
literals(
|
||||
strings.bot.commands.rating_leaderboard.options.limit
|
||||
.description,
|
||||
[`${DEFAULT_LIMIT}`],
|
||||
),
|
||||
{ count: DEFAULT_LIMIT }
|
||||
)
|
||||
)
|
||||
.setDescriptionLocalizations(
|
||||
localizationObject(
|
||||
(strings) =>
|
||||
strings.bot.commands.rating_leaderboard.options.limit
|
||||
.description,
|
||||
[`${DEFAULT_LIMIT}`],
|
||||
),
|
||||
{ count: DEFAULT_LIMIT }
|
||||
)
|
||||
)
|
||||
.setRequired(false)
|
||||
.setMinValue(5)
|
||||
.setMaxValue(30),
|
||||
.setMaxValue(30)
|
||||
)
|
||||
.addStringOption((option) =>
|
||||
option
|
||||
.setName(
|
||||
strings.bot.commands.rating_leaderboard.options.season.name,
|
||||
strings.bot.commands.rating_leaderboard.options.season.name
|
||||
)
|
||||
.setNameLocalizations(
|
||||
localizationObject(
|
||||
(strings) =>
|
||||
strings.bot.commands.rating_leaderboard.options.season.name,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
true
|
||||
)
|
||||
)
|
||||
.setDescription(
|
||||
strings.bot.commands.rating_leaderboard.options.season
|
||||
.description,
|
||||
strings.bot.commands.rating_leaderboard.options.season.description
|
||||
)
|
||||
.setDescriptionLocalizations(
|
||||
localizationObject(
|
||||
(strings) =>
|
||||
strings.bot.commands.rating_leaderboard.options.season
|
||||
.description,
|
||||
),
|
||||
.description
|
||||
)
|
||||
)
|
||||
.setRequired(false)
|
||||
.addChoices(
|
||||
@@ -124,7 +123,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
? literals(
|
||||
strings.bot.commands.rating_leaderboard.options.season
|
||||
.choices.current,
|
||||
[`${number}`],
|
||||
{ season: number }
|
||||
)
|
||||
: `${number}`,
|
||||
value: `${number}`,
|
||||
@@ -133,27 +132,27 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
(strings) =>
|
||||
strings.bot.commands.rating_leaderboard.options
|
||||
.season.choices.current,
|
||||
[`${number}`],
|
||||
{ season: number }
|
||||
)
|
||||
: undefined,
|
||||
} satisfies APIApplicationCommandOptionChoice<string>;
|
||||
})
|
||||
.reverse(),
|
||||
),
|
||||
.reverse()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
resolve({
|
||||
command: createLocalizedCommand('rating-leaderboard', [
|
||||
command: createLocalizedCommand("rating-leaderboard", [
|
||||
{
|
||||
subcommand: 'neighbors',
|
||||
subcommand: "neighbors",
|
||||
modify(option: SlashCommandSubcommandBuilder) {
|
||||
option.addStringOption(addUsernameChoices);
|
||||
addOptions(option);
|
||||
},
|
||||
},
|
||||
{
|
||||
subcommand: 'league',
|
||||
subcommand: "league",
|
||||
modify(option: SlashCommandSubcommandBuilder) {
|
||||
option
|
||||
.addStringOption((option) => {
|
||||
@@ -161,7 +160,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
|
||||
return option
|
||||
.setName(
|
||||
strings.bot.commands.rating_leaderboard.options.league.name,
|
||||
strings.bot.commands.rating_leaderboard.options.league.name
|
||||
)
|
||||
.setNameLocalizations(
|
||||
localizationObject(
|
||||
@@ -169,19 +168,19 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
strings.bot.commands.rating_leaderboard.options.league
|
||||
.name,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
true
|
||||
)
|
||||
)
|
||||
.setDescription(
|
||||
strings.bot.commands.rating_leaderboard.options.league
|
||||
.description,
|
||||
.description
|
||||
)
|
||||
.setDescriptionLocalizations(
|
||||
localizationObject(
|
||||
(strings) =>
|
||||
strings.bot.commands.rating_leaderboard.options.league
|
||||
.description,
|
||||
),
|
||||
.description
|
||||
)
|
||||
)
|
||||
.addChoices(
|
||||
...LEAGUES.map(
|
||||
@@ -195,10 +194,10 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
(strings) =>
|
||||
(
|
||||
strings.common.leagues as Record<string, string>
|
||||
)[name],
|
||||
)[name]
|
||||
),
|
||||
}) satisfies APIApplicationCommandOptionChoice<string>,
|
||||
),
|
||||
} satisfies APIApplicationCommandOptionChoice<string>)
|
||||
)
|
||||
)
|
||||
.setRequired(true);
|
||||
})
|
||||
@@ -213,15 +212,15 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
async handler(interaction) {
|
||||
const { strings } = translator(interaction.locale);
|
||||
const subcommand = interaction.options.getSubcommand(true) as
|
||||
| 'league'
|
||||
| 'neighbors';
|
||||
const limit = interaction.options.getInteger('limit') ?? DEFAULT_LIMIT;
|
||||
const seasonOption = interaction.options.getString('season');
|
||||
| "league"
|
||||
| "neighbors";
|
||||
const limit = interaction.options.getInteger("limit") ?? DEFAULT_LIMIT;
|
||||
const seasonOption = interaction.options.getString("season");
|
||||
const season = seasonOption
|
||||
? parseInt(seasonOption)
|
||||
: onGoingSeason
|
||||
? latestArchivedSeasonNumber + 1
|
||||
: latestArchivedSeasonNumber;
|
||||
? latestArchivedSeasonNumber + 1
|
||||
: latestArchivedSeasonNumber;
|
||||
const isCurrentSeason = season === latestArchivedSeasonNumber + 1;
|
||||
let titleName: string;
|
||||
let titleImage: string | undefined;
|
||||
@@ -232,11 +231,11 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
let playerId: number | undefined;
|
||||
let regionRatingInfo: RatingInfo | null;
|
||||
|
||||
if (subcommand === 'league') {
|
||||
if (subcommand === "league") {
|
||||
const leagueIndex = parseInt(
|
||||
interaction.options.getString('league')!,
|
||||
interaction.options.getString("league")!
|
||||
);
|
||||
const region = interaction.options.getString('region') as Region;
|
||||
const region = interaction.options.getString("region") as Region;
|
||||
regionRatingInfo = await (isCurrentSeason
|
||||
? getRatingInfo(region)
|
||||
: null);
|
||||
@@ -259,8 +258,8 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
position: player.number,
|
||||
clan: player.clan_tag,
|
||||
nickname: player.nickname,
|
||||
}) satisfies SimplifiedPlayer,
|
||||
),
|
||||
} satisfies SimplifiedPlayer)
|
||||
)
|
||||
)
|
||||
: await getArchivedRatingLeaderboard(region, season!).then(
|
||||
async (players) => {
|
||||
@@ -269,17 +268,17 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
? 0
|
||||
: players.version!.value.entries.findIndex(
|
||||
(player) =>
|
||||
player.score < LEAGUES[leagueIndex - 1].minScore,
|
||||
player.score < LEAGUES[leagueIndex - 1].minScore
|
||||
);
|
||||
const lastPlayerIndex = firstPlayerIndex + limit - 1;
|
||||
const trimmed = players.version!.value.entries.slice(
|
||||
firstPlayerIndex,
|
||||
lastPlayerIndex + 1,
|
||||
lastPlayerIndex + 1
|
||||
);
|
||||
const clanData = await getClanAccountInfo(
|
||||
region,
|
||||
trimmed.map(({ id }) => id),
|
||||
['clan'],
|
||||
["clan"]
|
||||
);
|
||||
|
||||
return trimmed.map(
|
||||
@@ -294,16 +293,16 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
literals(
|
||||
strings.bot.commands.rating_leaderboard.body
|
||||
.deleted_player,
|
||||
[`${player.id}`],
|
||||
{ id: player.id }
|
||||
),
|
||||
}) satisfies SimplifiedPlayer,
|
||||
} satisfies SimplifiedPlayer)
|
||||
);
|
||||
},
|
||||
}
|
||||
);
|
||||
const leagueInfo = LEAGUES[leagueIndex];
|
||||
archivedLeaderboard = await getArchivedRatingLeaderboard(
|
||||
region,
|
||||
season,
|
||||
season
|
||||
);
|
||||
|
||||
players = result;
|
||||
@@ -316,12 +315,12 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
? (strings.common.leagues as Record<string, string>)[
|
||||
LEAGUES[leagueInfo.index].name
|
||||
]
|
||||
: ''
|
||||
: ""
|
||||
} - ${strings.common.regions.short[region]}`;
|
||||
titleImage = imgur(leagueInfo.icon);
|
||||
titleDescription = literals(
|
||||
strings.bot.commands.rating_leaderboard.body.top_players,
|
||||
[`${limit}`],
|
||||
{ count: limit }
|
||||
);
|
||||
} else {
|
||||
const { region, id } = await resolvePlayerFromCommand(interaction);
|
||||
@@ -338,7 +337,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
}
|
||||
|
||||
const accountInfo = await getAccountInfo(region, id);
|
||||
const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan;
|
||||
const clan = (await getClanAccountInfo(region, id, ["clan"]))?.clan;
|
||||
const neighbors = isCurrentSeason
|
||||
? await getRatingNeighbors(region, id, Math.round(limit / 2)).then(
|
||||
(players) =>
|
||||
@@ -350,13 +349,13 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
score: player.score,
|
||||
clan: player.clan_tag,
|
||||
nickname: player.nickname,
|
||||
}) satisfies SimplifiedPlayer,
|
||||
),
|
||||
} satisfies SimplifiedPlayer)
|
||||
)
|
||||
)
|
||||
: await getArchivedRatingLeaderboard(region, season!).then(
|
||||
async (players) => {
|
||||
const playerIndex = players.version!.value.entries.findIndex(
|
||||
(player) => player.id === id,
|
||||
(player) => player.id === id
|
||||
);
|
||||
|
||||
if (playerIndex === -1) {
|
||||
@@ -364,20 +363,20 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
literals(
|
||||
strings.bot.commands.rating_leaderboard.body
|
||||
.no_participation,
|
||||
[markdownEscape(accountInfo.nickname), `${season}`],
|
||||
),
|
||||
{ player: markdownEscape(accountInfo.nickname), season }
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const halfRange = Math.round(limit / 2);
|
||||
const trimmed = players.version!.value.entries.slice(
|
||||
playerIndex - halfRange,
|
||||
playerIndex + halfRange + 1,
|
||||
playerIndex + halfRange + 1
|
||||
);
|
||||
const clanData = await getClanAccountInfo(
|
||||
region,
|
||||
trimmed.map((player) => player.id),
|
||||
['clan'],
|
||||
["clan"]
|
||||
);
|
||||
|
||||
return trimmed.map((player, index) => ({
|
||||
@@ -389,18 +388,18 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
? clanData[index]!.account_name
|
||||
: `Deleted ${player.id}`,
|
||||
}));
|
||||
},
|
||||
}
|
||||
);
|
||||
archivedLeaderboard = await getArchivedRatingLeaderboard(
|
||||
region,
|
||||
season,
|
||||
season
|
||||
);
|
||||
|
||||
players = neighbors;
|
||||
titleImage = clan
|
||||
? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clan.emblem_set_id}.png`
|
||||
: undefined;
|
||||
titleName = neighbors ? accountInfo.nickname : '';
|
||||
titleName = neighbors ? accountInfo.nickname : "";
|
||||
titleDescription =
|
||||
strings.bot.commands.rating_leaderboard.body.neighbors;
|
||||
playersAfter =
|
||||
@@ -414,7 +413,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
const items = players.map((player) => {
|
||||
const midnightIndex =
|
||||
archivedLeaderboard?.version!.value.entries.findIndex(
|
||||
(item) => item.id === player.id,
|
||||
(item) => item.id === player.id
|
||||
);
|
||||
const midnightScore =
|
||||
midnightIndex === undefined
|
||||
@@ -453,7 +452,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
<Leaderboard.Gap
|
||||
message={literals(
|
||||
strings.bot.commands.rating_leaderboard.body.more,
|
||||
[`${playersAfter.toLocaleString(interaction.locale)}`],
|
||||
{ count: playersAfter.toLocaleString(interaction.locale) }
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
@@ -465,5 +464,5 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
||||
|
||||
autocomplete: autocompleteUsername,
|
||||
});
|
||||
},
|
||||
}
|
||||
);
|
||||
@@ -20,7 +20,7 @@ import { blitzStarsTankAverages } from '../core/blitzstars/tankAverages';
|
||||
import { buttonLink } from '../core/discord/buttonLink';
|
||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||
import { translator } from '../core/localization/translator';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
import { type CommandRegistry } from '../events/interactionCreate';
|
||||
import { theme } from '../stitches.config';
|
||||
|
||||
type WotInspectorReplaySubmission =
|
||||
|
||||
@@ -8,31 +8,31 @@ import {
|
||||
TANK_ICONS,
|
||||
TankType,
|
||||
TIER_ROMAN_NUMERALS,
|
||||
} from '@blitzkit/core';
|
||||
import { literals } from '@blitzkit/i18n';
|
||||
import { escapeMarkdown, Locale } from 'discord.js';
|
||||
import { CommandWrapper } from '../components/CommandWrapper';
|
||||
import { TitleBar } from '../components/TitleBar';
|
||||
import { resolveTankId } from '../core/blitz/resolveTankId';
|
||||
import { buildTechTreeLine } from '../core/blitzkit/buildTechTreeLine';
|
||||
import { iconPng } from '../core/blitzkit/iconPng';
|
||||
import { tankDefinitions } from '../core/blitzkit/nonBlockingPromises';
|
||||
import { resolveAncestry } from '../core/blitzkit/resolveAncestry';
|
||||
import { tankIconPng } from '../core/blitzkit/tankIconPng';
|
||||
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
|
||||
import { autocompleteTanks } from '../core/discord/autocompleteTanks';
|
||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||
import { localizationObject } from '../core/discord/localizationObject';
|
||||
import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand';
|
||||
import { translator } from '../core/localization/translator';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
import { theme } from '../stitches.config';
|
||||
} from "@blitzkit/core";
|
||||
import { literals } from "@blitzkit/i18n";
|
||||
import { escapeMarkdown, Locale } from "discord.js";
|
||||
import { CommandWrapper } from "../components/CommandWrapper";
|
||||
import { TitleBar } from "../components/TitleBar";
|
||||
import { resolveTankId } from "../core/blitz/resolveTankId";
|
||||
import { buildTechTreeLine } from "../core/blitzkit/buildTechTreeLine";
|
||||
import { iconPng } from "../core/blitzkit/iconPng";
|
||||
import { tankDefinitions } from "../core/blitzkit/nonBlockingPromises";
|
||||
import { resolveAncestry } from "../core/blitzkit/resolveAncestry";
|
||||
import { tankIconPng } from "../core/blitzkit/tankIconPng";
|
||||
import { addUsernameChoices } from "../core/discord/addUsernameChoices";
|
||||
import { autocompleteTanks } from "../core/discord/autocompleteTanks";
|
||||
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||
import { localizationObject } from "../core/discord/localizationObject";
|
||||
import { resolvePlayerFromCommand } from "../core/discord/resolvePlayerFromCommand";
|
||||
import { translator } from "../core/localization/translator";
|
||||
import { type CommandRegistry } from "../events/interactionCreate";
|
||||
import { theme } from "../stitches.config";
|
||||
|
||||
export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
const { strings } = translator(Locale.EnglishUS);
|
||||
|
||||
resolve({
|
||||
command: createLocalizedCommand('research')
|
||||
command: createLocalizedCommand("research")
|
||||
.addStringOption((option) =>
|
||||
option
|
||||
.setName(strings.bot.commands.research.options.target_tank.name)
|
||||
@@ -41,20 +41,20 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
(strings) =>
|
||||
strings.bot.commands.research.options.target_tank.name,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
true
|
||||
)
|
||||
)
|
||||
.setDescription(
|
||||
strings.bot.commands.research.options.target_tank.description,
|
||||
strings.bot.commands.research.options.target_tank.description
|
||||
)
|
||||
.setDescriptionLocalizations(
|
||||
localizationObject(
|
||||
(strings) =>
|
||||
strings.bot.commands.research.options.target_tank.description,
|
||||
),
|
||||
strings.bot.commands.research.options.target_tank.description
|
||||
)
|
||||
)
|
||||
.setAutocomplete(true)
|
||||
.setRequired(true),
|
||||
.setRequired(true)
|
||||
)
|
||||
.addStringOption((option) =>
|
||||
option
|
||||
@@ -64,20 +64,20 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
(strings) =>
|
||||
strings.bot.commands.research.options.starting_tank.name,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
true
|
||||
)
|
||||
)
|
||||
.setDescription(
|
||||
strings.bot.commands.research.options.starting_tank.description,
|
||||
strings.bot.commands.research.options.starting_tank.description
|
||||
)
|
||||
.setDescriptionLocalizations(
|
||||
localizationObject(
|
||||
(strings) =>
|
||||
strings.bot.commands.research.options.starting_tank.description,
|
||||
),
|
||||
strings.bot.commands.research.options.starting_tank.description
|
||||
)
|
||||
)
|
||||
.setAutocomplete(true)
|
||||
.setRequired(false),
|
||||
.setRequired(false)
|
||||
)
|
||||
.addStringOption(addUsernameChoices),
|
||||
|
||||
@@ -85,11 +85,11 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
const { strings, unwrap } = translator(interaction.locale);
|
||||
const awaitedTankDefinitions = await tankDefinitions;
|
||||
const targetTankId = await resolveTankId(
|
||||
interaction.options.getString('target-tank', true),
|
||||
interaction.options.getString("target-tank", true),
|
||||
interaction.locale,
|
||||
true,
|
||||
true
|
||||
);
|
||||
const startingTankRaw = interaction.options.getString('starting-tank');
|
||||
const startingTankRaw = interaction.options.getString("starting-tank");
|
||||
const targetTank = awaitedTankDefinitions.tanks[targetTankId];
|
||||
const { id, region } = await resolvePlayerFromCommand(interaction);
|
||||
let startingTankId: number;
|
||||
@@ -99,46 +99,45 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
startingTankId = await resolveTankId(
|
||||
startingTankRaw,
|
||||
interaction.locale,
|
||||
true,
|
||||
true
|
||||
);
|
||||
|
||||
if (targetTankId === startingTankId) {
|
||||
return literals(
|
||||
strings.bot.commands.research.errors.start_end_equal,
|
||||
[escapeMarkdown(unwrap(targetTank.name))],
|
||||
{ tank: escapeMarkdown(unwrap(targetTank.name)) }
|
||||
);
|
||||
}
|
||||
|
||||
const startingTank = awaitedTankDefinitions.tanks[startingTankId];
|
||||
|
||||
if (startingTank.tier > targetTank.tier) {
|
||||
return literals(strings.bot.commands.research.errors.unordered, [
|
||||
escapeMarkdown(unwrap(startingTank.name)),
|
||||
escapeMarkdown(unwrap(targetTank.name)),
|
||||
escapeMarkdown(unwrap(targetTank.name)),
|
||||
]);
|
||||
return literals(strings.bot.commands.research.errors.unordered, {
|
||||
tank_1: escapeMarkdown(unwrap(startingTank.name)),
|
||||
tank_2: escapeMarkdown(unwrap(targetTank.name)),
|
||||
});
|
||||
}
|
||||
|
||||
if (!targetTankAncestry.includes(startingTankId)) {
|
||||
return literals(
|
||||
strings.bot.commands.research.errors.tanks_not_on_line,
|
||||
[
|
||||
escapeMarkdown(unwrap(targetTank.name)),
|
||||
escapeMarkdown(unwrap(startingTank.name)),
|
||||
],
|
||||
{
|
||||
tank_1: escapeMarkdown(unwrap(targetTank.name)),
|
||||
tank_2: escapeMarkdown(unwrap(startingTank.name)),
|
||||
}
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (targetTank.type !== TankType.RESEARCHABLE) {
|
||||
return literals(strings.bot.commands.research.errors.non_tech_tree, [
|
||||
escapeMarkdown(unwrap(targetTank.name)),
|
||||
]);
|
||||
return literals(strings.bot.commands.research.errors.non_tech_tree, {
|
||||
tank: escapeMarkdown(unwrap(targetTank.name)),
|
||||
});
|
||||
}
|
||||
|
||||
if (targetTank.tier === 1) {
|
||||
return literals(strings.bot.commands.research.errors.tier_1, [
|
||||
escapeMarkdown(unwrap(targetTank.name)),
|
||||
]);
|
||||
return literals(strings.bot.commands.research.errors.tier_1, {
|
||||
tank: escapeMarkdown(unwrap(targetTank.name)),
|
||||
});
|
||||
}
|
||||
|
||||
const tankStats = await getTankStats(region, id);
|
||||
@@ -150,18 +149,18 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
if (tankStats.some(({ tank_id }) => tank_id === targetTankId)) {
|
||||
return literals(
|
||||
strings.bot.commands.research.errors.already_researched,
|
||||
[escapeMarkdown(unwrap(targetTank.name))],
|
||||
{ tank: escapeMarkdown(unwrap(targetTank.name)) }
|
||||
);
|
||||
}
|
||||
|
||||
const foundAncestor = targetTankAncestry.find(
|
||||
(id) =>
|
||||
awaitedTankDefinitions.tanks[id].tier === 1 ||
|
||||
tankStats.some(({ tank_id }) => tank_id === id),
|
||||
tankStats.some(({ tank_id }) => tank_id === id)
|
||||
);
|
||||
|
||||
if (foundAncestor === undefined) {
|
||||
throw new Error('No first owned tech tree found.');
|
||||
throw new Error("No first owned tech tree found.");
|
||||
}
|
||||
|
||||
startingTankId = foundAncestor;
|
||||
@@ -169,12 +168,12 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
|
||||
const techTreeLine = await buildTechTreeLine(
|
||||
startingTankId,
|
||||
targetTankId,
|
||||
targetTankId
|
||||
);
|
||||
|
||||
const line = [...techTreeLine, startingTankId].reverse();
|
||||
const { nickname } = await getAccountInfo(region, id);
|
||||
const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan;
|
||||
const clan = (await getClanAccountInfo(region, id, ["clan"]))?.clan;
|
||||
const clanImage = clan ? emblemURL(clan.emblem_set_id) : undefined;
|
||||
const costs = await Promise.all(
|
||||
line.map(async (id) => {
|
||||
@@ -187,18 +186,18 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
tank.turrets.forEach((turret) => {
|
||||
if (
|
||||
turret.research_cost !== undefined &&
|
||||
typeof turret.research_cost.research_cost_type!.value === 'number'
|
||||
typeof turret.research_cost.research_cost_type!.value === "number"
|
||||
) {
|
||||
turretXps.set(
|
||||
turret.id,
|
||||
turret.research_cost.research_cost_type!.value,
|
||||
turret.research_cost.research_cost_type!.value
|
||||
);
|
||||
}
|
||||
|
||||
turret.guns.forEach((gun) => {
|
||||
if (
|
||||
gun.research_cost !== undefined &&
|
||||
typeof gun.research_cost.research_cost_type!.value === 'number'
|
||||
typeof gun.research_cost.research_cost_type!.value === "number"
|
||||
) {
|
||||
gunXps.set(gun.id, gun.research_cost.research_cost_type!.value);
|
||||
}
|
||||
@@ -208,11 +207,11 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
tank.engines.forEach((engine) => {
|
||||
if (
|
||||
engine.research_cost !== undefined &&
|
||||
typeof engine.research_cost.research_cost_type!.value === 'number'
|
||||
typeof engine.research_cost.research_cost_type!.value === "number"
|
||||
) {
|
||||
engineXps.set(
|
||||
engine.id,
|
||||
engine.research_cost.research_cost_type!.value,
|
||||
engine.research_cost.research_cost_type!.value
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -220,11 +219,11 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
tank.tracks.forEach((track) => {
|
||||
if (
|
||||
track.research_cost !== undefined &&
|
||||
typeof track.research_cost.research_cost_type!.value === 'number'
|
||||
typeof track.research_cost.research_cost_type!.value === "number"
|
||||
) {
|
||||
trackXps.set(
|
||||
track.id,
|
||||
track.research_cost.research_cost_type!.value,
|
||||
track.research_cost.research_cost_type!.value
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -249,10 +248,10 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
purchase: tank.price.value,
|
||||
equipment: equipmentPriceMatrix[tank.tier].reduce(
|
||||
(a, b) => a + 3 * b,
|
||||
0,
|
||||
0
|
||||
),
|
||||
};
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
const image = (
|
||||
@@ -267,14 +266,14 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: 8,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
borderRadius: 4,
|
||||
backgroundColor: theme.colors.appBackground2,
|
||||
border: theme.borderStyles.subtle,
|
||||
@@ -285,10 +284,10 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
position: 'relative',
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
position: "relative",
|
||||
right: 24,
|
||||
}}
|
||||
>
|
||||
@@ -304,23 +303,23 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
gap: 8,
|
||||
padding: '16px 0',
|
||||
padding: "16px 0",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
gap: 4,
|
||||
alignItems: 'center',
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt="XP icon"
|
||||
src={await iconPng(asset('icons/currencies/xp.webp'))}
|
||||
src={await iconPng(asset("icons/currencies/xp.webp"))}
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
@@ -330,7 +329,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
color: theme.colors.textHighContrast,
|
||||
}}
|
||||
>
|
||||
Experience:{' '}
|
||||
Experience:{" "}
|
||||
{costs
|
||||
.reduce((a, b) => a + (b.research ?? 0) + b.upgrades, 0)
|
||||
.toLocaleString(interaction.locale)}
|
||||
@@ -339,14 +338,14 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
gap: 4,
|
||||
alignItems: 'center',
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt="Silver icon"
|
||||
src={await iconPng(asset('icons/currencies/silver.webp'))}
|
||||
src={await iconPng(asset("icons/currencies/silver.webp"))}
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
@@ -356,7 +355,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
color: theme.colors.textHighContrast,
|
||||
}}
|
||||
>
|
||||
Purchase:{' '}
|
||||
Purchase:{" "}
|
||||
{costs
|
||||
.reduce((a, b) => a + b.purchase, 0)
|
||||
.toLocaleString(interaction.locale)}
|
||||
@@ -365,14 +364,14 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
gap: 4,
|
||||
alignItems: 'center',
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt="Silver icon"
|
||||
src={await iconPng(asset('icons/currencies/silver.webp'))}
|
||||
src={await iconPng(asset("icons/currencies/silver.webp"))}
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
@@ -382,7 +381,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
color: theme.colors.textHighContrast,
|
||||
}}
|
||||
>
|
||||
Equipment:{' '}
|
||||
Equipment:{" "}
|
||||
{costs
|
||||
.reduce((a, b) => a + b.equipment, 0)
|
||||
.toLocaleString(interaction.locale)}
|
||||
@@ -399,7 +398,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
<div
|
||||
key={id}
|
||||
style={{
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
borderRadius: 4,
|
||||
backgroundColor: theme.colors.appBackground2,
|
||||
border: theme.borderStyles.subtle,
|
||||
@@ -408,9 +407,9 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
<div
|
||||
style={{
|
||||
width: 48,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
@@ -426,9 +425,9 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
position: 'relative',
|
||||
right: '5%',
|
||||
display: "flex",
|
||||
position: "relative",
|
||||
right: "5%",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
@@ -437,23 +436,23 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
style={{
|
||||
height: 100,
|
||||
width: 100,
|
||||
objectFit: 'contain',
|
||||
position: 'absolute',
|
||||
left: '25%',
|
||||
objectFit: "contain",
|
||||
position: "absolute",
|
||||
left: "25%",
|
||||
top: 0,
|
||||
}}
|
||||
/>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
gap: 4,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
position: 'absolute',
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
position: "absolute",
|
||||
bottom: 8,
|
||||
left: '50%',
|
||||
transform: 'translateX(-50%)',
|
||||
left: "50%",
|
||||
transform: "translateX(-50%)",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
@@ -476,23 +475,23 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
gap: 8,
|
||||
padding: '16px 0',
|
||||
padding: "16px 0",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
gap: 4,
|
||||
alignItems: 'center',
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt="XP icon"
|
||||
src={await iconPng(asset('icons/currencies/xp.webp'))}
|
||||
src={await iconPng(asset("icons/currencies/xp.webp"))}
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
@@ -504,11 +503,11 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
>
|
||||
{literals(
|
||||
strings.bot.commands.research.body.research,
|
||||
[
|
||||
`${costs[index].research?.toLocaleString(
|
||||
interaction.locale,
|
||||
)}`,
|
||||
],
|
||||
{
|
||||
cost: costs[index].research?.toLocaleString(
|
||||
interaction.locale
|
||||
),
|
||||
}
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
@@ -516,15 +515,15 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
{costs[index].upgrades > 0 && (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
gap: 4,
|
||||
alignItems: 'center',
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt="Free XP icon"
|
||||
src={await iconPng(
|
||||
asset('icons/currencies/free-xp.webp'),
|
||||
asset("icons/currencies/free-xp.webp")
|
||||
)}
|
||||
width={16}
|
||||
height={16}
|
||||
@@ -537,7 +536,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
>
|
||||
{literals(
|
||||
strings.bot.commands.research.body.upgrades,
|
||||
[costs[index].upgrades.toLocaleString()],
|
||||
{ cost: costs[index].upgrades.toLocaleString() }
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
@@ -545,15 +544,15 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
gap: 4,
|
||||
alignItems: 'center',
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt="Silver icon"
|
||||
src={await iconPng(
|
||||
asset('icons/currencies/silver.webp'),
|
||||
asset("icons/currencies/silver.webp")
|
||||
)}
|
||||
width={16}
|
||||
height={16}
|
||||
@@ -566,26 +565,26 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
>
|
||||
{literals(
|
||||
strings.bot.commands.research.body.purchase,
|
||||
[
|
||||
costs[index].purchase.toLocaleString(
|
||||
interaction.locale,
|
||||
{
|
||||
cost: costs[index].purchase.toLocaleString(
|
||||
interaction.locale
|
||||
),
|
||||
],
|
||||
}
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
gap: 4,
|
||||
alignItems: 'center',
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt="Silver icon"
|
||||
src={await iconPng(
|
||||
asset('icons/currencies/silver.webp'),
|
||||
asset("icons/currencies/silver.webp")
|
||||
)}
|
||||
width={16}
|
||||
height={16}
|
||||
@@ -598,18 +597,18 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
>
|
||||
{literals(
|
||||
strings.bot.commands.research.body.equipment,
|
||||
[
|
||||
costs[index].equipment.toLocaleString(
|
||||
interaction.locale,
|
||||
{
|
||||
cost: costs[index].equipment.toLocaleString(
|
||||
interaction.locale
|
||||
),
|
||||
],
|
||||
}
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}),
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
</CommandWrapper>
|
||||
@@ -621,7 +620,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
},
|
||||
|
||||
autocomplete(interaction) {
|
||||
autocompleteTanks(interaction, true, ['target-tank', 'starting-tank']);
|
||||
autocompleteTanks(interaction, true, ["target-tank", "starting-tank"]);
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -1,23 +1,23 @@
|
||||
import { ClanList, fetchBlitz, Region } from '@blitzkit/core';
|
||||
import { literals } from '@blitzkit/i18n';
|
||||
import { Locale } from 'discord.js';
|
||||
import markdownEscape from 'markdown-escape';
|
||||
import { addClanChoices } from '../core/discord/addClanChoices';
|
||||
import { addRegionChoices } from '../core/discord/addRegionChoices';
|
||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||
import { embedInfo } from '../core/discord/embedInfo';
|
||||
import { localizationObject } from '../core/discord/localizationObject';
|
||||
import { translator } from '../core/localization/translator';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
import { type ClanList, fetchBlitz, type Region } from "@blitzkit/core";
|
||||
import { literals } from "@blitzkit/i18n";
|
||||
import { Locale } from "discord.js";
|
||||
import markdownEscape from "markdown-escape";
|
||||
import { addClanChoices } from "../core/discord/addClanChoices";
|
||||
import { addRegionChoices } from "../core/discord/addRegionChoices";
|
||||
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||
import { embedInfo } from "../core/discord/embedInfo";
|
||||
import { localizationObject } from "../core/discord/localizationObject";
|
||||
import { translator } from "../core/localization/translator";
|
||||
import type { CommandRegistry } from "../events/interactionCreate";
|
||||
|
||||
const DEFAULT_LIMIT = 25;
|
||||
|
||||
export const searchClansCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
resolve({
|
||||
command: createLocalizedCommand('search-clans')
|
||||
command: createLocalizedCommand("search-clans")
|
||||
.addStringOption(addRegionChoices)
|
||||
.addStringOption((option) =>
|
||||
addClanChoices(option).setAutocomplete(false),
|
||||
addClanChoices(option).setAutocomplete(false)
|
||||
)
|
||||
.addIntegerOption((option) => {
|
||||
const { strings } = translator(Locale.EnglishUS);
|
||||
@@ -28,21 +28,21 @@ export const searchClansCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
localizationObject(
|
||||
(strings) => strings.bot.commands.search_clans.options.limit.name,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
true
|
||||
)
|
||||
)
|
||||
.setDescription(
|
||||
literals(
|
||||
strings.bot.commands.search_clans.options.limit.description,
|
||||
[`${DEFAULT_LIMIT}`],
|
||||
),
|
||||
{ limit: DEFAULT_LIMIT }
|
||||
)
|
||||
)
|
||||
.setDescriptionLocalizations(
|
||||
localizationObject(
|
||||
(strings) =>
|
||||
strings.bot.commands.search_clans.options.limit.description,
|
||||
[`${DEFAULT_LIMIT}`],
|
||||
),
|
||||
{ limit: DEFAULT_LIMIT }
|
||||
)
|
||||
)
|
||||
.setMinValue(1)
|
||||
.setMaxValue(100);
|
||||
@@ -50,24 +50,24 @@ export const searchClansCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
|
||||
async handler(interaction) {
|
||||
const { strings } = translator(interaction.locale);
|
||||
const region = interaction.options.getString('region') as Region;
|
||||
const clan = interaction.options.getString('clan')!;
|
||||
const limit = interaction.options.getInteger('limit') ?? DEFAULT_LIMIT;
|
||||
const clanList = await fetchBlitz<ClanList>(region, 'clans/list', {
|
||||
const region = interaction.options.getString("region") as Region;
|
||||
const clan = interaction.options.getString("clan")!;
|
||||
const limit = interaction.options.getInteger("limit") ?? DEFAULT_LIMIT;
|
||||
const clanList = await fetchBlitz<ClanList>(region, "clans/list", {
|
||||
search: clan,
|
||||
limit,
|
||||
});
|
||||
|
||||
return embedInfo(
|
||||
literals(strings.bot.commands.search_clans.body.title, [
|
||||
markdownEscape(clan),
|
||||
strings.common.regions.normal[region],
|
||||
]),
|
||||
literals(strings.bot.commands.search_clans.body.title, {
|
||||
search: markdownEscape(clan),
|
||||
region: strings.common.regions.normal[region],
|
||||
}),
|
||||
clanList.length === 0
|
||||
? strings.bot.commands.search_clans.body.no_results
|
||||
: `\`\`\`\n${clanList
|
||||
.map((clan) => `${clan.name} [${clan.tag}]`)
|
||||
.join('\n')}\n\`\`\``,
|
||||
.join("\n")}\n\`\`\``
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import {
|
||||
AccountList,
|
||||
type AccountList,
|
||||
fetchBlitz,
|
||||
Region,
|
||||
type Region,
|
||||
usernamePattern,
|
||||
} from '@blitzkit/core';
|
||||
import { literals } from '@blitzkit/i18n';
|
||||
import { Locale } from 'discord.js';
|
||||
import markdownEscape from 'markdown-escape';
|
||||
import { addRegionChoices } from '../core/discord/addRegionChoices';
|
||||
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
|
||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||
import { embedInfo } from '../core/discord/embedInfo';
|
||||
import { localizationObject } from '../core/discord/localizationObject';
|
||||
import { translator } from '../core/localization/translator';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
} from "@blitzkit/core";
|
||||
import { literals } from "@blitzkit/i18n";
|
||||
import { Locale } from "discord.js";
|
||||
import markdownEscape from "markdown-escape";
|
||||
import { addRegionChoices } from "../core/discord/addRegionChoices";
|
||||
import { addUsernameChoices } from "../core/discord/addUsernameChoices";
|
||||
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||
import { embedInfo } from "../core/discord/embedInfo";
|
||||
import { localizationObject } from "../core/discord/localizationObject";
|
||||
import { translator } from "../core/localization/translator";
|
||||
import type { CommandRegistry } from "../events/interactionCreate";
|
||||
|
||||
const DEFAULT_LIMIT = 25;
|
||||
|
||||
@@ -21,10 +21,10 @@ export const searchPlayersCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
const { strings } = translator(Locale.EnglishUS);
|
||||
|
||||
resolve({
|
||||
command: createLocalizedCommand('search-players')
|
||||
command: createLocalizedCommand("search-players")
|
||||
.addStringOption(addRegionChoices)
|
||||
.addStringOption((option) =>
|
||||
addUsernameChoices(option).setAutocomplete(false).setRequired(true),
|
||||
addUsernameChoices(option).setAutocomplete(false).setRequired(true)
|
||||
)
|
||||
.addIntegerOption((option) =>
|
||||
option
|
||||
@@ -34,49 +34,49 @@ export const searchPlayersCommand = new Promise<CommandRegistry>((resolve) => {
|
||||
(strings) =>
|
||||
strings.bot.commands.search_players.options.limit.name,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
true
|
||||
)
|
||||
)
|
||||
.setDescription(
|
||||
literals(
|
||||
strings.bot.commands.search_players.options.limit.description,
|
||||
[`${DEFAULT_LIMIT}`],
|
||||
),
|
||||
{ limit: DEFAULT_LIMIT }
|
||||
)
|
||||
)
|
||||
.setDescriptionLocalizations(
|
||||
localizationObject(
|
||||
(strings) =>
|
||||
strings.bot.commands.search_players.options.limit.description,
|
||||
[`${DEFAULT_LIMIT}`],
|
||||
),
|
||||
{ limit: DEFAULT_LIMIT }
|
||||
)
|
||||
)
|
||||
.setMinValue(1)
|
||||
.setMaxValue(100),
|
||||
.setMaxValue(100)
|
||||
),
|
||||
|
||||
async handler(interaction) {
|
||||
const { strings } = translator(interaction.locale);
|
||||
const region = interaction.options.getString('region') as Region;
|
||||
const name = interaction.options.getString('username')!;
|
||||
const limit = interaction.options.getInteger('limit') ?? 25;
|
||||
const region = interaction.options.getString("region") as Region;
|
||||
const name = interaction.options.getString("username")!;
|
||||
const limit = interaction.options.getInteger("limit") ?? 25;
|
||||
const trimmedSearch = name.trim();
|
||||
const players = usernamePattern.test(trimmedSearch)
|
||||
? await fetchBlitz<AccountList>(region, 'account/list', {
|
||||
? await fetchBlitz<AccountList>(region, "account/list", {
|
||||
search: trimmedSearch,
|
||||
limit,
|
||||
})
|
||||
: [];
|
||||
|
||||
return embedInfo(
|
||||
literals(strings.bot.commands.search_players.body.title, [
|
||||
markdownEscape(trimmedSearch),
|
||||
strings.common.regions.normal[region],
|
||||
]),
|
||||
literals(strings.bot.commands.search_players.body.title, {
|
||||
search: markdownEscape(trimmedSearch),
|
||||
region: strings.common.regions.normal[region],
|
||||
}),
|
||||
`\`\`\`${
|
||||
players.length === 0
|
||||
? strings.bot.commands.search_players.body.no_results
|
||||
: players.map((player) => player.nickname).join('\n')
|
||||
}\`\`\``,
|
||||
: players.map((player) => player.nickname).join("\n")
|
||||
}\`\`\``
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,50 +1,50 @@
|
||||
import { emblemURL, getAccountInfo, getClanAccountInfo } from '@blitzkit/core';
|
||||
import { Locale } from 'discord.js';
|
||||
import { AllStatsOverview } from '../components/AllStatsOverview';
|
||||
import { CommandWrapper } from '../components/CommandWrapper';
|
||||
import { NoData } from '../components/NoData';
|
||||
import { TitleBar } from '../components/TitleBar';
|
||||
import { filtersToDescription } from '../core/blitzkit/filtersToDescription';
|
||||
import { filterStats, StatFilters } from '../core/blitzstars/filterStats';
|
||||
import { getBlitzStarsLinkButton } from '../core/blitzstars/getBlitzStarsLinkButton';
|
||||
import { getStatsInPeriod } from '../core/blitzstars/getStatsInPeriod';
|
||||
import { addPeriodicFilterOptions } from '../core/discord/addPeriodicFilterOptions';
|
||||
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
|
||||
import { autocompleteTanks } from '../core/discord/autocompleteTanks';
|
||||
import { autocompleteUsername } from '../core/discord/autocompleteUsername';
|
||||
import { buttonRefresh } from '../core/discord/buttonRefresh';
|
||||
import { commandToURL } from '../core/discord/commandToURL';
|
||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||
import { getCustomPeriodParams } from '../core/discord/getCustomPeriodParams';
|
||||
import { getFiltersFromButton } from '../core/discord/getFiltersFromButton';
|
||||
import { getFiltersFromCommand } from '../core/discord/getFiltersFromCommand';
|
||||
import { resolvePeriodFromButton } from '../core/discord/resolvePeriodFromButton';
|
||||
import { emblemURL, getAccountInfo, getClanAccountInfo } from "@blitzkit/core";
|
||||
import { Locale } from "discord.js";
|
||||
import { AllStatsOverview } from "../components/AllStatsOverview";
|
||||
import { CommandWrapper } from "../components/CommandWrapper";
|
||||
import { NoData } from "../components/NoData";
|
||||
import { TitleBar } from "../components/TitleBar";
|
||||
import { filtersToDescription } from "../core/blitzkit/filtersToDescription";
|
||||
import { filterStats, type StatFilters } from "../core/blitzstars/filterStats";
|
||||
import { getBlitzStarsLinkButton } from "../core/blitzstars/getBlitzStarsLinkButton";
|
||||
import { getStatsInPeriod } from "../core/blitzstars/getStatsInPeriod";
|
||||
import { addPeriodicFilterOptions } from "../core/discord/addPeriodicFilterOptions";
|
||||
import { addUsernameChoices } from "../core/discord/addUsernameChoices";
|
||||
import { autocompleteTanks } from "../core/discord/autocompleteTanks";
|
||||
import { autocompleteUsername } from "../core/discord/autocompleteUsername";
|
||||
import { buttonRefresh } from "../core/discord/buttonRefresh";
|
||||
import { commandToURL } from "../core/discord/commandToURL";
|
||||
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||
import { getCustomPeriodParams } from "../core/discord/getCustomPeriodParams";
|
||||
import { getFiltersFromButton } from "../core/discord/getFiltersFromButton";
|
||||
import { getFiltersFromCommand } from "../core/discord/getFiltersFromCommand";
|
||||
import { resolvePeriodFromButton } from "../core/discord/resolvePeriodFromButton";
|
||||
import {
|
||||
ResolvedPeriod,
|
||||
type ResolvedPeriod,
|
||||
resolvePeriodFromCommand,
|
||||
} from '../core/discord/resolvePeriodFromCommand';
|
||||
import { resolvePlayerFromButton } from '../core/discord/resolvePlayerFromButton';
|
||||
} from "../core/discord/resolvePeriodFromCommand";
|
||||
import { resolvePlayerFromButton } from "../core/discord/resolvePlayerFromButton";
|
||||
import {
|
||||
ResolvedPlayer,
|
||||
type ResolvedPlayer,
|
||||
resolvePlayerFromCommand,
|
||||
} from '../core/discord/resolvePlayerFromCommand';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
} from "../core/discord/resolvePlayerFromCommand";
|
||||
import type { CommandRegistry } from "../events/interactionCreate";
|
||||
|
||||
async function render(
|
||||
{ region, id }: ResolvedPlayer,
|
||||
{ start, end, name }: ResolvedPeriod,
|
||||
filters: StatFilters,
|
||||
locale: Locale,
|
||||
locale: Locale
|
||||
) {
|
||||
const { nickname } = await getAccountInfo(region, id);
|
||||
const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan;
|
||||
const clan = (await getClanAccountInfo(region, id, ["clan"]))?.clan;
|
||||
const clanImage = clan ? emblemURL(clan.emblem_set_id) : undefined;
|
||||
const diffedTankStats = await getStatsInPeriod(
|
||||
region,
|
||||
id,
|
||||
start,
|
||||
end,
|
||||
locale,
|
||||
locale
|
||||
);
|
||||
const { stats, supplementary } = await filterStats(diffedTankStats, filters);
|
||||
const filterDescriptions = await filtersToDescription(filters, locale);
|
||||
@@ -72,8 +72,8 @@ async function render(
|
||||
export const statsCommand = new Promise<CommandRegistry>(async (resolve) => {
|
||||
resolve({
|
||||
command: await addPeriodicFilterOptions(
|
||||
createLocalizedCommand('stats'),
|
||||
(option) => option.addStringOption(addUsernameChoices),
|
||||
createLocalizedCommand("stats"),
|
||||
(option) => option.addStringOption(addUsernameChoices)
|
||||
),
|
||||
|
||||
async handler(interaction) {
|
||||
|
||||
@@ -14,7 +14,7 @@ import { resolvePeriodFromButton } from '../core/discord/resolvePeriodFromButton
|
||||
import { resolvePeriodFromCommand } from '../core/discord/resolvePeriodFromCommand';
|
||||
import { resolvePlayerFromButton } from '../core/discord/resolvePlayerFromButton';
|
||||
import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
import { type CommandRegistry } from '../events/interactionCreate';
|
||||
import { renderBreakdown } from './breakdown/render';
|
||||
|
||||
export const todayCommand = new Promise<CommandRegistry>(async (resolve) => {
|
||||
|
||||
+10
-10
@@ -1,8 +1,8 @@
|
||||
import { ComponentProps } from 'react';
|
||||
import { type ComponentProps } from "react";
|
||||
|
||||
export interface GlowProps extends ComponentProps<'div'> {
|
||||
export interface GlowProps extends ComponentProps<"div"> {
|
||||
color: string;
|
||||
direction?: 'default' | 'reverse';
|
||||
direction?: "default" | "reverse";
|
||||
rotation?: number;
|
||||
startOpacity?: number;
|
||||
endOpacity?: number;
|
||||
@@ -12,7 +12,7 @@ const width = 16;
|
||||
|
||||
export function Glow({
|
||||
color,
|
||||
direction = 'default',
|
||||
direction = "default",
|
||||
rotation = 0,
|
||||
style,
|
||||
startOpacity = 0,
|
||||
@@ -21,20 +21,20 @@ export function Glow({
|
||||
}: GlowProps) {
|
||||
const color1 = `${color}${Math.round(endOpacity * 255)
|
||||
.toString(16)
|
||||
.padStart(2, '0')}`;
|
||||
.padStart(2, "0")}`;
|
||||
const color2 = `${color}${Math.round(startOpacity * 255)
|
||||
.toString(16)
|
||||
.padStart(2, '0')}`;
|
||||
.padStart(2, "0")}`;
|
||||
const background = `linear-gradient(${
|
||||
direction === 'default' ? 0 : 180
|
||||
direction === "default" ? 0 : 180
|
||||
}deg, ${color1} 0%, ${color2} 100%)`;
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: direction === 'default' ? 'flex-end' : 'flex-start',
|
||||
justifyContent: 'center',
|
||||
display: "flex",
|
||||
alignItems: direction === "default" ? "flex-end" : "flex-start",
|
||||
justifyContent: "center",
|
||||
transform: `rotate(${rotation}deg)`,
|
||||
...style,
|
||||
}}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
getWN8Percentile,
|
||||
Percentile,
|
||||
SupplementaryStats,
|
||||
type SupplementaryStats,
|
||||
} from '@blitzkit/core';
|
||||
import { Locale } from 'discord.js';
|
||||
import { translator } from '../../core/localization/translator';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { type ReactNode } from "react";
|
||||
|
||||
interface RootProps {
|
||||
children: ReactNode;
|
||||
@@ -8,7 +8,7 @@ export function Root({ children }: RootProps) {
|
||||
return (
|
||||
<div
|
||||
className="session-tracker-root"
|
||||
style={{ display: 'flex', flexDirection: 'column', gap: 8 }}
|
||||
style={{ display: "flex", flexDirection: "column", gap: 8 }}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@@ -6,8 +6,8 @@ import {
|
||||
} from '@blitzkit/core';
|
||||
import { createColors } from 'bepaint';
|
||||
import {
|
||||
AccentColor,
|
||||
GrayColor,
|
||||
type AccentColor,
|
||||
type GrayColor,
|
||||
PALETTES,
|
||||
} from '../../../core/radix/radixColors';
|
||||
import { RowStat } from './RowStat';
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Percentile } from '@blitzkit/core';
|
||||
import { createColors } from 'bepaint';
|
||||
import { Percentile } from "@blitzkit/core";
|
||||
import { createColors } from "bepaint";
|
||||
import {
|
||||
AccentColor,
|
||||
GrayColor,
|
||||
type AccentColor,
|
||||
type GrayColor,
|
||||
PALETTES,
|
||||
} from '../../../core/radix/radixColors';
|
||||
import { PercentileIndicator } from '../../PercentileIndicator';
|
||||
} from "../../../core/radix/radixColors";
|
||||
import { PercentileIndicator } from "../../PercentileIndicator";
|
||||
|
||||
interface RowStatProps {
|
||||
name: string;
|
||||
@@ -22,7 +22,7 @@ export function RowStat({
|
||||
delta,
|
||||
percentile,
|
||||
minimized = false,
|
||||
color = 'slate',
|
||||
color = "slate",
|
||||
}: RowStatProps) {
|
||||
const theme = createColors(PALETTES[`${color}Dark`]);
|
||||
|
||||
@@ -30,29 +30,29 @@ export function RowStat({
|
||||
<div
|
||||
className="session-tracker-row-stat"
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: 4,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
flex: 1,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
gap: 4,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
{percentile === undefined && delta !== undefined && delta !== 0 && (
|
||||
<img
|
||||
alt={delta > 0 ? 'Increase' : 'Decrease'}
|
||||
alt={delta > 0 ? "Increase" : "Decrease"}
|
||||
src={
|
||||
(delta ?? 0) > 0
|
||||
? 'https://i.imgur.com/qbjiXa1.png'
|
||||
: 'https://i.imgur.com/3uyNhun.png'
|
||||
? "https://i.imgur.com/qbjiXa1.png"
|
||||
: "https://i.imgur.com/3uyNhun.png"
|
||||
}
|
||||
style={{ width: 12, height: 12 }}
|
||||
/>
|
||||
@@ -66,7 +66,7 @@ export function RowStat({
|
||||
color: theme.textHighContrast,
|
||||
fontWeight: 700,
|
||||
fontSize: 16,
|
||||
textAlign: 'center',
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
{value}
|
||||
@@ -77,7 +77,7 @@ export function RowStat({
|
||||
<span
|
||||
style={{
|
||||
color: theme.textLowContrast,
|
||||
textAlign: 'center',
|
||||
textAlign: "center",
|
||||
fontSize: 12,
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { theme } from '../stitches.config';
|
||||
import { type ReactNode } from "react";
|
||||
import { theme } from "../stitches.config";
|
||||
|
||||
interface CommandWrapperProps {
|
||||
children: ReactNode;
|
||||
@@ -10,14 +10,14 @@ export function CommandWrapper({ children, fat }: CommandWrapperProps) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
padding: `16px ${fat ? 0 : 16}px`,
|
||||
gap: 16,
|
||||
width: '100%',
|
||||
width: "100%",
|
||||
color: theme.colors.textHighContrast,
|
||||
backgroundColor: theme.colors.appBackground1,
|
||||
position: 'relative',
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import {
|
||||
BlitzStats,
|
||||
type BlitzStats,
|
||||
getWN8Percentile,
|
||||
isNumber,
|
||||
SupplementaryStats,
|
||||
type SupplementaryStats,
|
||||
} from '@blitzkit/core';
|
||||
import { Locale } from 'discord.js';
|
||||
import { translator } from '../../core/localization/translator';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { GenericStat } from '..';
|
||||
import { GenericStatsRow } from './GenericStatsRow';
|
||||
import { type GenericStat } from "..";
|
||||
import { GenericStatsRow } from "./GenericStatsRow";
|
||||
|
||||
interface GenericStatsColumnProps {
|
||||
stats: GenericStat[];
|
||||
@@ -7,7 +7,7 @@ interface GenericStatsColumnProps {
|
||||
|
||||
export function GenericStatsColumn({ stats }: GenericStatsColumnProps) {
|
||||
return (
|
||||
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 8 }}>
|
||||
<div style={{ flex: 1, display: "flex", flexDirection: "column", gap: 8 }}>
|
||||
{stats.map((stat) => (
|
||||
<GenericStatsRow key={stat[0]} stat={stat} />
|
||||
))}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { GenericStat } from '..';
|
||||
import { theme } from '../../../stitches.config';
|
||||
import { PercentileIndicator } from '../../PercentileIndicator';
|
||||
import { type GenericStat } from "..";
|
||||
import { theme } from "../../../stitches.config";
|
||||
import { PercentileIndicator } from "../../PercentileIndicator";
|
||||
|
||||
interface GenericStatsRowProps {
|
||||
stat: GenericStat;
|
||||
@@ -10,18 +10,18 @@ export function GenericStatsRow({ stat }: GenericStatsRowProps) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
color: theme.colors.textLowContrast,
|
||||
fontSize: 16,
|
||||
whiteSpace: 'nowrap',
|
||||
whiteSpace: "nowrap",
|
||||
maxWidth: 128,
|
||||
overflow: 'hidden',
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
{stat[0]}
|
||||
@@ -32,9 +32,9 @@ export function GenericStatsRow({ stat }: GenericStatsRowProps) {
|
||||
color: theme.colors.textHighContrast,
|
||||
fontWeight: 700,
|
||||
fontSize: 16,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: 4,
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { times } from 'lodash-es';
|
||||
import { theme } from '../../../../stitches.config';
|
||||
import { MarginInputProps } from '../Root';
|
||||
import { type MarginInputProps } from '../Root';
|
||||
import { MarginOrientation } from './constants';
|
||||
|
||||
interface MarginProps extends MarginInputProps {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { times } from 'lodash-es';
|
||||
import { ReactNode } from 'react';
|
||||
import { theme } from '../../../../stitches.config';
|
||||
import { Margin } from '../Margin';
|
||||
import { MarginOrientation } from '../Margin/constants';
|
||||
import { GRAPH_HEIGHT, GRAPH_WIDTH } from './constants';
|
||||
import { times } from "lodash-es";
|
||||
import { type ReactNode } from "react";
|
||||
import { theme } from "../../../../stitches.config";
|
||||
import { Margin } from "../Margin";
|
||||
import { MarginOrientation } from "../Margin/constants";
|
||||
import { GRAPH_HEIGHT, GRAPH_WIDTH } from "./constants";
|
||||
|
||||
export interface MarginInputProps {
|
||||
min: number;
|
||||
@@ -32,12 +32,12 @@ export function Root({
|
||||
xTitle,
|
||||
}: RootProps) {
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
|
||||
<div
|
||||
style={{
|
||||
paddingLeft: 64,
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<span style={{ fontSize: 16, color: theme.colors.textHighContrast }}>
|
||||
@@ -48,7 +48,7 @@ export function Root({
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', gap: 16 }}>
|
||||
<div style={{ display: "flex", gap: 16 }}>
|
||||
{verticalMargin && (
|
||||
<Margin {...verticalMargin} verticalSeparations={separations} />
|
||||
)}
|
||||
@@ -94,9 +94,9 @@ export function Root({
|
||||
<div
|
||||
style={{
|
||||
paddingLeft: 64,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<span style={{ fontSize: 16, color: theme.colors.textLowContrast }}>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ComponentProps } from 'react';
|
||||
import { theme } from '../../../stitches.config';
|
||||
import { type ComponentProps } from "react";
|
||||
import { theme } from "../../../stitches.config";
|
||||
|
||||
interface ItemProps extends ComponentProps<'div'> {
|
||||
interface ItemProps extends ComponentProps<"div"> {
|
||||
position: number;
|
||||
deltaPosition?: number;
|
||||
score?: number;
|
||||
@@ -25,15 +25,15 @@ export function Item({
|
||||
<div
|
||||
{...props}
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
display: 'flex',
|
||||
cursor: "pointer",
|
||||
display: "flex",
|
||||
padding: 8,
|
||||
backgroundColor: highlight
|
||||
? theme.colors.componentInteractive_blue
|
||||
: theme.colors.componentInteractive,
|
||||
borderRadius: 4,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: 8,
|
||||
}}
|
||||
>
|
||||
@@ -50,9 +50,9 @@ export function Item({
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: 4,
|
||||
}}
|
||||
>
|
||||
@@ -83,22 +83,22 @@ export function Item({
|
||||
{deltaPosition !== undefined && deltaPosition !== 0 && (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: 4,
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt={deltaPosition > 0 ? 'Increase' : 'Decrease'}
|
||||
alt={deltaPosition > 0 ? "Increase" : "Decrease"}
|
||||
style={{
|
||||
width: 8,
|
||||
height: 8,
|
||||
}}
|
||||
src={
|
||||
deltaPosition > 0
|
||||
? 'https://i.imgur.com/qbjiXa1.png'
|
||||
: 'https://i.imgur.com/3uyNhun.png'
|
||||
? "https://i.imgur.com/qbjiXa1.png"
|
||||
: "https://i.imgur.com/3uyNhun.png"
|
||||
}
|
||||
/>
|
||||
<span
|
||||
@@ -119,11 +119,11 @@ export function Item({
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
gap: 4,
|
||||
alignItems: 'center',
|
||||
alignItems: "center",
|
||||
width: 96,
|
||||
justifyContent: 'flex-end',
|
||||
justifyContent: "flex-end",
|
||||
}}
|
||||
>
|
||||
{deltaScore !== 0 && deltaScore !== undefined && (
|
||||
@@ -140,15 +140,15 @@ export function Item({
|
||||
)}
|
||||
{deltaScore !== 0 && deltaScore !== undefined && (
|
||||
<img
|
||||
alt={deltaScore > 0 ? 'Increase' : 'Decrease'}
|
||||
alt={deltaScore > 0 ? "Increase" : "Decrease"}
|
||||
style={{
|
||||
width: 8,
|
||||
height: 8,
|
||||
}}
|
||||
src={
|
||||
deltaScore > 0
|
||||
? 'https://i.imgur.com/qbjiXa1.png'
|
||||
: 'https://i.imgur.com/3uyNhun.png'
|
||||
? "https://i.imgur.com/qbjiXa1.png"
|
||||
: "https://i.imgur.com/3uyNhun.png"
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { type ReactNode } from 'react';
|
||||
|
||||
interface RootProps {
|
||||
children: ReactNode;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { type ReactNode } from 'react';
|
||||
|
||||
interface ColumnProps {
|
||||
children: ReactNode;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { type ReactNode } from "react";
|
||||
|
||||
interface RootProps {
|
||||
children: ReactNode;
|
||||
@@ -8,8 +8,8 @@ export function Root({ children }: RootProps) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: 16,
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { type ReactNode } from "react";
|
||||
|
||||
interface RowProps {
|
||||
children: ReactNode;
|
||||
@@ -8,7 +8,7 @@ export function Row({ children }: RowProps) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
gap: 8,
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { TierWeightsRecord } from '@blitzkit/core';
|
||||
import { theme } from '../stitches.config';
|
||||
import type { TierWeightsRecord } from "@blitzkit/core";
|
||||
import { theme } from "../stitches.config";
|
||||
|
||||
interface TierWeightsProps {
|
||||
weights: TierWeightsRecord;
|
||||
@@ -7,7 +7,7 @@ interface TierWeightsProps {
|
||||
|
||||
export function TierWeights({ weights }: TierWeightsProps) {
|
||||
return (
|
||||
<div style={{ display: 'flex', gap: 8 }}>
|
||||
<div style={{ display: "flex", gap: 8 }}>
|
||||
{Object.entries(weights).map(([tier, weight]) => (
|
||||
<div
|
||||
key={tier}
|
||||
@@ -15,11 +15,11 @@ export function TierWeights({ weights }: TierWeightsProps) {
|
||||
backgroundColor: theme.colors.appBackground1_light,
|
||||
flex: weight,
|
||||
color: theme.colors.textHighContrast_light,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
fontSize: 16,
|
||||
overflow: 'hidden',
|
||||
overflow: "hidden",
|
||||
borderRadius: 8,
|
||||
minWidth: 16,
|
||||
}}
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
import { SEARCH_KEYS } from '@blitzkit/core';
|
||||
import { literals } from '@blitzkit/i18n';
|
||||
import { Locale } from 'discord.js';
|
||||
import { go } from 'fuzzysort';
|
||||
import markdownEscape from 'markdown-escape';
|
||||
import { tankDefinitions, tankNames } from '../blitzkit/nonBlockingPromises';
|
||||
import { UserError } from '../blitzkit/userError';
|
||||
import { tankNamesTechTreeOnly } from '../discord/autocompleteTanks';
|
||||
import { translator } from '../localization/translator';
|
||||
import { SEARCH_KEYS } from "@blitzkit/core";
|
||||
import { literals } from "@blitzkit/i18n";
|
||||
import { Locale } from "discord.js";
|
||||
import { go } from "fuzzysort";
|
||||
import markdownEscape from "markdown-escape";
|
||||
import { tankDefinitions, tankNames } from "../blitzkit/nonBlockingPromises";
|
||||
import { UserError } from "../blitzkit/userError";
|
||||
import { tankNamesTechTreeOnly } from "../discord/autocompleteTanks";
|
||||
import { translator } from "../localization/translator";
|
||||
|
||||
export async function resolveTankId(
|
||||
tank: string | number,
|
||||
locale: Locale,
|
||||
techTreeOnly = false,
|
||||
techTreeOnly = false
|
||||
) {
|
||||
const { strings } = translator(locale);
|
||||
const number = typeof tank === 'string' ? Number(tank) : tank;
|
||||
const number = typeof tank === "string" ? Number(tank) : tank;
|
||||
|
||||
if (Number.isNaN(number)) {
|
||||
const searchResult = go(
|
||||
`${tank}`,
|
||||
await (techTreeOnly ? tankNamesTechTreeOnly : tankNames),
|
||||
{ keys: SEARCH_KEYS, limit: 1 },
|
||||
{ keys: SEARCH_KEYS, limit: 1 }
|
||||
);
|
||||
|
||||
if (searchResult.length === 0) {
|
||||
throw new UserError(
|
||||
literals(strings.bot.common.errors.tank_name_not_found, [
|
||||
markdownEscape(`${tank}`),
|
||||
]),
|
||||
literals(strings.bot.common.errors.tank_name_not_found, {
|
||||
search: markdownEscape(`${tank}`),
|
||||
})
|
||||
);
|
||||
} else {
|
||||
return searchResult[0].obj.id;
|
||||
@@ -39,7 +39,9 @@ export async function resolveTankId(
|
||||
return number;
|
||||
} else {
|
||||
throw new UserError(
|
||||
literals(strings.bot.common.errors.tank_id_not_found, [`${number}`]),
|
||||
literals(strings.bot.common.errors.tank_id_not_found, {
|
||||
search: number,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { fetchTankDefinitions, TIER_ROMAN_NUMERALS } from '@blitzkit/core';
|
||||
import { literals } from '@blitzkit/i18n';
|
||||
import { Locale } from 'discord.js';
|
||||
import { StatFilters } from '../blitzstars/filterStats';
|
||||
import { translator } from '../localization/translator';
|
||||
import { fetchTankDefinitions, TIER_ROMAN_NUMERALS } from "@blitzkit/core";
|
||||
import { literals } from "@blitzkit/i18n";
|
||||
import { Locale } from "discord.js";
|
||||
import { type StatFilters } from "../blitzstars/filterStats";
|
||||
import { translator } from "../localization/translator";
|
||||
|
||||
export async function filtersToDescription(
|
||||
{ nation, tier, type, class: tankClass, tank }: StatFilters,
|
||||
locale: Locale,
|
||||
locale: Locale
|
||||
) {
|
||||
const { strings, bkLocale } = translator(locale);
|
||||
const awaitedTankDefinitions = await fetchTankDefinitions();
|
||||
@@ -18,11 +18,13 @@ export async function filtersToDescription(
|
||||
info.push((strings.common.nations as Record<string, string>)[nation]);
|
||||
if (tier) {
|
||||
info.push(
|
||||
literals(strings.bot.common.filters.tier, [TIER_ROMAN_NUMERALS[tier]]),
|
||||
literals(strings.bot.common.filters.tier, {
|
||||
tier: TIER_ROMAN_NUMERALS[tier],
|
||||
})
|
||||
);
|
||||
}
|
||||
if (tankClass) info.push(strings.common.tank_class_short[tankClass]);
|
||||
if (type) info.push(strings.common.tree_type[type]);
|
||||
|
||||
return info.length === 0 ? strings.bot.common.filters.none : info.join(', ');
|
||||
return info.length === 0 ? strings.bot.common.filters.none : info.join(", ");
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { getTimeDaysAgo, Region } from '@blitzkit/core';
|
||||
import { PeriodSize } from '../discord/addPeriodSubCommands';
|
||||
import { getTimeDaysAgo, type Region } from '@blitzkit/core';
|
||||
import type { PeriodSize } from '../discord/addPeriodSubCommands';
|
||||
|
||||
export function getPeriodStart(region: Region, period: PeriodSize) {
|
||||
if (period === 'career') return 0;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import sharp from 'sharp';
|
||||
import sharp from "sharp";
|
||||
|
||||
const cache: Record<string, Buffer> = {};
|
||||
|
||||
export async function iconPng(url: string) {
|
||||
if (!url.endsWith('.webp')) return url;
|
||||
if (!url.endsWith(".webp")) return url;
|
||||
|
||||
if (!cache[url]) {
|
||||
const imageResponse = await fetch(url);
|
||||
@@ -12,5 +12,5 @@ export async function iconPng(url: string) {
|
||||
cache[url] = imageBufferPNG;
|
||||
}
|
||||
|
||||
return `data:image/png;base64,${cache[url].toString('base64')}`;
|
||||
return `data:image/png;base64,${cache[url].toString("base64")}`;
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import { TankClass, TankType } from '@blitzkit/core';
|
||||
import { SupplementaryStats } from '@blitzkit/core/src/blitz/getAccountInfo';
|
||||
import { calculateWN8 } from '@blitzkit/core/src/statistics/calculateWN8';
|
||||
import { BlitzStats } from '@blitzkit/core/src/statistics/compositeStats';
|
||||
import { sumAllStats } from '@blitzkit/core/src/statistics/sumAllStats';
|
||||
import { tankDefinitions } from '../blitzkit/nonBlockingPromises';
|
||||
import { blitzStarsTankAverages } from './tankAverages';
|
||||
import { TankClass, TankType } from "@blitzkit/core";
|
||||
import type { SupplementaryStats } from "@blitzkit/core/src/blitz/getAccountInfo";
|
||||
import { calculateWN8 } from "@blitzkit/core/src/statistics/calculateWN8";
|
||||
import type { BlitzStats } from "@blitzkit/core/src/statistics/compositeStats";
|
||||
import { sumAllStats } from "@blitzkit/core/src/statistics/sumAllStats";
|
||||
import { tankDefinitions } from "../blitzkit/nonBlockingPromises";
|
||||
import { blitzStarsTankAverages } from "./tankAverages";
|
||||
|
||||
export interface StatFilters {
|
||||
nation?: string;
|
||||
@@ -16,7 +16,7 @@ export interface StatFilters {
|
||||
|
||||
export async function filterStats(
|
||||
{ diff, order }: DiffedTankStats,
|
||||
filters: StatFilters,
|
||||
filters: StatFilters
|
||||
) {
|
||||
// tank, if provided, takes priority over all other filters
|
||||
if (filters.tank) filters = { tank: filters.tank };
|
||||
@@ -45,14 +45,14 @@ export async function filterStats(
|
||||
const battlesOfTanksWithAverages = filteredOrder.reduce<number>(
|
||||
(accumulator, id) =>
|
||||
awaitedTankAverages[id] ? accumulator + diff[id].battles : accumulator,
|
||||
0,
|
||||
0
|
||||
);
|
||||
const battlesOfTanksWithTankDefinition = filteredOrder.reduce<number>(
|
||||
(accumulator, id) =>
|
||||
awaitedTankDefinitions.tanks[id]
|
||||
? accumulator + diff[id].battles
|
||||
: accumulator,
|
||||
0,
|
||||
0
|
||||
);
|
||||
const supplementary = {
|
||||
WN8:
|
||||
@@ -63,7 +63,7 @@ export async function filterStats(
|
||||
calculateWN8(awaitedTankAverages[id].all, diff[id]) *
|
||||
diff[id].battles
|
||||
: accumulator,
|
||||
0,
|
||||
0
|
||||
) / battlesOfTanksWithAverages,
|
||||
tier:
|
||||
filteredOrder.reduce<number>(
|
||||
@@ -72,7 +72,7 @@ export async function filterStats(
|
||||
? accumulator +
|
||||
awaitedTankDefinitions.tanks[id]!.tier * diff[id].battles
|
||||
: accumulator,
|
||||
0,
|
||||
0
|
||||
) / battlesOfTanksWithTankDefinition,
|
||||
} satisfies SupplementaryStats;
|
||||
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { getAccountInfo, Region } from '@blitzkit/core';
|
||||
import { buttonLink } from '../discord/buttonLink';
|
||||
import { getAccountInfo, type Region } from "@blitzkit/core";
|
||||
import { buttonLink } from "../discord/buttonLink";
|
||||
|
||||
export async function getBlitzStarsLinkButton(
|
||||
region: Region,
|
||||
id: number,
|
||||
tankId?: number,
|
||||
tankId?: number
|
||||
) {
|
||||
const { nickname } = await getAccountInfo(region, id);
|
||||
|
||||
return buttonLink(
|
||||
`https://www.blitzstars.com/player/${region}/${nickname}${
|
||||
tankId ? `/tank/${tankId!}` : ''
|
||||
tankId ? `/tank/${tankId!}` : ""
|
||||
}`,
|
||||
'BlitzStars',
|
||||
"BlitzStars"
|
||||
);
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Region, emptyAllStats, getAccountInfo } from '@blitzkit/core';
|
||||
import { type Region, emptyAllStats, getAccountInfo } from '@blitzkit/core';
|
||||
import {
|
||||
GetHistoriesOptions,
|
||||
Histories,
|
||||
History,
|
||||
type GetHistoriesOptions,
|
||||
type Histories,
|
||||
type History,
|
||||
getHistoriesDefaultOptions,
|
||||
} from '../../types/histories';
|
||||
import { PlayerStats } from './getPlayerStats';
|
||||
import type { PlayerStats } from './getPlayerStats';
|
||||
|
||||
export async function getPlayerHistories(
|
||||
server: Region,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BlitzStats, Region } from '@blitzkit/core';
|
||||
import type { BlitzStats, Region } from "@blitzkit/core";
|
||||
|
||||
interface IndividualPlayerStats {
|
||||
clan: { clan_id: number; name: string; tag: string };
|
||||
@@ -25,13 +25,13 @@ export async function getPlayerStats<
|
||||
ReturnType = Ids extends number ? PlayerStats : PlayerStats[],
|
||||
>(region: Region, ids: Ids) {
|
||||
const stats = await Promise.all(
|
||||
((typeof ids === 'number' ? [ids] : ids) as number[]).map((id) =>
|
||||
((typeof ids === "number" ? [ids] : ids) as number[]).map((id) =>
|
||||
fetch(`https://www.blitzstars.com/api/playerstats/${id}`).then(
|
||||
(response) => response.json() as Promise<PlayerStats>,
|
||||
),
|
||||
),
|
||||
(response) => response.json() as Promise<PlayerStats>
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
if (typeof ids === 'number') return stats[0] as ReturnType;
|
||||
if (typeof ids === "number") return stats[0] as ReturnType;
|
||||
return stats as ReturnType;
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import { BlitzStats, emptyAllStats, Region } from '@blitzkit/core';
|
||||
import { Locale } from 'discord.js';
|
||||
import { DiffedTankStats } from './filterStats';
|
||||
import { type BlitzStats, emptyAllStats, type Region } from "@blitzkit/core";
|
||||
import { Locale } from "discord.js";
|
||||
import type { DiffedTankStats } from "./filterStats";
|
||||
import {
|
||||
getTankHistories,
|
||||
TankHistories,
|
||||
TankHistoryRaw,
|
||||
} from './getTankHistories';
|
||||
type TankHistories,
|
||||
type TankHistoryRaw,
|
||||
} from "./getTankHistories";
|
||||
|
||||
const emptyTankHistoryNode: TankHistoryRaw = {
|
||||
account_id: 0,
|
||||
@@ -22,7 +22,7 @@ export async function getStatsInPeriod(
|
||||
id: number,
|
||||
start: number,
|
||||
end: number,
|
||||
locale: Locale,
|
||||
locale: Locale
|
||||
) {
|
||||
const history = await getTankHistories(server, id, locale, {
|
||||
includeLatestHistories: true,
|
||||
@@ -43,8 +43,8 @@ export async function getStatsInPeriod(
|
||||
// find the ones played within the range
|
||||
const inRangeTanks = tanks.filter((tankId) =>
|
||||
tankSortedHistory[tankId].some(
|
||||
(node) => node.last_battle_time > start && node.last_battle_time < end,
|
||||
),
|
||||
(node) => node.last_battle_time > start && node.last_battle_time < end
|
||||
)
|
||||
);
|
||||
const playedTanks: number[] = [];
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { Region, emptyAllStats, getTankStats } from '@blitzkit/core';
|
||||
import { Locale } from 'discord.js';
|
||||
import { type Region, emptyAllStats, getTankStats } from "@blitzkit/core";
|
||||
import { Locale } from "discord.js";
|
||||
import {
|
||||
GetHistoriesOptions,
|
||||
Histories,
|
||||
History,
|
||||
type GetHistoriesOptions,
|
||||
type Histories,
|
||||
type History,
|
||||
getHistoriesDefaultOptions,
|
||||
} from '../../types/histories';
|
||||
import { UserError } from '../blitzkit/userError';
|
||||
import { translator } from '../localization/translator';
|
||||
} from "../../types/histories";
|
||||
import { UserError } from "../blitzkit/userError";
|
||||
import { translator } from "../localization/translator";
|
||||
|
||||
export interface TankHistory extends History {
|
||||
tank_id: number;
|
||||
@@ -36,12 +36,12 @@ export async function getTankHistories(
|
||||
server: Region,
|
||||
id: number,
|
||||
locale: Locale,
|
||||
options?: Partial<GetTankHistoriesOptions>,
|
||||
options?: Partial<GetTankHistoriesOptions>
|
||||
) {
|
||||
const { strings } = translator(locale);
|
||||
const mergedOptions = { ...getTankHistoriesDefaultOptions, ...options };
|
||||
const tankHistoriesResponse = await fetch(
|
||||
`https://www.blitzstars.com/api/tankhistories/for/${id}/`,
|
||||
`https://www.blitzstars.com/api/tankhistories/for/${id}/`
|
||||
);
|
||||
const tankHistories = (
|
||||
(await tankHistoriesResponse.json()) as TankHistoriesRaw
|
||||
@@ -51,7 +51,7 @@ export async function getTankHistories(
|
||||
all: history.all,
|
||||
last_battle_time: history.last_battle_time,
|
||||
tank_id: history.tank_id,
|
||||
}) satisfies TankHistory,
|
||||
}) satisfies TankHistory
|
||||
);
|
||||
const compliantTanks: number[] = [];
|
||||
const lastBattles: Record<number, number> = {};
|
||||
@@ -73,7 +73,7 @@ export async function getTankHistories(
|
||||
lastBattles[tank_id] = battles;
|
||||
|
||||
return complies;
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const latestNodes: TankHistories = [];
|
||||
@@ -112,7 +112,7 @@ export async function getTankHistories(
|
||||
all: emptyAllStats,
|
||||
last_battle_time: -Infinity,
|
||||
tank_id: tankId,
|
||||
} satisfies TankHistory),
|
||||
} satisfies TankHistory)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
TANK_TYPES,
|
||||
} from '@blitzkit/core';
|
||||
import {
|
||||
APIApplicationCommandOptionChoice,
|
||||
type APIApplicationCommandOptionChoice,
|
||||
Locale,
|
||||
SlashCommandBuilder,
|
||||
SlashCommandSubcommandBuilder,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { REGIONS } from '@blitzkit/core';
|
||||
import { REGIONS } from "@blitzkit/core";
|
||||
import {
|
||||
APIApplicationCommandOptionChoice,
|
||||
type APIApplicationCommandOptionChoice,
|
||||
Locale,
|
||||
SlashCommandStringOption,
|
||||
} from 'discord.js';
|
||||
import { translator } from '../localization/translator';
|
||||
import { localizationObject } from './localizationObject';
|
||||
} from "discord.js";
|
||||
import { translator } from "../localization/translator";
|
||||
import { localizationObject } from "./localizationObject";
|
||||
|
||||
export function addRegionChoices(option: SlashCommandStringOption) {
|
||||
const { strings } = translator(Locale.EnglishUS);
|
||||
@@ -16,14 +16,14 @@ export function addRegionChoices(option: SlashCommandStringOption) {
|
||||
localizationObject(
|
||||
(strings) => strings.bot.common.options.region.name,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
true
|
||||
)
|
||||
)
|
||||
.setDescription(strings.bot.common.options.region.description)
|
||||
.setDescriptionLocalizations(
|
||||
localizationObject(
|
||||
(strings) => strings.bot.common.options.region.description,
|
||||
),
|
||||
(strings) => strings.bot.common.options.region.description
|
||||
)
|
||||
)
|
||||
.addChoices(
|
||||
...REGIONS.map(
|
||||
@@ -32,10 +32,10 @@ export function addRegionChoices(option: SlashCommandStringOption) {
|
||||
value: region,
|
||||
name: strings.common.regions.normal[region],
|
||||
name_localizations: localizationObject(
|
||||
(strings) => strings.common.regions.normal[region],
|
||||
(strings) => strings.common.regions.normal[region]
|
||||
),
|
||||
}) satisfies APIApplicationCommandOptionChoice,
|
||||
),
|
||||
}) satisfies APIApplicationCommandOptionChoice
|
||||
)
|
||||
)
|
||||
.setRequired(true);
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
import { numberToRomanNumeral } from '@blitzkit/core';
|
||||
import { literals } from '@blitzkit/i18n';
|
||||
import { numberToRomanNumeral } from "@blitzkit/core";
|
||||
import { literals } from "@blitzkit/i18n";
|
||||
import {
|
||||
APIApplicationCommandOptionChoice,
|
||||
Locale,
|
||||
SlashCommandStringOption,
|
||||
} from 'discord.js';
|
||||
import { range } from 'lodash-es';
|
||||
import { translator } from '../localization/translator';
|
||||
import { localizationObject } from './localizationObject';
|
||||
} from "discord.js";
|
||||
import { range } from "lodash-es";
|
||||
import { translator } from "../localization/translator";
|
||||
import { localizationObject } from "./localizationObject";
|
||||
|
||||
export function addTierChoices(option: SlashCommandStringOption) {
|
||||
const { strings } = translator(Locale.EnglishUS);
|
||||
@@ -18,30 +18,30 @@ export function addTierChoices(option: SlashCommandStringOption) {
|
||||
localizationObject(
|
||||
(strings) => strings.bot.common.options.tier.name,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
true
|
||||
)
|
||||
)
|
||||
.setDescription(strings.bot.common.options.tier.description)
|
||||
.setDescriptionLocalizations(
|
||||
localizationObject(
|
||||
(strings) => strings.bot.common.options.tier.description,
|
||||
),
|
||||
(strings) => strings.bot.common.options.tier.description
|
||||
)
|
||||
)
|
||||
.setChoices(
|
||||
...range(10, 0).map(
|
||||
(tier) =>
|
||||
({
|
||||
name: literals(strings.bot.common.options.tier.choices.tier, [
|
||||
`${tier}`,
|
||||
numberToRomanNumeral(tier),
|
||||
]),
|
||||
name: literals(strings.bot.common.options.tier.choices.tier, {
|
||||
tier_1: tier,
|
||||
tier_2: numberToRomanNumeral(tier),
|
||||
}),
|
||||
value: `${tier}`,
|
||||
name_localizations: localizationObject(
|
||||
(strings) => strings.bot.common.options.tier.choices.tier,
|
||||
[`${tier}`, numberToRomanNumeral(tier)],
|
||||
{ tier_1: tier, tier_2: numberToRomanNumeral(tier) }
|
||||
),
|
||||
}) satisfies APIApplicationCommandOptionChoice,
|
||||
),
|
||||
}) satisfies APIApplicationCommandOptionChoice
|
||||
)
|
||||
)
|
||||
.setRequired(true);
|
||||
}
|
||||
@@ -1,25 +1,25 @@
|
||||
import { fetchTankNames, SEARCH_KEYS, TankType } from '@blitzkit/core';
|
||||
import { fetchTankNames, SEARCH_KEYS, TankType } from "@blitzkit/core";
|
||||
import {
|
||||
ApplicationCommandOptionChoiceData,
|
||||
type ApplicationCommandOptionChoiceData,
|
||||
AutocompleteInteraction,
|
||||
CacheType,
|
||||
} from 'discord.js';
|
||||
import { go } from 'fuzzysort';
|
||||
import { tankNames } from '../blitzkit/nonBlockingPromises';
|
||||
import { translator } from '../localization/translator';
|
||||
type CacheType,
|
||||
} from "discord.js";
|
||||
import { go } from "fuzzysort";
|
||||
import { tankNames } from "../blitzkit/nonBlockingPromises";
|
||||
import { translator } from "../localization/translator";
|
||||
import {
|
||||
DISCORD_CHOICES_MAX_NAME_SIZE,
|
||||
OVERFLOW_SUFFIX,
|
||||
} from './autocompleteClan/constants';
|
||||
} from "./autocompleteClan/constants";
|
||||
|
||||
export const tankNamesTechTreeOnly = fetchTankNames().then((names) =>
|
||||
names.filter((tank) => tank.treeType === TankType.RESEARCHABLE),
|
||||
names.filter((tank) => tank.treeType === TankType.RESEARCHABLE)
|
||||
);
|
||||
|
||||
export async function autocompleteTanks(
|
||||
interaction: AutocompleteInteraction<CacheType>,
|
||||
techTreeOnly = false,
|
||||
fields = ['tank'],
|
||||
fields = ["tank"]
|
||||
) {
|
||||
const { unwrap } = translator(interaction.locale);
|
||||
const focusedOption = interaction.options.getFocused(true);
|
||||
@@ -31,7 +31,7 @@ export async function autocompleteTanks(
|
||||
go(
|
||||
focusedOption.value,
|
||||
await (techTreeOnly ? tankNamesTechTreeOnly : tankNames),
|
||||
{ keys: SEARCH_KEYS, limit: 10 },
|
||||
{ keys: SEARCH_KEYS, limit: 10 }
|
||||
).map(async (item) => {
|
||||
let name = unwrap(item.obj.searchableName);
|
||||
|
||||
@@ -42,7 +42,7 @@ export async function autocompleteTanks(
|
||||
0,
|
||||
unwrap(item.obj.searchableName).length -
|
||||
overSize -
|
||||
OVERFLOW_SUFFIX.length,
|
||||
OVERFLOW_SUFFIX.length
|
||||
)}${OVERFLOW_SUFFIX}`;
|
||||
}
|
||||
|
||||
@@ -50,8 +50,8 @@ export async function autocompleteTanks(
|
||||
name,
|
||||
value: `${item.obj.id}`,
|
||||
} satisfies ApplicationCommandOptionChoiceData<string>;
|
||||
}),
|
||||
})
|
||||
)
|
||||
: [],
|
||||
: []
|
||||
);
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
import {
|
||||
ButtonBuilder,
|
||||
ButtonStyle,
|
||||
ComponentEmojiResolvable,
|
||||
} from 'discord.js';
|
||||
type ComponentEmojiResolvable,
|
||||
} from "discord.js";
|
||||
|
||||
export function buttonLink(
|
||||
url: string,
|
||||
label: string,
|
||||
emoji?: ComponentEmojiResolvable,
|
||||
emoji?: ComponentEmojiResolvable
|
||||
) {
|
||||
const button = new ButtonBuilder()
|
||||
.setStyle(ButtonStyle.Link)
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import {
|
||||
ButtonBuilder,
|
||||
ButtonStyle,
|
||||
ComponentEmojiResolvable,
|
||||
} from 'discord.js';
|
||||
type ComponentEmojiResolvable,
|
||||
} from "discord.js";
|
||||
|
||||
export function buttonPrimary(
|
||||
id: string,
|
||||
label: string,
|
||||
emoji?: ComponentEmojiResolvable,
|
||||
emoji?: ComponentEmojiResolvable
|
||||
) {
|
||||
const button = new ButtonBuilder()
|
||||
.setCustomId(id)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CacheType, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { type CacheType, ChatInputCommandInteraction } from "discord.js";
|
||||
|
||||
export interface Parameters {
|
||||
[key: string]: string | number | boolean | undefined | null;
|
||||
@@ -9,7 +9,7 @@ export interface Parameters {
|
||||
|
||||
export function commandToURL(
|
||||
interaction: ChatInputCommandInteraction<CacheType>,
|
||||
parameters?: Parameters,
|
||||
parameters?: Parameters
|
||||
) {
|
||||
const pathname = [
|
||||
interaction.commandName,
|
||||
@@ -17,11 +17,11 @@ export function commandToURL(
|
||||
interaction.options.getSubcommand(false),
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join('/');
|
||||
.join("/");
|
||||
const params = (parameters ? Object.entries(parameters) : [])
|
||||
.filter(([, value]) => value !== undefined && value !== null)
|
||||
.map(([key, value]) => `${key}=${value}`)
|
||||
.join('&');
|
||||
.join("&");
|
||||
|
||||
return `${pathname}${params.length > 0 ? `?${params}` : ''}`;
|
||||
return `${pathname}${params.length > 0 ? `?${params}` : ""}`;
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
import { BlitzKitStrings } from '@blitzkit/i18n';
|
||||
import { type BlitzKitStrings } from "@blitzkit/i18n";
|
||||
import {
|
||||
Locale,
|
||||
SlashCommandBuilder,
|
||||
SlashCommandSubcommandBuilder,
|
||||
SlashCommandSubcommandGroupBuilder,
|
||||
} from 'discord.js';
|
||||
import { translator } from '../localization/translator';
|
||||
import { localizationObject } from './localizationObject';
|
||||
} from "discord.js";
|
||||
import { translator } from "../localization/translator";
|
||||
import { localizationObject } from "./localizationObject";
|
||||
|
||||
interface CreateLocalizedCommandSubcommand {
|
||||
subcommand: string;
|
||||
@@ -22,12 +22,12 @@ export function createLocalizedCommand(
|
||||
command: string,
|
||||
sub?:
|
||||
| CreateLocalizedCommandSubcommand[]
|
||||
| CreateLocalizedCommandSubcommandGroup[],
|
||||
| CreateLocalizedCommandSubcommandGroup[]
|
||||
) {
|
||||
const commandPathItem = command.replaceAll(
|
||||
'-',
|
||||
'_',
|
||||
) as keyof BlitzKitStrings['bot']['commands'];
|
||||
"-",
|
||||
"_"
|
||||
) as keyof BlitzKitStrings["bot"]["commands"];
|
||||
const { strings } = translator(Locale.EnglishUS);
|
||||
const slashCommand = new SlashCommandBuilder()
|
||||
.setName(command)
|
||||
@@ -35,19 +35,19 @@ export function createLocalizedCommand(
|
||||
localizationObject(
|
||||
(strings) => strings.bot.commands[commandPathItem].name,
|
||||
undefined,
|
||||
true,
|
||||
),
|
||||
true
|
||||
)
|
||||
)
|
||||
.setDescription(strings.bot.commands[commandPathItem].description)
|
||||
.setDescriptionLocalizations(
|
||||
localizationObject(
|
||||
(strings) => strings.bot.commands[commandPathItem].description,
|
||||
),
|
||||
(strings) => strings.bot.commands[commandPathItem].description
|
||||
)
|
||||
);
|
||||
|
||||
if (sub) {
|
||||
sub.map((subItem) => {
|
||||
if ('subcommand' in subItem) {
|
||||
if ("subcommand" in subItem) {
|
||||
const subcommandNameLocalizations = localizationObject(
|
||||
(strings) =>
|
||||
(
|
||||
@@ -56,7 +56,7 @@ export function createLocalizedCommand(
|
||||
}
|
||||
).subcommands[subItem.subcommand].name,
|
||||
undefined,
|
||||
true,
|
||||
true
|
||||
);
|
||||
const subcommandDescriptionLocalizations = localizationObject(
|
||||
(strings) =>
|
||||
@@ -64,7 +64,7 @@ export function createLocalizedCommand(
|
||||
strings.bot.commands[commandPathItem] as unknown as {
|
||||
subcommands: Record<string, { description: string }>;
|
||||
}
|
||||
).subcommands[subItem.subcommand].description,
|
||||
).subcommands[subItem.subcommand].description
|
||||
);
|
||||
|
||||
slashCommand.addSubcommand((option) => {
|
||||
@@ -78,7 +78,7 @@ export function createLocalizedCommand(
|
||||
strings.bot.commands[commandPathItem] as unknown as {
|
||||
subcommands: Record<string, { description: string }>;
|
||||
}
|
||||
).subcommands[subItem.subcommand].description,
|
||||
).subcommands[subItem.subcommand].description
|
||||
)
|
||||
.setDescriptionLocalizations(subcommandDescriptionLocalizations);
|
||||
});
|
||||
@@ -91,7 +91,7 @@ export function createLocalizedCommand(
|
||||
}
|
||||
).groups[subItem.group].name,
|
||||
undefined,
|
||||
true,
|
||||
true
|
||||
);
|
||||
const subcommandDescriptionLocalizations = localizationObject(
|
||||
(strings) =>
|
||||
@@ -99,7 +99,7 @@ export function createLocalizedCommand(
|
||||
strings.bot.commands[commandPathItem] as unknown as {
|
||||
groups: Record<string, { description: string }>;
|
||||
}
|
||||
).groups[subItem.group].description,
|
||||
).groups[subItem.group].description
|
||||
);
|
||||
|
||||
slashCommand.addSubcommandGroup((option) => {
|
||||
@@ -113,7 +113,7 @@ export function createLocalizedCommand(
|
||||
strings.bot.commands[commandPathItem] as unknown as {
|
||||
groups: Record<string, { description: string }>;
|
||||
}
|
||||
).groups[subItem.group].description,
|
||||
).groups[subItem.group].description
|
||||
)
|
||||
.setDescriptionLocalizations(subcommandDescriptionLocalizations);
|
||||
});
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { ChatInputCommandInteraction } from 'discord.js';
|
||||
import { PeriodType } from './addPeriodSubCommands';
|
||||
import { ChatInputCommandInteraction } from "discord.js";
|
||||
import type { PeriodType } from "./addPeriodSubCommands";
|
||||
|
||||
export function getCustomPeriodParams(
|
||||
interaction: ChatInputCommandInteraction,
|
||||
forcedNonCustomPeriod = false,
|
||||
forcedNonCustomPeriod = false
|
||||
) {
|
||||
if (
|
||||
!forcedNonCustomPeriod &&
|
||||
(interaction.options.getSubcommand() as PeriodType) === 'custom'
|
||||
(interaction.options.getSubcommand() as PeriodType) === "custom"
|
||||
) {
|
||||
return {
|
||||
start: interaction.options.getInteger('start', true),
|
||||
end: interaction.options.getInteger('end', true),
|
||||
start: interaction.options.getInteger("start", true),
|
||||
end: interaction.options.getInteger("end", true),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { TankClass, TankType } from '@blitzkit/core';
|
||||
import { ButtonInteraction } from 'discord.js';
|
||||
import { StatFilters } from '../blitzstars/filterStats';
|
||||
import { TankClass, TankType } from "@blitzkit/core";
|
||||
import { ButtonInteraction } from "discord.js";
|
||||
import type { StatFilters } from "../blitzstars/filterStats";
|
||||
|
||||
export function getFiltersFromButton(interaction: ButtonInteraction) {
|
||||
const url = new URL(`https://example.com/${interaction.customId}`);
|
||||
|
||||
return {
|
||||
nation: url.searchParams.get('nation') ?? undefined,
|
||||
tank: parseInt(url.searchParams.get('tank') ?? '0') || undefined,
|
||||
nation: url.searchParams.get("nation") ?? undefined,
|
||||
tank: parseInt(url.searchParams.get("tank") ?? "0") || undefined,
|
||||
class:
|
||||
(Number(url.searchParams.get('class')) as TankClass | undefined) ??
|
||||
(Number(url.searchParams.get("class")) as TankClass | undefined) ??
|
||||
undefined,
|
||||
type:
|
||||
(Number(url.searchParams.get('type')) as TankType | undefined) ??
|
||||
(Number(url.searchParams.get("type")) as TankType | undefined) ??
|
||||
undefined,
|
||||
tier: parseInt(url.searchParams.get('tier') ?? '0') || undefined,
|
||||
tier: parseInt(url.searchParams.get("tier") ?? "0") || undefined,
|
||||
} satisfies StatFilters;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ChatInputCommandInteraction } from 'discord.js';
|
||||
import { resolveTankId } from '../blitz/resolveTankId';
|
||||
import { StatFilters } from '../blitzstars/filterStats';
|
||||
import type { StatFilters } from '../blitzstars/filterStats';
|
||||
|
||||
export async function getFiltersFromCommand(
|
||||
interaction: ChatInputCommandInteraction,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { literals } from '@blitzkit/i18n';
|
||||
import { Locale } from 'discord.js';
|
||||
import { translator } from '../localization/translator';
|
||||
import { PeriodSize } from './addPeriodSubCommands';
|
||||
import { literals } from "@blitzkit/i18n";
|
||||
import { Locale } from "discord.js";
|
||||
import { translator } from "../localization/translator";
|
||||
import type { PeriodSize } from "./addPeriodSubCommands";
|
||||
|
||||
export function getPeriodOptionName(period: PeriodSize, locale: Locale) {
|
||||
const { strings } = translator(locale);
|
||||
if (period === 'career') return strings.bot.common.periods.career;
|
||||
if (period === 'today') return strings.bot.common.periods.today;
|
||||
return literals(strings.bot.common.periods.days, [period]);
|
||||
if (period === "career") return strings.bot.common.periods.career;
|
||||
if (period === "today") return strings.bot.common.periods.today;
|
||||
return literals(strings.bot.common.periods.days, { days: period });
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
import { BlitzKitStrings, literals as literalsFunc } from '@blitzkit/i18n';
|
||||
import { Locale } from 'discord.js';
|
||||
import { SUPPORTED_LOCALES_DISCORD } from '../localization/strings/constants';
|
||||
import { translator } from '../localization/translator';
|
||||
import { type BlitzKitStrings, literals as literalsFunc } from "@blitzkit/i18n";
|
||||
import { Locale } from "discord.js";
|
||||
import { SUPPORTED_LOCALES_DISCORD } from "../localization/strings/constants";
|
||||
import { translator } from "../localization/translator";
|
||||
|
||||
const validNameRegex =
|
||||
/^[\p{Ll}\p{Lm}\p{Lo}\p{N}\p{sc=Devanagari}\p{sc=Thai}_-]+$/u;
|
||||
|
||||
export function localizationObject(
|
||||
path: (strings: BlitzKitStrings) => string,
|
||||
literals: string[] = [],
|
||||
strict = false,
|
||||
literals?: Record<string, string | number>,
|
||||
strict = false
|
||||
) {
|
||||
return SUPPORTED_LOCALES_DISCORD.reduce<Partial<Record<Locale, string>>>(
|
||||
(localizations, locale) => {
|
||||
@@ -19,9 +19,9 @@ export function localizationObject(
|
||||
(!strict || validNameRegex.test(translation)) && translation.length > 0;
|
||||
|
||||
if (!isValid) {
|
||||
console.warn(
|
||||
`Invalid localization for ${locale} (${path}): ${translation}; skipping...`,
|
||||
);
|
||||
// console.warn(
|
||||
// `Invalid localization for ${locale} (${path}): ${translation}; skipping...`
|
||||
// );
|
||||
return localizations;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,6 @@ export function localizationObject(
|
||||
[locale]: translation,
|
||||
};
|
||||
},
|
||||
{},
|
||||
{}
|
||||
);
|
||||
}
|
||||
@@ -3,12 +3,12 @@ import {
|
||||
AttachmentBuilder,
|
||||
ButtonBuilder,
|
||||
EmbedBuilder,
|
||||
InteractionEditReplyOptions,
|
||||
InteractionReplyOptions,
|
||||
MessageEditOptions,
|
||||
type InteractionEditReplyOptions,
|
||||
type InteractionReplyOptions,
|
||||
type MessageEditOptions,
|
||||
} from "discord.js";
|
||||
import { InteractionReturnable } from "../../events/interactionCreate";
|
||||
import { Writeable } from "../../types/writable";
|
||||
import type { InteractionReturnable } from "../../events/interactionCreate";
|
||||
import type { Writeable } from "../../types/writable";
|
||||
import { jsxToPngThreaded } from "../blitzkit/jsxToPngThreaded";
|
||||
import { RenderConfiguration } from "../blitzkit/renderConfiguration";
|
||||
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
import { Region } from '@blitzkit/core';
|
||||
import { literals } from '@blitzkit/i18n';
|
||||
import { ChatInputCommandInteraction } from 'discord.js';
|
||||
import markdownEscape from 'markdown-escape';
|
||||
import { searchClansAcrossRegionsBotWrapper } from '../blitz/searchClansAcrossRegionsBotWrapper';
|
||||
import { UserError } from '../blitzkit/userError';
|
||||
import { translator } from '../localization/translator';
|
||||
import type { Region } from "@blitzkit/core";
|
||||
import { literals } from "@blitzkit/i18n";
|
||||
import { ChatInputCommandInteraction } from "discord.js";
|
||||
import markdownEscape from "markdown-escape";
|
||||
import { searchClansAcrossRegionsBotWrapper } from "../blitz/searchClansAcrossRegionsBotWrapper";
|
||||
import { UserError } from "../blitzkit/userError";
|
||||
import { translator } from "../localization/translator";
|
||||
|
||||
export const serverAndIdPattern = /(com|eu|asia)\/[0-9]+/;
|
||||
|
||||
export async function resolveClanFromCommand(
|
||||
interaction: ChatInputCommandInteraction,
|
||||
interaction: ChatInputCommandInteraction
|
||||
) {
|
||||
const { strings } = translator(interaction.locale);
|
||||
const clan = interaction.options.getString('clan', true);
|
||||
const clan = interaction.options.getString("clan", true);
|
||||
|
||||
if (serverAndIdPattern.test(clan)) {
|
||||
const [server, accountId] = clan.split('/');
|
||||
const [server, accountId] = clan.split("/");
|
||||
return { region: server as Region, id: Number(accountId) };
|
||||
} else {
|
||||
const accounts = await searchClansAcrossRegionsBotWrapper(
|
||||
clan,
|
||||
interaction.locale,
|
||||
interaction.locale
|
||||
);
|
||||
|
||||
if (accounts[0]) {
|
||||
return { region: accounts[0].region, id: accounts[0].clan_id };
|
||||
} else {
|
||||
throw new UserError(
|
||||
literals(strings.bot.common.errors.clan_not_found, [
|
||||
markdownEscape(clan),
|
||||
]),
|
||||
literals(strings.bot.common.errors.clan_not_found, {
|
||||
search: markdownEscape(clan),
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Loaded 100 of 380 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user