Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb1c0c7625 | ||
|
|
1170676141 | ||
|
|
7baaaa9db9 | ||
|
|
c1ecef0179 | ||
|
|
032d98e6bd | ||
|
|
f4a90cd531 | ||
|
|
82f03583fe | ||
|
|
81c1a50052 | ||
|
|
c7207799e2 | ||
|
|
e0a719ee93 | ||
|
|
ee6d2e7d82 | ||
|
|
14710b5218 | ||
|
|
4b5e0d1c1c | ||
|
|
9968e8346c | ||
|
|
bc4da17f10 | ||
|
|
da56c563fc | ||
|
|
95f4dbf6a0 | ||
|
|
6c36f343ea | ||
|
|
9257ebdd1e | ||
|
|
8f1017195f | ||
|
|
20da294b36 | ||
|
|
a63415461a | ||
|
|
2bb4c78551 | ||
|
|
2909e61721 | ||
|
|
171da2a194 | ||
|
|
933c468e2d | ||
|
|
0d1eb9b12e | ||
|
|
02a03b4d68 | ||
|
|
ef9f3bd361 | ||
|
|
f18b6ee754 | ||
|
|
deb9e463e1 | ||
|
|
c799534009 | ||
|
|
aea01072fb | ||
|
|
ca8dd5a702 | ||
|
|
e79c4a1e3f | ||
|
|
e19d0d948a | ||
|
|
a160ccd095 | ||
|
|
d2d0477186 | ||
|
|
c742c0725d | ||
|
|
9e6343a111 | ||
|
|
faf33b5f2d | ||
|
|
8a78ac7d82 | ||
|
|
d7c7814b3a | ||
|
|
15bf02f829 | ||
|
|
4f62a594b3 | ||
|
|
64253d0120 | ||
|
|
286936131d | ||
|
|
95588ef6b6 | ||
|
|
daef04ccd8 | ||
|
|
86e385c548 | ||
|
|
cc193b99d6 | ||
|
|
c026007937 | ||
|
|
dbc117a9d4 | ||
|
|
34665bd97a | ||
|
|
6d05e607b3 | ||
|
|
14bfc3018b | ||
|
|
7e4f195f94 | ||
|
|
4b6de934a5 | ||
|
|
14fbb595fd | ||
|
|
18a5fb80d0 | ||
|
|
551ef79f03 | ||
|
|
81aec6bad2 | ||
|
|
d08949c597 | ||
|
|
212aee0373 | ||
|
|
059b78c9fb | ||
|
|
11ba5fc28e | ||
|
|
fbde9ec64f | ||
|
|
6600bba58c | ||
|
|
9e340d2e16 | ||
|
|
6f784e9e94 | ||
|
|
62f6aabf05 | ||
|
|
46457a8bdc | ||
|
|
1081e836f5 | ||
|
|
dfe1e37592 | ||
|
|
0b9db21c10 | ||
|
|
57527115d4 | ||
|
|
8d2679c458 | ||
|
|
ca00f18a6a | ||
|
|
466f68701f | ||
|
|
1bb83da253 | ||
|
|
bd1e17bac9 | ||
|
|
322f95e42e | ||
|
|
3ea77f5899 | ||
|
|
882e61ff44 | ||
|
|
d991be4934 | ||
|
|
dfba8bd15f | ||
|
|
87e23673fb | ||
|
|
8fbd1acf4a | ||
|
|
d7dfbaa799 | ||
|
|
04ce20857e | ||
|
|
4550246d63 | ||
|
|
dd673f44ae | ||
|
|
3245284ecc | ||
|
|
3d9872a671 | ||
|
|
f02db2a2d8 | ||
|
|
d1ebb79432 | ||
|
|
44f03a1fce | ||
|
|
6b6ee94d9b | ||
|
|
81c2ca80bc | ||
|
|
c83cec8ed7 | ||
|
|
93ffe5c507 | ||
|
|
8cd1a70333 | ||
|
|
06ae024c2c | ||
|
|
ce8fd0f22f | ||
|
|
7dc1aff1dc | ||
|
|
a60003127f | ||
|
|
7d1ba58806 | ||
|
|
963d71e221 | ||
|
|
2a804fda1c | ||
|
|
597a1a04d2 | ||
|
|
aec6f49ea1 | ||
|
|
e1ad710de5 | ||
|
|
edc8447617 | ||
|
|
f726d4f8e8 | ||
|
|
1f333f6b88 | ||
|
|
9b855fca47 | ||
|
|
c5f433fdf4 | ||
|
|
fd61dae1c1 | ||
|
|
2c45f9759f | ||
|
|
0d3261f748 | ||
|
|
d38e873800 | ||
|
|
ca38914095 | ||
|
|
ee5beeea45 | ||
|
|
a9d40d9f49 | ||
|
|
b8be916c18 | ||
|
|
24d0313fd3 | ||
|
|
c3d2a81327 | ||
|
|
dfeb117663 | ||
|
|
495a830506 | ||
|
|
a730f67ca5 | ||
|
|
9a3867770d | ||
|
|
f8a66bb5e7 | ||
|
|
4b00e1342e | ||
|
|
f9e0584f00 | ||
|
|
900c290e75 | ||
|
|
69e1577d18 | ||
|
|
e3864b86a4 |
No files matched your search
@@ -29,4 +29,4 @@ jobs:
|
|||||||
unzip bot.zip
|
unzip bot.zip
|
||||||
|
|
||||||
- name: Run bot
|
- name: Run bot
|
||||||
run: NODE_ENV=production GH_TOKEN=${{ secrets.GH_TOKEN }} DISCORD_TOKEN=${{ secrets.DISCORD_TOKEN }} node --enable-source-maps index.cjs
|
run: NODE_ENV=production GH_TOKEN=${{ secrets.GH_TOKEN }} DISCORD_TOKEN=${{ secrets.DISCORD_TOKEN }} DISCORD_BLITZ_DB=${{ secrets.DISCORD_BLITZ_DB }} node --enable-source-maps index.cjs
|
||||||
@@ -13,4 +13,5 @@ test.*
|
|||||||
/docs/.vitepress/cache
|
/docs/.vitepress/cache
|
||||||
servers.csv
|
servers.csv
|
||||||
.next/
|
.next/
|
||||||
tsconfig.tsbuildinfo
|
tsconfig.tsbuildinfo
|
||||||
|
temp/
|
||||||
@@ -6,15 +6,19 @@
|
|||||||
"blitzstars",
|
"blitzstars",
|
||||||
"cleandist",
|
"cleandist",
|
||||||
"cockroackdb",
|
"cockroackdb",
|
||||||
|
"corsproxy",
|
||||||
"customperiod",
|
"customperiod",
|
||||||
"deburr",
|
"deburr",
|
||||||
"diacriticless",
|
"diacriticless",
|
||||||
"Everytime",
|
"Everytime",
|
||||||
"fullstats",
|
"fullstats",
|
||||||
"fuzzysort",
|
"fuzzysort",
|
||||||
|
"immer",
|
||||||
"kranvagn",
|
"kranvagn",
|
||||||
"Leaderboard",
|
"Leaderboard",
|
||||||
"leaderboards",
|
"leaderboards",
|
||||||
|
"MURICA",
|
||||||
|
"Oopsy",
|
||||||
"outdir",
|
"outdir",
|
||||||
"outfile",
|
"outfile",
|
||||||
"ownedtanks",
|
"ownedtanks",
|
||||||
@@ -35,6 +39,7 @@
|
|||||||
"subnation",
|
"subnation",
|
||||||
"tankaverages",
|
"tankaverages",
|
||||||
"tankopedia",
|
"tankopedia",
|
||||||
|
"tankopedias",
|
||||||
"tankstats",
|
"tankstats",
|
||||||
"tresabhi",
|
"tresabhi",
|
||||||
"Unicum",
|
"Unicum",
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export default defineConfig({
|
|||||||
themeConfig: {
|
themeConfig: {
|
||||||
nav: [
|
nav: [
|
||||||
{ text: 'Home', link: '/' },
|
{ text: 'Home', link: '/' },
|
||||||
{ text: 'Guide', link: '/guide/about' },
|
{ text: 'Guide', link: '/guide/introduction' },
|
||||||
],
|
],
|
||||||
|
|
||||||
sidebar: [
|
sidebar: [
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Blitzkrieg 1.14.1
|
||||||
|
|
||||||
|
Oopsy daisy, a memory leak!
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
- Simplified message for users to use `/link`
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
- Fixed a memory leak in the render thread manager
|
||||||
|
|
||||||
|
## Technical changes
|
||||||
|
|
||||||
|
- Added `UserError` extending `Error`
|
||||||
|
- This lets the bot avoid spamming the debug console for mistakes users make
|
||||||
|
- Reduced render threads to 2
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# Blitzkrieg 1.14.2
|
||||||
|
|
||||||
|
A late night hot fix for my homies
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
- Clan info is now fetched using `clan_id` instead of `account_id`
|
||||||
|
- Generic stats row names are now bounded by a max width
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# Blitzkrieg 1.14.3
|
||||||
|
|
||||||
|
Pull your brush out and start polishing.
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- Rows in `/breakdown` are now chunked into `8` per image
|
||||||
|
- This is true unless the number of pages exceeds `10` in which case the rows are even distributed across all `10` pages
|
||||||
|
- `10` is the maximum number of images that Discord allows
|
||||||
|
- Short region names are used in username autocomplete instead of their full names
|
||||||
|
- Example: "North America" is "NA" now
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
- Removed reference to username inference in the username option description
|
||||||
|
- Removed an unnecessary type `asd` that did absolutely nothing used nowhere
|
||||||
|
|
||||||
|
## Technical changes
|
||||||
|
|
||||||
|
- Image orders are now preserved even if processed in parallel and all completed out of order
|
||||||
|
- `fetchBlitz` is now throttled to `10` requests per second
|
||||||
|
- Request overflows are managed by queueing
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# Blitzkrieg 1.14.4
|
||||||
|
|
||||||
|
Streamers get some knowledge today
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
- Added a video in the embed button prompt
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
# Blitzkrieg 1.15.0
|
||||||
|
|
||||||
|
Ratings will never be the same.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- Blitzkrieg ratings leaderboard
|
||||||
|
- Available at https://blitz-krieg.vercel.app/tools/ratings
|
||||||
|
- **Highly experimental; [please report bugs on Discord server](https://discord.gg/nDt7AjGJQH)**
|
||||||
|
- Current season leaderboards are live meaning that there may be scores and positions that are out of order
|
||||||
|
- Options
|
||||||
|
- Regions can be swapped between NA, EU, and Asia
|
||||||
|
- The current season and all archived season (from 49 and onwards) are available
|
||||||
|
- Jump: used to jump to certain players
|
||||||
|
- Score: finds the first player with at least your desired score
|
||||||
|
- Position: finds the player at that position
|
||||||
|
- League: finds the top-most player in that league
|
||||||
|
- Player: finds the player
|
||||||
|
- **Jumping to scores and positions for the current season is a compounding function (hence the loading screen) and is limited to the speed of Wargaming's APIs which is pretty darn slow**
|
||||||
|
- Page controls
|
||||||
|
- Flip between pages left and right
|
||||||
|
- 33 players are loaded at once
|
||||||
|
- Left control is disabled on the first page and vice versa
|
||||||
|
- Page turning controls are also disabled if the next/previous page hasn't loaded yet
|
||||||
|
- Page numbers can be manually typed in the text field too
|
||||||
|
- This is available both at the top and the bottom of the screen
|
||||||
|
- Players
|
||||||
|
- Shows the position of the player
|
||||||
|
- Shows the player nickname and clan name
|
||||||
|
- "Deleted player ID" us shown when loading a deleted player
|
||||||
|
- A skeleton is shown when the player and clan is loading
|
||||||
|
- Shows the player percentile
|
||||||
|
- Shows reward for the position
|
||||||
|
- This is blank when there is no reward
|
||||||
|
- This column isn't available for archived seasons
|
||||||
|
- Shows the score
|
||||||
|
- A skeleton is shown when loading
|
||||||
|
- Click on a row to highlight it
|
||||||
|
- Using a jump button also highlights a row
|
||||||
|
- `/credits`: thanks who helped made this project
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
- Error messages that user face are now even more simplified
|
||||||
|
- Increased username and clan autocomplete size limits to `25`
|
||||||
|
- Added capability for per-page accent theming
|
||||||
|
- Example: the ratings page is red while the session tracker is blue
|
||||||
|
- Simplified welcome message for SKLLD clan server
|
||||||
|
|
||||||
|
## Technical Changes
|
||||||
|
|
||||||
|
- Isolated tankopedia info
|
||||||
|
- Implemented `corsproxy.io` for non-timeout tolerant endpoints
|
||||||
|
- Latest archived season number is now data driven
|
||||||
|
- Ratings archives are now public
|
||||||
|
- This eliminates the need of `octokit`
|
||||||
|
- Ratings leaderboard archiver is now error tolerant
|
||||||
|
- Fetch attempts are made every `1000ms`
|
||||||
|
- Blitz errors are now managed better freeing up the queue faster
|
||||||
|
- Isolated first archived season number
|
||||||
|
- Added more advanced PSAs
|
||||||
|
- These can now feature images or embeds
|
||||||
|
- These can now modify message contents
|
||||||
|
- These can also now be secret
|
||||||
|
|
||||||
|
## Experimental Changes
|
||||||
|
|
||||||
|
- Added tankopedia extractor
|
||||||
|
- Retrieves tankopedia data from the game files
|
||||||
|
- This is not utilized as of right now
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# Blitzkrieg 1.15.1
|
||||||
|
|
||||||
|
'MURICA!
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
- "Neighbor" is now spelled in the American way
|
||||||
|
- Updated documentation website with the latest information
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
- Fixed the guide link on the documentation website
|
||||||
|
|
||||||
|
## Technical features
|
||||||
|
|
||||||
|
- Added capability to fetch patiently while also parsing the JSON
|
||||||
|
|
||||||
|
## Technical changes
|
||||||
|
|
||||||
|
- Removed neighbor relative position caching
|
||||||
|
|
||||||
|
## Technical fixes
|
||||||
|
|
||||||
|
- `/ratings` now uses the non-caching APIs
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Blitzkrieg 1.15.2
|
||||||
|
|
||||||
|
Infinity reduced to a finite value.
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
- Added a maximum value to the jump to position button
|
||||||
|
- Removed neighbor checking for the seeding player
|
||||||
|
- This fixes the jump to position button not loading at all
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
# Installing Blitzkrieg
|
|
||||||
|
|
||||||
It's easy as 1, 2, 3!
|
|
||||||
|
|
||||||
## Step 1
|
|
||||||
|
|
||||||
Join a server that already has Blitzkrieg or [join our official Discord server](https://discord.gg/nDt7AjGJQH).
|
|
||||||
|
|
||||||
## Step 2
|
|
||||||
|
|
||||||
View the bot's profile and click "Add to Server".
|
|
||||||
|
|
||||||
<video src="https://i.imgur.com/Zq0P3m2.mp4" autoplay loop muted width=320 title="how to install"></video>
|
|
||||||
|
|
||||||
## Step 3
|
|
||||||
|
|
||||||
Review the permissions the bot is asking for and add it to your server!
|
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
# Introduction
|
# Introduction
|
||||||
|
|
||||||
Blitzkrieg is a multipurpose bot for anything and everything World of Tanks Blitz.
|
🎉 Services for everything and anything World of Tanks Blitz
|
||||||
|
|
||||||
|
## Bot
|
||||||
|
|
||||||
|
Blitzkrieg is available as a Discord bot and can be [installed very easily](/guide/invite).
|
||||||
|
|
||||||
- Track statistics
|
- Track statistics
|
||||||
- Find inactive players in a clan
|
- Find inactive players in a clan
|
||||||
@@ -8,3 +12,11 @@ Blitzkrieg is a multipurpose bot for anything and everything World of Tanks Blit
|
|||||||
- Create embeds for streams
|
- Create embeds for streams
|
||||||
- Track a player/tanks statistical evolution
|
- Track a player/tanks statistical evolution
|
||||||
- ...and a variety of other similar things
|
- ...and a variety of other similar things
|
||||||
|
|
||||||
|
## Website
|
||||||
|
|
||||||
|
Provides a bunch of very helpful tools; https://blitz-krieg.vercel.app/
|
||||||
|
|
||||||
|
- Session tracker
|
||||||
|
- Ratings leaderboard
|
||||||
|
- Way more to come
|
||||||
@@ -12,10 +12,10 @@ hero:
|
|||||||
actions:
|
actions:
|
||||||
- theme: brand
|
- theme: brand
|
||||||
text: Add to Discord Server
|
text: Add to Discord Server
|
||||||
link: guide/install
|
link: /guide/invite
|
||||||
- theme: alt
|
- theme: alt
|
||||||
text: Guide
|
text: Guide
|
||||||
link: /guide/about
|
link: /guide/introduction
|
||||||
|
|
||||||
features:
|
features:
|
||||||
- title: Clean and Efficient UI
|
- title: Clean and Efficient UI
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 183 KiB |
@@ -7,6 +7,7 @@ const nextConfig = {
|
|||||||
...withVanillaExtract(),
|
...withVanillaExtract(),
|
||||||
|
|
||||||
distDir: 'dist/website',
|
distDir: 'dist/website',
|
||||||
|
reactStrictMode: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default nextConfig;
|
export default nextConfig;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "blitzkrieg",
|
"name": "blitzkrieg",
|
||||||
"version": "1.14.0",
|
"version": "1.15.2",
|
||||||
"description": "🎉 Tools for everything World of Tanks Blitz",
|
"description": "🎉 Tools for everything World of Tanks Blitz",
|
||||||
"main": "src/server.ts",
|
"main": "src/server.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
"build:website": "next build",
|
"build:website": "next build",
|
||||||
"build:docs": "vitepress build docs",
|
"build:docs": "vitepress build docs",
|
||||||
"build:ratingsleaderboard": "vite-node --script scripts/buildRatingsLeaderboard",
|
"build:ratingsleaderboard": "vite-node --script scripts/buildRatingsLeaderboard",
|
||||||
|
"build:tankopedia": "vite-node --script scripts/buildTankopedia",
|
||||||
"dev:bot": "yarn build:bot && yarn start:bot",
|
"dev:bot": "yarn build:bot && yarn start:bot",
|
||||||
"dev:docs": "vitepress dev docs",
|
"dev:docs": "vitepress dev docs",
|
||||||
"dev:website": "next dev",
|
"dev:website": "next dev",
|
||||||
@@ -33,12 +34,14 @@
|
|||||||
"@resvg/resvg-js": "^2.4.1",
|
"@resvg/resvg-js": "^2.4.1",
|
||||||
"@vanilla-extract/css": "^1.13.0",
|
"@vanilla-extract/css": "^1.13.0",
|
||||||
"bepaint": "^0.1.0-beta.3",
|
"bepaint": "^0.1.0-beta.3",
|
||||||
|
"decompress": "^4.2.1",
|
||||||
"discord.js": "^14.13.0",
|
"discord.js": "^14.13.0",
|
||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
"esbuild": "^0.19.3",
|
"esbuild": "^0.19.3",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"fuzzysort": "^2.0.4",
|
"fuzzysort": "^2.0.4",
|
||||||
"immer": "^10.0.2",
|
"immer": "^10.0.2",
|
||||||
|
"js-yaml": "^4.1.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"markdown-escape": "^2.0.0",
|
"markdown-escape": "^2.0.0",
|
||||||
"next": "^13.5.2",
|
"next": "^13.5.2",
|
||||||
@@ -49,12 +52,15 @@
|
|||||||
"vite-node": "^0.34.5",
|
"vite-node": "^0.34.5",
|
||||||
"vitepress": "^1.0.0-rc.20",
|
"vitepress": "^1.0.0-rc.20",
|
||||||
"vitepress-plugin-auto-sidebar": "^1.1.0",
|
"vitepress-plugin-auto-sidebar": "^1.1.0",
|
||||||
|
"xml-js": "^1.6.11",
|
||||||
"zustand": "^4.4.1"
|
"zustand": "^4.4.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@total-typescript/ts-reset": "^0.5.1",
|
"@total-typescript/ts-reset": "^0.5.1",
|
||||||
|
"@types/decompress": "^4.2.6",
|
||||||
"@types/escape-html": "^1.0.2",
|
"@types/escape-html": "^1.0.2",
|
||||||
"@types/express": "^4.17.18",
|
"@types/express": "^4.17.18",
|
||||||
|
"@types/js-yaml": "^4.0.8",
|
||||||
"@types/lodash": "^4.14.199",
|
"@types/lodash": "^4.14.199",
|
||||||
"@types/markdown-escape": "^1.1.0",
|
"@types/markdown-escape": "^1.1.0",
|
||||||
"@types/node": "^20.6.5",
|
"@types/node": "^20.6.5",
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.6 MiB After Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 8.2 MiB After Width: | Height: | Size: 5.4 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 3.8 MiB After Width: | Height: | Size: 2.4 MiB |
|
Before Width: | Height: | Size: 2.1 MiB After Width: | Height: | Size: 2.1 MiB |
|
Before Width: | Height: | Size: 2.4 MiB After Width: | Height: | Size: 1.3 MiB |
@@ -1,36 +1,44 @@
|
|||||||
import { build } from 'esbuild';
|
import { build } from 'esbuild';
|
||||||
import { readdir } from 'fs/promises';
|
import { cp, readdir } from 'fs/promises';
|
||||||
|
|
||||||
const FILES = [
|
readdir('src/workers').then((workers) => {
|
||||||
'bot',
|
const files = [
|
||||||
'index',
|
'bot',
|
||||||
|
'index',
|
||||||
|
|
||||||
(await readdir('src/workers')).map(
|
...workers.map((file) => `workers/${file.replace('.ts', '')}`),
|
||||||
(file) => `workers/${file.replace('.ts', '')}`,
|
];
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
await build({
|
build({
|
||||||
entryPoints: FILES.map((file) => `src/${file}.ts`),
|
entryPoints: files.map((file) => `src/${file}.ts`),
|
||||||
outdir: 'dist/bot',
|
outdir: 'dist/bot',
|
||||||
|
|
||||||
platform: 'node',
|
platform: 'node',
|
||||||
tsconfig: 'tsconfig.esbuild.json',
|
tsconfig: 'tsconfig.esbuild.json',
|
||||||
loader: {
|
loader: {
|
||||||
'.node': 'copy',
|
'.node': 'copy',
|
||||||
'.ttf': 'file',
|
'.ttf': 'file',
|
||||||
},
|
},
|
||||||
format: 'cjs',
|
format: 'cjs',
|
||||||
jsx: 'transform',
|
jsx: 'transform',
|
||||||
outExtension: {
|
outExtension: {
|
||||||
'.js': '.cjs',
|
'.js': '.cjs',
|
||||||
},
|
},
|
||||||
|
|
||||||
bundle: true,
|
bundle: true,
|
||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
minifyIdentifiers: false, // cause goofy errors
|
minifyIdentifiers: false, // causes goofy errors
|
||||||
minifySyntax: true,
|
minifySyntax: true,
|
||||||
minifyWhitespace: true,
|
minifyWhitespace: true,
|
||||||
|
|
||||||
logLevel: 'info',
|
logLevel: 'info',
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// copy schema.prisma
|
||||||
|
cp('prisma/schema.prisma', 'dist/bot/schema.prisma');
|
||||||
|
readdir('node_modules/prisma').then((files) =>
|
||||||
|
files
|
||||||
|
.filter((file) => file.endsWith('.node'))
|
||||||
|
.map((file) => cp(`node_modules/prisma/${file}`, `dist/bot/${file}`)),
|
||||||
|
);
|
||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
RatingsNeighbors,
|
RatingsNeighbors,
|
||||||
RatingsPlayer,
|
RatingsPlayer,
|
||||||
} from '../src/commands/ratings';
|
} from '../src/commands/ratings';
|
||||||
|
import { patientFetchJSON } from '../src/core/blitzkrieg/patientFetchJSON';
|
||||||
import commitMultipleFiles from './commitMultipleFiles.js';
|
import commitMultipleFiles from './commitMultipleFiles.js';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -13,12 +14,13 @@ import commitMultipleFiles from './commitMultipleFiles.js';
|
|||||||
* Central European Time (UTC+1): use 0 23 * * *.
|
* Central European Time (UTC+1): use 0 23 * * *.
|
||||||
* Central Asia Standard Time (UTC+7): use 0 17 * * *.
|
* Central Asia Standard Time (UTC+7): use 0 17 * * *.
|
||||||
*
|
*
|
||||||
* na/ratings/49/info.json: season info like rewards, start & end time, etc.
|
* [region]/ratings/[season]/info.json: season info like rewards, start & end time, etc.
|
||||||
* na/ratings/49/latest.json: the absolute latest leaderboard recorded at UTC+0 midnight
|
* [region]/ratings/[season]/latest.json: the absolute latest leaderboard recorded at UTC+0 midnight
|
||||||
* na/ratings/49/midnight.json: last midnight's leaderboard relative to the region
|
* [region]/ratings/[season]/midnight.json: last midnight's leaderboard relative to the region
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export type BlitzkriegRatingsLeaderboard = { id: number; score: number }[];
|
export type BlitzkriegRatingsLeaderboardEntry = { id: number; score: number };
|
||||||
|
export type BlitzkriegRatingsLeaderboard = BlitzkriegRatingsLeaderboardEntry[];
|
||||||
|
|
||||||
const publish = argv.includes('--publish');
|
const publish = argv.includes('--publish');
|
||||||
const latest = argv.includes('--latest');
|
const latest = argv.includes('--latest');
|
||||||
@@ -26,12 +28,10 @@ const server = argv.find((arg) => arg.startsWith('--region='))?.split('=')[1];
|
|||||||
|
|
||||||
if (!server) throw new Error('Region parameter not specified');
|
if (!server) throw new Error('Region parameter not specified');
|
||||||
|
|
||||||
const NEIGHBORS = 1000;
|
const NEIGHBORS = 1024;
|
||||||
const PLAYERS = await fetch(
|
const PLAYERS = await patientFetchJSON<RatingsInfo>(
|
||||||
`https://${server}.wotblitz.com/en/api/rating-leaderboards/season/`,
|
`https://${server}.wotblitz.com/en/api/rating-leaderboards/season/`,
|
||||||
)
|
).then((data) => (data.detail === undefined ? data.count : undefined));
|
||||||
.then((response) => response.json() as Promise<RatingsInfo>)
|
|
||||||
.then((data) => (data.detail === undefined ? data.count : undefined));
|
|
||||||
|
|
||||||
if (PLAYERS === undefined) {
|
if (PLAYERS === undefined) {
|
||||||
throw new Error('No current ratings season running');
|
throw new Error('No current ratings season running');
|
||||||
@@ -42,9 +42,9 @@ const leaderboard: BlitzkriegRatingsLeaderboard = [];
|
|||||||
let registered = 0;
|
let registered = 0;
|
||||||
|
|
||||||
async function branchFromPlayer(id: number) {
|
async function branchFromPlayer(id: number) {
|
||||||
const { neighbors } = await fetch(
|
const { neighbors } = await patientFetchJSON<RatingsNeighbors>(
|
||||||
`https://${server}.wotblitz.com/en/api/rating-leaderboards/user/${id}/?neighbors=${NEIGHBORS}`,
|
`https://${server}.wotblitz.com/en/api/rating-leaderboards/user/${id}/?neighbors=${NEIGHBORS}`,
|
||||||
).then((response) => response.json() as Promise<RatingsNeighbors>);
|
);
|
||||||
|
|
||||||
const firstPlayer = neighbors[0];
|
const firstPlayer = neighbors[0];
|
||||||
const lastPlayer = neighbors[neighbors.length - 1];
|
const lastPlayer = neighbors[neighbors.length - 1];
|
||||||
@@ -73,9 +73,9 @@ async function branchFromPlayer(id: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function branchFromLeague(id: number) {
|
async function branchFromLeague(id: number) {
|
||||||
const { result } = await fetch(
|
const { result } = await patientFetchJSON<{ result: RatingsPlayer[] }>(
|
||||||
`https://${server}.wotblitz.com/en/api/rating-leaderboards/league/${id}/top/`,
|
`https://${server}.wotblitz.com/en/api/rating-leaderboards/league/${id}/top/`,
|
||||||
).then((response) => response.json() as Promise<{ result: RatingsPlayer[] }>);
|
);
|
||||||
|
|
||||||
const firstPlayer = result[0];
|
const firstPlayer = result[0];
|
||||||
const lastPlayer = result[result.length - 1];
|
const lastPlayer = result[result.length - 1];
|
||||||
@@ -111,11 +111,8 @@ const leaderboardJSON = JSON.stringify(leaderboard);
|
|||||||
if (publish) {
|
if (publish) {
|
||||||
const octokit = new Octokit({ auth: env.GH_TOKEN });
|
const octokit = new Octokit({ auth: env.GH_TOKEN });
|
||||||
|
|
||||||
const info = await fetch(
|
const info = await patientFetchJSON<RatingsInfo & { detail: undefined }>(
|
||||||
`https://${server}.wotblitz.com/en/api/rating-leaderboards/season/`,
|
`https://${server}.wotblitz.com/en/api/rating-leaderboards/season/`,
|
||||||
).then(
|
|
||||||
(response) =>
|
|
||||||
response.json() as Promise<RatingsInfo & { detail: undefined }>,
|
|
||||||
);
|
);
|
||||||
const normalizedServer = server === 'na' ? 'com' : server;
|
const normalizedServer = server === 'na' ? 'com' : server;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
import decompress from 'decompress';
|
||||||
|
import { readFile, readdir, rm, writeFile } from 'fs/promises';
|
||||||
|
import { load } from 'js-yaml';
|
||||||
|
import { ElementCompact, xml2js } from 'xml-js';
|
||||||
|
import { NATION_IDS } from '../src/constants/nations';
|
||||||
|
|
||||||
|
const REWRITE = false;
|
||||||
|
const VEHICLE_DEFS = 'assets/Data/XML/item_defs/vehicles';
|
||||||
|
const APK_FILE = 'temp/blitz.apk';
|
||||||
|
const STRINGS = 'assets/Data/Strings';
|
||||||
|
const LANGUAGE = 'en';
|
||||||
|
const DIRECTORIES_OF_INTEREST = [VEHICLE_DEFS, STRINGS];
|
||||||
|
|
||||||
|
if (REWRITE) {
|
||||||
|
await rm('temp/blitz', { recursive: true, force: true });
|
||||||
|
decompress(APK_FILE, 'temp/blitz', {
|
||||||
|
filter: (file) =>
|
||||||
|
DIRECTORIES_OF_INTEREST.some((dir) => file.path.startsWith(dir)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BlitzkriegTankopedia {
|
||||||
|
id: number;
|
||||||
|
nation: string;
|
||||||
|
name: string;
|
||||||
|
name_short?: string;
|
||||||
|
is_premium: boolean;
|
||||||
|
is_collector: boolean;
|
||||||
|
tier: number;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const strings = await readFile(
|
||||||
|
`temp/blitz/${STRINGS}/${LANGUAGE}.yaml`,
|
||||||
|
'utf-8',
|
||||||
|
).then((data) => load(data) as Record<string, string>);
|
||||||
|
const tankopedia: Record<number, BlitzkriegTankopedia> = {};
|
||||||
|
const nations = await readdir(`temp/blitz/${VEHICLE_DEFS}`);
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
nations
|
||||||
|
.filter((nation) => nation !== 'common')
|
||||||
|
.map(async (nation) => {
|
||||||
|
const listXML = await readFile(
|
||||||
|
`temp/blitz/${VEHICLE_DEFS}/${nation}/list.xml`,
|
||||||
|
'utf-8',
|
||||||
|
);
|
||||||
|
const list = xml2js(listXML, { compact: true }) as ElementCompact;
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
Object.entries<ElementCompact>(list.root).map(
|
||||||
|
async ([vehicle, vehicleData]) => {
|
||||||
|
// const vehicleXML = await readFile(
|
||||||
|
// `temp/blitz/${VEHICLE_DEFS}/${nation}/${vehicle}.xml`,
|
||||||
|
// 'utf-8',
|
||||||
|
// ).then((xml) => xml2js(xml, { compact: true }) as ElementCompact);
|
||||||
|
const nationVehicleId = parseInt(list.root[vehicle].id._text);
|
||||||
|
const id = (nationVehicleId << 8) + (NATION_IDS[nation] << 4) + 1;
|
||||||
|
// TODO: use userString property instead
|
||||||
|
const name = strings[`#${nation}_vehicles:${vehicle}`];
|
||||||
|
const name_short = strings[`#${nation}_vehicles:${vehicle}_short`];
|
||||||
|
const is_premium = 'gold' in vehicleData.price;
|
||||||
|
const is_collector = vehicleData.sellPrice
|
||||||
|
? 'gold' in vehicleData.sellPrice
|
||||||
|
: false;
|
||||||
|
const tier = parseInt(vehicleData.level._text);
|
||||||
|
const tags = vehicleData.tags._text.split(' ');
|
||||||
|
const type = tags[0];
|
||||||
|
|
||||||
|
tankopedia[id] = {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
name_short,
|
||||||
|
nation,
|
||||||
|
is_premium,
|
||||||
|
is_collector,
|
||||||
|
tier,
|
||||||
|
type,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
writeFile('dist/tankopedia.json', JSON.stringify(tankopedia, null, 2));
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { NextResponse } from 'next/server';
|
||||||
|
import { Region } from '../../../../../../constants/regions';
|
||||||
|
import getRatingsInfo from '../../../../../../core/blitz/getRatingsInfo';
|
||||||
|
|
||||||
|
export async function GET(
|
||||||
|
request: Request,
|
||||||
|
{ params }: { params: { region: Region } },
|
||||||
|
) {
|
||||||
|
return NextResponse.json(await getRatingsInfo(params.region));
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { NextResponse } from 'next/server';
|
||||||
|
import { Region } from '../../../../../../../constants/regions';
|
||||||
|
import { getRatingsLeague } from '../../../../../../../core/blitz/getRatingsLeague';
|
||||||
|
|
||||||
|
export async function GET(
|
||||||
|
request: Request,
|
||||||
|
{ params }: { params: { region: Region; league: number } },
|
||||||
|
) {
|
||||||
|
return NextResponse.json(
|
||||||
|
await getRatingsLeague(params.region, params.league, true),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { NextResponse } from 'next/server';
|
||||||
|
import { Region } from '../../../../../../../constants/regions';
|
||||||
|
import { searchCurrentRatingsPlayers } from '../../../../../../../core/blitz/searchCurrentRatingsPlayers';
|
||||||
|
|
||||||
|
export async function GET(
|
||||||
|
request: Request,
|
||||||
|
{ params }: { params: { region: Region; username: string } },
|
||||||
|
) {
|
||||||
|
return NextResponse.json(
|
||||||
|
await searchCurrentRatingsPlayers(params.region, params.username),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ import { useEffect, useState } from 'react';
|
|||||||
import * as Breakdown from '../../../components/Breakdown';
|
import * as Breakdown from '../../../components/Breakdown';
|
||||||
import { TreeTypeEnum } from '../../../components/Tanks';
|
import { TreeTypeEnum } from '../../../components/Tanks';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../../../constants/wargamingApplicationID';
|
import { WARGAMING_APPLICATION_ID } from '../../../constants/wargamingApplicationID';
|
||||||
import fetchBlitz from '../../../core/blitz/fetchWargaming';
|
import fetchBlitz from '../../../core/blitz/fetchBlitz';
|
||||||
import { tankAverages } from '../../../core/blitzstars/tankAverages';
|
import { tankAverages } from '../../../core/blitzstars/tankAverages';
|
||||||
import {
|
import {
|
||||||
TankopediaEntry,
|
TankopediaEntry,
|
||||||
|
|||||||
@@ -41,12 +41,7 @@ export default function RootLayout({ children }: RootLayoutProps) {
|
|||||||
: 'url(https://i.imgur.com/PhS06NJ.png)',
|
: 'url(https://i.imgur.com/PhS06NJ.png)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Theme
|
<Theme appearance="dark" panelBackground="translucent" radius="full">
|
||||||
appearance="dark"
|
|
||||||
panelBackground="translucent"
|
|
||||||
radius="full"
|
|
||||||
accentColor="blue"
|
|
||||||
>
|
|
||||||
{!isEmbed && <Navbar />}
|
{!isEmbed && <Navbar />}
|
||||||
|
|
||||||
<Flex direction="column">{children}</Flex>
|
<Flex direction="column">{children}</Flex>
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { keyframes, style } from '@vanilla-extract/css';
|
||||||
|
|
||||||
|
const spinKeyframes = keyframes({
|
||||||
|
from: { transform: 'rotate(0deg)' },
|
||||||
|
to: { transform: 'rotate(360deg)' },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const infiniteSpin = style({
|
||||||
|
animationName: spinKeyframes,
|
||||||
|
animationIterationCount: 'infinite',
|
||||||
|
animationTimingFunction: 'linear',
|
||||||
|
animationDuration: '1s',
|
||||||
|
});
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
import {
|
||||||
|
CaretLeftIcon,
|
||||||
|
CaretRightIcon,
|
||||||
|
SymbolIcon,
|
||||||
|
} from '@radix-ui/react-icons';
|
||||||
|
import { Button, Flex, TextField } from '@radix-ui/themes';
|
||||||
|
import { range } from 'lodash';
|
||||||
|
import { useEffect, useRef } from 'react';
|
||||||
|
import { infiniteSpin } from './index.css';
|
||||||
|
|
||||||
|
interface PageTurnerProps {
|
||||||
|
leaderboard: Record<number, number>;
|
||||||
|
page: number;
|
||||||
|
rowsPerPage: number;
|
||||||
|
pages: number;
|
||||||
|
onPageChange: (page: number, isButtonClick: boolean) => void;
|
||||||
|
totalPlayers: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function PageTurner({
|
||||||
|
page,
|
||||||
|
rowsPerPage,
|
||||||
|
onPageChange,
|
||||||
|
pages,
|
||||||
|
leaderboard,
|
||||||
|
totalPlayers,
|
||||||
|
}: PageTurnerProps) {
|
||||||
|
const pageInput = useRef<HTMLInputElement>(null);
|
||||||
|
const rightLoading = range(
|
||||||
|
page * rowsPerPage,
|
||||||
|
Math.min(totalPlayers - 1, page * rowsPerPage + rowsPerPage + 1),
|
||||||
|
).some((index) => !(index in leaderboard));
|
||||||
|
const leftLoading = range(
|
||||||
|
Math.max(0, page * rowsPerPage - 1),
|
||||||
|
Math.max(0, page * rowsPerPage - rowsPerPage - 1) + 1,
|
||||||
|
).some((index) => !(index in leaderboard));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (pageInput.current) pageInput.current.value = `${page + 1}`;
|
||||||
|
}, [page]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex justify="center" wrap="wrap" gap="2">
|
||||||
|
<Flex gap="2">
|
||||||
|
<Button
|
||||||
|
variant="soft"
|
||||||
|
onClick={() => onPageChange(Math.max(page - 1, 0), true)}
|
||||||
|
disabled={page === 0 || leftLoading}
|
||||||
|
>
|
||||||
|
{leftLoading ? (
|
||||||
|
<SymbolIcon className={infiniteSpin} />
|
||||||
|
) : (
|
||||||
|
<CaretLeftIcon />
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
<TextField.Root>
|
||||||
|
<TextField.Slot>Page</TextField.Slot>
|
||||||
|
<TextField.Input
|
||||||
|
type="number"
|
||||||
|
ref={pageInput}
|
||||||
|
style={{ width: 64, textAlign: 'center' }}
|
||||||
|
onBlur={(event) => {
|
||||||
|
onPageChange(
|
||||||
|
Math.max(
|
||||||
|
0,
|
||||||
|
Math.min(pages - 1, event.target.valueAsNumber - 1),
|
||||||
|
),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
onKeyDown={(event) => {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
(event.target as HTMLInputElement).blur();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<TextField.Slot>out of {pages}</TextField.Slot>
|
||||||
|
</TextField.Root>
|
||||||
|
<Button
|
||||||
|
variant="soft"
|
||||||
|
onClick={() => {
|
||||||
|
onPageChange(Math.min(page + 1, pages - 1), true);
|
||||||
|
}}
|
||||||
|
disabled={page === pages - 1 || rightLoading}
|
||||||
|
>
|
||||||
|
{rightLoading ? (
|
||||||
|
<SymbolIcon className={infiniteSpin} />
|
||||||
|
) : (
|
||||||
|
<CaretRightIcon />
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -51,7 +51,7 @@ export default function Page() {
|
|||||||
}, [session]);
|
}, [session]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageWrapper>
|
<PageWrapper color="blue">
|
||||||
<div className={styles.toolBar}>
|
<div className={styles.toolBar}>
|
||||||
<div style={{ flex: 1, boxSizing: 'border-box', position: 'relative' }}>
|
<div style={{ flex: 1, boxSizing: 'border-box', position: 'relative' }}>
|
||||||
<TextField.Root>
|
<TextField.Root>
|
||||||
@@ -124,7 +124,7 @@ export default function Page() {
|
|||||||
<Button
|
<Button
|
||||||
variant="soft"
|
variant="soft"
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
if (!session.promptBeforeReset) {
|
if (!session.resetPrompt) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
resetSession();
|
resetSession();
|
||||||
}
|
}
|
||||||
@@ -153,7 +153,7 @@ export default function Page() {
|
|||||||
mr="1"
|
mr="1"
|
||||||
onCheckedChange={(checked) =>
|
onCheckedChange={(checked) =>
|
||||||
useSession.setState({
|
useSession.setState({
|
||||||
promptBeforeReset: !checked,
|
resetPrompt: !checked,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@@ -183,7 +183,7 @@ export default function Page() {
|
|||||||
variant="soft"
|
variant="soft"
|
||||||
className={styles.toolbarButton}
|
className={styles.toolbarButton}
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
if (!session.showEmbedPrompt) {
|
if (!session.embedPrompt) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
resetSession();
|
resetSession();
|
||||||
}
|
}
|
||||||
@@ -202,14 +202,17 @@ export default function Page() {
|
|||||||
Copied link to your clipboard
|
Copied link to your clipboard
|
||||||
</AlertDialog.Title>
|
</AlertDialog.Title>
|
||||||
<AlertDialog.Description>
|
<AlertDialog.Description>
|
||||||
Keep in mind:
|
Instructions for streamers:
|
||||||
<ul>
|
<iframe
|
||||||
<li>Paste this link into your browser to view the embed</li>
|
style={{
|
||||||
<li>Embeds abide your custom options</li>
|
width: '100%',
|
||||||
<li>
|
aspectRatio: '16 / 9',
|
||||||
Options do not transfer across websites/streaming softwares
|
border: 'none',
|
||||||
</li>
|
}}
|
||||||
</ul>
|
src="https://www.youtube-nocookie.com/embed/e--GwjyrJAE?si=ftEiCmpbMI2FUY5N"
|
||||||
|
title="YouTube video player"
|
||||||
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||||
|
></iframe>
|
||||||
</AlertDialog.Description>
|
</AlertDialog.Description>
|
||||||
|
|
||||||
<Text size="2">
|
<Text size="2">
|
||||||
@@ -217,7 +220,7 @@ export default function Page() {
|
|||||||
mr="1"
|
mr="1"
|
||||||
onCheckedChange={(checked) =>
|
onCheckedChange={(checked) =>
|
||||||
useSession.setState({
|
useSession.setState({
|
||||||
showEmbedPrompt: !checked,
|
embedPrompt: !checked,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { SlashCommandBuilder } from 'discord.js';
|
import { SlashCommandBuilder } from 'discord.js';
|
||||||
|
import { chunk } from 'lodash';
|
||||||
import * as Breakdown from '../components/Breakdown';
|
import * as Breakdown from '../components/Breakdown';
|
||||||
import NoData, { NoDataType } from '../components/NoData';
|
import NoData, { NoDataType } from '../components/NoData';
|
||||||
import TitleBar from '../components/TitleBar';
|
import TitleBar from '../components/TitleBar';
|
||||||
@@ -35,6 +36,9 @@ import { StatFilters, filterStats } from '../core/statistics/filterStats';
|
|||||||
import getWN8Percentile from '../core/statistics/getWN8Percentile';
|
import getWN8Percentile from '../core/statistics/getWN8Percentile';
|
||||||
import { CommandRegistryRaw } from '../events/interactionCreate';
|
import { CommandRegistryRaw } from '../events/interactionCreate';
|
||||||
|
|
||||||
|
const ROWS_PER_PAGE = 8;
|
||||||
|
const MAX_PAGES = 10;
|
||||||
|
|
||||||
async function render(
|
async function render(
|
||||||
{ region, id }: ResolvedPlayer,
|
{ region, id }: ResolvedPlayer,
|
||||||
{ start, end, name }: ResolvedPeriod,
|
{ start, end, name }: ResolvedPeriod,
|
||||||
@@ -132,38 +136,80 @@ async function render(
|
|||||||
0,
|
0,
|
||||||
) / careerBattles;
|
) / careerBattles;
|
||||||
|
|
||||||
return (
|
const children: JSX.Element[] = [];
|
||||||
<Wrapper>
|
|
||||||
<TitleBar
|
|
||||||
name={accountInfo.nickname}
|
|
||||||
image={
|
|
||||||
clanData?.clan
|
|
||||||
? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clanData?.clan?.emblem_set_id}.png`
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
description={`${name} • ${filterDescriptions}`}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{filteredOrder.length === 0 && (
|
if (filteredOrder.length > 0) {
|
||||||
<NoData type={NoDataType.BattlesInPeriod} />
|
children.push(
|
||||||
)}
|
<Breakdown.Row
|
||||||
{filteredOrder.length > 0 && (
|
key={id}
|
||||||
<Breakdown.Root>
|
type="summary"
|
||||||
|
title="Total"
|
||||||
|
stats={[
|
||||||
|
{
|
||||||
|
title: 'Battles',
|
||||||
|
current: currentBattles,
|
||||||
|
career: careerBattles,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Winrate',
|
||||||
|
current: `${(100 * currentWinrate).toFixed(2)}%`,
|
||||||
|
career: `${(100 * careerWinrate).toFixed(2)}%`,
|
||||||
|
delta: currentWinrate - careerWinrate,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'WN8',
|
||||||
|
current:
|
||||||
|
currentWN8 === undefined
|
||||||
|
? undefined
|
||||||
|
: Math.round(currentWN8).toLocaleString(),
|
||||||
|
career:
|
||||||
|
careerWN8 === undefined
|
||||||
|
? undefined
|
||||||
|
: Math.round(careerWN8).toLocaleString(),
|
||||||
|
percentile:
|
||||||
|
currentWN8 === undefined
|
||||||
|
? undefined
|
||||||
|
: getWN8Percentile(currentWN8),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Damage',
|
||||||
|
current: Math.round(currentDamage).toLocaleString(),
|
||||||
|
career: Math.round(careerDamage).toLocaleString(),
|
||||||
|
delta: currentDamage - careerDamage,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
filteredOrder.map(async (id, index) => {
|
||||||
|
const tankopediaEntry = awaitedTankopedia[id];
|
||||||
|
const current = orderedCurrentStats[index];
|
||||||
|
const career = orderedCareerStats[index];
|
||||||
|
const currentWN8 = orderedCurrentWN8[index];
|
||||||
|
const careerWN8 = orderedCareerWN8[index];
|
||||||
|
|
||||||
|
children.push(
|
||||||
<Breakdown.Row
|
<Breakdown.Row
|
||||||
key={id}
|
key={id}
|
||||||
type="summary"
|
type="tank"
|
||||||
title="Total"
|
tankType={tankopediaEntry?.type}
|
||||||
|
treeType={tankopediaEntry ? await getTreeType(id) : undefined}
|
||||||
|
title={await resolveTankName(id)}
|
||||||
stats={[
|
stats={[
|
||||||
{
|
{
|
||||||
title: 'Battles',
|
title: 'Battles',
|
||||||
current: currentBattles,
|
current: current.battles.toLocaleString(),
|
||||||
career: careerBattles,
|
career: career.battles.toLocaleString(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Winrate',
|
title: 'Winrate',
|
||||||
current: `${(100 * currentWinrate).toFixed(2)}%`,
|
current: `${(100 * (current.wins / current.battles)).toFixed(
|
||||||
career: `${(100 * careerWinrate).toFixed(2)}%`,
|
2,
|
||||||
delta: currentWinrate - careerWinrate,
|
)}%`,
|
||||||
|
career: `${(100 * (career.wins / career.battles)).toFixed(2)}%`,
|
||||||
|
delta:
|
||||||
|
current.wins / current.battles - career.wins / career.battles,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'WN8',
|
title: 'WN8',
|
||||||
@@ -182,83 +228,67 @@ async function render(
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Damage',
|
title: 'Damage',
|
||||||
current: Math.round(currentDamage).toLocaleString(),
|
current: Math.round(
|
||||||
career: Math.round(careerDamage).toLocaleString(),
|
current.damage_dealt / current.battles,
|
||||||
delta: currentDamage - careerDamage,
|
).toLocaleString(),
|
||||||
|
career: Math.round(
|
||||||
|
career.damage_dealt / career.battles,
|
||||||
|
).toLocaleString(),
|
||||||
|
delta:
|
||||||
|
current.damage_dealt / current.battles -
|
||||||
|
career.damage_dealt / career.battles,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
{await Promise.all(
|
const pages = chunk(
|
||||||
filteredOrder.map(async (id, index) => {
|
children,
|
||||||
const tankopediaEntry = awaitedTankopedia[id];
|
Math.max(Math.ceil(children.length / MAX_PAGES), ROWS_PER_PAGE),
|
||||||
const current = orderedCurrentStats[index];
|
|
||||||
const career = orderedCareerStats[index];
|
|
||||||
const currentWN8 = orderedCurrentWN8[index];
|
|
||||||
const careerWN8 = orderedCareerWN8[index];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Breakdown.Row
|
|
||||||
key={id}
|
|
||||||
type="tank"
|
|
||||||
tankType={tankopediaEntry?.type}
|
|
||||||
treeType={tankopediaEntry ? await getTreeType(id) : undefined}
|
|
||||||
title={await resolveTankName(id)}
|
|
||||||
stats={[
|
|
||||||
{
|
|
||||||
title: 'Battles',
|
|
||||||
current: current.battles.toLocaleString(),
|
|
||||||
career: career.battles.toLocaleString(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Winrate',
|
|
||||||
current: `${(
|
|
||||||
100 *
|
|
||||||
(current.wins / current.battles)
|
|
||||||
).toFixed(2)}%`,
|
|
||||||
career: `${(100 * (career.wins / career.battles)).toFixed(
|
|
||||||
2,
|
|
||||||
)}%`,
|
|
||||||
delta:
|
|
||||||
current.wins / current.battles -
|
|
||||||
career.wins / career.battles,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'WN8',
|
|
||||||
current:
|
|
||||||
currentWN8 === undefined
|
|
||||||
? undefined
|
|
||||||
: Math.round(currentWN8).toLocaleString(),
|
|
||||||
career:
|
|
||||||
careerWN8 === undefined
|
|
||||||
? undefined
|
|
||||||
: Math.round(careerWN8).toLocaleString(),
|
|
||||||
percentile:
|
|
||||||
currentWN8 === undefined
|
|
||||||
? undefined
|
|
||||||
: getWN8Percentile(currentWN8),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Damage',
|
|
||||||
current: Math.round(
|
|
||||||
current.damage_dealt / current.battles,
|
|
||||||
).toLocaleString(),
|
|
||||||
career: Math.round(
|
|
||||||
career.damage_dealt / career.battles,
|
|
||||||
).toLocaleString(),
|
|
||||||
delta:
|
|
||||||
current.damage_dealt / current.battles -
|
|
||||||
career.damage_dealt / career.battles,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
)}
|
|
||||||
</Breakdown.Root>
|
|
||||||
)}
|
|
||||||
</Wrapper>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (filteredOrder.length > 0) {
|
||||||
|
return pages
|
||||||
|
.map((page, index) => (
|
||||||
|
<Wrapper>
|
||||||
|
{index === 0 && (
|
||||||
|
<TitleBar
|
||||||
|
name={accountInfo.nickname}
|
||||||
|
image={
|
||||||
|
clanData?.clan
|
||||||
|
? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clanData?.clan?.emblem_set_id}.png`
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
description={`${name} • ${filterDescriptions}`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Breakdown.Root>{page}</Breakdown.Root>
|
||||||
|
</Wrapper>
|
||||||
|
))
|
||||||
|
.reverse();
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
<Wrapper>
|
||||||
|
<TitleBar
|
||||||
|
name={accountInfo.nickname}
|
||||||
|
image={
|
||||||
|
clanData?.clan
|
||||||
|
? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clanData?.clan?.emblem_set_id}.png`
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
description={`${name} • ${filterDescriptions}`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{filteredOrder.length === 0 && (
|
||||||
|
<NoData type={NoDataType.BattlesInPeriod} />
|
||||||
|
)}
|
||||||
|
</Wrapper>,
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const breakdownCommand = new Promise<CommandRegistryRaw>(
|
export const breakdownCommand = new Promise<CommandRegistryRaw>(
|
||||||
@@ -286,11 +316,11 @@ export const breakdownCommand = new Promise<CommandRegistryRaw>(
|
|||||||
...filters,
|
...filters,
|
||||||
});
|
});
|
||||||
|
|
||||||
return Promise.all([
|
return [
|
||||||
render(player, period, filters),
|
...(await render(player, period, filters)),
|
||||||
buttonPrimary(path, 'Refresh'),
|
buttonPrimary(path, 'Refresh'),
|
||||||
getBlitzStarsLinkButton(player.region, player.id),
|
await getBlitzStarsLinkButton(player.region, player.id),
|
||||||
]);
|
];
|
||||||
},
|
},
|
||||||
|
|
||||||
autocomplete: (interaction) => {
|
autocomplete: (interaction) => {
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { AttachmentBuilder, SlashCommandBuilder } from 'discord.js';
|
||||||
|
import { CommandRegistry } from '../events/interactionCreate';
|
||||||
|
|
||||||
|
export const creditsCommand: CommandRegistry = {
|
||||||
|
inProduction: true,
|
||||||
|
inPublic: true,
|
||||||
|
|
||||||
|
command: new SlashCommandBuilder()
|
||||||
|
.setName('credits')
|
||||||
|
.setDescription('Me and the people who made this bot'),
|
||||||
|
|
||||||
|
async handler() {
|
||||||
|
return new AttachmentBuilder('https://i.imgur.com/ZF5ch33.png');
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -8,13 +8,13 @@ import { getClanAccountInfo } from '../core/blitz/getClanAccountInfo';
|
|||||||
import getTankStats from '../core/blitz/getTankStats';
|
import getTankStats from '../core/blitz/getTankStats';
|
||||||
import getTreeType from '../core/blitz/getTreeType';
|
import getTreeType from '../core/blitz/getTreeType';
|
||||||
import resolveTankName from '../core/blitz/resolveTankName';
|
import resolveTankName from '../core/blitz/resolveTankName';
|
||||||
|
import { tankopediaInfo } from '../core/blitz/tankopediaInfo';
|
||||||
import { emblemIdToURL } from '../core/blitzkrieg/emblemIdToURL';
|
import { emblemIdToURL } from '../core/blitzkrieg/emblemIdToURL';
|
||||||
import {
|
import {
|
||||||
TIER_ROMAN_NUMERALS,
|
TIER_ROMAN_NUMERALS,
|
||||||
TankopediaEntry,
|
TankopediaEntry,
|
||||||
Tier,
|
Tier,
|
||||||
tankopedia,
|
tankopedia,
|
||||||
tankopediaInfo,
|
|
||||||
} from '../core/blitzstars/tankopedia';
|
} from '../core/blitzstars/tankopedia';
|
||||||
import addTierChoices from '../core/discord/addTierChoices';
|
import addTierChoices from '../core/discord/addTierChoices';
|
||||||
import addUsernameChoices from '../core/discord/addUsernameChoices';
|
import addUsernameChoices from '../core/discord/addUsernameChoices';
|
||||||
|
|||||||
@@ -4,21 +4,25 @@ import {
|
|||||||
SlashCommandSubcommandBuilder,
|
SlashCommandSubcommandBuilder,
|
||||||
SlashCommandSubcommandGroupBuilder,
|
SlashCommandSubcommandGroupBuilder,
|
||||||
} from 'discord.js';
|
} from 'discord.js';
|
||||||
|
import { range } from 'lodash';
|
||||||
import { BlitzkriegRatingsLeaderboard } from '../../scripts/buildRatingsLeaderboard';
|
import { BlitzkriegRatingsLeaderboard } from '../../scripts/buildRatingsLeaderboard';
|
||||||
import * as Leaderboard from '../components/Leaderboard';
|
import * as Leaderboard from '../components/Leaderboard';
|
||||||
import TitleBar from '../components/TitleBar';
|
import TitleBar from '../components/TitleBar';
|
||||||
import Wrapper from '../components/Wrapper';
|
import Wrapper from '../components/Wrapper';
|
||||||
|
import { LEAGUES } from '../constants/leagues';
|
||||||
|
import { FIRST_ARCHIVED_RATINGS_SEASON } from '../constants/ratings';
|
||||||
import { REGION_NAMES_SHORT, Region } from '../constants/regions';
|
import { REGION_NAMES_SHORT, Region } from '../constants/regions';
|
||||||
import { getAccountInfo } from '../core/blitz/getAccountInfo';
|
import { getAccountInfo } from '../core/blitz/getAccountInfo';
|
||||||
import { getClanAccountInfo } from '../core/blitz/getClanAccountInfo';
|
import { getClanAccountInfo } from '../core/blitz/getClanAccountInfo';
|
||||||
import getRatingsInfo from '../core/blitz/getRatingsInfo';
|
import getRatingsInfo from '../core/blitz/getRatingsInfo';
|
||||||
|
import { getRatingsLeague } from '../core/blitz/getRatingsLeague';
|
||||||
|
import { getRatingsNeighbors } from '../core/blitz/getRatingsNeighbors';
|
||||||
import regionToRegionSubdomain from '../core/blitz/regionToRegionSubdomain';
|
import regionToRegionSubdomain from '../core/blitz/regionToRegionSubdomain';
|
||||||
|
import { getArchivedLatestSeasonNumber } from '../core/blitzkrieg/getArchivedLatestSeasonNumber';
|
||||||
import getArchivedRatingsInfo from '../core/blitzkrieg/getArchivedRatingsInfo';
|
import getArchivedRatingsInfo from '../core/blitzkrieg/getArchivedRatingsInfo';
|
||||||
import getMidnightLeaderboard, {
|
import { getArchivedRatingsLeaderboard } from '../core/blitzkrieg/getArchivedRatingsLeaderboard';
|
||||||
DATABASE_REPO,
|
import getArchivedRatingsMidnightLeaderboard from '../core/blitzkrieg/getArchivedRatingsMidnightLeaderboard';
|
||||||
} from '../core/blitzkrieg/getMidnightLeaderboard';
|
import { UserError } from '../core/blitzkrieg/userError';
|
||||||
import { octokit } from '../core/blitzkrieg/octokit';
|
|
||||||
import throwError from '../core/blitzkrieg/throwError';
|
|
||||||
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';
|
||||||
@@ -113,14 +117,6 @@ export interface RatingsNeighbors {
|
|||||||
neighbors: RatingsPlayer[];
|
neighbors: RatingsPlayer[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const LEAGUES = [
|
|
||||||
{ name: 'Diamond', minScore: 5000 },
|
|
||||||
{ name: 'Platinum', minScore: 4000 },
|
|
||||||
{ name: 'Gold', minScore: 3000 },
|
|
||||||
{ name: 'Silver', minScore: 2000 },
|
|
||||||
{ name: 'Bronze', minScore: 0 },
|
|
||||||
];
|
|
||||||
|
|
||||||
function addSubcommands(
|
function addSubcommands(
|
||||||
option: SlashCommandSubcommandGroupBuilder,
|
option: SlashCommandSubcommandGroupBuilder,
|
||||||
addOptions = (option: SlashCommandSubcommandBuilder) => option,
|
addOptions = (option: SlashCommandSubcommandBuilder) => option,
|
||||||
@@ -129,14 +125,14 @@ function addSubcommands(
|
|||||||
.addSubcommand((option) =>
|
.addSubcommand((option) =>
|
||||||
addOptions(
|
addOptions(
|
||||||
option
|
option
|
||||||
.setName('neighbours')
|
.setName('neighbors')
|
||||||
.setDescription('Your position and neighbours'),
|
.setDescription('Your position and neighbors'),
|
||||||
)
|
)
|
||||||
.addStringOption(addUsernameChoices)
|
.addStringOption(addUsernameChoices)
|
||||||
.addIntegerOption((option) =>
|
.addIntegerOption((option) =>
|
||||||
option
|
option
|
||||||
.setName('limit')
|
.setName('limit')
|
||||||
.setDescription('How many neighbours to display (default: 10)')
|
.setDescription('How many neighbors to display (default: 10)')
|
||||||
.setRequired(false)
|
.setRequired(false)
|
||||||
.setMinValue(5)
|
.setMinValue(5)
|
||||||
.setMaxValue(30),
|
.setMaxValue(30),
|
||||||
@@ -178,23 +174,17 @@ const noOngoingSeason = embedNegative(
|
|||||||
|
|
||||||
export const ratingsCommand = new Promise<CommandRegistryRaw>(
|
export const ratingsCommand = new Promise<CommandRegistryRaw>(
|
||||||
async (resolve) => {
|
async (resolve) => {
|
||||||
const seasonChoices = await octokit.repos
|
const latestArchivedSeasonNumber = await getArchivedLatestSeasonNumber();
|
||||||
.getContent({
|
const seasonChoices = range(
|
||||||
...DATABASE_REPO,
|
FIRST_ARCHIVED_RATINGS_SEASON,
|
||||||
path: 'com/ratings',
|
latestArchivedSeasonNumber + 1,
|
||||||
})
|
).map(
|
||||||
.then(({ data }) => {
|
(number) =>
|
||||||
if (!Array.isArray(data)) {
|
({
|
||||||
throw throwError('Archived ratings data is malformed');
|
name: `Season ${number}`,
|
||||||
}
|
value: `${number}`,
|
||||||
return data.map(
|
}) satisfies APIApplicationCommandOptionChoice<string>,
|
||||||
(folder) =>
|
);
|
||||||
({
|
|
||||||
name: `Season ${folder.name}`,
|
|
||||||
value: folder.name,
|
|
||||||
}) satisfies APIApplicationCommandOptionChoice<string>,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const command = new SlashCommandBuilder()
|
const command = new SlashCommandBuilder()
|
||||||
.setName('ratings')
|
.setName('ratings')
|
||||||
@@ -240,7 +230,7 @@ export const ratingsCommand = new Promise<CommandRegistryRaw>(
|
|||||||
| 'current';
|
| 'current';
|
||||||
const subcommand = interaction.options.getSubcommand(true) as
|
const subcommand = interaction.options.getSubcommand(true) as
|
||||||
| 'league'
|
| 'league'
|
||||||
| 'neighbours';
|
| 'neighbors';
|
||||||
const limit = interaction.options.getInteger('limit') ?? 10;
|
const limit = interaction.options.getInteger('limit') ?? 10;
|
||||||
const seasonOption = interaction.options.getString('season');
|
const seasonOption = interaction.options.getString('season');
|
||||||
const season = seasonOption ? parseInt(seasonOption) : undefined;
|
const season = seasonOption ? parseInt(seasonOption) : undefined;
|
||||||
@@ -262,18 +252,15 @@ export const ratingsCommand = new Promise<CommandRegistryRaw>(
|
|||||||
const region = interaction.options.getString('region') as Region;
|
const region = interaction.options.getString('region') as Region;
|
||||||
regionRatingsInfo = await (subcommandGroup === 'current'
|
regionRatingsInfo = await (subcommandGroup === 'current'
|
||||||
? getRatingsInfo(region)
|
? getRatingsInfo(region)
|
||||||
: getArchivedRatingsInfo(season!, region));
|
: getArchivedRatingsInfo(region, season!));
|
||||||
|
|
||||||
if (regionRatingsInfo.detail !== undefined) return noOngoingSeason;
|
if (regionRatingsInfo.detail !== undefined) return noOngoingSeason;
|
||||||
|
|
||||||
const regionSubdomain = regionToRegionSubdomain(region);
|
const regionSubdomain = regionToRegionSubdomain(region);
|
||||||
const result =
|
const result =
|
||||||
subcommandGroup === 'current'
|
subcommandGroup === 'current'
|
||||||
? await fetch(
|
? await getRatingsLeague(region, leagueIndex).then(
|
||||||
`https://${regionSubdomain}.wotblitz.com/en/api/rating-leaderboards/league/${leagueIndex}/top/`,
|
async (result) =>
|
||||||
)
|
|
||||||
.then((response) => response.json() as Promise<LeagueTop>)
|
|
||||||
.then(async ({ result }) =>
|
|
||||||
result.slice(0, limit).map(
|
result.slice(0, limit).map(
|
||||||
(player) =>
|
(player) =>
|
||||||
({
|
({
|
||||||
@@ -284,20 +271,9 @@ export const ratingsCommand = new Promise<CommandRegistryRaw>(
|
|||||||
nickname: player.nickname,
|
nickname: player.nickname,
|
||||||
}) satisfies SimplifiedPlayer,
|
}) satisfies SimplifiedPlayer,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: await octokit.repos
|
: await getArchivedRatingsLeaderboard(region, season!).then(
|
||||||
.getContent({
|
async (players) => {
|
||||||
...DATABASE_REPO,
|
|
||||||
path: `${region}/ratings/${season}/latest.json`,
|
|
||||||
})
|
|
||||||
.then(async ({ data }) => {
|
|
||||||
if (Array.isArray(data) || data.type !== 'file') {
|
|
||||||
throw throwError('Archived ratings data is malformed');
|
|
||||||
}
|
|
||||||
|
|
||||||
const players = (await fetch(data.download_url!).then(
|
|
||||||
(response) => response.json(),
|
|
||||||
)) as BlitzkriegRatingsLeaderboard;
|
|
||||||
const firstPlayerIndex =
|
const firstPlayerIndex =
|
||||||
leagueIndex === 0
|
leagueIndex === 0
|
||||||
? 0
|
? 0
|
||||||
@@ -328,15 +304,16 @@ export const ratingsCommand = new Promise<CommandRegistryRaw>(
|
|||||||
`Unknown Player ${player.id}`,
|
`Unknown Player ${player.id}`,
|
||||||
}) satisfies SimplifiedPlayer,
|
}) satisfies SimplifiedPlayer,
|
||||||
);
|
);
|
||||||
});
|
},
|
||||||
|
);
|
||||||
const leagueInfo = regionRatingsInfo.leagues[leagueIndex];
|
const leagueInfo = regionRatingsInfo.leagues[leagueIndex];
|
||||||
midnightLeaderboard =
|
midnightLeaderboard =
|
||||||
subcommandGroup === 'current'
|
subcommandGroup === 'current'
|
||||||
? await getMidnightLeaderboard(
|
? await getArchivedRatingsMidnightLeaderboard(
|
||||||
region,
|
region,
|
||||||
regionRatingsInfo.current_season,
|
regionRatingsInfo.current_season,
|
||||||
)
|
)
|
||||||
: await getMidnightLeaderboard(region, season!);
|
: await getArchivedRatingsMidnightLeaderboard(region, season!);
|
||||||
|
|
||||||
players = result;
|
players = result;
|
||||||
playersBefore = result ? result[0].position - 1 : 0;
|
playersBefore = result ? result[0].position - 1 : 0;
|
||||||
@@ -354,73 +331,43 @@ export const ratingsCommand = new Promise<CommandRegistryRaw>(
|
|||||||
regionRatingsInfo =
|
regionRatingsInfo =
|
||||||
subcommandGroup === 'current'
|
subcommandGroup === 'current'
|
||||||
? await getRatingsInfo(region)
|
? await getRatingsInfo(region)
|
||||||
: await octokit.repos
|
: await getArchivedRatingsInfo(region, season!);
|
||||||
.getContent({
|
|
||||||
...DATABASE_REPO,
|
|
||||||
path: `${region}/ratings/${season}/info.json`,
|
|
||||||
})
|
|
||||||
.then(async ({ data }) => {
|
|
||||||
if (Array.isArray(data) || data.type !== 'file') {
|
|
||||||
throw throwError('Archived ratings info is malformed');
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = fetch(data.download_url!);
|
|
||||||
return (await response.then((response) =>
|
|
||||||
response.json(),
|
|
||||||
)) as RatingsInfo;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (regionRatingsInfo.detail !== undefined) return noOngoingSeason;
|
if (regionRatingsInfo.detail !== undefined) return noOngoingSeason;
|
||||||
|
|
||||||
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 regionSubdomain = regionToRegionSubdomain(region);
|
|
||||||
const neighbors =
|
const neighbors =
|
||||||
subcommandGroup === 'current'
|
subcommandGroup === 'current'
|
||||||
? await fetch(
|
? await getRatingsNeighbors(
|
||||||
`https://${regionSubdomain}.wotblitz.com/en/api/rating-leaderboards/user/${id}/?neighbors=${Math.round(
|
region,
|
||||||
limit / 2,
|
id,
|
||||||
)}`,
|
Math.round(limit / 2),
|
||||||
|
).then((players) =>
|
||||||
|
players.neighbors.map(
|
||||||
|
(player) =>
|
||||||
|
({
|
||||||
|
id: player.spa_id,
|
||||||
|
position: player.number,
|
||||||
|
score: player.score,
|
||||||
|
clan: player.clan_tag,
|
||||||
|
nickname: player.nickname,
|
||||||
|
}) satisfies SimplifiedPlayer,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.then(
|
: await getArchivedRatingsLeaderboard(region, season!).then(
|
||||||
(response) => response.json() as Promise<RatingsNeighbors>,
|
async (players) => {
|
||||||
)
|
|
||||||
.then((players) =>
|
|
||||||
players.neighbors.map(
|
|
||||||
(player) =>
|
|
||||||
({
|
|
||||||
id: player.spa_id,
|
|
||||||
position: player.number,
|
|
||||||
score: player.score,
|
|
||||||
clan: player.clan_tag,
|
|
||||||
nickname: player.nickname,
|
|
||||||
}) satisfies SimplifiedPlayer,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: await octokit.repos
|
|
||||||
.getContent({
|
|
||||||
...DATABASE_REPO,
|
|
||||||
path: `${region}/ratings/${season}/latest.json`,
|
|
||||||
})
|
|
||||||
.then(async ({ data }) => {
|
|
||||||
if (Array.isArray(data) || data.type !== 'file') {
|
|
||||||
throw throwError(
|
|
||||||
'Archived ratings latest data is malformed',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = fetch(data.download_url!);
|
|
||||||
const players = (await response.then((response) =>
|
|
||||||
response.json(),
|
|
||||||
)) as BlitzkriegRatingsLeaderboard;
|
|
||||||
const playerIndex = players.findIndex(
|
const playerIndex = players.findIndex(
|
||||||
(player) => player.id === id,
|
(player) => player.id === id,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (playerIndex === -1) {
|
if (playerIndex === -1) {
|
||||||
throw throwError(
|
throw new UserError(
|
||||||
`${accountInfo.nickname} didn't player ratings in season ${season}`,
|
`${accountInfo.nickname} didn't player ratings in season ${season}`,
|
||||||
'This player did not participate in this season or did not get past calibration.',
|
{
|
||||||
|
cause:
|
||||||
|
'This player did not participate in this season or did not get past calibration.',
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -442,14 +389,15 @@ export const ratingsCommand = new Promise<CommandRegistryRaw>(
|
|||||||
clan: clanData[player.id]!.clan?.tag,
|
clan: clanData[player.id]!.clan?.tag,
|
||||||
nickname: clanData[player.id]!.account_name,
|
nickname: clanData[player.id]!.account_name,
|
||||||
}));
|
}));
|
||||||
});
|
},
|
||||||
|
);
|
||||||
midnightLeaderboard =
|
midnightLeaderboard =
|
||||||
subcommandGroup === 'current'
|
subcommandGroup === 'current'
|
||||||
? await getMidnightLeaderboard(
|
? await getArchivedRatingsMidnightLeaderboard(
|
||||||
region,
|
region,
|
||||||
regionRatingsInfo.current_season,
|
regionRatingsInfo.current_season,
|
||||||
)
|
)
|
||||||
: await getMidnightLeaderboard(region, season!);
|
: await getArchivedRatingsMidnightLeaderboard(region, season!);
|
||||||
|
|
||||||
if (clan && neighbors) titleNameDiscriminator = `[${clan.tag}]`;
|
if (clan && neighbors) titleNameDiscriminator = `[${clan.tag}]`;
|
||||||
|
|
||||||
@@ -460,7 +408,7 @@ export const ratingsCommand = new Promise<CommandRegistryRaw>(
|
|||||||
titleName = neighbors
|
titleName = neighbors
|
||||||
? accountInfo.nickname
|
? accountInfo.nickname
|
||||||
: `No Ongoing Season - ${REGION_NAMES_SHORT[region]}`;
|
: `No Ongoing Season - ${REGION_NAMES_SHORT[region]}`;
|
||||||
titleDescription = 'Ratings neighbours';
|
titleDescription = 'Ratings neighbors';
|
||||||
playersBefore = neighbors ? neighbors[0].position - 1 : 0;
|
playersBefore = neighbors ? neighbors[0].position - 1 : 0;
|
||||||
playersAfter =
|
playersAfter =
|
||||||
regionRatingsInfo.count - neighbors[neighbors.length - 1].position;
|
regionRatingsInfo.count - neighbors[neighbors.length - 1].position;
|
||||||
@@ -513,10 +461,12 @@ export const ratingsCommand = new Promise<CommandRegistryRaw>(
|
|||||||
{items && (
|
{items && (
|
||||||
<Leaderboard.Root>
|
<Leaderboard.Root>
|
||||||
{playersBefore > 0 && (
|
{playersBefore > 0 && (
|
||||||
<Leaderboard.Gap number={playersBefore} />
|
<Leaderboard.Gap message={`+ ${playersBefore} more`} />
|
||||||
)}
|
)}
|
||||||
{items}
|
{items}
|
||||||
{playersAfter > 0 && <Leaderboard.Gap number={playersAfter} />}
|
{playersAfter > 0 && (
|
||||||
|
<Leaderboard.Gap message={`+ ${playersAfter} more`} />
|
||||||
|
)}
|
||||||
</Leaderboard.Root>
|
</Leaderboard.Root>
|
||||||
)}
|
)}
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { SlashCommandBuilder } from 'discord.js';
|
|||||||
import markdownEscape from 'markdown-escape';
|
import markdownEscape from 'markdown-escape';
|
||||||
import { REGION_NAMES, Region } from '../constants/regions';
|
import { REGION_NAMES, Region } from '../constants/regions';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../constants/wargamingApplicationID';
|
import { WARGAMING_APPLICATION_ID } from '../constants/wargamingApplicationID';
|
||||||
import fetchBlitz from '../core/blitz/fetchWargaming';
|
import fetchBlitz from '../core/blitz/fetchBlitz';
|
||||||
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 embedInfo from '../core/discord/embedInfo';
|
import embedInfo from '../core/discord/embedInfo';
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { SlashCommandBuilder } from 'discord.js';
|
|||||||
import markdownEscape from 'markdown-escape';
|
import markdownEscape from 'markdown-escape';
|
||||||
import { REGION_NAMES, Region } from '../constants/regions';
|
import { REGION_NAMES, Region } from '../constants/regions';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../constants/wargamingApplicationID';
|
import { WARGAMING_APPLICATION_ID } from '../constants/wargamingApplicationID';
|
||||||
import fetchBlitz from '../core/blitz/fetchWargaming';
|
import fetchBlitz from '../core/blitz/fetchBlitz';
|
||||||
import {
|
import {
|
||||||
AccountList,
|
AccountList,
|
||||||
usernamePattern,
|
usernamePattern,
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
import { SlashCommandBuilder } from 'discord.js';
|
|
||||||
import { CommandRegistry } from '../events/interactionCreate';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: @deprecated
|
|
||||||
*/
|
|
||||||
export const todayCommand: CommandRegistry = {
|
|
||||||
inProduction: true,
|
|
||||||
inPublic: true,
|
|
||||||
|
|
||||||
command: new SlashCommandBuilder()
|
|
||||||
.setName('today')
|
|
||||||
.setDescription('This command has been renamed to `/breakdown today`'),
|
|
||||||
|
|
||||||
handler() {
|
|
||||||
return '# This command has been renamed to `/breakdown today`\n`/today` now comes with filters, periods and more! Use `/breakdown today` for same old functionality but also do checkout the other options.';
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -15,9 +15,18 @@ export default function GenericStatsRow({ stat }: GenericStatsRowProps) {
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span style={{ color: theme.colors.textLowContrast, fontSize: 16 }}>
|
<span
|
||||||
|
style={{
|
||||||
|
color: theme.colors.textLowContrast,
|
||||||
|
fontSize: 16,
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
maxWidth: 128,
|
||||||
|
overflow: 'hidden',
|
||||||
|
}}
|
||||||
|
>
|
||||||
{stat[0]}
|
{stat[0]}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
style={{
|
style={{
|
||||||
color: theme.colors.textHighContrast,
|
color: theme.colors.textHighContrast,
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { theme } from '../../../stitches.config';
|
import { theme } from '../../../stitches.config';
|
||||||
|
|
||||||
export interface GapProps {
|
export interface GapProps {
|
||||||
number: number;
|
message: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Gap({ number }: GapProps) {
|
export function Gap({ message: number }: GapProps) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@@ -22,7 +22,7 @@ export function Gap({ number }: GapProps) {
|
|||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
+ {number.toLocaleString()} more
|
{number.toLocaleString()}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
|
import { ComponentProps } from 'react';
|
||||||
import { RatingsReward } from '../../../commands/ratings';
|
import { RatingsReward } from '../../../commands/ratings';
|
||||||
import { theme } from '../../../stitches.config';
|
import { theme } from '../../../stitches.config';
|
||||||
|
|
||||||
export interface ItemProps {
|
export interface ItemProps extends ComponentProps<'div'> {
|
||||||
position: number;
|
position: number;
|
||||||
deltaPosition?: number;
|
deltaPosition?: number;
|
||||||
score: number;
|
score?: number;
|
||||||
deltaScore?: number;
|
deltaScore?: number;
|
||||||
reward?: RatingsReward;
|
reward?: RatingsReward;
|
||||||
nickname: string;
|
nickname: string;
|
||||||
@@ -25,10 +26,13 @@ export function Item({
|
|||||||
nickname,
|
nickname,
|
||||||
clan,
|
clan,
|
||||||
highlight,
|
highlight,
|
||||||
|
...props
|
||||||
}: ItemProps) {
|
}: ItemProps) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
{...props}
|
||||||
style={{
|
style={{
|
||||||
|
cursor: 'pointer',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
padding: 8,
|
padding: 8,
|
||||||
backgroundColor: highlight
|
backgroundColor: highlight
|
||||||
@@ -185,16 +189,18 @@ export function Item({
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<span
|
{score !== undefined && (
|
||||||
style={{
|
<span
|
||||||
color: highlight
|
style={{
|
||||||
? theme.colors.textHighContrast_blue
|
color: highlight
|
||||||
: theme.colors.textHighContrast,
|
? theme.colors.textHighContrast_blue
|
||||||
fontSize: 16,
|
: theme.colors.textHighContrast,
|
||||||
}}
|
fontSize: 16,
|
||||||
>
|
}}
|
||||||
{score.toLocaleString()}
|
>
|
||||||
</span>
|
{score.toLocaleString()}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,24 +1,31 @@
|
|||||||
|
import { Flex, Theme } from '@radix-ui/themes';
|
||||||
import { ComponentProps } from 'react';
|
import { ComponentProps } from 'react';
|
||||||
|
|
||||||
|
interface PageWrapperProps extends ComponentProps<typeof Flex> {
|
||||||
|
color?: ComponentProps<typeof Theme>['accentColor'];
|
||||||
|
}
|
||||||
|
|
||||||
export default function PageWrapper({
|
export default function PageWrapper({
|
||||||
style,
|
style,
|
||||||
|
color,
|
||||||
...props
|
...props
|
||||||
}: ComponentProps<'div'>) {
|
}: PageWrapperProps) {
|
||||||
return (
|
return (
|
||||||
<div
|
<Theme accentColor={color}>
|
||||||
style={{
|
<Flex
|
||||||
width: '100%',
|
{...props}
|
||||||
maxWidth: 800,
|
direction="column"
|
||||||
margin: 'auto',
|
gap="4"
|
||||||
display: 'flex',
|
style={{
|
||||||
padding: 16,
|
width: '100%',
|
||||||
flexDirection: 'column',
|
maxWidth: 800,
|
||||||
gap: 16,
|
margin: 'auto',
|
||||||
boxSizing: 'border-box',
|
padding: 16,
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
|
||||||
...style,
|
...style,
|
||||||
}}
|
}}
|
||||||
{...props}
|
/>
|
||||||
/>
|
</Theme>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { slateDarkA } from '@radix-ui/colors';
|
||||||
|
import { keyframes, style } from '@vanilla-extract/css';
|
||||||
|
|
||||||
|
const animation = keyframes({
|
||||||
|
'0%': { backgroundColor: slateDarkA.slateA2 },
|
||||||
|
'50%': { backgroundColor: slateDarkA.slateA3 },
|
||||||
|
'100%': { backgroundColor: slateDarkA.slateA2 },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const skeleton = style({
|
||||||
|
borderRadius: 8,
|
||||||
|
animation: `${animation} 2s ease-in-out infinite`,
|
||||||
|
});
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { ComponentProps } from 'react';
|
||||||
|
import { skeleton } from './index.css';
|
||||||
|
|
||||||
|
export function Skeleton({ className, ...props }: ComponentProps<'div'>) {
|
||||||
|
return <div {...props} className={`${className} ${skeleton}`} />;
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export const LEAGUES = [
|
||||||
|
{ name: 'Diamond', minScore: 5000 },
|
||||||
|
{ name: 'Platinum', minScore: 4000 },
|
||||||
|
{ name: 'Gold', minScore: 3000 },
|
||||||
|
{ name: 'Silver', minScore: 2000 },
|
||||||
|
{ name: 'Bronze', minScore: 0 },
|
||||||
|
];
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
export const NATION_IDS: Record<string, number> = {
|
||||||
|
ussr: 0,
|
||||||
|
germany: 1,
|
||||||
|
usa: 2,
|
||||||
|
china: 3,
|
||||||
|
france: 4,
|
||||||
|
uk: 5,
|
||||||
|
japan: 6,
|
||||||
|
other: 7,
|
||||||
|
european: 8,
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export const FIRST_ARCHIVED_RATINGS_SEASON = 49;
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
export type Region = 'com' | 'eu' | 'asia';
|
export const REGIONS = ['com', 'eu', 'asia'] as const;
|
||||||
|
|
||||||
|
export type Region = (typeof REGIONS)[number];
|
||||||
export type RegionSubdomain = 'na' | 'eu' | 'asia';
|
export type RegionSubdomain = 'na' | 'eu' | 'asia';
|
||||||
|
|
||||||
export const REGION_NAMES: Record<Region, string> = {
|
export const REGION_NAMES: Record<Region, string> = {
|
||||||
@@ -11,7 +13,7 @@ export const REGION_NAMES: Record<Region, string> = {
|
|||||||
export const REGION_NAMES_SHORT: Record<Region, string> = {
|
export const REGION_NAMES_SHORT: Record<Region, string> = {
|
||||||
com: 'NA',
|
com: 'NA',
|
||||||
eu: 'EU',
|
eu: 'EU',
|
||||||
asia: 'AS',
|
asia: 'ASIA',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const REGION_SUBDOMAIN_NAMES: Record<RegionSubdomain, string> = {
|
export const REGION_SUBDOMAIN_NAMES: Record<RegionSubdomain, string> = {
|
||||||
|
|||||||
@@ -9,13 +9,12 @@ export const TOOLS: Tool[] = [
|
|||||||
{
|
{
|
||||||
id: 'session',
|
id: 'session',
|
||||||
title: 'Session tracker',
|
title: 'Session tracker',
|
||||||
description: 'Track your performance in a session',
|
description: 'Live session performance tracker',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'ratings',
|
id: 'ratings',
|
||||||
title: 'Ratings',
|
title: 'Ratings',
|
||||||
description: 'Current and old season leader boards',
|
description: 'Current and archived ratings leaderboards',
|
||||||
disabled: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'tankopedia',
|
id: 'tankopedia',
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
export type BlitzResponse<Data extends object> =
|
||||||
|
| {
|
||||||
|
status: 'error';
|
||||||
|
error: { field: string; message: string; code: 402; value: null };
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
status: 'ok';
|
||||||
|
data: Data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const MAX_CALLS_PER_SECOND = 10;
|
||||||
|
|
||||||
|
const queue: { url: string; resolve: (data: object) => void }[] = [];
|
||||||
|
let inProgress = 0;
|
||||||
|
|
||||||
|
async function manageQueue() {
|
||||||
|
if (queue.length > 0 && inProgress < MAX_CALLS_PER_SECOND) {
|
||||||
|
inProgress++;
|
||||||
|
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
inProgress--;
|
||||||
|
manageQueue();
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
const { url, resolve } = queue.shift()!;
|
||||||
|
const data = (await fetch(url)
|
||||||
|
.then((response) => response.json())
|
||||||
|
.catch((error) => ({ status: 'error', error }))) as BlitzResponse<
|
||||||
|
typeof resolve
|
||||||
|
>;
|
||||||
|
|
||||||
|
if (data.status === 'ok') {
|
||||||
|
resolve(data.data);
|
||||||
|
} else {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
inProgress--;
|
||||||
|
manageQueue();
|
||||||
|
|
||||||
|
throw new Error(`Wargaming response error status: "${data.status}"`, {
|
||||||
|
cause: `Message: "${data.error.message}"\nURL: "${url}"`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function fetchBlitz<Data extends object>(url: string) {
|
||||||
|
return new Promise<Data>((resolve) => {
|
||||||
|
queue.push({
|
||||||
|
url,
|
||||||
|
resolve(data) {
|
||||||
|
resolve(data as Data);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
manageQueue();
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import throwError from '../blitzkrieg/throwError';
|
|
||||||
|
|
||||||
export type BlitzResponse<Data extends object> =
|
|
||||||
| {
|
|
||||||
status: 'error';
|
|
||||||
error: { field: string; message: string; code: 402; value: null };
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
status: 'ok';
|
|
||||||
data: Data;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default async function fetchBlitz<Data extends object>(url: string) {
|
|
||||||
const data = (await fetch(url).then((response) =>
|
|
||||||
response.json(),
|
|
||||||
)) as BlitzResponse<Data>;
|
|
||||||
|
|
||||||
if (data.status === 'ok') {
|
|
||||||
return data.data;
|
|
||||||
} else {
|
|
||||||
throw throwError(
|
|
||||||
`Wargaming response error status:"${data.status}"`,
|
|
||||||
`Message: "${data.error.message}"\nURL: "${url}"`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Region } from '../../constants/regions';
|
import { Region } from '../../constants/regions';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
|
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
|
||||||
import fetchBlitz from './fetchWargaming';
|
import fetchBlitz from './fetchBlitz';
|
||||||
import { normalizeIds } from './normalizeIds';
|
import { normalizeIds } from './normalizeIds';
|
||||||
|
|
||||||
interface IndividualAccountAchievements {
|
interface IndividualAccountAchievements {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Region } from '../../constants/regions';
|
import { Region } from '../../constants/regions';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
|
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
|
||||||
import fetchBlitz from './fetchWargaming';
|
import fetchBlitz from './fetchBlitz';
|
||||||
import { normalizeIds } from './normalizeIds';
|
import { normalizeIds } from './normalizeIds';
|
||||||
|
|
||||||
export interface AllStats {
|
export interface AllStats {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Region } from '../../constants/regions';
|
import { Region } from '../../constants/regions';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
|
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
|
||||||
import fetchBlitz from './fetchWargaming';
|
import fetchBlitz from './fetchBlitz';
|
||||||
import { normalizeExtra } from './normalizeExtra';
|
import { normalizeExtra } from './normalizeExtra';
|
||||||
import { normalizeIds } from './normalizeIds';
|
import { normalizeIds } from './normalizeIds';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Region } from '../../constants/regions';
|
import { Region } from '../../constants/regions';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
|
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
|
||||||
import fetchBlitz from './fetchWargaming';
|
import fetchBlitz from './fetchBlitz';
|
||||||
import { normalizeIds } from './normalizeIds';
|
import { normalizeIds } from './normalizeIds';
|
||||||
|
|
||||||
export interface IndividualClanInfo {
|
export interface IndividualClanInfo {
|
||||||
@@ -41,7 +41,7 @@ export async function getClanInfo<
|
|||||||
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>(
|
const object = await fetchBlitz<ClanInfo>(
|
||||||
`https://api.wotblitz.${region}/wotb/clans/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${normalizeIds(
|
`https://api.wotblitz.${region}/wotb/clans/info/?application_id=${WARGAMING_APPLICATION_ID}&clan_id=${normalizeIds(
|
||||||
ids,
|
ids,
|
||||||
)}`,
|
)}`,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
import { RatingsInfo } from '../../commands/ratings';
|
import { RatingsInfo } from '../../commands/ratings';
|
||||||
import { Region } from '../../constants/regions';
|
import { Region } from '../../constants/regions';
|
||||||
|
import { context } from '../blitzkrieg/context';
|
||||||
|
import { patientFetch } from '../blitzkrieg/patientFetch';
|
||||||
import regionToRegionSubdomain from './regionToRegionSubdomain';
|
import regionToRegionSubdomain from './regionToRegionSubdomain';
|
||||||
|
|
||||||
export default async function getRatingsInfo(region: Region) {
|
export default async function getRatingsInfo(region: Region) {
|
||||||
const response = await fetch(
|
const response = await patientFetch(
|
||||||
`https://${regionToRegionSubdomain(
|
context === 'website'
|
||||||
region,
|
? `/api/${region}/ratings/current/info`
|
||||||
)}.wotblitz.com/en/api/rating-leaderboards/season/`,
|
: `https://${regionToRegionSubdomain(
|
||||||
|
region,
|
||||||
|
)}.wotblitz.com/en/api/rating-leaderboards/season/`,
|
||||||
|
undefined,
|
||||||
|
{ cache: 'no-store' },
|
||||||
);
|
);
|
||||||
|
|
||||||
return (await response.json()) as Promise<RatingsInfo>;
|
return (await response.json()) as Promise<RatingsInfo>;
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { LeagueTop, RatingsPlayer } from '../../commands/ratings';
|
||||||
|
import { Region } from '../../constants/regions';
|
||||||
|
import { context } from '../blitzkrieg/context';
|
||||||
|
import { patientFetch } from '../blitzkrieg/patientFetch';
|
||||||
|
import regionToRegionSubdomain from './regionToRegionSubdomain';
|
||||||
|
|
||||||
|
export async function getRatingsLeague(
|
||||||
|
region: Region,
|
||||||
|
league: number,
|
||||||
|
usePatientFetcher = false,
|
||||||
|
) {
|
||||||
|
const fetcher = (url: string) =>
|
||||||
|
usePatientFetcher
|
||||||
|
? patientFetch(url, undefined, { cache: 'no-store' })
|
||||||
|
: fetch(url, { cache: 'no-store' });
|
||||||
|
const response = await fetcher(
|
||||||
|
context === 'website'
|
||||||
|
? `/api/${region}/ratings/current/league/${league}`
|
||||||
|
: `https://${regionToRegionSubdomain(
|
||||||
|
region,
|
||||||
|
)}.wotblitz.com/en/api/rating-leaderboards/league/${league}/top/`,
|
||||||
|
);
|
||||||
|
const json = await response.json();
|
||||||
|
|
||||||
|
return context === 'website'
|
||||||
|
? (json as RatingsPlayer[])
|
||||||
|
: (json as LeagueTop).result;
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { RatingsNeighbors } from '../../commands/ratings';
|
||||||
|
import { Region } from '../../constants/regions';
|
||||||
|
import { patientFetch } from '../blitzkrieg/patientFetch';
|
||||||
|
import { withCORSProxy } from '../blitzkrieg/withCORSProxy';
|
||||||
|
import regionToRegionSubdomain from './regionToRegionSubdomain';
|
||||||
|
|
||||||
|
export async function getRatingsNeighbors(
|
||||||
|
region: Region,
|
||||||
|
id: number,
|
||||||
|
count: number,
|
||||||
|
usePatientFetcher = false,
|
||||||
|
) {
|
||||||
|
const fetcher = (url: string) =>
|
||||||
|
usePatientFetcher
|
||||||
|
? patientFetch(url, undefined, { cache: 'no-store' })
|
||||||
|
: fetch(url, { cache: 'no-store' });
|
||||||
|
const response = await fetcher(
|
||||||
|
withCORSProxy(
|
||||||
|
`https://${regionToRegionSubdomain(
|
||||||
|
region,
|
||||||
|
)}.wotblitz.com/en/api/rating-leaderboards/user/${id}/?neighbors=${count}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
const json = (await response.json()) as RatingsNeighbors;
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Region } from '../../constants/regions';
|
import { Region } from '../../constants/regions';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
|
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
|
||||||
import { TanksStats } from '../../types/tanksStats';
|
import { TanksStats } from '../../types/tanksStats';
|
||||||
import throwError from '../blitzkrieg/throwError';
|
import { UserError } from '../blitzkrieg/userError';
|
||||||
import fetchBlitz from './fetchWargaming';
|
import fetchBlitz from './fetchBlitz';
|
||||||
|
|
||||||
export default async function getTankStats(region: Region, id: number) {
|
export default async function getTankStats(region: Region, id: number) {
|
||||||
const tankStats = await fetchBlitz<TanksStats>(
|
const tankStats = await fetchBlitz<TanksStats>(
|
||||||
@@ -10,10 +10,10 @@ export default async function getTankStats(region: Region, id: number) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (tankStats[id] === null) {
|
if (tankStats[id] === null) {
|
||||||
throw throwError(
|
throw new UserError('No tank stats available', {
|
||||||
'No tank stats available',
|
cause:
|
||||||
"This player doesn't have any stats for tanks available. This may not be the player you are looking for.",
|
"This player doesn't have any stats for tanks available. This may not be the player you are looking for.",
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return tankStats[id];
|
return tankStats[id];
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { go } from 'fuzzysort';
|
import { go } from 'fuzzysort';
|
||||||
import throwError from '../blitzkrieg/throwError';
|
import { UserError } from '../blitzkrieg/userError';
|
||||||
import { TANKS, tankopedia } from '../blitzstars/tankopedia';
|
import { TANKS, tankopedia } from '../blitzstars/tankopedia';
|
||||||
|
|
||||||
export default async function resolveTankId(tank: string | number) {
|
export default async function resolveTankId(tank: string | number) {
|
||||||
@@ -12,10 +12,9 @@ export default async function resolveTankId(tank: string | number) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (searchResult.length === 0) {
|
if (searchResult.length === 0) {
|
||||||
throw throwError(
|
throw new UserError('Tank not found', {
|
||||||
'Tank not found',
|
cause: `Could not find tank by the name "${tank}".`,
|
||||||
`Could not find tank by the name "${tank}".`,
|
});
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
return searchResult[0].obj.tank_id;
|
return searchResult[0].obj.tank_id;
|
||||||
}
|
}
|
||||||
@@ -23,10 +22,9 @@ export default async function resolveTankId(tank: string | number) {
|
|||||||
if ((await tankopedia)[number]) {
|
if ((await tankopedia)[number]) {
|
||||||
return number;
|
return number;
|
||||||
} else {
|
} else {
|
||||||
throw throwError(
|
throw new UserError('Tank not found', {
|
||||||
'Tank not found',
|
cause: `Could not find tank by the ID "${number}".`,
|
||||||
`Could not find tank by the ID "${number}".`,
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Region } from '../../constants/regions';
|
import { Region } from '../../constants/regions';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
|
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
|
||||||
import { Clan, ClanList } from '../../types/clanList';
|
import { Clan, ClanList } from '../../types/clanList';
|
||||||
import fetchBlitz from './fetchWargaming';
|
import fetchBlitz from './fetchBlitz';
|
||||||
|
|
||||||
export type ClanListWithRegion = (Clan & {
|
export type ClanListWithRegion = (Clan & {
|
||||||
region: 'com' | 'eu' | 'asia';
|
region: 'com' | 'eu' | 'asia';
|
||||||
@@ -9,43 +9,45 @@ export type ClanListWithRegion = (Clan & {
|
|||||||
|
|
||||||
export default async function searchClansAcrossRegions(
|
export default async function searchClansAcrossRegions(
|
||||||
search: string,
|
search: string,
|
||||||
limit = 9,
|
limit = 25,
|
||||||
) {
|
) {
|
||||||
const trimmed = search.trim();
|
const trimmed = search.trim();
|
||||||
if (trimmed.length < 3 && trimmed.length > 100) return [];
|
if (trimmed.length < 3 && trimmed.length > 100) return [];
|
||||||
const normalizedLimit = Math.round(limit / 3);
|
const normalizedLimit = Math.floor(limit / 3);
|
||||||
|
|
||||||
|
// TODO: remove splice once wargaming fixes the functionality of the limit felid
|
||||||
|
|
||||||
return (
|
return (
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
fetchBlitz<ClanList>(
|
fetchBlitz<ClanList>(
|
||||||
`https://api.wotblitz.com/wotb/clans/list/?application_id=${WARGAMING_APPLICATION_ID}&search=${search}&limit=${normalizedLimit}`,
|
`https://api.wotblitz.com/wotb/clans/list/?application_id=${WARGAMING_APPLICATION_ID}&search=${search}&limit=${normalizedLimit}`,
|
||||||
).then(
|
).then((value) =>
|
||||||
(value) =>
|
value
|
||||||
value &&
|
.map((account) => ({
|
||||||
value.map((account) => ({
|
|
||||||
...account,
|
...account,
|
||||||
region: 'com' as Region,
|
region: 'com' as Region,
|
||||||
})),
|
}))
|
||||||
|
.splice(0, normalizedLimit),
|
||||||
),
|
),
|
||||||
fetchBlitz<ClanList>(
|
fetchBlitz<ClanList>(
|
||||||
`https://api.wotblitz.eu/wotb/clans/list/?application_id=${WARGAMING_APPLICATION_ID}&search=${search}&limit=${normalizedLimit}`,
|
`https://api.wotblitz.eu/wotb/clans/list/?application_id=${WARGAMING_APPLICATION_ID}&search=${search}&limit=${normalizedLimit}`,
|
||||||
).then(
|
).then((value) =>
|
||||||
(value) =>
|
value
|
||||||
value &&
|
.map((account) => ({
|
||||||
value.map((account) => ({
|
|
||||||
...account,
|
...account,
|
||||||
region: 'eu' as Region,
|
region: 'eu' as Region,
|
||||||
})),
|
}))
|
||||||
|
.splice(0, normalizedLimit),
|
||||||
),
|
),
|
||||||
fetchBlitz<ClanList>(
|
fetchBlitz<ClanList>(
|
||||||
`https://api.wotblitz.asia/wotb/clans/list/?application_id=${WARGAMING_APPLICATION_ID}&search=${search}&limit=${normalizedLimit}`,
|
`https://api.wotblitz.asia/wotb/clans/list/?application_id=${WARGAMING_APPLICATION_ID}&search=${search}&limit=${normalizedLimit}`,
|
||||||
).then(
|
).then((value) =>
|
||||||
(value) =>
|
value
|
||||||
value &&
|
.map((account) => ({
|
||||||
value.map((account) => ({
|
|
||||||
...account,
|
...account,
|
||||||
region: 'asia' as Region,
|
region: 'asia' as Region,
|
||||||
})),
|
}))
|
||||||
|
.splice(0, normalizedLimit),
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { Region } from '../../constants/regions';
|
||||||
|
import { context } from '../blitzkrieg/context';
|
||||||
|
import regionToRegionSubdomain from './regionToRegionSubdomain';
|
||||||
|
|
||||||
|
type CurrentRatingsPlayerSearch = Record<
|
||||||
|
number,
|
||||||
|
{
|
||||||
|
spa_id: number;
|
||||||
|
nickname: string;
|
||||||
|
clan_tag: string;
|
||||||
|
} & (
|
||||||
|
| {
|
||||||
|
skip: true;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
skip: false;
|
||||||
|
mmr: number;
|
||||||
|
season_number: number;
|
||||||
|
calibrationBattlesLeft: number;
|
||||||
|
number: number;
|
||||||
|
updated_at: string;
|
||||||
|
neighbors: [];
|
||||||
|
}
|
||||||
|
)
|
||||||
|
>;
|
||||||
|
|
||||||
|
export async function searchCurrentRatingsPlayers(
|
||||||
|
region: Region,
|
||||||
|
name: string,
|
||||||
|
) {
|
||||||
|
const encodedSearch = encodeURIComponent(name);
|
||||||
|
const response = await fetch(
|
||||||
|
context === 'website'
|
||||||
|
? `/api/${region}/ratings/current/search/${encodedSearch}`
|
||||||
|
: `https://${regionToRegionSubdomain(
|
||||||
|
region,
|
||||||
|
)}.wotblitz.com/en/api/rating-leaderboards/search/?prefix=${encodedSearch}`,
|
||||||
|
{ cache: 'no-store' },
|
||||||
|
);
|
||||||
|
const accountList = (await response.json()) as CurrentRatingsPlayerSearch;
|
||||||
|
|
||||||
|
return accountList;
|
||||||
|
}
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
import { Region } from '../../constants/regions';
|
import { Region } from '../../constants/regions';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
|
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
|
||||||
import fetchBlitz from './fetchWargaming';
|
import fetchBlitz from './fetchBlitz';
|
||||||
|
|
||||||
interface Account {
|
export interface AccountListItem {
|
||||||
nickname: string;
|
nickname: string;
|
||||||
account_id: number;
|
account_id: number;
|
||||||
}
|
}
|
||||||
export type AccountList = Account[];
|
export type AccountList = AccountListItem[];
|
||||||
export type AccountListWithServer = (Account & {
|
export type AccountListWithServer = (AccountListItem & {
|
||||||
region: Region;
|
region: Region;
|
||||||
})[];
|
})[];
|
||||||
|
|
||||||
@@ -16,10 +16,10 @@ export const usernamePatternWithoutPosition = /[a-zA-Z0-9_]{3,24}/;
|
|||||||
|
|
||||||
export default async function searchPlayersAcrossRegions(
|
export default async function searchPlayersAcrossRegions(
|
||||||
search: string,
|
search: string,
|
||||||
limit = 9,
|
limit = 25,
|
||||||
) {
|
) {
|
||||||
const trimmedSearch = search.trim();
|
const trimmedSearch = search.trim();
|
||||||
const normalizedLimit = Math.round(limit / 3);
|
const normalizedLimit = Math.floor(limit / 3);
|
||||||
const encodedSearch = encodeURIComponent(trimmedSearch);
|
const encodedSearch = encodeURIComponent(trimmedSearch);
|
||||||
|
|
||||||
if (usernamePattern.test(trimmedSearch)) {
|
if (usernamePattern.test(trimmedSearch)) {
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
|
||||||
|
import fetchBlitz from './fetchBlitz';
|
||||||
|
|
||||||
|
export interface TankopediaInfo {
|
||||||
|
achievement_sections: Record<string, { name: string; order: number }>;
|
||||||
|
tanks_updated_at: number;
|
||||||
|
languages: Record<string, string>;
|
||||||
|
vehicle_types: Record<string, string>;
|
||||||
|
vehicle_nations: Record<string, string>;
|
||||||
|
game_version: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const tankopediaInfo = fetchBlitz<TankopediaInfo>(
|
||||||
|
`https://api.wotblitz.com/wotb/encyclopedia/info/?application_id=${WARGAMING_APPLICATION_ID}`,
|
||||||
|
);
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
type Context = 'website' | 'bot';
|
type Context = 'website' | 'server';
|
||||||
|
|
||||||
export const context: Context =
|
export const context: Context =
|
||||||
typeof window === 'undefined' ? 'bot' : 'website';
|
typeof window === 'undefined' ? 'server' : 'website';
|
||||||
@@ -1,10 +1,7 @@
|
|||||||
import { TREE_TYPE_NAMES } from '../../components/Tanks';
|
import { TREE_TYPE_NAMES } from '../../components/Tanks';
|
||||||
import resolveTankName from '../blitz/resolveTankName';
|
import resolveTankName from '../blitz/resolveTankName';
|
||||||
import {
|
import { tankopediaInfo } from '../blitz/tankopediaInfo';
|
||||||
TIER_ROMAN_NUMERALS,
|
import { TIER_ROMAN_NUMERALS, Tier } from '../blitzstars/tankopedia';
|
||||||
Tier,
|
|
||||||
tankopediaInfo,
|
|
||||||
} from '../blitzstars/tankopedia';
|
|
||||||
import { StatFilters } from '../statistics/filterStats';
|
import { StatFilters } from '../statistics/filterStats';
|
||||||
|
|
||||||
export async function filtersToDescription({
|
export async function filtersToDescription({
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import getRatingsInfo from '../blitz/getRatingsInfo';
|
||||||
|
|
||||||
|
let cachedLatestArchivedSeasonNumber: number | null = null;
|
||||||
|
|
||||||
|
export async function getArchivedLatestSeasonNumber() {
|
||||||
|
const ratingsInfo = await getRatingsInfo('com');
|
||||||
|
|
||||||
|
if (ratingsInfo.detail) {
|
||||||
|
const comURL = (
|
||||||
|
(await fetch(
|
||||||
|
'https://api.github.com/repos/tresabhi/blitzkrieg-db/git/trees/main',
|
||||||
|
).then((response) => response.json())) as {
|
||||||
|
tree: { path: string; url: string }[];
|
||||||
|
}
|
||||||
|
).tree.find(({ path }) => path === 'com')!.url;
|
||||||
|
const ratingsURL = (
|
||||||
|
(await fetch(comURL).then((response) => response.json())) as {
|
||||||
|
tree: [{ url: string }];
|
||||||
|
}
|
||||||
|
).tree[0].url;
|
||||||
|
const latestSeason = Math.max(
|
||||||
|
...(
|
||||||
|
(await fetch(ratingsURL).then((response) => {
|
||||||
|
response.json();
|
||||||
|
})) as { tree: { path: string }[] }
|
||||||
|
).tree.map(({ path }) => parseInt(path)),
|
||||||
|
);
|
||||||
|
|
||||||
|
return latestSeason;
|
||||||
|
} else {
|
||||||
|
return ratingsInfo.current_season - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,7 @@
|
|||||||
import { RatingsInfo } from '../../commands/ratings';
|
import { RatingsInfo } from '../../commands/ratings';
|
||||||
import { Region } from '../../constants/regions';
|
import { Region } from '../../constants/regions';
|
||||||
import { DATABASE_REPO } from './getMidnightLeaderboard';
|
|
||||||
import { octokit } from './octokit';
|
|
||||||
import throwError from './throwError';
|
|
||||||
|
|
||||||
const ARCHIVED_RATINGS_CACHE: Record<
|
const INFO_CACHE: Record<
|
||||||
Region,
|
Region,
|
||||||
Record<
|
Record<
|
||||||
number,
|
number,
|
||||||
@@ -19,27 +16,21 @@ const ARCHIVED_RATINGS_CACHE: Record<
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default async function getArchivedRatingsInfo(
|
export default async function getArchivedRatingsInfo(
|
||||||
season: number,
|
|
||||||
region: Region,
|
region: Region,
|
||||||
|
season: number,
|
||||||
) {
|
) {
|
||||||
if (ARCHIVED_RATINGS_CACHE[region][season]) {
|
if (INFO_CACHE[region][season]) {
|
||||||
return ARCHIVED_RATINGS_CACHE[region][season];
|
return INFO_CACHE[region][season];
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data } = await octokit.repos.getContent({
|
const response = await fetch(
|
||||||
...DATABASE_REPO,
|
`https://raw.githubusercontent.com/tresabhi/blitzkrieg-db/main/${region}/ratings/${season}/info.json`,
|
||||||
path: `${region}/ratings/${season}/info.json`,
|
);
|
||||||
});
|
const jsonContent = (await response.json()) as RatingsInfo & {
|
||||||
|
|
||||||
if (Array.isArray(data) || data.type !== 'file')
|
|
||||||
throw throwError('Archived ratings info is malformed');
|
|
||||||
|
|
||||||
const content = Buffer.from(data.content, 'base64').toString();
|
|
||||||
const jsonContent = JSON.parse(content) as RatingsInfo & {
|
|
||||||
detail: undefined;
|
detail: undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
ARCHIVED_RATINGS_CACHE[region][season] = jsonContent;
|
INFO_CACHE[region][season] = jsonContent;
|
||||||
|
|
||||||
return jsonContent;
|
return jsonContent;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { BlitzkriegRatingsLeaderboard } from '../../../scripts/buildRatingsLeaderboard';
|
||||||
|
import { Region } from '../../constants/regions';
|
||||||
|
|
||||||
|
const LEADERBOARD_CACHE: Record<
|
||||||
|
Region,
|
||||||
|
Record<number, BlitzkriegRatingsLeaderboard>
|
||||||
|
> = {
|
||||||
|
asia: {},
|
||||||
|
eu: {},
|
||||||
|
com: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function getArchivedRatingsLeaderboard(
|
||||||
|
region: Region,
|
||||||
|
season: number,
|
||||||
|
) {
|
||||||
|
if (LEADERBOARD_CACHE[region][season]) {
|
||||||
|
return LEADERBOARD_CACHE[region][season];
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(
|
||||||
|
`https://raw.githubusercontent.com/tresabhi/blitzkrieg-db/main/${region}/ratings/${season}/latest.json`,
|
||||||
|
);
|
||||||
|
const json = (await response.json()) as BlitzkriegRatingsLeaderboard;
|
||||||
|
LEADERBOARD_CACHE[region][season] = json;
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import { BlitzkriegRatingsLeaderboard } from '../../../scripts/buildRatingsLeaderboard';
|
import { BlitzkriegRatingsLeaderboard } from '../../../scripts/buildRatingsLeaderboard';
|
||||||
import { Region } from '../../constants/regions';
|
import { Region } from '../../constants/regions';
|
||||||
import { octokit } from './octokit';
|
|
||||||
|
|
||||||
export const DATABASE_REPO = { owner: 'tresabhi', repo: 'blitzkrieg-db' };
|
export const DATABASE_REPO = { owner: 'tresabhi', repo: 'blitzkrieg-db' };
|
||||||
|
|
||||||
@@ -13,7 +12,7 @@ const MIDNIGHT_LEADERBOARD_CACHE: Record<
|
|||||||
asia: {},
|
asia: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default async function getMidnightLeaderboard(
|
export default async function getArchivedRatingsMidnightLeaderboard(
|
||||||
region: Region,
|
region: Region,
|
||||||
season: number,
|
season: number,
|
||||||
) {
|
) {
|
||||||
@@ -22,19 +21,13 @@ export default async function getMidnightLeaderboard(
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { data } = await octokit.repos.getContent({
|
const response = await fetch(
|
||||||
...DATABASE_REPO,
|
`https://raw.githubusercontent.com/tresabhi/blitzkrieg-db/main/${region}/ratings/${season}/midnight.json`,
|
||||||
path: `${region}/ratings/${season}/midnight.json`,
|
);
|
||||||
});
|
const json = (await response.json()) as BlitzkriegRatingsLeaderboard;
|
||||||
|
MIDNIGHT_LEADERBOARD_CACHE[region][season] = json;
|
||||||
|
|
||||||
if (!Array.isArray(data) && data.type === 'file') {
|
return json;
|
||||||
const response = await fetch(data.download_url!);
|
|
||||||
const json = (await response.json()) as BlitzkriegRatingsLeaderboard;
|
|
||||||
|
|
||||||
MIDNIGHT_LEADERBOARD_CACHE[region][season] = json;
|
|
||||||
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(
|
console.warn(
|
||||||
`No midnight leaderboard found for season ${season}, region ${region}`,
|
`No midnight leaderboard found for season ${season}, region ${region}`,
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
export async function patientFetch(
|
||||||
|
url: string,
|
||||||
|
timeout = 1000,
|
||||||
|
options?: Partial<{ cache: 'no-store' }>,
|
||||||
|
) {
|
||||||
|
let patientResponse: Response;
|
||||||
|
|
||||||
|
while (patientResponse! === undefined) {
|
||||||
|
try {
|
||||||
|
patientResponse = await fetch(url, options);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(
|
||||||
|
`Failed to fetch ${url}; retrying in ${timeout}ms...`,
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, timeout));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return patientResponse;
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
export async function patientFetchJSON<Type extends object>(
|
||||||
|
url: string,
|
||||||
|
timeout = 1000,
|
||||||
|
options?: Partial<{ cache: 'no-store' }>,
|
||||||
|
) {
|
||||||
|
let patientJSON: Type;
|
||||||
|
|
||||||
|
while (patientJSON! === undefined) {
|
||||||
|
try {
|
||||||
|
patientJSON = (await fetch(url, options).then(({ json }) =>
|
||||||
|
json(),
|
||||||
|
)) as Type;
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(
|
||||||
|
`Failed to fetch ${url} as JSON; retrying in ${timeout}ms...`,
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, timeout));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return patientJSON;
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Worker } from 'worker_threads';
|
import { Worker } from 'worker_threads';
|
||||||
|
|
||||||
const MAX_WORKERS = 3;
|
const MAX_WORKERS = 2;
|
||||||
|
|
||||||
const workers = [...Array(MAX_WORKERS)].map(() => {
|
const workers = [...Array(MAX_WORKERS)].map(() => {
|
||||||
const worker = new Worker(`${__dirname}/workers/render.cjs`);
|
const worker = new Worker(`${__dirname}/workers/render.cjs`);
|
||||||
@@ -19,18 +19,22 @@ function manageQueue() {
|
|||||||
|
|
||||||
function cleanup() {
|
function cleanup() {
|
||||||
workers.push(worker);
|
workers.push(worker);
|
||||||
|
worker.off('message', handleMessage);
|
||||||
|
worker.off('error', handleError);
|
||||||
manageQueue();
|
manageQueue();
|
||||||
}
|
}
|
||||||
|
function handleMessage(png: Uint8Array) {
|
||||||
|
cleanup();
|
||||||
|
resolve(Buffer.from(png));
|
||||||
|
}
|
||||||
|
function handleError(error: Error) {
|
||||||
|
cleanup();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
worker.postMessage(svg);
|
worker.postMessage(svg);
|
||||||
worker.on('message', (png: Uint8Array) => {
|
worker.on('message', handleMessage);
|
||||||
cleanup();
|
worker.on('error', handleError);
|
||||||
resolve(Buffer.from(png));
|
|
||||||
});
|
|
||||||
worker.on('error', (error) => {
|
|
||||||
cleanup();
|
|
||||||
throw error;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
export default function throwError(message: string, cause?: string) {
|
|
||||||
return new Error(message, { cause });
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* Discriminator for user caused errors to not report in logs
|
||||||
|
*/
|
||||||
|
export class UserError extends Error {}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { context } from './context';
|
||||||
|
|
||||||
|
export function withCORSProxy(url: string) {
|
||||||
|
return context === 'website'
|
||||||
|
? `https://corsproxy.io/?${encodeURIComponent(url)}`
|
||||||
|
: url;
|
||||||
|
}
|
||||||
@@ -85,13 +85,13 @@ tankopedia;
|
|||||||
export type TankAverages = Record<number, IndividualTankAverage>;
|
export type TankAverages = Record<number, IndividualTankAverage>;
|
||||||
|
|
||||||
export const tankAverages = fetch(
|
export const tankAverages = fetch(
|
||||||
context === 'bot'
|
context === 'website'
|
||||||
? 'https://www.blitzstars.com/api/tankaverages.json'
|
? '/api/tank-averages'
|
||||||
: '/api/tank-averages',
|
: 'https://www.blitzstars.com/api/tankaverages.json',
|
||||||
)
|
)
|
||||||
.then((response) => response.json())
|
.then((response) => response.json())
|
||||||
.then((json) => {
|
.then((json) => {
|
||||||
if (context === 'bot') {
|
if (context === 'server') {
|
||||||
const partialTankAverages: TankAverages = {};
|
const partialTankAverages: TankAverages = {};
|
||||||
|
|
||||||
(json as IndividualTankAverage[]).forEach((individualTankAverage) => {
|
(json as IndividualTankAverage[]).forEach((individualTankAverage) => {
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
import { deburr } from 'lodash';
|
import { deburr } from 'lodash';
|
||||||
import { TreeTypeString } from '../../components/Tanks';
|
|
||||||
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
|
|
||||||
import fetchBlitz from '../blitz/fetchWargaming';
|
|
||||||
import { context } from '../blitzkrieg/context';
|
import { context } from '../blitzkrieg/context';
|
||||||
|
|
||||||
export interface TankopediaEntry {
|
export interface TankopediaEntry {
|
||||||
@@ -16,19 +13,18 @@ export interface TankopediaEntry {
|
|||||||
type: string;
|
type: string;
|
||||||
description: string;
|
description: string;
|
||||||
}
|
}
|
||||||
type asd = TreeTypeString;
|
|
||||||
export interface Tankopedia {
|
export interface Tankopedia {
|
||||||
[id: number]: TankopediaEntry | undefined;
|
[id: number]: TankopediaEntry | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const tankopedia = fetch(
|
export const tankopedia = fetch(
|
||||||
context === 'bot'
|
context === 'website'
|
||||||
? 'https://www.blitzstars.com/bs-tankopedia.json'
|
? '/api/tankopedia'
|
||||||
: '/api/tankopedia',
|
: 'https://www.blitzstars.com/bs-tankopedia.json',
|
||||||
)
|
)
|
||||||
.then(async (response) => response.json())
|
.then(async (response) => response.json())
|
||||||
.then((wrapperTankopedia) =>
|
.then((wrapperTankopedia) =>
|
||||||
context === 'bot'
|
context === 'server'
|
||||||
? (wrapperTankopedia as { data: Tankopedia }).data
|
? (wrapperTankopedia as { data: Tankopedia }).data
|
||||||
: (wrapperTankopedia as Tankopedia),
|
: (wrapperTankopedia as Tankopedia),
|
||||||
);
|
);
|
||||||
@@ -58,20 +54,6 @@ export const TANK_NAMES_DIACRITICS = TANK_NAMES.then((tankNames) =>
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
export interface TankopediaInfo {
|
|
||||||
achievement_sections: Record<string, { name: string; order: number }>;
|
|
||||||
tanks_updated_at: number;
|
|
||||||
languages: Record<string, string>;
|
|
||||||
vehicle_types: Record<string, string>;
|
|
||||||
vehicle_nations: Record<string, string>;
|
|
||||||
game_version: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is blocking because info is needed for command creation
|
|
||||||
export const tankopediaInfo = fetchBlitz<TankopediaInfo>(
|
|
||||||
`https://api.wotblitz.com/wotb/encyclopedia/info/?application_id=${WARGAMING_APPLICATION_ID}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
export const TANK_ICONS: Record<string, string> = {
|
export const TANK_ICONS: Record<string, string> = {
|
||||||
'AT-SPG': 'https://i.imgur.com/BIHSEH0.png',
|
'AT-SPG': 'https://i.imgur.com/BIHSEH0.png',
|
||||||
lightTank: 'https://i.imgur.com/CSNha5V.png',
|
lightTank: 'https://i.imgur.com/CSNha5V.png',
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
SlashCommandSubcommandGroupBuilder,
|
SlashCommandSubcommandGroupBuilder,
|
||||||
} from 'discord.js';
|
} from 'discord.js';
|
||||||
import { TREE_TYPE_NAMES, TreeTypeString } from '../../components/Tanks';
|
import { TREE_TYPE_NAMES, TreeTypeString } from '../../components/Tanks';
|
||||||
import { tankopediaInfo } from '../blitzstars/tankopedia';
|
import { tankopediaInfo } from '../blitz/tankopediaInfo';
|
||||||
import addPeriodSubCommands from './addPeriodSubCommands';
|
import addPeriodSubCommands from './addPeriodSubCommands';
|
||||||
import addTankChoices from './addTankChoices';
|
import addTankChoices from './addTankChoices';
|
||||||
import addTierChoices from './addTierChoices';
|
import addTierChoices from './addTierChoices';
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ import { SlashCommandStringOption } from 'discord.js';
|
|||||||
export default function addUsernameChoices(option: SlashCommandStringOption) {
|
export default function addUsernameChoices(option: SlashCommandStringOption) {
|
||||||
return option
|
return option
|
||||||
.setName('username')
|
.setName('username')
|
||||||
.setDescription(
|
.setDescription('The username you use in Blitz')
|
||||||
'The username you use in Blitz (inferred from Discord nickname by default)',
|
|
||||||
)
|
|
||||||
.setAutocomplete(true);
|
.setAutocomplete(true);
|
||||||
}
|
}
|
||||||
@@ -3,26 +3,39 @@ import {
|
|||||||
AutocompleteInteraction,
|
AutocompleteInteraction,
|
||||||
CacheType,
|
CacheType,
|
||||||
} from 'discord.js';
|
} from 'discord.js';
|
||||||
import { REGION_NAMES } from '../../constants/regions';
|
import { REGION_NAMES_SHORT } from '../../constants/regions';
|
||||||
import searchClansAcrossRegions from '../blitz/searchClansAcrossRegions';
|
import searchClansAcrossRegions from '../blitz/searchClansAcrossRegions';
|
||||||
|
|
||||||
|
export const DISCORD_CHOICES_MAX_NAME_SIZE = 25;
|
||||||
|
export const OVERFLOW_SUFFIX = '…';
|
||||||
|
|
||||||
export default async function autocompleteClan(
|
export default async function autocompleteClan(
|
||||||
interaction: AutocompleteInteraction<CacheType>,
|
interaction: AutocompleteInteraction<CacheType>,
|
||||||
) {
|
) {
|
||||||
const focusedOption = interaction.options.getFocused(true);
|
const focusedOption = interaction.options.getFocused(true);
|
||||||
if (focusedOption.name !== 'clan') return;
|
if (focusedOption.name !== 'clan') return;
|
||||||
if (focusedOption.value.length < 2) return interaction.respond([]);
|
if (focusedOption.value.length < 2) return interaction.respond([]);
|
||||||
const players = await searchClansAcrossRegions(focusedOption.value);
|
const clans = await searchClansAcrossRegions(focusedOption.value);
|
||||||
|
|
||||||
await interaction.respond(
|
await interaction.respond(
|
||||||
players
|
clans
|
||||||
? players.map(
|
? clans.map((clan) => {
|
||||||
(player) =>
|
let name = `${clan.name} (${REGION_NAMES_SHORT[clan.region]})`;
|
||||||
({
|
|
||||||
name: `${player.name} (${REGION_NAMES[player.region]})`,
|
if (name.length > DISCORD_CHOICES_MAX_NAME_SIZE) {
|
||||||
value: `${player.region}/${player.clan_id}`,
|
const overSize = name.length - DISCORD_CHOICES_MAX_NAME_SIZE;
|
||||||
}) satisfies ApplicationCommandOptionChoiceData<string>,
|
|
||||||
)
|
name = `${clan.name.slice(
|
||||||
|
0,
|
||||||
|
clan.name.length - overSize - OVERFLOW_SUFFIX.length,
|
||||||
|
)}${OVERFLOW_SUFFIX} (${REGION_NAMES_SHORT[clan.region]})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
value: `${clan.region}/${clan.clan_id}`,
|
||||||
|
} satisfies ApplicationCommandOptionChoiceData<string>;
|
||||||
|
})
|
||||||
: [],
|
: [],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,10 @@ import {
|
|||||||
} from 'discord.js';
|
} from 'discord.js';
|
||||||
import { go } from 'fuzzysort';
|
import { go } from 'fuzzysort';
|
||||||
import { TANK_NAMES_DIACRITICS } from '../blitzstars/tankopedia';
|
import { TANK_NAMES_DIACRITICS } from '../blitzstars/tankopedia';
|
||||||
|
import {
|
||||||
|
DISCORD_CHOICES_MAX_NAME_SIZE,
|
||||||
|
OVERFLOW_SUFFIX,
|
||||||
|
} from './autocompleteClan';
|
||||||
|
|
||||||
export default async function autocompleteTanks(
|
export default async function autocompleteTanks(
|
||||||
interaction: AutocompleteInteraction<CacheType>,
|
interaction: AutocompleteInteraction<CacheType>,
|
||||||
@@ -18,13 +22,23 @@ export default async function autocompleteTanks(
|
|||||||
go(focusedOption.value, await TANK_NAMES_DIACRITICS, {
|
go(focusedOption.value, await TANK_NAMES_DIACRITICS, {
|
||||||
keys: ['combined'],
|
keys: ['combined'],
|
||||||
limit: 10,
|
limit: 10,
|
||||||
}).map(
|
}).map(async (item) => {
|
||||||
async (item) =>
|
let name = item.obj.original;
|
||||||
({
|
|
||||||
name: item.obj.original,
|
if (name.length > DISCORD_CHOICES_MAX_NAME_SIZE) {
|
||||||
value: `${item.obj.id}`,
|
const overSize = name.length - DISCORD_CHOICES_MAX_NAME_SIZE;
|
||||||
}) satisfies ApplicationCommandOptionChoiceData<string>,
|
|
||||||
),
|
name = `${item.obj.original.slice(
|
||||||
|
0,
|
||||||
|
item.obj.original.length - overSize - OVERFLOW_SUFFIX.length,
|
||||||
|
)}${OVERFLOW_SUFFIX}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
value: `${item.obj.id}`,
|
||||||
|
} satisfies ApplicationCommandOptionChoiceData<string>;
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
: [],
|
: [],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,8 +3,12 @@ import {
|
|||||||
AutocompleteInteraction,
|
AutocompleteInteraction,
|
||||||
CacheType,
|
CacheType,
|
||||||
} from 'discord.js';
|
} from 'discord.js';
|
||||||
import { REGION_NAMES } from '../../constants/regions';
|
import { REGION_NAMES_SHORT } from '../../constants/regions';
|
||||||
import searchPlayersAcrossRegions from '../blitz/searchPlayersAcrossRegions';
|
import searchPlayersAcrossRegions from '../blitz/searchPlayersAcrossRegions';
|
||||||
|
import {
|
||||||
|
DISCORD_CHOICES_MAX_NAME_SIZE,
|
||||||
|
OVERFLOW_SUFFIX,
|
||||||
|
} from './autocompleteClan';
|
||||||
|
|
||||||
export default async function autocompleteUsername(
|
export default async function autocompleteUsername(
|
||||||
interaction: AutocompleteInteraction<CacheType>,
|
interaction: AutocompleteInteraction<CacheType>,
|
||||||
@@ -16,13 +20,25 @@ export default async function autocompleteUsername(
|
|||||||
try {
|
try {
|
||||||
await interaction.respond(
|
await interaction.respond(
|
||||||
players
|
players
|
||||||
? players.map(
|
? players.map((player) => {
|
||||||
(player) =>
|
let name = `${player.nickname} (${
|
||||||
({
|
REGION_NAMES_SHORT[player.region]
|
||||||
name: `${player.nickname} (${REGION_NAMES[player.region]})`,
|
})`;
|
||||||
value: `${player.region}/${player.account_id}`,
|
|
||||||
}) satisfies ApplicationCommandOptionChoiceData<string>,
|
if (name.length > DISCORD_CHOICES_MAX_NAME_SIZE) {
|
||||||
)
|
const overSize = name.length - DISCORD_CHOICES_MAX_NAME_SIZE;
|
||||||
|
|
||||||
|
name = `${player.nickname.slice(
|
||||||
|
0,
|
||||||
|
player.nickname.length - overSize - OVERFLOW_SUFFIX.length,
|
||||||
|
)}${OVERFLOW_SUFFIX} (${REGION_NAMES_SHORT[player.region]})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
value: `${player.region}/${player.account_id}`,
|
||||||
|
} satisfies ApplicationCommandOptionChoiceData<string>;
|
||||||
|
})
|
||||||
: [],
|
: [],
|
||||||
);
|
);
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import jsxToPngThreaded from '../blitzkrieg/jsxToPngThreaded';
|
|||||||
export default async function normalizeInteractionReturnable(
|
export default async function normalizeInteractionReturnable(
|
||||||
returnable: InteractionReturnable,
|
returnable: InteractionReturnable,
|
||||||
) {
|
) {
|
||||||
|
const images: [number, Buffer][] = [];
|
||||||
const reply: InteractionEditReplyOptions = {};
|
const reply: InteractionEditReplyOptions = {};
|
||||||
|
|
||||||
const awaitedReturnable = await returnable;
|
const awaitedReturnable = await returnable;
|
||||||
@@ -19,7 +20,7 @@ export default async function normalizeInteractionReturnable(
|
|||||||
: [awaitedReturnable];
|
: [awaitedReturnable];
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
normalizedReturnable.map(async (item) => {
|
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) {
|
||||||
@@ -38,11 +39,16 @@ export default async function normalizeInteractionReturnable(
|
|||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
const image = await jsxToPngThreaded(item);
|
const image = await jsxToPngThreaded(item);
|
||||||
if (!reply.files) reply.files = [];
|
images.push([index, image]);
|
||||||
reply.files.push(image);
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (images.length > 0) {
|
||||||
|
images.sort(([a], [b]) => b - a);
|
||||||
|
if (!reply.files) reply.files = [];
|
||||||
|
images.forEach(([, image]) => reply.files?.push(image));
|
||||||
|
}
|
||||||
|
|
||||||
return reply;
|
return reply;
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,30 @@
|
|||||||
export interface PSAData {
|
export type PSAData = {
|
||||||
title: string;
|
secret?: boolean;
|
||||||
description: string;
|
links?: {
|
||||||
}
|
title: string;
|
||||||
|
url: string;
|
||||||
|
}[];
|
||||||
|
commands?: string[];
|
||||||
|
} & (
|
||||||
|
| {
|
||||||
|
type: 'image';
|
||||||
|
image: string;
|
||||||
|
title?: string;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'embed';
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export interface PSA {
|
export interface PSA {
|
||||||
data: PSAData | null;
|
data: PSAData | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const psa: PSA = { data: null };
|
export const psa: PSA = {
|
||||||
|
data: null,
|
||||||
|
};
|
||||||
|
|
||||||
fetch(
|
fetch(
|
||||||
'https://gist.githubusercontent.com/tresabhi/ed4b136f08856e615212e573aabe1968/raw',
|
'https://gist.githubusercontent.com/tresabhi/ed4b136f08856e615212e573aabe1968/raw',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ChatInputCommandInteraction } from 'discord.js';
|
import { ChatInputCommandInteraction } from 'discord.js';
|
||||||
import { Region } from '../../constants/regions';
|
import { Region } from '../../constants/regions';
|
||||||
import searchClansAcrossRegions from '../blitz/searchClansAcrossRegions';
|
import searchClansAcrossRegions from '../blitz/searchClansAcrossRegions';
|
||||||
import throwError from '../blitzkrieg/throwError';
|
import { UserError } from '../blitzkrieg/userError';
|
||||||
import { serverAndIdPattern } from './resolvePlayerFromCommand';
|
import { serverAndIdPattern } from './resolvePlayerFromCommand';
|
||||||
|
|
||||||
export default async function resolveClanFromCommand(
|
export default async function resolveClanFromCommand(
|
||||||
@@ -18,10 +18,9 @@ export default async function resolveClanFromCommand(
|
|||||||
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 throwError(
|
throw new UserError('Could not find clan', {
|
||||||
'Could not find clan',
|
cause: `I couldn't find clan \`${clan}\`. Try selecting a username from the search result.`,
|
||||||
`I couldn't find clan \`${clan}\`. Try selecting a username from the search result.`,
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@ import markdownEscape from 'markdown-escape';
|
|||||||
import { Region } from '../../constants/regions';
|
import { Region } from '../../constants/regions';
|
||||||
import searchPlayersAcrossRegions from '../blitz/searchPlayersAcrossRegions';
|
import searchPlayersAcrossRegions from '../blitz/searchPlayersAcrossRegions';
|
||||||
import { getBlitzFromDiscord } from '../blitzkrieg/discordBlitz';
|
import { getBlitzFromDiscord } from '../blitzkrieg/discordBlitz';
|
||||||
import throwError from '../blitzkrieg/throwError';
|
import { UserError } from '../blitzkrieg/userError';
|
||||||
|
|
||||||
export const serverAndIdPattern = /(com|eu|asia)\/[0-9]+/;
|
export const serverAndIdPattern = /(com|eu|asia)\/[0-9]+/;
|
||||||
|
|
||||||
@@ -34,12 +34,11 @@ export default async function resolvePlayerFromCommand(
|
|||||||
id: accounts[0].account_id,
|
id: accounts[0].account_id,
|
||||||
} satisfies ResolvedPlayer;
|
} satisfies ResolvedPlayer;
|
||||||
} else {
|
} else {
|
||||||
throw throwError(
|
throw new UserError('Could not find user', {
|
||||||
'Could not find user',
|
cause: `I couldn't find user "${markdownEscape(
|
||||||
`I couldn't find user "${markdownEscape(
|
|
||||||
commandUsername,
|
commandUsername,
|
||||||
)}". Try picking an user from the search result, typing in a valid username, or using the \`/link\` command.`,
|
)}". Try picking an user from the search result, typing in a valid username, or using the \`/link\` command.`,
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -48,10 +47,9 @@ export default async function resolvePlayerFromCommand(
|
|||||||
if (account) {
|
if (account) {
|
||||||
return { region: account.region, id: account.blitz };
|
return { region: account.region, id: account.blitz };
|
||||||
} else {
|
} else {
|
||||||
throw throwError(
|
throw new UserError('Use the `/link` command', {
|
||||||
"You're not linked",
|
cause: 'Link your Blitz and Discord accounts to get started.',
|
||||||
'Use the `/link` command to link your Discord and Blitz accounts.',
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,5 @@
|
|||||||
import {
|
import { AttachmentBuilder, GuildMember, TextChannel } from 'discord.js';
|
||||||
AttachmentBuilder,
|
|
||||||
EmbedBuilder,
|
|
||||||
GuildMember,
|
|
||||||
TextChannel,
|
|
||||||
} from 'discord.js';
|
|
||||||
import markdownEscape from 'markdown-escape';
|
|
||||||
import discord from '../../discord.json' assert { type: 'json' };
|
import discord from '../../discord.json' assert { type: 'json' };
|
||||||
import { ACCENT_COLOR } from '../constants/colors';
|
|
||||||
|
|
||||||
export default async function (member: GuildMember) {
|
export default async function (member: GuildMember) {
|
||||||
if (member.guild.id === discord.sklld_guild_id) {
|
if (member.guild.id === discord.sklld_guild_id) {
|
||||||
@@ -15,15 +8,7 @@ export default async function (member: GuildMember) {
|
|||||||
discord.sklld_verify_channel,
|
discord.sklld_verify_channel,
|
||||||
) as TextChannel
|
) as TextChannel
|
||||||
).send({
|
).send({
|
||||||
content: member.toString(),
|
content: `# Welcome, ${member.toString()}!\nPlease use the \`/link\` command like the video below to continue.`,
|
||||||
embeds: [
|
|
||||||
new EmbedBuilder()
|
|
||||||
.setColor(ACCENT_COLOR)
|
|
||||||
.setTitle(`Welcome, ${markdownEscape(member.user.displayName)}`)
|
|
||||||
.setDescription(
|
|
||||||
`Welcome to the Skilled server! To continue, please use the \`/link\` command.`,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
files: [new AttachmentBuilder('https://i.imgur.com/2p6GFgC.gif')],
|
files: [new AttachmentBuilder('https://i.imgur.com/2p6GFgC.gif')],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { ButtonInteraction, CacheType } from 'discord.js';
|
import { ButtonInteraction, CacheType } from 'discord.js';
|
||||||
import { commands } from '..';
|
import { commands } from '..';
|
||||||
import throwError from '../../../core/blitzkrieg/throwError';
|
|
||||||
import normalizeInteractionReturnable from '../../../core/discord/normalizeInteractionReturnable';
|
import normalizeInteractionReturnable from '../../../core/discord/normalizeInteractionReturnable';
|
||||||
|
|
||||||
export default async function handleButton(
|
export default async function handleButton(
|
||||||
@@ -13,8 +12,7 @@ export default async function handleButton(
|
|||||||
const button = (await commands)[commandName]?.button;
|
const button = (await commands)[commandName]?.button;
|
||||||
|
|
||||||
if (!button) {
|
if (!button) {
|
||||||
throwError(`Button handler not found for "${commandName}"`);
|
throw new Error(`Button handler not found for "${commandName}"`);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (interaction.message.interaction?.user.id !== interaction.user.id) {
|
if (interaction.message.interaction?.user.id !== interaction.user.id) {
|
||||||
|
|||||||
@@ -1,16 +1,20 @@
|
|||||||
import {
|
import {
|
||||||
ActionRowBuilder,
|
ActionRowBuilder,
|
||||||
|
AttachmentBuilder,
|
||||||
ButtonBuilder,
|
ButtonBuilder,
|
||||||
ButtonStyle,
|
ButtonStyle,
|
||||||
CacheType,
|
CacheType,
|
||||||
ChatInputCommandInteraction,
|
ChatInputCommandInteraction,
|
||||||
|
InteractionReplyOptions,
|
||||||
} from 'discord.js';
|
} from 'discord.js';
|
||||||
import { commands } from '..';
|
import { commands } from '..';
|
||||||
import discord from '../../../../discord.json' assert { type: 'json' };
|
import discord from '../../../../discord.json' assert { type: 'json' };
|
||||||
import { psa } from '../../../core/discord/psa';
|
import { UserError } from '../../../core/blitzkrieg/userError';
|
||||||
|
import buttonLink from '../../../core/discord/buttonLink';
|
||||||
import embedNegative from '../../../core/discord/embedNegative';
|
import embedNegative from '../../../core/discord/embedNegative';
|
||||||
import embedWarning from '../../../core/discord/embedWarning';
|
import embedWarning from '../../../core/discord/embedWarning';
|
||||||
import normalizeInteractionReturnable from '../../../core/discord/normalizeInteractionReturnable';
|
import normalizeInteractionReturnable from '../../../core/discord/normalizeInteractionReturnable';
|
||||||
|
import { psa } from '../../../core/discord/psa';
|
||||||
|
|
||||||
export default async function handleChatInputCommand(
|
export default async function handleChatInputCommand(
|
||||||
interaction: ChatInputCommandInteraction<CacheType>,
|
interaction: ChatInputCommandInteraction<CacheType>,
|
||||||
@@ -19,50 +23,122 @@ export default async function handleChatInputCommand(
|
|||||||
|
|
||||||
await interaction.deferReply();
|
await interaction.deferReply();
|
||||||
|
|
||||||
|
if (registry.inPreview && interaction.guildId !== discord.tres_guild_id) {
|
||||||
|
interaction.editReply({
|
||||||
|
embeds: [
|
||||||
|
embedWarning(
|
||||||
|
`\`/${registry.command.name}\` is in Public Preview`,
|
||||||
|
'[Join the official Discord server](https://discord.gg/nDt7AjGJQH) to gain early access to this command before it is released to the public.',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (registry.inPreview && interaction.guildId !== discord.tres_guild_id) {
|
|
||||||
interaction.editReply({
|
|
||||||
embeds: [
|
|
||||||
embedWarning(
|
|
||||||
`\`/${registry.command.name}\` is in Public Preview`,
|
|
||||||
'[Join the official Discord server](https://discord.gg/nDt7AjGJQH) to gain early access to this command before it is released to the public.',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const returnable = await registry.handler(interaction);
|
const returnable = await registry.handler(interaction);
|
||||||
|
|
||||||
if (registry.handlesInteraction) return;
|
if (registry.handlesInteraction) return;
|
||||||
|
|
||||||
const reply = await normalizeInteractionReturnable(returnable!);
|
const reply = await normalizeInteractionReturnable(returnable!);
|
||||||
|
|
||||||
if (psa.data) {
|
if (
|
||||||
if (!reply.embeds) reply.embeds = [];
|
psa.data &&
|
||||||
reply.embeds.push(embedWarning(psa.data.title, psa.data.description));
|
!psa.data.secret &&
|
||||||
|
(psa.data.commands === undefined ||
|
||||||
|
psa.data.commands.includes(interaction.commandName))
|
||||||
|
) {
|
||||||
|
if (psa.data.type === 'embed') {
|
||||||
|
if (!reply.embeds) reply.embeds = [];
|
||||||
|
reply.embeds.push(embedWarning(psa.data.title, psa.data.description));
|
||||||
|
} else {
|
||||||
|
if (!reply.files) reply.files = [];
|
||||||
|
reply.files.push(new AttachmentBuilder(psa.data.image));
|
||||||
|
reply.content = reply.content ?? psa.data.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (psa.data.links) {
|
||||||
|
if (!reply.components?.[0]) {
|
||||||
|
reply.components = [new ActionRowBuilder<ButtonBuilder>()];
|
||||||
|
}
|
||||||
|
|
||||||
|
(reply.components[0] as ActionRowBuilder<ButtonBuilder>).addComponents(
|
||||||
|
psa.data.links.map(({ title, url }) => buttonLink(url, title)),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await interaction.editReply(reply);
|
await interaction.editReply(reply);
|
||||||
|
|
||||||
|
if (
|
||||||
|
psa.data &&
|
||||||
|
psa.data.secret &&
|
||||||
|
(psa.data.commands === undefined ||
|
||||||
|
psa.data.commands.includes(interaction.commandName))
|
||||||
|
) {
|
||||||
|
const followUp: InteractionReplyOptions = {
|
||||||
|
ephemeral: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (psa.data.type === 'embed') {
|
||||||
|
if (!followUp.embeds) followUp.embeds = [];
|
||||||
|
followUp.embeds.push(
|
||||||
|
embedWarning(psa.data.title, psa.data.description),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
if (!followUp.files) followUp.files = [];
|
||||||
|
followUp.files.push(new AttachmentBuilder(psa.data.image));
|
||||||
|
followUp.content = followUp.content ?? psa.data.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (psa.data.links) {
|
||||||
|
if (!followUp.components?.[0]) {
|
||||||
|
followUp.components = [new ActionRowBuilder<ButtonBuilder>()];
|
||||||
|
}
|
||||||
|
|
||||||
|
(
|
||||||
|
followUp.components[0] as ActionRowBuilder<ButtonBuilder>
|
||||||
|
).addComponents(
|
||||||
|
psa.data.links.map(({ title, url }) => buttonLink(url, title)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interaction.followUp(followUp);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const actionRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
|
const components = [
|
||||||
new ButtonBuilder()
|
new ActionRowBuilder<ButtonBuilder>().addComponents(
|
||||||
.setLabel('Get Help on Discord Server')
|
new ButtonBuilder()
|
||||||
.setURL('https://discord.gg/nDt7AjGJQH')
|
.setLabel('Get Help on Discord Server')
|
||||||
.setStyle(ButtonStyle.Link),
|
.setURL('https://discord.gg/nDt7AjGJQH')
|
||||||
);
|
.setStyle(ButtonStyle.Link),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
console.error(interaction.commandName, error);
|
if (error instanceof UserError) {
|
||||||
|
interaction.editReply({
|
||||||
|
embeds: [
|
||||||
|
embedNegative(
|
||||||
|
error.message,
|
||||||
|
(error.cause as string | undefined) ??
|
||||||
|
'No further information is available.',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
components,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.error(interaction.commandName, error);
|
||||||
|
|
||||||
await interaction.editReply({
|
interaction.editReply({
|
||||||
embeds: [
|
embeds: [
|
||||||
embedNegative(
|
embedNegative(
|
||||||
(error as Error).message,
|
'Blitzkrieg ran into an error!',
|
||||||
`${(error as Error).cause ?? 'No further information is available.'}`,
|
"We're so sorry about this. Feel free to [join the official Discord server](https://discord.gg/nDt7AjGJQH) to get help.",
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
components: [actionRow],
|
components,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
import discord from '../../../discord.json' assert { type: 'json' };
|
import discord from '../../../discord.json' assert { type: 'json' };
|
||||||
import { aboutCommand } from '../../commands/about';
|
import { aboutCommand } from '../../commands/about';
|
||||||
import { breakdownCommand } from '../../commands/breakdown';
|
import { breakdownCommand } from '../../commands/breakdown';
|
||||||
|
import { creditsCommand } from '../../commands/credits';
|
||||||
import { debugCommand } from '../../commands/debug';
|
import { debugCommand } from '../../commands/debug';
|
||||||
import { eligibleCommand } from '../../commands/eligible';
|
import { eligibleCommand } from '../../commands/eligible';
|
||||||
import { evolutionCommand } from '../../commands/evolution';
|
import { evolutionCommand } from '../../commands/evolution';
|
||||||
@@ -31,7 +32,6 @@ import { searchClansCommand } from '../../commands/searchClans';
|
|||||||
import { searchPlayersCommand } from '../../commands/searchPlayers';
|
import { searchPlayersCommand } from '../../commands/searchPlayers';
|
||||||
import { searchTanksCommand } from '../../commands/searchTanks';
|
import { searchTanksCommand } from '../../commands/searchTanks';
|
||||||
import { statsCommand } from '../../commands/stats';
|
import { statsCommand } from '../../commands/stats';
|
||||||
import { todayCommand } from '../../commands/today';
|
|
||||||
import getClientId from '../../core/blitzkrieg/getClientId';
|
import getClientId from '../../core/blitzkrieg/getClientId';
|
||||||
import isDev from '../../core/blitzkrieg/isDev';
|
import isDev from '../../core/blitzkrieg/isDev';
|
||||||
import { secrets } from '../../core/blitzkrieg/secrets';
|
import { secrets } from '../../core/blitzkrieg/secrets';
|
||||||
@@ -102,7 +102,7 @@ export const COMMANDS_RAW: CommandRegistry[] = [
|
|||||||
evolutionCommand,
|
evolutionCommand,
|
||||||
statsCommand,
|
statsCommand,
|
||||||
ratingsCommand,
|
ratingsCommand,
|
||||||
todayCommand,
|
creditsCommand,
|
||||||
];
|
];
|
||||||
|
|
||||||
export const commands = Promise.all(COMMANDS_RAW).then((rawCommands) =>
|
export const commands = Promise.all(COMMANDS_RAW).then((rawCommands) =>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
amberDark,
|
amberDark,
|
||||||
blueDark,
|
blueDark,
|
||||||
|
orangeDark,
|
||||||
slate,
|
slate,
|
||||||
slateDark,
|
slateDark,
|
||||||
tealDark,
|
tealDark,
|
||||||
@@ -16,6 +17,7 @@ export const theme = {
|
|||||||
...createColors(slate, 'light'),
|
...createColors(slate, 'light'),
|
||||||
|
|
||||||
...createColors(amberDark, 'amber'),
|
...createColors(amberDark, 'amber'),
|
||||||
|
...createColors(orangeDark, 'orange'),
|
||||||
...createColors(blueDark, 'blue'),
|
...createColors(blueDark, 'blue'),
|
||||||
...createColors(tealDark, 'teal'),
|
...createColors(tealDark, 'teal'),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { devtools, persist } from 'zustand/middleware';
|
|||||||
import { CustomColumnDisplay } from '../app/tools/session/components/CustomColumn';
|
import { CustomColumnDisplay } from '../app/tools/session/components/CustomColumn';
|
||||||
import { Region } from '../constants/regions';
|
import { Region } from '../constants/regions';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../constants/wargamingApplicationID';
|
import { WARGAMING_APPLICATION_ID } from '../constants/wargamingApplicationID';
|
||||||
import fetchBlitz from '../core/blitz/fetchWargaming';
|
import fetchBlitz from '../core/blitz/fetchBlitz';
|
||||||
import { NormalizedTankStats, TanksStats } from '../types/tanksStats';
|
import { NormalizedTankStats, TanksStats } from '../types/tanksStats';
|
||||||
|
|
||||||
type Session = (
|
type Session = (
|
||||||
@@ -21,8 +21,8 @@ type Session = (
|
|||||||
time: number;
|
time: number;
|
||||||
}
|
}
|
||||||
) & {
|
) & {
|
||||||
promptBeforeReset: boolean;
|
resetPrompt: boolean;
|
||||||
showEmbedPrompt: boolean;
|
embedPrompt: boolean;
|
||||||
customColumns: {
|
customColumns: {
|
||||||
display: CustomColumnDisplay;
|
display: CustomColumnDisplay;
|
||||||
showDelta: boolean;
|
showDelta: boolean;
|
||||||
@@ -67,10 +67,10 @@ export const useSession = create<Session>()(
|
|||||||
persist(
|
persist(
|
||||||
(set) => ({
|
(set) => ({
|
||||||
isTracking: false,
|
isTracking: false,
|
||||||
promptBeforeReset: true,
|
resetPrompt: true,
|
||||||
showTotal: true,
|
showTotal: true,
|
||||||
showCareer: true,
|
showCareer: true,
|
||||||
showEmbedPrompt: true,
|
embedPrompt: true,
|
||||||
|
|
||||||
customColumns: [
|
customColumns: [
|
||||||
{ display: 'battles', showDelta: false },
|
{ display: 'battles', showDelta: false },
|
||||||
|
|||||||
@@ -185,6 +185,16 @@
|
|||||||
"@jridgewell/trace-mapping" "^0.3.17"
|
"@jridgewell/trace-mapping" "^0.3.17"
|
||||||
jsesc "^2.5.1"
|
jsesc "^2.5.1"
|
||||||
|
|
||||||
|
"@babel/generator@^7.23.0":
|
||||||
|
version "7.23.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420"
|
||||||
|
integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==
|
||||||
|
dependencies:
|
||||||
|
"@babel/types" "^7.23.0"
|
||||||
|
"@jridgewell/gen-mapping" "^0.3.2"
|
||||||
|
"@jridgewell/trace-mapping" "^0.3.17"
|
||||||
|
jsesc "^2.5.1"
|
||||||
|
|
||||||
"@babel/helper-compilation-targets@^7.22.15":
|
"@babel/helper-compilation-targets@^7.22.15":
|
||||||
version "7.22.15"
|
version "7.22.15"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz#0698fc44551a26cf29f18d4662d5bf545a6cfc52"
|
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz#0698fc44551a26cf29f18d4662d5bf545a6cfc52"
|
||||||
@@ -201,13 +211,13 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167"
|
resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167"
|
||||||
integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==
|
integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==
|
||||||
|
|
||||||
"@babel/helper-function-name@^7.22.5":
|
"@babel/helper-function-name@^7.23.0":
|
||||||
version "7.22.5"
|
version "7.23.0"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be"
|
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759"
|
||||||
integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==
|
integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/template" "^7.22.5"
|
"@babel/template" "^7.22.15"
|
||||||
"@babel/types" "^7.22.5"
|
"@babel/types" "^7.23.0"
|
||||||
|
|
||||||
"@babel/helper-hoist-variables@^7.22.5":
|
"@babel/helper-hoist-variables@^7.22.5":
|
||||||
version "7.22.5"
|
version "7.22.5"
|
||||||
@@ -291,6 +301,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.16.tgz#180aead7f247305cce6551bea2720934e2fa2c95"
|
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.16.tgz#180aead7f247305cce6551bea2720934e2fa2c95"
|
||||||
integrity sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA==
|
integrity sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA==
|
||||||
|
|
||||||
|
"@babel/parser@^7.23.0":
|
||||||
|
version "7.23.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719"
|
||||||
|
integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==
|
||||||
|
|
||||||
"@babel/plugin-syntax-typescript@^7.20.0":
|
"@babel/plugin-syntax-typescript@^7.20.0":
|
||||||
version "7.22.5"
|
version "7.22.5"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz#aac8d383b062c5072c647a31ef990c1d0af90272"
|
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz#aac8d383b062c5072c647a31ef990c1d0af90272"
|
||||||
@@ -312,7 +327,7 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.14.0"
|
regenerator-runtime "^0.14.0"
|
||||||
|
|
||||||
"@babel/template@^7.22.15", "@babel/template@^7.22.5":
|
"@babel/template@^7.22.15":
|
||||||
version "7.22.15"
|
version "7.22.15"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38"
|
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38"
|
||||||
integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==
|
integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==
|
||||||
@@ -322,18 +337,18 @@
|
|||||||
"@babel/types" "^7.22.15"
|
"@babel/types" "^7.22.15"
|
||||||
|
|
||||||
"@babel/traverse@^7.22.15", "@babel/traverse@^7.22.20":
|
"@babel/traverse@^7.22.15", "@babel/traverse@^7.22.20":
|
||||||
version "7.22.20"
|
version "7.23.2"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.20.tgz#db572d9cb5c79e02d83e5618b82f6991c07584c9"
|
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8"
|
||||||
integrity sha512-eU260mPZbU7mZ0N+X10pxXhQFMGTeLb9eFS0mxehS8HZp9o1uSnFeWQuG1UPrlxgA7QoUzFhOnilHDp0AXCyHw==
|
integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/code-frame" "^7.22.13"
|
"@babel/code-frame" "^7.22.13"
|
||||||
"@babel/generator" "^7.22.15"
|
"@babel/generator" "^7.23.0"
|
||||||
"@babel/helper-environment-visitor" "^7.22.20"
|
"@babel/helper-environment-visitor" "^7.22.20"
|
||||||
"@babel/helper-function-name" "^7.22.5"
|
"@babel/helper-function-name" "^7.23.0"
|
||||||
"@babel/helper-hoist-variables" "^7.22.5"
|
"@babel/helper-hoist-variables" "^7.22.5"
|
||||||
"@babel/helper-split-export-declaration" "^7.22.6"
|
"@babel/helper-split-export-declaration" "^7.22.6"
|
||||||
"@babel/parser" "^7.22.16"
|
"@babel/parser" "^7.23.0"
|
||||||
"@babel/types" "^7.22.19"
|
"@babel/types" "^7.23.0"
|
||||||
debug "^4.1.0"
|
debug "^4.1.0"
|
||||||
globals "^11.1.0"
|
globals "^11.1.0"
|
||||||
|
|
||||||
@@ -346,6 +361,15 @@
|
|||||||
"@babel/helper-validator-identifier" "^7.22.19"
|
"@babel/helper-validator-identifier" "^7.22.19"
|
||||||
to-fast-properties "^2.0.0"
|
to-fast-properties "^2.0.0"
|
||||||
|
|
||||||
|
"@babel/types@^7.23.0":
|
||||||
|
version "7.23.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb"
|
||||||
|
integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==
|
||||||
|
dependencies:
|
||||||
|
"@babel/helper-string-parser" "^7.22.5"
|
||||||
|
"@babel/helper-validator-identifier" "^7.22.20"
|
||||||
|
to-fast-properties "^2.0.0"
|
||||||
|
|
||||||
"@discordjs/builders@^1.6.5":
|
"@discordjs/builders@^1.6.5":
|
||||||
version "1.6.5"
|
version "1.6.5"
|
||||||
resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-1.6.5.tgz#3e23912eaab1d542b61ca0fa7202e5aaef2b7200"
|
resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-1.6.5.tgz#3e23912eaab1d542b61ca0fa7202e5aaef2b7200"
|
||||||
@@ -1858,6 +1882,13 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/decompress@^4.2.6":
|
||||||
|
version "4.2.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/decompress/-/decompress-4.2.6.tgz#2ae3fb360c4d27593f51293e54990b9e709477ae"
|
||||||
|
integrity sha512-ULmsMkKxU7aIGbnxP4Rz28hLdRZ4q0cdy6kcb8dx+UgDGOn+id5fibvoeTnjuolhrRM7f7TOtGADeKEk60SSsQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
"@types/escape-html@^1.0.2":
|
"@types/escape-html@^1.0.2":
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/@types/escape-html/-/escape-html-1.0.2.tgz#072b7b13784fb3cee9c2450c22f36405983f5e3c"
|
resolved "https://registry.yarnpkg.com/@types/escape-html/-/escape-html-1.0.2.tgz#072b7b13784fb3cee9c2450c22f36405983f5e3c"
|
||||||
@@ -1888,6 +1919,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.2.tgz#a86e00bbde8950364f8e7846687259ffcd96e8c2"
|
resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.2.tgz#a86e00bbde8950364f8e7846687259ffcd96e8c2"
|
||||||
integrity sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg==
|
integrity sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg==
|
||||||
|
|
||||||
|
"@types/js-yaml@^4.0.8":
|
||||||
|
version "4.0.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.8.tgz#7574e422d70d4a1b41f517d1d9abc61be2299a97"
|
||||||
|
integrity sha512-m6jnPk1VhlYRiLFm3f8X9Uep761f+CK8mHyS65LutH2OhmBF0BeMEjHgg05usH8PLZMWWc/BUR9RPmkvpWnyRA==
|
||||||
|
|
||||||
"@types/linkify-it@*":
|
"@types/linkify-it@*":
|
||||||
version "3.0.3"
|
version "3.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-3.0.3.tgz#15a0712296c5041733c79efe233ba17ae5a7587b"
|
resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-3.0.3.tgz#15a0712296c5041733c79efe233ba17ae5a7587b"
|
||||||
@@ -2309,6 +2345,11 @@ [email protected]:
|
|||||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-0.0.8.tgz#1101e9544f4a76b1bc3b26d452ca96d7a35e7978"
|
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-0.0.8.tgz#1101e9544f4a76b1bc3b26d452ca96d7a35e7978"
|
||||||
integrity sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==
|
integrity sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==
|
||||||
|
|
||||||
|
base64-js@^1.3.1:
|
||||||
|
version "1.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||||
|
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
||||||
|
|
||||||
before-after-hook@^2.2.0:
|
before-after-hook@^2.2.0:
|
||||||
version "2.2.3"
|
version "2.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c"
|
resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c"
|
||||||
@@ -2324,6 +2365,14 @@ big.js@^5.2.2:
|
|||||||
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
|
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
|
||||||
integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
|
integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
|
||||||
|
|
||||||
|
bl@^1.0.0:
|
||||||
|
version "1.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.3.tgz#1e8dd80142eac80d7158c9dccc047fb620e035e7"
|
||||||
|
integrity sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==
|
||||||
|
dependencies:
|
||||||
|
readable-stream "^2.3.5"
|
||||||
|
safe-buffer "^5.1.1"
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
version "1.20.1"
|
version "1.20.1"
|
||||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668"
|
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668"
|
||||||
@@ -2366,11 +2415,42 @@ browserslist@^4.21.9:
|
|||||||
node-releases "^2.0.13"
|
node-releases "^2.0.13"
|
||||||
update-browserslist-db "^1.0.13"
|
update-browserslist-db "^1.0.13"
|
||||||
|
|
||||||
|
buffer-alloc-unsafe@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
|
||||||
|
integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==
|
||||||
|
|
||||||
|
buffer-alloc@^1.2.0:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec"
|
||||||
|
integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==
|
||||||
|
dependencies:
|
||||||
|
buffer-alloc-unsafe "^1.1.0"
|
||||||
|
buffer-fill "^1.0.0"
|
||||||
|
|
||||||
|
buffer-crc32@~0.2.3:
|
||||||
|
version "0.2.13"
|
||||||
|
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
|
||||||
|
integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==
|
||||||
|
|
||||||
|
buffer-fill@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
|
||||||
|
integrity sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==
|
||||||
|
|
||||||
buffer-from@^1.0.0:
|
buffer-from@^1.0.0:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
|
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
|
||||||
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
|
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
|
||||||
|
|
||||||
|
buffer@^5.2.1:
|
||||||
|
version "5.7.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
|
||||||
|
integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
|
||||||
|
dependencies:
|
||||||
|
base64-js "^1.3.1"
|
||||||
|
ieee754 "^1.1.13"
|
||||||
|
|
||||||
[email protected], busboy@^1.6.0:
|
[email protected], busboy@^1.6.0:
|
||||||
version "1.6.0"
|
version "1.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893"
|
resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893"
|
||||||
@@ -2472,6 +2552,11 @@ color-name@^1.1.4, color-name@~1.1.4:
|
|||||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
|
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
|
||||||
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||||
|
|
||||||
|
commander@^2.8.1:
|
||||||
|
version "2.20.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
||||||
|
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
|
||||||
|
|
||||||
commander@^4.1.1:
|
commander@^4.1.1:
|
||||||
version "4.1.1"
|
version "4.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
|
||||||
@@ -2516,6 +2601,11 @@ [email protected]:
|
|||||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
|
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
|
||||||
integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
|
integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
|
||||||
|
|
||||||
|
core-util-is@~1.0.0:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
|
||||||
|
integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==
|
||||||
|
|
||||||
cross-spawn@^7.0.0:
|
cross-spawn@^7.0.0:
|
||||||
version "7.0.3"
|
version "7.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
|
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
|
||||||
@@ -2578,6 +2668,59 @@ debug@^4.1.0, debug@^4.3.1, debug@^4.3.4:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ms "2.1.2"
|
ms "2.1.2"
|
||||||
|
|
||||||
|
decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1:
|
||||||
|
version "4.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1"
|
||||||
|
integrity sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==
|
||||||
|
dependencies:
|
||||||
|
file-type "^5.2.0"
|
||||||
|
is-stream "^1.1.0"
|
||||||
|
tar-stream "^1.5.2"
|
||||||
|
|
||||||
|
decompress-tarbz2@^4.0.0:
|
||||||
|
version "4.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz#3082a5b880ea4043816349f378b56c516be1a39b"
|
||||||
|
integrity sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==
|
||||||
|
dependencies:
|
||||||
|
decompress-tar "^4.1.0"
|
||||||
|
file-type "^6.1.0"
|
||||||
|
is-stream "^1.1.0"
|
||||||
|
seek-bzip "^1.0.5"
|
||||||
|
unbzip2-stream "^1.0.9"
|
||||||
|
|
||||||
|
decompress-targz@^4.0.0:
|
||||||
|
version "4.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/decompress-targz/-/decompress-targz-4.1.1.tgz#c09bc35c4d11f3de09f2d2da53e9de23e7ce1eee"
|
||||||
|
integrity sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==
|
||||||
|
dependencies:
|
||||||
|
decompress-tar "^4.1.1"
|
||||||
|
file-type "^5.2.0"
|
||||||
|
is-stream "^1.1.0"
|
||||||
|
|
||||||
|
decompress-unzip@^4.0.1:
|
||||||
|
version "4.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/decompress-unzip/-/decompress-unzip-4.0.1.tgz#deaaccdfd14aeaf85578f733ae8210f9b4848f69"
|
||||||
|
integrity sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw==
|
||||||
|
dependencies:
|
||||||
|
file-type "^3.8.0"
|
||||||
|
get-stream "^2.2.0"
|
||||||
|
pify "^2.3.0"
|
||||||
|
yauzl "^2.4.2"
|
||||||
|
|
||||||
|
decompress@^4.2.1:
|
||||||
|
version "4.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.1.tgz#007f55cc6a62c055afa37c07eb6a4ee1b773f118"
|
||||||
|
integrity sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==
|
||||||
|
dependencies:
|
||||||
|
decompress-tar "^4.0.0"
|
||||||
|
decompress-tarbz2 "^4.0.0"
|
||||||
|
decompress-targz "^4.0.0"
|
||||||
|
decompress-unzip "^4.0.1"
|
||||||
|
graceful-fs "^4.1.10"
|
||||||
|
make-dir "^1.0.0"
|
||||||
|
pify "^2.3.0"
|
||||||
|
strip-dirs "^2.0.0"
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
version "0.0.1"
|
version "0.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/deep-freeze/-/deep-freeze-0.0.1.tgz#3a0b0005de18672819dfd38cd31f91179c893e84"
|
resolved "https://registry.yarnpkg.com/deep-freeze/-/deep-freeze-0.0.1.tgz#3a0b0005de18672819dfd38cd31f91179c893e84"
|
||||||
@@ -2706,6 +2849,13 @@ encodeurl@~1.0.2:
|
|||||||
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
||||||
integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
|
integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
|
||||||
|
|
||||||
|
end-of-stream@^1.0.0:
|
||||||
|
version "1.4.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
|
||||||
|
integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
|
||||||
|
dependencies:
|
||||||
|
once "^1.4.0"
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
version "0.17.6"
|
version "0.17.6"
|
||||||
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.17.6.tgz#bbccd4433629deb6e0a83860b3b61da120ba4e01"
|
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.17.6.tgz#bbccd4433629deb6e0a83860b3b61da120ba4e01"
|
||||||
@@ -2883,11 +3033,33 @@ fastq@^1.6.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
reusify "^1.0.4"
|
reusify "^1.0.4"
|
||||||
|
|
||||||
|
fd-slicer@~1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
|
||||||
|
integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==
|
||||||
|
dependencies:
|
||||||
|
pend "~1.2.0"
|
||||||
|
|
||||||
fflate@^0.7.3:
|
fflate@^0.7.3:
|
||||||
version "0.7.4"
|
version "0.7.4"
|
||||||
resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.7.4.tgz#61587e5d958fdabb5a9368a302c25363f4f69f50"
|
resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.7.4.tgz#61587e5d958fdabb5a9368a302c25363f4f69f50"
|
||||||
integrity sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==
|
integrity sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==
|
||||||
|
|
||||||
|
file-type@^3.8.0:
|
||||||
|
version "3.9.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9"
|
||||||
|
integrity sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==
|
||||||
|
|
||||||
|
file-type@^5.2.0:
|
||||||
|
version "5.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/file-type/-/file-type-5.2.0.tgz#2ddbea7c73ffe36368dfae49dc338c058c2b8ad6"
|
||||||
|
integrity sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ==
|
||||||
|
|
||||||
|
file-type@^6.1.0:
|
||||||
|
version "6.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/file-type/-/file-type-6.2.0.tgz#e50cd75d356ffed4e306dc4f5bcf52a79903a919"
|
||||||
|
integrity sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==
|
||||||
|
|
||||||
fill-range@^7.0.1:
|
fill-range@^7.0.1:
|
||||||
version "7.0.1"
|
version "7.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
|
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
|
||||||
@@ -2951,6 +3123,11 @@ [email protected]:
|
|||||||
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
|
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
|
||||||
integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==
|
integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==
|
||||||
|
|
||||||
|
fs-constants@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
|
||||||
|
integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
|
||||||
|
|
||||||
fsevents@~2.3.2:
|
fsevents@~2.3.2:
|
||||||
version "2.3.3"
|
version "2.3.3"
|
||||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
|
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
|
||||||
@@ -2986,6 +3163,14 @@ get-nonce@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/get-nonce/-/get-nonce-1.0.1.tgz#fdf3f0278073820d2ce9426c18f07481b1e0cdf3"
|
resolved "https://registry.yarnpkg.com/get-nonce/-/get-nonce-1.0.1.tgz#fdf3f0278073820d2ce9426c18f07481b1e0cdf3"
|
||||||
integrity sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==
|
integrity sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==
|
||||||
|
|
||||||
|
get-stream@^2.2.0:
|
||||||
|
version "2.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de"
|
||||||
|
integrity sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==
|
||||||
|
dependencies:
|
||||||
|
object-assign "^4.0.1"
|
||||||
|
pinkie-promise "^2.0.0"
|
||||||
|
|
||||||
glob-parent@^5.1.2:
|
glob-parent@^5.1.2:
|
||||||
version "5.1.2"
|
version "5.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
|
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
|
||||||
@@ -3025,7 +3210,7 @@ globby@^13.1.3:
|
|||||||
merge2 "^1.4.1"
|
merge2 "^1.4.1"
|
||||||
slash "^4.0.0"
|
slash "^4.0.0"
|
||||||
|
|
||||||
graceful-fs@^4.1.2:
|
graceful-fs@^4.1.10, graceful-fs@^4.1.2:
|
||||||
version "4.2.11"
|
version "4.2.11"
|
||||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
|
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
|
||||||
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
|
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
|
||||||
@@ -3090,6 +3275,11 @@ identity-function@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/identity-function/-/identity-function-1.0.0.tgz#bea1159f0985239be3ca348edf40ce2f0dd2c21d"
|
resolved "https://registry.yarnpkg.com/identity-function/-/identity-function-1.0.0.tgz#bea1159f0985239be3ca348edf40ce2f0dd2c21d"
|
||||||
integrity sha512-kNrgUK0qI+9qLTBidsH85HjDLpZfrrS0ElquKKe/fJFdB3D7VeKdXXEvOPDUHSHOzdZKCAAaQIWWyp0l2yq6pw==
|
integrity sha512-kNrgUK0qI+9qLTBidsH85HjDLpZfrrS0ElquKKe/fJFdB3D7VeKdXXEvOPDUHSHOzdZKCAAaQIWWyp0l2yq6pw==
|
||||||
|
|
||||||
|
ieee754@^1.1.13:
|
||||||
|
version "1.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
||||||
|
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
|
||||||
|
|
||||||
ignore@^5.1.8, ignore@^5.2.4:
|
ignore@^5.1.8, ignore@^5.2.4:
|
||||||
version "5.2.4"
|
version "5.2.4"
|
||||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324"
|
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324"
|
||||||
@@ -3105,7 +3295,7 @@ indent-string@^4.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
|
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
|
||||||
integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
|
integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
|
||||||
|
|
||||||
[email protected]:
|
[email protected], inherits@~2.0.3:
|
||||||
version "2.0.4"
|
version "2.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||||
@@ -3144,6 +3334,11 @@ is-iterable@^1.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/is-iterable/-/is-iterable-1.1.1.tgz#71f9aa6f113e1d968ebe1d41cff4c8fb23a817bc"
|
resolved "https://registry.yarnpkg.com/is-iterable/-/is-iterable-1.1.1.tgz#71f9aa6f113e1d968ebe1d41cff4c8fb23a817bc"
|
||||||
integrity sha512-EdOZCr0NsGE00Pot+x1ZFx9MJK3C6wy91geZpXwvwexDLJvA4nzYyZf7r+EIwSeVsOLDdBz7ATg9NqKTzuNYuQ==
|
integrity sha512-EdOZCr0NsGE00Pot+x1ZFx9MJK3C6wy91geZpXwvwexDLJvA4nzYyZf7r+EIwSeVsOLDdBz7ATg9NqKTzuNYuQ==
|
||||||
|
|
||||||
|
is-natural-number@^4.0.1:
|
||||||
|
version "4.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8"
|
||||||
|
integrity sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ==
|
||||||
|
|
||||||
is-number@^4.0.0:
|
is-number@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff"
|
resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff"
|
||||||
@@ -3159,6 +3354,16 @@ is-plain-object@^5.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344"
|
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344"
|
||||||
integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==
|
integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==
|
||||||
|
|
||||||
|
is-stream@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
|
||||||
|
integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==
|
||||||
|
|
||||||
|
isarray@~1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
||||||
|
integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==
|
||||||
|
|
||||||
isexe@^2.0.0:
|
isexe@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
||||||
@@ -3325,6 +3530,13 @@ magic-string@^0.30.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@jridgewell/sourcemap-codec" "^1.4.15"
|
"@jridgewell/sourcemap-codec" "^1.4.15"
|
||||||
|
|
||||||
|
make-dir@^1.0.0:
|
||||||
|
version "1.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"
|
||||||
|
integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==
|
||||||
|
dependencies:
|
||||||
|
pify "^3.0.0"
|
||||||
|
|
||||||
map-obj@^2.0.0:
|
map-obj@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-2.0.0.tgz#a65cd29087a92598b8791257a523e021222ac1f9"
|
resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-2.0.0.tgz#a65cd29087a92598b8791257a523e021222ac1f9"
|
||||||
@@ -3483,6 +3695,11 @@ npm-normalize-package-bin@^3.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz#25447e32a9a7de1f51362c61a559233b89947832"
|
resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz#25447e32a9a7de1f51362c61a559233b89947832"
|
||||||
integrity sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==
|
integrity sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==
|
||||||
|
|
||||||
|
object-assign@^4.0.1:
|
||||||
|
version "4.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||||
|
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
|
||||||
|
|
||||||
object-inspect@^1.9.0:
|
object-inspect@^1.9.0:
|
||||||
version "1.12.3"
|
version "1.12.3"
|
||||||
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9"
|
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9"
|
||||||
@@ -3594,6 +3811,11 @@ pathe@^1.1.0, pathe@^1.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.1.tgz#1dd31d382b974ba69809adc9a7a347e65d84829a"
|
resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.1.tgz#1dd31d382b974ba69809adc9a7a347e65d84829a"
|
||||||
integrity sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==
|
integrity sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==
|
||||||
|
|
||||||
|
pend@~1.2.0:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
|
||||||
|
integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==
|
||||||
|
|
||||||
picocolors@^1.0.0:
|
picocolors@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
|
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
|
||||||
@@ -3604,6 +3826,28 @@ picomatch@^2.3.1:
|
|||||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
|
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
|
||||||
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
||||||
|
|
||||||
|
pify@^2.3.0:
|
||||||
|
version "2.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
|
||||||
|
integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==
|
||||||
|
|
||||||
|
pify@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
|
||||||
|
integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==
|
||||||
|
|
||||||
|
pinkie-promise@^2.0.0:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
|
||||||
|
integrity sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==
|
||||||
|
dependencies:
|
||||||
|
pinkie "^2.0.0"
|
||||||
|
|
||||||
|
pinkie@^2.0.0:
|
||||||
|
version "2.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
|
||||||
|
integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==
|
||||||
|
|
||||||
pkg-types@^1.0.3:
|
pkg-types@^1.0.3:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.0.3.tgz#988b42ab19254c01614d13f4f65a2cfc7880f868"
|
resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.0.3.tgz#988b42ab19254c01614d13f4f65a2cfc7880f868"
|
||||||
@@ -3660,6 +3904,11 @@ prisma@^5.4.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@prisma/engines" "5.4.1"
|
"@prisma/engines" "5.4.1"
|
||||||
|
|
||||||
|
process-nextick-args@~2.0.0:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
|
||||||
|
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
|
||||||
|
|
||||||
proxy-addr@~2.0.7:
|
proxy-addr@~2.0.7:
|
||||||
version "2.0.7"
|
version "2.0.7"
|
||||||
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
|
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
|
||||||
@@ -3754,6 +4003,19 @@ read-package-json-fast@^3.0.0:
|
|||||||
json-parse-even-better-errors "^3.0.0"
|
json-parse-even-better-errors "^3.0.0"
|
||||||
npm-normalize-package-bin "^3.0.0"
|
npm-normalize-package-bin "^3.0.0"
|
||||||
|
|
||||||
|
readable-stream@^2.3.0, readable-stream@^2.3.5:
|
||||||
|
version "2.3.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b"
|
||||||
|
integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==
|
||||||
|
dependencies:
|
||||||
|
core-util-is "~1.0.0"
|
||||||
|
inherits "~2.0.3"
|
||||||
|
isarray "~1.0.0"
|
||||||
|
process-nextick-args "~2.0.0"
|
||||||
|
safe-buffer "~5.1.1"
|
||||||
|
string_decoder "~1.1.1"
|
||||||
|
util-deprecate "~1.0.1"
|
||||||
|
|
||||||
regenerator-runtime@^0.14.0:
|
regenerator-runtime@^0.14.0:
|
||||||
version "0.14.0"
|
version "0.14.0"
|
||||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45"
|
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45"
|
||||||
@@ -3788,11 +4050,16 @@ run-parallel@^1.1.9:
|
|||||||
dependencies:
|
dependencies:
|
||||||
queue-microtask "^1.2.2"
|
queue-microtask "^1.2.2"
|
||||||
|
|
||||||
[email protected]:
|
[email protected], safe-buffer@^5.1.1:
|
||||||
version "5.2.1"
|
version "5.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||||
|
|
||||||
|
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||||
|
version "5.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||||
|
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
||||||
|
|
||||||
"safer-buffer@>= 2.1.2 < 3":
|
"safer-buffer@>= 2.1.2 < 3":
|
||||||
version "2.1.2"
|
version "2.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||||
@@ -3814,6 +4081,11 @@ satori@^0.10.8:
|
|||||||
postcss-value-parser "^4.2.0"
|
postcss-value-parser "^4.2.0"
|
||||||
yoga-wasm-web "^0.3.3"
|
yoga-wasm-web "^0.3.3"
|
||||||
|
|
||||||
|
sax@^1.2.4:
|
||||||
|
version "1.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0"
|
||||||
|
integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==
|
||||||
|
|
||||||
scheduler@^0.23.0:
|
scheduler@^0.23.0:
|
||||||
version "0.23.0"
|
version "0.23.0"
|
||||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
|
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
|
||||||
@@ -3821,6 +4093,13 @@ scheduler@^0.23.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
loose-envify "^1.1.0"
|
loose-envify "^1.1.0"
|
||||||
|
|
||||||
|
seek-bzip@^1.0.5:
|
||||||
|
version "1.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.6.tgz#35c4171f55a680916b52a07859ecf3b5857f21c4"
|
||||||
|
integrity sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==
|
||||||
|
dependencies:
|
||||||
|
commander "^2.8.1"
|
||||||
|
|
||||||
semver@^6.3.1:
|
semver@^6.3.1:
|
||||||
version "6.3.1"
|
version "6.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
|
||||||
@@ -3963,6 +4242,13 @@ string.prototype.codepointat@^0.2.1:
|
|||||||
resolved "https://registry.yarnpkg.com/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz#004ad44c8afc727527b108cd462b4d971cd469bc"
|
resolved "https://registry.yarnpkg.com/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz#004ad44c8afc727527b108cd462b4d971cd469bc"
|
||||||
integrity sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg==
|
integrity sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg==
|
||||||
|
|
||||||
|
string_decoder@~1.1.1:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
|
||||||
|
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
|
||||||
|
dependencies:
|
||||||
|
safe-buffer "~5.1.0"
|
||||||
|
|
||||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||||
name strip-ansi-cjs
|
name strip-ansi-cjs
|
||||||
version "6.0.1"
|
version "6.0.1"
|
||||||
@@ -3978,6 +4264,13 @@ strip-ansi@^7.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ansi-regex "^6.0.1"
|
ansi-regex "^6.0.1"
|
||||||
|
|
||||||
|
strip-dirs@^2.0.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/strip-dirs/-/strip-dirs-2.1.0.tgz#4987736264fc344cf20f6c34aca9d13d1d4ed6c5"
|
||||||
|
integrity sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==
|
||||||
|
dependencies:
|
||||||
|
is-natural-number "^4.0.1"
|
||||||
|
|
||||||
strip-json-comments@^5.0.0:
|
strip-json-comments@^5.0.0:
|
||||||
version "5.0.1"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-5.0.1.tgz#0d8b7d01b23848ed7dbdf4baaaa31a8250d8cfa0"
|
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-5.0.1.tgz#0d8b7d01b23848ed7dbdf4baaaa31a8250d8cfa0"
|
||||||
@@ -4014,11 +4307,34 @@ tabbable@^6.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-6.2.0.tgz#732fb62bc0175cfcec257330be187dcfba1f3b97"
|
resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-6.2.0.tgz#732fb62bc0175cfcec257330be187dcfba1f3b97"
|
||||||
integrity sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==
|
integrity sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==
|
||||||
|
|
||||||
|
tar-stream@^1.5.2:
|
||||||
|
version "1.6.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555"
|
||||||
|
integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==
|
||||||
|
dependencies:
|
||||||
|
bl "^1.0.0"
|
||||||
|
buffer-alloc "^1.2.0"
|
||||||
|
end-of-stream "^1.0.0"
|
||||||
|
fs-constants "^1.0.0"
|
||||||
|
readable-stream "^2.3.0"
|
||||||
|
to-buffer "^1.1.1"
|
||||||
|
xtend "^4.0.0"
|
||||||
|
|
||||||
|
through@^2.3.8:
|
||||||
|
version "2.3.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
|
||||||
|
integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==
|
||||||
|
|
||||||
tiny-inflate@^1.0.0:
|
tiny-inflate@^1.0.0:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.3.tgz#122715494913a1805166aaf7c93467933eea26c4"
|
resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.3.tgz#122715494913a1805166aaf7c93467933eea26c4"
|
||||||
integrity sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==
|
integrity sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==
|
||||||
|
|
||||||
|
to-buffer@^1.1.1:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80"
|
||||||
|
integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==
|
||||||
|
|
||||||
to-fast-properties@^2.0.0:
|
to-fast-properties@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
|
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
|
||||||
@@ -4083,6 +4399,14 @@ ufo@^1.3.0:
|
|||||||
resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.3.0.tgz#c92f8ac209daff607c57bbd75029e190930a0019"
|
resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.3.0.tgz#c92f8ac209daff607c57bbd75029e190930a0019"
|
||||||
integrity sha512-bRn3CsoojyNStCZe0BG0Mt4Nr/4KF+rhFlnNXybgqt5pXHNFRlqinSoQaTrGyzE4X8aHplSb+TorH+COin9Yxw==
|
integrity sha512-bRn3CsoojyNStCZe0BG0Mt4Nr/4KF+rhFlnNXybgqt5pXHNFRlqinSoQaTrGyzE4X8aHplSb+TorH+COin9Yxw==
|
||||||
|
|
||||||
|
unbzip2-stream@^1.0.9:
|
||||||
|
version "1.4.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7"
|
||||||
|
integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==
|
||||||
|
dependencies:
|
||||||
|
buffer "^5.2.1"
|
||||||
|
through "^2.3.8"
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
version "5.22.1"
|
version "5.22.1"
|
||||||
resolved "https://registry.yarnpkg.com/undici/-/undici-5.22.1.tgz#877d512effef2ac8be65e695f3586922e1a57d7b"
|
resolved "https://registry.yarnpkg.com/undici/-/undici-5.22.1.tgz#877d512effef2ac8be65e695f3586922e1a57d7b"
|
||||||
@@ -4143,6 +4467,11 @@ [email protected]:
|
|||||||
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
|
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
|
||||||
integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==
|
integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==
|
||||||
|
|
||||||
|
util-deprecate@~1.0.1:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||||
|
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
||||||
@@ -4294,11 +4623,31 @@ ws@^8.13.0:
|
|||||||
resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f"
|
resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f"
|
||||||
integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==
|
integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==
|
||||||
|
|
||||||
|
xml-js@^1.6.11:
|
||||||
|
version "1.6.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/xml-js/-/xml-js-1.6.11.tgz#927d2f6947f7f1c19a316dd8eea3614e8b18f8e9"
|
||||||
|
integrity sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==
|
||||||
|
dependencies:
|
||||||
|
sax "^1.2.4"
|
||||||
|
|
||||||
|
xtend@^4.0.0:
|
||||||
|
version "4.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
||||||
|
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
|
||||||
|
|
||||||
yallist@^3.0.2:
|
yallist@^3.0.2:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
|
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
|
||||||
integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
|
integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
|
||||||
|
|
||||||
|
yauzl@^2.4.2:
|
||||||
|
version "2.10.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
|
||||||
|
integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==
|
||||||
|
dependencies:
|
||||||
|
buffer-crc32 "~0.2.3"
|
||||||
|
fd-slicer "~1.1.0"
|
||||||
|
|
||||||
yocto-queue@^0.1.0:
|
yocto-queue@^0.1.0:
|
||||||
version "0.1.0"
|
version "0.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
||||||
|
|||||||