mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-11-11 11:46:10 +01:00
Compare commits
166
Commits
v1.27.17
...
1f06b1fef4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
907e71ce03 | ||
|
|
10942879a6 | ||
|
|
d60551d30a | ||
|
|
b8c8528e1c | ||
|
|
0c9643016a | ||
|
|
61d1ebbad6 | ||
|
|
e9b7059cc0 | ||
|
|
a359897a2a | ||
|
|
e5f7d72df3 | ||
|
|
57092d8df0 | ||
|
|
86508d5413 | ||
|
|
33373c286f | ||
|
|
7443f75347 | ||
|
|
d09f934b80 | ||
|
|
7f919ebb87 | ||
|
|
0929825555 | ||
|
|
4de0fc5ec1 | ||
|
|
e66610f86e | ||
|
|
674abc4923 | ||
|
|
25cee8a36b | ||
|
|
dcbac5e017 | ||
|
|
0c94d42f3f | ||
|
|
7ab5e94323 | ||
|
|
f9ea5ba74a | ||
|
|
1e7162d2da | ||
|
|
cfba53a655 | ||
|
|
77a85641f0 | ||
|
|
5330a27e96 | ||
|
|
a791aff4a0 | ||
|
|
df8a5967f0 | ||
|
|
6261dfc9cd | ||
|
|
766801826c | ||
|
|
7b728596ca | ||
|
|
0bf6ac33a5 | ||
|
|
9a33b84b1c | ||
|
|
c14efe86cc | ||
|
|
4ff9a2ec7f | ||
|
|
75d7b4554b | ||
|
|
27c8b348bb | ||
|
|
a55ab5e7ee | ||
|
|
12e90cc6ee | ||
|
|
01755a2a64 | ||
|
|
080f0a0eb7 | ||
|
|
a646cc1e57 | ||
|
|
d107813c37 | ||
|
|
27d3bbf346 | ||
|
|
675a613a0e | ||
|
|
50c77e0ae1 | ||
|
|
6e0429332d | ||
|
|
7afce6cbcb | ||
|
|
17c7a5c7f5 | ||
|
|
f5aa2ff9de | ||
|
|
96a8f225fc | ||
|
|
d15a5aee69 | ||
|
|
84dc5e4960 | ||
|
|
58b5ee7f83 | ||
|
|
8a99d34af9 | ||
|
|
7cd7bd83dc | ||
|
|
c29d36a4b7 | ||
|
|
7d3b6221d7 | ||
|
|
fd66f9ab17 | ||
|
|
6fbb0e81cb | ||
|
|
6dc82588c1 | ||
|
|
c6eae9ad51 | ||
|
|
ebdd7629ff | ||
|
|
646bccf370 | ||
|
|
86206f2d89 | ||
|
|
bc0dd87e31 | ||
|
|
e26bb4efe0 | ||
|
|
66cf1fd223 | ||
|
|
335cd4d699 | ||
|
|
95fa5bc1a5 | ||
|
|
50eac9eba6 | ||
|
|
1598ae973f | ||
|
|
69f3a95379 | ||
|
|
3846cbab4a | ||
|
|
30e87743fc | ||
|
|
fe4afdcbf0 | ||
|
|
ef2a47e756 | ||
|
|
c2d625e1ed | ||
|
|
d33990bdb4 | ||
|
|
96482d4268 | ||
|
|
8b527f2333 | ||
|
|
f56c31fb48 | ||
|
|
7d164e415a | ||
|
|
b3a9f0889e | ||
|
|
6d24ed92d0 | ||
|
|
d287e66265 | ||
|
|
ac29d1ead0 | ||
|
|
b6c3fdc4e9 | ||
|
|
4efdaaf7c9 | ||
|
|
87b6a36520 | ||
|
|
cf38cb3a6e | ||
|
|
97fc386bc7 | ||
|
|
abfb3f8ddb | ||
|
|
9cb5113f6e | ||
|
|
7dd1c0c32a | ||
|
|
03233f3fdc | ||
|
|
377159c298 | ||
|
|
d8431fb620 | ||
|
|
0546ddefbd | ||
|
|
26f6889012 | ||
|
|
3ad6088565 | ||
|
|
b6fd568cc6 | ||
|
|
3861b07aef | ||
|
|
2bf57e2c72 | ||
|
|
744c6654cf | ||
|
|
b82e905757 | ||
|
|
c42e5b7dc3 | ||
|
|
b716218ecd | ||
|
|
511346e4bb | ||
|
|
5c2e49453e | ||
|
|
5d397de766 | ||
|
|
aa763f65d1 | ||
|
|
94cbd95509 | ||
|
|
f3762e356f | ||
|
|
5747d7549f | ||
|
|
aea4905d20 |
No files matched your search
@@ -1,4 +1,4 @@
|
|||||||
name: Build Assets
|
name: Assets
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -21,7 +21,7 @@ on:
|
|||||||
description: Root dir of ZIP which houses the executable and Data dir
|
description: Root dir of ZIP which houses the executable and Data dir
|
||||||
|
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 2 * * *'
|
- cron: "0 2 * * *"
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event.inputs.cdn_branch }}
|
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event.inputs.cdn_branch }}
|
||||||
@@ -67,7 +67,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
root-reserve-mb: 512
|
root-reserve-mb: 512
|
||||||
swap-size-mb: 1024
|
swap-size-mb: 1024
|
||||||
remove-dotnet: 'true'
|
remove-dotnet: "true"
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
@@ -131,9 +131,6 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: bun install
|
run: bun install
|
||||||
|
|
||||||
- name: Lint
|
|
||||||
run: bun run lint:website
|
|
||||||
|
|
||||||
- name: Download depots
|
- name: Download depots
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
name: Build Bot
|
name: Bot
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -15,6 +15,10 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
token: ${{ secrets.GH_TOKEN }}
|
||||||
|
|
||||||
- uses: oven-sh/setup-bun@v1
|
- uses: oven-sh/setup-bun@v1
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
name: Discover IDs
|
name: IDs
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 0 * * 1'
|
- cron: "0 0 * * 1"
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
name: Host Bot
|
name: Host
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: main
|
branches: main
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 */5 * * *'
|
- cron: "0 */5 * * *"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
release:
|
release:
|
||||||
types: [created, published, released, edited]
|
types: [created, published, released, edited]
|
||||||
@@ -22,6 +22,19 @@ env:
|
|||||||
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
|
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
|
||||||
PUBLIC_ASSET_FAST_USER: ${{ secrets.PUBLIC_ASSET_FAST_USER }}
|
PUBLIC_ASSET_FAST_USER: ${{ secrets.PUBLIC_ASSET_FAST_USER }}
|
||||||
PUBLIC_ASSET_FAST_REPO: ${{ secrets.PUBLIC_ASSET_FAST_REPO }}
|
PUBLIC_ASSET_FAST_REPO: ${{ secrets.PUBLIC_ASSET_FAST_REPO }}
|
||||||
|
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
|
||||||
|
ALLOW_ROBOTS: ${{ secrets.ALLOW_ROBOTS }}
|
||||||
|
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 }}
|
||||||
|
PUBLIC_GUIDE_WEBSITE: ${{ secrets.PUBLIC_GUIDE_WEBSITE }}
|
||||||
|
SERVICING: ${{ secrets.SERVICING }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
host:
|
host:
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 4 * * *'
|
- cron: "0 4 * * *"
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|||||||
+26
-1
@@ -24,7 +24,32 @@ public/sitemap*
|
|||||||
target
|
target
|
||||||
# packages/core/src/protos/*.ts
|
# packages/core/src/protos/*.ts
|
||||||
/.vercel
|
/.vercel
|
||||||
/.astro
|
.astro
|
||||||
.vercel
|
.vercel
|
||||||
/src
|
/src
|
||||||
/packages/bot/src/prisma
|
/packages/bot/src/prisma
|
||||||
|
dev-dist
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
+3
-3
@@ -1,9 +1,9 @@
|
|||||||
[submodule "submodules/blitzkit-closed"]
|
|
||||||
path = submodules/blitzkit-closed
|
|
||||||
url = https://github.com/tresabhi/blitzkit-closed
|
|
||||||
[submodule "submodules/CUE4Parse"]
|
[submodule "submodules/CUE4Parse"]
|
||||||
path = submodules/CUE4Parse
|
path = submodules/CUE4Parse
|
||||||
url = https://github.com/FabianFG/CUE4Parse
|
url = https://github.com/FabianFG/CUE4Parse
|
||||||
[submodule "submodules/blitzkit-internal"]
|
[submodule "submodules/blitzkit-internal"]
|
||||||
path = submodules/blitzkit-internal
|
path = submodules/blitzkit-internal
|
||||||
url = https://github.com/blitzkit/internal
|
url = https://github.com/blitzkit/internal
|
||||||
|
[submodule "packages/closed"]
|
||||||
|
path = packages/closed
|
||||||
|
url = https://github.com/blitzkit/closed.git
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"trailingComma": "all",
|
|
||||||
"tabWidth": 2,
|
|
||||||
"semi": true,
|
|
||||||
"singleQuote": true,
|
|
||||||
"printWidth": 80
|
|
||||||
}
|
|
||||||
Vendored
+10
-7
@@ -21,6 +21,7 @@
|
|||||||
"AYUV",
|
"AYUV",
|
||||||
"battleresults",
|
"battleresults",
|
||||||
"bbox",
|
"bbox",
|
||||||
|
"beforeinstallprompt",
|
||||||
"bepaint",
|
"bepaint",
|
||||||
"betakrieg",
|
"betakrieg",
|
||||||
"BINORMAL",
|
"BINORMAL",
|
||||||
@@ -76,18 +77,22 @@
|
|||||||
"echarts",
|
"echarts",
|
||||||
"Everytime",
|
"Everytime",
|
||||||
"FASTNAME",
|
"FASTNAME",
|
||||||
|
"favicons",
|
||||||
"flexdirection",
|
"flexdirection",
|
||||||
|
"fontsource",
|
||||||
"FOURCC",
|
"FOURCC",
|
||||||
"frameloop",
|
"frameloop",
|
||||||
"fsformat",
|
"fsformat",
|
||||||
"Fullscreen",
|
"Fullscreen",
|
||||||
"fullstats",
|
"fullstats",
|
||||||
"fuzzysort",
|
"fuzzysort",
|
||||||
|
"gamemode",
|
||||||
"gameplay",
|
"gameplay",
|
||||||
"glsl",
|
"glsl",
|
||||||
"GLTF",
|
"GLTF",
|
||||||
"gtag",
|
"gtag",
|
||||||
"haha",
|
"haha",
|
||||||
|
"Herdbreaker",
|
||||||
"highp",
|
"highp",
|
||||||
"iife",
|
"iife",
|
||||||
"imgur",
|
"imgur",
|
||||||
@@ -154,6 +159,7 @@
|
|||||||
"Pyogenics",
|
"Pyogenics",
|
||||||
"ratingleaderboard",
|
"ratingleaderboard",
|
||||||
"raycaster",
|
"raycaster",
|
||||||
|
"Redirector",
|
||||||
"researchable",
|
"researchable",
|
||||||
"Resvg",
|
"Resvg",
|
||||||
"rizz",
|
"rizz",
|
||||||
@@ -167,6 +173,7 @@
|
|||||||
"searchtanks",
|
"searchtanks",
|
||||||
"SHAREDEXP",
|
"SHAREDEXP",
|
||||||
"SKLLD",
|
"SKLLD",
|
||||||
|
"smoothstep",
|
||||||
"Smri",
|
"Smri",
|
||||||
"Specularity",
|
"Specularity",
|
||||||
"SSAO",
|
"SSAO",
|
||||||
@@ -186,6 +193,7 @@
|
|||||||
"tankopedia",
|
"tankopedia",
|
||||||
"tankopedias",
|
"tankopedias",
|
||||||
"tankstats",
|
"tankstats",
|
||||||
|
"tauri",
|
||||||
"texconv",
|
"texconv",
|
||||||
"TEXCOORD",
|
"TEXCOORD",
|
||||||
"tresabhi",
|
"tresabhi",
|
||||||
@@ -222,12 +230,7 @@
|
|||||||
"yolo",
|
"yolo",
|
||||||
"youtuber",
|
"youtuber",
|
||||||
"youtubers",
|
"youtubers",
|
||||||
"zoomable",
|
"zoomable"
|
||||||
"zustand"
|
|
||||||
],
|
],
|
||||||
"deno.enable": false,
|
"typescript.tsdk": "node_modules\\typescript\\lib"
|
||||||
"deno.unstable": false,
|
|
||||||
"cSpell.enableFiletypes": ["glsl", "proto3"],
|
|
||||||
"typescript.tsdk": "node_modules\\typescript\\lib",
|
|
||||||
"liveServer.settings.multiRootWorkspaceName": "blitzkit"
|
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,49 @@
|
|||||||
## BlitzKit
|
# BlitzKit
|
||||||
|
|
||||||
Your premier source for all things Blitz.
|
Tools for everything and anything, World of Tanks Blitz.
|
||||||
|
|
||||||
### https://blitzkit.app/
|
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.
|
||||||
|
|
||||||
## Invite the bot
|
## Packages
|
||||||
|
|
||||||
Go to [application directory for BlitzKit](https://discord.com/application-directory/1097673957865443370) and click "Add to Server." Voila! You're ready to go.
|
| Package | Title | Source | Memo |
|
||||||
|
| ------------------------------------ | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ |
|
||||||
|
| [packages/app](packages/app) | BlitzKit | [Play Store](https://play.google.com/store/apps/details?id=com.tresabhi.blitzkit), [App Store](https://apps.apple.com/app/4321), [Microsoft Store](https://apps.microsoft.com/detail/zyxw) | The native app of BlitzKit for all platforms. Unavailable from stores at this time. |
|
||||||
|
| [packages/bot](packages/bot) | BlitzKit Discord Bot | [Discord Application Directory](https://discord.com/discovery/applications/1097673957865443370) | The one-in-all Discord bot for statistics. |
|
||||||
|
| [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 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#. |
|
||||||
|
|
||||||
|
## 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 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. |
|
||||||
|
| `PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID` | [Google Analytics](https://analytics.google.com/) | `430711767` | Google Analytics' unique property ID for [BlitzKit's apex domain](https://blitzkit.app/) and subdomains. |
|
||||||
|
| `GOOGLE_APPLICATION_CREDENTIALS` | [Google Cloud](https://cloud.google.com/) | | Google Cloud's API key, primarily used for ranking popular tanks and finding relevant tank review videos in Tankopedia. |
|
||||||
|
| `PUBLIC_DISCORD_SERVER_ID` | [BlitzKit Discord Server](https://discord.gg/nDt7AjGJQH) | `734786591205359697` | BlitzKit Discord Server's id. |
|
||||||
|
| `PUBLIC_ASSET_BASE` | [blitzkit/assets](https://github.com/blitzkit/assets) | `https://blitzkit.github.io/assets` | The root domain and path that serves the external assets like tank models and icons. |
|
||||||
|
| `PUBLIC_BRANCH` | | `main` | The branch that the website is operating on. This is completely nominal. |
|
||||||
|
| `PUBLIC_GUIDE_WEBSITE` | [GuidesBlitz](https://guidesblitz.com/) | `https://guidesblitz.com` | The website that serves the encyclopedia-like content in BlitzKit. |
|
||||||
|
| `SERVICING` | | `false` | Wether the website is down for servicing. |
|
||||||
|
| `DISCORD_CLIENT_ID` | [Discord Developer Portal](https://discord.com/developers/applications) | `1097673957865443370` | The BlitzKit Discord Bot's user ID. |
|
||||||
|
| `GH_TOKEN` | [GitHub Fine-Grained Tokens](https://github.com/settings/personal-access-tokens) | | The GitHub API key used for checking out private submodules and pushing content to the asset repository. |
|
||||||
|
| `DISCORD_TOKEN` | [Discord Developer Portal](https://discord.com/developers/applications) | | The Discord API Key for the BlitzKit Discord Bot. |
|
||||||
|
| `DATABASE_URL` | [CockroachDB](https://www.cockroachlabs.com/) | | The CockroachDB-SQL database URL used to link Wargaming and Discord users. |
|
||||||
|
| `WOTB_DLC_CDN` | | | |
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# BlitzKit 1.27.18
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Added a mini-game to BlitzKit similar to "Guess Who's That Pokemon" but with tanks: http://blitzkit.vercel.app/guess
|
||||||
|
- Switched to a new renderer for better lighting for the Tankopedia
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
- The loading bar for the Tankopedia sandbox now shows up again
|
||||||
|
- Removed a lot of dead code, improving performance
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# BlitzKit 1.27.19
|
||||||
|
|
||||||
|
Two updates in a day? I just got a little excited by the new Tankopedia lighting.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- The camera is now lower down in the Tankopedia, giving the tank a more heroic feel
|
||||||
|
- Tanks are better framed across all devices, minimizing whitespace
|
||||||
|
- Added a skip button for the reload visualizer to skip to the next event
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
- Removed the useless "show grid" setting
|
||||||
|
- Taking screenshots now no longer includes the scene props like the floor
|
||||||
|
- The subtler color contrast for the advanced highlighting has now been reverted back to the higher contrast
|
||||||
|
- The shot display card is now properly aligned as a proper table again
|
||||||
|
- Tank modules are once again considered in the penetration tester
|
||||||
|
- Tank filters in the antagonist selector in the Tankopedia sandbox are now properly aligned
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# BlitzKit 1.27.20
|
||||||
|
|
||||||
|
It's all technical updates! The HD tanks will be rolled out over the span of few days.
|
||||||
|
|
||||||
|
## Technical Changes
|
||||||
|
|
||||||
|
- Shadows are disabled if your framerate drops below 30fps
|
||||||
|
- Pages that redirect to other pages (like the old /tools/tankopedia) now load a few hundred times faster
|
||||||
|
- Replaced Zustand with a custom state manager called Varuna with the goal to rewrite a significant portion of the website to not require any React for rendering increasing performance
|
||||||
|
- The transition wireframe in the Tankopedia sandbox now uses a lower quality model for performance reasons
|
||||||
|
- Tanks with PBR now use the correct maximum resolution textures
|
||||||
|
- This is a significant update as you can now literally see individual splats of dust on tanks and scratches haha
|
||||||
|
- Baked the ambient occlusion maps into the textures, hence reducing the overall model load times even with higher resolution textures
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# BlitzKit 1.28.0
|
||||||
|
|
||||||
|
BlitzKit, as an app and the new Mixer tool!
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Created the BlitzKit app that can be downloaded on most devices
|
||||||
|
- Re-did the BlitzKit logo to be a bit warmer
|
||||||
|
- Added the Mixer tool which lets you combine tank models into Frankensteins
|
||||||
|
- Updated all dependencies to the latest version for a decent performance boost
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
- BlitzKit no longer forgets persistent data if the app crashes without properly closing
|
||||||
|
- The tools list in the navbar no longer overflows into the neighboring elements on certain devices
|
||||||
|
- Added keying to a few places, speeding up re-rendering
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# BlitzKit 1.28.1
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
- The lights no longer crash the Mixer tool
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# BlitzKit 1.28.2
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
- BlitzKit no longer tries to redirect translated pages to the same page, over and over again
|
||||||
@@ -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
|
||||||
+18
-10
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "blitzkit",
|
"name": "blitzkit",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.27.17",
|
"version": "1.28.4",
|
||||||
"description": "🎉 Tools for everything World of Tanks Blitz",
|
"description": "🎉 Tools for everything World of Tanks Blitz",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"types": "./types.d.ts",
|
"types": "./types.d.ts",
|
||||||
@@ -12,15 +12,18 @@
|
|||||||
"build:assets": "cd packages/scripts && bun run build:assets",
|
"build:assets": "cd packages/scripts && bun run build:assets",
|
||||||
"build:averages": "bun --filter=@blitzkit/scripts run build:averages",
|
"build:averages": "bun --filter=@blitzkit/scripts run build:averages",
|
||||||
"build:bot": "cd packages/scripts && bun run build:bot",
|
"build:bot": "cd packages/scripts && bun run build:bot",
|
||||||
"build:website": "bun build:prisma --no-hints && cd packages/website && bun run build",
|
"build:website": "bun build:prisma && cd packages/website && bun run build",
|
||||||
"build:prisma": "prisma generate",
|
"build:prisma": "prisma generate",
|
||||||
"build:sitemap": "bun scripts/sitemap.ts",
|
"build:sitemap": "bun scripts/sitemap.ts",
|
||||||
"build:docs": "vitepress build docs",
|
"build:docs": "vitepress build docs",
|
||||||
|
"build:proxyclient": "cd packages/scripts && bun run build:proxyclient",
|
||||||
"build:ratingleaderboard": "cd packages/scripts && bun run build:ratingleaderboard",
|
"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",
|
"dev:bot": "bun run build:bot --dev && bun start:bot",
|
||||||
"start:bot": "bun dist/bot/main.js",
|
"start:bot": "bun dist/bot/main.js",
|
||||||
"dev:docs": "vitepress dev docs",
|
"dev:docs": "vitepress dev docs",
|
||||||
"dev:website": "cd packages/website && bun run dev",
|
"dev:website": "cd packages/website && bun run dev",
|
||||||
|
"dev:app": "cd packages/app && bun run dev",
|
||||||
"dev": "bun run dev:website",
|
"dev": "bun run dev:website",
|
||||||
"dev:api": "bun --filter=@blitzkit/api run dev",
|
"dev:api": "bun --filter=@blitzkit/api run dev",
|
||||||
"setup:depotdownloader": "bun --filter=@blitzkit/scripts run setup:depotdownloader",
|
"setup:depotdownloader": "bun --filter=@blitzkit/scripts run setup:depotdownloader",
|
||||||
@@ -41,19 +44,24 @@
|
|||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@prisma/client": "^6.17.1",
|
||||||
|
"@types/xmpp__client": "^0.13.3",
|
||||||
|
"@xmpp/client": "^0.13.6",
|
||||||
|
"astro-favicons": "^3.1.5",
|
||||||
"cheerio": "^1.1.2",
|
"cheerio": "^1.1.2",
|
||||||
"googleapis": "^144.0.0",
|
"googleapis": "^160.0.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"protobufjs": "^7.5.2",
|
"protobufjs": "^7.5.4",
|
||||||
"quicklime": "^1.2.0",
|
"quicklime": "^1.2.0",
|
||||||
"sharp": "^0.33.5"
|
"sharp": "^0.34.4",
|
||||||
|
"yaml": "^2.8.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "latest",
|
"@types/bun": "^1.2.22",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/node": "^22.14.1",
|
"@types/node": "^24.5.2",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.6.2",
|
||||||
"prisma": "^6.6.0",
|
"prisma": "^6.16.2",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.9.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"name": "@blitzkit/app",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "tsc && vite build",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"tauri": "tauri"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@tauri-apps/api": "^2",
|
||||||
|
"@tauri-apps/plugin-opener": "^2",
|
||||||
|
"@tauri-apps/cli": "^2",
|
||||||
|
"vite": "^6.0.3",
|
||||||
|
"typescript": "~5.6.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# Generated by Cargo
|
||||||
|
# will have compiled files and executables
|
||||||
|
/target/
|
||||||
|
|
||||||
|
# Generated by Tauri
|
||||||
|
# will have schema files for capabilities auto-completion
|
||||||
|
/gen/schemas
|
||||||
Generated
+5286
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,25 @@
|
|||||||
|
[package]
|
||||||
|
name = "blitzkit"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "A Tauri App"
|
||||||
|
authors = ["you"]
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
# The `_lib` suffix may seem redundant but it is necessary
|
||||||
|
# to make the lib name unique and wouldn't conflict with the bin name.
|
||||||
|
# This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519
|
||||||
|
name = "blitzkit_lib"
|
||||||
|
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
tauri-build = { version = "2", features = [] }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
tauri = { version = "2", features = [] }
|
||||||
|
tauri-plugin-opener = "2"
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
serde_json = "1"
|
||||||
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fn main() {
|
||||||
|
tauri_build::build()
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"$schema": "../gen/schemas/desktop-schema.json",
|
||||||
|
"identifier": "default",
|
||||||
|
"description": "Capability for the main window",
|
||||||
|
"windows": ["main"],
|
||||||
|
"permissions": [
|
||||||
|
"core:default",
|
||||||
|
"opener:default"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
||||||
|
#[tauri::command]
|
||||||
|
fn greet(name: &str) -> String {
|
||||||
|
format!("Hello, {}! You've been greeted from Rust!", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||||
|
pub fn run() {
|
||||||
|
tauri::Builder::default()
|
||||||
|
.plugin(tauri_plugin_opener::init())
|
||||||
|
.invoke_handler(tauri::generate_handler![greet])
|
||||||
|
.run(tauri::generate_context!())
|
||||||
|
.expect("error while running tauri application");
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||||
|
|
||||||
|
use std::process::{Command, Stdio};
|
||||||
|
use tauri::Manager;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
tauri::Builder::default()
|
||||||
|
.setup(|app| {
|
||||||
|
let app_handle = app.handle();
|
||||||
|
|
||||||
|
let entry = app_handle
|
||||||
|
.path_resolver()
|
||||||
|
.resolve_resource("../../dist/website/server/entry.mjs")
|
||||||
|
.expect("failed to resolve server.ts");
|
||||||
|
|
||||||
|
Command::new("bun")
|
||||||
|
.arg("run")
|
||||||
|
.arg(entry)
|
||||||
|
.stderr(Stdio::null())
|
||||||
|
.spawn()
|
||||||
|
.expect("failed to start Bun server");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.run(tauri::generate_context!())
|
||||||
|
.expect("error while running Tauri app");
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
|
"productName": "blitzkit",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"identifier": "com.tresabhi.blitzkit",
|
||||||
|
"build": {
|
||||||
|
"beforeDevCommand": "cd ../website && bun run dev",
|
||||||
|
"_beforeBuildCommand": "cd ../website && bun run build",
|
||||||
|
"frontendDist": "../../../dist/website/client",
|
||||||
|
"devUrl": "http://localhost:4321"
|
||||||
|
},
|
||||||
|
"app": {
|
||||||
|
"withGlobalTauri": true,
|
||||||
|
"windows": [
|
||||||
|
{
|
||||||
|
"url": "http://localhost:4321",
|
||||||
|
"title": "BlitzKit",
|
||||||
|
"fullscreen": false,
|
||||||
|
"resizable": true,
|
||||||
|
"maximized": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"security": {
|
||||||
|
"csp": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bundle": {
|
||||||
|
"active": true,
|
||||||
|
"targets": "all",
|
||||||
|
"icon": [
|
||||||
|
"icons/32x32.png",
|
||||||
|
"icons/128x128.png",
|
||||||
|
"icons/[email protected]",
|
||||||
|
"icons/icon.icns",
|
||||||
|
"icons/icon.ico"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"extends": ["../../tsconfig.json"],
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,15 +6,16 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@blitzkit/core": "workspace:*",
|
"@blitzkit/core": "workspace:*",
|
||||||
"@blitzkit/i18n": "workspace:*",
|
"@blitzkit/i18n": "workspace:*",
|
||||||
"satori": "^0.10.14",
|
|
||||||
"@resvg/resvg-js": "^2.6.2",
|
|
||||||
"discord.js": "^14.16.3",
|
|
||||||
"react": "^18.3.1",
|
|
||||||
"@radix-ui/colors": "^3.0.0",
|
"@radix-ui/colors": "^3.0.0",
|
||||||
"fuzzysort": "^2.0.4",
|
"@resvg/resvg-js": "^2.6.2",
|
||||||
"bepaint": "^0.1.0-beta.3",
|
"bepaint": "^0.1.0-beta.3",
|
||||||
"dotenv": "^16.4.5",
|
"discord.js": "^14.22.1",
|
||||||
"markdown-escape": "^2.0.0"
|
"dotenv": "^17.2.2",
|
||||||
|
"fuzzysort": "^3.1.0",
|
||||||
|
"markdown-escape": "^2.0.0",
|
||||||
|
"prisma": "^6.17.1",
|
||||||
|
"react": "^19.1.1",
|
||||||
|
"satori": "^0.18.3"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "bun --watch src/main.ts",
|
"dev": "bun --watch src/main.ts",
|
||||||
|
|||||||
@@ -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');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -6,23 +6,23 @@ import {
|
|||||||
getTankStats,
|
getTankStats,
|
||||||
getWN8Percentile,
|
getWN8Percentile,
|
||||||
TankDefinition,
|
TankDefinition,
|
||||||
} from '@blitzkit/core';
|
} from "@blitzkit/core";
|
||||||
import { literals } from '@blitzkit/i18n';
|
import { literals } from "@blitzkit/i18n";
|
||||||
import { Locale } from 'discord.js';
|
import { Locale } from "discord.js";
|
||||||
import { chunk } from 'lodash-es';
|
import { chunk } from "lodash-es";
|
||||||
import * as Breakdown from '../../components/Breakdown';
|
import * as Breakdown from "../../components/Breakdown";
|
||||||
import { CommandWrapper } from '../../components/CommandWrapper';
|
import { CommandWrapper } from "../../components/CommandWrapper";
|
||||||
import { NoData } from '../../components/NoData';
|
import { NoData } from "../../components/NoData";
|
||||||
import { TitleBar } from '../../components/TitleBar';
|
import { TitleBar } from "../../components/TitleBar";
|
||||||
import { filtersToDescription } from '../../core/blitzkit/filtersToDescription';
|
import { filtersToDescription } from "../../core/blitzkit/filtersToDescription";
|
||||||
import { tankDefinitions } from '../../core/blitzkit/nonBlockingPromises';
|
import { tankDefinitions } from "../../core/blitzkit/nonBlockingPromises";
|
||||||
import { UserError } from '../../core/blitzkit/userError';
|
import { UserError } from "../../core/blitzkit/userError";
|
||||||
import { filterStats, StatFilters } from '../../core/blitzstars/filterStats';
|
import { filterStats, StatFilters } from "../../core/blitzstars/filterStats";
|
||||||
import { getStatsInPeriod } from '../../core/blitzstars/getStatsInPeriod';
|
import { getStatsInPeriod } from "../../core/blitzstars/getStatsInPeriod";
|
||||||
import { blitzStarsTankAverages } from '../../core/blitzstars/tankAverages';
|
import { blitzStarsTankAverages } from "../../core/blitzstars/tankAverages";
|
||||||
import { ResolvedPeriod } from '../../core/discord/resolvePeriodFromCommand';
|
import { ResolvedPeriod } from "../../core/discord/resolvePeriodFromCommand";
|
||||||
import { ResolvedPlayer } from '../../core/discord/resolvePlayerFromCommand';
|
import { ResolvedPlayer } from "../../core/discord/resolvePlayerFromCommand";
|
||||||
import { translator } from '../../core/localization/translator';
|
import { translator } from "../../core/localization/translator";
|
||||||
|
|
||||||
const ROWS_PER_PAGE = 8;
|
const ROWS_PER_PAGE = 8;
|
||||||
const MAX_PAGES = 9;
|
const MAX_PAGES = 9;
|
||||||
@@ -31,7 +31,7 @@ export async function renderBreakdown(
|
|||||||
{ region, id }: ResolvedPlayer,
|
{ region, id }: ResolvedPlayer,
|
||||||
{ start, end, name }: ResolvedPeriod,
|
{ start, end, name }: ResolvedPeriod,
|
||||||
filters: StatFilters,
|
filters: StatFilters,
|
||||||
locale: Locale,
|
locale: Locale
|
||||||
) {
|
) {
|
||||||
const { strings, unwrap } = translator(locale);
|
const { strings, unwrap } = translator(locale);
|
||||||
const awaitedTankDefinitions = await tankDefinitions;
|
const awaitedTankDefinitions = await tankDefinitions;
|
||||||
@@ -39,7 +39,7 @@ export async function renderBreakdown(
|
|||||||
const statsInPeriod = await getStatsInPeriod(region, id, start, end, locale);
|
const statsInPeriod = await getStatsInPeriod(region, id, start, end, locale);
|
||||||
const { filteredOrder } = await filterStats(statsInPeriod, filters);
|
const { filteredOrder } = await filterStats(statsInPeriod, filters);
|
||||||
const accountInfo = await getAccountInfo(region, id);
|
const accountInfo = await getAccountInfo(region, id);
|
||||||
const clanData = await getClanAccountInfo(region, id, ['clan']);
|
const clanData = await getClanAccountInfo(region, id, ["clan"]);
|
||||||
const tankStats = await getTankStats(region, id);
|
const tankStats = await getTankStats(region, id);
|
||||||
const filterDescriptions = await filtersToDescription(filters, locale);
|
const filterDescriptions = await filtersToDescription(filters, locale);
|
||||||
const orderedCurrentStats: BlitzStats[] = [];
|
const orderedCurrentStats: BlitzStats[] = [];
|
||||||
@@ -74,63 +74,63 @@ export async function renderBreakdown(
|
|||||||
orderedCareerWN8Full.push(
|
orderedCareerWN8Full.push(
|
||||||
tankAveragesEntry
|
tankAveragesEntry
|
||||||
? calculateWN8(tankAveragesEntry.all, tank.all)
|
? calculateWN8(tankAveragesEntry.all, tank.all)
|
||||||
: undefined,
|
: undefined
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const currentBattles = orderedCurrentStats.reduce(
|
const currentBattles = orderedCurrentStats.reduce(
|
||||||
(accumulator, current) => accumulator + current.battles,
|
(accumulator, current) => accumulator + current.battles,
|
||||||
0,
|
0
|
||||||
);
|
);
|
||||||
const careerBattles = tankStats.reduce(
|
const careerBattles = tankStats.reduce(
|
||||||
(accumulator, current) => accumulator + current.all.battles,
|
(accumulator, current) => accumulator + current.all.battles,
|
||||||
0,
|
0
|
||||||
);
|
);
|
||||||
const currentWinrate =
|
const currentWinrate =
|
||||||
orderedCurrentStats.reduce(
|
orderedCurrentStats.reduce(
|
||||||
(accumulator, current) => accumulator + current.wins,
|
(accumulator, current) => accumulator + current.wins,
|
||||||
0,
|
0
|
||||||
) / currentBattles;
|
) / currentBattles;
|
||||||
const careerWinrate =
|
const careerWinrate =
|
||||||
tankStats.reduce(
|
tankStats.reduce(
|
||||||
(accumulator, current) => accumulator + current.all.wins,
|
(accumulator, current) => accumulator + current.all.wins,
|
||||||
0,
|
0
|
||||||
) / careerBattles;
|
) / careerBattles;
|
||||||
const currentWN8 =
|
const currentWN8 =
|
||||||
orderedCurrentWN8.reduce<number>(
|
orderedCurrentWN8.reduce<number>(
|
||||||
(accumulator, current, index) =>
|
(accumulator, current, index) =>
|
||||||
accumulator + (current ?? 0) * orderedCurrentStats[index].battles,
|
accumulator + (current ?? 0) * orderedCurrentStats[index].battles,
|
||||||
0,
|
0
|
||||||
) /
|
) /
|
||||||
orderedCurrentStats.reduce(
|
orderedCurrentStats.reduce(
|
||||||
(accumulator, current, index) =>
|
(accumulator, current, index) =>
|
||||||
accumulator +
|
accumulator +
|
||||||
(orderedCurrentWN8[index] === undefined ? 0 : current.battles),
|
(orderedCurrentWN8[index] === undefined ? 0 : current.battles),
|
||||||
0,
|
0
|
||||||
);
|
);
|
||||||
const careerWN8 =
|
const careerWN8 =
|
||||||
orderedCareerWN8Full.reduce<number>(
|
orderedCareerWN8Full.reduce<number>(
|
||||||
(accumulator, current) => accumulator + (current ?? 0),
|
(accumulator, current) => accumulator + (current ?? 0),
|
||||||
0,
|
0
|
||||||
) /
|
) /
|
||||||
tankStats.reduce(
|
tankStats.reduce(
|
||||||
(accumulator, current, index) =>
|
(accumulator, current, index) =>
|
||||||
accumulator +
|
accumulator +
|
||||||
(orderedCareerWN8Full[index] === undefined ? 0 : current.all.battles),
|
(orderedCareerWN8Full[index] === undefined ? 0 : current.all.battles),
|
||||||
0,
|
0
|
||||||
);
|
);
|
||||||
const currentDamage =
|
const currentDamage =
|
||||||
orderedCurrentStats.reduce(
|
orderedCurrentStats.reduce(
|
||||||
(accumulator, current) => accumulator + current.damage_dealt,
|
(accumulator, current) => accumulator + current.damage_dealt,
|
||||||
0,
|
0
|
||||||
) / currentBattles;
|
) / currentBattles;
|
||||||
const careerDamage =
|
const careerDamage =
|
||||||
tankStats.reduce(
|
tankStats.reduce(
|
||||||
(accumulator, current) => accumulator + current.all.damage_dealt,
|
(accumulator, current) => accumulator + current.all.damage_dealt,
|
||||||
0,
|
0
|
||||||
) / careerBattles;
|
) / careerBattles;
|
||||||
|
|
||||||
const children: JSX.Element[] = [];
|
const children: React.JSX.Element[] = [];
|
||||||
|
|
||||||
if (filteredOrder.length > 0) {
|
if (filteredOrder.length > 0) {
|
||||||
children.push(
|
children.push(
|
||||||
@@ -172,7 +172,7 @@ export async function renderBreakdown(
|
|||||||
delta: currentDamage - careerDamage,
|
delta: currentDamage - careerDamage,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>,
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
filteredOrder.forEach((id, index) => {
|
filteredOrder.forEach((id, index) => {
|
||||||
@@ -193,9 +193,9 @@ export async function renderBreakdown(
|
|||||||
title={
|
title={
|
||||||
tankDefinition
|
tankDefinition
|
||||||
? unwrap(tankDefinition.name)
|
? unwrap(tankDefinition.name)
|
||||||
: literals(strings.bot.commands.breakdown.body.unknown_tank, [
|
: literals(strings.bot.commands.breakdown.body.unknown_tank, {
|
||||||
`${id}`,
|
id,
|
||||||
])
|
})
|
||||||
}
|
}
|
||||||
stats={[
|
stats={[
|
||||||
{
|
{
|
||||||
@@ -206,7 +206,7 @@ export async function renderBreakdown(
|
|||||||
{
|
{
|
||||||
title: strings.bot.commands.breakdown.body.winrate,
|
title: strings.bot.commands.breakdown.body.winrate,
|
||||||
current: `${(100 * (current.wins / current.battles)).toFixed(
|
current: `${(100 * (current.wins / current.battles)).toFixed(
|
||||||
2,
|
2
|
||||||
)}%`,
|
)}%`,
|
||||||
career: `${(100 * (career.wins / career.battles)).toFixed(2)}%`,
|
career: `${(100 * (career.wins / career.battles)).toFixed(2)}%`,
|
||||||
delta:
|
delta:
|
||||||
@@ -230,24 +230,24 @@ export async function renderBreakdown(
|
|||||||
{
|
{
|
||||||
title: strings.bot.commands.breakdown.body.damage,
|
title: strings.bot.commands.breakdown.body.damage,
|
||||||
current: Math.round(
|
current: Math.round(
|
||||||
current.damage_dealt / current.battles,
|
current.damage_dealt / current.battles
|
||||||
).toLocaleString(),
|
).toLocaleString(),
|
||||||
career: Math.round(
|
career: Math.round(
|
||||||
career.damage_dealt / career.battles,
|
career.damage_dealt / career.battles
|
||||||
).toLocaleString(),
|
).toLocaleString(),
|
||||||
delta:
|
delta:
|
||||||
current.damage_dealt / current.battles -
|
current.damage_dealt / current.battles -
|
||||||
career.damage_dealt / career.battles,
|
career.damage_dealt / career.battles,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>,
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const pages = chunk(
|
const pages = chunk(
|
||||||
children,
|
children,
|
||||||
Math.max(Math.ceil(children.length / MAX_PAGES), ROWS_PER_PAGE),
|
Math.max(Math.ceil(children.length / MAX_PAGES), ROWS_PER_PAGE)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (filteredOrder.length > 0) {
|
if (filteredOrder.length > 0) {
|
||||||
|
|||||||
@@ -1,44 +1,44 @@
|
|||||||
import { getAccountInfo, getClanInfo } from '@blitzkit/core';
|
import { getAccountInfo, getClanInfo } from "@blitzkit/core";
|
||||||
import { literals } from '@blitzkit/i18n';
|
import { literals } from "@blitzkit/i18n";
|
||||||
import { Locale } from 'discord.js';
|
import { Locale } from "discord.js";
|
||||||
import { CommandWrapper } from '../components/CommandWrapper';
|
import { CommandWrapper } from "../components/CommandWrapper";
|
||||||
import { GenericStats } from '../components/GenericStats';
|
import { GenericStats } from "../components/GenericStats";
|
||||||
import { NoData } from '../components/NoData';
|
import { NoData } from "../components/NoData";
|
||||||
import { TitleBar } from '../components/TitleBar';
|
import { TitleBar } from "../components/TitleBar";
|
||||||
import { addClanChoices } from '../core/discord/addClanChoices';
|
import { addClanChoices } from "../core/discord/addClanChoices";
|
||||||
import { autocompleteClan } from '../core/discord/autocompleteClan';
|
import { autocompleteClan } from "../core/discord/autocompleteClan";
|
||||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||||
import { localizationObject } from '../core/discord/localizationObject';
|
import { localizationObject } from "../core/discord/localizationObject";
|
||||||
import { resolveClanFromCommand } from '../core/discord/resolveClanFromCommand';
|
import { resolveClanFromCommand } from "../core/discord/resolveClanFromCommand";
|
||||||
import { translator } from '../core/localization/translator';
|
import { translator } from "../core/localization/translator";
|
||||||
import { CommandRegistry } from '../events/interactionCreate';
|
import { CommandRegistry } from "../events/interactionCreate";
|
||||||
|
|
||||||
const DEFAULT_THRESHOLD = 7;
|
const DEFAULT_THRESHOLD = 7;
|
||||||
|
|
||||||
export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
|
export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
|
||||||
resolve({
|
resolve({
|
||||||
command: createLocalizedCommand('inactive')
|
command: createLocalizedCommand("inactive")
|
||||||
.addStringOption(addClanChoices)
|
.addStringOption(addClanChoices)
|
||||||
.addNumberOption((option) => {
|
.addNumberOption((option) => {
|
||||||
const { strings } = translator(Locale.EnglishUS);
|
const { strings } = translator(Locale.EnglishUS);
|
||||||
|
|
||||||
return option
|
return option
|
||||||
.setName('threshold')
|
.setName("threshold")
|
||||||
.setNameLocalizations(
|
.setNameLocalizations(
|
||||||
localizationObject(
|
localizationObject(
|
||||||
(strings) => strings.bot.commands.inactive.options.threshold.name,
|
(strings) => strings.bot.commands.inactive.options.threshold.name,
|
||||||
undefined,
|
undefined,
|
||||||
true,
|
true
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
.setDescription(
|
.setDescription(
|
||||||
strings.bot.commands.inactive.options.threshold.description,
|
strings.bot.commands.inactive.options.threshold.description
|
||||||
)
|
)
|
||||||
.setDescriptionLocalizations(
|
.setDescriptionLocalizations(
|
||||||
localizationObject(
|
localizationObject(
|
||||||
(strings) =>
|
(strings) =>
|
||||||
strings.bot.commands.inactive.options.threshold.description,
|
strings.bot.commands.inactive.options.threshold.description
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
.setMinValue(0);
|
.setMinValue(0);
|
||||||
}),
|
}),
|
||||||
@@ -47,7 +47,7 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
const { strings } = translator(interaction.locale);
|
const { strings } = translator(interaction.locale);
|
||||||
const { region, id } = await resolveClanFromCommand(interaction);
|
const { region, id } = await resolveClanFromCommand(interaction);
|
||||||
const threshold =
|
const threshold =
|
||||||
interaction.options.getNumber('threshold')! ?? DEFAULT_THRESHOLD;
|
interaction.options.getNumber("threshold")! ?? DEFAULT_THRESHOLD;
|
||||||
const time = new Date().getTime() / 1000;
|
const time = new Date().getTime() / 1000;
|
||||||
const clanInfo = await getClanInfo(region, id);
|
const clanInfo = await getClanInfo(region, id);
|
||||||
const accountInfo = await getAccountInfo(region, clanInfo.members_ids);
|
const accountInfo = await getAccountInfo(region, clanInfo.members_ids);
|
||||||
@@ -57,7 +57,7 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
[
|
[
|
||||||
account.nickname,
|
account.nickname,
|
||||||
(time - account.last_battle_time) / 60 / 60 / 24,
|
(time - account.last_battle_time) / 60 / 60 / 24,
|
||||||
] as [string, number],
|
] as [string, number]
|
||||||
)
|
)
|
||||||
.filter(([, inactiveDays]) => inactiveDays >= threshold)
|
.filter(([, inactiveDays]) => inactiveDays >= threshold)
|
||||||
.sort((a, b) => b[1] - a[1])
|
.sort((a, b) => b[1] - a[1])
|
||||||
@@ -65,10 +65,10 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
([name, days]) =>
|
([name, days]) =>
|
||||||
[
|
[
|
||||||
name,
|
name,
|
||||||
literals(strings.bot.commands.inactive.body.listing, [
|
literals(strings.bot.commands.inactive.body.listing, {
|
||||||
days.toFixed(0),
|
days: days.toFixed(0),
|
||||||
]),
|
}),
|
||||||
] as [string, string],
|
] as [string, string]
|
||||||
);
|
);
|
||||||
const hasInactiveMembers = inactive.length > 0;
|
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`}
|
image={`https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clanInfo.emblem_set_id}.png`}
|
||||||
description={`${literals(
|
description={`${literals(
|
||||||
strings.bot.commands.inactive.body.subtitle,
|
strings.bot.commands.inactive.body.subtitle,
|
||||||
[`${threshold}`],
|
{ days: threshold }
|
||||||
)} • ${new Date().toLocaleDateString(interaction.locale)}`}
|
)} • ${new Date().toLocaleDateString(interaction.locale)}`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -5,31 +5,31 @@ import {
|
|||||||
flags,
|
flags,
|
||||||
getAccountInfo,
|
getAccountInfo,
|
||||||
getTankStats,
|
getTankStats,
|
||||||
} from '@blitzkit/core';
|
} from "@blitzkit/core";
|
||||||
import { literals } from '@blitzkit/i18n';
|
import { literals } from "@blitzkit/i18n";
|
||||||
import markdownEscape from 'markdown-escape';
|
import markdownEscape from "markdown-escape";
|
||||||
import {
|
import {
|
||||||
gameDefinitions,
|
gameDefinitions,
|
||||||
tankDefinitions,
|
tankDefinitions,
|
||||||
} from '../core/blitzkit/nonBlockingPromises';
|
} from "../core/blitzkit/nonBlockingPromises";
|
||||||
import { addTierChoices } from '../core/discord/addTierChoices';
|
import { addTierChoices } from "../core/discord/addTierChoices";
|
||||||
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
|
import { addUsernameChoices } from "../core/discord/addUsernameChoices";
|
||||||
import { autocompleteUsername } from '../core/discord/autocompleteUsername';
|
import { autocompleteUsername } from "../core/discord/autocompleteUsername";
|
||||||
import { chunkLines } from '../core/discord/chunkLines';
|
import { chunkLines } from "../core/discord/chunkLines";
|
||||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||||
import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand';
|
import { resolvePlayerFromCommand } from "../core/discord/resolvePlayerFromCommand";
|
||||||
import { translator } from '../core/localization/translator';
|
import { translator } from "../core/localization/translator";
|
||||||
import { CommandRegistry } from '../events/interactionCreate';
|
import { CommandRegistry } from "../events/interactionCreate";
|
||||||
|
|
||||||
export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
|
export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
|
||||||
resolve({
|
resolve({
|
||||||
command: createLocalizedCommand('owned-tanks')
|
command: createLocalizedCommand("owned-tanks")
|
||||||
.addStringOption(addTierChoices)
|
.addStringOption(addTierChoices)
|
||||||
.addStringOption(addUsernameChoices),
|
.addStringOption(addUsernameChoices),
|
||||||
|
|
||||||
async handler(interaction) {
|
async handler(interaction) {
|
||||||
const { strings, unwrap } = translator(interaction.locale);
|
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 { id, region } = await resolvePlayerFromCommand(interaction);
|
||||||
const accountInfo = await getAccountInfo(region, id);
|
const accountInfo = await getAccountInfo(region, id);
|
||||||
const tankStats = await getTankStats(region, id);
|
const tankStats = await getTankStats(region, id);
|
||||||
@@ -43,7 +43,7 @@ export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
tankStats.map(async (tankData) => ({
|
tankStats.map(async (tankData) => ({
|
||||||
tankDefinitions: (await tankDefinitions).tanks[tankData.tank_id]!,
|
tankDefinitions: (await tankDefinitions).tanks[tankData.tank_id]!,
|
||||||
id: tankData.tank_id,
|
id: tankData.tank_id,
|
||||||
})),
|
}))
|
||||||
)
|
)
|
||||||
).filter((tank) => tank.tankDefinitions?.tier === tier);
|
).filter((tank) => tank.tankDefinitions?.tier === tier);
|
||||||
const groupedTanks: Record<string, TankDefinition[]> = {};
|
const groupedTanks: Record<string, TankDefinition[]> = {};
|
||||||
@@ -60,25 +60,25 @@ export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
|
|
||||||
const awaitedTankDefinitions = await tankDefinitions;
|
const awaitedTankDefinitions = await tankDefinitions;
|
||||||
const awaitedGameDefinitions = await gameDefinitions;
|
const awaitedGameDefinitions = await gameDefinitions;
|
||||||
const title = literals(strings.bot.commands.owned_tanks.body.title, [
|
const title = literals(strings.bot.commands.owned_tanks.body.title, {
|
||||||
markdownEscape(accountInfo.nickname),
|
name: markdownEscape(accountInfo.nickname),
|
||||||
TIER_ROMAN_NUMERALS[tier],
|
tier: TIER_ROMAN_NUMERALS[tier],
|
||||||
]);
|
});
|
||||||
const tankList = tankStats
|
const tankList = tankStats
|
||||||
.map(({ tank_id }) => awaitedTankDefinitions.tanks[tank_id])
|
.map(({ tank_id }) => awaitedTankDefinitions.tanks[tank_id])
|
||||||
.filter((tank) => tank.tier === tier)
|
.filter((tank) => tank.tier === tier)
|
||||||
.sort(
|
.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
TANK_CLASSES.indexOf(a.class) - TANK_CLASSES.indexOf(b.class),
|
TANK_CLASSES.indexOf(a.class) - TANK_CLASSES.indexOf(b.class)
|
||||||
)
|
)
|
||||||
.sort(
|
.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
awaitedGameDefinitions.nations.indexOf(a.nation) -
|
awaitedGameDefinitions.nations.indexOf(a.nation) -
|
||||||
awaitedGameDefinitions.nations.indexOf(b.nation),
|
awaitedGameDefinitions.nations.indexOf(b.nation)
|
||||||
)
|
)
|
||||||
.map(
|
.map(
|
||||||
(tank) =>
|
(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];
|
const lines = [title, ...tankList];
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { literals } from '@blitzkit/i18n';
|
import { literals } from "@blitzkit/i18n";
|
||||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||||
import { translator } from '../core/localization/translator';
|
import { translator } from "../core/localization/translator";
|
||||||
import { CommandRegistry } from '../events/interactionCreate';
|
import { CommandRegistry } from "../events/interactionCreate";
|
||||||
|
|
||||||
export const permissionsCommand = new Promise<CommandRegistry>((resolve) => {
|
export const permissionsCommand = new Promise<CommandRegistry>((resolve) => {
|
||||||
resolve({
|
resolve({
|
||||||
command: createLocalizedCommand('permissions'),
|
command: createLocalizedCommand("permissions"),
|
||||||
|
|
||||||
async handler(interaction) {
|
async handler(interaction) {
|
||||||
const { strings } = translator(interaction.locale);
|
const { strings } = translator(interaction.locale);
|
||||||
@@ -14,33 +14,33 @@ export const permissionsCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
new Date().getDay() === 5 && new Date().getDate() === 13;
|
new Date().getDay() === 5 && new Date().getDate() === 13;
|
||||||
const permissions = [
|
const permissions = [
|
||||||
[
|
[
|
||||||
interaction.appPermissions?.has('ViewChannel'),
|
interaction.appPermissions?.has("ViewChannel"),
|
||||||
strings.bot.commands.permissions.body.view_channels,
|
strings.bot.commands.permissions.body.view_channels,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
interaction.appPermissions?.has('ReadMessageHistory'),
|
interaction.appPermissions?.has("ReadMessageHistory"),
|
||||||
strings.bot.commands.permissions.body.read_message_history,
|
strings.bot.commands.permissions.body.read_message_history,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
interaction.appPermissions?.has('AttachFiles'),
|
interaction.appPermissions?.has("AttachFiles"),
|
||||||
strings.bot.commands.permissions.body.attach_files,
|
strings.bot.commands.permissions.body.attach_files,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
interaction.appPermissions?.has('UseExternalEmojis'),
|
interaction.appPermissions?.has("UseExternalEmojis"),
|
||||||
strings.bot.commands.permissions.body.external_emojis,
|
strings.bot.commands.permissions.body.external_emojis,
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
const body = permissions
|
const body = permissions
|
||||||
.map(([hasPermission, description]) => {
|
.map(([hasPermission, description]) => {
|
||||||
if (hasPermission) {
|
if (hasPermission) {
|
||||||
return `${isFridayThe13th ? '😇' : '✅'} ${description}`;
|
return `${isFridayThe13th ? "😇" : "✅"} ${description}`;
|
||||||
} else {
|
} 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 { literals } from "@blitzkit/i18n";
|
||||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||||
import { translator } from '../core/localization/translator';
|
import { translator } from "../core/localization/translator";
|
||||||
import { CommandRegistry } from '../events/interactionCreate';
|
import { CommandRegistry } from "../events/interactionCreate";
|
||||||
|
|
||||||
export const pingCommand = new Promise<CommandRegistry>((resolve) => {
|
export const pingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||||
resolve({
|
resolve({
|
||||||
handlesInteraction: true,
|
handlesInteraction: true,
|
||||||
|
|
||||||
command: createLocalizedCommand('ping', [
|
command: createLocalizedCommand("ping", [
|
||||||
{ subcommand: 'blitzkit' },
|
{ subcommand: "blitzkit" },
|
||||||
{ subcommand: 'wotb' },
|
{ subcommand: "wotb" },
|
||||||
]),
|
]),
|
||||||
|
|
||||||
async handler(interaction) {
|
async handler(interaction) {
|
||||||
@@ -17,17 +17,17 @@ export const pingCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
const subcommand = interaction.options.getSubcommand();
|
const subcommand = interaction.options.getSubcommand();
|
||||||
const executionStart = Date.now();
|
const executionStart = Date.now();
|
||||||
|
|
||||||
if (subcommand === 'blitzkit') {
|
if (subcommand === "blitzkit") {
|
||||||
await interaction.editReply(strings.bot.commands.ping.body.pong);
|
await interaction.editReply(strings.bot.commands.ping.body.pong);
|
||||||
} else {
|
} else {
|
||||||
await fetch('https://api.wotblitz.com/');
|
await fetch("https://api.wotblitz.com/");
|
||||||
}
|
}
|
||||||
|
|
||||||
const executionTime = Date.now() - executionStart;
|
const executionTime = Date.now() - executionStart;
|
||||||
interaction.editReply(
|
interaction.editReply(
|
||||||
literals(strings.bot.commands.ping.body.pong_with_time, [
|
literals(strings.bot.commands.ping.body.pong_with_time, {
|
||||||
`${Math.round(executionTime)}`,
|
time: executionTime.toFixed(0),
|
||||||
]),
|
})
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
import { getAccountInfo } from '@blitzkit/core';
|
import { getAccountInfo } from "@blitzkit/core";
|
||||||
import { literals } from '@blitzkit/i18n';
|
import { literals } from "@blitzkit/i18n";
|
||||||
import markdownEscape from 'markdown-escape';
|
import markdownEscape from "markdown-escape";
|
||||||
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
|
import { addUsernameChoices } from "../core/discord/addUsernameChoices";
|
||||||
import { autocompleteUsername } from '../core/discord/autocompleteUsername';
|
import { autocompleteUsername } from "../core/discord/autocompleteUsername";
|
||||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||||
import { embedInfo } from '../core/discord/embedInfo';
|
import { embedInfo } from "../core/discord/embedInfo";
|
||||||
import { markdownTable } from '../core/discord/markdownTable';
|
import { markdownTable } from "../core/discord/markdownTable";
|
||||||
import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand';
|
import { resolvePlayerFromCommand } from "../core/discord/resolvePlayerFromCommand";
|
||||||
import { translator } from '../core/localization/translator';
|
import { translator } from "../core/localization/translator";
|
||||||
import { CommandRegistry } from '../events/interactionCreate';
|
import { CommandRegistry } from "../events/interactionCreate";
|
||||||
|
|
||||||
export const playerInfoCommand = new Promise<CommandRegistry>((resolve) => {
|
export const playerInfoCommand = new Promise<CommandRegistry>((resolve) => {
|
||||||
resolve({
|
resolve({
|
||||||
command:
|
command:
|
||||||
createLocalizedCommand('player-info').addStringOption(addUsernameChoices),
|
createLocalizedCommand("player-info").addStringOption(addUsernameChoices),
|
||||||
|
|
||||||
async handler(interaction) {
|
async handler(interaction) {
|
||||||
const { strings } = translator(interaction.locale);
|
const { strings } = translator(interaction.locale);
|
||||||
@@ -22,9 +22,9 @@ export const playerInfoCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
const accountInfo = await getAccountInfo(region, id);
|
const accountInfo = await getAccountInfo(region, id);
|
||||||
|
|
||||||
return embedInfo(
|
return embedInfo(
|
||||||
literals(strings.bot.commands.player_info.body.title, [
|
literals(strings.bot.commands.player_info.body.title, {
|
||||||
markdownEscape(accountInfo.nickname),
|
name: markdownEscape(accountInfo.nickname),
|
||||||
]),
|
}),
|
||||||
|
|
||||||
markdownTable([
|
markdownTable([
|
||||||
[
|
[
|
||||||
@@ -51,16 +51,16 @@ export const playerInfoCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
[
|
[
|
||||||
strings.bot.commands.player_info.body.created,
|
strings.bot.commands.player_info.body.created,
|
||||||
new Date(accountInfo.created_at * 1000).toLocaleDateString(
|
new Date(accountInfo.created_at * 1000).toLocaleDateString(
|
||||||
interaction.locale,
|
interaction.locale
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
strings.bot.commands.player_info.body.last_battle,
|
strings.bot.commands.player_info.body.last_battle,
|
||||||
new Date(accountInfo.last_battle_time * 1000).toLocaleDateString(
|
new Date(accountInfo.last_battle_time * 1000).toLocaleDateString(
|
||||||
interaction.locale,
|
interaction.locale
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
]),
|
])
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -8,42 +8,42 @@ import {
|
|||||||
getRatingInfo,
|
getRatingInfo,
|
||||||
getRatingNeighbors,
|
getRatingNeighbors,
|
||||||
imgur,
|
imgur,
|
||||||
} from '@blitzkit/core';
|
} from "@blitzkit/core";
|
||||||
import { literals } from '@blitzkit/i18n';
|
import { literals } from "@blitzkit/i18n";
|
||||||
import { SlashCommandSubcommandBuilder } from 'discord.js';
|
import { SlashCommandSubcommandBuilder } from "discord.js";
|
||||||
import markdownEscape from 'markdown-escape';
|
import markdownEscape from "markdown-escape";
|
||||||
import { Glow } from '../components/AllStatsOverview/components/HeroStat/components/Glow';
|
import { Glow } from "../components/AllStatsOverview/components/HeroStat/components/Glow";
|
||||||
import { CommandWrapper } from '../components/CommandWrapper';
|
import { CommandWrapper } from "../components/CommandWrapper";
|
||||||
import { DeltaCaret } from '../components/DeltaCaret';
|
import { DeltaCaret } from "../components/DeltaCaret";
|
||||||
import { TitleBar } from '../components/TitleBar';
|
import { TitleBar } from "../components/TitleBar";
|
||||||
import { normalizeLeagueIcon } from '../core/blitz/normalizeLeagueIcon';
|
import { normalizeLeagueIcon } from "../core/blitz/normalizeLeagueIcon";
|
||||||
import { iconPng } from '../core/blitzkit/iconPng';
|
import { iconPng } from "../core/blitzkit/iconPng";
|
||||||
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
|
import { addUsernameChoices } from "../core/discord/addUsernameChoices";
|
||||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||||
import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand';
|
import { resolvePlayerFromCommand } from "../core/discord/resolvePlayerFromCommand";
|
||||||
import { translator } from '../core/localization/translator';
|
import { translator } from "../core/localization/translator";
|
||||||
import { CommandRegistry } from '../events/interactionCreate';
|
import { CommandRegistry } from "../events/interactionCreate";
|
||||||
import { theme } from '../stitches.config';
|
import { theme } from "../stitches.config";
|
||||||
|
|
||||||
const LEAGUE_ACCENT = [
|
const LEAGUE_ACCENT = [
|
||||||
'_purple',
|
"_purple",
|
||||||
'_sand',
|
"_sand",
|
||||||
'_amber',
|
"_amber",
|
||||||
'_mauve',
|
"_mauve",
|
||||||
'_brown',
|
"_brown",
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
||||||
resolve({
|
resolve({
|
||||||
command: createLocalizedCommand('rating', [
|
command: createLocalizedCommand("rating", [
|
||||||
{
|
{
|
||||||
subcommand: 'today',
|
subcommand: "today",
|
||||||
modify(option: SlashCommandSubcommandBuilder) {
|
modify(option: SlashCommandSubcommandBuilder) {
|
||||||
option.addStringOption(addUsernameChoices);
|
option.addStringOption(addUsernameChoices);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
subcommand: 'season',
|
subcommand: "season",
|
||||||
modify(option: SlashCommandSubcommandBuilder) {
|
modify(option: SlashCommandSubcommandBuilder) {
|
||||||
option.addStringOption(addUsernameChoices);
|
option.addStringOption(addUsernameChoices);
|
||||||
},
|
},
|
||||||
@@ -52,11 +52,11 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
|
|
||||||
async handler(interaction) {
|
async handler(interaction) {
|
||||||
const subcommand = interaction.options.getSubcommand(true) as
|
const subcommand = interaction.options.getSubcommand(true) as
|
||||||
| 'season'
|
| "season"
|
||||||
| 'today';
|
| "today";
|
||||||
const { strings } = translator(interaction.locale);
|
const { strings } = translator(interaction.locale);
|
||||||
const { id, region } = await resolvePlayerFromCommand(interaction);
|
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 clanImage = clan ? emblemURL(clan.emblem_set_id) : undefined;
|
||||||
|
|
||||||
const ratingInfo = await getRatingInfo(region);
|
const ratingInfo = await getRatingInfo(region);
|
||||||
@@ -67,20 +67,20 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
|
|
||||||
const leaderboard = await getArchivedRatingLeaderboard(
|
const leaderboard = await getArchivedRatingLeaderboard(
|
||||||
region,
|
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(
|
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, [
|
const accountInfo = await getAccountInfo(region, id, [
|
||||||
'statistics.rating',
|
"statistics.rating",
|
||||||
]);
|
]);
|
||||||
const statsIndex = leaderboard.version!.value.entries.findIndex(
|
const statsIndex = leaderboard.version!.value.entries.findIndex(
|
||||||
(entry) => entry.id === id,
|
(entry) => entry.id === id
|
||||||
);
|
);
|
||||||
const statsA = leaderboard
|
const statsA = leaderboard
|
||||||
? leaderboard.version!.value.entries[statsIndex!]
|
? leaderboard.version!.value.entries[statsIndex!]
|
||||||
@@ -90,9 +90,9 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
const statsB2Array = (await getRatingNeighbors(region, id, 0)).neighbors;
|
const statsB2Array = (await getRatingNeighbors(region, id, 0)).neighbors;
|
||||||
|
|
||||||
if (statsB2Array === undefined) {
|
if (statsB2Array === undefined) {
|
||||||
return literals(strings.bot.commands.rating.errors.no_participation, [
|
return literals(strings.bot.commands.rating.errors.no_participation, {
|
||||||
markdownEscape(accountInfo.nickname),
|
name: markdownEscape(accountInfo.nickname),
|
||||||
]);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const [statsB2] = statsB2Array;
|
const [statsB2] = statsB2Array;
|
||||||
@@ -102,15 +102,15 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
return strings.bot.commands.rating.errors.unrecorded_stats;
|
return strings.bot.commands.rating.errors.unrecorded_stats;
|
||||||
}
|
}
|
||||||
if (!statsB1) {
|
if (!statsB1) {
|
||||||
return literals(strings.bot.commands.rating.errors.no_participation, [
|
return literals(strings.bot.commands.rating.errors.no_participation, {
|
||||||
markdownEscape(accountInfo.nickname),
|
name: markdownEscape(accountInfo.nickname),
|
||||||
]);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const reward = ratingInfo.rewards.find(
|
const reward = ratingInfo.rewards.find(
|
||||||
(reward) =>
|
(reward) =>
|
||||||
statsB2.number >= reward.from_position &&
|
statsB2.number >= reward.from_position &&
|
||||||
statsB2.number <= reward.to_position,
|
statsB2.number <= reward.to_position
|
||||||
);
|
);
|
||||||
const positionDelta = leaderboard
|
const positionDelta = leaderboard
|
||||||
? statsB2.number - positionA!
|
? statsB2.number - positionA!
|
||||||
@@ -118,11 +118,11 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
const delta = deltaBkrlBlitzStats(
|
const delta = deltaBkrlBlitzStats(
|
||||||
statsA,
|
statsA,
|
||||||
accountInfo.statistics.rating!,
|
accountInfo.statistics.rating!,
|
||||||
statsB2,
|
statsB2
|
||||||
);
|
);
|
||||||
const leagueTheme = LEAGUE_ACCENT[league.index];
|
const leagueTheme = LEAGUE_ACCENT[league.index];
|
||||||
let rewardImage = reward
|
let rewardImage = reward
|
||||||
? reward.type === 'vehicle'
|
? reward.type === "vehicle"
|
||||||
? reward.vehicle.preview_image_url
|
? reward.vehicle.preview_image_url
|
||||||
: reward.stuff.image_url
|
: reward.stuff.image_url
|
||||||
: undefined;
|
: undefined;
|
||||||
@@ -131,7 +131,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
const { ok } = await fetch(rewardImage);
|
const { ok } = await fetch(rewardImage);
|
||||||
|
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
rewardImage = imgur('kP88vqr');
|
rewardImage = imgur("kP88vqr");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,24 +147,24 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
position: 'relative',
|
position: "relative",
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
overflow: 'hidden',
|
overflow: "hidden",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
position: 'relative',
|
position: "relative",
|
||||||
padding: 16,
|
padding: 16,
|
||||||
backgroundColor: theme.colors[`appBackground2${leagueTheme}`],
|
backgroundColor: theme.colors[`appBackground2${leagueTheme}`],
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
border: `1px solid ${
|
border: `1px solid ${
|
||||||
theme.colors[`componentInteractive${leagueTheme}`]
|
theme.colors[`componentInteractive${leagueTheme}`]
|
||||||
}`,
|
}`,
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
alignItems: 'center',
|
alignItems: "center",
|
||||||
justifyContent: 'space-between',
|
justifyContent: "space-between",
|
||||||
width: '100%',
|
width: "100%",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{reward === undefined && (
|
{reward === undefined && (
|
||||||
@@ -172,17 +172,17 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
endOpacity={3 / 4}
|
endOpacity={3 / 4}
|
||||||
color={theme.colors[`solidBackground${leagueTheme}`]}
|
color={theme.colors[`solidBackground${leagueTheme}`]}
|
||||||
style={{
|
style={{
|
||||||
transform: 'translateX(-16px) rotate(90deg)',
|
transform: "translateX(-16px) rotate(90deg)",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
gap: 8,
|
gap: 8,
|
||||||
justifyContent: 'center',
|
justifyContent: "center",
|
||||||
alignItems: 'center',
|
alignItems: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
@@ -190,24 +190,24 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
style={{
|
style={{
|
||||||
width: 64,
|
width: 64,
|
||||||
height: 64,
|
height: 64,
|
||||||
objectFit: 'contain',
|
objectFit: "contain",
|
||||||
}}
|
}}
|
||||||
src={normalizeLeagueIcon(
|
src={normalizeLeagueIcon(
|
||||||
ratingInfo.leagues[league.index].big_icon,
|
ratingInfo.leagues[league.index].big_icon
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
flexDirection: 'column',
|
flexDirection: "column",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
alignItems: 'center',
|
alignItems: "center",
|
||||||
justifyContent: 'flex-start',
|
justifyContent: "flex-start",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@@ -232,7 +232,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{Math.abs(delta.score).toLocaleString(
|
{Math.abs(delta.score).toLocaleString(
|
||||||
interaction.locale,
|
interaction.locale
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
</>
|
</>
|
||||||
@@ -241,9 +241,9 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
alignItems: 'center',
|
alignItems: "center",
|
||||||
justifyContent: 'flex-start',
|
justifyContent: "flex-start",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@@ -267,7 +267,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{Math.abs(positionDelta).toLocaleString(
|
{Math.abs(positionDelta).toLocaleString(
|
||||||
interaction.locale,
|
interaction.locale
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
</>
|
</>
|
||||||
@@ -279,23 +279,23 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
{reward && (
|
{reward && (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
gap: 8,
|
gap: 8,
|
||||||
justifyContent: 'center',
|
justifyContent: "center",
|
||||||
alignItems: 'center',
|
alignItems: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
flexDirection: 'column',
|
flexDirection: "column",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
alignItems: 'center',
|
alignItems: "center",
|
||||||
justifyContent: 'flex-end',
|
justifyContent: "flex-end",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@@ -303,13 +303,13 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
color: theme.colors[`textHighContrast${leagueTheme}`],
|
color: theme.colors[`textHighContrast${leagueTheme}`],
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
maxWidth: 136,
|
maxWidth: 136,
|
||||||
textAlign: 'right',
|
textAlign: "right",
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: "nowrap",
|
||||||
textOverflow: 'ellipsis',
|
textOverflow: "ellipsis",
|
||||||
overflow: 'hidden',
|
overflow: "hidden",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{reward.type === 'vehicle'
|
{reward.type === "vehicle"
|
||||||
? reward.vehicle.user_string
|
? reward.vehicle.user_string
|
||||||
: reward.stuff.title}
|
: reward.stuff.title}
|
||||||
</span>
|
</span>
|
||||||
@@ -317,9 +317,9 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
alignItems: 'center',
|
alignItems: "center",
|
||||||
justifyContent: 'flex-end',
|
justifyContent: "flex-end",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@@ -329,7 +329,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Reward
|
Reward
|
||||||
{reward.type === 'stuff' ? ` x${reward.count}` : ''}
|
{reward.type === "stuff" ? ` x${reward.count}` : ""}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -338,7 +338,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
style={{
|
style={{
|
||||||
width: 64,
|
width: 64,
|
||||||
height: 64,
|
height: 64,
|
||||||
objectFit: 'cover',
|
objectFit: "cover",
|
||||||
}}
|
}}
|
||||||
src={await iconPng(rewardImage!)}
|
src={await iconPng(rewardImage!)}
|
||||||
/>
|
/>
|
||||||
@@ -350,7 +350,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
endOpacity={3 / 4}
|
endOpacity={3 / 4}
|
||||||
color={theme.colors[`solidBackground${leagueTheme}`]}
|
color={theme.colors[`solidBackground${leagueTheme}`]}
|
||||||
style={{
|
style={{
|
||||||
transform: 'translateX(16px) rotate(-90deg)',
|
transform: "translateX(16px) rotate(-90deg)",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -362,10 +362,10 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
direction="reverse"
|
direction="reverse"
|
||||||
color={theme.colors[`solidBackground${leagueTheme}`]}
|
color={theme.colors[`solidBackground${leagueTheme}`]}
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: "absolute",
|
||||||
top: -40,
|
top: -40,
|
||||||
left: '50%',
|
left: "50%",
|
||||||
transform: 'translateX(-50%)',
|
transform: "translateX(-50%)",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -373,32 +373,32 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
<Glow
|
<Glow
|
||||||
color={theme.colors[`solidBackground${leagueTheme}`]}
|
color={theme.colors[`solidBackground${leagueTheme}`]}
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: "absolute",
|
||||||
bottom: -40,
|
bottom: -40,
|
||||||
left: '50%',
|
left: "50%",
|
||||||
transform: 'translateX(-50%)',
|
transform: "translateX(-50%)",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{delta.battles > 0 && (
|
{delta.battles > 0 && (
|
||||||
<div style={{ padding: '8px 0', display: 'flex' }}>
|
<div style={{ padding: "8px 0", display: "flex" }}>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
alignItems: 'center',
|
alignItems: "center",
|
||||||
justifyContent: 'center',
|
justifyContent: "center",
|
||||||
gap: 4,
|
gap: 4,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
flexDirection: 'column',
|
flexDirection: "column",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
style={{
|
style={{
|
||||||
fontSize: 24,
|
fontSize: 24,
|
||||||
color: theme.colors.textHighContrast,
|
color: theme.colors.textHighContrast,
|
||||||
fontWeight: 'bold',
|
fontWeight: "bold",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{delta.battles.toLocaleString(interaction.locale)}
|
{delta.battles.toLocaleString(interaction.locale)}
|
||||||
@@ -414,19 +414,19 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
alignItems: 'center',
|
alignItems: "center",
|
||||||
justifyContent: 'center',
|
justifyContent: "center",
|
||||||
gap: 4,
|
gap: 4,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
flexDirection: 'column',
|
flexDirection: "column",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
style={{
|
style={{
|
||||||
fontSize: 24,
|
fontSize: 24,
|
||||||
color: theme.colors.textHighContrast,
|
color: theme.colors.textHighContrast,
|
||||||
fontWeight: 'bold',
|
fontWeight: "bold",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{(100 * (delta.wins / delta.battles)).toFixed(0)}%
|
{(100 * (delta.wins / delta.battles)).toFixed(0)}%
|
||||||
@@ -442,23 +442,23 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
alignItems: 'center',
|
alignItems: "center",
|
||||||
justifyContent: 'center',
|
justifyContent: "center",
|
||||||
gap: 4,
|
gap: 4,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
flexDirection: 'column',
|
flexDirection: "column",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
style={{
|
style={{
|
||||||
fontSize: 24,
|
fontSize: 24,
|
||||||
color: theme.colors.textHighContrast,
|
color: theme.colors.textHighContrast,
|
||||||
fontWeight: 'bold',
|
fontWeight: "bold",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{Math.round(delta.damage / delta.battles).toLocaleString(
|
{Math.round(delta.damage / delta.battles).toLocaleString(
|
||||||
interaction.locale,
|
interaction.locale
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
@@ -472,19 +472,19 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
alignItems: 'center',
|
alignItems: "center",
|
||||||
justifyContent: 'center',
|
justifyContent: "center",
|
||||||
gap: 4,
|
gap: 4,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
flexDirection: 'column',
|
flexDirection: "column",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
style={{
|
style={{
|
||||||
fontSize: 24,
|
fontSize: 24,
|
||||||
color: theme.colors.textHighContrast,
|
color: theme.colors.textHighContrast,
|
||||||
fontWeight: 'bold',
|
fontWeight: "bold",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{(100 * (delta.survived / delta.battles)).toFixed(0)}%
|
{(100 * (delta.survived / delta.battles)).toFixed(0)}%
|
||||||
@@ -500,19 +500,19 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
alignItems: 'center',
|
alignItems: "center",
|
||||||
justifyContent: 'center',
|
justifyContent: "center",
|
||||||
gap: 4,
|
gap: 4,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
flexDirection: 'column',
|
flexDirection: "column",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
style={{
|
style={{
|
||||||
fontSize: 24,
|
fontSize: 24,
|
||||||
color: theme.colors.textHighContrast,
|
color: theme.colors.textHighContrast,
|
||||||
fontWeight: 'bold',
|
fontWeight: "bold",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{(delta.kills / delta.battles).toFixed(2)}
|
{(delta.kills / delta.battles).toFixed(2)}
|
||||||
|
|||||||
@@ -13,27 +13,27 @@ import {
|
|||||||
RatingInfo,
|
RatingInfo,
|
||||||
RatingLeaderboard,
|
RatingLeaderboard,
|
||||||
Region,
|
Region,
|
||||||
} from '@blitzkit/core';
|
} from "@blitzkit/core";
|
||||||
import { literals } from '@blitzkit/i18n';
|
import { literals } from "@blitzkit/i18n";
|
||||||
import {
|
import {
|
||||||
APIApplicationCommandOptionChoice,
|
APIApplicationCommandOptionChoice,
|
||||||
Locale,
|
Locale,
|
||||||
SlashCommandSubcommandBuilder,
|
SlashCommandSubcommandBuilder,
|
||||||
} from 'discord.js';
|
} from "discord.js";
|
||||||
import { range } from 'lodash-es';
|
import { range } from "lodash-es";
|
||||||
import markdownEscape from 'markdown-escape';
|
import markdownEscape from "markdown-escape";
|
||||||
import { CommandWrapper } from '../components/CommandWrapper';
|
import { CommandWrapper } from "../components/CommandWrapper";
|
||||||
import * as Leaderboard from '../components/Leaderboard';
|
import * as Leaderboard from "../components/Leaderboard";
|
||||||
import { TitleBar } from '../components/TitleBar';
|
import { TitleBar } from "../components/TitleBar";
|
||||||
import { UserError } from '../core/blitzkit/userError';
|
import { UserError } from "../core/blitzkit/userError";
|
||||||
import { addRegionChoices } from '../core/discord/addRegionChoices';
|
import { addRegionChoices } from "../core/discord/addRegionChoices";
|
||||||
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
|
import { addUsernameChoices } from "../core/discord/addUsernameChoices";
|
||||||
import { autocompleteUsername } from '../core/discord/autocompleteUsername';
|
import { autocompleteUsername } from "../core/discord/autocompleteUsername";
|
||||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||||
import { localizationObject } from '../core/discord/localizationObject';
|
import { localizationObject } from "../core/discord/localizationObject";
|
||||||
import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand';
|
import { resolvePlayerFromCommand } from "../core/discord/resolvePlayerFromCommand";
|
||||||
import { translator } from '../core/localization/translator';
|
import { translator } from "../core/localization/translator";
|
||||||
import { CommandRegistry } from '../events/interactionCreate';
|
import { CommandRegistry } from "../events/interactionCreate";
|
||||||
|
|
||||||
interface SimplifiedPlayer {
|
interface SimplifiedPlayer {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -51,7 +51,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
|||||||
const onGoingSeason = await isOnGoingRatingSeason();
|
const onGoingSeason = await isOnGoingRatingSeason();
|
||||||
const seasonNumbers = range(
|
const seasonNumbers = range(
|
||||||
FIRST_MINIMAL_ARCHIVED_RATING_SEASON,
|
FIRST_MINIMAL_ARCHIVED_RATING_SEASON,
|
||||||
latestArchivedSeasonNumber + (onGoingSeason ? 1 : 0) + 1,
|
latestArchivedSeasonNumber + (onGoingSeason ? 1 : 0) + 1
|
||||||
);
|
);
|
||||||
|
|
||||||
function addOptions(option: SlashCommandSubcommandBuilder) {
|
function addOptions(option: SlashCommandSubcommandBuilder) {
|
||||||
@@ -66,51 +66,50 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
|||||||
(strings) =>
|
(strings) =>
|
||||||
strings.bot.commands.rating_leaderboard.options.limit.name,
|
strings.bot.commands.rating_leaderboard.options.limit.name,
|
||||||
undefined,
|
undefined,
|
||||||
true,
|
true
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
.setDescription(
|
.setDescription(
|
||||||
literals(
|
literals(
|
||||||
strings.bot.commands.rating_leaderboard.options.limit
|
strings.bot.commands.rating_leaderboard.options.limit
|
||||||
.description,
|
.description,
|
||||||
[`${DEFAULT_LIMIT}`],
|
{ count: DEFAULT_LIMIT }
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
.setDescriptionLocalizations(
|
.setDescriptionLocalizations(
|
||||||
localizationObject(
|
localizationObject(
|
||||||
(strings) =>
|
(strings) =>
|
||||||
strings.bot.commands.rating_leaderboard.options.limit
|
strings.bot.commands.rating_leaderboard.options.limit
|
||||||
.description,
|
.description,
|
||||||
[`${DEFAULT_LIMIT}`],
|
{ count: DEFAULT_LIMIT }
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
.setRequired(false)
|
.setRequired(false)
|
||||||
.setMinValue(5)
|
.setMinValue(5)
|
||||||
.setMaxValue(30),
|
.setMaxValue(30)
|
||||||
)
|
)
|
||||||
.addStringOption((option) =>
|
.addStringOption((option) =>
|
||||||
option
|
option
|
||||||
.setName(
|
.setName(
|
||||||
strings.bot.commands.rating_leaderboard.options.season.name,
|
strings.bot.commands.rating_leaderboard.options.season.name
|
||||||
)
|
)
|
||||||
.setNameLocalizations(
|
.setNameLocalizations(
|
||||||
localizationObject(
|
localizationObject(
|
||||||
(strings) =>
|
(strings) =>
|
||||||
strings.bot.commands.rating_leaderboard.options.season.name,
|
strings.bot.commands.rating_leaderboard.options.season.name,
|
||||||
undefined,
|
undefined,
|
||||||
true,
|
true
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
.setDescription(
|
.setDescription(
|
||||||
strings.bot.commands.rating_leaderboard.options.season
|
strings.bot.commands.rating_leaderboard.options.season.description
|
||||||
.description,
|
|
||||||
)
|
)
|
||||||
.setDescriptionLocalizations(
|
.setDescriptionLocalizations(
|
||||||
localizationObject(
|
localizationObject(
|
||||||
(strings) =>
|
(strings) =>
|
||||||
strings.bot.commands.rating_leaderboard.options.season
|
strings.bot.commands.rating_leaderboard.options.season
|
||||||
.description,
|
.description
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
.setRequired(false)
|
.setRequired(false)
|
||||||
.addChoices(
|
.addChoices(
|
||||||
@@ -124,7 +123,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
|||||||
? literals(
|
? literals(
|
||||||
strings.bot.commands.rating_leaderboard.options.season
|
strings.bot.commands.rating_leaderboard.options.season
|
||||||
.choices.current,
|
.choices.current,
|
||||||
[`${number}`],
|
{ season: number }
|
||||||
)
|
)
|
||||||
: `${number}`,
|
: `${number}`,
|
||||||
value: `${number}`,
|
value: `${number}`,
|
||||||
@@ -133,27 +132,27 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
|||||||
(strings) =>
|
(strings) =>
|
||||||
strings.bot.commands.rating_leaderboard.options
|
strings.bot.commands.rating_leaderboard.options
|
||||||
.season.choices.current,
|
.season.choices.current,
|
||||||
[`${number}`],
|
{ season: number }
|
||||||
)
|
)
|
||||||
: undefined,
|
: undefined,
|
||||||
} satisfies APIApplicationCommandOptionChoice<string>;
|
} satisfies APIApplicationCommandOptionChoice<string>;
|
||||||
})
|
})
|
||||||
.reverse(),
|
.reverse()
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve({
|
resolve({
|
||||||
command: createLocalizedCommand('rating-leaderboard', [
|
command: createLocalizedCommand("rating-leaderboard", [
|
||||||
{
|
{
|
||||||
subcommand: 'neighbors',
|
subcommand: "neighbors",
|
||||||
modify(option: SlashCommandSubcommandBuilder) {
|
modify(option: SlashCommandSubcommandBuilder) {
|
||||||
option.addStringOption(addUsernameChoices);
|
option.addStringOption(addUsernameChoices);
|
||||||
addOptions(option);
|
addOptions(option);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
subcommand: 'league',
|
subcommand: "league",
|
||||||
modify(option: SlashCommandSubcommandBuilder) {
|
modify(option: SlashCommandSubcommandBuilder) {
|
||||||
option
|
option
|
||||||
.addStringOption((option) => {
|
.addStringOption((option) => {
|
||||||
@@ -161,7 +160,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
|||||||
|
|
||||||
return option
|
return option
|
||||||
.setName(
|
.setName(
|
||||||
strings.bot.commands.rating_leaderboard.options.league.name,
|
strings.bot.commands.rating_leaderboard.options.league.name
|
||||||
)
|
)
|
||||||
.setNameLocalizations(
|
.setNameLocalizations(
|
||||||
localizationObject(
|
localizationObject(
|
||||||
@@ -169,19 +168,19 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
|||||||
strings.bot.commands.rating_leaderboard.options.league
|
strings.bot.commands.rating_leaderboard.options.league
|
||||||
.name,
|
.name,
|
||||||
undefined,
|
undefined,
|
||||||
true,
|
true
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
.setDescription(
|
.setDescription(
|
||||||
strings.bot.commands.rating_leaderboard.options.league
|
strings.bot.commands.rating_leaderboard.options.league
|
||||||
.description,
|
.description
|
||||||
)
|
)
|
||||||
.setDescriptionLocalizations(
|
.setDescriptionLocalizations(
|
||||||
localizationObject(
|
localizationObject(
|
||||||
(strings) =>
|
(strings) =>
|
||||||
strings.bot.commands.rating_leaderboard.options.league
|
strings.bot.commands.rating_leaderboard.options.league
|
||||||
.description,
|
.description
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
.addChoices(
|
.addChoices(
|
||||||
...LEAGUES.map(
|
...LEAGUES.map(
|
||||||
@@ -195,10 +194,10 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
|||||||
(strings) =>
|
(strings) =>
|
||||||
(
|
(
|
||||||
strings.common.leagues as Record<string, string>
|
strings.common.leagues as Record<string, string>
|
||||||
)[name],
|
)[name]
|
||||||
),
|
),
|
||||||
}) satisfies APIApplicationCommandOptionChoice<string>,
|
}) satisfies APIApplicationCommandOptionChoice<string>
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
.setRequired(true);
|
.setRequired(true);
|
||||||
})
|
})
|
||||||
@@ -213,10 +212,10 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
|||||||
async handler(interaction) {
|
async handler(interaction) {
|
||||||
const { strings } = translator(interaction.locale);
|
const { strings } = translator(interaction.locale);
|
||||||
const subcommand = interaction.options.getSubcommand(true) as
|
const subcommand = interaction.options.getSubcommand(true) as
|
||||||
| 'league'
|
| "league"
|
||||||
| 'neighbors';
|
| "neighbors";
|
||||||
const limit = interaction.options.getInteger('limit') ?? DEFAULT_LIMIT;
|
const limit = interaction.options.getInteger("limit") ?? DEFAULT_LIMIT;
|
||||||
const seasonOption = interaction.options.getString('season');
|
const seasonOption = interaction.options.getString("season");
|
||||||
const season = seasonOption
|
const season = seasonOption
|
||||||
? parseInt(seasonOption)
|
? parseInt(seasonOption)
|
||||||
: onGoingSeason
|
: onGoingSeason
|
||||||
@@ -232,11 +231,11 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
|||||||
let playerId: number | undefined;
|
let playerId: number | undefined;
|
||||||
let regionRatingInfo: RatingInfo | null;
|
let regionRatingInfo: RatingInfo | null;
|
||||||
|
|
||||||
if (subcommand === 'league') {
|
if (subcommand === "league") {
|
||||||
const leagueIndex = parseInt(
|
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
|
regionRatingInfo = await (isCurrentSeason
|
||||||
? getRatingInfo(region)
|
? getRatingInfo(region)
|
||||||
: null);
|
: null);
|
||||||
@@ -259,8 +258,8 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
|||||||
position: player.number,
|
position: player.number,
|
||||||
clan: player.clan_tag,
|
clan: player.clan_tag,
|
||||||
nickname: player.nickname,
|
nickname: player.nickname,
|
||||||
}) satisfies SimplifiedPlayer,
|
}) satisfies SimplifiedPlayer
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
: await getArchivedRatingLeaderboard(region, season!).then(
|
: await getArchivedRatingLeaderboard(region, season!).then(
|
||||||
async (players) => {
|
async (players) => {
|
||||||
@@ -269,17 +268,17 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
|||||||
? 0
|
? 0
|
||||||
: players.version!.value.entries.findIndex(
|
: players.version!.value.entries.findIndex(
|
||||||
(player) =>
|
(player) =>
|
||||||
player.score < LEAGUES[leagueIndex - 1].minScore,
|
player.score < LEAGUES[leagueIndex - 1].minScore
|
||||||
);
|
);
|
||||||
const lastPlayerIndex = firstPlayerIndex + limit - 1;
|
const lastPlayerIndex = firstPlayerIndex + limit - 1;
|
||||||
const trimmed = players.version!.value.entries.slice(
|
const trimmed = players.version!.value.entries.slice(
|
||||||
firstPlayerIndex,
|
firstPlayerIndex,
|
||||||
lastPlayerIndex + 1,
|
lastPlayerIndex + 1
|
||||||
);
|
);
|
||||||
const clanData = await getClanAccountInfo(
|
const clanData = await getClanAccountInfo(
|
||||||
region,
|
region,
|
||||||
trimmed.map(({ id }) => id),
|
trimmed.map(({ id }) => id),
|
||||||
['clan'],
|
["clan"]
|
||||||
);
|
);
|
||||||
|
|
||||||
return trimmed.map(
|
return trimmed.map(
|
||||||
@@ -294,16 +293,16 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
|||||||
literals(
|
literals(
|
||||||
strings.bot.commands.rating_leaderboard.body
|
strings.bot.commands.rating_leaderboard.body
|
||||||
.deleted_player,
|
.deleted_player,
|
||||||
[`${player.id}`],
|
{ id: player.id }
|
||||||
),
|
),
|
||||||
}) satisfies SimplifiedPlayer,
|
}) satisfies SimplifiedPlayer
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
const leagueInfo = LEAGUES[leagueIndex];
|
const leagueInfo = LEAGUES[leagueIndex];
|
||||||
archivedLeaderboard = await getArchivedRatingLeaderboard(
|
archivedLeaderboard = await getArchivedRatingLeaderboard(
|
||||||
region,
|
region,
|
||||||
season,
|
season
|
||||||
);
|
);
|
||||||
|
|
||||||
players = result;
|
players = result;
|
||||||
@@ -316,12 +315,12 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
|||||||
? (strings.common.leagues as Record<string, string>)[
|
? (strings.common.leagues as Record<string, string>)[
|
||||||
LEAGUES[leagueInfo.index].name
|
LEAGUES[leagueInfo.index].name
|
||||||
]
|
]
|
||||||
: ''
|
: ""
|
||||||
} - ${strings.common.regions.short[region]}`;
|
} - ${strings.common.regions.short[region]}`;
|
||||||
titleImage = imgur(leagueInfo.icon);
|
titleImage = imgur(leagueInfo.icon);
|
||||||
titleDescription = literals(
|
titleDescription = literals(
|
||||||
strings.bot.commands.rating_leaderboard.body.top_players,
|
strings.bot.commands.rating_leaderboard.body.top_players,
|
||||||
[`${limit}`],
|
{ count: limit }
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const { region, id } = await resolvePlayerFromCommand(interaction);
|
const { region, id } = await resolvePlayerFromCommand(interaction);
|
||||||
@@ -338,7 +337,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const accountInfo = await getAccountInfo(region, id);
|
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
|
const neighbors = isCurrentSeason
|
||||||
? await getRatingNeighbors(region, id, Math.round(limit / 2)).then(
|
? await getRatingNeighbors(region, id, Math.round(limit / 2)).then(
|
||||||
(players) =>
|
(players) =>
|
||||||
@@ -350,13 +349,13 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
|||||||
score: player.score,
|
score: player.score,
|
||||||
clan: player.clan_tag,
|
clan: player.clan_tag,
|
||||||
nickname: player.nickname,
|
nickname: player.nickname,
|
||||||
}) satisfies SimplifiedPlayer,
|
}) satisfies SimplifiedPlayer
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
: await getArchivedRatingLeaderboard(region, season!).then(
|
: await getArchivedRatingLeaderboard(region, season!).then(
|
||||||
async (players) => {
|
async (players) => {
|
||||||
const playerIndex = players.version!.value.entries.findIndex(
|
const playerIndex = players.version!.value.entries.findIndex(
|
||||||
(player) => player.id === id,
|
(player) => player.id === id
|
||||||
);
|
);
|
||||||
|
|
||||||
if (playerIndex === -1) {
|
if (playerIndex === -1) {
|
||||||
@@ -364,20 +363,20 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
|||||||
literals(
|
literals(
|
||||||
strings.bot.commands.rating_leaderboard.body
|
strings.bot.commands.rating_leaderboard.body
|
||||||
.no_participation,
|
.no_participation,
|
||||||
[markdownEscape(accountInfo.nickname), `${season}`],
|
{ player: markdownEscape(accountInfo.nickname), season }
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const halfRange = Math.round(limit / 2);
|
const halfRange = Math.round(limit / 2);
|
||||||
const trimmed = players.version!.value.entries.slice(
|
const trimmed = players.version!.value.entries.slice(
|
||||||
playerIndex - halfRange,
|
playerIndex - halfRange,
|
||||||
playerIndex + halfRange + 1,
|
playerIndex + halfRange + 1
|
||||||
);
|
);
|
||||||
const clanData = await getClanAccountInfo(
|
const clanData = await getClanAccountInfo(
|
||||||
region,
|
region,
|
||||||
trimmed.map((player) => player.id),
|
trimmed.map((player) => player.id),
|
||||||
['clan'],
|
["clan"]
|
||||||
);
|
);
|
||||||
|
|
||||||
return trimmed.map((player, index) => ({
|
return trimmed.map((player, index) => ({
|
||||||
@@ -389,18 +388,18 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
|||||||
? clanData[index]!.account_name
|
? clanData[index]!.account_name
|
||||||
: `Deleted ${player.id}`,
|
: `Deleted ${player.id}`,
|
||||||
}));
|
}));
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
archivedLeaderboard = await getArchivedRatingLeaderboard(
|
archivedLeaderboard = await getArchivedRatingLeaderboard(
|
||||||
region,
|
region,
|
||||||
season,
|
season
|
||||||
);
|
);
|
||||||
|
|
||||||
players = neighbors;
|
players = neighbors;
|
||||||
titleImage = clan
|
titleImage = clan
|
||||||
? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clan.emblem_set_id}.png`
|
? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clan.emblem_set_id}.png`
|
||||||
: undefined;
|
: undefined;
|
||||||
titleName = neighbors ? accountInfo.nickname : '';
|
titleName = neighbors ? accountInfo.nickname : "";
|
||||||
titleDescription =
|
titleDescription =
|
||||||
strings.bot.commands.rating_leaderboard.body.neighbors;
|
strings.bot.commands.rating_leaderboard.body.neighbors;
|
||||||
playersAfter =
|
playersAfter =
|
||||||
@@ -414,7 +413,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
|||||||
const items = players.map((player) => {
|
const items = players.map((player) => {
|
||||||
const midnightIndex =
|
const midnightIndex =
|
||||||
archivedLeaderboard?.version!.value.entries.findIndex(
|
archivedLeaderboard?.version!.value.entries.findIndex(
|
||||||
(item) => item.id === player.id,
|
(item) => item.id === player.id
|
||||||
);
|
);
|
||||||
const midnightScore =
|
const midnightScore =
|
||||||
midnightIndex === undefined
|
midnightIndex === undefined
|
||||||
@@ -453,7 +452,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
|
|||||||
<Leaderboard.Gap
|
<Leaderboard.Gap
|
||||||
message={literals(
|
message={literals(
|
||||||
strings.bot.commands.rating_leaderboard.body.more,
|
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,
|
autocomplete: autocompleteUsername,
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
@@ -8,31 +8,31 @@ import {
|
|||||||
TANK_ICONS,
|
TANK_ICONS,
|
||||||
TankType,
|
TankType,
|
||||||
TIER_ROMAN_NUMERALS,
|
TIER_ROMAN_NUMERALS,
|
||||||
} from '@blitzkit/core';
|
} from "@blitzkit/core";
|
||||||
import { literals } from '@blitzkit/i18n';
|
import { literals } from "@blitzkit/i18n";
|
||||||
import { escapeMarkdown, Locale } from 'discord.js';
|
import { escapeMarkdown, Locale } from "discord.js";
|
||||||
import { CommandWrapper } from '../components/CommandWrapper';
|
import { CommandWrapper } from "../components/CommandWrapper";
|
||||||
import { TitleBar } from '../components/TitleBar';
|
import { TitleBar } from "../components/TitleBar";
|
||||||
import { resolveTankId } from '../core/blitz/resolveTankId';
|
import { resolveTankId } from "../core/blitz/resolveTankId";
|
||||||
import { buildTechTreeLine } from '../core/blitzkit/buildTechTreeLine';
|
import { buildTechTreeLine } from "../core/blitzkit/buildTechTreeLine";
|
||||||
import { iconPng } from '../core/blitzkit/iconPng';
|
import { iconPng } from "../core/blitzkit/iconPng";
|
||||||
import { tankDefinitions } from '../core/blitzkit/nonBlockingPromises';
|
import { tankDefinitions } from "../core/blitzkit/nonBlockingPromises";
|
||||||
import { resolveAncestry } from '../core/blitzkit/resolveAncestry';
|
import { resolveAncestry } from "../core/blitzkit/resolveAncestry";
|
||||||
import { tankIconPng } from '../core/blitzkit/tankIconPng';
|
import { tankIconPng } from "../core/blitzkit/tankIconPng";
|
||||||
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
|
import { addUsernameChoices } from "../core/discord/addUsernameChoices";
|
||||||
import { autocompleteTanks } from '../core/discord/autocompleteTanks';
|
import { autocompleteTanks } from "../core/discord/autocompleteTanks";
|
||||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||||
import { localizationObject } from '../core/discord/localizationObject';
|
import { localizationObject } from "../core/discord/localizationObject";
|
||||||
import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand';
|
import { resolvePlayerFromCommand } from "../core/discord/resolvePlayerFromCommand";
|
||||||
import { translator } from '../core/localization/translator';
|
import { translator } from "../core/localization/translator";
|
||||||
import { CommandRegistry } from '../events/interactionCreate';
|
import { CommandRegistry } from "../events/interactionCreate";
|
||||||
import { theme } from '../stitches.config';
|
import { theme } from "../stitches.config";
|
||||||
|
|
||||||
export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
||||||
const { strings } = translator(Locale.EnglishUS);
|
const { strings } = translator(Locale.EnglishUS);
|
||||||
|
|
||||||
resolve({
|
resolve({
|
||||||
command: createLocalizedCommand('research')
|
command: createLocalizedCommand("research")
|
||||||
.addStringOption((option) =>
|
.addStringOption((option) =>
|
||||||
option
|
option
|
||||||
.setName(strings.bot.commands.research.options.target_tank.name)
|
.setName(strings.bot.commands.research.options.target_tank.name)
|
||||||
@@ -41,20 +41,20 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
(strings) =>
|
(strings) =>
|
||||||
strings.bot.commands.research.options.target_tank.name,
|
strings.bot.commands.research.options.target_tank.name,
|
||||||
undefined,
|
undefined,
|
||||||
true,
|
true
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
.setDescription(
|
.setDescription(
|
||||||
strings.bot.commands.research.options.target_tank.description,
|
strings.bot.commands.research.options.target_tank.description
|
||||||
)
|
)
|
||||||
.setDescriptionLocalizations(
|
.setDescriptionLocalizations(
|
||||||
localizationObject(
|
localizationObject(
|
||||||
(strings) =>
|
(strings) =>
|
||||||
strings.bot.commands.research.options.target_tank.description,
|
strings.bot.commands.research.options.target_tank.description
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
.setAutocomplete(true)
|
.setAutocomplete(true)
|
||||||
.setRequired(true),
|
.setRequired(true)
|
||||||
)
|
)
|
||||||
.addStringOption((option) =>
|
.addStringOption((option) =>
|
||||||
option
|
option
|
||||||
@@ -64,20 +64,20 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
(strings) =>
|
(strings) =>
|
||||||
strings.bot.commands.research.options.starting_tank.name,
|
strings.bot.commands.research.options.starting_tank.name,
|
||||||
undefined,
|
undefined,
|
||||||
true,
|
true
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
.setDescription(
|
.setDescription(
|
||||||
strings.bot.commands.research.options.starting_tank.description,
|
strings.bot.commands.research.options.starting_tank.description
|
||||||
)
|
)
|
||||||
.setDescriptionLocalizations(
|
.setDescriptionLocalizations(
|
||||||
localizationObject(
|
localizationObject(
|
||||||
(strings) =>
|
(strings) =>
|
||||||
strings.bot.commands.research.options.starting_tank.description,
|
strings.bot.commands.research.options.starting_tank.description
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
.setAutocomplete(true)
|
.setAutocomplete(true)
|
||||||
.setRequired(false),
|
.setRequired(false)
|
||||||
)
|
)
|
||||||
.addStringOption(addUsernameChoices),
|
.addStringOption(addUsernameChoices),
|
||||||
|
|
||||||
@@ -85,11 +85,11 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
const { strings, unwrap } = translator(interaction.locale);
|
const { strings, unwrap } = translator(interaction.locale);
|
||||||
const awaitedTankDefinitions = await tankDefinitions;
|
const awaitedTankDefinitions = await tankDefinitions;
|
||||||
const targetTankId = await resolveTankId(
|
const targetTankId = await resolveTankId(
|
||||||
interaction.options.getString('target-tank', true),
|
interaction.options.getString("target-tank", true),
|
||||||
interaction.locale,
|
interaction.locale,
|
||||||
true,
|
true
|
||||||
);
|
);
|
||||||
const startingTankRaw = interaction.options.getString('starting-tank');
|
const startingTankRaw = interaction.options.getString("starting-tank");
|
||||||
const targetTank = awaitedTankDefinitions.tanks[targetTankId];
|
const targetTank = awaitedTankDefinitions.tanks[targetTankId];
|
||||||
const { id, region } = await resolvePlayerFromCommand(interaction);
|
const { id, region } = await resolvePlayerFromCommand(interaction);
|
||||||
let startingTankId: number;
|
let startingTankId: number;
|
||||||
@@ -99,46 +99,45 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
startingTankId = await resolveTankId(
|
startingTankId = await resolveTankId(
|
||||||
startingTankRaw,
|
startingTankRaw,
|
||||||
interaction.locale,
|
interaction.locale,
|
||||||
true,
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
if (targetTankId === startingTankId) {
|
if (targetTankId === startingTankId) {
|
||||||
return literals(
|
return literals(
|
||||||
strings.bot.commands.research.errors.start_end_equal,
|
strings.bot.commands.research.errors.start_end_equal,
|
||||||
[escapeMarkdown(unwrap(targetTank.name))],
|
{ tank: escapeMarkdown(unwrap(targetTank.name)) }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const startingTank = awaitedTankDefinitions.tanks[startingTankId];
|
const startingTank = awaitedTankDefinitions.tanks[startingTankId];
|
||||||
|
|
||||||
if (startingTank.tier > targetTank.tier) {
|
if (startingTank.tier > targetTank.tier) {
|
||||||
return literals(strings.bot.commands.research.errors.unordered, [
|
return literals(strings.bot.commands.research.errors.unordered, {
|
||||||
escapeMarkdown(unwrap(startingTank.name)),
|
tank_1: escapeMarkdown(unwrap(startingTank.name)),
|
||||||
escapeMarkdown(unwrap(targetTank.name)),
|
tank_2: escapeMarkdown(unwrap(targetTank.name)),
|
||||||
escapeMarkdown(unwrap(targetTank.name)),
|
});
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!targetTankAncestry.includes(startingTankId)) {
|
if (!targetTankAncestry.includes(startingTankId)) {
|
||||||
return literals(
|
return literals(
|
||||||
strings.bot.commands.research.errors.tanks_not_on_line,
|
strings.bot.commands.research.errors.tanks_not_on_line,
|
||||||
[
|
{
|
||||||
escapeMarkdown(unwrap(targetTank.name)),
|
tank_1: escapeMarkdown(unwrap(targetTank.name)),
|
||||||
escapeMarkdown(unwrap(startingTank.name)),
|
tank_2: escapeMarkdown(unwrap(startingTank.name)),
|
||||||
],
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (targetTank.type !== TankType.RESEARCHABLE) {
|
if (targetTank.type !== TankType.RESEARCHABLE) {
|
||||||
return literals(strings.bot.commands.research.errors.non_tech_tree, [
|
return literals(strings.bot.commands.research.errors.non_tech_tree, {
|
||||||
escapeMarkdown(unwrap(targetTank.name)),
|
tank: escapeMarkdown(unwrap(targetTank.name)),
|
||||||
]);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targetTank.tier === 1) {
|
if (targetTank.tier === 1) {
|
||||||
return literals(strings.bot.commands.research.errors.tier_1, [
|
return literals(strings.bot.commands.research.errors.tier_1, {
|
||||||
escapeMarkdown(unwrap(targetTank.name)),
|
tank: escapeMarkdown(unwrap(targetTank.name)),
|
||||||
]);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const tankStats = await getTankStats(region, id);
|
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)) {
|
if (tankStats.some(({ tank_id }) => tank_id === targetTankId)) {
|
||||||
return literals(
|
return literals(
|
||||||
strings.bot.commands.research.errors.already_researched,
|
strings.bot.commands.research.errors.already_researched,
|
||||||
[escapeMarkdown(unwrap(targetTank.name))],
|
{ tank: escapeMarkdown(unwrap(targetTank.name)) }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const foundAncestor = targetTankAncestry.find(
|
const foundAncestor = targetTankAncestry.find(
|
||||||
(id) =>
|
(id) =>
|
||||||
awaitedTankDefinitions.tanks[id].tier === 1 ||
|
awaitedTankDefinitions.tanks[id].tier === 1 ||
|
||||||
tankStats.some(({ tank_id }) => tank_id === id),
|
tankStats.some(({ tank_id }) => tank_id === id)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (foundAncestor === undefined) {
|
if (foundAncestor === undefined) {
|
||||||
throw new Error('No first owned tech tree found.');
|
throw new Error("No first owned tech tree found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
startingTankId = foundAncestor;
|
startingTankId = foundAncestor;
|
||||||
@@ -169,12 +168,12 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
|
|
||||||
const techTreeLine = await buildTechTreeLine(
|
const techTreeLine = await buildTechTreeLine(
|
||||||
startingTankId,
|
startingTankId,
|
||||||
targetTankId,
|
targetTankId
|
||||||
);
|
);
|
||||||
|
|
||||||
const line = [...techTreeLine, startingTankId].reverse();
|
const line = [...techTreeLine, startingTankId].reverse();
|
||||||
const { nickname } = await getAccountInfo(region, id);
|
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 clanImage = clan ? emblemURL(clan.emblem_set_id) : undefined;
|
||||||
const costs = await Promise.all(
|
const costs = await Promise.all(
|
||||||
line.map(async (id) => {
|
line.map(async (id) => {
|
||||||
@@ -187,18 +186,18 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
tank.turrets.forEach((turret) => {
|
tank.turrets.forEach((turret) => {
|
||||||
if (
|
if (
|
||||||
turret.research_cost !== undefined &&
|
turret.research_cost !== undefined &&
|
||||||
typeof turret.research_cost.research_cost_type!.value === 'number'
|
typeof turret.research_cost.research_cost_type!.value === "number"
|
||||||
) {
|
) {
|
||||||
turretXps.set(
|
turretXps.set(
|
||||||
turret.id,
|
turret.id,
|
||||||
turret.research_cost.research_cost_type!.value,
|
turret.research_cost.research_cost_type!.value
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
turret.guns.forEach((gun) => {
|
turret.guns.forEach((gun) => {
|
||||||
if (
|
if (
|
||||||
gun.research_cost !== undefined &&
|
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);
|
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) => {
|
tank.engines.forEach((engine) => {
|
||||||
if (
|
if (
|
||||||
engine.research_cost !== undefined &&
|
engine.research_cost !== undefined &&
|
||||||
typeof engine.research_cost.research_cost_type!.value === 'number'
|
typeof engine.research_cost.research_cost_type!.value === "number"
|
||||||
) {
|
) {
|
||||||
engineXps.set(
|
engineXps.set(
|
||||||
engine.id,
|
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) => {
|
tank.tracks.forEach((track) => {
|
||||||
if (
|
if (
|
||||||
track.research_cost !== undefined &&
|
track.research_cost !== undefined &&
|
||||||
typeof track.research_cost.research_cost_type!.value === 'number'
|
typeof track.research_cost.research_cost_type!.value === "number"
|
||||||
) {
|
) {
|
||||||
trackXps.set(
|
trackXps.set(
|
||||||
track.id,
|
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,
|
purchase: tank.price.value,
|
||||||
equipment: equipmentPriceMatrix[tank.tier].reduce(
|
equipment: equipmentPriceMatrix[tank.tier].reduce(
|
||||||
(a, b) => a + 3 * b,
|
(a, b) => a + 3 * b,
|
||||||
0,
|
0
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const image = (
|
const image = (
|
||||||
@@ -267,14 +266,14 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
flexDirection: 'column',
|
flexDirection: "column",
|
||||||
gap: 8,
|
gap: 8,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
backgroundColor: theme.colors.appBackground2,
|
backgroundColor: theme.colors.appBackground2,
|
||||||
border: theme.borderStyles.subtle,
|
border: theme.borderStyles.subtle,
|
||||||
@@ -285,10 +284,10 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
alignItems: 'center',
|
alignItems: "center",
|
||||||
justifyContent: 'center',
|
justifyContent: "center",
|
||||||
position: 'relative',
|
position: "relative",
|
||||||
right: 24,
|
right: 24,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -304,23 +303,23 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
flexDirection: 'column',
|
flexDirection: "column",
|
||||||
justifyContent: 'center',
|
justifyContent: "center",
|
||||||
gap: 8,
|
gap: 8,
|
||||||
padding: '16px 0',
|
padding: "16px 0",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
gap: 4,
|
gap: 4,
|
||||||
alignItems: 'center',
|
alignItems: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt="XP icon"
|
alt="XP icon"
|
||||||
src={await iconPng(asset('icons/currencies/xp.webp'))}
|
src={await iconPng(asset("icons/currencies/xp.webp"))}
|
||||||
width={16}
|
width={16}
|
||||||
height={16}
|
height={16}
|
||||||
/>
|
/>
|
||||||
@@ -330,7 +329,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
color: theme.colors.textHighContrast,
|
color: theme.colors.textHighContrast,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Experience:{' '}
|
Experience:{" "}
|
||||||
{costs
|
{costs
|
||||||
.reduce((a, b) => a + (b.research ?? 0) + b.upgrades, 0)
|
.reduce((a, b) => a + (b.research ?? 0) + b.upgrades, 0)
|
||||||
.toLocaleString(interaction.locale)}
|
.toLocaleString(interaction.locale)}
|
||||||
@@ -339,14 +338,14 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
gap: 4,
|
gap: 4,
|
||||||
alignItems: 'center',
|
alignItems: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt="Silver icon"
|
alt="Silver icon"
|
||||||
src={await iconPng(asset('icons/currencies/silver.webp'))}
|
src={await iconPng(asset("icons/currencies/silver.webp"))}
|
||||||
width={16}
|
width={16}
|
||||||
height={16}
|
height={16}
|
||||||
/>
|
/>
|
||||||
@@ -356,7 +355,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
color: theme.colors.textHighContrast,
|
color: theme.colors.textHighContrast,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Purchase:{' '}
|
Purchase:{" "}
|
||||||
{costs
|
{costs
|
||||||
.reduce((a, b) => a + b.purchase, 0)
|
.reduce((a, b) => a + b.purchase, 0)
|
||||||
.toLocaleString(interaction.locale)}
|
.toLocaleString(interaction.locale)}
|
||||||
@@ -365,14 +364,14 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
gap: 4,
|
gap: 4,
|
||||||
alignItems: 'center',
|
alignItems: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt="Silver icon"
|
alt="Silver icon"
|
||||||
src={await iconPng(asset('icons/currencies/silver.webp'))}
|
src={await iconPng(asset("icons/currencies/silver.webp"))}
|
||||||
width={16}
|
width={16}
|
||||||
height={16}
|
height={16}
|
||||||
/>
|
/>
|
||||||
@@ -382,7 +381,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
color: theme.colors.textHighContrast,
|
color: theme.colors.textHighContrast,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Equipment:{' '}
|
Equipment:{" "}
|
||||||
{costs
|
{costs
|
||||||
.reduce((a, b) => a + b.equipment, 0)
|
.reduce((a, b) => a + b.equipment, 0)
|
||||||
.toLocaleString(interaction.locale)}
|
.toLocaleString(interaction.locale)}
|
||||||
@@ -399,7 +398,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
<div
|
<div
|
||||||
key={id}
|
key={id}
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
backgroundColor: theme.colors.appBackground2,
|
backgroundColor: theme.colors.appBackground2,
|
||||||
border: theme.borderStyles.subtle,
|
border: theme.borderStyles.subtle,
|
||||||
@@ -408,9 +407,9 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
width: 48,
|
width: 48,
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
alignItems: 'center',
|
alignItems: "center",
|
||||||
justifyContent: 'center',
|
justifyContent: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@@ -426,9 +425,9 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
position: 'relative',
|
position: "relative",
|
||||||
right: '5%',
|
right: "5%",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
@@ -437,23 +436,23 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
style={{
|
style={{
|
||||||
height: 100,
|
height: 100,
|
||||||
width: 100,
|
width: 100,
|
||||||
objectFit: 'contain',
|
objectFit: "contain",
|
||||||
position: 'absolute',
|
position: "absolute",
|
||||||
left: '25%',
|
left: "25%",
|
||||||
top: 0,
|
top: 0,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
gap: 4,
|
gap: 4,
|
||||||
alignItems: 'center',
|
alignItems: "center",
|
||||||
justifyContent: 'center',
|
justifyContent: "center",
|
||||||
position: 'absolute',
|
position: "absolute",
|
||||||
bottom: 8,
|
bottom: 8,
|
||||||
left: '50%',
|
left: "50%",
|
||||||
transform: 'translateX(-50%)',
|
transform: "translateX(-50%)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
@@ -476,23 +475,23 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
flexDirection: 'column',
|
flexDirection: "column",
|
||||||
justifyContent: 'center',
|
justifyContent: "center",
|
||||||
gap: 8,
|
gap: 8,
|
||||||
padding: '16px 0',
|
padding: "16px 0",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
gap: 4,
|
gap: 4,
|
||||||
alignItems: 'center',
|
alignItems: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt="XP icon"
|
alt="XP icon"
|
||||||
src={await iconPng(asset('icons/currencies/xp.webp'))}
|
src={await iconPng(asset("icons/currencies/xp.webp"))}
|
||||||
width={16}
|
width={16}
|
||||||
height={16}
|
height={16}
|
||||||
/>
|
/>
|
||||||
@@ -504,11 +503,11 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
>
|
>
|
||||||
{literals(
|
{literals(
|
||||||
strings.bot.commands.research.body.research,
|
strings.bot.commands.research.body.research,
|
||||||
[
|
{
|
||||||
`${costs[index].research?.toLocaleString(
|
cost: costs[index].research?.toLocaleString(
|
||||||
interaction.locale,
|
interaction.locale
|
||||||
)}`,
|
),
|
||||||
],
|
}
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -516,15 +515,15 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
{costs[index].upgrades > 0 && (
|
{costs[index].upgrades > 0 && (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
gap: 4,
|
gap: 4,
|
||||||
alignItems: 'center',
|
alignItems: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt="Free XP icon"
|
alt="Free XP icon"
|
||||||
src={await iconPng(
|
src={await iconPng(
|
||||||
asset('icons/currencies/free-xp.webp'),
|
asset("icons/currencies/free-xp.webp")
|
||||||
)}
|
)}
|
||||||
width={16}
|
width={16}
|
||||||
height={16}
|
height={16}
|
||||||
@@ -537,7 +536,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
>
|
>
|
||||||
{literals(
|
{literals(
|
||||||
strings.bot.commands.research.body.upgrades,
|
strings.bot.commands.research.body.upgrades,
|
||||||
[costs[index].upgrades.toLocaleString()],
|
{ cost: costs[index].upgrades.toLocaleString() }
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -545,15 +544,15 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
gap: 4,
|
gap: 4,
|
||||||
alignItems: 'center',
|
alignItems: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt="Silver icon"
|
alt="Silver icon"
|
||||||
src={await iconPng(
|
src={await iconPng(
|
||||||
asset('icons/currencies/silver.webp'),
|
asset("icons/currencies/silver.webp")
|
||||||
)}
|
)}
|
||||||
width={16}
|
width={16}
|
||||||
height={16}
|
height={16}
|
||||||
@@ -566,26 +565,26 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
>
|
>
|
||||||
{literals(
|
{literals(
|
||||||
strings.bot.commands.research.body.purchase,
|
strings.bot.commands.research.body.purchase,
|
||||||
[
|
{
|
||||||
costs[index].purchase.toLocaleString(
|
cost: costs[index].purchase.toLocaleString(
|
||||||
interaction.locale,
|
interaction.locale
|
||||||
),
|
),
|
||||||
],
|
}
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
gap: 4,
|
gap: 4,
|
||||||
alignItems: 'center',
|
alignItems: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt="Silver icon"
|
alt="Silver icon"
|
||||||
src={await iconPng(
|
src={await iconPng(
|
||||||
asset('icons/currencies/silver.webp'),
|
asset("icons/currencies/silver.webp")
|
||||||
)}
|
)}
|
||||||
width={16}
|
width={16}
|
||||||
height={16}
|
height={16}
|
||||||
@@ -598,18 +597,18 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
>
|
>
|
||||||
{literals(
|
{literals(
|
||||||
strings.bot.commands.research.body.equipment,
|
strings.bot.commands.research.body.equipment,
|
||||||
[
|
{
|
||||||
costs[index].equipment.toLocaleString(
|
cost: costs[index].equipment.toLocaleString(
|
||||||
interaction.locale,
|
interaction.locale
|
||||||
),
|
),
|
||||||
],
|
}
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}),
|
})
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</CommandWrapper>
|
</CommandWrapper>
|
||||||
@@ -621,7 +620,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
autocomplete(interaction) {
|
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 { ClanList, fetchBlitz, Region } from "@blitzkit/core";
|
||||||
import { literals } from '@blitzkit/i18n';
|
import { literals } from "@blitzkit/i18n";
|
||||||
import { Locale } from 'discord.js';
|
import { Locale } from "discord.js";
|
||||||
import markdownEscape from 'markdown-escape';
|
import markdownEscape from "markdown-escape";
|
||||||
import { addClanChoices } from '../core/discord/addClanChoices';
|
import { addClanChoices } from "../core/discord/addClanChoices";
|
||||||
import { addRegionChoices } from '../core/discord/addRegionChoices';
|
import { addRegionChoices } from "../core/discord/addRegionChoices";
|
||||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||||
import { embedInfo } from '../core/discord/embedInfo';
|
import { embedInfo } from "../core/discord/embedInfo";
|
||||||
import { localizationObject } from '../core/discord/localizationObject';
|
import { localizationObject } from "../core/discord/localizationObject";
|
||||||
import { translator } from '../core/localization/translator';
|
import { translator } from "../core/localization/translator";
|
||||||
import { CommandRegistry } from '../events/interactionCreate';
|
import { CommandRegistry } from "../events/interactionCreate";
|
||||||
|
|
||||||
const DEFAULT_LIMIT = 25;
|
const DEFAULT_LIMIT = 25;
|
||||||
|
|
||||||
export const searchClansCommand = new Promise<CommandRegistry>((resolve) => {
|
export const searchClansCommand = new Promise<CommandRegistry>((resolve) => {
|
||||||
resolve({
|
resolve({
|
||||||
command: createLocalizedCommand('search-clans')
|
command: createLocalizedCommand("search-clans")
|
||||||
.addStringOption(addRegionChoices)
|
.addStringOption(addRegionChoices)
|
||||||
.addStringOption((option) =>
|
.addStringOption((option) =>
|
||||||
addClanChoices(option).setAutocomplete(false),
|
addClanChoices(option).setAutocomplete(false)
|
||||||
)
|
)
|
||||||
.addIntegerOption((option) => {
|
.addIntegerOption((option) => {
|
||||||
const { strings } = translator(Locale.EnglishUS);
|
const { strings } = translator(Locale.EnglishUS);
|
||||||
@@ -28,21 +28,21 @@ export const searchClansCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
localizationObject(
|
localizationObject(
|
||||||
(strings) => strings.bot.commands.search_clans.options.limit.name,
|
(strings) => strings.bot.commands.search_clans.options.limit.name,
|
||||||
undefined,
|
undefined,
|
||||||
true,
|
true
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
.setDescription(
|
.setDescription(
|
||||||
literals(
|
literals(
|
||||||
strings.bot.commands.search_clans.options.limit.description,
|
strings.bot.commands.search_clans.options.limit.description,
|
||||||
[`${DEFAULT_LIMIT}`],
|
{ limit: DEFAULT_LIMIT }
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
.setDescriptionLocalizations(
|
.setDescriptionLocalizations(
|
||||||
localizationObject(
|
localizationObject(
|
||||||
(strings) =>
|
(strings) =>
|
||||||
strings.bot.commands.search_clans.options.limit.description,
|
strings.bot.commands.search_clans.options.limit.description,
|
||||||
[`${DEFAULT_LIMIT}`],
|
{ limit: DEFAULT_LIMIT }
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
.setMinValue(1)
|
.setMinValue(1)
|
||||||
.setMaxValue(100);
|
.setMaxValue(100);
|
||||||
@@ -50,24 +50,24 @@ export const searchClansCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
|
|
||||||
async handler(interaction) {
|
async handler(interaction) {
|
||||||
const { strings } = translator(interaction.locale);
|
const { strings } = translator(interaction.locale);
|
||||||
const region = interaction.options.getString('region') as Region;
|
const region = interaction.options.getString("region") as Region;
|
||||||
const clan = interaction.options.getString('clan')!;
|
const clan = interaction.options.getString("clan")!;
|
||||||
const limit = interaction.options.getInteger('limit') ?? DEFAULT_LIMIT;
|
const limit = interaction.options.getInteger("limit") ?? DEFAULT_LIMIT;
|
||||||
const clanList = await fetchBlitz<ClanList>(region, 'clans/list', {
|
const clanList = await fetchBlitz<ClanList>(region, "clans/list", {
|
||||||
search: clan,
|
search: clan,
|
||||||
limit,
|
limit,
|
||||||
});
|
});
|
||||||
|
|
||||||
return embedInfo(
|
return embedInfo(
|
||||||
literals(strings.bot.commands.search_clans.body.title, [
|
literals(strings.bot.commands.search_clans.body.title, {
|
||||||
markdownEscape(clan),
|
search: markdownEscape(clan),
|
||||||
strings.common.regions.normal[region],
|
region: strings.common.regions.normal[region],
|
||||||
]),
|
}),
|
||||||
clanList.length === 0
|
clanList.length === 0
|
||||||
? strings.bot.commands.search_clans.body.no_results
|
? strings.bot.commands.search_clans.body.no_results
|
||||||
: `\`\`\`\n${clanList
|
: `\`\`\`\n${clanList
|
||||||
.map((clan) => `${clan.name} [${clan.tag}]`)
|
.map((clan) => `${clan.name} [${clan.tag}]`)
|
||||||
.join('\n')}\n\`\`\``,
|
.join("\n")}\n\`\`\``
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,17 +3,17 @@ import {
|
|||||||
fetchBlitz,
|
fetchBlitz,
|
||||||
Region,
|
Region,
|
||||||
usernamePattern,
|
usernamePattern,
|
||||||
} from '@blitzkit/core';
|
} from "@blitzkit/core";
|
||||||
import { literals } from '@blitzkit/i18n';
|
import { literals } from "@blitzkit/i18n";
|
||||||
import { Locale } from 'discord.js';
|
import { Locale } from "discord.js";
|
||||||
import markdownEscape from 'markdown-escape';
|
import markdownEscape from "markdown-escape";
|
||||||
import { addRegionChoices } from '../core/discord/addRegionChoices';
|
import { addRegionChoices } from "../core/discord/addRegionChoices";
|
||||||
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
|
import { addUsernameChoices } from "../core/discord/addUsernameChoices";
|
||||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
|
||||||
import { embedInfo } from '../core/discord/embedInfo';
|
import { embedInfo } from "../core/discord/embedInfo";
|
||||||
import { localizationObject } from '../core/discord/localizationObject';
|
import { localizationObject } from "../core/discord/localizationObject";
|
||||||
import { translator } from '../core/localization/translator';
|
import { translator } from "../core/localization/translator";
|
||||||
import { CommandRegistry } from '../events/interactionCreate';
|
import { CommandRegistry } from "../events/interactionCreate";
|
||||||
|
|
||||||
const DEFAULT_LIMIT = 25;
|
const DEFAULT_LIMIT = 25;
|
||||||
|
|
||||||
@@ -21,10 +21,10 @@ export const searchPlayersCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
const { strings } = translator(Locale.EnglishUS);
|
const { strings } = translator(Locale.EnglishUS);
|
||||||
|
|
||||||
resolve({
|
resolve({
|
||||||
command: createLocalizedCommand('search-players')
|
command: createLocalizedCommand("search-players")
|
||||||
.addStringOption(addRegionChoices)
|
.addStringOption(addRegionChoices)
|
||||||
.addStringOption((option) =>
|
.addStringOption((option) =>
|
||||||
addUsernameChoices(option).setAutocomplete(false).setRequired(true),
|
addUsernameChoices(option).setAutocomplete(false).setRequired(true)
|
||||||
)
|
)
|
||||||
.addIntegerOption((option) =>
|
.addIntegerOption((option) =>
|
||||||
option
|
option
|
||||||
@@ -34,49 +34,49 @@ export const searchPlayersCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
(strings) =>
|
(strings) =>
|
||||||
strings.bot.commands.search_players.options.limit.name,
|
strings.bot.commands.search_players.options.limit.name,
|
||||||
undefined,
|
undefined,
|
||||||
true,
|
true
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
.setDescription(
|
.setDescription(
|
||||||
literals(
|
literals(
|
||||||
strings.bot.commands.search_players.options.limit.description,
|
strings.bot.commands.search_players.options.limit.description,
|
||||||
[`${DEFAULT_LIMIT}`],
|
{ limit: DEFAULT_LIMIT }
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
.setDescriptionLocalizations(
|
.setDescriptionLocalizations(
|
||||||
localizationObject(
|
localizationObject(
|
||||||
(strings) =>
|
(strings) =>
|
||||||
strings.bot.commands.search_players.options.limit.description,
|
strings.bot.commands.search_players.options.limit.description,
|
||||||
[`${DEFAULT_LIMIT}`],
|
{ limit: DEFAULT_LIMIT }
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
.setMinValue(1)
|
.setMinValue(1)
|
||||||
.setMaxValue(100),
|
.setMaxValue(100)
|
||||||
),
|
),
|
||||||
|
|
||||||
async handler(interaction) {
|
async handler(interaction) {
|
||||||
const { strings } = translator(interaction.locale);
|
const { strings } = translator(interaction.locale);
|
||||||
const region = interaction.options.getString('region') as Region;
|
const region = interaction.options.getString("region") as Region;
|
||||||
const name = interaction.options.getString('username')!;
|
const name = interaction.options.getString("username")!;
|
||||||
const limit = interaction.options.getInteger('limit') ?? 25;
|
const limit = interaction.options.getInteger("limit") ?? 25;
|
||||||
const trimmedSearch = name.trim();
|
const trimmedSearch = name.trim();
|
||||||
const players = usernamePattern.test(trimmedSearch)
|
const players = usernamePattern.test(trimmedSearch)
|
||||||
? await fetchBlitz<AccountList>(region, 'account/list', {
|
? await fetchBlitz<AccountList>(region, "account/list", {
|
||||||
search: trimmedSearch,
|
search: trimmedSearch,
|
||||||
limit,
|
limit,
|
||||||
})
|
})
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
return embedInfo(
|
return embedInfo(
|
||||||
literals(strings.bot.commands.search_players.body.title, [
|
literals(strings.bot.commands.search_players.body.title, {
|
||||||
markdownEscape(trimmedSearch),
|
search: markdownEscape(trimmedSearch),
|
||||||
strings.common.regions.normal[region],
|
region: strings.common.regions.normal[region],
|
||||||
]),
|
}),
|
||||||
`\`\`\`${
|
`\`\`\`${
|
||||||
players.length === 0
|
players.length === 0
|
||||||
? strings.bot.commands.search_players.body.no_results
|
? strings.bot.commands.search_players.body.no_results
|
||||||
: players.map((player) => player.nickname).join('\n')
|
: players.map((player) => player.nickname).join("\n")
|
||||||
}\`\`\``,
|
}\`\`\``
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,33 +1,33 @@
|
|||||||
import { SEARCH_KEYS } from '@blitzkit/core';
|
import { SEARCH_KEYS } from "@blitzkit/core";
|
||||||
import { literals } from '@blitzkit/i18n';
|
import { literals } from "@blitzkit/i18n";
|
||||||
import { Locale } from 'discord.js';
|
import { Locale } from "discord.js";
|
||||||
import { go } from 'fuzzysort';
|
import { go } from "fuzzysort";
|
||||||
import markdownEscape from 'markdown-escape';
|
import markdownEscape from "markdown-escape";
|
||||||
import { tankDefinitions, tankNames } from '../blitzkit/nonBlockingPromises';
|
import { tankDefinitions, tankNames } from "../blitzkit/nonBlockingPromises";
|
||||||
import { UserError } from '../blitzkit/userError';
|
import { UserError } from "../blitzkit/userError";
|
||||||
import { tankNamesTechTreeOnly } from '../discord/autocompleteTanks';
|
import { tankNamesTechTreeOnly } from "../discord/autocompleteTanks";
|
||||||
import { translator } from '../localization/translator';
|
import { translator } from "../localization/translator";
|
||||||
|
|
||||||
export async function resolveTankId(
|
export async function resolveTankId(
|
||||||
tank: string | number,
|
tank: string | number,
|
||||||
locale: Locale,
|
locale: Locale,
|
||||||
techTreeOnly = false,
|
techTreeOnly = false
|
||||||
) {
|
) {
|
||||||
const { strings } = translator(locale);
|
const { strings } = translator(locale);
|
||||||
const number = typeof tank === 'string' ? Number(tank) : tank;
|
const number = typeof tank === "string" ? Number(tank) : tank;
|
||||||
|
|
||||||
if (Number.isNaN(number)) {
|
if (Number.isNaN(number)) {
|
||||||
const searchResult = go(
|
const searchResult = go(
|
||||||
`${tank}`,
|
`${tank}`,
|
||||||
await (techTreeOnly ? tankNamesTechTreeOnly : tankNames),
|
await (techTreeOnly ? tankNamesTechTreeOnly : tankNames),
|
||||||
{ keys: SEARCH_KEYS, limit: 1 },
|
{ keys: SEARCH_KEYS, limit: 1 }
|
||||||
);
|
);
|
||||||
|
|
||||||
if (searchResult.length === 0) {
|
if (searchResult.length === 0) {
|
||||||
throw new UserError(
|
throw new UserError(
|
||||||
literals(strings.bot.common.errors.tank_name_not_found, [
|
literals(strings.bot.common.errors.tank_name_not_found, {
|
||||||
markdownEscape(`${tank}`),
|
search: markdownEscape(`${tank}`),
|
||||||
]),
|
})
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return searchResult[0].obj.id;
|
return searchResult[0].obj.id;
|
||||||
@@ -39,7 +39,9 @@ export async function resolveTankId(
|
|||||||
return number;
|
return number;
|
||||||
} else {
|
} else {
|
||||||
throw new UserError(
|
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 { fetchTankDefinitions, TIER_ROMAN_NUMERALS } from "@blitzkit/core";
|
||||||
import { literals } from '@blitzkit/i18n';
|
import { literals } from "@blitzkit/i18n";
|
||||||
import { Locale } from 'discord.js';
|
import { Locale } from "discord.js";
|
||||||
import { StatFilters } from '../blitzstars/filterStats';
|
import { StatFilters } from "../blitzstars/filterStats";
|
||||||
import { translator } from '../localization/translator';
|
import { translator } from "../localization/translator";
|
||||||
|
|
||||||
export async function filtersToDescription(
|
export async function filtersToDescription(
|
||||||
{ nation, tier, type, class: tankClass, tank }: StatFilters,
|
{ nation, tier, type, class: tankClass, tank }: StatFilters,
|
||||||
locale: Locale,
|
locale: Locale
|
||||||
) {
|
) {
|
||||||
const { strings, bkLocale } = translator(locale);
|
const { strings, bkLocale } = translator(locale);
|
||||||
const awaitedTankDefinitions = await fetchTankDefinitions();
|
const awaitedTankDefinitions = await fetchTankDefinitions();
|
||||||
@@ -18,11 +18,13 @@ export async function filtersToDescription(
|
|||||||
info.push((strings.common.nations as Record<string, string>)[nation]);
|
info.push((strings.common.nations as Record<string, string>)[nation]);
|
||||||
if (tier) {
|
if (tier) {
|
||||||
info.push(
|
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 (tankClass) info.push(strings.common.tank_class_short[tankClass]);
|
||||||
if (type) info.push(strings.common.tree_type[type]);
|
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,7 +1,7 @@
|
|||||||
import { jsxToSvg } from './jsxToSvg';
|
import { jsxToSvg } from "./jsxToSvg";
|
||||||
import { svgToPng } from './svgToPng';
|
import { svgToPng } from "./svgToPng";
|
||||||
|
|
||||||
export async function jsxToPng(jsx: JSX.Element) {
|
export async function jsxToPng(jsx: React.JSX.Element) {
|
||||||
const svg = await jsxToSvg(jsx);
|
const svg = await jsxToSvg(jsx);
|
||||||
const png = svgToPng(svg);
|
const png = svgToPng(svg);
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { jsxToSvg } from './jsxToSvg';
|
import { jsxToSvg } from "./jsxToSvg";
|
||||||
import { RenderConfiguration } from './renderConfiguration';
|
import { RenderConfiguration } from "./renderConfiguration";
|
||||||
import { svgToPngThreaded } from './svgToPngThreaded';
|
import { svgToPngThreaded } from "./svgToPngThreaded";
|
||||||
|
|
||||||
export async function jsxToPngThreaded(
|
export async function jsxToPngThreaded(
|
||||||
jsx: JSX.Element,
|
jsx: React.JSX.Element,
|
||||||
renderConfiguration: RenderConfiguration,
|
renderConfiguration: RenderConfiguration
|
||||||
) {
|
) {
|
||||||
const svg = await jsxToSvg(jsx, renderConfiguration);
|
const svg = await jsxToSvg(jsx, renderConfiguration);
|
||||||
const png = await svgToPngThreaded(svg);
|
const png = await svgToPngThreaded(svg);
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { readFile } from 'fs/promises';
|
import { readFile } from "fs/promises";
|
||||||
import satori from 'satori';
|
import satori from "satori";
|
||||||
import { RenderConfiguration } from '../renderConfiguration';
|
import { RenderConfiguration } from "../renderConfiguration";
|
||||||
import robotoBlack from './Roboto-Black.ttf';
|
import robotoBlack from "./Roboto-Black.ttf";
|
||||||
import robotoBold from './Roboto-Bold.ttf';
|
import robotoBold from "./Roboto-Bold.ttf";
|
||||||
import roboto from './Roboto.ttf';
|
import roboto from "./Roboto.ttf";
|
||||||
|
|
||||||
const fontsPromise = [
|
const fontsPromise = [
|
||||||
{ file: roboto, weight: 400 as const },
|
{ file: roboto, weight: 400 as const },
|
||||||
@@ -11,12 +11,12 @@ const fontsPromise = [
|
|||||||
{ file: robotoBlack, weight: 900 as const },
|
{ file: robotoBlack, weight: 900 as const },
|
||||||
].map(async ({ file, weight }) => {
|
].map(async ({ file, weight }) => {
|
||||||
const data = await readFile(`${__dirname}/${file}`);
|
const data = await readFile(`${__dirname}/${file}`);
|
||||||
return { name: 'Roboto', weight, data };
|
return { name: "Roboto", weight, data };
|
||||||
});
|
});
|
||||||
|
|
||||||
export async function jsxToSvg(
|
export async function jsxToSvg(
|
||||||
jsx: JSX.Element,
|
jsx: React.JSX.Element,
|
||||||
renderConfiguration = new RenderConfiguration(),
|
renderConfiguration = new RenderConfiguration()
|
||||||
) {
|
) {
|
||||||
const fonts = await Promise.all(fontsPromise);
|
const fonts = await Promise.all(fontsPromise);
|
||||||
return await satori(jsx, { width: renderConfiguration.width, fonts });
|
return await satori(jsx, { width: renderConfiguration.width, fonts });
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { numberToRomanNumeral } from '@blitzkit/core';
|
import { numberToRomanNumeral } from "@blitzkit/core";
|
||||||
import { literals } from '@blitzkit/i18n';
|
import { literals } from "@blitzkit/i18n";
|
||||||
import {
|
import {
|
||||||
APIApplicationCommandOptionChoice,
|
APIApplicationCommandOptionChoice,
|
||||||
Locale,
|
Locale,
|
||||||
SlashCommandStringOption,
|
SlashCommandStringOption,
|
||||||
} from 'discord.js';
|
} from "discord.js";
|
||||||
import { range } from 'lodash-es';
|
import { range } from "lodash-es";
|
||||||
import { translator } from '../localization/translator';
|
import { translator } from "../localization/translator";
|
||||||
import { localizationObject } from './localizationObject';
|
import { localizationObject } from "./localizationObject";
|
||||||
|
|
||||||
export function addTierChoices(option: SlashCommandStringOption) {
|
export function addTierChoices(option: SlashCommandStringOption) {
|
||||||
const { strings } = translator(Locale.EnglishUS);
|
const { strings } = translator(Locale.EnglishUS);
|
||||||
@@ -18,30 +18,30 @@ export function addTierChoices(option: SlashCommandStringOption) {
|
|||||||
localizationObject(
|
localizationObject(
|
||||||
(strings) => strings.bot.common.options.tier.name,
|
(strings) => strings.bot.common.options.tier.name,
|
||||||
undefined,
|
undefined,
|
||||||
true,
|
true
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
.setDescription(strings.bot.common.options.tier.description)
|
.setDescription(strings.bot.common.options.tier.description)
|
||||||
.setDescriptionLocalizations(
|
.setDescriptionLocalizations(
|
||||||
localizationObject(
|
localizationObject(
|
||||||
(strings) => strings.bot.common.options.tier.description,
|
(strings) => strings.bot.common.options.tier.description
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
.setChoices(
|
.setChoices(
|
||||||
...range(10, 0).map(
|
...range(10, 0).map(
|
||||||
(tier) =>
|
(tier) =>
|
||||||
({
|
({
|
||||||
name: literals(strings.bot.common.options.tier.choices.tier, [
|
name: literals(strings.bot.common.options.tier.choices.tier, {
|
||||||
`${tier}`,
|
tier_1: tier,
|
||||||
numberToRomanNumeral(tier),
|
tier_2: numberToRomanNumeral(tier),
|
||||||
]),
|
}),
|
||||||
value: `${tier}`,
|
value: `${tier}`,
|
||||||
name_localizations: localizationObject(
|
name_localizations: localizationObject(
|
||||||
(strings) => strings.bot.common.options.tier.choices.tier,
|
(strings) => strings.bot.common.options.tier.choices.tier,
|
||||||
[`${tier}`, numberToRomanNumeral(tier)],
|
{ tier_1: tier, tier_2: numberToRomanNumeral(tier) }
|
||||||
),
|
),
|
||||||
}) satisfies APIApplicationCommandOptionChoice,
|
}) satisfies APIApplicationCommandOptionChoice
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
.setRequired(true);
|
.setRequired(true);
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
import { literals } from '@blitzkit/i18n';
|
import { literals } from "@blitzkit/i18n";
|
||||||
import { Locale } from 'discord.js';
|
import { Locale } from "discord.js";
|
||||||
import { translator } from '../localization/translator';
|
import { translator } from "../localization/translator";
|
||||||
import { PeriodSize } from './addPeriodSubCommands';
|
import { PeriodSize } from "./addPeriodSubCommands";
|
||||||
|
|
||||||
export function getPeriodOptionName(period: PeriodSize, locale: Locale) {
|
export function getPeriodOptionName(period: PeriodSize, locale: Locale) {
|
||||||
const { strings } = translator(locale);
|
const { strings } = translator(locale);
|
||||||
if (period === 'career') return strings.bot.common.periods.career;
|
if (period === "career") return strings.bot.common.periods.career;
|
||||||
if (period === 'today') return strings.bot.common.periods.today;
|
if (period === "today") return strings.bot.common.periods.today;
|
||||||
return literals(strings.bot.common.periods.days, [period]);
|
return literals(strings.bot.common.periods.days, { days: period });
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
import { BlitzKitStrings, literals as literalsFunc } from '@blitzkit/i18n';
|
import { BlitzKitStrings, literals as literalsFunc } from "@blitzkit/i18n";
|
||||||
import { Locale } from 'discord.js';
|
import { Locale } from "discord.js";
|
||||||
import { SUPPORTED_LOCALES_DISCORD } from '../localization/strings/constants';
|
import { SUPPORTED_LOCALES_DISCORD } from "../localization/strings/constants";
|
||||||
import { translator } from '../localization/translator';
|
import { translator } from "../localization/translator";
|
||||||
|
|
||||||
const validNameRegex =
|
const validNameRegex =
|
||||||
/^[\p{Ll}\p{Lm}\p{Lo}\p{N}\p{sc=Devanagari}\p{sc=Thai}_-]+$/u;
|
/^[\p{Ll}\p{Lm}\p{Lo}\p{N}\p{sc=Devanagari}\p{sc=Thai}_-]+$/u;
|
||||||
|
|
||||||
export function localizationObject(
|
export function localizationObject(
|
||||||
path: (strings: BlitzKitStrings) => string,
|
path: (strings: BlitzKitStrings) => string,
|
||||||
literals: string[] = [],
|
literals?: Record<string, string | number>,
|
||||||
strict = false,
|
strict = false
|
||||||
) {
|
) {
|
||||||
return SUPPORTED_LOCALES_DISCORD.reduce<Partial<Record<Locale, string>>>(
|
return SUPPORTED_LOCALES_DISCORD.reduce<Partial<Record<Locale, string>>>(
|
||||||
(localizations, locale) => {
|
(localizations, locale) => {
|
||||||
@@ -20,7 +20,7 @@ export function localizationObject(
|
|||||||
|
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
console.warn(
|
console.warn(
|
||||||
`Invalid localization for ${locale} (${path}): ${translation}; skipping...`,
|
`Invalid localization for ${locale} (${path}): ${translation}; skipping...`
|
||||||
);
|
);
|
||||||
return localizations;
|
return localizations;
|
||||||
}
|
}
|
||||||
@@ -30,6 +30,6 @@ export function localizationObject(
|
|||||||
[locale]: translation,
|
[locale]: translation,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
{},
|
{}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -6,14 +6,14 @@ import {
|
|||||||
InteractionEditReplyOptions,
|
InteractionEditReplyOptions,
|
||||||
InteractionReplyOptions,
|
InteractionReplyOptions,
|
||||||
MessageEditOptions,
|
MessageEditOptions,
|
||||||
} from 'discord.js';
|
} from "discord.js";
|
||||||
import { InteractionReturnable } from '../../events/interactionCreate';
|
import { InteractionReturnable } from "../../events/interactionCreate";
|
||||||
import { Writeable } from '../../types/writable';
|
import { Writeable } from "../../types/writable";
|
||||||
import { jsxToPngThreaded } from '../blitzkit/jsxToPngThreaded';
|
import { jsxToPngThreaded } from "../blitzkit/jsxToPngThreaded";
|
||||||
import { RenderConfiguration } from '../blitzkit/renderConfiguration';
|
import { RenderConfiguration } from "../blitzkit/renderConfiguration";
|
||||||
|
|
||||||
export async function normalizeInteractionReturnable(
|
export async function normalizeInteractionReturnable(
|
||||||
returnable: InteractionReturnable,
|
returnable: InteractionReturnable
|
||||||
) {
|
) {
|
||||||
const images: [number, Buffer][] = [];
|
const images: [number, Buffer][] = [];
|
||||||
const reply: InteractionEditReplyOptions &
|
const reply: InteractionEditReplyOptions &
|
||||||
@@ -27,7 +27,7 @@ export async function normalizeInteractionReturnable(
|
|||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
normalizedReturnable.map(async (item, index) => {
|
normalizedReturnable.map(async (item, index) => {
|
||||||
if (typeof item === 'string') {
|
if (typeof item === "string") {
|
||||||
reply.content = item;
|
reply.content = item;
|
||||||
} else if (item instanceof EmbedBuilder) {
|
} else if (item instanceof EmbedBuilder) {
|
||||||
if (!reply.embeds) reply.embeds = [];
|
if (!reply.embeds) reply.embeds = [];
|
||||||
@@ -36,7 +36,7 @@ export async function normalizeInteractionReturnable(
|
|||||||
if (!reply.components)
|
if (!reply.components)
|
||||||
reply.components = [new ActionRowBuilder<ButtonBuilder>()];
|
reply.components = [new ActionRowBuilder<ButtonBuilder>()];
|
||||||
(reply.components[0] as ActionRowBuilder<ButtonBuilder>).addComponents(
|
(reply.components[0] as ActionRowBuilder<ButtonBuilder>).addComponents(
|
||||||
item,
|
item
|
||||||
);
|
);
|
||||||
} else if (item instanceof AttachmentBuilder) {
|
} else if (item instanceof AttachmentBuilder) {
|
||||||
if (!reply.files) reply.files = [];
|
if (!reply.files) reply.files = [];
|
||||||
@@ -49,14 +49,14 @@ export async function normalizeInteractionReturnable(
|
|||||||
const image = await jsxToPngThreaded(item, renderConfiguration);
|
const image = await jsxToPngThreaded(item, renderConfiguration);
|
||||||
images.push([index, image]);
|
images.push([index, image]);
|
||||||
}
|
}
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
if (images.length > 0) {
|
if (images.length > 0) {
|
||||||
images.sort(([a], [b]) => b - a);
|
images.sort(([a], [b]) => b - a);
|
||||||
if (!reply.files) reply.files = [];
|
if (!reply.files) reply.files = [];
|
||||||
images.forEach(([, image]) =>
|
images.forEach(([, image]) =>
|
||||||
(reply.files as Writeable<typeof reply.files>)?.push(image),
|
(reply.files as Writeable<typeof reply.files>)?.push(image)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,35 +1,35 @@
|
|||||||
import { Region } from '@blitzkit/core';
|
import { Region } from "@blitzkit/core";
|
||||||
import { literals } from '@blitzkit/i18n';
|
import { literals } from "@blitzkit/i18n";
|
||||||
import { ChatInputCommandInteraction } from 'discord.js';
|
import { ChatInputCommandInteraction } from "discord.js";
|
||||||
import markdownEscape from 'markdown-escape';
|
import markdownEscape from "markdown-escape";
|
||||||
import { searchClansAcrossRegionsBotWrapper } from '../blitz/searchClansAcrossRegionsBotWrapper';
|
import { searchClansAcrossRegionsBotWrapper } from "../blitz/searchClansAcrossRegionsBotWrapper";
|
||||||
import { UserError } from '../blitzkit/userError';
|
import { UserError } from "../blitzkit/userError";
|
||||||
import { translator } from '../localization/translator';
|
import { translator } from "../localization/translator";
|
||||||
|
|
||||||
export const serverAndIdPattern = /(com|eu|asia)\/[0-9]+/;
|
export const serverAndIdPattern = /(com|eu|asia)\/[0-9]+/;
|
||||||
|
|
||||||
export async function resolveClanFromCommand(
|
export async function resolveClanFromCommand(
|
||||||
interaction: ChatInputCommandInteraction,
|
interaction: ChatInputCommandInteraction
|
||||||
) {
|
) {
|
||||||
const { strings } = translator(interaction.locale);
|
const { strings } = translator(interaction.locale);
|
||||||
const clan = interaction.options.getString('clan', true);
|
const clan = interaction.options.getString("clan", true);
|
||||||
|
|
||||||
if (serverAndIdPattern.test(clan)) {
|
if (serverAndIdPattern.test(clan)) {
|
||||||
const [server, accountId] = clan.split('/');
|
const [server, accountId] = clan.split("/");
|
||||||
return { region: server as Region, id: Number(accountId) };
|
return { region: server as Region, id: Number(accountId) };
|
||||||
} else {
|
} else {
|
||||||
const accounts = await searchClansAcrossRegionsBotWrapper(
|
const accounts = await searchClansAcrossRegionsBotWrapper(
|
||||||
clan,
|
clan,
|
||||||
interaction.locale,
|
interaction.locale
|
||||||
);
|
);
|
||||||
|
|
||||||
if (accounts[0]) {
|
if (accounts[0]) {
|
||||||
return { region: accounts[0].region, id: accounts[0].clan_id };
|
return { region: accounts[0].region, id: accounts[0].clan_id };
|
||||||
} else {
|
} else {
|
||||||
throw new UserError(
|
throw new UserError(
|
||||||
literals(strings.bot.common.errors.clan_not_found, [
|
literals(strings.bot.common.errors.clan_not_found, {
|
||||||
markdownEscape(clan),
|
search: markdownEscape(clan),
|
||||||
]),
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { getTimeDaysAgo, Region } from '@blitzkit/core';
|
import { getTimeDaysAgo, Region } from "@blitzkit/core";
|
||||||
import { literals } from '@blitzkit/i18n';
|
import { literals } from "@blitzkit/i18n";
|
||||||
import { CacheType, ChatInputCommandInteraction } from 'discord.js';
|
import { CacheType, ChatInputCommandInteraction } from "discord.js";
|
||||||
import { getPeriodNow } from '../blitzkit/getPeriodNow';
|
import { getPeriodNow } from "../blitzkit/getPeriodNow";
|
||||||
import { getPeriodStart } from '../blitzkit/getPeriodStart';
|
import { getPeriodStart } from "../blitzkit/getPeriodStart";
|
||||||
import { translator } from '../localization/translator';
|
import { translator } from "../localization/translator";
|
||||||
import { PeriodType } from './addPeriodSubCommands';
|
import { PeriodType } from "./addPeriodSubCommands";
|
||||||
import { getPeriodOptionName } from './getPeriodOptionName';
|
import { getPeriodOptionName } from "./getPeriodOptionName";
|
||||||
|
|
||||||
export interface ResolvedPeriod {
|
export interface ResolvedPeriod {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -16,7 +16,7 @@ export interface ResolvedPeriod {
|
|||||||
export function resolvePeriodFromCommand(
|
export function resolvePeriodFromCommand(
|
||||||
region: Region,
|
region: Region,
|
||||||
interaction: ChatInputCommandInteraction<CacheType>,
|
interaction: ChatInputCommandInteraction<CacheType>,
|
||||||
forcedPeriod?: PeriodType,
|
forcedPeriod?: PeriodType
|
||||||
) {
|
) {
|
||||||
const { strings } = translator(interaction.locale);
|
const { strings } = translator(interaction.locale);
|
||||||
let name: string;
|
let name: string;
|
||||||
@@ -26,16 +26,16 @@ export function resolvePeriodFromCommand(
|
|||||||
? forcedPeriod
|
? forcedPeriod
|
||||||
: (interaction.options.getSubcommand() as PeriodType);
|
: (interaction.options.getSubcommand() as PeriodType);
|
||||||
|
|
||||||
if (periodSubcommand === 'custom') {
|
if (periodSubcommand === "custom") {
|
||||||
const startOption = interaction.options.getInteger('start', true);
|
const startOption = interaction.options.getInteger("start", true);
|
||||||
const endOption = interaction.options.getInteger('end', true);
|
const endOption = interaction.options.getInteger("end", true);
|
||||||
const startDaysAgoMin = Math.min(startOption, endOption);
|
const startDaysAgoMin = Math.min(startOption, endOption);
|
||||||
const endDaysAgoMax = Math.max(startOption, endOption);
|
const endDaysAgoMax = Math.max(startOption, endOption);
|
||||||
|
|
||||||
name = literals(strings.bot.common.periods.custom, [
|
name = literals(strings.bot.common.periods.custom, {
|
||||||
`${startDaysAgoMin}`,
|
day_1: startDaysAgoMin,
|
||||||
`${endDaysAgoMax}`,
|
day_2: endDaysAgoMax,
|
||||||
]);
|
});
|
||||||
start = getTimeDaysAgo(region, endDaysAgoMax);
|
start = getTimeDaysAgo(region, endDaysAgoMax);
|
||||||
end = getTimeDaysAgo(region, startDaysAgoMin);
|
end = getTimeDaysAgo(region, startDaysAgoMin);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { idToRegion, Region } from '@blitzkit/core';
|
import { idToRegion, Region } from "@blitzkit/core";
|
||||||
import { literals } from '@blitzkit/i18n';
|
import { literals } from "@blitzkit/i18n";
|
||||||
import { CacheType, ChatInputCommandInteraction } from 'discord.js';
|
import { CacheType, ChatInputCommandInteraction } from "discord.js";
|
||||||
import markdownEscape from 'markdown-escape';
|
import markdownEscape from "markdown-escape";
|
||||||
import { searchPlayersAcrossRegionsBotWrapper } from '../../blitz/searchPlayersAcrossRegionsBotWrapper';
|
import { searchPlayersAcrossRegionsBotWrapper } from "../../blitz/searchPlayersAcrossRegionsBotWrapper";
|
||||||
import { getBlitzFromDiscord } from '../../blitzkit/getBlitzFromDiscord';
|
import { getBlitzFromDiscord } from "../../blitzkit/getBlitzFromDiscord";
|
||||||
import { UserError } from '../../blitzkit/userError';
|
import { UserError } from "../../blitzkit/userError";
|
||||||
import { translator } from '../../localization/translator';
|
import { translator } from "../../localization/translator";
|
||||||
|
|
||||||
export interface ResolvedPlayer {
|
export interface ResolvedPlayer {
|
||||||
region: Region;
|
region: Region;
|
||||||
@@ -13,10 +13,10 @@ export interface ResolvedPlayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function resolvePlayerFromCommand(
|
export async function resolvePlayerFromCommand(
|
||||||
interaction: ChatInputCommandInteraction<CacheType>,
|
interaction: ChatInputCommandInteraction<CacheType>
|
||||||
) {
|
) {
|
||||||
const { strings } = translator(interaction.locale);
|
const { strings } = translator(interaction.locale);
|
||||||
const commandUsername = interaction.options.getString('username');
|
const commandUsername = interaction.options.getString("username");
|
||||||
|
|
||||||
if (commandUsername !== null) {
|
if (commandUsername !== null) {
|
||||||
const id = parseInt(commandUsername);
|
const id = parseInt(commandUsername);
|
||||||
@@ -27,7 +27,7 @@ export async function resolvePlayerFromCommand(
|
|||||||
} else {
|
} else {
|
||||||
const accounts = await searchPlayersAcrossRegionsBotWrapper(
|
const accounts = await searchPlayersAcrossRegionsBotWrapper(
|
||||||
commandUsername,
|
commandUsername,
|
||||||
interaction.locale,
|
interaction.locale
|
||||||
);
|
);
|
||||||
|
|
||||||
if (accounts[0]) {
|
if (accounts[0]) {
|
||||||
@@ -37,9 +37,9 @@ export async function resolvePlayerFromCommand(
|
|||||||
} satisfies ResolvedPlayer;
|
} satisfies ResolvedPlayer;
|
||||||
} else {
|
} else {
|
||||||
throw new UserError(
|
throw new UserError(
|
||||||
literals(strings.bot.common.errors.player_not_found, [
|
literals(strings.bot.common.errors.player_not_found, {
|
||||||
markdownEscape(commandUsername),
|
search: markdownEscape(commandUsername),
|
||||||
]),
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { DEFAULT_LOCALE, SupportedLocale } from '@blitzkit/i18n';
|
import locales from "@blitzkit/i18n/locales.json";
|
||||||
import { Locale } from 'discord.js';
|
import { Locale } from "discord.js";
|
||||||
|
|
||||||
const SUPPORTED_LOCALES_DISCORD_MAP: Record<SupportedLocale, Locale> = {
|
const SUPPORTED_LOCALES_DISCORD_MAP: Record<string, Locale> = {
|
||||||
en: Locale.EnglishUS,
|
en: Locale.EnglishUS,
|
||||||
es: Locale.SpanishES,
|
es: Locale.SpanishES,
|
||||||
pt: Locale.PortugueseBR,
|
pt: Locale.PortugueseBR,
|
||||||
@@ -14,12 +14,12 @@ const SUPPORTED_LOCALES_DISCORD_MAP: Record<SupportedLocale, Locale> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const SUPPORTED_LOCALES_DISCORD_MAP_INVERSE = Object.fromEntries(
|
export const SUPPORTED_LOCALES_DISCORD_MAP_INVERSE = Object.fromEntries(
|
||||||
Object.entries(SUPPORTED_LOCALES_DISCORD_MAP).map(([k, v]) => [v, k]),
|
Object.entries(SUPPORTED_LOCALES_DISCORD_MAP).map(([k, v]) => [v, k])
|
||||||
) as Record<Locale, SupportedLocale>;
|
) as Record<Locale, string>;
|
||||||
|
|
||||||
export const SUPPORTED_LOCALES_DISCORD = Object.values(
|
export const SUPPORTED_LOCALES_DISCORD = Object.values(
|
||||||
SUPPORTED_LOCALES_DISCORD_MAP,
|
SUPPORTED_LOCALES_DISCORD_MAP
|
||||||
);
|
);
|
||||||
|
|
||||||
export const DEFAULT_LOCALE_DISCORD =
|
export const DEFAULT_LOCALE_DISCORD =
|
||||||
SUPPORTED_LOCALES_DISCORD_MAP[DEFAULT_LOCALE];
|
SUPPORTED_LOCALES_DISCORD_MAP[locales.default];
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import { ReadStream } from '@blitzkit/core';
|
import { ReadStream } from "@blitzkit/core";
|
||||||
import AdmZip from 'adm-zip';
|
import AdmZip from "adm-zip";
|
||||||
import { readFile, writeFile } from 'fs/promises';
|
import { readFile } from "fs/promises";
|
||||||
import { Parser } from 'pickleparser';
|
import { Parser } from "pickleparser";
|
||||||
import protobuf from 'protobufjs';
|
import protobuf from "protobufjs";
|
||||||
|
|
||||||
interface WotbReplayMetaJson {
|
interface WotbReplayMetaJson {
|
||||||
version: string;
|
version: string;
|
||||||
@@ -24,26 +24,24 @@ export class WotbReplayReadStream extends ReadStream {
|
|||||||
async wotbReplay() {
|
async wotbReplay() {
|
||||||
const pickleParser = new Parser();
|
const pickleParser = new Parser();
|
||||||
const zip = new AdmZip(Buffer.from(this.buffer));
|
const zip = new AdmZip(Buffer.from(this.buffer));
|
||||||
const metaJsonBuffer = zip.getEntry('meta.json')?.getData();
|
const metaJsonBuffer = zip.getEntry("meta.json")?.getData();
|
||||||
const battleResultsDat = zip.getEntry('battle_results.dat')?.getData();
|
const battleResultsDat = zip.getEntry("battle_results.dat")?.getData();
|
||||||
|
|
||||||
if (!metaJsonBuffer || !battleResultsDat)
|
if (!metaJsonBuffer || !battleResultsDat)
|
||||||
throw new SyntaxError('meta.json not found');
|
throw new SyntaxError("meta.json not found");
|
||||||
|
|
||||||
const metaJson = JSON.parse(
|
const metaJson = JSON.parse(
|
||||||
metaJsonBuffer.toString(),
|
metaJsonBuffer.toString()
|
||||||
) as WotbReplayMetaJson;
|
) as WotbReplayMetaJson;
|
||||||
const unpickledBattleResults = pickleParser.parse(battleResultsDat);
|
const unpickledBattleResults = pickleParser.parse(battleResultsDat);
|
||||||
const [, battleResultsString] = unpickledBattleResults as [number, string];
|
const [, battleResultsString] = unpickledBattleResults as [number, string];
|
||||||
const root = await protobuf.load(
|
const root = await protobuf.load(
|
||||||
`${__dirname}/../../protos/BattleResultsClient.proto`,
|
`${__dirname}/../../protos/BattleResultsClient.proto`
|
||||||
);
|
);
|
||||||
const BattleResults = root.lookupType(
|
const BattleResults = root.lookupType(
|
||||||
'BattleResultsGenerated.BattleResultsClient',
|
"BattleResultsGenerated.BattleResultsClient"
|
||||||
);
|
);
|
||||||
|
|
||||||
writeFile('test.dat', Buffer.from(battleResultsString));
|
|
||||||
|
|
||||||
const reader = protobuf.Reader.create(Buffer.from(battleResultsString));
|
const reader = protobuf.Reader.create(Buffer.from(battleResultsString));
|
||||||
|
|
||||||
// const decodedBattleResults = BattleResults.decodeDelimited(
|
// const decodedBattleResults = BattleResults.decodeDelimited(
|
||||||
@@ -53,7 +51,7 @@ export class WotbReplayReadStream extends ReadStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const replay = await readFile('temp/test3.wotbreplay');
|
const replay = await readFile("temp/test3.wotbreplay");
|
||||||
const stream = new WotbReplayReadStream(replay.buffer as ArrayBuffer);
|
const stream = new WotbReplayReadStream(replay.buffer as ArrayBuffer);
|
||||||
|
|
||||||
stream.wotbReplay();
|
stream.wotbReplay();
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { assertSecret } from '@blitzkit/core';
|
import { assertSecret } from "@blitzkit/core";
|
||||||
import {
|
import {
|
||||||
AttachmentBuilder,
|
AttachmentBuilder,
|
||||||
AutocompleteInteraction,
|
AutocompleteInteraction,
|
||||||
@@ -13,30 +13,30 @@ import {
|
|||||||
SlashCommandBuilder,
|
SlashCommandBuilder,
|
||||||
SlashCommandOptionsOnlyBuilder,
|
SlashCommandOptionsOnlyBuilder,
|
||||||
SlashCommandSubcommandsOnlyBuilder,
|
SlashCommandSubcommandsOnlyBuilder,
|
||||||
} from 'discord.js';
|
} from "discord.js";
|
||||||
import { aboutCommand } from '../../commands/about';
|
import { aboutCommand } from "../../commands/about";
|
||||||
import { breakdownCommand } from '../../commands/breakdown';
|
import { breakdownCommand } from "../../commands/breakdown";
|
||||||
import { debugCommand } from '../../commands/debug';
|
import { debugCommand } from "../../commands/debug";
|
||||||
import { evolutionCommand } from '../../commands/evolution';
|
import { evolutionCommand } from "../../commands/evolution";
|
||||||
import { fullStatsCommand } from '../../commands/fullStats';
|
import { fullStatsCommand } from "../../commands/fullStats";
|
||||||
import { inactiveCommand } from '../../commands/inactive';
|
import { inactiveCommand } from "../../commands/inactive";
|
||||||
import { verifyCommand } from '../../commands/link';
|
import { verifyCommand } from "../../commands/link";
|
||||||
import { ownedTanksCommand } from '../../commands/ownedTanks';
|
import { ownedTanksCommand } from "../../commands/ownedTanks";
|
||||||
import { permissionsCommand } from '../../commands/permissions';
|
import { permissionsCommand } from "../../commands/permissions";
|
||||||
import { pingCommand } from '../../commands/ping';
|
import { pingCommand } from "../../commands/ping";
|
||||||
import { playerInfoCommand } from '../../commands/playerInfo';
|
import { playerInfoCommand } from "../../commands/playerInfo";
|
||||||
import { ratingCommand } from '../../commands/rating';
|
import { ratingCommand } from "../../commands/rating";
|
||||||
import { ratingLeaderboardCommand } from '../../commands/ratingLeaderboard';
|
import { ratingLeaderboardCommand } from "../../commands/ratingLeaderboard";
|
||||||
import { replayCommand } from '../../commands/replay';
|
import { replayCommand } from "../../commands/replay";
|
||||||
import { researchCommand } from '../../commands/research';
|
import { researchCommand } from "../../commands/research";
|
||||||
import { searchClansCommand } from '../../commands/searchClans';
|
import { searchClansCommand } from "../../commands/searchClans";
|
||||||
import { searchPlayersCommand } from '../../commands/searchPlayers';
|
import { searchPlayersCommand } from "../../commands/searchPlayers";
|
||||||
import { statsCommand } from '../../commands/stats';
|
import { statsCommand } from "../../commands/stats";
|
||||||
import { todayCommand } from '../../commands/today';
|
import { todayCommand } from "../../commands/today";
|
||||||
import { RenderConfiguration } from '../../core/blitzkit/renderConfiguration';
|
import { RenderConfiguration } from "../../core/blitzkit/renderConfiguration";
|
||||||
import { handleAutocomplete } from './handlers/autocomplete';
|
import { handleAutocomplete } from "./handlers/autocomplete";
|
||||||
import { handleButton } from './handlers/button';
|
import { handleButton } from "./handlers/button";
|
||||||
import { handleChatInputCommand } from './handlers/chatInputCommand';
|
import { handleChatInputCommand } from "./handlers/chatInputCommand";
|
||||||
|
|
||||||
export type InteractionRawReturnable =
|
export type InteractionRawReturnable =
|
||||||
| string
|
| string
|
||||||
@@ -44,7 +44,7 @@ export type InteractionRawReturnable =
|
|||||||
| ButtonBuilder
|
| ButtonBuilder
|
||||||
| AttachmentBuilder
|
| AttachmentBuilder
|
||||||
| RenderConfiguration
|
| RenderConfiguration
|
||||||
| JSX.Element
|
| React.JSX.Element
|
||||||
| null;
|
| null;
|
||||||
export type InteractionIterableReturnable =
|
export type InteractionIterableReturnable =
|
||||||
| InteractionRawReturnable
|
| InteractionRawReturnable
|
||||||
@@ -58,7 +58,7 @@ interface CommandRegistryBase {
|
|||||||
| SlashCommandBuilder
|
| SlashCommandBuilder
|
||||||
| SlashCommandSubcommandsOnlyBuilder
|
| SlashCommandSubcommandsOnlyBuilder
|
||||||
| SlashCommandOptionsOnlyBuilder
|
| SlashCommandOptionsOnlyBuilder
|
||||||
| Omit<SlashCommandBuilder, 'addSubcommand' | 'addSubcommandGroup'>;
|
| Omit<SlashCommandBuilder, "addSubcommand" | "addSubcommandGroup">;
|
||||||
|
|
||||||
autocomplete?: (interaction: AutocompleteInteraction<CacheType>) => void;
|
autocomplete?: (interaction: AutocompleteInteraction<CacheType>) => void;
|
||||||
button?: (interaction: ButtonInteraction<CacheType>) => InteractionReturnable;
|
button?: (interaction: ButtonInteraction<CacheType>) => InteractionReturnable;
|
||||||
@@ -72,7 +72,7 @@ interface CommandRegistryImplicit extends CommandRegistryBase {
|
|||||||
interface CommandRegistryExplicit extends CommandRegistryBase {
|
interface CommandRegistryExplicit extends CommandRegistryBase {
|
||||||
handlesInteraction?: false;
|
handlesInteraction?: false;
|
||||||
handler: (
|
handler: (
|
||||||
interaction: ChatInputCommandInteraction<CacheType>,
|
interaction: ChatInputCommandInteraction<CacheType>
|
||||||
) => InteractionReturnable;
|
) => InteractionReturnable;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,16 +104,16 @@ export const COMMANDS_RAW: Promise<CommandRegistry>[] = [
|
|||||||
export const commands = Promise.allSettled(COMMANDS_RAW).then((rawCommands) => {
|
export const commands = Promise.allSettled(COMMANDS_RAW).then((rawCommands) => {
|
||||||
return rawCommands.reduce<Record<string, CommandRegistry>>(
|
return rawCommands.reduce<Record<string, CommandRegistry>>(
|
||||||
(commands, registry, index) => {
|
(commands, registry, index) => {
|
||||||
if (registry.status === 'rejected') {
|
if (registry.status === "rejected") {
|
||||||
console.warn(
|
console.warn(
|
||||||
`Command ${index} failed to load; skipping...`,
|
`Command ${index} failed to load; skipping...`,
|
||||||
registry.reason,
|
registry.reason
|
||||||
);
|
);
|
||||||
return commands;
|
return commands;
|
||||||
}
|
}
|
||||||
return { ...commands, [registry.value.command.name]: registry.value };
|
return { ...commands, [registry.value.command.name]: registry.value };
|
||||||
},
|
},
|
||||||
{},
|
{}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -121,12 +121,12 @@ const rest = new REST().setToken(assertSecret(import.meta.env.DISCORD_TOKEN));
|
|||||||
|
|
||||||
commands.then((awaitedCommands) => {
|
commands.then((awaitedCommands) => {
|
||||||
const body = Object.values(awaitedCommands).map((registry) =>
|
const body = Object.values(awaitedCommands).map((registry) =>
|
||||||
registry.command.toJSON(),
|
registry.command.toJSON()
|
||||||
);
|
);
|
||||||
|
|
||||||
rest.put(
|
rest.put(
|
||||||
Routes.applicationCommands(assertSecret(import.meta.env.DISCORD_CLIENT_ID)),
|
Routes.applicationCommands(assertSecret(import.meta.env.DISCORD_CLIENT_ID)),
|
||||||
{ body },
|
{ body }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Submodule
+1
Submodule packages/closed added at 1caf47f66d.
+13
-11
@@ -4,20 +4,22 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@octokit/plugin-throttling": "^8.2.0",
|
"@blitzkit/i18n": "workspace:*",
|
||||||
"@octokit/rest": "^20.1.1",
|
"@bufbuild/protobuf": "^2.9.0",
|
||||||
"@prisma/client": "^6.6.0",
|
"@octokit/plugin-throttling": "^11.0.1",
|
||||||
|
"@octokit/rest": "^22.0.0",
|
||||||
|
"@prisma/client": "^6.16.2",
|
||||||
|
"@types/adm-zip": "^0.5.7",
|
||||||
|
"@types/lz4js": "^0.2.1",
|
||||||
|
"@types/three": "^0.180.0",
|
||||||
"adm-zip": "^0.5.16",
|
"adm-zip": "^0.5.16",
|
||||||
"crc": "^4.3.2",
|
"crc": "^4.3.2",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^17.2.2",
|
||||||
"lz4js": "^0.2.0",
|
"lz4js": "^0.2.0",
|
||||||
"pickleparser": "^0.2.1",
|
"pickleparser": "^0.2.1",
|
||||||
"protobufjs": "^7.4.0",
|
"protobufjs": "^7.5.4",
|
||||||
"ts-proto": "^2.2.2",
|
"three": "^0.180.0",
|
||||||
"yaml": "^2.5.0"
|
"ts-proto": "^2.7.7",
|
||||||
},
|
"yaml": "^2.8.1"
|
||||||
"devDependencies": {
|
|
||||||
"@types/adm-zip": "^0.5.5",
|
|
||||||
"@types/lz4js": "^0.2.1"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { type ShellDefinition, ShellType } from '@blitzkit/core';
|
import { ShellDefinition, ShellType } from "../protos";
|
||||||
|
|
||||||
export function canSplash(type: ShellDefinition['type']): type is ShellType.HE {
|
export function canSplash(type: ShellDefinition["type"]): type is ShellType.HE {
|
||||||
return type === ShellType.HE;
|
return type === ShellType.HE;
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { BlitzStats, calculateWN8 } from '@blitzkit/core';
|
import { BlitzStats, calculateWN8 } from "../statistics";
|
||||||
|
|
||||||
export type Stat = keyof ReturnType<typeof generateStats>;
|
export type Stat = keyof ReturnType<typeof generateStats>;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { fetchBlitz, Region } from '@blitzkit/core';
|
import { fetchBlitz } from "./fetchBlitz";
|
||||||
import { normalizeIds } from './normalizeIds';
|
import { normalizeIds } from "./normalizeIds";
|
||||||
|
import { Region } from "./regions";
|
||||||
|
|
||||||
interface IndividualAccountAchievements {
|
interface IndividualAccountAchievements {
|
||||||
achievements: {
|
achievements: {
|
||||||
@@ -22,11 +23,11 @@ export async function getAccountAchievements<
|
|||||||
>(region: Region, ids: Ids) {
|
>(region: Region, ids: Ids) {
|
||||||
const object = await fetchBlitz<AccountAchievements>(
|
const object = await fetchBlitz<AccountAchievements>(
|
||||||
region,
|
region,
|
||||||
'account/achievements',
|
"account/achievements",
|
||||||
{ account_id: normalizeIds(ids) },
|
{ account_id: normalizeIds(ids) }
|
||||||
);
|
);
|
||||||
|
|
||||||
if (typeof ids === 'number') {
|
if (typeof ids === "number") {
|
||||||
return object[ids as number] as ReturnType;
|
return object[ids as number] as ReturnType;
|
||||||
} else {
|
} else {
|
||||||
return ids.map((id) => object[id]) as ReturnType;
|
return ids.map((id) => object[id]) as ReturnType;
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
import {
|
import { BlitzStats } from "../statistics";
|
||||||
BlitzStats,
|
import { FetchBlitzParams, fetchBlitz } from "./fetchBlitz";
|
||||||
FetchBlitzParams,
|
import { normalizeIds } from "./normalizeIds";
|
||||||
Region,
|
import { Region } from "./regions";
|
||||||
fetchBlitz,
|
|
||||||
} from '@blitzkit/core';
|
|
||||||
import { normalizeIds } from './normalizeIds';
|
|
||||||
|
|
||||||
export interface SupplementaryStats {
|
export interface SupplementaryStats {
|
||||||
WN8?: number;
|
WN8?: number;
|
||||||
@@ -83,15 +80,15 @@ export async function getAccountInfo<
|
|||||||
region: Region,
|
region: Region,
|
||||||
ids: Ids,
|
ids: Ids,
|
||||||
extra: string[] = [],
|
extra: string[] = [],
|
||||||
params: FetchBlitzParams = {},
|
params: FetchBlitzParams = {}
|
||||||
) {
|
) {
|
||||||
const object = await fetchBlitz<AccountInfo>(region, 'account/info', {
|
const object = await fetchBlitz<AccountInfo>(region, "account/info", {
|
||||||
account_id: normalizeIds(ids),
|
account_id: normalizeIds(ids),
|
||||||
extra: extra.length === 0 ? undefined : extra.join(','),
|
extra: extra.length === 0 ? undefined : extra.join(","),
|
||||||
...params,
|
...params,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (typeof ids === 'number') {
|
if (typeof ids === "number") {
|
||||||
return object[ids as number] as ReturnType;
|
return object[ids as number] as ReturnType;
|
||||||
} else {
|
} else {
|
||||||
return ids.map((id) => object[id]) as ReturnType;
|
return ids.map((id) => object[id]) as ReturnType;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { fetchBlitz, Region } from '@blitzkit/core';
|
import { fetchBlitz } from "./fetchBlitz";
|
||||||
import { normalizeIds } from './normalizeIds';
|
import { normalizeIds } from "./normalizeIds";
|
||||||
|
import { Region } from "./regions";
|
||||||
|
|
||||||
type IndividualClanAccountInfo = {
|
type IndividualClanAccountInfo = {
|
||||||
account_id: number;
|
account_id: number;
|
||||||
@@ -30,14 +31,14 @@ export async function getClanAccountInfo<
|
|||||||
>(region: Region, ids: Ids, extra: string[] = []) {
|
>(region: Region, ids: Ids, extra: string[] = []) {
|
||||||
const object = await fetchBlitz<ClanAccountInfo>(
|
const object = await fetchBlitz<ClanAccountInfo>(
|
||||||
region,
|
region,
|
||||||
'clans/accountinfo',
|
"clans/accountinfo",
|
||||||
{
|
{
|
||||||
account_id: normalizeIds(ids),
|
account_id: normalizeIds(ids),
|
||||||
extra: extra.length === 0 ? undefined : extra.join(','),
|
extra: extra.length === 0 ? undefined : extra.join(","),
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (typeof ids === 'number') {
|
if (typeof ids === "number") {
|
||||||
return object[ids as number] as ReturnType;
|
return object[ids as number] as ReturnType;
|
||||||
} else {
|
} else {
|
||||||
return ids.map((id) => object[id]) as ReturnType;
|
return ids.map((id) => object[id]) as ReturnType;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { fetchBlitz, Region } from '@blitzkit/core';
|
import { fetchBlitz } from "./fetchBlitz";
|
||||||
import { normalizeIds } from './normalizeIds';
|
import { normalizeIds } from "./normalizeIds";
|
||||||
|
import { Region } from "./regions";
|
||||||
|
|
||||||
interface IndividualClanInfo {
|
interface IndividualClanInfo {
|
||||||
recruiting_options: {
|
recruiting_options: {
|
||||||
@@ -38,11 +39,11 @@ export async function getClanInfo<
|
|||||||
Ids extends number | number[],
|
Ids extends number | number[],
|
||||||
ReturnType = Ids extends number ? IndividualClanInfo : IndividualClanInfo[],
|
ReturnType = Ids extends number ? IndividualClanInfo : IndividualClanInfo[],
|
||||||
>(region: Region, ids: Ids) {
|
>(region: Region, ids: Ids) {
|
||||||
const object = await fetchBlitz<ClanInfo>(region, 'clans/info', {
|
const object = await fetchBlitz<ClanInfo>(region, "clans/info", {
|
||||||
clan_id: normalizeIds(ids),
|
clan_id: normalizeIds(ids),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (typeof ids === 'number') {
|
if (typeof ids === "number") {
|
||||||
return object[ids as number] as ReturnType;
|
return object[ids as number] as ReturnType;
|
||||||
} else {
|
} else {
|
||||||
return ids.map((id) => object[id]) as ReturnType;
|
return ids.map((id) => object[id]) as ReturnType;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { LEAGUES } from '@blitzkit/core';
|
import { LEAGUES } from "./rating";
|
||||||
|
|
||||||
export function getLeagueFromScore(score: number) {
|
export function getLeagueFromScore(score: number) {
|
||||||
return LEAGUES.find(({ minScore }) => score >= minScore)!;
|
return LEAGUES.find(({ minScore }) => score >= minScore)!;
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { RatingLeague, RatingReward, Region } from '@blitzkit/core';
|
import { context } from "../blitzkit/context";
|
||||||
import { context } from '../blitzkit/context';
|
import { patientFetch } from "../blitzkit/patientFetch";
|
||||||
import { patientFetch } from '../blitzkit/patientFetch';
|
import { RatingLeague, RatingReward } from "./rating";
|
||||||
import { regionToRegionSubdomain } from './regionToRegionSubdomain';
|
import { Region } from "./regions";
|
||||||
|
import { regionToRegionSubdomain } from "./regionToRegionSubdomain";
|
||||||
|
|
||||||
export type RatingInfo =
|
export type RatingInfo =
|
||||||
| {
|
| {
|
||||||
@@ -26,12 +27,12 @@ export async function getRatingInfo(region: Region) {
|
|||||||
if (cache[region]) return cache[region]!;
|
if (cache[region]) return cache[region]!;
|
||||||
|
|
||||||
const response = await patientFetch(
|
const response = await patientFetch(
|
||||||
context === 'website'
|
context === "website"
|
||||||
? `/api/${region}/rating/current/info`
|
? `/api/${region}/rating/current/info`
|
||||||
: `https://${regionToRegionSubdomain(
|
: `https://${regionToRegionSubdomain(
|
||||||
region,
|
region
|
||||||
)}.wotblitz.com/en/api/rating-leaderboards/season/`,
|
)}.wotblitz.com/en/api/rating-leaderboards/season/`,
|
||||||
undefined,
|
undefined
|
||||||
);
|
);
|
||||||
const data = (await response.json()) as RatingInfo;
|
const data = (await response.json()) as RatingInfo;
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,29 @@
|
|||||||
import { LeagueTop, RatingPlayer, Region } from '@blitzkit/core';
|
import { context } from "../blitzkit/context";
|
||||||
import { context } from '../blitzkit/context';
|
import { patientFetch } from "../blitzkit/patientFetch";
|
||||||
import { patientFetch } from '../blitzkit/patientFetch';
|
import { RatingPlayer } from "../statistics";
|
||||||
import { regionToRegionSubdomain } from './regionToRegionSubdomain';
|
import { LeagueTop } from "./rating";
|
||||||
|
import { Region } from "./regions";
|
||||||
|
import { regionToRegionSubdomain } from "./regionToRegionSubdomain";
|
||||||
|
|
||||||
export async function getRatingLeague(
|
export async function getRatingLeague(
|
||||||
region: Region,
|
region: Region,
|
||||||
league: number,
|
league: number,
|
||||||
usePatientFetcher = false,
|
usePatientFetcher = false
|
||||||
) {
|
) {
|
||||||
const fetcher = (url: string) =>
|
const fetcher = (url: string) =>
|
||||||
usePatientFetcher
|
usePatientFetcher
|
||||||
? patientFetch(url, undefined, { cache: 'no-store' })
|
? patientFetch(url, undefined, { cache: "no-store" })
|
||||||
: fetch(url, { cache: 'no-store' });
|
: fetch(url, { cache: "no-store" });
|
||||||
const response = await fetcher(
|
const response = await fetcher(
|
||||||
context === 'website'
|
context === "website"
|
||||||
? `/api/${region}/rating/current/league/${league}`
|
? `/api/${region}/rating/current/league/${league}`
|
||||||
: `https://${regionToRegionSubdomain(
|
: `https://${regionToRegionSubdomain(
|
||||||
region,
|
region
|
||||||
)}.wotblitz.com/en/api/rating-leaderboards/league/${league}/top/`,
|
)}.wotblitz.com/en/api/rating-leaderboards/league/${league}/top/`
|
||||||
);
|
);
|
||||||
const json = await response.json();
|
const json = await response.json();
|
||||||
|
|
||||||
return context === 'website'
|
return context === "website"
|
||||||
? (json as RatingPlayer[])
|
? (json as RatingPlayer[])
|
||||||
: (json as LeagueTop).result;
|
: (json as LeagueTop).result;
|
||||||
}
|
}
|
||||||
@@ -1,24 +1,25 @@
|
|||||||
import { RatingNeighbors, Region } from '@blitzkit/core';
|
import { patientFetch } from "../blitzkit/patientFetch";
|
||||||
import { patientFetch } from '../blitzkit/patientFetch';
|
import { withCORSProxy } from "../blitzkit/withCORSProxy";
|
||||||
import { withCORSProxy } from '../blitzkit/withCORSProxy';
|
import { RatingNeighbors } from "./rating";
|
||||||
import { regionToRegionSubdomain } from './regionToRegionSubdomain';
|
import { Region } from "./regions";
|
||||||
|
import { regionToRegionSubdomain } from "./regionToRegionSubdomain";
|
||||||
|
|
||||||
export async function getRatingNeighbors(
|
export async function getRatingNeighbors(
|
||||||
region: Region,
|
region: Region,
|
||||||
id: number,
|
id: number,
|
||||||
count: number,
|
count: number,
|
||||||
usePatientFetcher = false,
|
usePatientFetcher = false
|
||||||
) {
|
) {
|
||||||
const fetcher = (url: string) =>
|
const fetcher = (url: string) =>
|
||||||
usePatientFetcher
|
usePatientFetcher
|
||||||
? patientFetch(url, undefined, { cache: 'no-store' })
|
? patientFetch(url, undefined, { cache: "no-store" })
|
||||||
: fetch(url, { cache: 'no-store' });
|
: fetch(url, { cache: "no-store" });
|
||||||
const response = await fetcher(
|
const response = await fetcher(
|
||||||
withCORSProxy(
|
withCORSProxy(
|
||||||
`https://${regionToRegionSubdomain(
|
`https://${regionToRegionSubdomain(
|
||||||
region,
|
region
|
||||||
)}.wotblitz.com/en/api/rating-leaderboards/user/${id}/?neighbors=${count}`,
|
)}.wotblitz.com/en/api/rating-leaderboards/user/${id}/?neighbors=${count}`
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
const json = (await response.json()) as RatingNeighbors;
|
const json = (await response.json()) as RatingNeighbors;
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
import {
|
import { TanksStats } from "../types";
|
||||||
fetchBlitz,
|
import { FetchBlitzParams, fetchBlitz } from "./fetchBlitz";
|
||||||
FetchBlitzParams,
|
import { Region } from "./regions";
|
||||||
Region,
|
|
||||||
TanksStats,
|
|
||||||
} from '@blitzkit/core';
|
|
||||||
|
|
||||||
export async function getTankStats(
|
export async function getTankStats(
|
||||||
region: Region,
|
region: Region,
|
||||||
id: number,
|
id: number,
|
||||||
params?: FetchBlitzParams,
|
params?: FetchBlitzParams
|
||||||
) {
|
) {
|
||||||
const tankStats = await fetchBlitz<TanksStats>(region, 'tanks/stats', {
|
const tankStats = await fetchBlitz<TanksStats>(region, "tanks/stats", {
|
||||||
account_id: id,
|
account_id: id,
|
||||||
...params,
|
...params,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { getRatingInfo } from '@blitzkit/core';
|
import { getRatingInfo } from "./getRatingInfo";
|
||||||
|
|
||||||
export async function isOnGoingRatingSeason() {
|
export async function isOnGoingRatingSeason() {
|
||||||
const ratingInfo = await getRatingInfo('com');
|
const ratingInfo = await getRatingInfo("com");
|
||||||
return ratingInfo.detail === undefined;
|
return ratingInfo.detail === undefined;
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
import { Region, assertSecret, idToRegion } from '@blitzkit/core';
|
import { assertSecret } from "../blitzkit";
|
||||||
|
import { idToRegion } from "./idToRegion";
|
||||||
|
import { Region } from "./regions";
|
||||||
|
|
||||||
export async function isValidBlitzId(id: number, token?: string) {
|
export async function isValidBlitzId(id: number, token?: string) {
|
||||||
let region: Region;
|
let region: Region;
|
||||||
@@ -11,20 +13,20 @@ export async function isValidBlitzId(id: number, token?: string) {
|
|||||||
|
|
||||||
const json = await fetch(
|
const json = await fetch(
|
||||||
`https://api.wotblitz.${region}/wotb/account/info/?account_id=${id}&application_id=${assertSecret(
|
`https://api.wotblitz.${region}/wotb/account/info/?account_id=${id}&application_id=${assertSecret(
|
||||||
import.meta.env.PUBLIC_WARGAMING_APPLICATION_ID,
|
import.meta.env.PUBLIC_WARGAMING_APPLICATION_ID
|
||||||
)}${
|
)}${
|
||||||
token === undefined ? '' : `&access_token=${token}`
|
token === undefined ? "" : `&access_token=${token}`
|
||||||
}&fields=account_id%2C-account_id`,
|
}&fields=account_id%2C-account_id`
|
||||||
).then(
|
).then(
|
||||||
(response) =>
|
(response) =>
|
||||||
response.json() as Promise<
|
response.json() as Promise<
|
||||||
| {
|
| {
|
||||||
status: 'ok';
|
status: "ok";
|
||||||
data: Record<number, {} | null>;
|
data: Record<number, {} | null>;
|
||||||
}
|
}
|
||||||
| { status: 'error' }
|
| { status: "error" }
|
||||||
>,
|
>
|
||||||
);
|
);
|
||||||
|
|
||||||
return json.status === 'ok' && json.data[id] !== null;
|
return json.status === "ok" && json.data[id] !== null;
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Region, RegionSubdomain } from '@blitzkit/core';
|
import { Region, RegionSubdomain } from "./regions";
|
||||||
|
|
||||||
export function regionSubdomainToRegion(regionDomain: RegionSubdomain): Region {
|
export function regionSubdomainToRegion(regionDomain: RegionSubdomain): Region {
|
||||||
return regionDomain === 'na' ? 'com' : regionDomain;
|
return regionDomain === "na" ? "com" : regionDomain;
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Region, RegionSubdomain } from '@blitzkit/core';
|
import { Region, RegionSubdomain } from "./regions";
|
||||||
|
|
||||||
export function regionToRegionSubdomain(regionDomain: Region): RegionSubdomain {
|
export function regionToRegionSubdomain(regionDomain: Region): RegionSubdomain {
|
||||||
return regionDomain === 'com' ? 'na' : regionDomain;
|
return regionDomain === "com" ? "na" : regionDomain;
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { fetchBlitz, Region } from '@blitzkit/core';
|
import { ClanList } from "./clans";
|
||||||
import { ClanList } from './clans';
|
import { fetchBlitz } from "./fetchBlitz";
|
||||||
|
import { Region } from "./regions";
|
||||||
|
|
||||||
export async function searchClansAcrossRegions(search: string, limit = 25) {
|
export async function searchClansAcrossRegions(search: string, limit = 25) {
|
||||||
const trimmed = search.trim();
|
const trimmed = search.trim();
|
||||||
@@ -10,38 +11,38 @@ export async function searchClansAcrossRegions(search: string, limit = 25) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
fetchBlitz<ClanList>('com', 'clans/list', {
|
fetchBlitz<ClanList>("com", "clans/list", {
|
||||||
search,
|
search,
|
||||||
limit: normalizedLimit,
|
limit: normalizedLimit,
|
||||||
}).then((value) =>
|
}).then((value) =>
|
||||||
value
|
value
|
||||||
.map((account) => ({
|
.map((account) => ({
|
||||||
...account,
|
...account,
|
||||||
region: 'com' as Region,
|
region: "com" as Region,
|
||||||
}))
|
}))
|
||||||
.splice(0, normalizedLimit),
|
.splice(0, normalizedLimit)
|
||||||
),
|
),
|
||||||
fetchBlitz<ClanList>('eu', 'clans/list', {
|
fetchBlitz<ClanList>("eu", "clans/list", {
|
||||||
search,
|
search,
|
||||||
limit: normalizedLimit,
|
limit: normalizedLimit,
|
||||||
}).then((value) =>
|
}).then((value) =>
|
||||||
value
|
value
|
||||||
.map((account) => ({
|
.map((account) => ({
|
||||||
...account,
|
...account,
|
||||||
region: 'eu' as Region,
|
region: "eu" as Region,
|
||||||
}))
|
}))
|
||||||
.splice(0, normalizedLimit),
|
.splice(0, normalizedLimit)
|
||||||
),
|
),
|
||||||
fetchBlitz<ClanList>('asia', 'clans/list', {
|
fetchBlitz<ClanList>("asia", "clans/list", {
|
||||||
search,
|
search,
|
||||||
limit: normalizedLimit,
|
limit: normalizedLimit,
|
||||||
}).then((value) =>
|
}).then((value) =>
|
||||||
value
|
value
|
||||||
.map((account) => ({
|
.map((account) => ({
|
||||||
...account,
|
...account,
|
||||||
region: 'asia' as Region,
|
region: "asia" as Region,
|
||||||
}))
|
}))
|
||||||
.splice(0, normalizedLimit),
|
.splice(0, normalizedLimit)
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Region } from '@blitzkit/core';
|
import { context } from "../blitzkit/context";
|
||||||
import { context } from '../blitzkit/context';
|
import { Region } from "./regions";
|
||||||
import { regionToRegionSubdomain } from './regionToRegionSubdomain';
|
import { regionToRegionSubdomain } from "./regionToRegionSubdomain";
|
||||||
|
|
||||||
type CurrentRatingPlayerSearch = Record<
|
type CurrentRatingPlayerSearch = Record<
|
||||||
number,
|
number,
|
||||||
@@ -27,12 +27,12 @@ type CurrentRatingPlayerSearch = Record<
|
|||||||
export async function searchCurrentRatingPlayers(region: Region, name: string) {
|
export async function searchCurrentRatingPlayers(region: Region, name: string) {
|
||||||
const encodedSearch = encodeURIComponent(name);
|
const encodedSearch = encodeURIComponent(name);
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
context === 'website'
|
context === "website"
|
||||||
? `/api/${region}/rating/current/search/${encodedSearch}`
|
? `/api/${region}/rating/current/search/${encodedSearch}`
|
||||||
: `https://${regionToRegionSubdomain(
|
: `https://${regionToRegionSubdomain(
|
||||||
region,
|
region
|
||||||
)}.wotblitz.com/en/api/rating-leaderboards/search/?prefix=${encodedSearch}`,
|
)}.wotblitz.com/en/api/rating-leaderboards/search/?prefix=${encodedSearch}`,
|
||||||
{ cache: 'no-store' },
|
{ cache: "no-store" }
|
||||||
);
|
);
|
||||||
const accountList = (await response.json()) as CurrentRatingPlayerSearch;
|
const accountList = (await response.json()) as CurrentRatingPlayerSearch;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { fetchBlitz, Region } from '@blitzkit/core';
|
import { fetchBlitz } from '../fetchBlitz';
|
||||||
|
import { Region } from '../regions';
|
||||||
import { usernamePattern } from './constants';
|
import { usernamePattern } from './constants';
|
||||||
|
|
||||||
export interface AccountListItem {
|
export interface AccountListItem {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { NATION_IDS } from "@blitzkit/core";
|
import { NATION_IDS } from "./nations";
|
||||||
|
|
||||||
export function toUniqueId(nation: string, id: number) {
|
export function toUniqueId(nation: string, id: number) {
|
||||||
return (id << 8) + (NATION_IDS[nation] << 4) + 1;
|
return (id << 8) + (NATION_IDS[nation] << 4) + 1;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export function assertSecret(secret?: string) {
|
export function assertSecret<Type>(secret?: Type) {
|
||||||
if (secret === undefined) throw new Error('Missing secret');
|
if (secret === undefined) throw new Error("Missing secret");
|
||||||
return secret;
|
return secret;
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,12 @@
|
|||||||
import {
|
import { GunDefinition, ProvisionDefinitions, TankDefinition } from "../protos";
|
||||||
GunDefinition,
|
import { checkConsumableProvisionInclusivity } from "./checkConsumableProvisionInclusivity";
|
||||||
ProvisionDefinitions,
|
|
||||||
TankDefinition,
|
|
||||||
} from '@blitzkit/core';
|
|
||||||
import { checkConsumableProvisionInclusivity } from './checkConsumableProvisionInclusivity';
|
|
||||||
|
|
||||||
export function availableProvisions(
|
export function availableProvisions(
|
||||||
tank: TankDefinition,
|
tank: TankDefinition,
|
||||||
gun: GunDefinition,
|
gun: GunDefinition,
|
||||||
provisionDefinitions: ProvisionDefinitions,
|
provisionDefinitions: ProvisionDefinitions
|
||||||
) {
|
) {
|
||||||
return Object.values(provisionDefinitions.provisions).filter((provision) =>
|
return Object.values(provisionDefinitions.provisions).filter((provision) =>
|
||||||
checkConsumableProvisionInclusivity(provision, tank, gun),
|
checkConsumableProvisionInclusivity(provision, tank, gun)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { SkillDefinitions } from '@blitzkit/core';
|
import { SkillDefinitions } from "..";
|
||||||
import { permanentSkills } from '../blitz/permanentSkills';
|
import { permanentSkills } from "../blitz/permanentSkills";
|
||||||
|
|
||||||
export function createDefaultSkills(skillDefinitions: SkillDefinitions) {
|
export function createDefaultSkills(skillDefinitions: SkillDefinitions) {
|
||||||
const skills: Record<string, number> = {};
|
const skills: Record<string, number> = {};
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { assertSecret, getRatingInfo } from '@blitzkit/core';
|
import { getRatingInfo } from "../blitz/getRatingInfo";
|
||||||
|
import { assertSecret } from "./assertSecret";
|
||||||
|
|
||||||
interface GitHubTrees {
|
interface GitHubTrees {
|
||||||
tree: { path: string; url: string }[];
|
tree: { path: string; url: string }[];
|
||||||
@@ -11,21 +12,21 @@ export async function getArchivedLatestSeasonNumber() {
|
|||||||
return cachedLatestArchivedSeasonNumber;
|
return cachedLatestArchivedSeasonNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ratingInfo = await getRatingInfo('com');
|
const ratingInfo = await getRatingInfo("com");
|
||||||
|
|
||||||
if (ratingInfo.detail) {
|
if (ratingInfo.detail) {
|
||||||
const regionsURL = (
|
const regionsURL = (
|
||||||
(await fetch(
|
(await fetch(
|
||||||
`https://api.github.com/repos/tresabhi/${assertSecret(
|
`https://api.github.com/repos/tresabhi/${assertSecret(
|
||||||
import.meta.env.PUBLIC_ASSET_REPO,
|
import.meta.env.PUBLIC_ASSET_REPO
|
||||||
)}/git/trees/main`,
|
)}/git/trees/main`
|
||||||
).then((response) => response.json())) as GitHubTrees
|
).then((response) => response.json())) as GitHubTrees
|
||||||
).tree.find(({ path }) => path === 'regions')!.url;
|
).tree.find(({ path }) => path === "regions")!.url;
|
||||||
const comURL = (
|
const comURL = (
|
||||||
(await fetch(regionsURL).then((response) =>
|
(await fetch(regionsURL).then((response) =>
|
||||||
response.json(),
|
response.json()
|
||||||
)) as GitHubTrees
|
)) as GitHubTrees
|
||||||
).tree.find(({ path }) => path === 'com')!.url;
|
).tree.find(({ path }) => path === "com")!.url;
|
||||||
const ratingURL = (
|
const ratingURL = (
|
||||||
(await fetch(comURL).then((response) => response.json())) as {
|
(await fetch(comURL).then((response) => response.json())) as {
|
||||||
tree: [{ url: string }];
|
tree: [{ url: string }];
|
||||||
@@ -36,7 +37,7 @@ export async function getArchivedLatestSeasonNumber() {
|
|||||||
(await fetch(ratingURL).then((response) => response.json())) as {
|
(await fetch(ratingURL).then((response) => response.json())) as {
|
||||||
tree: { path: string }[];
|
tree: { path: string }[];
|
||||||
}
|
}
|
||||||
).tree.map(({ path }) => Number(path)),
|
).tree.map(({ path }) => Number(path))
|
||||||
);
|
);
|
||||||
|
|
||||||
cachedLatestArchivedSeasonNumber = latestSeason;
|
cachedLatestArchivedSeasonNumber = latestSeason;
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { asset, RatingLeaderboard, Region } from '@blitzkit/core';
|
import { Region } from "../blitz/regions";
|
||||||
|
import { RatingLeaderboard } from "../protos";
|
||||||
|
import { asset } from "./asset";
|
||||||
|
|
||||||
const cache: Record<Region, Record<number, RatingLeaderboard>> = {
|
const cache: Record<Region, Record<number, RatingLeaderboard>> = {
|
||||||
asia: {},
|
asia: {},
|
||||||
@@ -8,7 +10,7 @@ const cache: Record<Region, Record<number, RatingLeaderboard>> = {
|
|||||||
|
|
||||||
export async function getArchivedRatingLeaderboard(
|
export async function getArchivedRatingLeaderboard(
|
||||||
region: Region,
|
region: Region,
|
||||||
season: number,
|
season: number
|
||||||
) {
|
) {
|
||||||
if (!cache[region][season]) {
|
if (!cache[region][season]) {
|
||||||
const info = await fetch(asset(`regions/${region}/rating/${season}.pb`))
|
const info = await fetch(asset(`regions/${region}/rating/${season}.pb`))
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Region } from '@blitzkit/core';
|
import { Region } from "../blitz/regions";
|
||||||
|
|
||||||
const TIME_ZONE_MAPPINGS: Record<Region, number> = {
|
const TIME_ZONE_MAPPINGS: Record<Region, number> = {
|
||||||
com: -5, // Central North American Time
|
com: -5, // Central North American Time
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ShellDefinition, ShellType } from '@blitzkit/core';
|
import { ShellDefinition, ShellType } from "../protos";
|
||||||
|
|
||||||
export function isExplosive(
|
export function isExplosive(
|
||||||
type: ShellDefinition['type'],
|
type: ShellDefinition["type"]
|
||||||
): type is ShellType.HEAT | ShellType.HE {
|
): type is ShellType.HEAT | ShellType.HE {
|
||||||
return type === ShellType.HEAT || type === ShellType.HE;
|
return type === ShellType.HEAT || type === ShellType.HE;
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { TANK_CLASSES } from '../blitz/constants';
|
import { TANK_CLASSES } from "../blitz/constants";
|
||||||
import { GameDefinitions, TankDefinition, TankType } from '../protos';
|
import { GameDefinitions, TankDefinition, TankType } from "../protos";
|
||||||
|
|
||||||
const treeTypeOrder = [
|
const treeTypeOrder = [
|
||||||
TankType.RESEARCHABLE,
|
TankType.RESEARCHABLE,
|
||||||
@@ -9,20 +9,20 @@ const treeTypeOrder = [
|
|||||||
|
|
||||||
export function metaSortTank(
|
export function metaSortTank(
|
||||||
tanks: TankDefinition[],
|
tanks: TankDefinition[],
|
||||||
gameDefinitions: GameDefinitions,
|
gameDefinitions: GameDefinitions
|
||||||
) {
|
) {
|
||||||
return tanks
|
return tanks
|
||||||
.sort((a, b) => b.tier - a.tier)
|
.sort((a, b) => b.tier - a.tier)
|
||||||
.sort(
|
.sort(
|
||||||
(a, b) => treeTypeOrder.indexOf(b.type) - treeTypeOrder.indexOf(a.type),
|
(a, b) => treeTypeOrder.indexOf(b.type) - treeTypeOrder.indexOf(a.type)
|
||||||
)
|
)
|
||||||
.sort(
|
.sort(
|
||||||
(a, b) => TANK_CLASSES.indexOf(b.class) - TANK_CLASSES.indexOf(a.class),
|
(a, b) => TANK_CLASSES.indexOf(b.class) - TANK_CLASSES.indexOf(a.class)
|
||||||
)
|
)
|
||||||
.sort(
|
.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
gameDefinitions.nations.indexOf(b.nation) -
|
gameDefinitions.nations.indexOf(b.nation) -
|
||||||
gameDefinitions.nations.indexOf(a.nation),
|
gameDefinitions.nations.indexOf(a.nation)
|
||||||
)
|
)
|
||||||
.sort((a, b) => a.tier - b.tier);
|
.sort((a, b) => a.tier - b.tier);
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { BoundingBox, Vector3 } from '@blitzkit/core';
|
import { Vector3 } from "three";
|
||||||
|
import { BoundingBox } from "../protos";
|
||||||
|
|
||||||
export function normalizeBoundingBox(boundingBox: BoundingBox) {
|
export function normalizeBoundingBox(boundingBox: BoundingBox) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { BKNI_COLORS, BkniPercentile, getBkniPercentile } from '@blitzkit/core';
|
import { BKNI_COLORS, BkniPercentile, getBkniPercentile } from "../statistics";
|
||||||
|
|
||||||
export function parseBkni(bkni: number) {
|
export function parseBkni(bkni: number) {
|
||||||
const bkniFraction = (bkni + 1) / 2;
|
const bkniFraction = (bkni + 1) / 2;
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
import { asset } from '@blitzkit/core';
|
import { asset } from "./asset";
|
||||||
|
|
||||||
export const BLITZKIT_TANK_ICON_SIZE = { width: 40 * 16, height: 30 * 16 };
|
export const BLITZKIT_TANK_ICON_SIZE = { width: 40 * 16, height: 30 * 16 };
|
||||||
|
|
||||||
export function tankIcon(
|
export function tankIcon(id: number, size: "big" | "small" = "big") {
|
||||||
id: number,
|
|
||||||
size: 'big' | 'small' | 'blitzkit' = 'blitzkit',
|
|
||||||
) {
|
|
||||||
return asset(`icons/tanks/${size}/${id}.webp`);
|
return asset(`icons/tanks/${size}/${id}.webp`);
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import { deburr } from 'lodash-es';
|
import locales from "@blitzkit/i18n/locales.json";
|
||||||
import { SUPPORTED_LOCALES } from '../../../i18n/src/strings';
|
import { deburr } from "lodash-es";
|
||||||
import { I18nString } from '../protos';
|
import { I18nString } from "../protos";
|
||||||
import { fetchCamouflageDefinitions } from './camouflageDefinitions';
|
import { fetchCamouflageDefinitions } from "./camouflageDefinitions";
|
||||||
import { fetchTankDefinitions } from './tankDefinitions';
|
import { fetchTankDefinitions } from "./tankDefinitions";
|
||||||
|
|
||||||
export async function fetchTankNames() {
|
export async function fetchTankNames() {
|
||||||
const [tankDefinitions, camouflageDefinitions] = await Promise.all([
|
const [tankDefinitions, camouflageDefinitions] = await Promise.all([
|
||||||
@@ -26,25 +26,27 @@ export async function fetchTankNames() {
|
|||||||
searchableNameDeburr,
|
searchableNameDeburr,
|
||||||
camouflages: tank.camouflages
|
camouflages: tank.camouflages
|
||||||
?.map((id) =>
|
?.map((id) =>
|
||||||
SUPPORTED_LOCALES.map(
|
locales.supported.map(
|
||||||
(locale) =>
|
({ locale }) =>
|
||||||
camouflageDefinitions.camouflages[id]?.name.locales[locale],
|
camouflageDefinitions.camouflages[id]?.name.locales[locale]
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
.flat()
|
.flat()
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.map(deburr)
|
.map(deburr)
|
||||||
.join(' '),
|
.join(" "),
|
||||||
treeType: tank.type,
|
treeType: tank.type,
|
||||||
};
|
};
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SEARCH_KEYS = [
|
export const SEARCH_KEYS = [
|
||||||
...SUPPORTED_LOCALES.map((locale) => [
|
...locales.supported
|
||||||
`searchableName.locales.${locale}`,
|
.map(({ locale }) => [
|
||||||
`searchableNameDeburr.locales.${locale}`,
|
`searchableName.locales.${locale}`,
|
||||||
]).flat(),
|
`searchableNameDeburr.locales.${locale}`,
|
||||||
'camouflages',
|
])
|
||||||
|
.flat(),
|
||||||
|
"camouflages",
|
||||||
];
|
];
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { BoundingBox } from '@blitzkit/core';
|
import { BoundingBox } from "../protos";
|
||||||
|
|
||||||
export function unionBoundingBox(a: BoundingBox, b: BoundingBox) {
|
export function unionBoundingBox(a: BoundingBox, b: BoundingBox) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
+101
-102
@@ -1,20 +1,19 @@
|
|||||||
import { Vector3Tuple, Vector4Tuple } from 'three';
|
import { Vector3Tuple, Vector4Tuple } from "three";
|
||||||
import { ScpgReadStream } from './scpg';
|
import { ScpgReadStream } from "./scpg";
|
||||||
|
|
||||||
export interface SC2 {
|
export interface SC2 {
|
||||||
'#dataNodes': DataNode[];
|
"#dataNodes": DataNode[];
|
||||||
'#hierarchy': Hierarchy[];
|
"#hierarchy": Hierarchy[];
|
||||||
'#sceneComponents': SceneComponents;
|
"#sceneComponents": SceneComponents;
|
||||||
}
|
}
|
||||||
|
|
||||||
type DataNode = {
|
type DataNode = {
|
||||||
'##name': 'NMaterial';
|
"##name": "NMaterial";
|
||||||
'#id': ArrayBuffer;
|
"#id": ArrayBuffer;
|
||||||
materialName: string;
|
materialName: string;
|
||||||
parentMaterialKey?: bigint;
|
parentMaterialKey?: bigint;
|
||||||
qualityGroup?: string;
|
qualityGroup?: string;
|
||||||
fxName?: string;
|
fxName?: string;
|
||||||
|
|
||||||
textures?: Textures;
|
textures?: Textures;
|
||||||
} & (
|
} & (
|
||||||
| ({
|
| ({
|
||||||
@@ -80,8 +79,8 @@ export interface Textures {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Hierarchy {
|
export interface Hierarchy {
|
||||||
'##name': 'Entity';
|
"##name": "Entity";
|
||||||
'#hierarchy'?: Hierarchy[];
|
"#hierarchy"?: Hierarchy[];
|
||||||
components: Components;
|
components: Components;
|
||||||
flags: number;
|
flags: number;
|
||||||
id: bigint;
|
id: bigint;
|
||||||
@@ -97,12 +96,12 @@ type Components = {
|
|||||||
type RoBatches = Record<
|
type RoBatches = Record<
|
||||||
string,
|
string,
|
||||||
{
|
{
|
||||||
'##name': 'RenderBatch';
|
"##name": "RenderBatch";
|
||||||
'rb.aabbox': RbAabbox;
|
"rb.aabbox": RbAabbox;
|
||||||
'rb.classname': 'RenderBatch';
|
"rb.classname": "RenderBatch";
|
||||||
'rb.datasource': bigint;
|
"rb.datasource": bigint;
|
||||||
'rb.nmatname': bigint;
|
"rb.nmatname": bigint;
|
||||||
'rb.sortingKey': number;
|
"rb.sortingKey": number;
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
|
|
||||||
@@ -113,50 +112,50 @@ interface RbAabbox {
|
|||||||
|
|
||||||
export type Component =
|
export type Component =
|
||||||
| {
|
| {
|
||||||
'comp.typename': 'TransformComponent';
|
"comp.typename": "TransformComponent";
|
||||||
'tc.localRotation': Vector4Tuple;
|
"tc.localRotation": Vector4Tuple;
|
||||||
'tc.localScale': Vector3Tuple;
|
"tc.localScale": Vector3Tuple;
|
||||||
'tc.localTranslation': Vector3Tuple;
|
"tc.localTranslation": Vector3Tuple;
|
||||||
'tc.worldRotation': Vector4Tuple;
|
"tc.worldRotation": Vector4Tuple;
|
||||||
'tc.worldScale': Vector3Tuple;
|
"tc.worldScale": Vector3Tuple;
|
||||||
'tc.worldTranslation': Vector3Tuple;
|
"tc.worldTranslation": Vector3Tuple;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
'comp.typename': 'RenderComponent';
|
"comp.typename": "RenderComponent";
|
||||||
'rc.renderObj': {
|
"rc.renderObj": {
|
||||||
'##name': 'Mesh';
|
"##name": "Mesh";
|
||||||
'ro.batchCount': number;
|
"ro.batchCount": number;
|
||||||
'ro.batches': RoBatches;
|
"ro.batches": RoBatches;
|
||||||
'ro.debugflags': number;
|
"ro.debugflags": number;
|
||||||
'ro.flags': number;
|
"ro.flags": number;
|
||||||
'ro.notShadowOnly': boolean;
|
"ro.notShadowOnly": boolean;
|
||||||
'ro.sOclCull': boolean;
|
"ro.sOclCull": boolean;
|
||||||
'ro.sOclIndex': number;
|
"ro.sOclIndex": number;
|
||||||
} & Record<`rb${number}.${'lodIndex' | 'switchIndex'}`, number>;
|
} & Record<`rb${number}.${"lodIndex" | "switchIndex"}`, number>;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
'comp.typename': 'SlotComponent';
|
"comp.typename": "SlotComponent";
|
||||||
'sc.attachmentRotation': number[];
|
"sc.attachmentRotation": number[];
|
||||||
'sc.attachmentScale': number[];
|
"sc.attachmentScale": number[];
|
||||||
'sc.attachmentTranslation': number[];
|
"sc.attachmentTranslation": number[];
|
||||||
'sc.configFilePath': string;
|
"sc.configFilePath": string;
|
||||||
'sc.slotName': string;
|
"sc.slotName": string;
|
||||||
'sc.template': string;
|
"sc.template": string;
|
||||||
'sc.typeFiltersCount': number;
|
"sc.typeFiltersCount": number;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
'comp.typename': 'LodComponent';
|
"comp.typename": "LodComponent";
|
||||||
'lc.loddist': Record<`distance${number}`, number>;
|
"lc.loddist": Record<`distance${number}`, number>;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
'comp.typename': 'DecorItemComponent';
|
"comp.typename": "DecorItemComponent";
|
||||||
drawOrder: number;
|
drawOrder: number;
|
||||||
shouldApplyCamo: boolean;
|
shouldApplyCamo: boolean;
|
||||||
vanishTargetAlpha: number;
|
vanishTargetAlpha: number;
|
||||||
vanishWhenArmorShown: boolean;
|
vanishWhenArmorShown: boolean;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
'comp.typename': 'NewSlotComponent';
|
"comp.typename": "NewSlotComponent";
|
||||||
slotItemsCount: number;
|
slotItemsCount: number;
|
||||||
slots: {
|
slots: {
|
||||||
[key: string]: {
|
[key: string]: {
|
||||||
@@ -171,109 +170,109 @@ export type Component =
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
'comp.typename': 'ScenarioComponent';
|
"comp.typename": "ScenarioComponent";
|
||||||
'scenario.scriptId': string;
|
"scenario.scriptId": string;
|
||||||
}
|
}
|
||||||
| ({
|
| ({
|
||||||
'comp.typename': 'StateSwitcherComponent';
|
"comp.typename": "StateSwitcherComponent";
|
||||||
'ssc.activeState': number;
|
"ssc.activeState": number;
|
||||||
'ssc.statesCount': number;
|
"ssc.statesCount": number;
|
||||||
} & Record<`ssc.state${number}`, string>)
|
} & Record<`ssc.state${number}`, string>)
|
||||||
| {
|
| {
|
||||||
'comp.typename': 'CustomPropertiesComponent';
|
"comp.typename": "CustomPropertiesComponent";
|
||||||
'cpc.properties.archive': {
|
"cpc.properties.archive": {
|
||||||
'editor.donotremove': number;
|
"editor.donotremove": number;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
| ({
|
| ({
|
||||||
'comp.typename': 'ActionComponent';
|
"comp.typename": "ActionComponent";
|
||||||
'ac.actionCount': number;
|
"ac.actionCount": number;
|
||||||
} & Record<
|
} & Record<
|
||||||
string, // 0000, 0001, ...
|
string, // 0000, 0001, ...
|
||||||
{
|
{
|
||||||
'act.delay': number;
|
"act.delay": number;
|
||||||
'act.delayVariation': number;
|
"act.delayVariation": number;
|
||||||
'act.entityName': string;
|
"act.entityName": string;
|
||||||
'act.event': number;
|
"act.event": number;
|
||||||
'act.eventToTrigger': string;
|
"act.eventToTrigger": string;
|
||||||
'act.motionFrameTime': number;
|
"act.motionFrameTime": number;
|
||||||
'act.motionSpeed': number;
|
"act.motionSpeed": number;
|
||||||
'act.stateActivated': number;
|
"act.stateActivated": number;
|
||||||
'act.stopAfterNRepeats': number;
|
"act.stopAfterNRepeats": number;
|
||||||
'act.stopWhenEmpty': boolean;
|
"act.stopWhenEmpty": boolean;
|
||||||
'act.switchIndex': number;
|
"act.switchIndex": number;
|
||||||
'act.type': number;
|
"act.type": number;
|
||||||
'act.userEventId': string;
|
"act.userEventId": string;
|
||||||
}
|
}
|
||||||
>)
|
>)
|
||||||
| {
|
| {
|
||||||
'comp.typename': 'ParticleEffectComponent';
|
"comp.typename": "ParticleEffectComponent";
|
||||||
'pe.clearOnRestart': boolean;
|
"pe.clearOnRestart": boolean;
|
||||||
'pe.effectDuration': number;
|
"pe.effectDuration": number;
|
||||||
'pe.emitters': {
|
"pe.emitters": {
|
||||||
'##name': string;
|
"##name": string;
|
||||||
'emitter.data': {
|
"emitter.data": {
|
||||||
'emitter.id': `${number}`;
|
"emitter.id": `${number}`;
|
||||||
'emitter.quality': string;
|
"emitter.quality": string;
|
||||||
}[];
|
}[];
|
||||||
'emitter.position': Vector3Tuple;
|
"emitter.position": Vector3Tuple;
|
||||||
'emitter.rotation': Vector4Tuple;
|
"emitter.rotation": Vector4Tuple;
|
||||||
'emitter.scale': Vector3Tuple;
|
"emitter.scale": Vector3Tuple;
|
||||||
}[];
|
}[];
|
||||||
'pe.inheritScale': boolean;
|
"pe.inheritScale": boolean;
|
||||||
'pe.nestedEmitters': boolean;
|
"pe.nestedEmitters": boolean;
|
||||||
'pe.repeatsCount': number;
|
"pe.repeatsCount": number;
|
||||||
'pe.startFromTime': number;
|
"pe.startFromTime": number;
|
||||||
'pe.stopWhenEmpty': boolean;
|
"pe.stopWhenEmpty": boolean;
|
||||||
'pe.version': number;
|
"pe.version": number;
|
||||||
'ro.flags': number;
|
"ro.flags": number;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
'comp.typename': 'TankElementComponent';
|
"comp.typename": "TankElementComponent";
|
||||||
isDestructible: boolean;
|
isDestructible: boolean;
|
||||||
shouldCastShadow: boolean;
|
shouldCastShadow: boolean;
|
||||||
shouldGenerateSilhouette: boolean;
|
shouldGenerateSilhouette: boolean;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
'comp.typename': 'MotionComponent';
|
"comp.typename": "MotionComponent";
|
||||||
'motion.filepath': string;
|
"motion.filepath": string;
|
||||||
'motion.playbackRate': number;
|
"motion.playbackRate": number;
|
||||||
'simpleMotion.repeatsCount': number;
|
"simpleMotion.repeatsCount": number;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
'comp.typename': 'SkeletonComponent';
|
"comp.typename": "SkeletonComponent";
|
||||||
joints: Record<
|
joints: Record<
|
||||||
string, // 0000, 0001, ...
|
string, // 0000, 0001, ...
|
||||||
{
|
{
|
||||||
'joint.bbox.max': Vector3Tuple;
|
"joint.bbox.max": Vector3Tuple;
|
||||||
'joint.bbox.min': Vector3Tuple;
|
"joint.bbox.min": Vector3Tuple;
|
||||||
'joint.bindPose': [
|
"joint.bindPose": [
|
||||||
Vector4Tuple,
|
Vector4Tuple,
|
||||||
Vector4Tuple,
|
Vector4Tuple,
|
||||||
Vector4Tuple,
|
Vector4Tuple,
|
||||||
Vector4Tuple,
|
Vector4Tuple,
|
||||||
];
|
];
|
||||||
'joint.invBindPose': [
|
"joint.invBindPose": [
|
||||||
Vector4Tuple,
|
Vector4Tuple,
|
||||||
Vector4Tuple,
|
Vector4Tuple,
|
||||||
Vector4Tuple,
|
Vector4Tuple,
|
||||||
Vector4Tuple,
|
Vector4Tuple,
|
||||||
];
|
];
|
||||||
'joint.name': string;
|
"joint.name": string;
|
||||||
'joint.parentIndex': number;
|
"joint.parentIndex": number;
|
||||||
'joint.uid': string;
|
"joint.uid": string;
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
jointsCount: number;
|
jointsCount: number;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
'comp.typename': 'AnimationComponent';
|
"comp.typename": "AnimationComponent";
|
||||||
animation: bigint;
|
animation: bigint;
|
||||||
animationTimeScale: number;
|
animationTimeScale: number;
|
||||||
repeatsCount: number;
|
repeatsCount: number;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
'comp.typename': 'SkeletonJointBindingComponent';
|
"comp.typename": "SkeletonJointBindingComponent";
|
||||||
count: number;
|
count: number;
|
||||||
map: Record<
|
map: Record<
|
||||||
string, // 0000, 0001, ...
|
string, // 0000, 0001, ...
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ts-deepmerge": "^7.0.2"
|
"ts-deepmerge": "^7.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {}
|
"devDependencies": {}
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
export * from './literals';
|
export * from "./literals";
|
||||||
export * from './strings';
|
export * from "./strings";
|
||||||
export * from './unwrapper';
|
export * from "./unwrapper";
|
||||||
|
export * from "./wrapper";
|
||||||
@@ -1,5 +1,16 @@
|
|||||||
export function literals(string: string, literals: string[]) {
|
export function literals(
|
||||||
return literals.reduce((accumulator, literal, index) => {
|
string: string,
|
||||||
return accumulator.replace(`%s${index + 1}`, literal);
|
literals?: Record<string, string | number | undefined>
|
||||||
}, string);
|
) {
|
||||||
|
for (const key in literals) {
|
||||||
|
const mutated = string.replaceAll(`{${key}}`, `${literals[key]}`);
|
||||||
|
|
||||||
|
if (import.meta.env.DEV && mutated === string) {
|
||||||
|
console.warn(`Could not find literal {${key}} in string "${string}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
string = mutated;
|
||||||
|
}
|
||||||
|
|
||||||
|
return string;
|
||||||
}
|
}
|
||||||
@@ -1,98 +1,66 @@
|
|||||||
import { merge } from 'ts-deepmerge';
|
import { merge } from "ts-deepmerge";
|
||||||
import en from '../strings/en.json';
|
import locales from "../locales.json";
|
||||||
import es from '../strings/es.json';
|
import en from "../strings/en.json";
|
||||||
import fr from '../strings/fr.json';
|
import es from "../strings/es.json";
|
||||||
import ja from '../strings/ja.json';
|
import fr from "../strings/fr.json";
|
||||||
import pl from '../strings/pl.json';
|
import ja from "../strings/ja.json";
|
||||||
import pt from '../strings/pt.json';
|
import pl from "../strings/pl.json";
|
||||||
import ru from '../strings/ru.json';
|
import pt from "../strings/pt.json";
|
||||||
import uk from '../strings/uk.json';
|
import ru from "../strings/ru.json";
|
||||||
import zh from '../strings/zh.json';
|
import uk from "../strings/uk.json";
|
||||||
|
import zh from "../strings/zh.json";
|
||||||
|
|
||||||
export type BlitzKitStrings = typeof en;
|
export type BlitzKitStrings = typeof en;
|
||||||
|
|
||||||
const stringsRaw = { en, es, fr, ja, pl, pt, ru, uk, zh };
|
const stringsRaw: Record<string, TranslationTree> = {
|
||||||
|
en,
|
||||||
|
es,
|
||||||
|
fr,
|
||||||
|
ja,
|
||||||
|
pl,
|
||||||
|
pt,
|
||||||
|
ru,
|
||||||
|
uk,
|
||||||
|
zh,
|
||||||
|
};
|
||||||
|
|
||||||
export const SUPPORTED_LOCALES = Object.keys(stringsRaw) as SupportedLocale[];
|
export const STRINGS: Record<string, BlitzKitStrings> = {};
|
||||||
|
|
||||||
export const DEFAULT_LOCALE = SUPPORTED_LOCALES[0];
|
|
||||||
|
|
||||||
function redact(string: string) {
|
|
||||||
const matches = [...string.matchAll(/%s\d+/g)];
|
|
||||||
const characters = string
|
|
||||||
.split('')
|
|
||||||
.map((char) => (char === ' ' ? ' ' : '█')) as string[];
|
|
||||||
|
|
||||||
matches.forEach((match) => {
|
|
||||||
const start = match.index;
|
|
||||||
const matchText = match[0];
|
|
||||||
for (let i = 0; i < matchText.length; i++) {
|
|
||||||
characters[start + i] = matchText[i];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return characters.join('');
|
|
||||||
}
|
|
||||||
|
|
||||||
function nuke(object: TranslationTree) {
|
|
||||||
for (const key in object) {
|
|
||||||
const typedKey = key as keyof typeof object;
|
|
||||||
|
|
||||||
if (typeof object[typedKey] === 'string') {
|
|
||||||
const value = object[typedKey];
|
|
||||||
object[typedKey] = redact(value as string);
|
|
||||||
} else nuke(object[typedKey] as TranslationTree);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const stringsPartial: Partial<Record<SupportedLocale, TranslationTree>> = {};
|
|
||||||
|
|
||||||
for (const locale in stringsRaw) {
|
for (const locale in stringsRaw) {
|
||||||
if (locale === DEFAULT_LOCALE) {
|
if (locale === locales.default) {
|
||||||
stringsPartial[DEFAULT_LOCALE] = stringsRaw[DEFAULT_LOCALE];
|
STRINGS[locales.default] = stringsRaw[locales.default] as BlitzKitStrings;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const localeStrings = stringsRaw[locale as SupportedLocale];
|
const localeStrings = stringsRaw[locale];
|
||||||
stringsPartial[locale as SupportedLocale] = merge(
|
STRINGS[locale] = merge(
|
||||||
stringsRaw[DEFAULT_LOCALE],
|
stringsRaw[locales.default],
|
||||||
localeStrings,
|
localeStrings
|
||||||
);
|
) as BlitzKitStrings;
|
||||||
|
|
||||||
if (import.meta.env.PUBLIC_DEBUG_MISSING_I18N === 'true') {
|
|
||||||
nuke(stringsPartial[locale as SupportedLocale]!);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const STRINGS = stringsPartial as Record<
|
export const SUPPORTED_LOCALE_BLITZ_MAP: Record<string, string> = {
|
||||||
SupportedLocale,
|
en: "en",
|
||||||
BlitzKitStrings
|
es: "es",
|
||||||
>;
|
fr: "fr",
|
||||||
|
ja: "ja",
|
||||||
export type SupportedLocale = keyof typeof stringsRaw;
|
pt: "pt",
|
||||||
|
ru: "ru",
|
||||||
export const SUPPORTED_LOCALE_BLITZ_MAP: Record<SupportedLocale, string> = {
|
uk: "uk",
|
||||||
en: 'en',
|
zh: "zh-Hans",
|
||||||
es: 'es',
|
pl: "pl",
|
||||||
fr: 'fr',
|
|
||||||
ja: 'ja',
|
|
||||||
pt: 'pt',
|
|
||||||
ru: 'ru',
|
|
||||||
uk: 'uk',
|
|
||||||
zh: 'zh-Hans',
|
|
||||||
pl: 'pl',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SUPPORTED_LOCALE_FLAGS: Record<SupportedLocale, string> = {
|
export const SUPPORTED_LOCALE_FLAGS: Record<string, string> = {
|
||||||
en: '🇺🇸',
|
en: "🇺🇸",
|
||||||
es: '🇪🇸',
|
es: "🇪🇸",
|
||||||
fr: '🇫🇷',
|
fr: "🇫🇷",
|
||||||
ja: '🇯🇵',
|
ja: "🇯🇵",
|
||||||
pt: '🇵🇹',
|
pt: "🇵🇹",
|
||||||
ru: '🇷🇺',
|
ru: "🇷🇺",
|
||||||
uk: '🇺🇦',
|
uk: "🇺🇦",
|
||||||
zh: '🇨🇳',
|
zh: "🇨🇳",
|
||||||
pl: '🇵🇱',
|
pl: "🇵🇱",
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TranslationTree = { [key: string]: TranslationNode };
|
export type TranslationTree = { [key: string]: TranslationNode };
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { I18nString } from '@blitzkit/core';
|
import { I18nString } from "../../core/src";
|
||||||
import { DEFAULT_LOCALE } from './strings';
|
import locales from "../locales.json";
|
||||||
|
|
||||||
export function unwrapper(locale: string) {
|
export function unwrapper(locale = locales.default) {
|
||||||
return function (i18nString: I18nString) {
|
return function (i18nString: I18nString) {
|
||||||
if (locale in i18nString.locales) {
|
if (locale in i18nString.locales) {
|
||||||
return i18nString.locales[locale];
|
return i18nString.locales[locale];
|
||||||
}
|
}
|
||||||
|
|
||||||
return i18nString.locales[DEFAULT_LOCALE];
|
return i18nString.locales[locales.default];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import locales from "../locales.json";
|
||||||
|
import { BlitzKitStrings, STRINGS } from "./strings";
|
||||||
|
|
||||||
|
export function wrapper(slicer: (strings: BlitzKitStrings) => string) {
|
||||||
|
const defaultString = slicer(STRINGS[locales.default] as BlitzKitStrings);
|
||||||
|
|
||||||
|
const object = { [locales.default]: defaultString };
|
||||||
|
|
||||||
|
for (const { locale } of locales.supported) {
|
||||||
|
const string = slicer(STRINGS[locale] as BlitzKitStrings);
|
||||||
|
if (string !== defaultString) object[locale] = string;
|
||||||
|
}
|
||||||
|
|
||||||
|
return object;
|
||||||
|
}
|
||||||
Loaded 100 of 360 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user