mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 10:47:04 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a0afee97a | ||
|
|
3cca68a8f0 | ||
|
|
622cd92a13 | ||
|
|
6d9e6973d5 | ||
|
|
098e6c09b9 | ||
|
|
186d45e3d9 | ||
|
|
0fdc87a88c | ||
|
|
1d4ac2f133 | ||
|
|
21158e9081 | ||
|
|
fedad8df3e | ||
|
|
6c8f5ca478 | ||
|
|
463af106be | ||
|
|
feeb850ca9 | ||
|
|
80ffff322c | ||
|
|
0833375562 | ||
|
|
297f2f6580 | ||
|
|
438feefa83 | ||
|
|
8f6388b42b | ||
|
|
405f544774 | ||
|
|
dd0644e09f | ||
|
|
d8ac0b2ef6 | ||
|
|
87955214e3 | ||
|
|
f985cbf70b | ||
|
|
f64e72c312 | ||
|
|
ce6715cf2c | ||
|
|
18e5405c54 | ||
|
|
a0f15fc1c4 | ||
|
|
cf20ae38d4 | ||
|
|
2bf5eb78be | ||
|
|
57271e3c23 | ||
|
|
2258b0f936 | ||
|
|
aa8115d260 | ||
|
|
f8dfa386a0 | ||
|
|
00771e130f | ||
|
|
bd612bf777 | ||
|
|
ca208e44a1 | ||
|
|
72c4cd664b | ||
|
|
f907c79d68 | ||
|
|
ae3d0a08cd | ||
|
|
fb66027de0 |
No files matched your search
@@ -1,4 +1,4 @@
|
|||||||
name: Host
|
name: CD Host
|
||||||
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
name: CD Ratings Leaderboard North America
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 5 * * *'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
host:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Get code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Use node
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 18
|
||||||
|
|
||||||
|
- name: Generate leaderboard
|
||||||
|
run: GH_TOKEN=${{ secrets.GH_TOKEN }} yarn publish:ratingsleaderboard --region=asia
|
||||||
@@ -16,7 +16,7 @@ jobs:
|
|||||||
node-version: [18.x]
|
node-version: [18.x]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Get code
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Use node
|
- name: Use node
|
||||||
|
|||||||
+2
-1
@@ -5,9 +5,10 @@ package-lock.json
|
|||||||
/playwright-report/
|
/playwright-report/
|
||||||
/playwright/.cache/
|
/playwright/.cache/
|
||||||
dist/
|
dist/
|
||||||
|
dist.*
|
||||||
yarn-error.log
|
yarn-error.log
|
||||||
dist.zip
|
|
||||||
commands.txt
|
commands.txt
|
||||||
/test.*
|
/test.*
|
||||||
/docs/.vitepress/dist
|
/docs/.vitepress/dist
|
||||||
/docs/.vitepress/cache
|
/docs/.vitepress/cache
|
||||||
|
servers.csv
|
||||||
Vendored
+6
@@ -7,12 +7,16 @@
|
|||||||
"cleandist",
|
"cleandist",
|
||||||
"customperiod",
|
"customperiod",
|
||||||
"Everytime",
|
"Everytime",
|
||||||
|
"fullstats",
|
||||||
"fuzzysort",
|
"fuzzysort",
|
||||||
"kranvagn",
|
"kranvagn",
|
||||||
|
"Leaderboard",
|
||||||
|
"leaderboards",
|
||||||
"outfile",
|
"outfile",
|
||||||
"ownedtanks",
|
"ownedtanks",
|
||||||
"playerachievements",
|
"playerachievements",
|
||||||
"playerinfo",
|
"playerinfo",
|
||||||
|
"ratingsleaderboard",
|
||||||
"Resvg",
|
"Resvg",
|
||||||
"roboto",
|
"roboto",
|
||||||
"searchclans",
|
"searchclans",
|
||||||
@@ -22,10 +26,12 @@
|
|||||||
"Smri",
|
"Smri",
|
||||||
"startcase",
|
"startcase",
|
||||||
"statsfull",
|
"statsfull",
|
||||||
|
"Stringifying",
|
||||||
"strvk",
|
"strvk",
|
||||||
"tankaverages",
|
"tankaverages",
|
||||||
"tankopedia",
|
"tankopedia",
|
||||||
"tankstats",
|
"tankstats",
|
||||||
|
"tresabhi",
|
||||||
"Unicum",
|
"Unicum",
|
||||||
"Unregistering",
|
"Unregistering",
|
||||||
"vickers",
|
"vickers",
|
||||||
|
|||||||
+10
-10
@@ -17,23 +17,23 @@ diverse, inclusive, and healthy community.
|
|||||||
Examples of behavior that contributes to a positive environment for our
|
Examples of behavior that contributes to a positive environment for our
|
||||||
community include:
|
community include:
|
||||||
|
|
||||||
* Demonstrating empathy and kindness toward other people
|
- Demonstrating empathy and kindness toward other people
|
||||||
* Being respectful of differing opinions, viewpoints, and experiences
|
- Being respectful of differing opinions, viewpoints, and experiences
|
||||||
* Giving and gracefully accepting constructive feedback
|
- Giving and gracefully accepting constructive feedback
|
||||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
- Accepting responsibility and apologizing to those affected by our mistakes,
|
||||||
and learning from the experience
|
and learning from the experience
|
||||||
* Focusing on what is best not just for us as individuals, but for the
|
- Focusing on what is best not just for us as individuals, but for the
|
||||||
overall community
|
overall community
|
||||||
|
|
||||||
Examples of unacceptable behavior include:
|
Examples of unacceptable behavior include:
|
||||||
|
|
||||||
* The use of sexualized language or imagery, and sexual attention or
|
- The use of sexualized language or imagery, and sexual attention or
|
||||||
advances of any kind
|
advances of any kind
|
||||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
* Public or private harassment
|
- Public or private harassment
|
||||||
* Publishing others' private information, such as a physical or email
|
- Publishing others' private information, such as a physical or email
|
||||||
address, without their explicit permission
|
address, without their explicit permission
|
||||||
* Other conduct which could reasonably be considered inappropriate in a
|
- Other conduct which could reasonably be considered inappropriate in a
|
||||||
professional setting
|
professional setting
|
||||||
|
|
||||||
## Enforcement Responsibilities
|
## Enforcement Responsibilities
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
// https://vitepress.dev/guide/custom-theme
|
// https://vitepress.dev/guide/custom-theme
|
||||||
import { h } from 'vue'
|
import { h } from 'vue';
|
||||||
import Theme from 'vitepress/theme'
|
import Theme from 'vitepress/theme';
|
||||||
import './style.css'
|
import './style.css';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
extends: Theme,
|
extends: Theme,
|
||||||
Layout: () => {
|
Layout: () => {
|
||||||
return h(Theme.Layout, null, {
|
return h(Theme.Layout, null, {
|
||||||
// https://vitepress.dev/guide/extending-default-theme#layout-slots
|
// https://vitepress.dev/guide/extending-default-theme#layout-slots
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
enhanceApp({ app, router, siteData }) {
|
enhanceApp({ app, router, siteData }) {
|
||||||
// ...
|
// ...
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
* Colors
|
* Colors
|
||||||
* -------------------------------------------------------------------------- */
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--vp-c-brand: #646cff;
|
--vp-c-brand: #646cff;
|
||||||
--vp-c-brand-light: #747bff;
|
--vp-c-brand-light: #747bff;
|
||||||
--vp-c-brand-lighter: #9499ff;
|
--vp-c-brand-lighter: #9499ff;
|
||||||
@@ -88,4 +88,3 @@
|
|||||||
.DocSearch {
|
.DocSearch {
|
||||||
--docsearch-primary-color: var(--vp-c-brand) !important;
|
--docsearch-primary-color: var(--vp-c-brand) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# Blitzkrieg 1.10.0
|
||||||
|
|
||||||
|
This update addresses many readability issues that users have reported over time.
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
- `/ownedtanks`
|
||||||
|
- Tanks are now displayed in two columns
|
||||||
|
- Collector and premium tanks now have their own colors
|
||||||
|
- Tank types are also displayed
|
||||||
|
- A stylistic image of the tank is also displayed for each tank to the right
|
||||||
|
- `/fullstats`
|
||||||
|
- Was previously called `/statsfull`; this was changed to avoid the clutter that would show up when searching for `/stats`
|
||||||
|
- Added four catagories in which all the stats are sorted into
|
||||||
|
- Catagories now have backgrounds to compartmentalize them
|
||||||
|
- `/evolution`
|
||||||
|
- Gave the axis markings a high contrast color
|
||||||
|
- Added a label for the x axis affirming that it is plotted over the number of battles
|
||||||
|
- `/today`
|
||||||
|
- Added a border to all the rows to compartmentalize them
|
||||||
|
- This command can now generate embeds too
|
||||||
|
- Removed survivability
|
||||||
|
- Added a limit option to cut off after a certain number of tanks played (this is mainly intended to be used by streamers to limit the content of the embeds)
|
||||||
|
- Tanks are now displayed in order of the most recent battle
|
||||||
|
- `/help`
|
||||||
|
- A significant amount of subcommands were added
|
||||||
|
- These new subcommands now return normal Discord messages instead of embeds in favor of readability
|
||||||
|
- The contents of the replies are directly fetched from the Blitzkrieg documentation
|
||||||
|
- Time zones
|
||||||
|
- What is considered a "day's worth of battles" has been adjusted as the "reset time" was the same for all Blitz servers
|
||||||
|
- The reset time is now dependent on the server of the player
|
||||||
|
- Use `/help timezones` to learn more
|
||||||
|
|
||||||
|
## Technical Changes
|
||||||
|
|
||||||
|
- All themes have been merged to leverage Bepaint's suffixes
|
||||||
|
- Switched to Node module resolution
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# Blitzkrieg 1.10.1
|
||||||
|
|
||||||
|
This is a bit of an experiment.
|
||||||
|
|
||||||
|
## Technical Changes
|
||||||
|
|
||||||
|
- Every midnight in the Central North American Time, a permanent record of the entire ratings leaderboard will be stored for North America
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# Blitzkrieg 1.9.4
|
||||||
|
|
||||||
|
Haha, I lied. More fixes.
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
- Tank names once again show when searching for tank stats
|
||||||
|
- Tier weights now ignore tanks without entries in the `tankopedia`
|
||||||
|
|
||||||
|
## Technical Changes
|
||||||
|
|
||||||
|
- All `tankopedia` derivatives are now promises
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Blitzkrieg 1.9.5
|
||||||
|
|
||||||
|
Hopefully this is the last round of fixes for Blitzkrieg `1.9`.
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
- All commands have been tweaked to work even with a lack of a tankopedia entry
|
||||||
|
- This should fix test tanks not showing up
|
||||||
|
- `/today` now properly preserves all WN8 entries
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# About
|
||||||
|
|
||||||
|
Blitzkrieg is a multipurpose bot for anything and everything World of Tanks Blitz.
|
||||||
|
|
||||||
|
- Track statistics
|
||||||
|
- Find inactive players in a clan
|
||||||
|
- Lookup owned tanks
|
||||||
|
- Create embeds for streams
|
||||||
|
- Track a player/tanks statistical evolution
|
||||||
|
- ...and a variety of other similar things
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# Embeds
|
||||||
|
|
||||||
|
Embeds are generated by a few commands as a URL that can be loaded in a browser/streaming software.
|
||||||
|
|
||||||
|
## Creating Embeds
|
||||||
|
|
||||||
|
1. Use a command of your liking (for example, `/stats`).
|
||||||
|
2. Customize the command however you like and the URL generated for the embed will match your needs.
|
||||||
|
3. Send the command and when the bot is "done thinking," an "Embed" button will appear below the bot's reply.
|
||||||
|
|
||||||
|
## Using Embeds in Streaming Softwares
|
||||||
|
|
||||||
|
1. Click on the "Embed" button.
|
||||||
|
2. Copy the URL from your browser.
|
||||||
|
3. Paste the URL into a Browser Source.
|
||||||
|
4. Set the refresh rate/FPS to something very low like `0.0167` (that's 1 frame every 60 seconds).
|
||||||
|
|
||||||
|
## How Embeds Behave
|
||||||
|
|
||||||
|
- They load like any normal website albeit with a significant delay.
|
||||||
|
- They auto refresh every `60s`.
|
||||||
|
- All embeds and images generated by Blitzkrieg have a width of `640px` but the height varies and must be found manually through trial and error.
|
||||||
|
- It's okay to have a Browser Source that has a height larger than needed as Blitzkrieg embeds are transparent.
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# Inviting the Bot
|
||||||
|
|
||||||
|
It's quite simple really. Just follow the steps below:
|
||||||
|
|
||||||
|
1. Join a Discord server that already has Blitzkrieg installed or [the official Discord Server](https://discord.gg/nDt7AjGJQH).
|
||||||
|
2. Click on the bot's profile.
|
||||||
|
3. Click the "Add to Server" button under the bot's name and ID.
|
||||||
|
4. Select what server you want it in and see what permissions you seem fit for the bot.
|
||||||
|
|
||||||
|
Voila! You're ready to go.
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Timezones
|
||||||
|
|
||||||
|
What is considered a "day's worth of games" is determined by the reset time. The reset time is generally at midnight everyday after which newer games are counted towards the next day. Reset times vary from region to region:
|
||||||
|
|
||||||
|
- North America: `UTC-5:00` (Central North American Time midnight)
|
||||||
|
- Europe: `UTC+1:00` (Central European Time midnight)
|
||||||
|
- Asia: `UTC+7:00` (Central Asia Standard Time midnight)
|
||||||
|
|
||||||
|
**Reset times are different from Blitz Stars** (Blitz Stars resets at 5:00 am locally relative to the user opening their website). Inconsistencies with Blitz Stars only occur with sleep-deprived players and players who play in the wrong region.
|
||||||
+7
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "blitzkrieg",
|
"name": "blitzkrieg",
|
||||||
"version": "1.9.3",
|
"version": "1.10.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "src/server.ts",
|
"main": "src/server.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
"build:bot": "node scripts/build --build=bot",
|
"build:bot": "node scripts/build --build=bot",
|
||||||
"build:server": "node scripts/build --build=server",
|
"build:server": "node scripts/build --build=server",
|
||||||
"build:docs": "vitepress build docs",
|
"build:docs": "vitepress build docs",
|
||||||
|
"build:ratingsleaderboard": "node scripts/generateRatingsLeaderboard",
|
||||||
"build": "node scripts/build --build=all",
|
"build": "node scripts/build --build=all",
|
||||||
"dev:bot": "yarn build:bot --dev && yarn start:bot",
|
"dev:bot": "yarn build:bot --dev && yarn start:bot",
|
||||||
"dev:server": "yarn build:server --dev && yarn start:server",
|
"dev:server": "yarn build:server --dev && yarn start:server",
|
||||||
@@ -15,8 +16,10 @@
|
|||||||
"start:bot": "node --enable-source-maps --trace-warnings dist/bot.cjs",
|
"start:bot": "node --enable-source-maps --trace-warnings dist/bot.cjs",
|
||||||
"start:server": "node --enable-source-maps --trace-warnings dist/server.cjs",
|
"start:server": "node --enable-source-maps --trace-warnings dist/server.cjs",
|
||||||
"host:docs": "vitepress preview docs",
|
"host:docs": "vitepress preview docs",
|
||||||
|
"publish:ratingsleaderboard": "node scripts/generateRatingsLeaderboard --publish",
|
||||||
"zip": "zip -r dist.zip dist",
|
"zip": "zip -r dist.zip dist",
|
||||||
"lint": "npx tsc"
|
"lint": "npx tsc",
|
||||||
|
"format": "prettier --write \"**/*.@(json|js|jsx|css|md|ts|tsx|html|xml|yml|gltf)\""
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -35,6 +38,7 @@
|
|||||||
"vitepress-plugin-auto-sidebar": "^1.1.0"
|
"vitepress-plugin-auto-sidebar": "^1.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@octokit/rest": "^20.0.1",
|
||||||
"@total-typescript/ts-reset": "^0.4.2",
|
"@total-typescript/ts-reset": "^0.4.2",
|
||||||
"@types/escape-html": "^1.0.2",
|
"@types/escape-html": "^1.0.2",
|
||||||
"@types/express": "^4.17.17",
|
"@types/express": "^4.17.17",
|
||||||
@@ -42,6 +46,7 @@
|
|||||||
"@types/markdown-escape": "^1.1.0",
|
"@types/markdown-escape": "^1.1.0",
|
||||||
"@types/node": "^20.2.1",
|
"@types/node": "^20.2.1",
|
||||||
"@types/react": "^18.2.7",
|
"@types/react": "^18.2.7",
|
||||||
|
"prettier": "^3.0.0",
|
||||||
"typescript": "^5.0.4",
|
"typescript": "^5.0.4",
|
||||||
"vitepress": "^1.0.0-beta.3"
|
"vitepress": "^1.0.0-beta.3"
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-12
@@ -4,22 +4,22 @@ import { argv } from 'process';
|
|||||||
|
|
||||||
const isProd = !argv.includes('--dev');
|
const isProd = !argv.includes('--dev');
|
||||||
const buildAll = argv.includes('--build=all');
|
const buildAll = argv.includes('--build=all');
|
||||||
const buildBot = argv.includes('--build=bot') | buildAll;
|
const buildBot = argv.includes('--build=bot') || buildAll;
|
||||||
const buildServer = argv.includes('--build=server') | buildAll;
|
const buildServer = argv.includes('--build=server') || buildAll;
|
||||||
|
|
||||||
if (isProd) {
|
if (isProd) {
|
||||||
// only remove in production to ensure no time is wasted in dev
|
// only remove in production to ensure no time is wasted in dev
|
||||||
console.log('🟡 Removing dist...');
|
console.log('Removing dist...');
|
||||||
await rm('dist', { recursive: true, force: true });
|
await rm('dist', { recursive: true, force: true });
|
||||||
console.log('✅ Dist removed');
|
console.log('Dist removed');
|
||||||
|
|
||||||
console.log('🟡 Creating dist...');
|
console.log('Creating dist...');
|
||||||
await mkdir('dist');
|
await mkdir('dist');
|
||||||
console.log('✅ Dist created');
|
console.log('Dist created');
|
||||||
|
|
||||||
console.log('🟡 Copying package.json...');
|
console.log('Copying package.json...');
|
||||||
copyFile('package.dist.json', 'dist/package.json').then(() =>
|
copyFile('package.dist.json', 'dist/package.json').then(() =>
|
||||||
console.log('✅ package.json copied'),
|
console.log('package.json copied'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,25 +40,25 @@ const commonOptions = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (buildBot) {
|
if (buildBot) {
|
||||||
console.log('🟡 Building bot...');
|
console.log('Building bot...');
|
||||||
build({
|
build({
|
||||||
...commonOptions,
|
...commonOptions,
|
||||||
|
|
||||||
entryPoints: ['src/bot.ts'],
|
entryPoints: ['src/bot.ts'],
|
||||||
outfile: 'dist/bot.cjs',
|
outfile: 'dist/bot.cjs',
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
console.log('✅ Bot built');
|
console.log('Bot built');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buildServer) {
|
if (buildServer) {
|
||||||
console.log('🟡 Building server...');
|
console.log('Building server...');
|
||||||
build({
|
build({
|
||||||
...commonOptions,
|
...commonOptions,
|
||||||
|
|
||||||
entryPoints: ['src/server.ts'],
|
entryPoints: ['src/server.ts'],
|
||||||
outfile: 'dist/server.cjs',
|
outfile: 'dist/server.cjs',
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
console.log('✅ Server built');
|
console.log('Server built');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
export default async function commitMultipleFiles(
|
||||||
|
octokit,
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
branch,
|
||||||
|
message,
|
||||||
|
changes,
|
||||||
|
) {
|
||||||
|
// Get the latest commit SHA on the specified branch
|
||||||
|
const { data: refData } = await octokit.git.getRef({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
ref: `heads/${branch}`,
|
||||||
|
});
|
||||||
|
const latestCommitSha = refData.object.sha;
|
||||||
|
|
||||||
|
// Get the tree SHA for the latest commit
|
||||||
|
const { data: commitData } = await octokit.git.getCommit({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
commit_sha: latestCommitSha,
|
||||||
|
});
|
||||||
|
const treeSha = commitData.tree.sha;
|
||||||
|
|
||||||
|
// Create blobs for each file
|
||||||
|
const blobs = await Promise.all(
|
||||||
|
changes.map(async (change) => {
|
||||||
|
const { data: blobData } = await octokit.git.createBlob({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
content: change.content,
|
||||||
|
encoding: 'utf-8',
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
sha: blobData.sha,
|
||||||
|
path: change.path,
|
||||||
|
mode: '100644',
|
||||||
|
type: 'blob',
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create a new tree with the new blobs
|
||||||
|
const { data: treeData } = await octokit.git.createTree({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
base_tree: treeSha,
|
||||||
|
tree: blobs,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create a new commit with the new tree
|
||||||
|
const { data: newCommitData } = await octokit.git.createCommit({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
message,
|
||||||
|
tree: treeData.sha,
|
||||||
|
parents: [latestCommitSha],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update the branch reference to point to the new commit
|
||||||
|
await octokit.git.updateRef({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
ref: `heads/${branch}`,
|
||||||
|
sha: newCommitData.sha,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
import { Octokit } from '@octokit/rest';
|
||||||
|
import { rm, writeFile } from 'fs/promises';
|
||||||
|
import { argv, env } from 'process';
|
||||||
|
import commitMultipleFiles from './commitMultipleFiles.js';
|
||||||
|
|
||||||
|
/*
|
||||||
|
For Central North American Time (UTC-5), use 0 5 * * *.
|
||||||
|
For Central European Time (UTC+1), use 0 23 * * *.
|
||||||
|
For Central Asia Standard Time (UTC+7), use 0 17 * * *.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const publish = argv.includes('--publish');
|
||||||
|
const server = argv.find((arg) => arg.startsWith('--region='))?.split('=')[1];
|
||||||
|
|
||||||
|
if (!server) throw new Error('Region parameter not specified');
|
||||||
|
|
||||||
|
const NEIGHBORS = 1000;
|
||||||
|
const PLAYERS = await fetch(
|
||||||
|
`https://${server}.wotblitz.com/en/api/rating-leaderboards/season/`,
|
||||||
|
)
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((data) => data.count);
|
||||||
|
|
||||||
|
const players = {};
|
||||||
|
const leaderboard = [];
|
||||||
|
let registered = 0;
|
||||||
|
|
||||||
|
async function branchFromPlayer(id) {
|
||||||
|
const { neighbors } = await fetch(
|
||||||
|
`https://${server}.wotblitz.com/en/api/rating-leaderboards/user/${id}/?neighbors=${NEIGHBORS}`,
|
||||||
|
).then((response) => response.json());
|
||||||
|
|
||||||
|
const firstPlayer = neighbors[0];
|
||||||
|
const lastPlayer = neighbors[neighbors.length - 1];
|
||||||
|
|
||||||
|
const branches = [];
|
||||||
|
if (!players[firstPlayer.number])
|
||||||
|
branches.push(branchFromPlayer(firstPlayer.spa_id));
|
||||||
|
if (!players[lastPlayer.number])
|
||||||
|
branches.push(branchFromPlayer(lastPlayer.spa_id));
|
||||||
|
|
||||||
|
neighbors.forEach((neighbor) => {
|
||||||
|
if (players[neighbor.number]) return;
|
||||||
|
|
||||||
|
players[neighbor.number] = neighbor;
|
||||||
|
registered++;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`Registered ${registered} / ${PLAYERS} (${(
|
||||||
|
(registered / PLAYERS) *
|
||||||
|
100
|
||||||
|
).toFixed(2)}%)`,
|
||||||
|
);
|
||||||
|
|
||||||
|
await Promise.all(branches);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function branchFromLeague(id) {
|
||||||
|
const { result } = await fetch(
|
||||||
|
`https://${server}.wotblitz.com/en/api/rating-leaderboards/league/${id}/top/`,
|
||||||
|
).then((response) => response.json());
|
||||||
|
|
||||||
|
const firstPlayer = result[0];
|
||||||
|
const lastPlayer = result[result.length - 1];
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
branchFromPlayer(firstPlayer.spa_id),
|
||||||
|
branchFromPlayer(lastPlayer.spa_id),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Branching form leagues...');
|
||||||
|
await Promise.all([
|
||||||
|
branchFromLeague(0),
|
||||||
|
branchFromLeague(1),
|
||||||
|
branchFromLeague(2),
|
||||||
|
branchFromLeague(3),
|
||||||
|
branchFromLeague(4),
|
||||||
|
]);
|
||||||
|
|
||||||
|
console.log('Converting to an array...');
|
||||||
|
for (let index = 0; index < PLAYERS; index++) {
|
||||||
|
leaderboard[index] = players[index + 1];
|
||||||
|
if (!leaderboard[index]) console.warn(`${index} had no player`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const leaderboardJSON = JSON.stringify(leaderboard);
|
||||||
|
|
||||||
|
if (publish) {
|
||||||
|
const octokit = new Octokit({ auth: env.GH_TOKEN });
|
||||||
|
|
||||||
|
const info = await fetch(
|
||||||
|
`https://${server}.wotblitz.com/en/api/rating-leaderboards/season/`,
|
||||||
|
).then((response) => response.json());
|
||||||
|
const time = Math.round(Date.now() / 1000);
|
||||||
|
const normalizedServer = server === 'na' ? 'com' : server;
|
||||||
|
|
||||||
|
const infoPath = `${normalizedServer}/ratings/seasons/${info.current_season}.json`;
|
||||||
|
const leaderboardPath = `${normalizedServer}/ratings/leaderboards/${info.current_season}/${time}.json`;
|
||||||
|
const infoJSON = JSON.stringify(info);
|
||||||
|
|
||||||
|
console.log(`Publishing to season ${info.current_season}...`);
|
||||||
|
commitMultipleFiles(
|
||||||
|
octokit,
|
||||||
|
'tresabhi',
|
||||||
|
'blitzkrieg-db',
|
||||||
|
'main',
|
||||||
|
`${info.current_season}/${time}`,
|
||||||
|
[
|
||||||
|
{ path: infoPath, content: infoJSON },
|
||||||
|
{ path: leaderboardPath, content: leaderboardJSON },
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.log('Writing to disk...');
|
||||||
|
await rm('dist.json', { force: true });
|
||||||
|
await writeFile('dist.json', leaderboardJSON);
|
||||||
|
}
|
||||||
+5
-5
@@ -1,9 +1,9 @@
|
|||||||
import { Client, GatewayIntentBits } from 'discord.js';
|
import { Client, GatewayIntentBits } from 'discord.js';
|
||||||
import { DISCORD_TOKEN } from './core/node/arguments.js';
|
import { DISCORD_TOKEN } from './core/node/arguments';
|
||||||
import { registerErrorHandlers } from './events/error.js';
|
import { registerErrorHandlers } from './events/error';
|
||||||
import guildMemberAdd from './events/guildMemberAdd.js';
|
import guildMemberAdd from './events/guildMemberAdd';
|
||||||
import interactionCreate from './events/interactionCreate/index.js';
|
import interactionCreate from './events/interactionCreate';
|
||||||
import ready from './events/ready.js';
|
import ready from './events/ready';
|
||||||
|
|
||||||
export const client = new Client({
|
export const client = new Client({
|
||||||
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers],
|
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers],
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import { SlashCommandBuilder } from 'discord.js';
|
import { SlashCommandBuilder } from 'discord.js';
|
||||||
import packageJSON from '../../package.json' assert { type: 'json' };
|
import packageJSON from '../../package.json' assert { type: 'json' };
|
||||||
import { client } from '../bot.js';
|
import { client } from '../bot';
|
||||||
import { tankopedia } from '../core/blitz/tankopedia.js';
|
import { tankopedia } from '../core/blitz/tankopedia';
|
||||||
import { tankAverages } from '../core/blitzstars/tankAverages.js';
|
import { tankAverages } from '../core/blitzstars/tankAverages';
|
||||||
import embedInfo from '../core/discord/embedInfo.js';
|
import embedInfo from '../core/discord/embedInfo';
|
||||||
import markdownTable from '../core/discord/markdownTable.js';
|
import markdownTable from '../core/discord/markdownTable';
|
||||||
import getClientId from '../core/node/getClientId.js';
|
import getClientId from '../core/node/getClientId';
|
||||||
import { CommandRegistry } from '../events/interactionCreate/index.js';
|
import { CommandRegistry } from '../events/interactionCreate';
|
||||||
|
|
||||||
const executionStart = new Date().getTime();
|
const executionStart = new Date().getTime();
|
||||||
|
|
||||||
export const debugCommand: CommandRegistry = {
|
export const debugCommand: CommandRegistry = {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: true,
|
inDevelopment: false,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command: new SlashCommandBuilder()
|
command: new SlashCommandBuilder()
|
||||||
|
|||||||
+40
-25
@@ -1,21 +1,21 @@
|
|||||||
import { SlashCommandBuilder } from 'discord.js';
|
import { SlashCommandBuilder } from 'discord.js';
|
||||||
import calculateWN8 from '../core/blitz/calculateWN8.js';
|
import calculateWN8 from '../core/blitz/calculateWN8';
|
||||||
import getWargamingResponse from '../core/blitz/getWargamingResponse.js';
|
import getWargamingResponse from '../core/blitz/getWargamingResponse';
|
||||||
import sumStats from '../core/blitz/sumStats.js';
|
import sumStats from '../core/blitz/sumStats';
|
||||||
import { tankopedia } from '../core/blitz/tankopedia.js';
|
import { tankopedia } from '../core/blitz/tankopedia';
|
||||||
import getPeriodNow from '../core/blitzstars/getPeriodNow.js';
|
import getDiffedTankStats from '../core/blitzstars/getDiffedTankStats';
|
||||||
import getPeriodStart from '../core/blitzstars/getPeriodStart.js';
|
import getPeriodNow from '../core/blitzstars/getPeriodNow';
|
||||||
import getTankStatsDiffed from '../core/blitzstars/getTankStatsDiffed.js';
|
import getTimeDaysAgo from '../core/blitzstars/getTimeDaysAgo';
|
||||||
import { tankAverages } from '../core/blitzstars/tankAverages.js';
|
import { tankAverages } from '../core/blitzstars/tankAverages';
|
||||||
import addUsernameChoices from '../core/discord/addUsernameChoices.js';
|
import addUsernameChoices from '../core/discord/addUsernameChoices';
|
||||||
import embedNegative from '../core/discord/embedNegative.js';
|
import embedNegative from '../core/discord/embedNegative';
|
||||||
import embedPositive from '../core/discord/embedPositive.js';
|
import embedPositive from '../core/discord/embedPositive';
|
||||||
import markdownTable, { TableInput } from '../core/discord/markdownTable.js';
|
import markdownTable, { TableInput } from '../core/discord/markdownTable';
|
||||||
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand.js';
|
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments.js';
|
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments';
|
||||||
import { CommandRegistry } from '../events/interactionCreate/index.js';
|
import { CommandRegistry } from '../events/interactionCreate';
|
||||||
import { AccountInfo } from '../types/accountInfo.js';
|
import { AccountInfo } from '../types/accountInfo';
|
||||||
import { PossiblyPromise } from '../types/possiblyPromise.js';
|
import { PossiblyPromise } from '../types/possiblyPromise';
|
||||||
|
|
||||||
export type SkilledClan = 'SKLLD' | 'SMRI';
|
export type SkilledClan = 'SKLLD' | 'SMRI';
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ export const SKILLED_CLANS: Record<SkilledClan, string> = {
|
|||||||
|
|
||||||
export const eligibleCommand: CommandRegistry = {
|
export const eligibleCommand: CommandRegistry = {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: true,
|
inDevelopment: false,
|
||||||
inPublic: false,
|
inPublic: false,
|
||||||
|
|
||||||
command: new SlashCommandBuilder()
|
command: new SlashCommandBuilder()
|
||||||
@@ -59,24 +59,39 @@ export const eligibleCommand: CommandRegistry = {
|
|||||||
let body = '';
|
let body = '';
|
||||||
|
|
||||||
if (clan === 'SKLLD') {
|
if (clan === 'SKLLD') {
|
||||||
const tankStatsOverTime = await getTankStatsDiffed(
|
const { diffed } = await getDiffedTankStats(
|
||||||
server,
|
server,
|
||||||
id,
|
id,
|
||||||
getPeriodStart('30'),
|
getTimeDaysAgo(server, 30),
|
||||||
getPeriodNow(),
|
getPeriodNow(),
|
||||||
);
|
);
|
||||||
const entries = Object.entries(tankStatsOverTime);
|
const entries = Object.entries(diffed);
|
||||||
const stats = sumStats(entries.map(([, stats]) => stats));
|
const stats = sumStats(entries.map(([, stats]) => stats));
|
||||||
const averageTier =
|
const averageTier =
|
||||||
(await entries.reduce<PossiblyPromise<number>>(
|
(await entries.reduce<PossiblyPromise<number>>(
|
||||||
async (accumulator, [tankIdString, stats]) => {
|
async (accumulator, [tankIdString, stats]) => {
|
||||||
const tankId = parseInt(tankIdString);
|
const tankId = parseInt(tankIdString);
|
||||||
const tankTier = (await tankopedia)[tankId].tier;
|
const tankopediaEntry = (await tankopedia)[tankId];
|
||||||
|
|
||||||
|
if (!tankopediaEntry) return accumulator;
|
||||||
|
|
||||||
|
const tankTier = tankopediaEntry.tier;
|
||||||
|
|
||||||
return (await accumulator) + tankTier * stats.battles;
|
return (await accumulator) + tankTier * stats.battles;
|
||||||
},
|
},
|
||||||
0,
|
0,
|
||||||
)) / stats.battles;
|
)) /
|
||||||
|
(await entries.reduce<PossiblyPromise<number>>(
|
||||||
|
async (accumulator, [tankIdString, stats]) => {
|
||||||
|
const tankId = parseInt(tankIdString);
|
||||||
|
const tankopediaEntry = (await tankopedia)[tankId];
|
||||||
|
|
||||||
|
if (!tankopediaEntry) return accumulator;
|
||||||
|
|
||||||
|
return stats.battles;
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
));
|
||||||
const WN8 =
|
const WN8 =
|
||||||
(await entries.reduce<PossiblyPromise<number>>(
|
(await entries.reduce<PossiblyPromise<number>>(
|
||||||
async (accumulator, [tankIdString, stats]) => {
|
async (accumulator, [tankIdString, stats]) => {
|
||||||
@@ -101,7 +116,7 @@ export const eligibleCommand: CommandRegistry = {
|
|||||||
const tankId = parseInt(tankIdString);
|
const tankId = parseInt(tankIdString);
|
||||||
|
|
||||||
if ((await tankopedia)[tankId]?.tier === 10) {
|
if ((await tankopedia)[tankId]?.tier === 10) {
|
||||||
const tankStats = tankStatsOverTime[tankId];
|
const tankStats = diffed[tankId];
|
||||||
return (await accumulator) + tankStats.damage_dealt;
|
return (await accumulator) + tankStats.damage_dealt;
|
||||||
} else return accumulator;
|
} else return accumulator;
|
||||||
},
|
},
|
||||||
@@ -112,7 +127,7 @@ export const eligibleCommand: CommandRegistry = {
|
|||||||
const tankId = parseInt(tankIdString);
|
const tankId = parseInt(tankIdString);
|
||||||
|
|
||||||
if ((await tankopedia)[tankId]?.tier === 10) {
|
if ((await tankopedia)[tankId]?.tier === 10) {
|
||||||
const tankStats = tankStatsOverTime[tankId];
|
const tankStats = diffed[tankId];
|
||||||
return (await accumulator) + tankStats.battles;
|
return (await accumulator) + tankStats.battles;
|
||||||
} else return accumulator;
|
} else return accumulator;
|
||||||
},
|
},
|
||||||
|
|||||||
+23
-22
@@ -1,26 +1,26 @@
|
|||||||
import { SlashCommandBuilder } from 'discord.js';
|
import { SlashCommandBuilder } from 'discord.js';
|
||||||
import { CYCLIC_API } from '../constants/cyclic.js';
|
import { CYCLIC_API } from '../constants/cyclic';
|
||||||
import getWargamingResponse from '../core/blitz/getWargamingResponse.js';
|
import getWargamingResponse from '../core/blitz/getWargamingResponse';
|
||||||
import resolveTankId from '../core/blitz/resolveTankId.js';
|
import resolveTankId from '../core/blitz/resolveTankId';
|
||||||
import addStatTypeSubCommandGroups from '../core/discord/addStatTypeSubCommandGroups.js';
|
import addStatTypeSubCommandGroups from '../core/discord/addStatTypeSubCommandGroups';
|
||||||
import autocompleteTanks from '../core/discord/autocompleteTanks.js';
|
import autocompleteTanks from '../core/discord/autocompleteTanks';
|
||||||
import autocompleteUsername from '../core/discord/autocompleteUsername.js';
|
import autocompleteUsername from '../core/discord/autocompleteUsername';
|
||||||
import interactionToURL from '../core/discord/interactionToURL.js';
|
import interactionToURL from '../core/discord/interactionToURL';
|
||||||
import linkButton from '../core/discord/linkButton.js';
|
import linkButton from '../core/discord/linkButton';
|
||||||
import primaryButton from '../core/discord/primaryButton.js';
|
import primaryButton from '../core/discord/primaryButton';
|
||||||
import resolvePeriodFromButton from '../core/discord/resolvePeriodFromButton.js';
|
import resolvePeriodFromButton from '../core/discord/resolvePeriodFromButton';
|
||||||
import resolvePeriodFromCommand from '../core/discord/resolvePeriodFromCommand.js';
|
import resolvePeriodFromCommand from '../core/discord/resolvePeriodFromCommand';
|
||||||
import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton.js';
|
import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton';
|
||||||
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand.js';
|
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments.js';
|
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments';
|
||||||
import { CommandRegistry } from '../events/interactionCreate/index.js';
|
import { CommandRegistry } from '../events/interactionCreate';
|
||||||
import evolution from '../renderers/evolution.js';
|
import evolution from '../renderers/evolution';
|
||||||
import { StatType } from '../renderers/statsfull.js';
|
import { StatType } from '../renderers/fullStats';
|
||||||
import { AccountInfo } from '../types/accountInfo.js';
|
import { AccountInfo } from '../types/accountInfo';
|
||||||
|
|
||||||
export const evolutionCommand: CommandRegistry = {
|
export const evolutionCommand: CommandRegistry = {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: true,
|
inDevelopment: false,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command: addStatTypeSubCommandGroups(
|
command: addStatTypeSubCommandGroups(
|
||||||
@@ -34,9 +34,10 @@ export const evolutionCommand: CommandRegistry = {
|
|||||||
true,
|
true,
|
||||||
) as StatType;
|
) as StatType;
|
||||||
const player = await resolvePlayerFromCommand(interaction);
|
const player = await resolvePlayerFromCommand(interaction);
|
||||||
const period = resolvePeriodFromCommand(interaction);
|
const period = resolvePeriodFromCommand(player.server, interaction);
|
||||||
const tankIdRaw = interaction.options.getString('tank')!;
|
const tankIdRaw = interaction.options.getString('tank')!;
|
||||||
const tankId = commandGroup === 'tank' ? await resolveTankId(tankIdRaw) : null;
|
const tankId =
|
||||||
|
commandGroup === 'tank' ? await resolveTankId(tankIdRaw) : null;
|
||||||
const start = interaction.options.getInteger('start');
|
const start = interaction.options.getInteger('start');
|
||||||
const end = interaction.options.getInteger('end');
|
const end = interaction.options.getInteger('end');
|
||||||
const { nickname } = (
|
const { nickname } = (
|
||||||
@@ -73,8 +74,8 @@ export const evolutionCommand: CommandRegistry = {
|
|||||||
const url = new URL(`${CYCLIC_API}/${interaction.customId}`);
|
const url = new URL(`${CYCLIC_API}/${interaction.customId}`);
|
||||||
const path = url.pathname.split('/').filter(Boolean);
|
const path = url.pathname.split('/').filter(Boolean);
|
||||||
const commandGroup = path[1] as StatType;
|
const commandGroup = path[1] as StatType;
|
||||||
const period = resolvePeriodFromButton(interaction);
|
|
||||||
const player = await resolvePlayerFromButton(interaction);
|
const player = await resolvePlayerFromButton(interaction);
|
||||||
|
const period = resolvePeriodFromButton(player.server, interaction);
|
||||||
|
|
||||||
return await evolution(
|
return await evolution(
|
||||||
commandGroup,
|
commandGroup,
|
||||||
|
|||||||
@@ -1,30 +1,30 @@
|
|||||||
import { SlashCommandBuilder } from 'discord.js';
|
import { SlashCommandBuilder } from 'discord.js';
|
||||||
import { CYCLIC_API } from '../constants/cyclic.js';
|
import { CYCLIC_API } from '../constants/cyclic';
|
||||||
import getWargamingResponse from '../core/blitz/getWargamingResponse.js';
|
import getWargamingResponse from '../core/blitz/getWargamingResponse';
|
||||||
import resolveTankId from '../core/blitz/resolveTankId.js';
|
import resolveTankId from '../core/blitz/resolveTankId';
|
||||||
import addStatTypeSubCommandGroups from '../core/discord/addStatTypeSubCommandGroups.js';
|
import addStatTypeSubCommandGroups from '../core/discord/addStatTypeSubCommandGroups';
|
||||||
import autocompleteTanks from '../core/discord/autocompleteTanks.js';
|
import autocompleteTanks from '../core/discord/autocompleteTanks';
|
||||||
import autocompleteUsername from '../core/discord/autocompleteUsername.js';
|
import autocompleteUsername from '../core/discord/autocompleteUsername';
|
||||||
import interactionToURL from '../core/discord/interactionToURL.js';
|
import interactionToURL from '../core/discord/interactionToURL';
|
||||||
import linkButton from '../core/discord/linkButton.js';
|
import linkButton from '../core/discord/linkButton';
|
||||||
import primaryButton from '../core/discord/primaryButton.js';
|
import primaryButton from '../core/discord/primaryButton';
|
||||||
import resolvePeriodFromButton from '../core/discord/resolvePeriodFromButton.js';
|
import resolvePeriodFromButton from '../core/discord/resolvePeriodFromButton';
|
||||||
import resolvePeriodFromCommand from '../core/discord/resolvePeriodFromCommand.js';
|
import resolvePeriodFromCommand from '../core/discord/resolvePeriodFromCommand';
|
||||||
import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton.js';
|
import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton';
|
||||||
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand.js';
|
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments.js';
|
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments';
|
||||||
import { CommandRegistry } from '../events/interactionCreate/index.js';
|
import { CommandRegistry } from '../events/interactionCreate';
|
||||||
import statsfull, { StatType } from '../renderers/statsfull.js';
|
import fullStats, { StatType } from '../renderers/fullStats';
|
||||||
import { AccountInfo } from '../types/accountInfo.js';
|
import { AccountInfo } from '../types/accountInfo';
|
||||||
|
|
||||||
export const statsFullCommand: CommandRegistry = {
|
export const fullStatsCommand: CommandRegistry = {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: true,
|
inDevelopment: false,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command: addStatTypeSubCommandGroups(
|
command: addStatTypeSubCommandGroups(
|
||||||
new SlashCommandBuilder()
|
new SlashCommandBuilder()
|
||||||
.setName('statsfull')
|
.setName('fullstats')
|
||||||
.setDescription('Full in-game statistics'),
|
.setDescription('Full in-game statistics'),
|
||||||
),
|
),
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ export const statsFullCommand: CommandRegistry = {
|
|||||||
true,
|
true,
|
||||||
) as StatType;
|
) as StatType;
|
||||||
const player = await resolvePlayerFromCommand(interaction);
|
const player = await resolvePlayerFromCommand(interaction);
|
||||||
const period = resolvePeriodFromCommand(interaction);
|
const period = resolvePeriodFromCommand(player.server, interaction);
|
||||||
const tankIdRaw = interaction.options.getString('tank')!;
|
const tankIdRaw = interaction.options.getString('tank')!;
|
||||||
const tankId =
|
const tankId =
|
||||||
commandGroup === 'tank' ? await resolveTankId(tankIdRaw) : null;
|
commandGroup === 'tank' ? await resolveTankId(tankIdRaw) : null;
|
||||||
@@ -52,7 +52,7 @@ export const statsFullCommand: CommandRegistry = {
|
|||||||
)[player.id];
|
)[player.id];
|
||||||
|
|
||||||
return [
|
return [
|
||||||
await statsfull(commandGroup, period, player, tankId),
|
await fullStats(commandGroup, period, player, tankId),
|
||||||
primaryButton(path, 'Refresh'),
|
primaryButton(path, 'Refresh'),
|
||||||
// linkButton(`${CYCLIC_API}/${path}`, 'Embed'),
|
// linkButton(`${CYCLIC_API}/${path}`, 'Embed'),
|
||||||
linkButton(
|
linkButton(
|
||||||
@@ -71,10 +71,10 @@ export const statsFullCommand: CommandRegistry = {
|
|||||||
const url = new URL(`${CYCLIC_API}/${interaction.customId}`);
|
const url = new URL(`${CYCLIC_API}/${interaction.customId}`);
|
||||||
const path = url.pathname.split('/').filter(Boolean);
|
const path = url.pathname.split('/').filter(Boolean);
|
||||||
const commandGroup = path[1] as StatType;
|
const commandGroup = path[1] as StatType;
|
||||||
const period = resolvePeriodFromButton(interaction);
|
|
||||||
const player = await resolvePlayerFromButton(interaction);
|
const player = await resolvePlayerFromButton(interaction);
|
||||||
|
const period = resolvePeriodFromButton(player.server, interaction);
|
||||||
|
|
||||||
return await statsfull(
|
return await fullStats(
|
||||||
commandGroup,
|
commandGroup,
|
||||||
period,
|
period,
|
||||||
player,
|
player,
|
||||||
+54
-24
@@ -1,36 +1,66 @@
|
|||||||
import { SlashCommandBuilder } from 'discord.js';
|
|
||||||
import discord from '../../discord.json' assert { type: 'json' };
|
|
||||||
import { client } from '../bot.js';
|
|
||||||
import embedInfo from '../core/discord/embedInfo.js';
|
|
||||||
import {
|
import {
|
||||||
CommandRegistry,
|
SlashCommandBuilder,
|
||||||
guildCommands,
|
SlashCommandSubcommandsOnlyBuilder,
|
||||||
publicCommands,
|
} from 'discord.js';
|
||||||
} from '../events/interactionCreate/index.js';
|
import { COMMANDS_RAW, CommandRegistry } from '../events/interactionCreate';
|
||||||
|
|
||||||
|
const RAW_PATH = `https://raw.githubusercontent.com/tresabhi/blitzkrieg/main/docs/`;
|
||||||
|
const DOCS = {
|
||||||
|
permissions: 'guide/permissions',
|
||||||
|
embeds: 'guide/embeds',
|
||||||
|
about: 'guide/about',
|
||||||
|
invite: 'guide/invite',
|
||||||
|
timezones: 'guide/timezones',
|
||||||
|
};
|
||||||
|
const DOC_DESCRIPTIONS: Record<keyof typeof DOCS, string> = {
|
||||||
|
permissions: 'The permissions needed for the bot to function',
|
||||||
|
embeds: 'How embeds work',
|
||||||
|
about: 'About the bot',
|
||||||
|
invite: 'How to invite the bot',
|
||||||
|
timezones: 'How Blitzkrieg infers and uses your timezone',
|
||||||
|
};
|
||||||
|
|
||||||
|
function addDocsSubcommands(option: SlashCommandSubcommandsOnlyBuilder) {
|
||||||
|
Object.entries(DOC_DESCRIPTIONS).forEach(([key, value]) => {
|
||||||
|
option.addSubcommand((sub) => sub.setName(key).setDescription(value));
|
||||||
|
});
|
||||||
|
|
||||||
|
return option;
|
||||||
|
}
|
||||||
|
|
||||||
export const helpCommand: CommandRegistry = {
|
export const helpCommand: CommandRegistry = {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: true,
|
inDevelopment: false,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command: new SlashCommandBuilder()
|
command: addDocsSubcommands(
|
||||||
|
new SlashCommandBuilder()
|
||||||
.setName('help')
|
.setName('help')
|
||||||
.setDescription('All the help you need about the bot'),
|
.setDescription('All the help you need about the bot')
|
||||||
|
.addSubcommand((option) =>
|
||||||
|
option
|
||||||
|
.setName('commands')
|
||||||
|
.setDescription('List of all commands and their descriptions'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
handler(interaction) {
|
handler(interaction) {
|
||||||
return embedInfo(
|
const subcommand = interaction.options.getSubcommand();
|
||||||
`${client.user?.username} help`,
|
|
||||||
`**About**\n${
|
if (subcommand === 'commands') {
|
||||||
client.user?.username
|
return `# Commands\n\nBlitzkrieg offers the following commands:\n\n${COMMANDS_RAW.filter(
|
||||||
} automates many mundane tasks and provide statistics in numerous flexible ways.\n\nMade by TresAbhi from the Skilled [SKLLD] clan.\nGitHub: https://github.com/tresabhi/blitzkrieg\nDiscord server: https://discord.gg/nDt7AjGJQH\nSkilled server: https://discord.gg/ZPvcEG7DS8\n\n**Commands**\n${(interaction.guildId ===
|
(registry) => registry.inPublic && registry.inProduction,
|
||||||
discord.sklld_guild_id
|
|
||||||
? [...guildCommands, ...publicCommands]
|
|
||||||
: publicCommands
|
|
||||||
)
|
)
|
||||||
.map((command) => `**/${command.name}**: ${command.description}`)
|
.map(
|
||||||
.join(
|
(registry) =>
|
||||||
'\n',
|
`- \`/${registry.command.name}\`: ${registry.command.description}`,
|
||||||
)}\n\n*I am a new bot; I may make mistakes and go offline unexpectedly from time to time.*`,
|
)
|
||||||
);
|
.join('\n')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = `${RAW_PATH}${DOCS[subcommand as keyof typeof DOCS]}.md`;
|
||||||
|
const content = fetch(url).then((response) => response.text());
|
||||||
|
|
||||||
|
return content;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
+16
-16
@@ -1,24 +1,24 @@
|
|||||||
import { SlashCommandBuilder } from 'discord.js';
|
import { SlashCommandBuilder } from 'discord.js';
|
||||||
import GenericStats from '../components/GenericStats/index.js';
|
import GenericStats from '../components/GenericStats';
|
||||||
import NoData, { NoDataType } from '../components/NoData.js';
|
import NoData, { NoDataType } from '../components/NoData';
|
||||||
import PoweredBy, { PoweredByType } from '../components/PoweredBy.js';
|
import PoweredBy, { PoweredByType } from '../components/PoweredBy';
|
||||||
import TitleBar from '../components/TitleBar.js';
|
import TitleBar from '../components/TitleBar';
|
||||||
import Wrapper from '../components/Wrapper.js';
|
import Wrapper from '../components/Wrapper';
|
||||||
import { BLITZ_SERVERS } from '../constants/servers.js';
|
import { REGION_DOMAIN_NAMES } from '../constants/regions';
|
||||||
import getBlitzClan from '../core/blitz/getBlitzClan.js';
|
import getBlitzClan from '../core/blitz/getBlitzClan';
|
||||||
import getWargamingResponse from '../core/blitz/getWargamingResponse.js';
|
import getWargamingResponse from '../core/blitz/getWargamingResponse';
|
||||||
import addClanChoices from '../core/discord/addClanChoices.js';
|
import addClanChoices from '../core/discord/addClanChoices';
|
||||||
import autocompleteClan from '../core/discord/autocompleteClan.js';
|
import autocompleteClan from '../core/discord/autocompleteClan';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments.js';
|
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments';
|
||||||
import { CommandRegistry } from '../events/interactionCreate/index.js';
|
import { CommandRegistry } from '../events/interactionCreate';
|
||||||
import { AccountInfo } from '../types/accountInfo.js';
|
import { AccountInfo } from '../types/accountInfo';
|
||||||
import { ClanInfo } from '../types/clanInfo.js';
|
import { ClanInfo } from '../types/clanInfo';
|
||||||
|
|
||||||
const DEFAULT_THRESHOLD = 7;
|
const DEFAULT_THRESHOLD = 7;
|
||||||
|
|
||||||
export const inactiveCommand: CommandRegistry = {
|
export const inactiveCommand: CommandRegistry = {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: true,
|
inDevelopment: false,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command: new SlashCommandBuilder()
|
command: new SlashCommandBuilder()
|
||||||
@@ -72,7 +72,7 @@ export const inactiveCommand: CommandRegistry = {
|
|||||||
nameDiscriminator="(Inactivity)"
|
nameDiscriminator="(Inactivity)"
|
||||||
image={`https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clanData.emblem_set_id}.png`}
|
image={`https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clanData.emblem_set_id}.png`}
|
||||||
description={`${threshold}+ Days • ${new Date().toDateString()} • ${
|
description={`${threshold}+ Days • ${new Date().toDateString()} • ${
|
||||||
BLITZ_SERVERS[server]
|
REGION_DOMAIN_NAMES[server]
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
+50
-25
@@ -1,24 +1,26 @@
|
|||||||
import { SlashCommandBuilder } from 'discord.js';
|
import { SlashCommandBuilder } from 'discord.js';
|
||||||
import NoData, { NoDataType } from '../components/NoData.js';
|
import NoData, { NoDataType } from '../components/NoData';
|
||||||
import PoweredBy, { PoweredByType } from '../components/PoweredBy.js';
|
import PoweredBy, { PoweredByType } from '../components/PoweredBy';
|
||||||
import * as Tanks from '../components/Tanks/index.js';
|
import * as Tanks from '../components/Tanks';
|
||||||
import TitleBar from '../components/TitleBar.js';
|
import TitleBar from '../components/TitleBar';
|
||||||
import Wrapper from '../components/Wrapper.js';
|
import Wrapper from '../components/Wrapper';
|
||||||
import { BLITZ_SERVERS } from '../constants/servers.js';
|
import { REGION_DOMAIN_NAMES } from '../constants/regions';
|
||||||
import getTankStats from '../core/blitz/getTankStats.js';
|
import getTankStats from '../core/blitz/getTankStats';
|
||||||
import getWargamingResponse from '../core/blitz/getWargamingResponse.js';
|
import getTreeType from '../core/blitz/getTreeType';
|
||||||
|
import getWargamingResponse from '../core/blitz/getWargamingResponse';
|
||||||
|
import resolveTankName from '../core/blitz/resolveTankName';
|
||||||
import {
|
import {
|
||||||
TIER_ROMAN_NUMERALS,
|
TIER_ROMAN_NUMERALS,
|
||||||
Tier,
|
Tier,
|
||||||
tankopedia,
|
tankopedia,
|
||||||
} from '../core/blitz/tankopedia.js';
|
} from '../core/blitz/tankopedia';
|
||||||
import addUsernameChoices from '../core/discord/addUsernameChoices.js';
|
import addUsernameChoices from '../core/discord/addUsernameChoices';
|
||||||
import autocompleteUsername from '../core/discord/autocompleteUsername.js';
|
import autocompleteUsername from '../core/discord/autocompleteUsername';
|
||||||
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand.js';
|
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments.js';
|
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments';
|
||||||
import { CommandRegistry } from '../events/interactionCreate/index.js';
|
import { CommandRegistry } from '../events/interactionCreate';
|
||||||
import { AccountInfo } from '../types/accountInfo.js';
|
import { AccountInfo } from '../types/accountInfo';
|
||||||
import { PlayerClanData } from '../types/playerClanData.js';
|
import { PlayerClanData } from '../types/playerClanData';
|
||||||
|
|
||||||
const COMP_TANKS = [
|
const COMP_TANKS = [
|
||||||
// light tanks
|
// light tanks
|
||||||
@@ -43,7 +45,7 @@ const COMP_TANKS = [
|
|||||||
|
|
||||||
export const ownedTanksCommand: CommandRegistry = {
|
export const ownedTanksCommand: CommandRegistry = {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: true,
|
inDevelopment: false,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command: new SlashCommandBuilder()
|
command: new SlashCommandBuilder()
|
||||||
@@ -79,16 +81,21 @@ export const ownedTanksCommand: CommandRegistry = {
|
|||||||
const tankStats = await getTankStats(server, id);
|
const tankStats = await getTankStats(server, id);
|
||||||
const tanks = (
|
const tanks = (
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
tankStats.map(async (tankData) => (await tankopedia)[tankData.tank_id]),
|
tankStats.map(async (tankData) => ({
|
||||||
|
...(await tankopedia)[tankData.tank_id],
|
||||||
|
tank_id: tankData.tank_id,
|
||||||
|
})),
|
||||||
)
|
)
|
||||||
).filter((tank) => tank?.tier === tier);
|
).filter((tank) => tank?.tier === tier);
|
||||||
const clanData = await getWargamingResponse<PlayerClanData>(
|
const clanData = await getWargamingResponse<PlayerClanData>(
|
||||||
`https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`,
|
`https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`,
|
||||||
);
|
);
|
||||||
|
const leftColumnSize = Math.ceil(tanks.length / 2);
|
||||||
|
const leftColumn = tanks.slice(0, leftColumnSize);
|
||||||
|
const rightColumn = tanks.slice(leftColumnSize);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
{/* TODO: integrate some of these into title bar */}
|
|
||||||
<TitleBar
|
<TitleBar
|
||||||
name={accountInfo[id].nickname}
|
name={accountInfo[id].nickname}
|
||||||
nameDiscriminator={`(Tier ${TIER_ROMAN_NUMERALS[tier as Tier]})`}
|
nameDiscriminator={`(Tier ${TIER_ROMAN_NUMERALS[tier as Tier]})`}
|
||||||
@@ -98,7 +105,7 @@ export const ownedTanksCommand: CommandRegistry = {
|
|||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
description={`Owned tanks • ${new Date().toDateString()} • ${
|
description={`Owned tanks • ${new Date().toDateString()} • ${
|
||||||
BLITZ_SERVERS[server]
|
REGION_DOMAIN_NAMES[server]
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -106,14 +113,32 @@ export const ownedTanksCommand: CommandRegistry = {
|
|||||||
|
|
||||||
{tanks.length > 0 && (
|
{tanks.length > 0 && (
|
||||||
<Tanks.Root>
|
<Tanks.Root>
|
||||||
{tanks.map((tank) => (
|
<Tanks.Column>
|
||||||
|
{await Promise.all(
|
||||||
|
leftColumn.map(async (tank) => (
|
||||||
<Tanks.Item
|
<Tanks.Item
|
||||||
key={tank.tank_id}
|
key={tank.tank_id}
|
||||||
name={tank.name}
|
name={await resolveTankName(tank.tank_id)}
|
||||||
type={tank.type}
|
tankType={tank.type}
|
||||||
icon={tank.images.normal}
|
image={tank.images?.normal}
|
||||||
|
treeType={await getTreeType(tank.tank_id)}
|
||||||
/>
|
/>
|
||||||
))}
|
)),
|
||||||
|
)}
|
||||||
|
</Tanks.Column>
|
||||||
|
<Tanks.Column>
|
||||||
|
{await Promise.all(
|
||||||
|
rightColumn.map(async (tank) => (
|
||||||
|
<Tanks.Item
|
||||||
|
key={tank.tank_id}
|
||||||
|
name={await resolveTankName(tank.tank_id)}
|
||||||
|
tankType={tank.type}
|
||||||
|
image={tank.images?.normal}
|
||||||
|
treeType={await getTreeType(tank.tank_id)}
|
||||||
|
/>
|
||||||
|
)),
|
||||||
|
)}
|
||||||
|
</Tanks.Column>
|
||||||
</Tanks.Root>
|
</Tanks.Root>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { SlashCommandBuilder } from 'discord.js';
|
import { SlashCommandBuilder } from 'discord.js';
|
||||||
import { CommandRegistry } from '../events/interactionCreate/index.js';
|
import { CommandRegistry } from '../events/interactionCreate';
|
||||||
|
|
||||||
export const pingCommand: CommandRegistry<true> = {
|
export const pingCommand: CommandRegistry<true> = {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: true,
|
inDevelopment: false,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
handlesInteraction: true,
|
handlesInteraction: true,
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,22 @@
|
|||||||
import { SlashCommandBuilder } from 'discord.js';
|
import { SlashCommandBuilder } from 'discord.js';
|
||||||
import { startCase } from 'lodash';
|
import { startCase } from 'lodash';
|
||||||
import markdownEscape from 'markdown-escape';
|
import markdownEscape from 'markdown-escape';
|
||||||
import getWargamingResponse from '../core/blitz/getWargamingResponse.js';
|
import getWargamingResponse from '../core/blitz/getWargamingResponse';
|
||||||
import addUsernameChoices from '../core/discord/addUsernameChoices.js';
|
import addUsernameChoices from '../core/discord/addUsernameChoices';
|
||||||
import autocompleteUsername from '../core/discord/autocompleteUsername.js';
|
import autocompleteUsername from '../core/discord/autocompleteUsername';
|
||||||
import embedInfo from '../core/discord/embedInfo.js';
|
import embedInfo from '../core/discord/embedInfo';
|
||||||
import markdownTable, {
|
import markdownTable, { TableInputEntry } from '../core/discord/markdownTable';
|
||||||
TableInputEntry,
|
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
|
||||||
} from '../core/discord/markdownTable.js';
|
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments';
|
||||||
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand.js';
|
import { CommandRegistry } from '../events/interactionCreate';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments.js';
|
import { AccountAchievements } from '../types/accountAchievements';
|
||||||
import { CommandRegistry } from '../events/interactionCreate/index.js';
|
import { AccountInfo } from '../types/accountInfo';
|
||||||
import { AccountAchievements } from '../types/accountAchievements.js';
|
|
||||||
import { AccountInfo } from '../types/accountInfo.js';
|
|
||||||
|
|
||||||
type SortBy = 'name' | 'count';
|
type SortBy = 'name' | 'count';
|
||||||
|
|
||||||
export const playerAchievementsCommand: CommandRegistry = {
|
export const playerAchievementsCommand: CommandRegistry = {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: true,
|
inDevelopment: false,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command: new SlashCommandBuilder()
|
command: new SlashCommandBuilder()
|
||||||
|
|||||||
+10
-10
@@ -1,18 +1,18 @@
|
|||||||
import { SlashCommandBuilder } from 'discord.js';
|
import { SlashCommandBuilder } from 'discord.js';
|
||||||
import markdownEscape from 'markdown-escape';
|
import markdownEscape from 'markdown-escape';
|
||||||
import getWargamingResponse from '../core/blitz/getWargamingResponse.js';
|
import getWargamingResponse from '../core/blitz/getWargamingResponse';
|
||||||
import addUsernameChoices from '../core/discord/addUsernameChoices.js';
|
import addUsernameChoices from '../core/discord/addUsernameChoices';
|
||||||
import autocompleteUsername from '../core/discord/autocompleteUsername.js';
|
import autocompleteUsername from '../core/discord/autocompleteUsername';
|
||||||
import embedInfo from '../core/discord/embedInfo.js';
|
import embedInfo from '../core/discord/embedInfo';
|
||||||
import markdownTable from '../core/discord/markdownTable.js';
|
import markdownTable from '../core/discord/markdownTable';
|
||||||
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand.js';
|
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments.js';
|
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments';
|
||||||
import { CommandRegistry } from '../events/interactionCreate/index.js';
|
import { CommandRegistry } from '../events/interactionCreate';
|
||||||
import { AccountInfo } from '../types/accountInfo.js';
|
import { AccountInfo } from '../types/accountInfo';
|
||||||
|
|
||||||
export const playerInfoCommand: CommandRegistry = {
|
export const playerInfoCommand: CommandRegistry = {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: true,
|
inDevelopment: false,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command: new SlashCommandBuilder()
|
command: new SlashCommandBuilder()
|
||||||
|
|||||||
+13
-11
@@ -1,17 +1,17 @@
|
|||||||
import { SlashCommandBuilder } from 'discord.js';
|
import { SlashCommandBuilder } from 'discord.js';
|
||||||
import markdownEscape from 'markdown-escape';
|
import markdownEscape from 'markdown-escape';
|
||||||
import { BLITZ_SERVERS, BlitzServer } from '../constants/servers.js';
|
import { REGION_DOMAIN_NAMES, RegionDomain } from '../constants/regions';
|
||||||
import getWargamingResponse from '../core/blitz/getWargamingResponse.js';
|
import getWargamingResponse from '../core/blitz/getWargamingResponse';
|
||||||
import addClanChoices from '../core/discord/addClanChoices.js';
|
import addClanChoices from '../core/discord/addClanChoices';
|
||||||
import addServerChoices from '../core/discord/addServerChoices.js';
|
import addServerChoices from '../core/discord/addServerChoices';
|
||||||
import embedInfo from '../core/discord/embedInfo.js';
|
import embedInfo from '../core/discord/embedInfo';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments.js';
|
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments';
|
||||||
import { CommandRegistry } from '../events/interactionCreate/index.js';
|
import { CommandRegistry } from '../events/interactionCreate';
|
||||||
import { ClanList } from '../types/clanList.js';
|
import { ClanList } from '../types/clanList';
|
||||||
|
|
||||||
export const searchClansCommand: CommandRegistry = {
|
export const searchClansCommand: CommandRegistry = {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: true,
|
inDevelopment: false,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command: new SlashCommandBuilder()
|
command: new SlashCommandBuilder()
|
||||||
@@ -28,7 +28,7 @@ export const searchClansCommand: CommandRegistry = {
|
|||||||
),
|
),
|
||||||
|
|
||||||
async handler(interaction) {
|
async handler(interaction) {
|
||||||
const server = interaction.options.getString('server') as BlitzServer;
|
const server = interaction.options.getString('server') as RegionDomain;
|
||||||
const clan = interaction.options.getString('clan')!;
|
const clan = interaction.options.getString('clan')!;
|
||||||
const limit = interaction.options.getInteger('limit') ?? 25;
|
const limit = interaction.options.getInteger('limit') ?? 25;
|
||||||
const clanList = await getWargamingResponse<ClanList>(
|
const clanList = await getWargamingResponse<ClanList>(
|
||||||
@@ -36,7 +36,9 @@ export const searchClansCommand: CommandRegistry = {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return embedInfo(
|
return embedInfo(
|
||||||
`Clan search for "${markdownEscape(clan)}" in ${BLITZ_SERVERS[server]}`,
|
`Clan search for "${markdownEscape(clan)}" in ${
|
||||||
|
REGION_DOMAIN_NAMES[server]
|
||||||
|
}`,
|
||||||
clanList.length === 0
|
clanList.length === 0
|
||||||
? 'No clans found.'
|
? 'No clans found.'
|
||||||
: `\`\`\`\n${clanList
|
: `\`\`\`\n${clanList
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import { SlashCommandBuilder } from 'discord.js';
|
import { SlashCommandBuilder } from 'discord.js';
|
||||||
import markdownEscape from 'markdown-escape';
|
import markdownEscape from 'markdown-escape';
|
||||||
import { BLITZ_SERVERS, BlitzServer } from '../constants/servers.js';
|
import { REGION_DOMAIN_NAMES, RegionDomain } from '../constants/regions';
|
||||||
import getWargamingResponse from '../core/blitz/getWargamingResponse.js';
|
import getWargamingResponse from '../core/blitz/getWargamingResponse';
|
||||||
import { usernamePattern } from '../core/blitz/listPlayers.js';
|
import { usernamePattern } from '../core/blitz/listPlayers';
|
||||||
import addServerChoices from '../core/discord/addServerChoices.js';
|
import addServerChoices from '../core/discord/addServerChoices';
|
||||||
import addUsernameChoices from '../core/discord/addUsernameChoices.js';
|
import addUsernameChoices from '../core/discord/addUsernameChoices';
|
||||||
import embedInfo from '../core/discord/embedInfo.js';
|
import embedInfo from '../core/discord/embedInfo';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments.js';
|
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments';
|
||||||
import { CommandRegistry } from '../events/interactionCreate/index.js';
|
import { CommandRegistry } from '../events/interactionCreate';
|
||||||
import { AccountList } from '../types/accountList.js';
|
import { AccountList } from '../types/accountList';
|
||||||
|
|
||||||
export const searchPlayersCommand: CommandRegistry = {
|
export const searchPlayersCommand: CommandRegistry = {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: true,
|
inDevelopment: false,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command: new SlashCommandBuilder()
|
command: new SlashCommandBuilder()
|
||||||
@@ -31,7 +31,7 @@ export const searchPlayersCommand: CommandRegistry = {
|
|||||||
),
|
),
|
||||||
|
|
||||||
async handler(interaction) {
|
async handler(interaction) {
|
||||||
const server = interaction.options.getString('server') as BlitzServer;
|
const server = interaction.options.getString('server') as RegionDomain;
|
||||||
const name = interaction.options.getString('username')!;
|
const name = interaction.options.getString('username')!;
|
||||||
const limit = interaction.options.getInteger('limit') ?? 25;
|
const limit = interaction.options.getInteger('limit') ?? 25;
|
||||||
const trimmedSearch = name.trim();
|
const trimmedSearch = name.trim();
|
||||||
@@ -44,7 +44,9 @@ export const searchPlayersCommand: CommandRegistry = {
|
|||||||
: [];
|
: [];
|
||||||
|
|
||||||
return embedInfo(
|
return embedInfo(
|
||||||
`Player search for "${markdownEscape(name)}" in ${BLITZ_SERVERS[server]}`,
|
`Player search for "${markdownEscape(name)}" in ${
|
||||||
|
REGION_DOMAIN_NAMES[server]
|
||||||
|
}`,
|
||||||
`\`\`\`${
|
`\`\`\`${
|
||||||
players.length === 0
|
players.length === 0
|
||||||
? 'No players found.'
|
? 'No players found.'
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { SlashCommandBuilder } from 'discord.js';
|
import { SlashCommandBuilder } from 'discord.js';
|
||||||
import { go } from 'fuzzysort';
|
import { go } from 'fuzzysort';
|
||||||
import markdownEscape from 'markdown-escape';
|
import markdownEscape from 'markdown-escape';
|
||||||
import { TANK_NAMES } from '../core/blitz/tankopedia.js';
|
import { TANK_NAMES } from '../core/blitz/tankopedia';
|
||||||
import addTankChoices from '../core/discord/addTankChoices.js';
|
import addTankChoices from '../core/discord/addTankChoices';
|
||||||
import embedInfo from '../core/discord/embedInfo.js';
|
import embedInfo from '../core/discord/embedInfo';
|
||||||
import { CommandRegistry } from '../events/interactionCreate/index.js';
|
import { CommandRegistry } from '../events/interactionCreate';
|
||||||
|
|
||||||
export const searchTanksCommand: CommandRegistry = {
|
export const searchTanksCommand: CommandRegistry = {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: true,
|
inDevelopment: false,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command: new SlashCommandBuilder()
|
command: new SlashCommandBuilder()
|
||||||
@@ -26,7 +26,7 @@ export const searchTanksCommand: CommandRegistry = {
|
|||||||
async handler(interaction) {
|
async handler(interaction) {
|
||||||
const tank = interaction.options.getString('tank')!;
|
const tank = interaction.options.getString('tank')!;
|
||||||
const limit = interaction.options.getInteger('limit') ?? 25;
|
const limit = interaction.options.getInteger('limit') ?? 25;
|
||||||
const results = go(tank, TANK_NAMES, { limit }).map(
|
const results = go(tank, await TANK_NAMES, { limit }).map(
|
||||||
(result) => result.target,
|
(result) => result.target,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
+21
-21
@@ -1,26 +1,26 @@
|
|||||||
import { SlashCommandBuilder } from 'discord.js';
|
import { SlashCommandBuilder } from 'discord.js';
|
||||||
import { CYCLIC_API } from '../constants/cyclic.js';
|
import { CYCLIC_API } from '../constants/cyclic';
|
||||||
import getWargamingResponse from '../core/blitz/getWargamingResponse.js';
|
import getWargamingResponse from '../core/blitz/getWargamingResponse';
|
||||||
import resolveTankId from '../core/blitz/resolveTankId.js';
|
import resolveTankId from '../core/blitz/resolveTankId';
|
||||||
import addStatTypeSubCommandGroups from '../core/discord/addStatTypeSubCommandGroups.js';
|
import addStatTypeSubCommandGroups from '../core/discord/addStatTypeSubCommandGroups';
|
||||||
import autocompleteTanks from '../core/discord/autocompleteTanks.js';
|
import autocompleteTanks from '../core/discord/autocompleteTanks';
|
||||||
import autocompleteUsername from '../core/discord/autocompleteUsername.js';
|
import autocompleteUsername from '../core/discord/autocompleteUsername';
|
||||||
import interactionToURL from '../core/discord/interactionToURL.js';
|
import interactionToURL from '../core/discord/interactionToURL';
|
||||||
import linkButton from '../core/discord/linkButton.js';
|
import linkButton from '../core/discord/linkButton';
|
||||||
import primaryButton from '../core/discord/primaryButton.js';
|
import primaryButton from '../core/discord/primaryButton';
|
||||||
import resolvePeriodFromButton from '../core/discord/resolvePeriodFromButton.js';
|
import resolvePeriodFromButton from '../core/discord/resolvePeriodFromButton';
|
||||||
import resolvePeriodFromCommand from '../core/discord/resolvePeriodFromCommand.js';
|
import resolvePeriodFromCommand from '../core/discord/resolvePeriodFromCommand';
|
||||||
import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton.js';
|
import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton';
|
||||||
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand.js';
|
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments.js';
|
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments';
|
||||||
import { CommandRegistry } from '../events/interactionCreate/index.js';
|
import { CommandRegistry } from '../events/interactionCreate';
|
||||||
import stats from '../renderers/stats.js';
|
import { StatType } from '../renderers/fullStats';
|
||||||
import { StatType } from '../renderers/statsfull.js';
|
import stats from '../renderers/stats';
|
||||||
import { AccountInfo } from '../types/accountInfo.js';
|
import { AccountInfo } from '../types/accountInfo';
|
||||||
|
|
||||||
export const statsCommand: CommandRegistry = {
|
export const statsCommand: CommandRegistry = {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: true,
|
inDevelopment: false,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command: addStatTypeSubCommandGroups(
|
command: addStatTypeSubCommandGroups(
|
||||||
@@ -34,7 +34,7 @@ export const statsCommand: CommandRegistry = {
|
|||||||
true,
|
true,
|
||||||
) as StatType;
|
) as StatType;
|
||||||
const player = await resolvePlayerFromCommand(interaction);
|
const player = await resolvePlayerFromCommand(interaction);
|
||||||
const period = resolvePeriodFromCommand(interaction);
|
const period = resolvePeriodFromCommand(player.server, interaction);
|
||||||
const tankIdRaw = interaction.options.getString('tank')!;
|
const tankIdRaw = interaction.options.getString('tank')!;
|
||||||
const tankId =
|
const tankId =
|
||||||
commandGroup === 'tank' ? await resolveTankId(tankIdRaw) : null;
|
commandGroup === 'tank' ? await resolveTankId(tankIdRaw) : null;
|
||||||
@@ -72,8 +72,8 @@ export const statsCommand: CommandRegistry = {
|
|||||||
const url = new URL(`${CYCLIC_API}/${interaction.customId}`);
|
const url = new URL(`${CYCLIC_API}/${interaction.customId}`);
|
||||||
const path = url.pathname.split('/').filter(Boolean);
|
const path = url.pathname.split('/').filter(Boolean);
|
||||||
const commandGroup = path[1] as StatType;
|
const commandGroup = path[1] as StatType;
|
||||||
const period = resolvePeriodFromButton(interaction);
|
|
||||||
const player = await resolvePlayerFromButton(interaction);
|
const player = await resolvePlayerFromButton(interaction);
|
||||||
|
const period = resolvePeriodFromButton(player.server, interaction);
|
||||||
|
|
||||||
return await stats(
|
return await stats(
|
||||||
commandGroup,
|
commandGroup,
|
||||||
|
|||||||
+27
-16
@@ -1,29 +1,40 @@
|
|||||||
import { SlashCommandBuilder } from 'discord.js';
|
import { SlashCommandBuilder } from 'discord.js';
|
||||||
import getWargamingResponse from '../core/blitz/getWargamingResponse.js';
|
import { CYCLIC_API } from '../constants/cyclic';
|
||||||
import addUsernameChoices from '../core/discord/addUsernameChoices.js';
|
import getWargamingResponse from '../core/blitz/getWargamingResponse';
|
||||||
import autocompleteUsername from '../core/discord/autocompleteUsername.js';
|
import addUsernameChoices from '../core/discord/addUsernameChoices';
|
||||||
import interactionToURL from '../core/discord/interactionToURL.js';
|
import autocompleteUsername from '../core/discord/autocompleteUsername';
|
||||||
import linkButton from '../core/discord/linkButton.js';
|
import interactionToURL from '../core/discord/interactionToURL';
|
||||||
import primaryButton from '../core/discord/primaryButton.js';
|
import linkButton from '../core/discord/linkButton';
|
||||||
import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton.js';
|
import primaryButton from '../core/discord/primaryButton';
|
||||||
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand.js';
|
import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments.js';
|
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
|
||||||
import { CommandRegistry } from '../events/interactionCreate/index.js';
|
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments';
|
||||||
import today from '../renderers/today.js';
|
import { CommandRegistry } from '../events/interactionCreate';
|
||||||
import { AccountInfo } from '../types/accountInfo.js';
|
import today from '../renderers/today';
|
||||||
|
import { AccountInfo } from '../types/accountInfo';
|
||||||
|
|
||||||
export const todayCommand: CommandRegistry = {
|
export const todayCommand: CommandRegistry = {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: true,
|
inDevelopment: false,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command: new SlashCommandBuilder()
|
command: new SlashCommandBuilder()
|
||||||
.setName('today')
|
.setName('today')
|
||||||
.setDescription('A general daily breakdown of your performance')
|
.setDescription('A general daily breakdown of your performance')
|
||||||
.addStringOption(addUsernameChoices),
|
.addStringOption(addUsernameChoices)
|
||||||
|
.addIntegerOption((option) =>
|
||||||
|
option
|
||||||
|
.setName('limit')
|
||||||
|
.setDescription(
|
||||||
|
'The maximum number of tanks to display (default: Infinity)',
|
||||||
|
)
|
||||||
|
.setRequired(false)
|
||||||
|
.setMinValue(1),
|
||||||
|
),
|
||||||
|
|
||||||
async handler(interaction) {
|
async handler(interaction) {
|
||||||
const player = await resolvePlayerFromCommand(interaction);
|
const player = await resolvePlayerFromCommand(interaction);
|
||||||
|
const limit = interaction.options.getInteger('limit') ?? Infinity;
|
||||||
const { nickname } = (
|
const { nickname } = (
|
||||||
await getWargamingResponse<AccountInfo>(
|
await getWargamingResponse<AccountInfo>(
|
||||||
`https://api.wotblitz.${player.server}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${player.id}`,
|
`https://api.wotblitz.${player.server}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${player.id}`,
|
||||||
@@ -32,9 +43,9 @@ export const todayCommand: CommandRegistry = {
|
|||||||
const path = interactionToURL(interaction, player);
|
const path = interactionToURL(interaction, player);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
await today(player),
|
await today(player, limit, false),
|
||||||
primaryButton(path, 'Refresh'),
|
primaryButton(path, 'Refresh'),
|
||||||
// linkButton(`${CYCLIC_API}/${path}`, 'Embed'),
|
linkButton(`${CYCLIC_API}/${path}`, 'Embed'),
|
||||||
linkButton(
|
linkButton(
|
||||||
`https://www.blitzstars.com/player/${player.server}/${nickname}`,
|
`https://www.blitzstars.com/player/${player.server}/${nickname}`,
|
||||||
'BlitzStars',
|
'BlitzStars',
|
||||||
|
|||||||
+11
-11
@@ -1,20 +1,20 @@
|
|||||||
import { SlashCommandBuilder } from 'discord.js';
|
import { SlashCommandBuilder } from 'discord.js';
|
||||||
import markdownEscape from 'markdown-escape';
|
import markdownEscape from 'markdown-escape';
|
||||||
import discord from '../../discord.json' assert { type: 'json' };
|
import discord from '../../discord.json' assert { type: 'json' };
|
||||||
import getWargamingResponse from '../core/blitz/getWargamingResponse.js';
|
import getWargamingResponse from '../core/blitz/getWargamingResponse';
|
||||||
import addUsernameChoices from '../core/discord/addUsernameChoices.js';
|
import addUsernameChoices from '../core/discord/addUsernameChoices';
|
||||||
import autocompleteUsername from '../core/discord/autocompleteUsername.js';
|
import autocompleteUsername from '../core/discord/autocompleteUsername';
|
||||||
import embedNegative from '../core/discord/embedNegative.js';
|
import embedNegative from '../core/discord/embedNegative';
|
||||||
import embedPositive from '../core/discord/embedPositive.js';
|
import embedPositive from '../core/discord/embedPositive';
|
||||||
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand.js';
|
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments.js';
|
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments';
|
||||||
import { CommandRegistry } from '../events/interactionCreate/index.js';
|
import { CommandRegistry } from '../events/interactionCreate';
|
||||||
import { AccountInfo } from '../types/accountInfo.js';
|
import { AccountInfo } from '../types/accountInfo';
|
||||||
import { PlayerClanData } from '../types/playerClanData.js';
|
import { PlayerClanData } from '../types/playerClanData';
|
||||||
|
|
||||||
export const verifyCommand: CommandRegistry = {
|
export const verifyCommand: CommandRegistry = {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: true,
|
inDevelopment: false,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command: new SlashCommandBuilder()
|
command: new SlashCommandBuilder()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Percentile } from '../../../../../constants/percentiles.js';
|
import { Percentile } from '../../../../../constants/percentiles';
|
||||||
import hslToHex from '../../../../../core/node/hslToHex.js';
|
import hslToHex from '../../../../../core/node/hslToHex';
|
||||||
import { PERCENTILE_COLORS } from '../../../../PercentileIndicator.js';
|
import { PERCENTILE_COLORS } from '../../../../PercentileIndicator';
|
||||||
|
|
||||||
export enum GlowSide {
|
export enum GlowSide {
|
||||||
Top,
|
Top,
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Percentile } from '../../../../constants/percentiles.js';
|
import { Percentile } from '../../../../constants/percentiles';
|
||||||
import getWN8Percentile from '../../../../core/blitz/getWN8Percentile.js';
|
import getWN8Percentile from '../../../../core/blitz/getWN8Percentile';
|
||||||
import { theme } from '../../../../stitches.config.js';
|
import { theme } from '../../../../stitches.config';
|
||||||
import { PERCENTILE_COLORS } from '../../../PercentileIndicator.js';
|
import { PERCENTILE_COLORS } from '../../../PercentileIndicator';
|
||||||
import { Glow, GlowSide } from './components/Glow.js';
|
import { Glow, GlowSide } from './components/Glow';
|
||||||
|
|
||||||
export interface WN8DisplayProps {
|
export interface WN8DisplayProps {
|
||||||
WN8?: number;
|
WN8?: number;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { theme } from '../../../stitches.config.js';
|
import { theme } from '../../../stitches.config';
|
||||||
|
|
||||||
export enum WN8SurroundingStatAlign {
|
export enum WN8SurroundingStatAlign {
|
||||||
Left,
|
Left,
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { AllStats, SupplementaryStats } from '../../types/accountInfo.js';
|
import { AllStats, SupplementaryStats } from '../../types/accountInfo';
|
||||||
import { WN8Display } from './components/WN8Display/index.js';
|
import { WN8Display } from './components/WN8Display';
|
||||||
import {
|
import {
|
||||||
WN8SurroundingStat,
|
WN8SurroundingStat,
|
||||||
WN8SurroundingStatAlign,
|
WN8SurroundingStatAlign,
|
||||||
} from './components/WN8SurroundingStat.js';
|
} from './components/WN8SurroundingStat';
|
||||||
|
|
||||||
export interface AllStatsOverviewProps {
|
export interface AllStatsOverviewProps {
|
||||||
stats: AllStats;
|
stats: AllStats;
|
||||||
@@ -69,7 +69,9 @@ export default function AllStatsOverview({
|
|||||||
>
|
>
|
||||||
<WN8SurroundingStat
|
<WN8SurroundingStat
|
||||||
label="Tier"
|
label="Tier"
|
||||||
value={supplementaryStats.tier.toFixed(2)}
|
value={
|
||||||
|
supplementaryStats.tier ? supplementaryStats.tier.toFixed(2) : '--'
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<WN8SurroundingStat
|
<WN8SurroundingStat
|
||||||
label="Survival"
|
label="Survival"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export interface RootProps {
|
|||||||
|
|
||||||
export function Root({ children }: RootProps) {
|
export function Root({ children }: RootProps) {
|
||||||
return (
|
return (
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import getWN8Percentile from '../../../core/blitz/getWN8Percentile.js';
|
import getWN8Percentile from '../../../core/blitz/getWN8Percentile';
|
||||||
import { RowDiscriminator } from './RowDiscriminator.js';
|
import { theme } from '../../../stitches.config';
|
||||||
import { RowStat } from './RowStat.js';
|
import { RowDiscriminator } from './RowDiscriminator';
|
||||||
|
import { RowStat } from './RowStat';
|
||||||
|
|
||||||
export interface RowProps {
|
export interface RowProps {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -10,14 +11,17 @@ export interface RowProps {
|
|||||||
careerWN8?: number;
|
careerWN8?: number;
|
||||||
damage: number;
|
damage: number;
|
||||||
careerDamage: number;
|
careerDamage: number;
|
||||||
survival: number;
|
|
||||||
careerSurvival: number;
|
|
||||||
battles: number;
|
battles: number;
|
||||||
careerBattles: number;
|
careerBattles: number;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
|
minimized: boolean;
|
||||||
|
isListing: boolean;
|
||||||
|
naked?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Row({
|
export function Row({
|
||||||
|
isListing,
|
||||||
|
minimized,
|
||||||
name,
|
name,
|
||||||
winrate,
|
winrate,
|
||||||
careerWinrate,
|
careerWinrate,
|
||||||
@@ -25,44 +29,46 @@ export function Row({
|
|||||||
careerWN8,
|
careerWN8,
|
||||||
damage,
|
damage,
|
||||||
careerDamage,
|
careerDamage,
|
||||||
survival,
|
|
||||||
careerSurvival,
|
|
||||||
battles,
|
battles,
|
||||||
careerBattles,
|
careerBattles,
|
||||||
icon,
|
icon,
|
||||||
|
naked,
|
||||||
}: RowProps) {
|
}: RowProps) {
|
||||||
return (
|
return (
|
||||||
<div style={{ display: 'flex', gap: 16 }}>
|
<div
|
||||||
<RowDiscriminator name={name} icon={icon} />
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
borderRadius: 4,
|
||||||
|
backgroundColor: isListing
|
||||||
|
? theme.colors.componentInteractive
|
||||||
|
: theme.colors.appBackground2,
|
||||||
|
padding: 8,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RowDiscriminator minimized={minimized} name={name} icon={icon} />
|
||||||
|
|
||||||
<RowStat
|
<RowStat
|
||||||
name="Winrate"
|
minimized={minimized}
|
||||||
|
name={`Winrate • ${(careerWinrate * 100).toFixed(2)}%`}
|
||||||
value={`${(winrate * 100).toFixed(2)}%`}
|
value={`${(winrate * 100).toFixed(2)}%`}
|
||||||
career={`${(careerWinrate * 100).toFixed(2)}%`}
|
|
||||||
delta={winrate - careerWinrate}
|
delta={winrate - careerWinrate}
|
||||||
/>
|
/>
|
||||||
<RowStat
|
<RowStat
|
||||||
name="WN8"
|
minimized={minimized}
|
||||||
|
name={`WN8 • ${careerWN8 === undefined ? '--' : careerWN8.toFixed(0)}`}
|
||||||
value={WN8 === undefined ? '--' : WN8.toFixed(0)}
|
value={WN8 === undefined ? '--' : WN8.toFixed(0)}
|
||||||
career={careerWN8 === undefined ? '--' : careerWN8.toFixed(0)}
|
|
||||||
percentile={WN8 === undefined ? undefined : getWN8Percentile(WN8)}
|
percentile={WN8 === undefined ? undefined : getWN8Percentile(WN8)}
|
||||||
/>
|
/>
|
||||||
<RowStat
|
<RowStat
|
||||||
name="Damage"
|
minimized={minimized}
|
||||||
value={damage.toFixed(2)}
|
name={`Damage • ${careerDamage.toFixed(0)}`}
|
||||||
career={careerDamage.toFixed(2)}
|
value={damage.toFixed(0)}
|
||||||
delta={damage - careerDamage}
|
delta={damage - careerDamage}
|
||||||
/>
|
/>
|
||||||
<RowStat
|
<RowStat
|
||||||
name="Survival"
|
minimized={minimized}
|
||||||
value={`${(survival * 100).toFixed(2)}%`}
|
name={`Battles • ${careerBattles.toFixed(0)}`}
|
||||||
career={`${(careerSurvival * 100).toFixed(2)}%`}
|
|
||||||
delta={survival - careerSurvival}
|
|
||||||
/>
|
|
||||||
<RowStat
|
|
||||||
name="Battles"
|
|
||||||
value={battles.toFixed(0)}
|
value={battles.toFixed(0)}
|
||||||
career={careerBattles.toFixed(0)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
import { theme } from '../../../stitches.config.js';
|
import { theme } from '../../../stitches.config';
|
||||||
|
|
||||||
export interface RowDiscriminatorProps {
|
export interface RowDiscriminatorProps {
|
||||||
name: string;
|
name: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
|
minimized: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function RowDiscriminator({ name, icon }: RowDiscriminatorProps) {
|
export function RowDiscriminator({
|
||||||
|
name,
|
||||||
|
icon,
|
||||||
|
minimized,
|
||||||
|
}: RowDiscriminatorProps) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@@ -19,7 +24,7 @@ export function RowDiscriminator({ name, icon }: RowDiscriminatorProps) {
|
|||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{icon && (
|
{!minimized && icon && (
|
||||||
<img
|
<img
|
||||||
src={icon}
|
src={icon}
|
||||||
height={0}
|
height={0}
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
import { Percentile } from '../../../constants/percentiles.js';
|
import { Percentile } from '../../../constants/percentiles';
|
||||||
import { theme } from '../../../stitches.config.js';
|
import { theme } from '../../../stitches.config';
|
||||||
import PercentileIndicator from '../../PercentileIndicator.js';
|
import PercentileIndicator from '../../PercentileIndicator';
|
||||||
|
|
||||||
export interface RowStatProps {
|
export interface RowStatProps {
|
||||||
name: string;
|
name: string;
|
||||||
value: string;
|
value: string | number;
|
||||||
career: string;
|
|
||||||
delta?: number;
|
delta?: number;
|
||||||
percentile?: Percentile;
|
percentile?: Percentile;
|
||||||
|
minimized?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function RowStat({
|
export function RowStat({
|
||||||
name,
|
name,
|
||||||
value,
|
value,
|
||||||
career,
|
|
||||||
delta,
|
delta,
|
||||||
percentile,
|
percentile,
|
||||||
|
minimized,
|
||||||
}: RowStatProps) {
|
}: RowStatProps) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -28,10 +28,6 @@ export function RowStat({
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span style={{ color: theme.colors.textLowContrast, fontSize: 12 }}>
|
|
||||||
{career}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@@ -58,16 +54,17 @@ export function RowStat({
|
|||||||
style={{
|
style={{
|
||||||
color: theme.colors.textHighContrast,
|
color: theme.colors.textHighContrast,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: 'bold',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{value}
|
{value}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{!minimized && (
|
||||||
<span style={{ color: theme.colors.textLowContrast, fontSize: 12 }}>
|
<span style={{ color: theme.colors.textLowContrast, fontSize: 12 }}>
|
||||||
{name}
|
{name}
|
||||||
</span>
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export * from './components/Root.js';
|
export * from './components/Root';
|
||||||
export * from './components/Row.js';
|
export * from './components/Row';
|
||||||
export * from './components/RowDiscriminator.js';
|
export * from './components/RowDiscriminator';
|
||||||
export * from './components/RowStat.js';
|
export * from './components/RowStat';
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
import getWN8Percentile from '../core/blitz/getWN8Percentile.js';
|
|
||||||
import isNumber from '../core/node/isNumber.js';
|
|
||||||
import { AllStats, SupplementaryStats } from '../types/accountInfo.js';
|
|
||||||
import GenericStats from './GenericStats/index.js';
|
|
||||||
|
|
||||||
export interface GenericAllStatsProps {
|
|
||||||
stats: AllStats;
|
|
||||||
supplementaryStats?: SupplementaryStats;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function GenericAllStats({
|
|
||||||
stats,
|
|
||||||
supplementaryStats,
|
|
||||||
}: GenericAllStatsProps) {
|
|
||||||
const damageRatio = stats.damage_dealt / stats.damage_received;
|
|
||||||
const killsToDeathRatio =
|
|
||||||
stats.frags / (stats.battles - stats.survived_battles);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<GenericStats
|
|
||||||
stats={[
|
|
||||||
['Winrate', `${(100 * (stats.wins / stats.battles)).toFixed(2)}%`],
|
|
||||||
[
|
|
||||||
'WN8',
|
|
||||||
supplementaryStats
|
|
||||||
? isNumber(supplementaryStats.WN8)
|
|
||||||
? supplementaryStats.WN8!.toFixed(0)
|
|
||||||
: '--'
|
|
||||||
: undefined,
|
|
||||||
isNumber(supplementaryStats?.WN8)
|
|
||||||
? getWN8Percentile(supplementaryStats!.WN8!)
|
|
||||||
: undefined,
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'Survival',
|
|
||||||
`${(100 * (stats.survived_battles / stats.battles)).toFixed(2)}%`,
|
|
||||||
],
|
|
||||||
['Accuracy', `${((stats.hits / stats.shots) * 100).toFixed(2)}%`],
|
|
||||||
['Battles', stats.battles],
|
|
||||||
['Wins', stats.wins],
|
|
||||||
['Losses', stats.losses],
|
|
||||||
['Average damage', (stats.damage_dealt / stats.battles).toFixed(0)],
|
|
||||||
['Average tier', supplementaryStats?.tier.toFixed(2)],
|
|
||||||
['Average XP', (stats.xp / stats.battles).toFixed(0)],
|
|
||||||
['Average shots', (stats.shots / stats.battles).toFixed(2)],
|
|
||||||
['Average hits', (stats.hits / stats.battles).toFixed(2)],
|
|
||||||
['Average kills', (stats.frags / stats.battles).toFixed(2)],
|
|
||||||
['Average spots', (stats.spotted / stats.battles).toFixed(2)],
|
|
||||||
['Damage ratio', isFinite(damageRatio) ? damageRatio.toFixed(2) : '--'],
|
|
||||||
[
|
|
||||||
'Kills to death ratio',
|
|
||||||
isFinite(killsToDeathRatio) ? killsToDeathRatio.toFixed(2) : '--',
|
|
||||||
],
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
import { Percentile } from '../../../constants/percentiles';
|
||||||
|
import { theme } from '../../../stitches.config';
|
||||||
|
import PercentileIndicator from '../../PercentileIndicator';
|
||||||
|
|
||||||
|
export interface CardItem {
|
||||||
|
label: string;
|
||||||
|
value: string | number | undefined;
|
||||||
|
percentile?: Percentile;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CardProps {
|
||||||
|
items: CardItem[];
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Card({ items, title }: CardProps) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: 16,
|
||||||
|
flex: 1,
|
||||||
|
borderRadius: 4,
|
||||||
|
backgroundColor: theme.colors.componentInteractive,
|
||||||
|
padding: 8,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
color: theme.colors.textHighContrast,
|
||||||
|
fontSize: 24,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: 8,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{items.map((item) => (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
gap: 8,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span style={{ fontSize: 16, color: theme.colors.textLowContrast }}>
|
||||||
|
{item.label}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
height: 1,
|
||||||
|
borderRadius: 0.5,
|
||||||
|
backgroundColor: theme.colors.borderNonInteractive,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
gap: 4,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{item.percentile !== undefined && (
|
||||||
|
<PercentileIndicator percentile={item.percentile} />
|
||||||
|
)}
|
||||||
|
<span
|
||||||
|
style={{ fontSize: 16, color: theme.colors.textHighContrast }}
|
||||||
|
>
|
||||||
|
{item.value === undefined || Number.isNaN(item.value)
|
||||||
|
? '--'
|
||||||
|
: item.value}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
export interface RootProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Root({ children }: RootProps) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: 8,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export interface RowProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Row({ children }: RowProps) {
|
||||||
|
return <div style={{ display: 'flex', gap: 8 }}>{children}</div>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
import getWN8Percentile from '../../core/blitz/getWN8Percentile';
|
||||||
|
import isNumber from '../../core/node/isNumber';
|
||||||
|
import { AllStats, SupplementaryStats } from '../../types/accountInfo';
|
||||||
|
import { Card } from './components/Card';
|
||||||
|
import { Root } from './components/Root';
|
||||||
|
import { Row } from './components/Row';
|
||||||
|
|
||||||
|
// TODO: remove the .js endings
|
||||||
|
|
||||||
|
export interface GenericAllStatsProps {
|
||||||
|
stats: AllStats;
|
||||||
|
supplementaryStats?: SupplementaryStats;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function GenericAllStats({
|
||||||
|
stats,
|
||||||
|
supplementaryStats,
|
||||||
|
}: GenericAllStatsProps) {
|
||||||
|
const damageRatio = stats.damage_dealt / stats.damage_received;
|
||||||
|
const killsToDeathRatio =
|
||||||
|
stats.frags / (stats.battles - stats.survived_battles);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Root>
|
||||||
|
<Row>
|
||||||
|
<Card
|
||||||
|
title="Games"
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
label: 'Winrate',
|
||||||
|
value: `${(100 * (stats.wins / stats.battles)).toFixed(2)}%`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Battles',
|
||||||
|
value: stats.battles,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Wins / Losses',
|
||||||
|
value: `${stats.wins} / ${stats.losses}`,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<Card
|
||||||
|
title="Efficiency"
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
label: 'WN8',
|
||||||
|
value:
|
||||||
|
supplementaryStats && isNumber(supplementaryStats.WN8)
|
||||||
|
? supplementaryStats.WN8!.toFixed(0)
|
||||||
|
: undefined,
|
||||||
|
percentile: isNumber(supplementaryStats?.WN8)
|
||||||
|
? getWN8Percentile(supplementaryStats!.WN8!)
|
||||||
|
: undefined,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Survival',
|
||||||
|
value: `${(
|
||||||
|
100 *
|
||||||
|
(stats.survived_battles / stats.battles)
|
||||||
|
).toFixed(2)}%`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Accuracy',
|
||||||
|
value: `${((stats.hits / stats.shots) * 100).toFixed(2)}%`,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Card
|
||||||
|
title="Lethality"
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
label: 'Average damage',
|
||||||
|
value: (stats.damage_dealt / stats.battles).toFixed(0),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Damage per tier',
|
||||||
|
value: supplementaryStats?.tier
|
||||||
|
? (
|
||||||
|
stats.damage_dealt /
|
||||||
|
stats.battles /
|
||||||
|
supplementaryStats.tier
|
||||||
|
).toFixed(0)
|
||||||
|
: undefined,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Damage ratio',
|
||||||
|
value: isFinite(damageRatio) ? damageRatio.toFixed(2) : undefined,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Average kills',
|
||||||
|
value: (stats.frags / stats.battles).toFixed(0),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<Card
|
||||||
|
title="Efficiency"
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
label: 'Average tier',
|
||||||
|
value: supplementaryStats?.tier?.toFixed(2),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Average spots',
|
||||||
|
value: (stats.spotted / stats.battles).toFixed(2),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Average XP',
|
||||||
|
value: (stats.xp / stats.battles).toFixed(0),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Kills to death ratio',
|
||||||
|
value: isFinite(killsToDeathRatio)
|
||||||
|
? killsToDeathRatio.toFixed(2)
|
||||||
|
: undefined,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Row>
|
||||||
|
</Root>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Stat } from '../index.js';
|
import { Stat } from '..';
|
||||||
import GenericStatsRow from './GenericStatsRow.js';
|
import GenericStatsRow from './GenericStatsRow';
|
||||||
|
|
||||||
export interface GenericStatsColumnProps {
|
export interface GenericStatsColumnProps {
|
||||||
stats: Stat[];
|
stats: Stat[];
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { theme } from '../../../stitches.config.js';
|
import { Stat } from '..';
|
||||||
import PercentileIndicator from '../../PercentileIndicator.js';
|
import { theme } from '../../../stitches.config';
|
||||||
import { Stat } from '../index.js';
|
import PercentileIndicator from '../../PercentileIndicator';
|
||||||
|
|
||||||
export interface GenericStatsRowProps {
|
export interface GenericStatsRowProps {
|
||||||
stat: Stat;
|
stat: Stat;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Percentile } from '../../constants/percentiles.js';
|
import { Percentile } from '../../constants/percentiles';
|
||||||
import GenericStatsColumn from './components/GenericStatsColumn.js';
|
import GenericStatsColumn from './components/GenericStatsColumn';
|
||||||
|
|
||||||
export type Stat =
|
export type Stat =
|
||||||
| [string, string | number | undefined]
|
| [string, string | number | undefined]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { blue, green, red, slateDark } from '@radix-ui/colors';
|
import { blue, green, red, slateDark } from '@radix-ui/colors';
|
||||||
import { GRAPH_HEIGHT, GRAPH_WIDTH } from './Root.js';
|
import { GRAPH_HEIGHT, GRAPH_WIDTH } from './Root';
|
||||||
|
|
||||||
export enum LineColor {
|
export enum LineColor {
|
||||||
Red,
|
Red,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { times } from 'lodash';
|
import { times } from 'lodash';
|
||||||
import { theme } from '../../../stitches.config.js';
|
import { theme } from '../../../stitches.config';
|
||||||
import { MarginInputProps } from './Root.js';
|
import { MarginInputProps } from './Root';
|
||||||
|
|
||||||
export enum MarginOrientation {
|
export enum MarginOrientation {
|
||||||
Vertical,
|
Vertical,
|
||||||
@@ -37,7 +37,7 @@ export function Margin({
|
|||||||
key={index}
|
key={index}
|
||||||
style={{
|
style={{
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
color: theme.colors.textLowContrast,
|
color: theme.colors.textHighContrast,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{`${(
|
{`${(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { times } from 'lodash';
|
import { times } from 'lodash';
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
import { theme } from '../../../stitches.config.js';
|
import { theme } from '../../../stitches.config';
|
||||||
import { Margin, MarginOrientation } from './Margin.js';
|
import { Margin, MarginOrientation } from './Margin';
|
||||||
|
|
||||||
export const GRAPH_WIDTH = 544;
|
export const GRAPH_WIDTH = 544;
|
||||||
export const GRAPH_HEIGHT = 320;
|
export const GRAPH_HEIGHT = 320;
|
||||||
@@ -20,6 +20,7 @@ export interface RootProps {
|
|||||||
separations?: number;
|
separations?: number;
|
||||||
xMinLabel: string;
|
xMinLabel: string;
|
||||||
xMaxLabel: string;
|
xMaxLabel: string;
|
||||||
|
xTitle: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Root({
|
export function Root({
|
||||||
@@ -29,6 +30,7 @@ export function Root({
|
|||||||
separations = 5,
|
separations = 5,
|
||||||
xMinLabel,
|
xMinLabel,
|
||||||
xMaxLabel,
|
xMaxLabel,
|
||||||
|
xTitle,
|
||||||
}: RootProps) {
|
}: RootProps) {
|
||||||
return (
|
return (
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
||||||
@@ -39,10 +41,10 @@ export function Root({
|
|||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span style={{ fontSize: 16, color: theme.colors.textLowContrast }}>
|
<span style={{ fontSize: 16, color: theme.colors.textHighContrast }}>
|
||||||
{xMinLabel}
|
{xMinLabel}
|
||||||
</span>
|
</span>
|
||||||
<span style={{ fontSize: 16, color: theme.colors.textLowContrast }}>
|
<span style={{ fontSize: 16, color: theme.colors.textHighContrast }}>
|
||||||
{xMaxLabel}
|
{xMaxLabel}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -88,6 +90,19 @@ export function Root({
|
|||||||
orientation={MarginOrientation.Horizontal}
|
orientation={MarginOrientation.Horizontal}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
paddingLeft: 64,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span style={{ fontSize: 16, color: theme.colors.textLowContrast }}>
|
||||||
|
{xTitle}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
export * from './components/Line.js';
|
export * from './components/Line';
|
||||||
export * from './components/Margin.js';
|
export * from './components/Margin';
|
||||||
export * from './components/Root.js';
|
export * from './components/Root';
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { theme } from '../stitches.config.js';
|
import { theme } from '../stitches.config';
|
||||||
|
|
||||||
export enum NoDataType {
|
export enum NoDataType {
|
||||||
BattlesInPeriod,
|
BattlesInPeriod,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
teal,
|
teal,
|
||||||
yellow,
|
yellow,
|
||||||
} from '@radix-ui/colors';
|
} from '@radix-ui/colors';
|
||||||
import { Percentile } from '../constants/percentiles.js';
|
import { Percentile } from '../constants/percentiles';
|
||||||
|
|
||||||
export interface PercentileIndicatorProps {
|
export interface PercentileIndicatorProps {
|
||||||
percentile: Percentile;
|
percentile: Percentile;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
import { theme } from '../stitches.config.js';
|
import { theme } from '../stitches.config';
|
||||||
|
|
||||||
export enum PoweredByType {
|
export enum PoweredByType {
|
||||||
BlitzStars,
|
BlitzStars,
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
|
export interface ColumnProps {
|
||||||
|
children: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Column({ children }: ColumnProps) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: 8,
|
||||||
|
flex: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,35 +1,78 @@
|
|||||||
import { TANK_ICONS, TankType } from '../../../core/blitz/tankopedia.js';
|
import {
|
||||||
|
TANK_ICONS,
|
||||||
|
TANK_ICONS_COLLECTOR,
|
||||||
|
TANK_ICONS_PREMIUM,
|
||||||
|
} from '../../../core/blitz/tankopedia';
|
||||||
|
import { theme } from '../../../stitches.config';
|
||||||
|
|
||||||
export interface ItemProps {
|
export enum TreeType {
|
||||||
icon?: string;
|
TechTree,
|
||||||
type: TankType;
|
Premium,
|
||||||
name: string;
|
Collector,
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Item({ icon, type, name }: ItemProps) {
|
export interface ItemProps {
|
||||||
|
image?: string;
|
||||||
|
tankType?: string;
|
||||||
|
name: string;
|
||||||
|
treeType: TreeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TREE_TYPE_COLOR = {
|
||||||
|
[TreeType.TechTree]: '',
|
||||||
|
[TreeType.Premium]: '_amber',
|
||||||
|
[TreeType.Collector]: '_blue',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
const TREE_TYPE_ICONS = {
|
||||||
|
[TreeType.TechTree]: TANK_ICONS,
|
||||||
|
[TreeType.Premium]: TANK_ICONS_PREMIUM,
|
||||||
|
[TreeType.Collector]: TANK_ICONS_COLLECTOR,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function Item({ image, tankType, name, treeType }: ItemProps) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
flex: 1,
|
|
||||||
minWidth: 128,
|
|
||||||
height: 128,
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
height: 32,
|
||||||
|
paddingLeft: 8,
|
||||||
|
paddingRight: 8,
|
||||||
|
overflow: 'hidden',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
borderRadius: 4,
|
||||||
|
backgroundColor:
|
||||||
|
theme.colors[`componentInteractive${TREE_TYPE_COLOR[treeType]}`],
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
gap: 4,
|
||||||
|
flex: 1,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'flex-start',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{icon && <img src={icon} style={{ flex: 1, objectFit: 'contain' }} />}
|
{tankType && (
|
||||||
|
<img
|
||||||
<div style={{ display: 'flex', margin: 'auto', gap: 4 }}>
|
src={TREE_TYPE_ICONS[treeType][tankType]}
|
||||||
<img src={TANK_ICONS[type]} style={{ width: 14, height: 14 }} />
|
style={{ width: 14, height: 14 }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<span
|
<span
|
||||||
style={{ fontSize: 16, textAlign: 'center', whiteSpace: 'nowrap' }}
|
style={{
|
||||||
|
fontSize: 16,
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
color: theme.colors[`textLowContrast${TREE_TYPE_COLOR[treeType]}`],
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{name}
|
{name}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{image && <img src={image} style={{ flex: 1 }} />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -9,10 +9,7 @@ export function Root({ children }: RootProps) {
|
|||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexWrap: 'wrap',
|
gap: 8,
|
||||||
gap: 16,
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
export * from './components/Item.js';
|
export * from './components/Column';
|
||||||
export * from './components/Root.js';
|
export * from './components/Item';
|
||||||
|
export * from './components/Root';
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { TIER_ROMAN_NUMERALS, Tier } from '../core/blitz/tankopedia.js';
|
import { TIER_ROMAN_NUMERALS, Tier } from '../core/blitz/tankopedia';
|
||||||
import { themeLight } from '../stitches.config.js';
|
import { theme } from '../stitches.config';
|
||||||
|
|
||||||
export type TierWeightsRecord = Partial<Record<Tier, number>>;
|
export type TierWeightsRecord = Partial<Record<Tier, number>>;
|
||||||
|
|
||||||
@@ -14,9 +14,9 @@ export default function TierWeights({ weights }: TierWeightsProps) {
|
|||||||
<div
|
<div
|
||||||
key={tier}
|
key={tier}
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: themeLight.colors.appBackground1,
|
backgroundColor: theme.colors.appBackground1_light,
|
||||||
flex: weight,
|
flex: weight,
|
||||||
color: themeLight.colors.textHighContrast,
|
color: theme.colors.textHighContrast_light,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import isDev from '../core/node/isDev.js';
|
import isDev from '../core/node/isDev';
|
||||||
import { theme } from '../stitches.config.js';
|
import { theme } from '../stitches.config';
|
||||||
|
|
||||||
export interface TitleBarProps {
|
export interface TitleBarProps {
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
import { theme } from '../stitches.config.js';
|
import { theme } from '../stitches.config';
|
||||||
|
|
||||||
export interface WrapperProps {
|
export interface WrapperProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
|
naked?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Wrapper({ children }: WrapperProps) {
|
export default function Wrapper({ children, naked }: WrapperProps) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@@ -14,8 +15,11 @@ export default function Wrapper({ children }: WrapperProps) {
|
|||||||
padding: 16,
|
padding: 16,
|
||||||
gap: 32,
|
gap: 32,
|
||||||
width: 640,
|
width: 640,
|
||||||
background: 'url(https://i.imgur.com/PhS06NJ.png)',
|
|
||||||
color: theme.colors.textHighContrast,
|
color: theme.colors.textHighContrast,
|
||||||
|
|
||||||
|
...(!naked && {
|
||||||
|
background: 'url(https://i.imgur.com/PhS06NJ.png)',
|
||||||
|
}),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
export type RegionDomain = 'com' | 'eu' | 'asia';
|
||||||
|
export type RegionSubdomain = 'na' | 'eu' | 'asia';
|
||||||
|
|
||||||
|
export const REGION_DOMAIN_NAMES: Record<RegionDomain, string> = {
|
||||||
|
com: 'North America',
|
||||||
|
eu: 'Europe',
|
||||||
|
asia: 'Asia',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const REGION_SUBDOMAIN_NAMES: Record<RegionSubdomain, string> = {
|
||||||
|
na: 'North America',
|
||||||
|
eu: 'Europe',
|
||||||
|
asia: 'Asia',
|
||||||
|
};
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
export type BlitzServer = 'com' | 'eu' | 'asia';
|
|
||||||
|
|
||||||
export const BLITZ_SERVERS: Record<BlitzServer, string> = {
|
|
||||||
com: 'North America',
|
|
||||||
eu: 'Europe',
|
|
||||||
asia: 'Asia',
|
|
||||||
};
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { AllStats } from '../../types/accountInfo.js';
|
import { AllStats } from '../../types/accountInfo';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formula from https://web.archive.org/web/20190105064817/http://wiki.wnefficiency.net/pages/WN8
|
* Formula from https://web.archive.org/web/20190105064817/http://wiki.wnefficiency.net/pages/WN8
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { AllStats } from '../../types/accountInfo.js';
|
import { AllStats } from '../../types/accountInfo';
|
||||||
|
|
||||||
export default function diffStats(stats1: AllStats, stats2: AllStats) {
|
export default function diffStats(stats1: AllStats, stats2: AllStats) {
|
||||||
function diff(value: (allStats: AllStats) => number) {
|
function diff(value: (allStats: AllStats) => number) {
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { BlitzServer } from '../../constants/servers.js';
|
import { RegionDomain } from '../../constants/regions';
|
||||||
import { serverAndIdPattern } from '../discord/resolvePlayerFromCommand.js';
|
import { serverAndIdPattern } from '../discord/resolvePlayerFromCommand';
|
||||||
import throwError from '../node/throwError.js';
|
import throwError from '../node/throwError';
|
||||||
import listClans from './listClans.js';
|
import listClans from './listClans';
|
||||||
|
|
||||||
export default async function getBlitzClan(clan: string) {
|
export default async function getBlitzClan(clan: string) {
|
||||||
if (serverAndIdPattern.test(clan)) {
|
if (serverAndIdPattern.test(clan)) {
|
||||||
const [server, accountId] = clan.split('/');
|
const [server, accountId] = clan.split('/');
|
||||||
return { server: server as BlitzServer, id: Number(accountId) };
|
return { server: server as RegionDomain, id: Number(accountId) };
|
||||||
} else {
|
} else {
|
||||||
const accounts = await listClans(clan);
|
const accounts = await listClans(clan);
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { BLITZ_SERVERS, BlitzServer } from '../../constants/servers.js';
|
import { REGION_DOMAIN_NAMES, RegionDomain } from '../../constants/regions';
|
||||||
import { TanksStats } from '../../types/tanksStats.js';
|
import { TanksStats } from '../../types/tanksStats';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../node/arguments.js';
|
import { WARGAMING_APPLICATION_ID } from '../node/arguments';
|
||||||
import throwError from '../node/throwError.js';
|
import throwError from '../node/throwError';
|
||||||
import getWargamingResponse from './getWargamingResponse.js';
|
import getWargamingResponse from './getWargamingResponse';
|
||||||
|
|
||||||
export default async function getTankStats(server: BlitzServer, id: number) {
|
export default async function getTankStats(server: RegionDomain, id: number) {
|
||||||
const tankStats = await getWargamingResponse<TanksStats>(
|
const tankStats = await getWargamingResponse<TanksStats>(
|
||||||
`https://api.wotblitz.${server}/wotb/tanks/stats/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
`https://api.wotblitz.${server}/wotb/tanks/stats/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
||||||
);
|
);
|
||||||
@@ -12,7 +12,7 @@ export default async function getTankStats(server: BlitzServer, id: number) {
|
|||||||
if (tankStats[id] === null) {
|
if (tankStats[id] === null) {
|
||||||
throw throwError(
|
throw throwError(
|
||||||
'No tank stats available',
|
'No tank stats available',
|
||||||
`Wargaming says there is no tank stats for this account. This account may not have any battles/tanks or exist in the ${BLITZ_SERVERS[server]} server.`,
|
`Wargaming says there is no tank stats for this account. This account may not have any battles/tanks or exist in the ${REGION_DOMAIN_NAMES[server]} server.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { TreeType } from '../../components/Tanks';
|
||||||
|
import { tankopedia } from './tankopedia';
|
||||||
|
|
||||||
|
export default async function getTreeType(id: number) {
|
||||||
|
const entry = (await tankopedia)[id];
|
||||||
|
|
||||||
|
if (entry?.is_collectible) return TreeType.Collector;
|
||||||
|
if (entry?.is_premium) return TreeType.Premium;
|
||||||
|
|
||||||
|
return TreeType.TechTree;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Percentile } from '../../constants/percentiles.js';
|
import { Percentile } from '../../constants/percentiles';
|
||||||
|
|
||||||
export const WN8_PERCENTILES: [number, Percentile][] = [
|
export const WN8_PERCENTILES: [number, Percentile][] = [
|
||||||
[0, Percentile.VeryBad],
|
[0, Percentile.VeryBad],
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import throwError from '../node/throwError.js';
|
import throwError from '../node/throwError';
|
||||||
|
|
||||||
export type WargamingResponse<Data extends object> =
|
export type WargamingResponse<Data extends object> =
|
||||||
| {
|
| {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { BlitzServer } from '../../constants/servers.js';
|
import { RegionDomain } from '../../constants/regions';
|
||||||
import { Clan, ClanList } from '../../types/clanList.js';
|
import { Clan, ClanList } from '../../types/clanList';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../node/arguments.js';
|
import { WARGAMING_APPLICATION_ID } from '../node/arguments';
|
||||||
import getWargamingResponse from './getWargamingResponse.js';
|
import getWargamingResponse from './getWargamingResponse';
|
||||||
|
|
||||||
export type ClanListWithServer = (Clan & {
|
export type ClanListWithServer = (Clan & {
|
||||||
server: 'com' | 'eu' | 'asia';
|
server: 'com' | 'eu' | 'asia';
|
||||||
@@ -21,7 +21,7 @@ export default async function listClans(search: string, limit = 9) {
|
|||||||
value &&
|
value &&
|
||||||
value.map((account) => ({
|
value.map((account) => ({
|
||||||
...account,
|
...account,
|
||||||
server: 'com' as BlitzServer,
|
server: 'com' as RegionDomain,
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
getWargamingResponse<ClanList>(
|
getWargamingResponse<ClanList>(
|
||||||
@@ -31,7 +31,7 @@ export default async function listClans(search: string, limit = 9) {
|
|||||||
value &&
|
value &&
|
||||||
value.map((account) => ({
|
value.map((account) => ({
|
||||||
...account,
|
...account,
|
||||||
server: 'eu' as BlitzServer,
|
server: 'eu' as RegionDomain,
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
getWargamingResponse<ClanList>(
|
getWargamingResponse<ClanList>(
|
||||||
@@ -41,7 +41,7 @@ export default async function listClans(search: string, limit = 9) {
|
|||||||
value &&
|
value &&
|
||||||
value.map((account) => ({
|
value.map((account) => ({
|
||||||
...account,
|
...account,
|
||||||
server: 'asia' as BlitzServer,
|
server: 'asia' as RegionDomain,
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { BlitzServer } from '../../constants/servers.js';
|
import { RegionDomain } from '../../constants/regions';
|
||||||
import { Account, AccountList } from '../../types/accountList.js';
|
import { Account, AccountList } from '../../types/accountList';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../node/arguments.js';
|
import { WARGAMING_APPLICATION_ID } from '../node/arguments';
|
||||||
import getWargamingResponse from './getWargamingResponse.js';
|
import getWargamingResponse from './getWargamingResponse';
|
||||||
|
|
||||||
export type AccountListWithServer = (Account & {
|
export type AccountListWithServer = (Account & {
|
||||||
server: 'com' | 'eu' | 'asia';
|
server: 'com' | 'eu' | 'asia';
|
||||||
@@ -25,7 +25,7 @@ export default async function listPlayers(search: string, limit = 9) {
|
|||||||
value &&
|
value &&
|
||||||
value.map((account) => ({
|
value.map((account) => ({
|
||||||
...account,
|
...account,
|
||||||
server: 'com' as BlitzServer,
|
server: 'com' as RegionDomain,
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
getWargamingResponse<AccountList>(
|
getWargamingResponse<AccountList>(
|
||||||
@@ -35,7 +35,7 @@ export default async function listPlayers(search: string, limit = 9) {
|
|||||||
value &&
|
value &&
|
||||||
value.map((account) => ({
|
value.map((account) => ({
|
||||||
...account,
|
...account,
|
||||||
server: 'eu' as BlitzServer,
|
server: 'eu' as RegionDomain,
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
getWargamingResponse<AccountList>(
|
getWargamingResponse<AccountList>(
|
||||||
@@ -45,7 +45,7 @@ export default async function listPlayers(search: string, limit = 9) {
|
|||||||
value &&
|
value &&
|
||||||
value.map((account) => ({
|
value.map((account) => ({
|
||||||
...account,
|
...account,
|
||||||
server: 'asia' as BlitzServer,
|
server: 'asia' as RegionDomain,
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { go } from 'fuzzysort';
|
import { go } from 'fuzzysort';
|
||||||
import throwError from '../node/throwError.js';
|
import throwError from '../node/throwError';
|
||||||
import { TANKS, tankopedia } from './tankopedia.js';
|
import { TANKS, tankopedia } from './tankopedia';
|
||||||
|
|
||||||
export default async function resolveTankId(tank: string) {
|
export default async function resolveTankId(tank: string) {
|
||||||
const number = parseInt(tank);
|
const number = parseInt(tank);
|
||||||
|
|
||||||
if (Number.isNaN(number)) {
|
if (Number.isNaN(number)) {
|
||||||
const searchResult = go(tank, TANKS, { keys: ['name'], limit: 1 });
|
const searchResult = go(tank, await TANKS, { keys: ['name'], limit: 1 });
|
||||||
|
|
||||||
if (searchResult.length === 0) {
|
if (searchResult.length === 0) {
|
||||||
throw throwError(
|
throw throwError(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { tankopedia } from './tankopedia.js';
|
import { tankopedia } from './tankopedia';
|
||||||
|
|
||||||
export default async function resolveTankName(tankId: number) {
|
export default async function resolveTankName(tankId: number) {
|
||||||
const tankopediaName = (await tankopedia)[tankId].name;
|
const tankopediaName = (await tankopedia)[tankId]?.name;
|
||||||
return tankopediaName ? tankopediaName : `Unknown Tank ${tankId}`;
|
return tankopediaName ? tankopediaName : `Unknown Tank ${tankId}`;
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { AllStats } from '../../types/accountInfo.js';
|
import { AllStats } from '../../types/accountInfo';
|
||||||
import { emptyAllStats } from '../blitzstars/getTankStatsDiffed.js';
|
import { emptyAllStats } from '../blitzstars/getDiffedTankStats';
|
||||||
|
|
||||||
export default function sumStats(allStatsCollection: AllStats[]) {
|
export default function sumStats(allStatsCollection: AllStats[]) {
|
||||||
return allStatsCollection.reduce((accumulated, current) => {
|
return allStatsCollection.reduce((accumulated, current) => {
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
export type TankType = 'AT-SPG' | 'lightTank' | 'mediumTank' | 'heavyTank';
|
|
||||||
|
|
||||||
export interface TankopediaEntry {
|
export interface TankopediaEntry {
|
||||||
name: string;
|
name: string;
|
||||||
nation: string;
|
nation: string;
|
||||||
is_premium: boolean;
|
is_premium: boolean;
|
||||||
|
is_collectible: boolean;
|
||||||
tier: number;
|
tier: number;
|
||||||
cost: { price_credit: number; price_gold: number };
|
cost: { price_credit: number; price_gold: number };
|
||||||
images: { preview: string; normal: string };
|
images: { preview: string; normal: string };
|
||||||
tank_id: number;
|
tank_id: number;
|
||||||
type: TankType;
|
type: string;
|
||||||
description: string;
|
description: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Tankopedia {
|
export interface Tankopedia {
|
||||||
[id: number]: TankopediaEntry;
|
[id: number]: TankopediaEntry | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Caching tankopedia...');
|
console.log('Caching tankopedia...');
|
||||||
@@ -23,18 +22,37 @@ export const tankopedia = fetch('https://www.blitzstars.com/bs-tankopedia.json')
|
|||||||
console.log('Tankopedia cached');
|
console.log('Tankopedia cached');
|
||||||
return (wrapperTankopedia as { data: Tankopedia }).data;
|
return (wrapperTankopedia as { data: Tankopedia }).data;
|
||||||
});
|
});
|
||||||
const entries = Object.entries(tankopedia);
|
const entries = new Promise<TankopediaEntry[]>(async (resolve) => {
|
||||||
export const TANK_IDS = entries.map(([, { tank_id }]) => tank_id as number);
|
resolve(Object.entries(await tankopedia).map(([, entry]) => entry));
|
||||||
export const TANKS = entries.map(([, entry]) => entry as TankopediaEntry);
|
});
|
||||||
export const TANK_NAMES = entries.map(([, { name }]) => name);
|
export const TANKS = new Promise<TankopediaEntry[]>(async (resolve) => {
|
||||||
|
resolve((await entries).map((entry) => entry));
|
||||||
|
});
|
||||||
|
export const TANK_NAMES = new Promise<string[]>(async (resolve) => {
|
||||||
|
resolve((await entries).map(({ name }) => name));
|
||||||
|
});
|
||||||
|
|
||||||
export const TANK_ICONS: Record<TankType, 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',
|
||||||
mediumTank: 'https://i.imgur.com/wvf3ltm.png',
|
mediumTank: 'https://i.imgur.com/wvf3ltm.png',
|
||||||
heavyTank: 'https://i.imgur.com/ECeqlZa.png',
|
heavyTank: 'https://i.imgur.com/ECeqlZa.png',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const TANK_ICONS_PREMIUM: Record<string, string> = {
|
||||||
|
'AT-SPG': 'https://i.imgur.com/TCu3EdR.png',
|
||||||
|
lightTank: 'https://i.imgur.com/zdkpTRb.png',
|
||||||
|
mediumTank: 'https://i.imgur.com/3z7eHX6.png',
|
||||||
|
heavyTank: 'https://i.imgur.com/P3vbmyA.png',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TANK_ICONS_COLLECTOR: Record<string, string> = {
|
||||||
|
'AT-SPG': 'https://i.imgur.com/WTjeirB.png',
|
||||||
|
lightTank: 'https://i.imgur.com/EwhtKkU.png',
|
||||||
|
mediumTank: 'https://i.imgur.com/u8YDMBh.png',
|
||||||
|
heavyTank: 'https://i.imgur.com/8xRf3nc.png',
|
||||||
|
};
|
||||||
|
|
||||||
export type Tier = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;
|
export type Tier = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;
|
||||||
|
|
||||||
export const TIER_ROMAN_NUMERALS: Record<Tier, string> = {
|
export const TIER_ROMAN_NUMERALS: Record<Tier, string> = {
|
||||||
|
|||||||
+24
-12
@@ -1,9 +1,9 @@
|
|||||||
import { BlitzServer } from '../../constants/servers.js';
|
import { RegionDomain } from '../../constants/regions';
|
||||||
import { AllStats } from '../../types/accountInfo.js';
|
import { AllStats } from '../../types/accountInfo';
|
||||||
import getTankHistories, {
|
import getTankHistories, {
|
||||||
TankHistories,
|
TankHistories,
|
||||||
TankHistoryRaw,
|
TankHistoryRaw,
|
||||||
} from './getTankHistories.js';
|
} from './getTankHistories';
|
||||||
|
|
||||||
export const emptyAllStats: AllStats = {
|
export const emptyAllStats: AllStats = {
|
||||||
battles: 0,
|
battles: 0,
|
||||||
@@ -34,8 +34,8 @@ export const emptyTankHistoryNode: TankHistoryRaw = {
|
|||||||
all: emptyAllStats,
|
all: emptyAllStats,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default async function getTankStatsDiffed(
|
export default async function getDiffedTankStats(
|
||||||
server: BlitzServer,
|
server: RegionDomain,
|
||||||
id: number,
|
id: number,
|
||||||
start: number,
|
start: number,
|
||||||
end: number,
|
end: number,
|
||||||
@@ -46,26 +46,27 @@ export default async function getTankStatsDiffed(
|
|||||||
|
|
||||||
// sort them even though most won't be used
|
// sort them even though most won't be used
|
||||||
const tankSortedHistory: Record<number, TankHistories> = {};
|
const tankSortedHistory: Record<number, TankHistories> = {};
|
||||||
const tankIds: number[] = [];
|
const tanks: number[] = [];
|
||||||
history.forEach((node) => {
|
history.forEach((node) => {
|
||||||
if (!tankSortedHistory[node.tank_id]) {
|
if (!tankSortedHistory[node.tank_id]) {
|
||||||
tankSortedHistory[node.tank_id] = [node];
|
tankSortedHistory[node.tank_id] = [node];
|
||||||
tankIds.push(node.tank_id);
|
tanks.push(node.tank_id);
|
||||||
} else {
|
} else {
|
||||||
tankSortedHistory[node.tank_id].push(node);
|
tankSortedHistory[node.tank_id].push(node);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// find the ones played within the range
|
// find the ones played within the range
|
||||||
const tankIdsInRange = tankIds.filter((tankId) =>
|
const inRangeTanks = tanks.filter((tankId) =>
|
||||||
tankSortedHistory[tankId].some(
|
tankSortedHistory[tankId].some(
|
||||||
(node) => node.last_battle_time > start && node.last_battle_time < end,
|
(node) => node.last_battle_time > start && node.last_battle_time < end,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
const playedTanks: number[] = [];
|
||||||
|
|
||||||
// fetch history node right before "start" and right before "end"
|
// fetch history node right before "start" and right before "end"
|
||||||
const tanksBeforeAndAfter: Record<number, AllStats> = {};
|
const diffed: Record<number, AllStats> = {};
|
||||||
tankIdsInRange.forEach((tankId) => {
|
inRangeTanks.forEach((tankId) => {
|
||||||
const sortedHistory = tankSortedHistory[tankId];
|
const sortedHistory = tankSortedHistory[tankId];
|
||||||
const previousIndex =
|
const previousIndex =
|
||||||
sortedHistory.findIndex((node) => node.last_battle_time > start) - 1;
|
sortedHistory.findIndex((node) => node.last_battle_time > start) - 1;
|
||||||
@@ -81,7 +82,7 @@ export default async function getTankStatsDiffed(
|
|||||||
|
|
||||||
// check if there was a change in battles as games in ratings do update last_battle_time
|
// check if there was a change in battles as games in ratings do update last_battle_time
|
||||||
if (diff((a) => a.battles) > 0) {
|
if (diff((a) => a.battles) > 0) {
|
||||||
tanksBeforeAndAfter[tankId] = {
|
diffed[tankId] = {
|
||||||
battles: diff((a) => a.battles),
|
battles: diff((a) => a.battles),
|
||||||
capture_points: diff((a) => a.capture_points),
|
capture_points: diff((a) => a.capture_points),
|
||||||
damage_dealt: diff((a) => a.damage_dealt),
|
damage_dealt: diff((a) => a.damage_dealt),
|
||||||
@@ -100,8 +101,19 @@ export default async function getTankStatsDiffed(
|
|||||||
wins: diff((a) => a.wins),
|
wins: diff((a) => a.wins),
|
||||||
xp: diff((a) => a.xp),
|
xp: diff((a) => a.xp),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
playedTanks.push(tankId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return tanksBeforeAndAfter;
|
const order = playedTanks.sort((a, b) => {
|
||||||
|
const aHistories = tankSortedHistory[a];
|
||||||
|
const bHistories = tankSortedHistory[b];
|
||||||
|
const aLatest = aHistories[aHistories.length - 1];
|
||||||
|
const bLatest = bHistories[bHistories.length - 1];
|
||||||
|
|
||||||
|
return bLatest.last_battle_time - aLatest.last_battle_time;
|
||||||
|
});
|
||||||
|
|
||||||
|
return { diffed, order };
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,16 @@
|
|||||||
import { Period } from '../discord/addPeriodSubCommands.js';
|
import { RegionDomain } from '../../constants/regions';
|
||||||
import getTimeDaysAgo from './getTimeDaysAgo.js';
|
import { Period } from '../discord/addPeriodSubCommands';
|
||||||
|
import getTimeDaysAgo from './getTimeDaysAgo';
|
||||||
|
|
||||||
export default function getPeriodStart(period: Exclude<Period, 'custom'>) {
|
export default function getPeriodStart(
|
||||||
|
server: RegionDomain,
|
||||||
|
period: Exclude<Period, 'custom'>,
|
||||||
|
) {
|
||||||
if (period === 'career') {
|
if (period === 'career') {
|
||||||
return -Infinity;
|
return -Infinity;
|
||||||
} else if (period === 'today') {
|
} else if (period === 'today') {
|
||||||
return getTimeDaysAgo(0);
|
return getTimeDaysAgo(server, 1);
|
||||||
} else {
|
} else {
|
||||||
return getTimeDaysAgo(parseInt(period));
|
return getTimeDaysAgo(server, parseInt(period));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
import { BlitzServer } from '../../constants/servers.js';
|
import { RegionDomain } from '../../constants/regions';
|
||||||
import { AccountInfo, AllStats } from '../../types/accountInfo.js';
|
import { AccountInfo, AllStats } from '../../types/accountInfo';
|
||||||
import {
|
import {
|
||||||
GetHistoriesOptions,
|
GetHistoriesOptions,
|
||||||
Histories,
|
Histories,
|
||||||
History,
|
History,
|
||||||
getHistoriesDefaultOptions,
|
getHistoriesDefaultOptions,
|
||||||
} from '../../types/histories.js';
|
} from '../../types/histories';
|
||||||
import getWargamingResponse from '../blitz/getWargamingResponse.js';
|
import getWargamingResponse from '../blitz/getWargamingResponse';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../node/arguments.js';
|
import { WARGAMING_APPLICATION_ID } from '../node/arguments';
|
||||||
import { emptyAllStats } from './getTankStatsDiffed.js';
|
import { emptyAllStats } from './getDiffedTankStats';
|
||||||
|
|
||||||
export interface PlayerHistoryRaw {
|
export interface PlayerHistoryRaw {
|
||||||
clan: { clan_id: number; name: string; tag: string };
|
clan: { clan_id: number; name: string; tag: string };
|
||||||
@@ -25,13 +25,13 @@ export interface PlayerHistoryRaw {
|
|||||||
account_id: number;
|
account_id: number;
|
||||||
[key: string]: number;
|
[key: string]: number;
|
||||||
};
|
};
|
||||||
region: BlitzServer;
|
region: RegionDomain;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PlayerHistoriesRaw = PlayerHistoryRaw[];
|
export type PlayerHistoriesRaw = PlayerHistoryRaw[];
|
||||||
|
|
||||||
export default async function getPlayerHistories(
|
export default async function getPlayerHistories(
|
||||||
server: BlitzServer,
|
server: RegionDomain,
|
||||||
id: number,
|
id: number,
|
||||||
options?: Partial<GetHistoriesOptions>,
|
options?: Partial<GetHistoriesOptions>,
|
||||||
) {
|
) {
|
||||||
@@ -44,7 +44,7 @@ export default async function getPlayerHistories(
|
|||||||
({
|
({
|
||||||
all: history.statistics.all,
|
all: history.statistics.all,
|
||||||
last_battle_time: history.last_battle_time,
|
last_battle_time: history.last_battle_time,
|
||||||
} satisfies History),
|
}) satisfies History,
|
||||||
);
|
);
|
||||||
let lastNonCompliantIndex = -1;
|
let lastNonCompliantIndex = -1;
|
||||||
let hasCompliedOnce = false;
|
let hasCompliedOnce = false;
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { BlitzServer } from '../../constants/servers.js';
|
import { RegionDomain } from '../../constants/regions';
|
||||||
import {
|
import {
|
||||||
GetHistoriesOptions,
|
GetHistoriesOptions,
|
||||||
Histories,
|
Histories,
|
||||||
History,
|
History,
|
||||||
getHistoriesDefaultOptions,
|
getHistoriesDefaultOptions,
|
||||||
} from '../../types/histories.js';
|
} from '../../types/histories';
|
||||||
import getTankStats from '../blitz/getTankStats.js';
|
import getTankStats from '../blitz/getTankStats';
|
||||||
import { emptyAllStats } from './getTankStatsDiffed.js';
|
import { emptyAllStats } from './getDiffedTankStats';
|
||||||
|
|
||||||
export interface TankHistory extends History {
|
export interface TankHistory extends History {
|
||||||
tank_id: number;
|
tank_id: number;
|
||||||
@@ -32,7 +32,7 @@ export const getTankHistoriesDefaultOptions: GetTankHistoriesOptions = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default async function getTankHistories(
|
export default async function getTankHistories(
|
||||||
server: BlitzServer,
|
server: RegionDomain,
|
||||||
id: number,
|
id: number,
|
||||||
options?: Partial<GetTankHistoriesOptions>,
|
options?: Partial<GetTankHistoriesOptions>,
|
||||||
) {
|
) {
|
||||||
@@ -48,7 +48,7 @@ export default async function getTankHistories(
|
|||||||
all: history.all,
|
all: history.all,
|
||||||
last_battle_time: history.last_battle_time,
|
last_battle_time: history.last_battle_time,
|
||||||
tank_id: history.tank_id,
|
tank_id: history.tank_id,
|
||||||
} satisfies TankHistory),
|
}) satisfies TankHistory,
|
||||||
);
|
);
|
||||||
const compliantTanks: number[] = [];
|
const compliantTanks: number[] = [];
|
||||||
const lastBattles: Record<number, number> = {};
|
const lastBattles: Record<number, number> = {};
|
||||||
|
|||||||
@@ -1,12 +1,20 @@
|
|||||||
export default function getTimeDaysAgo(daysAgo: number) {
|
import { RegionDomain } from '../../constants/regions';
|
||||||
|
|
||||||
|
export const TIME_ZONE_MAPPINGS: Record<RegionDomain, number> = {
|
||||||
|
com: -5, // Central North American Time
|
||||||
|
eu: +1, // Central European Time
|
||||||
|
asia: +7, // Central Asia Standard Time
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function getTimeDaysAgo(server: RegionDomain, daysAgo: number) {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
now.setHours(5, 0, 0, 0);
|
if (daysAgo === 0) now.getTime() / 1000;
|
||||||
|
|
||||||
// if it's after 5:00 AM today, subtract one day
|
const time = new Date();
|
||||||
if (now.getTime() > Date.now()) now.setDate(now.getDate() - 1);
|
|
||||||
|
|
||||||
// subtract the period number of days from now
|
time.setUTCHours(-TIME_ZONE_MAPPINGS[server], 0, 0, 0);
|
||||||
now.setDate(now.getDate() - daysAgo);
|
if (time > now) time.setUTCDate(time.getUTCDate() - 1);
|
||||||
|
time.setUTCDate(time.getUTCDate() - daysAgo + 1);
|
||||||
|
|
||||||
return now.getTime() / 1000;
|
return time.getTime() / 1000;
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import sumStats from '../blitz/sumStats.js';
|
import sumStats from '../blitz/sumStats';
|
||||||
import { emptyAllStats } from './getTankStatsDiffed.js';
|
import { emptyAllStats } from './getDiffedTankStats';
|
||||||
import { tankAverages } from './tankAverages.js';
|
import { tankAverages } from './tankAverages';
|
||||||
|
|
||||||
console.log('Calculating player average...');
|
console.log('Calculating player average...');
|
||||||
export const playerAverage = Object.entries(await tankAverages).reduce(
|
export const playerAverage = Object.entries(await tankAverages).reduce(
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { AllStats } from '../../types/accountInfo.js';
|
import { AllStats } from '../../types/accountInfo';
|
||||||
|
|
||||||
export interface SpecialStats {
|
export interface SpecialStats {
|
||||||
winrate: number;
|
winrate: number;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import {
|
|||||||
SlashCommandSubcommandBuilder,
|
SlashCommandSubcommandBuilder,
|
||||||
SlashCommandSubcommandGroupBuilder,
|
SlashCommandSubcommandGroupBuilder,
|
||||||
} from 'discord.js';
|
} from 'discord.js';
|
||||||
import { PERIOD_NAMES } from '../discord/resolvePeriodFromCommand.js';
|
import { PERIOD_NAMES } from '../discord/resolvePeriodFromCommand';
|
||||||
|
|
||||||
export type Period = 'today' | '30' | '60' | '90' | 'career' | 'custom';
|
export type Period = 'today' | '30' | '60' | '90' | 'career' | 'custom';
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { SlashCommandStringOption } from 'discord.js';
|
import { SlashCommandStringOption } from 'discord.js';
|
||||||
import { BLITZ_SERVERS } from '../../constants/servers.js';
|
import { REGION_DOMAIN_NAMES } from '../../constants/regions';
|
||||||
|
|
||||||
export default function addServerChoices(option: SlashCommandStringOption) {
|
export default function addServerChoices(option: SlashCommandStringOption) {
|
||||||
return option
|
return option
|
||||||
.setName('server')
|
.setName('server')
|
||||||
.setDescription('The Blitz server you are in')
|
.setDescription('The Blitz server you are in')
|
||||||
.addChoices(
|
.addChoices(
|
||||||
{ name: BLITZ_SERVERS.com, value: 'com' },
|
{ name: REGION_DOMAIN_NAMES.com, value: 'com' },
|
||||||
{ name: BLITZ_SERVERS.eu, value: 'eu' },
|
{ name: REGION_DOMAIN_NAMES.eu, value: 'eu' },
|
||||||
{ name: BLITZ_SERVERS.asia, value: 'asia' },
|
{ name: REGION_DOMAIN_NAMES.asia, value: 'asia' },
|
||||||
)
|
)
|
||||||
.setRequired(true);
|
.setRequired(true);
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { SlashCommandBuilder } from 'discord.js';
|
import { SlashCommandBuilder } from 'discord.js';
|
||||||
import addPeriodSubCommands from './addPeriodSubCommands.js';
|
import addPeriodSubCommands from './addPeriodSubCommands';
|
||||||
import addTankChoices from './addTankChoices.js';
|
import addTankChoices from './addTankChoices';
|
||||||
import addUsernameChoices from './addUsernameChoices.js';
|
import addUsernameChoices from './addUsernameChoices';
|
||||||
|
|
||||||
export default function addStatTypeSubCommandGroups(
|
export default function addStatTypeSubCommandGroups(
|
||||||
option: SlashCommandBuilder,
|
option: SlashCommandBuilder,
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import {
|
|||||||
AutocompleteInteraction,
|
AutocompleteInteraction,
|
||||||
CacheType,
|
CacheType,
|
||||||
} from 'discord.js';
|
} from 'discord.js';
|
||||||
import { BLITZ_SERVERS } from '../../constants/servers.js';
|
import { REGION_DOMAIN_NAMES } from '../../constants/regions';
|
||||||
import listClans from '../blitz/listClans.js';
|
import listClans from '../blitz/listClans';
|
||||||
|
|
||||||
export default async function autocompleteClan(
|
export default async function autocompleteClan(
|
||||||
interaction: AutocompleteInteraction<CacheType>,
|
interaction: AutocompleteInteraction<CacheType>,
|
||||||
@@ -18,9 +18,9 @@ export default async function autocompleteClan(
|
|||||||
? players.map(
|
? players.map(
|
||||||
(player) =>
|
(player) =>
|
||||||
({
|
({
|
||||||
name: `${player.name} (${BLITZ_SERVERS[player.server]})`,
|
name: `${player.name} (${REGION_DOMAIN_NAMES[player.server]})`,
|
||||||
value: `${player.server}/${player.clan_id}`,
|
value: `${player.server}/${player.clan_id}`,
|
||||||
} satisfies ApplicationCommandOptionChoiceData<string>),
|
}) satisfies ApplicationCommandOptionChoiceData<string>,
|
||||||
)
|
)
|
||||||
: [],
|
: [],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import {
|
|||||||
CacheType,
|
CacheType,
|
||||||
} from 'discord.js';
|
} from 'discord.js';
|
||||||
import { go } from 'fuzzysort';
|
import { go } from 'fuzzysort';
|
||||||
import resolveTankName from '../blitz/resolveTankName.js';
|
import resolveTankName from '../blitz/resolveTankName';
|
||||||
import { TANKS } from '../blitz/tankopedia.js';
|
import { TANKS } from '../blitz/tankopedia';
|
||||||
|
|
||||||
export default async function autocompleteTanks(
|
export default async function autocompleteTanks(
|
||||||
interaction: AutocompleteInteraction<CacheType>,
|
interaction: AutocompleteInteraction<CacheType>,
|
||||||
@@ -16,14 +16,17 @@ export default async function autocompleteTanks(
|
|||||||
await interaction.respond(
|
await interaction.respond(
|
||||||
focusedOption.value
|
focusedOption.value
|
||||||
? await Promise.all(
|
? await Promise.all(
|
||||||
go(focusedOption.value, TANKS, { keys: ['name'], limit: 10 }).map(
|
go(focusedOption.value, await TANKS, {
|
||||||
|
keys: ['name'],
|
||||||
|
limit: 10,
|
||||||
|
}).map(
|
||||||
async (item) =>
|
async (item) =>
|
||||||
({
|
({
|
||||||
name: item.obj.name
|
name: item.obj.name
|
||||||
? item.obj.name
|
? item.obj.name
|
||||||
: await resolveTankName(item.obj.tank_id),
|
: await resolveTankName(item.obj.tank_id),
|
||||||
value: `${item.obj.tank_id}`,
|
value: `${item.obj.tank_id}`,
|
||||||
} satisfies ApplicationCommandOptionChoiceData<string>),
|
}) satisfies ApplicationCommandOptionChoiceData<string>,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: [],
|
: [],
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import {
|
|||||||
AutocompleteInteraction,
|
AutocompleteInteraction,
|
||||||
CacheType,
|
CacheType,
|
||||||
} from 'discord.js';
|
} from 'discord.js';
|
||||||
import { BLITZ_SERVERS } from '../../constants/servers.js';
|
import { REGION_DOMAIN_NAMES } from '../../constants/regions';
|
||||||
import listPlayers from '../blitz/listPlayers.js';
|
import listPlayers from '../blitz/listPlayers';
|
||||||
|
|
||||||
export default async function autocompleteUsername(
|
export default async function autocompleteUsername(
|
||||||
interaction: AutocompleteInteraction<CacheType>,
|
interaction: AutocompleteInteraction<CacheType>,
|
||||||
@@ -19,9 +19,11 @@ export default async function autocompleteUsername(
|
|||||||
? players.map(
|
? players.map(
|
||||||
(player) =>
|
(player) =>
|
||||||
({
|
({
|
||||||
name: `${player.nickname} (${BLITZ_SERVERS[player.server]})`,
|
name: `${player.nickname} (${
|
||||||
|
REGION_DOMAIN_NAMES[player.server]
|
||||||
|
})`,
|
||||||
value: `${player.server}/${player.account_id}`,
|
value: `${player.server}/${player.account_id}`,
|
||||||
} satisfies ApplicationCommandOptionChoiceData<string>),
|
}) satisfies ApplicationCommandOptionChoiceData<string>,
|
||||||
)
|
)
|
||||||
: [],
|
: [],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import { EmbedBuilder } from 'discord.js';
|
import { EmbedBuilder } from 'discord.js';
|
||||||
import { ACCENT_COLOR } from '../../constants/colors.js';
|
import { ACCENT_COLOR } from '../../constants/colors';
|
||||||
|
|
||||||
export default function embedInfo(title: string, description: string) {
|
export default function embedInfo(title: string, description: string) {
|
||||||
return new EmbedBuilder()
|
const embed = new EmbedBuilder()
|
||||||
.setColor(ACCENT_COLOR)
|
.setColor(ACCENT_COLOR)
|
||||||
.setTitle(title)
|
|
||||||
.setDescription(description);
|
.setDescription(description);
|
||||||
|
|
||||||
|
if (title) embed.setTitle(title);
|
||||||
|
|
||||||
|
return embed;
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { EmbedBuilder } from 'discord.js';
|
import { EmbedBuilder } from 'discord.js';
|
||||||
import { NEGATIVE_COLOR } from '../../constants/colors.js';
|
import { NEGATIVE_COLOR } from '../../constants/colors';
|
||||||
|
|
||||||
export default function embedNegative(title: string, description: string) {
|
export default function embedNegative(title: string, description: string) {
|
||||||
return new EmbedBuilder()
|
return new EmbedBuilder()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { EmbedBuilder } from 'discord.js';
|
import { EmbedBuilder } from 'discord.js';
|
||||||
import { POSITIVE_COLOR } from '../../constants/colors.js';
|
import { POSITIVE_COLOR } from '../../constants/colors';
|
||||||
|
|
||||||
export default function embedPositive(title: string, description: string) {
|
export default function embedPositive(title: string, description: string) {
|
||||||
return new EmbedBuilder()
|
return new EmbedBuilder()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { EmbedBuilder } from 'discord.js';
|
import { EmbedBuilder } from 'discord.js';
|
||||||
import { WARNING_COLOR } from '../../constants/colors.js';
|
import { WARNING_COLOR } from '../../constants/colors';
|
||||||
|
|
||||||
export default function embedWarning(title: string, description: string) {
|
export default function embedWarning(title: string, description: string) {
|
||||||
return new EmbedBuilder()
|
return new EmbedBuilder()
|
||||||
|
|||||||
Loaded 100 of 134 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user