diff --git a/.gitignore b/.gitignore index 2d75ee75a..bf0f85c9c 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ dist.zip commands.txt /test.* /docs/.vitepress/dist -/docs/.vitepress/cache \ No newline at end of file +/docs/.vitepress/cache +servers.csv \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 86108f64d..847ba49b0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,6 +7,7 @@ "cleandist", "customperiod", "Everytime", + "fullstats", "fuzzysort", "kranvagn", "outfile", diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 5bb61952c..c8195926a 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -17,23 +17,23 @@ diverse, inclusive, and healthy community. Examples of behavior that contributes to a positive environment for our community include: -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, 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 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 -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email 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 ## Enforcement Responsibilities @@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an +standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts index bdd506d91..107d0ae71 100644 --- a/docs/.vitepress/theme/index.ts +++ b/docs/.vitepress/theme/index.ts @@ -1,16 +1,16 @@ // https://vitepress.dev/guide/custom-theme -import { h } from 'vue' -import Theme from 'vitepress/theme' -import './style.css' +import { h } from 'vue'; +import Theme from 'vitepress/theme'; +import './style.css'; export default { extends: Theme, Layout: () => { return h(Theme.Layout, null, { // https://vitepress.dev/guide/extending-default-theme#layout-slots - }) + }); }, enhanceApp({ app, router, siteData }) { // ... - } -} + }, +}; diff --git a/docs/.vitepress/theme/style.css b/docs/.vitepress/theme/style.css index c23c17ac7..45d673a9e 100644 --- a/docs/.vitepress/theme/style.css +++ b/docs/.vitepress/theme/style.css @@ -7,7 +7,7 @@ * Colors * -------------------------------------------------------------------------- */ - :root { +:root { --vp-c-brand: #646cff; --vp-c-brand-light: #747bff; --vp-c-brand-lighter: #9499ff; @@ -88,4 +88,3 @@ .DocSearch { --docsearch-primary-color: var(--vp-c-brand) !important; } - diff --git a/docs/changelogs/1.10.0.md b/docs/changelogs/1.10.0.md new file mode 100644 index 000000000..d6e164470 --- /dev/null +++ b/docs/changelogs/1.10.0.md @@ -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 diff --git a/docs/changelogs/1.9.5.md b/docs/changelogs/1.9.5.md index 72913004c..f42e89242 100644 --- a/docs/changelogs/1.9.5.md +++ b/docs/changelogs/1.9.5.md @@ -1,6 +1,6 @@ # Blitzkrieg 1.9.5 -Hopefully this is the last round of fixes for Blitzkrieg `1.9``. +Hopefully this is the last round of fixes for Blitzkrieg `1.9`. ## Fixes diff --git a/docs/guide/about.md b/docs/guide/about.md new file mode 100644 index 000000000..40c3b25c3 --- /dev/null +++ b/docs/guide/about.md @@ -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 diff --git a/docs/guide/embeds.md b/docs/guide/embeds.md new file mode 100644 index 000000000..b663d0bfc --- /dev/null +++ b/docs/guide/embeds.md @@ -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. diff --git a/docs/guide/invite.md b/docs/guide/invite.md new file mode 100644 index 000000000..307d0fa89 --- /dev/null +++ b/docs/guide/invite.md @@ -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. diff --git a/docs/guide/timezones.md b/docs/guide/timezones.md new file mode 100644 index 000000000..631450aaf --- /dev/null +++ b/docs/guide/timezones.md @@ -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. diff --git a/package.json b/package.json index 324cdd060..296a85345 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "blitzkrieg", - "version": "1.9.4", + "version": "1.10.0", "description": "", "main": "src/server.ts", "type": "module", @@ -16,7 +16,8 @@ "start:server": "node --enable-source-maps --trace-warnings dist/server.cjs", "host:docs": "vitepress preview docs", "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", "dependencies": { @@ -42,6 +43,7 @@ "@types/markdown-escape": "^1.1.0", "@types/node": "^20.2.1", "@types/react": "^18.2.7", + "prettier": "^3.0.0", "typescript": "^5.0.4", "vitepress": "^1.0.0-beta.3" } diff --git a/src/bot.ts b/src/bot.ts index 8d66081c8..8297d739e 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -1,9 +1,9 @@ import { Client, GatewayIntentBits } from 'discord.js'; -import { DISCORD_TOKEN } from './core/node/arguments.js'; -import { registerErrorHandlers } from './events/error.js'; -import guildMemberAdd from './events/guildMemberAdd.js'; -import interactionCreate from './events/interactionCreate/index.js'; -import ready from './events/ready.js'; +import { DISCORD_TOKEN } from './core/node/arguments'; +import { registerErrorHandlers } from './events/error'; +import guildMemberAdd from './events/guildMemberAdd'; +import interactionCreate from './events/interactionCreate'; +import ready from './events/ready'; export const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers], diff --git a/src/commands/debug.ts b/src/commands/debug.ts index 77af7954d..76bd7c807 100644 --- a/src/commands/debug.ts +++ b/src/commands/debug.ts @@ -1,18 +1,18 @@ import { SlashCommandBuilder } from 'discord.js'; import packageJSON from '../../package.json' assert { type: 'json' }; -import { client } from '../bot.js'; -import { tankopedia } from '../core/blitz/tankopedia.js'; -import { tankAverages } from '../core/blitzstars/tankAverages.js'; -import embedInfo from '../core/discord/embedInfo.js'; -import markdownTable from '../core/discord/markdownTable.js'; -import getClientId from '../core/node/getClientId.js'; -import { CommandRegistry } from '../events/interactionCreate/index.js'; +import { client } from '../bot'; +import { tankopedia } from '../core/blitz/tankopedia'; +import { tankAverages } from '../core/blitzstars/tankAverages'; +import embedInfo from '../core/discord/embedInfo'; +import markdownTable from '../core/discord/markdownTable'; +import getClientId from '../core/node/getClientId'; +import { CommandRegistry } from '../events/interactionCreate'; const executionStart = new Date().getTime(); export const debugCommand: CommandRegistry = { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/eligible.ts b/src/commands/eligible.ts index dca377759..43c6991d6 100644 --- a/src/commands/eligible.ts +++ b/src/commands/eligible.ts @@ -1,21 +1,21 @@ import { SlashCommandBuilder } from 'discord.js'; -import calculateWN8 from '../core/blitz/calculateWN8.js'; -import getWargamingResponse from '../core/blitz/getWargamingResponse.js'; -import sumStats from '../core/blitz/sumStats.js'; -import { tankopedia } from '../core/blitz/tankopedia.js'; -import getPeriodNow from '../core/blitzstars/getPeriodNow.js'; -import getPeriodStart from '../core/blitzstars/getPeriodStart.js'; -import getTankStatsDiffed from '../core/blitzstars/getTankStatsDiffed.js'; -import { tankAverages } from '../core/blitzstars/tankAverages.js'; -import addUsernameChoices from '../core/discord/addUsernameChoices.js'; -import embedNegative from '../core/discord/embedNegative.js'; -import embedPositive from '../core/discord/embedPositive.js'; -import markdownTable, { TableInput } from '../core/discord/markdownTable.js'; -import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand.js'; -import { WARGAMING_APPLICATION_ID } from '../core/node/arguments.js'; -import { CommandRegistry } from '../events/interactionCreate/index.js'; -import { AccountInfo } from '../types/accountInfo.js'; -import { PossiblyPromise } from '../types/possiblyPromise.js'; +import calculateWN8 from '../core/blitz/calculateWN8'; +import getWargamingResponse from '../core/blitz/getWargamingResponse'; +import sumStats from '../core/blitz/sumStats'; +import { tankopedia } from '../core/blitz/tankopedia'; +import getDiffedTankStats from '../core/blitzstars/getDiffedTankStats'; +import getPeriodNow from '../core/blitzstars/getPeriodNow'; +import getTimeDaysAgo from '../core/blitzstars/getTimeDaysAgo'; +import { tankAverages } from '../core/blitzstars/tankAverages'; +import addUsernameChoices from '../core/discord/addUsernameChoices'; +import embedNegative from '../core/discord/embedNegative'; +import embedPositive from '../core/discord/embedPositive'; +import markdownTable, { TableInput } from '../core/discord/markdownTable'; +import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand'; +import { WARGAMING_APPLICATION_ID } from '../core/node/arguments'; +import { CommandRegistry } from '../events/interactionCreate'; +import { AccountInfo } from '../types/accountInfo'; +import { PossiblyPromise } from '../types/possiblyPromise'; export type SkilledClan = 'SKLLD' | 'SMRI'; @@ -26,7 +26,7 @@ export const SKILLED_CLANS: Record = { export const eligibleCommand: CommandRegistry = { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: false, command: new SlashCommandBuilder() @@ -59,13 +59,13 @@ export const eligibleCommand: CommandRegistry = { let body = ''; if (clan === 'SKLLD') { - const tankStatsOverTime = await getTankStatsDiffed( + const { diffed } = await getDiffedTankStats( server, id, - getPeriodStart('30'), + getTimeDaysAgo(server, 30), getPeriodNow(), ); - const entries = Object.entries(tankStatsOverTime); + const entries = Object.entries(diffed); const stats = sumStats(entries.map(([, stats]) => stats)); const averageTier = (await entries.reduce>( @@ -116,7 +116,7 @@ export const eligibleCommand: CommandRegistry = { const tankId = parseInt(tankIdString); if ((await tankopedia)[tankId]?.tier === 10) { - const tankStats = tankStatsOverTime[tankId]; + const tankStats = diffed[tankId]; return (await accumulator) + tankStats.damage_dealt; } else return accumulator; }, @@ -127,7 +127,7 @@ export const eligibleCommand: CommandRegistry = { const tankId = parseInt(tankIdString); if ((await tankopedia)[tankId]?.tier === 10) { - const tankStats = tankStatsOverTime[tankId]; + const tankStats = diffed[tankId]; return (await accumulator) + tankStats.battles; } else return accumulator; }, diff --git a/src/commands/evolution.tsx b/src/commands/evolution.tsx index 3187aab66..e3b62fbe4 100644 --- a/src/commands/evolution.tsx +++ b/src/commands/evolution.tsx @@ -1,26 +1,26 @@ import { SlashCommandBuilder } from 'discord.js'; -import { CYCLIC_API } from '../constants/cyclic.js'; -import getWargamingResponse from '../core/blitz/getWargamingResponse.js'; -import resolveTankId from '../core/blitz/resolveTankId.js'; -import addStatTypeSubCommandGroups from '../core/discord/addStatTypeSubCommandGroups.js'; -import autocompleteTanks from '../core/discord/autocompleteTanks.js'; -import autocompleteUsername from '../core/discord/autocompleteUsername.js'; -import interactionToURL from '../core/discord/interactionToURL.js'; -import linkButton from '../core/discord/linkButton.js'; -import primaryButton from '../core/discord/primaryButton.js'; -import resolvePeriodFromButton from '../core/discord/resolvePeriodFromButton.js'; -import resolvePeriodFromCommand from '../core/discord/resolvePeriodFromCommand.js'; -import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton.js'; -import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand.js'; -import { WARGAMING_APPLICATION_ID } from '../core/node/arguments.js'; -import { CommandRegistry } from '../events/interactionCreate/index.js'; -import evolution from '../renderers/evolution.js'; -import { StatType } from '../renderers/statsfull.js'; -import { AccountInfo } from '../types/accountInfo.js'; +import { CYCLIC_API } from '../constants/cyclic'; +import getWargamingResponse from '../core/blitz/getWargamingResponse'; +import resolveTankId from '../core/blitz/resolveTankId'; +import addStatTypeSubCommandGroups from '../core/discord/addStatTypeSubCommandGroups'; +import autocompleteTanks from '../core/discord/autocompleteTanks'; +import autocompleteUsername from '../core/discord/autocompleteUsername'; +import interactionToURL from '../core/discord/interactionToURL'; +import linkButton from '../core/discord/linkButton'; +import primaryButton from '../core/discord/primaryButton'; +import resolvePeriodFromButton from '../core/discord/resolvePeriodFromButton'; +import resolvePeriodFromCommand from '../core/discord/resolvePeriodFromCommand'; +import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton'; +import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand'; +import { WARGAMING_APPLICATION_ID } from '../core/node/arguments'; +import { CommandRegistry } from '../events/interactionCreate'; +import evolution from '../renderers/evolution'; +import { StatType } from '../renderers/fullStats'; +import { AccountInfo } from '../types/accountInfo'; export const evolutionCommand: CommandRegistry = { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: addStatTypeSubCommandGroups( @@ -34,9 +34,10 @@ export const evolutionCommand: CommandRegistry = { true, ) as StatType; const player = await resolvePlayerFromCommand(interaction); - const period = resolvePeriodFromCommand(interaction); + const period = resolvePeriodFromCommand(player.server, interaction); 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 end = interaction.options.getInteger('end'); const { nickname } = ( @@ -73,8 +74,8 @@ export const evolutionCommand: CommandRegistry = { const url = new URL(`${CYCLIC_API}/${interaction.customId}`); const path = url.pathname.split('/').filter(Boolean); const commandGroup = path[1] as StatType; - const period = resolvePeriodFromButton(interaction); const player = await resolvePlayerFromButton(interaction); + const period = resolvePeriodFromButton(player.server, interaction); return await evolution( commandGroup, diff --git a/src/commands/statsFull.tsx b/src/commands/fullStats.tsx similarity index 71% rename from src/commands/statsFull.tsx rename to src/commands/fullStats.tsx index 180cc59f6..adbd15159 100644 --- a/src/commands/statsFull.tsx +++ b/src/commands/fullStats.tsx @@ -1,30 +1,30 @@ import { SlashCommandBuilder } from 'discord.js'; -import { CYCLIC_API } from '../constants/cyclic.js'; -import getWargamingResponse from '../core/blitz/getWargamingResponse.js'; -import resolveTankId from '../core/blitz/resolveTankId.js'; -import addStatTypeSubCommandGroups from '../core/discord/addStatTypeSubCommandGroups.js'; -import autocompleteTanks from '../core/discord/autocompleteTanks.js'; -import autocompleteUsername from '../core/discord/autocompleteUsername.js'; -import interactionToURL from '../core/discord/interactionToURL.js'; -import linkButton from '../core/discord/linkButton.js'; -import primaryButton from '../core/discord/primaryButton.js'; -import resolvePeriodFromButton from '../core/discord/resolvePeriodFromButton.js'; -import resolvePeriodFromCommand from '../core/discord/resolvePeriodFromCommand.js'; -import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton.js'; -import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand.js'; -import { WARGAMING_APPLICATION_ID } from '../core/node/arguments.js'; -import { CommandRegistry } from '../events/interactionCreate/index.js'; -import statsfull, { StatType } from '../renderers/statsfull.js'; -import { AccountInfo } from '../types/accountInfo.js'; +import { CYCLIC_API } from '../constants/cyclic'; +import getWargamingResponse from '../core/blitz/getWargamingResponse'; +import resolveTankId from '../core/blitz/resolveTankId'; +import addStatTypeSubCommandGroups from '../core/discord/addStatTypeSubCommandGroups'; +import autocompleteTanks from '../core/discord/autocompleteTanks'; +import autocompleteUsername from '../core/discord/autocompleteUsername'; +import interactionToURL from '../core/discord/interactionToURL'; +import linkButton from '../core/discord/linkButton'; +import primaryButton from '../core/discord/primaryButton'; +import resolvePeriodFromButton from '../core/discord/resolvePeriodFromButton'; +import resolvePeriodFromCommand from '../core/discord/resolvePeriodFromCommand'; +import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton'; +import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand'; +import { WARGAMING_APPLICATION_ID } from '../core/node/arguments'; +import { CommandRegistry } from '../events/interactionCreate'; +import fullStats, { StatType } from '../renderers/fullStats'; +import { AccountInfo } from '../types/accountInfo'; -export const statsFullCommand: CommandRegistry = { +export const fullStatsCommand: CommandRegistry = { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: addStatTypeSubCommandGroups( new SlashCommandBuilder() - .setName('statsfull') + .setName('fullstats') .setDescription('Full in-game statistics'), ), @@ -33,7 +33,7 @@ export const statsFullCommand: CommandRegistry = { true, ) as StatType; const player = await resolvePlayerFromCommand(interaction); - const period = resolvePeriodFromCommand(interaction); + const period = resolvePeriodFromCommand(player.server, interaction); const tankIdRaw = interaction.options.getString('tank')!; const tankId = commandGroup === 'tank' ? await resolveTankId(tankIdRaw) : null; @@ -52,7 +52,7 @@ export const statsFullCommand: CommandRegistry = { )[player.id]; return [ - await statsfull(commandGroup, period, player, tankId), + await fullStats(commandGroup, period, player, tankId), primaryButton(path, 'Refresh'), // linkButton(`${CYCLIC_API}/${path}`, 'Embed'), linkButton( @@ -71,10 +71,10 @@ export const statsFullCommand: CommandRegistry = { const url = new URL(`${CYCLIC_API}/${interaction.customId}`); const path = url.pathname.split('/').filter(Boolean); const commandGroup = path[1] as StatType; - const period = resolvePeriodFromButton(interaction); const player = await resolvePlayerFromButton(interaction); + const period = resolvePeriodFromButton(player.server, interaction); - return await statsfull( + return await fullStats( commandGroup, period, player, diff --git a/src/commands/help.ts b/src/commands/help.ts index 164acd3df..2052fd12a 100644 --- a/src/commands/help.ts +++ b/src/commands/help.ts @@ -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 { - CommandRegistry, - guildCommands, - publicCommands, -} from '../events/interactionCreate/index.js'; + SlashCommandBuilder, + SlashCommandSubcommandsOnlyBuilder, +} from 'discord.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 = { + 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 = { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, - command: new SlashCommandBuilder() - .setName('help') - .setDescription('All the help you need about the bot'), + command: addDocsSubcommands( + new SlashCommandBuilder() + .setName('help') + .setDescription('All the help you need about the bot') + .addSubcommand((option) => + option + .setName('commands') + .setDescription('List of all commands and their descriptions'), + ), + ), handler(interaction) { - return embedInfo( - `${client.user?.username} help`, - `**About**\n${ - client.user?.username - } 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 === - discord.sklld_guild_id - ? [...guildCommands, ...publicCommands] - : publicCommands + const subcommand = interaction.options.getSubcommand(); + + if (subcommand === 'commands') { + return `# Commands\n\nBlitzkrieg offers the following commands:\n\n${COMMANDS_RAW.filter( + (registry) => registry.inPublic && registry.inProduction, ) - .map((command) => `**/${command.name}**: ${command.description}`) - .join( - '\n', - )}\n\n*I am a new bot; I may make mistakes and go offline unexpectedly from time to time.*`, - ); + .map( + (registry) => + `- \`/${registry.command.name}\`: ${registry.command.description}`, + ) + .join('\n')}`; + } + + const url = `${RAW_PATH}${DOCS[subcommand as keyof typeof DOCS]}.md`; + const content = fetch(url).then((response) => response.text()); + + return content; }, }; diff --git a/src/commands/inactive.tsx b/src/commands/inactive.tsx index 0dc891f13..4b904833d 100644 --- a/src/commands/inactive.tsx +++ b/src/commands/inactive.tsx @@ -1,24 +1,24 @@ import { SlashCommandBuilder } from 'discord.js'; -import GenericStats from '../components/GenericStats/index.js'; -import NoData, { NoDataType } from '../components/NoData.js'; -import PoweredBy, { PoweredByType } from '../components/PoweredBy.js'; -import TitleBar from '../components/TitleBar.js'; -import Wrapper from '../components/Wrapper.js'; -import { BLITZ_SERVERS } from '../constants/servers.js'; -import getBlitzClan from '../core/blitz/getBlitzClan.js'; -import getWargamingResponse from '../core/blitz/getWargamingResponse.js'; -import addClanChoices from '../core/discord/addClanChoices.js'; -import autocompleteClan from '../core/discord/autocompleteClan.js'; -import { WARGAMING_APPLICATION_ID } from '../core/node/arguments.js'; -import { CommandRegistry } from '../events/interactionCreate/index.js'; -import { AccountInfo } from '../types/accountInfo.js'; -import { ClanInfo } from '../types/clanInfo.js'; +import GenericStats from '../components/GenericStats'; +import NoData, { NoDataType } from '../components/NoData'; +import PoweredBy, { PoweredByType } from '../components/PoweredBy'; +import TitleBar from '../components/TitleBar'; +import Wrapper from '../components/Wrapper'; +import { BLITZ_SERVERS } from '../constants/servers'; +import getBlitzClan from '../core/blitz/getBlitzClan'; +import getWargamingResponse from '../core/blitz/getWargamingResponse'; +import addClanChoices from '../core/discord/addClanChoices'; +import autocompleteClan from '../core/discord/autocompleteClan'; +import { WARGAMING_APPLICATION_ID } from '../core/node/arguments'; +import { CommandRegistry } from '../events/interactionCreate'; +import { AccountInfo } from '../types/accountInfo'; +import { ClanInfo } from '../types/clanInfo'; const DEFAULT_THRESHOLD = 7; export const inactiveCommand: CommandRegistry = { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/ownedTanks.tsx b/src/commands/ownedTanks.tsx index 3688ed767..b43aafd68 100644 --- a/src/commands/ownedTanks.tsx +++ b/src/commands/ownedTanks.tsx @@ -1,25 +1,26 @@ import { SlashCommandBuilder } from 'discord.js'; -import NoData, { NoDataType } from '../components/NoData.js'; -import PoweredBy, { PoweredByType } from '../components/PoweredBy.js'; -import * as Tanks from '../components/Tanks/index.js'; -import TitleBar from '../components/TitleBar.js'; -import Wrapper from '../components/Wrapper.js'; -import { BLITZ_SERVERS } from '../constants/servers.js'; -import getTankStats from '../core/blitz/getTankStats.js'; -import getWargamingResponse from '../core/blitz/getWargamingResponse.js'; -import resolveTankName from '../core/blitz/resolveTankName.js'; +import NoData, { NoDataType } from '../components/NoData'; +import PoweredBy, { PoweredByType } from '../components/PoweredBy'; +import * as Tanks from '../components/Tanks'; +import TitleBar from '../components/TitleBar'; +import Wrapper from '../components/Wrapper'; +import { BLITZ_SERVERS } from '../constants/servers'; +import getTankStats from '../core/blitz/getTankStats'; +import getTreeType from '../core/blitz/getTreeType'; +import getWargamingResponse from '../core/blitz/getWargamingResponse'; +import resolveTankName from '../core/blitz/resolveTankName'; import { TIER_ROMAN_NUMERALS, Tier, tankopedia, -} from '../core/blitz/tankopedia.js'; -import addUsernameChoices from '../core/discord/addUsernameChoices.js'; -import autocompleteUsername from '../core/discord/autocompleteUsername.js'; -import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand.js'; -import { WARGAMING_APPLICATION_ID } from '../core/node/arguments.js'; -import { CommandRegistry } from '../events/interactionCreate/index.js'; -import { AccountInfo } from '../types/accountInfo.js'; -import { PlayerClanData } from '../types/playerClanData.js'; +} from '../core/blitz/tankopedia'; +import addUsernameChoices from '../core/discord/addUsernameChoices'; +import autocompleteUsername from '../core/discord/autocompleteUsername'; +import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand'; +import { WARGAMING_APPLICATION_ID } from '../core/node/arguments'; +import { CommandRegistry } from '../events/interactionCreate'; +import { AccountInfo } from '../types/accountInfo'; +import { PlayerClanData } from '../types/playerClanData'; const COMP_TANKS = [ // light tanks @@ -44,7 +45,7 @@ const COMP_TANKS = [ export const ownedTanksCommand: CommandRegistry = { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() @@ -89,10 +90,12 @@ export const ownedTanksCommand: CommandRegistry = { const clanData = await getWargamingResponse( `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 ( - {/* TODO: integrate some of these into title bar */} 0 && ( - {await Promise.all( - tanks.map(async (tank) => ( - - )), - )} + + {await Promise.all( + leftColumn.map(async (tank) => ( + + )), + )} + + + {await Promise.all( + rightColumn.map(async (tank) => ( + + )), + )} + )} diff --git a/src/commands/ping.ts b/src/commands/ping.ts index bcaa52371..2005400f0 100644 --- a/src/commands/ping.ts +++ b/src/commands/ping.ts @@ -1,9 +1,9 @@ import { SlashCommandBuilder } from 'discord.js'; -import { CommandRegistry } from '../events/interactionCreate/index.js'; +import { CommandRegistry } from '../events/interactionCreate'; export const pingCommand: CommandRegistry = { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, handlesInteraction: true, diff --git a/src/commands/playerAchievements.ts b/src/commands/playerAchievements.ts index fcd9c9a75..3301e5542 100644 --- a/src/commands/playerAchievements.ts +++ b/src/commands/playerAchievements.ts @@ -1,24 +1,22 @@ import { SlashCommandBuilder } from 'discord.js'; import { startCase } from 'lodash'; import markdownEscape from 'markdown-escape'; -import getWargamingResponse from '../core/blitz/getWargamingResponse.js'; -import addUsernameChoices from '../core/discord/addUsernameChoices.js'; -import autocompleteUsername from '../core/discord/autocompleteUsername.js'; -import embedInfo from '../core/discord/embedInfo.js'; -import markdownTable, { - TableInputEntry, -} from '../core/discord/markdownTable.js'; -import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand.js'; -import { WARGAMING_APPLICATION_ID } from '../core/node/arguments.js'; -import { CommandRegistry } from '../events/interactionCreate/index.js'; -import { AccountAchievements } from '../types/accountAchievements.js'; -import { AccountInfo } from '../types/accountInfo.js'; +import getWargamingResponse from '../core/blitz/getWargamingResponse'; +import addUsernameChoices from '../core/discord/addUsernameChoices'; +import autocompleteUsername from '../core/discord/autocompleteUsername'; +import embedInfo from '../core/discord/embedInfo'; +import markdownTable, { TableInputEntry } from '../core/discord/markdownTable'; +import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand'; +import { WARGAMING_APPLICATION_ID } from '../core/node/arguments'; +import { CommandRegistry } from '../events/interactionCreate'; +import { AccountAchievements } from '../types/accountAchievements'; +import { AccountInfo } from '../types/accountInfo'; type SortBy = 'name' | 'count'; export const playerAchievementsCommand: CommandRegistry = { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/playerInfo.ts b/src/commands/playerInfo.ts index 67ddcf1fa..d6661373f 100644 --- a/src/commands/playerInfo.ts +++ b/src/commands/playerInfo.ts @@ -1,18 +1,18 @@ import { SlashCommandBuilder } from 'discord.js'; import markdownEscape from 'markdown-escape'; -import getWargamingResponse from '../core/blitz/getWargamingResponse.js'; -import addUsernameChoices from '../core/discord/addUsernameChoices.js'; -import autocompleteUsername from '../core/discord/autocompleteUsername.js'; -import embedInfo from '../core/discord/embedInfo.js'; -import markdownTable from '../core/discord/markdownTable.js'; -import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand.js'; -import { WARGAMING_APPLICATION_ID } from '../core/node/arguments.js'; -import { CommandRegistry } from '../events/interactionCreate/index.js'; -import { AccountInfo } from '../types/accountInfo.js'; +import getWargamingResponse from '../core/blitz/getWargamingResponse'; +import addUsernameChoices from '../core/discord/addUsernameChoices'; +import autocompleteUsername from '../core/discord/autocompleteUsername'; +import embedInfo from '../core/discord/embedInfo'; +import markdownTable from '../core/discord/markdownTable'; +import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand'; +import { WARGAMING_APPLICATION_ID } from '../core/node/arguments'; +import { CommandRegistry } from '../events/interactionCreate'; +import { AccountInfo } from '../types/accountInfo'; export const playerInfoCommand: CommandRegistry = { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/searchClans.ts b/src/commands/searchClans.ts index 18bdc8f00..6b0e9581c 100644 --- a/src/commands/searchClans.ts +++ b/src/commands/searchClans.ts @@ -1,17 +1,17 @@ import { SlashCommandBuilder } from 'discord.js'; import markdownEscape from 'markdown-escape'; -import { BLITZ_SERVERS, BlitzServer } from '../constants/servers.js'; -import getWargamingResponse from '../core/blitz/getWargamingResponse.js'; -import addClanChoices from '../core/discord/addClanChoices.js'; -import addServerChoices from '../core/discord/addServerChoices.js'; -import embedInfo from '../core/discord/embedInfo.js'; -import { WARGAMING_APPLICATION_ID } from '../core/node/arguments.js'; -import { CommandRegistry } from '../events/interactionCreate/index.js'; -import { ClanList } from '../types/clanList.js'; +import { BLITZ_SERVERS, BlitzServer } from '../constants/servers'; +import getWargamingResponse from '../core/blitz/getWargamingResponse'; +import addClanChoices from '../core/discord/addClanChoices'; +import addServerChoices from '../core/discord/addServerChoices'; +import embedInfo from '../core/discord/embedInfo'; +import { WARGAMING_APPLICATION_ID } from '../core/node/arguments'; +import { CommandRegistry } from '../events/interactionCreate'; +import { ClanList } from '../types/clanList'; export const searchClansCommand: CommandRegistry = { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/searchPlayers.ts b/src/commands/searchPlayers.ts index 870342c7a..ad6f0bbc5 100644 --- a/src/commands/searchPlayers.ts +++ b/src/commands/searchPlayers.ts @@ -1,18 +1,18 @@ import { SlashCommandBuilder } from 'discord.js'; import markdownEscape from 'markdown-escape'; -import { BLITZ_SERVERS, BlitzServer } from '../constants/servers.js'; -import getWargamingResponse from '../core/blitz/getWargamingResponse.js'; -import { usernamePattern } from '../core/blitz/listPlayers.js'; -import addServerChoices from '../core/discord/addServerChoices.js'; -import addUsernameChoices from '../core/discord/addUsernameChoices.js'; -import embedInfo from '../core/discord/embedInfo.js'; -import { WARGAMING_APPLICATION_ID } from '../core/node/arguments.js'; -import { CommandRegistry } from '../events/interactionCreate/index.js'; -import { AccountList } from '../types/accountList.js'; +import { BLITZ_SERVERS, BlitzServer } from '../constants/servers'; +import getWargamingResponse from '../core/blitz/getWargamingResponse'; +import { usernamePattern } from '../core/blitz/listPlayers'; +import addServerChoices from '../core/discord/addServerChoices'; +import addUsernameChoices from '../core/discord/addUsernameChoices'; +import embedInfo from '../core/discord/embedInfo'; +import { WARGAMING_APPLICATION_ID } from '../core/node/arguments'; +import { CommandRegistry } from '../events/interactionCreate'; +import { AccountList } from '../types/accountList'; export const searchPlayersCommand: CommandRegistry = { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/searchTanks.ts b/src/commands/searchTanks.ts index 0d58bae86..7f1e87ab3 100644 --- a/src/commands/searchTanks.ts +++ b/src/commands/searchTanks.ts @@ -1,14 +1,14 @@ import { SlashCommandBuilder } from 'discord.js'; import { go } from 'fuzzysort'; import markdownEscape from 'markdown-escape'; -import { TANK_NAMES } from '../core/blitz/tankopedia.js'; -import addTankChoices from '../core/discord/addTankChoices.js'; -import embedInfo from '../core/discord/embedInfo.js'; -import { CommandRegistry } from '../events/interactionCreate/index.js'; +import { TANK_NAMES } from '../core/blitz/tankopedia'; +import addTankChoices from '../core/discord/addTankChoices'; +import embedInfo from '../core/discord/embedInfo'; +import { CommandRegistry } from '../events/interactionCreate'; export const searchTanksCommand: CommandRegistry = { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/stats.tsx b/src/commands/stats.tsx index 9a8e4e59f..4f5f5721f 100644 --- a/src/commands/stats.tsx +++ b/src/commands/stats.tsx @@ -1,26 +1,26 @@ import { SlashCommandBuilder } from 'discord.js'; -import { CYCLIC_API } from '../constants/cyclic.js'; -import getWargamingResponse from '../core/blitz/getWargamingResponse.js'; -import resolveTankId from '../core/blitz/resolveTankId.js'; -import addStatTypeSubCommandGroups from '../core/discord/addStatTypeSubCommandGroups.js'; -import autocompleteTanks from '../core/discord/autocompleteTanks.js'; -import autocompleteUsername from '../core/discord/autocompleteUsername.js'; -import interactionToURL from '../core/discord/interactionToURL.js'; -import linkButton from '../core/discord/linkButton.js'; -import primaryButton from '../core/discord/primaryButton.js'; -import resolvePeriodFromButton from '../core/discord/resolvePeriodFromButton.js'; -import resolvePeriodFromCommand from '../core/discord/resolvePeriodFromCommand.js'; -import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton.js'; -import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand.js'; -import { WARGAMING_APPLICATION_ID } from '../core/node/arguments.js'; -import { CommandRegistry } from '../events/interactionCreate/index.js'; -import stats from '../renderers/stats.js'; -import { StatType } from '../renderers/statsfull.js'; -import { AccountInfo } from '../types/accountInfo.js'; +import { CYCLIC_API } from '../constants/cyclic'; +import getWargamingResponse from '../core/blitz/getWargamingResponse'; +import resolveTankId from '../core/blitz/resolveTankId'; +import addStatTypeSubCommandGroups from '../core/discord/addStatTypeSubCommandGroups'; +import autocompleteTanks from '../core/discord/autocompleteTanks'; +import autocompleteUsername from '../core/discord/autocompleteUsername'; +import interactionToURL from '../core/discord/interactionToURL'; +import linkButton from '../core/discord/linkButton'; +import primaryButton from '../core/discord/primaryButton'; +import resolvePeriodFromButton from '../core/discord/resolvePeriodFromButton'; +import resolvePeriodFromCommand from '../core/discord/resolvePeriodFromCommand'; +import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton'; +import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand'; +import { WARGAMING_APPLICATION_ID } from '../core/node/arguments'; +import { CommandRegistry } from '../events/interactionCreate'; +import { StatType } from '../renderers/fullStats'; +import stats from '../renderers/stats'; +import { AccountInfo } from '../types/accountInfo'; export const statsCommand: CommandRegistry = { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: addStatTypeSubCommandGroups( @@ -34,7 +34,7 @@ export const statsCommand: CommandRegistry = { true, ) as StatType; const player = await resolvePlayerFromCommand(interaction); - const period = resolvePeriodFromCommand(interaction); + const period = resolvePeriodFromCommand(player.server, interaction); const tankIdRaw = interaction.options.getString('tank')!; const tankId = commandGroup === 'tank' ? await resolveTankId(tankIdRaw) : null; @@ -72,8 +72,8 @@ export const statsCommand: CommandRegistry = { const url = new URL(`${CYCLIC_API}/${interaction.customId}`); const path = url.pathname.split('/').filter(Boolean); const commandGroup = path[1] as StatType; - const period = resolvePeriodFromButton(interaction); const player = await resolvePlayerFromButton(interaction); + const period = resolvePeriodFromButton(player.server, interaction); return await stats( commandGroup, diff --git a/src/commands/today.tsx b/src/commands/today.tsx index 613014928..7026723fd 100644 --- a/src/commands/today.tsx +++ b/src/commands/today.tsx @@ -1,29 +1,40 @@ import { SlashCommandBuilder } from 'discord.js'; -import getWargamingResponse from '../core/blitz/getWargamingResponse.js'; -import addUsernameChoices from '../core/discord/addUsernameChoices.js'; -import autocompleteUsername from '../core/discord/autocompleteUsername.js'; -import interactionToURL from '../core/discord/interactionToURL.js'; -import linkButton from '../core/discord/linkButton.js'; -import primaryButton from '../core/discord/primaryButton.js'; -import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton.js'; -import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand.js'; -import { WARGAMING_APPLICATION_ID } from '../core/node/arguments.js'; -import { CommandRegistry } from '../events/interactionCreate/index.js'; -import today from '../renderers/today.js'; -import { AccountInfo } from '../types/accountInfo.js'; +import { CYCLIC_API } from '../constants/cyclic'; +import getWargamingResponse from '../core/blitz/getWargamingResponse'; +import addUsernameChoices from '../core/discord/addUsernameChoices'; +import autocompleteUsername from '../core/discord/autocompleteUsername'; +import interactionToURL from '../core/discord/interactionToURL'; +import linkButton from '../core/discord/linkButton'; +import primaryButton from '../core/discord/primaryButton'; +import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton'; +import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand'; +import { WARGAMING_APPLICATION_ID } from '../core/node/arguments'; +import { CommandRegistry } from '../events/interactionCreate'; +import today from '../renderers/today'; +import { AccountInfo } from '../types/accountInfo'; export const todayCommand: CommandRegistry = { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() .setName('today') .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) { const player = await resolvePlayerFromCommand(interaction); + const limit = interaction.options.getInteger('limit') ?? Infinity; const { nickname } = ( await getWargamingResponse( `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); return [ - await today(player), + await today(player, limit, false), primaryButton(path, 'Refresh'), - // linkButton(`${CYCLIC_API}/${path}`, 'Embed'), + linkButton(`${CYCLIC_API}/${path}`, 'Embed'), linkButton( `https://www.blitzstars.com/player/${player.server}/${nickname}`, 'BlitzStars', diff --git a/src/commands/verify.ts b/src/commands/verify.ts index 0dc4aa4a5..66c78d35e 100644 --- a/src/commands/verify.ts +++ b/src/commands/verify.ts @@ -1,20 +1,20 @@ import { SlashCommandBuilder } from 'discord.js'; import markdownEscape from 'markdown-escape'; import discord from '../../discord.json' assert { type: 'json' }; -import getWargamingResponse from '../core/blitz/getWargamingResponse.js'; -import addUsernameChoices from '../core/discord/addUsernameChoices.js'; -import autocompleteUsername from '../core/discord/autocompleteUsername.js'; -import embedNegative from '../core/discord/embedNegative.js'; -import embedPositive from '../core/discord/embedPositive.js'; -import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand.js'; -import { WARGAMING_APPLICATION_ID } from '../core/node/arguments.js'; -import { CommandRegistry } from '../events/interactionCreate/index.js'; -import { AccountInfo } from '../types/accountInfo.js'; -import { PlayerClanData } from '../types/playerClanData.js'; +import getWargamingResponse from '../core/blitz/getWargamingResponse'; +import addUsernameChoices from '../core/discord/addUsernameChoices'; +import autocompleteUsername from '../core/discord/autocompleteUsername'; +import embedNegative from '../core/discord/embedNegative'; +import embedPositive from '../core/discord/embedPositive'; +import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand'; +import { WARGAMING_APPLICATION_ID } from '../core/node/arguments'; +import { CommandRegistry } from '../events/interactionCreate'; +import { AccountInfo } from '../types/accountInfo'; +import { PlayerClanData } from '../types/playerClanData'; export const verifyCommand: CommandRegistry = { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/components/AllStatsOverview/components/WN8Display/components/Glow.tsx b/src/components/AllStatsOverview/components/WN8Display/components/Glow.tsx index 7c5d2f93e..d1acbffd4 100644 --- a/src/components/AllStatsOverview/components/WN8Display/components/Glow.tsx +++ b/src/components/AllStatsOverview/components/WN8Display/components/Glow.tsx @@ -1,6 +1,6 @@ -import { Percentile } from '../../../../../constants/percentiles.js'; -import hslToHex from '../../../../../core/node/hslToHex.js'; -import { PERCENTILE_COLORS } from '../../../../PercentileIndicator.js'; +import { Percentile } from '../../../../../constants/percentiles'; +import hslToHex from '../../../../../core/node/hslToHex'; +import { PERCENTILE_COLORS } from '../../../../PercentileIndicator'; export enum GlowSide { Top, diff --git a/src/components/AllStatsOverview/components/WN8Display/index.tsx b/src/components/AllStatsOverview/components/WN8Display/index.tsx index 08f257fe2..4fd0da495 100644 --- a/src/components/AllStatsOverview/components/WN8Display/index.tsx +++ b/src/components/AllStatsOverview/components/WN8Display/index.tsx @@ -1,8 +1,8 @@ -import { Percentile } from '../../../../constants/percentiles.js'; -import getWN8Percentile from '../../../../core/blitz/getWN8Percentile.js'; -import { theme } from '../../../../stitches.config.js'; -import { PERCENTILE_COLORS } from '../../../PercentileIndicator.js'; -import { Glow, GlowSide } from './components/Glow.js'; +import { Percentile } from '../../../../constants/percentiles'; +import getWN8Percentile from '../../../../core/blitz/getWN8Percentile'; +import { theme } from '../../../../stitches.config'; +import { PERCENTILE_COLORS } from '../../../PercentileIndicator'; +import { Glow, GlowSide } from './components/Glow'; export interface WN8DisplayProps { WN8?: number; diff --git a/src/components/AllStatsOverview/components/WN8SurroundingStat.tsx b/src/components/AllStatsOverview/components/WN8SurroundingStat.tsx index 4c48d00fa..807e2eb50 100644 --- a/src/components/AllStatsOverview/components/WN8SurroundingStat.tsx +++ b/src/components/AllStatsOverview/components/WN8SurroundingStat.tsx @@ -1,4 +1,4 @@ -import { theme } from '../../../stitches.config.js'; +import { theme } from '../../../stitches.config'; export enum WN8SurroundingStatAlign { Left, diff --git a/src/components/AllStatsOverview/index.tsx b/src/components/AllStatsOverview/index.tsx index e1d309e4e..da7ba6c34 100644 --- a/src/components/AllStatsOverview/index.tsx +++ b/src/components/AllStatsOverview/index.tsx @@ -1,9 +1,9 @@ -import { AllStats, SupplementaryStats } from '../../types/accountInfo.js'; -import { WN8Display } from './components/WN8Display/index.js'; +import { AllStats, SupplementaryStats } from '../../types/accountInfo'; +import { WN8Display } from './components/WN8Display'; import { WN8SurroundingStat, WN8SurroundingStatAlign, -} from './components/WN8SurroundingStat.js'; +} from './components/WN8SurroundingStat'; export interface AllStatsOverviewProps { stats: AllStats; diff --git a/src/components/Breakdown/components/Root.tsx b/src/components/Breakdown/components/Root.tsx index e5d06bea7..9f40d82f9 100644 --- a/src/components/Breakdown/components/Root.tsx +++ b/src/components/Breakdown/components/Root.tsx @@ -6,7 +6,7 @@ export interface RootProps { export function Root({ children }: RootProps) { return ( -
+
{children}
); diff --git a/src/components/Breakdown/components/Row.tsx b/src/components/Breakdown/components/Row.tsx index f5e6c8786..c92f28ced 100644 --- a/src/components/Breakdown/components/Row.tsx +++ b/src/components/Breakdown/components/Row.tsx @@ -1,6 +1,7 @@ -import getWN8Percentile from '../../../core/blitz/getWN8Percentile.js'; -import { RowDiscriminator } from './RowDiscriminator.js'; -import { RowStat } from './RowStat.js'; +import getWN8Percentile from '../../../core/blitz/getWN8Percentile'; +import { theme } from '../../../stitches.config'; +import { RowDiscriminator } from './RowDiscriminator'; +import { RowStat } from './RowStat'; export interface RowProps { name: string; @@ -10,14 +11,17 @@ export interface RowProps { careerWN8?: number; damage: number; careerDamage: number; - survival: number; - careerSurvival: number; battles: number; careerBattles: number; icon?: string; + minimized: boolean; + isListing: boolean; + naked?: boolean; } export function Row({ + isListing, + minimized, name, winrate, careerWinrate, @@ -25,44 +29,46 @@ export function Row({ careerWN8, damage, careerDamage, - survival, - careerSurvival, battles, careerBattles, icon, + naked, }: RowProps) { return ( -
- +
+ -
); diff --git a/src/components/Breakdown/components/RowDiscriminator.tsx b/src/components/Breakdown/components/RowDiscriminator.tsx index b4c9c8f2a..04bc4c2d3 100644 --- a/src/components/Breakdown/components/RowDiscriminator.tsx +++ b/src/components/Breakdown/components/RowDiscriminator.tsx @@ -1,11 +1,16 @@ -import { theme } from '../../../stitches.config.js'; +import { theme } from '../../../stitches.config'; export interface RowDiscriminatorProps { name: string; icon?: string; + minimized: boolean; } -export function RowDiscriminator({ name, icon }: RowDiscriminatorProps) { +export function RowDiscriminator({ + name, + icon, + minimized, +}: RowDiscriminatorProps) { return (
- {icon && ( + {!minimized && icon && ( - - {career} - -
{value}
- - {name} - + {!minimized && ( + + {name} + + )}
); } diff --git a/src/components/Breakdown/index.ts b/src/components/Breakdown/index.ts index c66d8dee9..8bb31e1ff 100644 --- a/src/components/Breakdown/index.ts +++ b/src/components/Breakdown/index.ts @@ -1,4 +1,4 @@ -export * from './components/Root.js'; -export * from './components/Row.js'; -export * from './components/RowDiscriminator.js'; -export * from './components/RowStat.js'; +export * from './components/Root'; +export * from './components/Row'; +export * from './components/RowDiscriminator'; +export * from './components/RowStat'; diff --git a/src/components/GenericAllStats.tsx b/src/components/GenericAllStats.tsx deleted file mode 100644 index 54ff834f3..000000000 --- a/src/components/GenericAllStats.tsx +++ /dev/null @@ -1,60 +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 ( - - ); -} diff --git a/src/components/GenericAllStats/components/Card.tsx b/src/components/GenericAllStats/components/Card.tsx new file mode 100644 index 000000000..71fb3d6e5 --- /dev/null +++ b/src/components/GenericAllStats/components/Card.tsx @@ -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 ( +
+ + {title} + + +
+ {items.map((item) => ( +
+ + {item.label} + + +
+ +
+ {item.percentile !== undefined && ( + + )} + + {item.value === undefined || Number.isNaN(item.value) + ? '--' + : item.value} + +
+
+ ))} +
+
+ ); +} diff --git a/src/components/GenericAllStats/components/Root.tsx b/src/components/GenericAllStats/components/Root.tsx new file mode 100644 index 000000000..0cf26bfde --- /dev/null +++ b/src/components/GenericAllStats/components/Root.tsx @@ -0,0 +1,17 @@ +export interface RootProps { + children: React.ReactNode; +} + +export function Root({ children }: RootProps) { + return ( +
+ {children} +
+ ); +} diff --git a/src/components/GenericAllStats/components/Row.tsx b/src/components/GenericAllStats/components/Row.tsx new file mode 100644 index 000000000..dcb083a60 --- /dev/null +++ b/src/components/GenericAllStats/components/Row.tsx @@ -0,0 +1,7 @@ +export interface RowProps { + children: React.ReactNode; +} + +export function Row({ children }: RowProps) { + return
{children}
; +} diff --git a/src/components/GenericAllStats/index.tsx b/src/components/GenericAllStats/index.tsx new file mode 100644 index 000000000..2a7784a4b --- /dev/null +++ b/src/components/GenericAllStats/index.tsx @@ -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 ( + + + + + + + + + + + ); +} diff --git a/src/components/GenericStats/components/GenericStatsColumn.tsx b/src/components/GenericStats/components/GenericStatsColumn.tsx index bece9ff4e..e8b3e6f00 100644 --- a/src/components/GenericStats/components/GenericStatsColumn.tsx +++ b/src/components/GenericStats/components/GenericStatsColumn.tsx @@ -1,5 +1,5 @@ -import { Stat } from '../index.js'; -import GenericStatsRow from './GenericStatsRow.js'; +import { Stat } from '..'; +import GenericStatsRow from './GenericStatsRow'; export interface GenericStatsColumnProps { stats: Stat[]; diff --git a/src/components/GenericStats/components/GenericStatsRow.tsx b/src/components/GenericStats/components/GenericStatsRow.tsx index 1630cccb8..1a4e5c7a0 100644 --- a/src/components/GenericStats/components/GenericStatsRow.tsx +++ b/src/components/GenericStats/components/GenericStatsRow.tsx @@ -1,6 +1,6 @@ -import { theme } from '../../../stitches.config.js'; -import PercentileIndicator from '../../PercentileIndicator.js'; -import { Stat } from '../index.js'; +import { Stat } from '..'; +import { theme } from '../../../stitches.config'; +import PercentileIndicator from '../../PercentileIndicator'; export interface GenericStatsRowProps { stat: Stat; diff --git a/src/components/GenericStats/index.tsx b/src/components/GenericStats/index.tsx index 94414baad..b22dce987 100644 --- a/src/components/GenericStats/index.tsx +++ b/src/components/GenericStats/index.tsx @@ -1,5 +1,5 @@ -import { Percentile } from '../../constants/percentiles.js'; -import GenericStatsColumn from './components/GenericStatsColumn.js'; +import { Percentile } from '../../constants/percentiles'; +import GenericStatsColumn from './components/GenericStatsColumn'; export type Stat = | [string, string | number | undefined] diff --git a/src/components/Graph/components/Line.tsx b/src/components/Graph/components/Line.tsx index 3c170eead..8a3e0d0ec 100644 --- a/src/components/Graph/components/Line.tsx +++ b/src/components/Graph/components/Line.tsx @@ -1,5 +1,5 @@ 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 { Red, diff --git a/src/components/Graph/components/Margin.tsx b/src/components/Graph/components/Margin.tsx index 144a7904a..292847455 100644 --- a/src/components/Graph/components/Margin.tsx +++ b/src/components/Graph/components/Margin.tsx @@ -1,6 +1,6 @@ import { times } from 'lodash'; -import { theme } from '../../../stitches.config.js'; -import { MarginInputProps } from './Root.js'; +import { theme } from '../../../stitches.config'; +import { MarginInputProps } from './Root'; export enum MarginOrientation { Vertical, @@ -37,7 +37,7 @@ export function Margin({ key={index} style={{ fontSize: 16, - color: theme.colors.textLowContrast, + color: theme.colors.textHighContrast, }} > {`${( diff --git a/src/components/Graph/components/Root.tsx b/src/components/Graph/components/Root.tsx index 5bc56b5db..cc5d6df8a 100644 --- a/src/components/Graph/components/Root.tsx +++ b/src/components/Graph/components/Root.tsx @@ -1,7 +1,7 @@ import { times } from 'lodash'; import { ReactNode } from 'react'; -import { theme } from '../../../stitches.config.js'; -import { Margin, MarginOrientation } from './Margin.js'; +import { theme } from '../../../stitches.config'; +import { Margin, MarginOrientation } from './Margin'; export const GRAPH_WIDTH = 544; export const GRAPH_HEIGHT = 320; @@ -20,6 +20,7 @@ export interface RootProps { separations?: number; xMinLabel: string; xMaxLabel: string; + xTitle: string; } export function Root({ @@ -29,6 +30,7 @@ export function Root({ separations = 5, xMinLabel, xMaxLabel, + xTitle, }: RootProps) { return (
@@ -39,10 +41,10 @@ export function Root({ justifyContent: 'space-between', }} > - + {xMinLabel} - + {xMaxLabel}
@@ -88,6 +90,19 @@ export function Root({ orientation={MarginOrientation.Horizontal} /> )} + +
+ + {xTitle} + +
); } diff --git a/src/components/Graph/index.ts b/src/components/Graph/index.ts index eceb36006..5aaadb77e 100644 --- a/src/components/Graph/index.ts +++ b/src/components/Graph/index.ts @@ -1,3 +1,3 @@ -export * from './components/Line.js'; -export * from './components/Margin.js'; -export * from './components/Root.js'; +export * from './components/Line'; +export * from './components/Margin'; +export * from './components/Root'; diff --git a/src/components/NoData.tsx b/src/components/NoData.tsx index 003ed8040..3bf47967b 100644 --- a/src/components/NoData.tsx +++ b/src/components/NoData.tsx @@ -1,4 +1,4 @@ -import { theme } from '../stitches.config.js'; +import { theme } from '../stitches.config'; export enum NoDataType { BattlesInPeriod, diff --git a/src/components/PercentileIndicator.tsx b/src/components/PercentileIndicator.tsx index 9a466a562..548a46039 100644 --- a/src/components/PercentileIndicator.tsx +++ b/src/components/PercentileIndicator.tsx @@ -10,7 +10,7 @@ import { teal, yellow, } from '@radix-ui/colors'; -import { Percentile } from '../constants/percentiles.js'; +import { Percentile } from '../constants/percentiles'; export interface PercentileIndicatorProps { percentile: Percentile; diff --git a/src/components/PoweredBy.tsx b/src/components/PoweredBy.tsx index fc376f057..8b224b1d7 100644 --- a/src/components/PoweredBy.tsx +++ b/src/components/PoweredBy.tsx @@ -1,5 +1,5 @@ import { ReactNode } from 'react'; -import { theme } from '../stitches.config.js'; +import { theme } from '../stitches.config'; export enum PoweredByType { BlitzStars, diff --git a/src/components/Tanks/components/Column.tsx b/src/components/Tanks/components/Column.tsx new file mode 100644 index 000000000..7215c6186 --- /dev/null +++ b/src/components/Tanks/components/Column.tsx @@ -0,0 +1,20 @@ +import { ReactNode } from 'react'; + +export interface ColumnProps { + children: ReactNode; +} + +export function Column({ children }: ColumnProps) { + return ( +
+ {children} +
+ ); +} diff --git a/src/components/Tanks/components/Item.tsx b/src/components/Tanks/components/Item.tsx index 181379f18..d7d2cd546 100644 --- a/src/components/Tanks/components/Item.tsx +++ b/src/components/Tanks/components/Item.tsx @@ -1,38 +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 { - icon?: string; - type?: TankType; - name: string; +export enum TreeType { + TechTree, + Premium, + 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 (
- {icon && } - -
- {type && ( - +
+ {tankType && ( + )} - {name}
+ + {image && }
); } diff --git a/src/components/Tanks/components/Root.tsx b/src/components/Tanks/components/Root.tsx index 1a23e18ea..9d47b099f 100644 --- a/src/components/Tanks/components/Root.tsx +++ b/src/components/Tanks/components/Root.tsx @@ -9,10 +9,7 @@ export function Root({ children }: RootProps) {
{children} diff --git a/src/components/Tanks/index.ts b/src/components/Tanks/index.ts index 9ac57386e..25362d8dd 100644 --- a/src/components/Tanks/index.ts +++ b/src/components/Tanks/index.ts @@ -1,2 +1,3 @@ -export * from './components/Item.js'; -export * from './components/Root.js'; +export * from './components/Column'; +export * from './components/Item'; +export * from './components/Root'; diff --git a/src/components/TierWeights.tsx b/src/components/TierWeights.tsx index 8923b027b..f08a7046f 100644 --- a/src/components/TierWeights.tsx +++ b/src/components/TierWeights.tsx @@ -1,5 +1,5 @@ -import { TIER_ROMAN_NUMERALS, Tier } from '../core/blitz/tankopedia.js'; -import { themeLight } from '../stitches.config.js'; +import { TIER_ROMAN_NUMERALS, Tier } from '../core/blitz/tankopedia'; +import { theme } from '../stitches.config'; export type TierWeightsRecord = Partial>; @@ -14,9 +14,9 @@ export default function TierWeights({ weights }: TierWeightsProps) {
{children} diff --git a/src/core/blitz/calculateWN8.ts b/src/core/blitz/calculateWN8.ts index 850c2f6ea..1c3a1fc24 100644 --- a/src/core/blitz/calculateWN8.ts +++ b/src/core/blitz/calculateWN8.ts @@ -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 diff --git a/src/core/blitz/diffStats.ts b/src/core/blitz/diffStats.ts index 3c336d4d2..96cc3694d 100644 --- a/src/core/blitz/diffStats.ts +++ b/src/core/blitz/diffStats.ts @@ -1,4 +1,4 @@ -import { AllStats } from '../../types/accountInfo.js'; +import { AllStats } from '../../types/accountInfo'; export default function diffStats(stats1: AllStats, stats2: AllStats) { function diff(value: (allStats: AllStats) => number) { diff --git a/src/core/blitz/getBlitzClan.ts b/src/core/blitz/getBlitzClan.ts index d8e7c5311..fb4785996 100644 --- a/src/core/blitz/getBlitzClan.ts +++ b/src/core/blitz/getBlitzClan.ts @@ -1,7 +1,7 @@ -import { BlitzServer } from '../../constants/servers.js'; -import { serverAndIdPattern } from '../discord/resolvePlayerFromCommand.js'; -import throwError from '../node/throwError.js'; -import listClans from './listClans.js'; +import { BlitzServer } from '../../constants/servers'; +import { serverAndIdPattern } from '../discord/resolvePlayerFromCommand'; +import throwError from '../node/throwError'; +import listClans from './listClans'; export default async function getBlitzClan(clan: string) { if (serverAndIdPattern.test(clan)) { diff --git a/src/core/blitz/getTankStats.ts b/src/core/blitz/getTankStats.ts index 2f75efd05..c8b2e7508 100644 --- a/src/core/blitz/getTankStats.ts +++ b/src/core/blitz/getTankStats.ts @@ -1,8 +1,8 @@ -import { BLITZ_SERVERS, BlitzServer } from '../../constants/servers.js'; -import { TanksStats } from '../../types/tanksStats.js'; -import { WARGAMING_APPLICATION_ID } from '../node/arguments.js'; -import throwError from '../node/throwError.js'; -import getWargamingResponse from './getWargamingResponse.js'; +import { BLITZ_SERVERS, BlitzServer } from '../../constants/servers'; +import { TanksStats } from '../../types/tanksStats'; +import { WARGAMING_APPLICATION_ID } from '../node/arguments'; +import throwError from '../node/throwError'; +import getWargamingResponse from './getWargamingResponse'; export default async function getTankStats(server: BlitzServer, id: number) { const tankStats = await getWargamingResponse( diff --git a/src/core/blitz/getTreeType.ts b/src/core/blitz/getTreeType.ts new file mode 100644 index 000000000..bdbf303be --- /dev/null +++ b/src/core/blitz/getTreeType.ts @@ -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; +} diff --git a/src/core/blitz/getWN8Percentile.ts b/src/core/blitz/getWN8Percentile.ts index e153cad4e..b0ece4d77 100644 --- a/src/core/blitz/getWN8Percentile.ts +++ b/src/core/blitz/getWN8Percentile.ts @@ -1,4 +1,4 @@ -import { Percentile } from '../../constants/percentiles.js'; +import { Percentile } from '../../constants/percentiles'; export const WN8_PERCENTILES: [number, Percentile][] = [ [0, Percentile.VeryBad], diff --git a/src/core/blitz/getWargamingResponse.ts b/src/core/blitz/getWargamingResponse.ts index 78edbaa93..d0ce1d917 100644 --- a/src/core/blitz/getWargamingResponse.ts +++ b/src/core/blitz/getWargamingResponse.ts @@ -1,4 +1,4 @@ -import throwError from '../node/throwError.js'; +import throwError from '../node/throwError'; export type WargamingResponse = | { diff --git a/src/core/blitz/listClans.ts b/src/core/blitz/listClans.ts index d751e2de7..5c83d6c48 100644 --- a/src/core/blitz/listClans.ts +++ b/src/core/blitz/listClans.ts @@ -1,7 +1,7 @@ -import { BlitzServer } from '../../constants/servers.js'; -import { Clan, ClanList } from '../../types/clanList.js'; -import { WARGAMING_APPLICATION_ID } from '../node/arguments.js'; -import getWargamingResponse from './getWargamingResponse.js'; +import { BlitzServer } from '../../constants/servers'; +import { Clan, ClanList } from '../../types/clanList'; +import { WARGAMING_APPLICATION_ID } from '../node/arguments'; +import getWargamingResponse from './getWargamingResponse'; export type ClanListWithServer = (Clan & { server: 'com' | 'eu' | 'asia'; diff --git a/src/core/blitz/listPlayers.ts b/src/core/blitz/listPlayers.ts index 5820e59d5..4ee46bcd0 100644 --- a/src/core/blitz/listPlayers.ts +++ b/src/core/blitz/listPlayers.ts @@ -1,7 +1,7 @@ -import { BlitzServer } from '../../constants/servers.js'; -import { Account, AccountList } from '../../types/accountList.js'; -import { WARGAMING_APPLICATION_ID } from '../node/arguments.js'; -import getWargamingResponse from './getWargamingResponse.js'; +import { BlitzServer } from '../../constants/servers'; +import { Account, AccountList } from '../../types/accountList'; +import { WARGAMING_APPLICATION_ID } from '../node/arguments'; +import getWargamingResponse from './getWargamingResponse'; export type AccountListWithServer = (Account & { server: 'com' | 'eu' | 'asia'; diff --git a/src/core/blitz/resolveTankId.ts b/src/core/blitz/resolveTankId.ts index 76c74a824..a7bb5d8a2 100644 --- a/src/core/blitz/resolveTankId.ts +++ b/src/core/blitz/resolveTankId.ts @@ -1,6 +1,6 @@ import { go } from 'fuzzysort'; -import throwError from '../node/throwError.js'; -import { TANKS, tankopedia } from './tankopedia.js'; +import throwError from '../node/throwError'; +import { TANKS, tankopedia } from './tankopedia'; export default async function resolveTankId(tank: string) { const number = parseInt(tank); diff --git a/src/core/blitz/resolveTankName.ts b/src/core/blitz/resolveTankName.ts index 8d3405c71..6d3d38a8d 100644 --- a/src/core/blitz/resolveTankName.ts +++ b/src/core/blitz/resolveTankName.ts @@ -1,4 +1,4 @@ -import { tankopedia } from './tankopedia.js'; +import { tankopedia } from './tankopedia'; export default async function resolveTankName(tankId: number) { const tankopediaName = (await tankopedia)[tankId]?.name; diff --git a/src/core/blitz/sumStats.ts b/src/core/blitz/sumStats.ts index 10b2095a0..160ddf593 100644 --- a/src/core/blitz/sumStats.ts +++ b/src/core/blitz/sumStats.ts @@ -1,5 +1,5 @@ -import { AllStats } from '../../types/accountInfo.js'; -import { emptyAllStats } from '../blitzstars/getTankStatsDiffed.js'; +import { AllStats } from '../../types/accountInfo'; +import { emptyAllStats } from '../blitzstars/getDiffedTankStats'; export default function sumStats(allStatsCollection: AllStats[]) { return allStatsCollection.reduce((accumulated, current) => { diff --git a/src/core/blitz/tankopedia.ts b/src/core/blitz/tankopedia.ts index 4452a611d..557f3a4e6 100644 --- a/src/core/blitz/tankopedia.ts +++ b/src/core/blitz/tankopedia.ts @@ -1,14 +1,13 @@ -export type TankType = 'AT-SPG' | 'lightTank' | 'mediumTank' | 'heavyTank'; - export interface TankopediaEntry { name: string; nation: string; is_premium: boolean; + is_collectible: boolean; tier: number; cost: { price_credit: number; price_gold: number }; images: { preview: string; normal: string }; tank_id: number; - type: TankType; + type: string; description: string; } @@ -33,13 +32,27 @@ export const TANK_NAMES = new Promise(async (resolve) => { resolve((await entries).map(({ name }) => name)); }); -export const TANK_ICONS: Record = { +export const TANK_ICONS: Record = { 'AT-SPG': 'https://i.imgur.com/BIHSEH0.png', lightTank: 'https://i.imgur.com/CSNha5V.png', mediumTank: 'https://i.imgur.com/wvf3ltm.png', heavyTank: 'https://i.imgur.com/ECeqlZa.png', }; +export const TANK_ICONS_PREMIUM: Record = { + '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 = { + '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 const TIER_ROMAN_NUMERALS: Record = { diff --git a/src/core/blitzstars/getTankStatsDiffed.ts b/src/core/blitzstars/getDiffedTankStats.ts similarity index 76% rename from src/core/blitzstars/getTankStatsDiffed.ts rename to src/core/blitzstars/getDiffedTankStats.ts index 513f7f21c..bdbab3773 100644 --- a/src/core/blitzstars/getTankStatsDiffed.ts +++ b/src/core/blitzstars/getDiffedTankStats.ts @@ -1,9 +1,9 @@ -import { BlitzServer } from '../../constants/servers.js'; -import { AllStats } from '../../types/accountInfo.js'; +import { BlitzServer } from '../../constants/servers'; +import { AllStats } from '../../types/accountInfo'; import getTankHistories, { TankHistories, TankHistoryRaw, -} from './getTankHistories.js'; +} from './getTankHistories'; export const emptyAllStats: AllStats = { battles: 0, @@ -34,7 +34,7 @@ export const emptyTankHistoryNode: TankHistoryRaw = { all: emptyAllStats, }; -export default async function getTankStatsDiffed( +export default async function getDiffedTankStats( server: BlitzServer, id: number, start: number, @@ -46,26 +46,27 @@ export default async function getTankStatsDiffed( // sort them even though most won't be used const tankSortedHistory: Record = {}; - const tankIds: number[] = []; + const tanks: number[] = []; history.forEach((node) => { if (!tankSortedHistory[node.tank_id]) { tankSortedHistory[node.tank_id] = [node]; - tankIds.push(node.tank_id); + tanks.push(node.tank_id); } else { tankSortedHistory[node.tank_id].push(node); } }); // find the ones played within the range - const tankIdsInRange = tankIds.filter((tankId) => + const inRangeTanks = tanks.filter((tankId) => tankSortedHistory[tankId].some( (node) => node.last_battle_time > start && node.last_battle_time < end, ), ); + const playedTanks: number[] = []; // fetch history node right before "start" and right before "end" - const tanksBeforeAndAfter: Record = {}; - tankIdsInRange.forEach((tankId) => { + const diffed: Record = {}; + inRangeTanks.forEach((tankId) => { const sortedHistory = tankSortedHistory[tankId]; const previousIndex = 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 if (diff((a) => a.battles) > 0) { - tanksBeforeAndAfter[tankId] = { + diffed[tankId] = { battles: diff((a) => a.battles), capture_points: diff((a) => a.capture_points), damage_dealt: diff((a) => a.damage_dealt), @@ -100,8 +101,19 @@ export default async function getTankStatsDiffed( wins: diff((a) => a.wins), 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 }; } diff --git a/src/core/blitzstars/getPeriodStart.ts b/src/core/blitzstars/getPeriodStart.ts index ec955af34..8357aab44 100644 --- a/src/core/blitzstars/getPeriodStart.ts +++ b/src/core/blitzstars/getPeriodStart.ts @@ -1,12 +1,16 @@ -import { Period } from '../discord/addPeriodSubCommands.js'; -import getTimeDaysAgo from './getTimeDaysAgo.js'; +import { BlitzServer } from '../../constants/servers'; +import { Period } from '../discord/addPeriodSubCommands'; +import getTimeDaysAgo from './getTimeDaysAgo'; -export default function getPeriodStart(period: Exclude) { +export default function getPeriodStart( + server: BlitzServer, + period: Exclude, +) { if (period === 'career') { return -Infinity; } else if (period === 'today') { - return getTimeDaysAgo(0); + return getTimeDaysAgo(server, 1); } else { - return getTimeDaysAgo(parseInt(period)); + return getTimeDaysAgo(server, parseInt(period)); } } diff --git a/src/core/blitzstars/getPlayerHistories.ts b/src/core/blitzstars/getPlayerHistories.ts index 72e6aa852..ac0419b93 100644 --- a/src/core/blitzstars/getPlayerHistories.ts +++ b/src/core/blitzstars/getPlayerHistories.ts @@ -1,14 +1,14 @@ -import { BlitzServer } from '../../constants/servers.js'; -import { AccountInfo, AllStats } from '../../types/accountInfo.js'; +import { BlitzServer } from '../../constants/servers'; +import { AccountInfo, AllStats } from '../../types/accountInfo'; import { GetHistoriesOptions, Histories, History, getHistoriesDefaultOptions, -} from '../../types/histories.js'; -import getWargamingResponse from '../blitz/getWargamingResponse.js'; -import { WARGAMING_APPLICATION_ID } from '../node/arguments.js'; -import { emptyAllStats } from './getTankStatsDiffed.js'; +} from '../../types/histories'; +import getWargamingResponse from '../blitz/getWargamingResponse'; +import { WARGAMING_APPLICATION_ID } from '../node/arguments'; +import { emptyAllStats } from './getDiffedTankStats'; export interface PlayerHistoryRaw { clan: { clan_id: number; name: string; tag: string }; @@ -44,7 +44,7 @@ export default async function getPlayerHistories( ({ all: history.statistics.all, last_battle_time: history.last_battle_time, - } satisfies History), + }) satisfies History, ); let lastNonCompliantIndex = -1; let hasCompliedOnce = false; diff --git a/src/core/blitzstars/getTankHistories.ts b/src/core/blitzstars/getTankHistories.ts index 4e69e198c..e6fb78415 100644 --- a/src/core/blitzstars/getTankHistories.ts +++ b/src/core/blitzstars/getTankHistories.ts @@ -1,12 +1,12 @@ -import { BlitzServer } from '../../constants/servers.js'; +import { BlitzServer } from '../../constants/servers'; import { GetHistoriesOptions, Histories, History, getHistoriesDefaultOptions, -} from '../../types/histories.js'; -import getTankStats from '../blitz/getTankStats.js'; -import { emptyAllStats } from './getTankStatsDiffed.js'; +} from '../../types/histories'; +import getTankStats from '../blitz/getTankStats'; +import { emptyAllStats } from './getDiffedTankStats'; export interface TankHistory extends History { tank_id: number; @@ -48,7 +48,7 @@ export default async function getTankHistories( all: history.all, last_battle_time: history.last_battle_time, tank_id: history.tank_id, - } satisfies TankHistory), + }) satisfies TankHistory, ); const compliantTanks: number[] = []; const lastBattles: Record = {}; diff --git a/src/core/blitzstars/getTimeDaysAgo.ts b/src/core/blitzstars/getTimeDaysAgo.ts index 86a3512ea..ff8452ec2 100644 --- a/src/core/blitzstars/getTimeDaysAgo.ts +++ b/src/core/blitzstars/getTimeDaysAgo.ts @@ -1,12 +1,20 @@ -export default function getTimeDaysAgo(daysAgo: number) { +import { BlitzServer } from '../../constants/servers'; + +export const TIME_ZONE_MAPPINGS: Record = { + com: -5, // Central North American Time + eu: +1, // Central European Time + asia: +7, // Central Asia Standard Time +}; + +export default function getTimeDaysAgo(server: BlitzServer, daysAgo: number) { 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 - if (now.getTime() > Date.now()) now.setDate(now.getDate() - 1); + const time = new Date(); - // subtract the period number of days from now - now.setDate(now.getDate() - daysAgo); + time.setUTCHours(-TIME_ZONE_MAPPINGS[server], 0, 0, 0); + if (time > now) time.setUTCDate(time.getUTCDate() - 1); + time.setUTCDate(time.getUTCDate() - daysAgo + 1); - return now.getTime() / 1000; + return time.getTime() / 1000; } diff --git a/src/core/blitzstars/playerAverage.ts b/src/core/blitzstars/playerAverage.ts index f2206d83c..b9b750957 100644 --- a/src/core/blitzstars/playerAverage.ts +++ b/src/core/blitzstars/playerAverage.ts @@ -1,6 +1,6 @@ -import sumStats from '../blitz/sumStats.js'; -import { emptyAllStats } from './getTankStatsDiffed.js'; -import { tankAverages } from './tankAverages.js'; +import sumStats from '../blitz/sumStats'; +import { emptyAllStats } from './getDiffedTankStats'; +import { tankAverages } from './tankAverages'; console.log('Calculating player average...'); export const playerAverage = Object.entries(await tankAverages).reduce( diff --git a/src/core/blitzstars/tankAverages.ts b/src/core/blitzstars/tankAverages.ts index 84733e336..c36b554ca 100644 --- a/src/core/blitzstars/tankAverages.ts +++ b/src/core/blitzstars/tankAverages.ts @@ -1,4 +1,4 @@ -import { AllStats } from '../../types/accountInfo.js'; +import { AllStats } from '../../types/accountInfo'; export interface SpecialStats { winrate: number; diff --git a/src/core/discord/addPeriodSubCommands.ts b/src/core/discord/addPeriodSubCommands.ts index 74fd61087..35d7a4dda 100644 --- a/src/core/discord/addPeriodSubCommands.ts +++ b/src/core/discord/addPeriodSubCommands.ts @@ -2,7 +2,7 @@ import { SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder, } 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'; diff --git a/src/core/discord/addServerChoices.ts b/src/core/discord/addServerChoices.ts index 5c6ad5def..e3f806aa9 100644 --- a/src/core/discord/addServerChoices.ts +++ b/src/core/discord/addServerChoices.ts @@ -1,5 +1,5 @@ import { SlashCommandStringOption } from 'discord.js'; -import { BLITZ_SERVERS } from '../../constants/servers.js'; +import { BLITZ_SERVERS } from '../../constants/servers'; export default function addServerChoices(option: SlashCommandStringOption) { return option diff --git a/src/core/discord/addStatTypeSubCommandGroups.ts b/src/core/discord/addStatTypeSubCommandGroups.ts index a5657db10..66fe7f586 100644 --- a/src/core/discord/addStatTypeSubCommandGroups.ts +++ b/src/core/discord/addStatTypeSubCommandGroups.ts @@ -1,7 +1,7 @@ import { SlashCommandBuilder } from 'discord.js'; -import addPeriodSubCommands from './addPeriodSubCommands.js'; -import addTankChoices from './addTankChoices.js'; -import addUsernameChoices from './addUsernameChoices.js'; +import addPeriodSubCommands from './addPeriodSubCommands'; +import addTankChoices from './addTankChoices'; +import addUsernameChoices from './addUsernameChoices'; export default function addStatTypeSubCommandGroups( option: SlashCommandBuilder, diff --git a/src/core/discord/autocompleteClan.ts b/src/core/discord/autocompleteClan.ts index 78c7df143..fb5296ddc 100644 --- a/src/core/discord/autocompleteClan.ts +++ b/src/core/discord/autocompleteClan.ts @@ -3,8 +3,8 @@ import { AutocompleteInteraction, CacheType, } from 'discord.js'; -import { BLITZ_SERVERS } from '../../constants/servers.js'; -import listClans from '../blitz/listClans.js'; +import { BLITZ_SERVERS } from '../../constants/servers'; +import listClans from '../blitz/listClans'; export default async function autocompleteClan( interaction: AutocompleteInteraction, @@ -20,7 +20,7 @@ export default async function autocompleteClan( ({ name: `${player.name} (${BLITZ_SERVERS[player.server]})`, value: `${player.server}/${player.clan_id}`, - } satisfies ApplicationCommandOptionChoiceData), + }) satisfies ApplicationCommandOptionChoiceData, ) : [], ); diff --git a/src/core/discord/autocompleteTanks.ts b/src/core/discord/autocompleteTanks.ts index ec75c052c..a7a1cdbaa 100644 --- a/src/core/discord/autocompleteTanks.ts +++ b/src/core/discord/autocompleteTanks.ts @@ -4,8 +4,8 @@ import { CacheType, } from 'discord.js'; import { go } from 'fuzzysort'; -import resolveTankName from '../blitz/resolveTankName.js'; -import { TANKS } from '../blitz/tankopedia.js'; +import resolveTankName from '../blitz/resolveTankName'; +import { TANKS } from '../blitz/tankopedia'; export default async function autocompleteTanks( interaction: AutocompleteInteraction, @@ -16,14 +16,17 @@ export default async function autocompleteTanks( await interaction.respond( focusedOption.value ? await Promise.all( - go(focusedOption.value, await TANKS, { keys: ['name'], limit: 10 }).map( + go(focusedOption.value, await TANKS, { + keys: ['name'], + limit: 10, + }).map( async (item) => ({ name: item.obj.name ? item.obj.name : await resolveTankName(item.obj.tank_id), value: `${item.obj.tank_id}`, - } satisfies ApplicationCommandOptionChoiceData), + }) satisfies ApplicationCommandOptionChoiceData, ), ) : [], diff --git a/src/core/discord/autocompleteUsername.ts b/src/core/discord/autocompleteUsername.ts index 871f424f2..2ee5b3464 100644 --- a/src/core/discord/autocompleteUsername.ts +++ b/src/core/discord/autocompleteUsername.ts @@ -3,8 +3,8 @@ import { AutocompleteInteraction, CacheType, } from 'discord.js'; -import { BLITZ_SERVERS } from '../../constants/servers.js'; -import listPlayers from '../blitz/listPlayers.js'; +import { BLITZ_SERVERS } from '../../constants/servers'; +import listPlayers from '../blitz/listPlayers'; export default async function autocompleteUsername( interaction: AutocompleteInteraction, @@ -21,7 +21,7 @@ export default async function autocompleteUsername( ({ name: `${player.nickname} (${BLITZ_SERVERS[player.server]})`, value: `${player.server}/${player.account_id}`, - } satisfies ApplicationCommandOptionChoiceData), + }) satisfies ApplicationCommandOptionChoiceData, ) : [], ); diff --git a/src/core/discord/embedInfo.ts b/src/core/discord/embedInfo.ts index 4c591e1f6..75ac86c80 100644 --- a/src/core/discord/embedInfo.ts +++ b/src/core/discord/embedInfo.ts @@ -1,9 +1,12 @@ 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) { - return new EmbedBuilder() + const embed = new EmbedBuilder() .setColor(ACCENT_COLOR) - .setTitle(title) .setDescription(description); + + if (title) embed.setTitle(title); + + return embed; } diff --git a/src/core/discord/embedNegative.ts b/src/core/discord/embedNegative.ts index 54e11ab8b..35704b20e 100644 --- a/src/core/discord/embedNegative.ts +++ b/src/core/discord/embedNegative.ts @@ -1,5 +1,5 @@ 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) { return new EmbedBuilder() diff --git a/src/core/discord/embedPositive.ts b/src/core/discord/embedPositive.ts index c9067d818..47174a0d1 100644 --- a/src/core/discord/embedPositive.ts +++ b/src/core/discord/embedPositive.ts @@ -1,5 +1,5 @@ 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) { return new EmbedBuilder() diff --git a/src/core/discord/embedWarning.ts b/src/core/discord/embedWarning.ts index 45ee01909..3148f7a7a 100644 --- a/src/core/discord/embedWarning.ts +++ b/src/core/discord/embedWarning.ts @@ -1,5 +1,5 @@ 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) { return new EmbedBuilder() diff --git a/src/core/discord/normalizeInteractionReturnable.ts b/src/core/discord/normalizeInteractionReturnable.ts index d935763a3..3f354735b 100644 --- a/src/core/discord/normalizeInteractionReturnable.ts +++ b/src/core/discord/normalizeInteractionReturnable.ts @@ -4,8 +4,8 @@ import { EmbedBuilder, InteractionEditReplyOptions, } from 'discord.js'; -import { InteractionReturnable } from '../../events/interactionCreate/index.js'; -import jsxToPng from '../node/jsxToPng.js'; +import { InteractionReturnable } from '../../events/interactionCreate'; +import jsxToPng from '../node/jsxToPng'; export default async function normalizeInteractionReturnable( returnable: InteractionReturnable, @@ -19,7 +19,9 @@ export default async function normalizeInteractionReturnable( await Promise.all( normalizedReturnable.map(async (item) => { - if (item instanceof EmbedBuilder) { + if (typeof item === 'string') { + reply.content = item; + } else if (item instanceof EmbedBuilder) { if (!reply.embeds) reply.embeds = []; reply.embeds.push(item); } else if (item instanceof ButtonBuilder) { diff --git a/src/core/discord/resolvePeriodFromButton.ts b/src/core/discord/resolvePeriodFromButton.ts index cda3c11ca..0c31e65e4 100644 --- a/src/core/discord/resolvePeriodFromButton.ts +++ b/src/core/discord/resolvePeriodFromButton.ts @@ -1,9 +1,11 @@ import { ButtonInteraction, CacheType } from 'discord.js'; -import { CYCLIC_API } from '../../constants/cyclic.js'; -import resolvePeriodFromURL from '../express/resolvePeriodFromURL.js'; +import { CYCLIC_API } from '../../constants/cyclic'; +import { BlitzServer } from '../../constants/servers'; +import resolvePeriodFromURL from '../express/resolvePeriodFromURL'; export default function resolvePeriodFromButton( + server: BlitzServer, interaction: ButtonInteraction, ) { - return resolvePeriodFromURL(`${CYCLIC_API}/${interaction.customId}`); + return resolvePeriodFromURL(server, `${CYCLIC_API}/${interaction.customId}`); } diff --git a/src/core/discord/resolvePeriodFromCommand.ts b/src/core/discord/resolvePeriodFromCommand.ts index 6c5a6bfe5..6672f41fb 100644 --- a/src/core/discord/resolvePeriodFromCommand.ts +++ b/src/core/discord/resolvePeriodFromCommand.ts @@ -1,8 +1,9 @@ import { CacheType, ChatInputCommandInteraction } from 'discord.js'; -import getPeriodNow from '../blitzstars/getPeriodNow.js'; -import getPeriodStart from '../blitzstars/getPeriodStart.js'; -import getTimeDaysAgo from '../blitzstars/getTimeDaysAgo.js'; -import { Period } from '../discord/addPeriodSubCommands.js'; +import { BlitzServer } from '../../constants/servers'; +import getPeriodNow from '../blitzstars/getPeriodNow'; +import getPeriodStart from '../blitzstars/getPeriodStart'; +import getTimeDaysAgo from '../blitzstars/getTimeDaysAgo'; +import { Period } from '../discord/addPeriodSubCommands'; export const PERIOD_NAMES: Record = { today: "Today's statistics", @@ -30,6 +31,7 @@ export interface ResolvedPeriod { } export default function resolvePeriodFromCommand( + server: BlitzServer, interaction: ChatInputCommandInteraction, ) { let statsName: string; @@ -46,12 +48,12 @@ export default function resolvePeriodFromCommand( statsName = `${startMin} to ${endMax} days' statistics`; evolutionName = `${startMin} to ${endMax} days' evolution`; - start = getTimeDaysAgo(startMin); - end = getTimeDaysAgo(endMax); + start = getTimeDaysAgo(server, startMin); + end = getTimeDaysAgo(server, endMax); } else { statsName = PERIOD_NAMES[period]; evolutionName = EVOLUTION_PERIOD_NAMES[period]; - start = getPeriodStart(period); + start = getPeriodStart(server, period); end = getPeriodNow(); } diff --git a/src/core/discord/resolvePlayerFromButton.ts b/src/core/discord/resolvePlayerFromButton.ts index c6b956359..eb469366b 100644 --- a/src/core/discord/resolvePlayerFromButton.ts +++ b/src/core/discord/resolvePlayerFromButton.ts @@ -1,6 +1,6 @@ import { ButtonInteraction, CacheType } from 'discord.js'; -import { CYCLIC_API } from '../../constants/cyclic.js'; -import resolvePlayerFromURL from '../express/resolvePlayerFromURL.js'; +import { CYCLIC_API } from '../../constants/cyclic'; +import resolvePlayerFromURL from '../express/resolvePlayerFromURL'; export default async function resolvePlayerFromButton( interaction: ButtonInteraction, diff --git a/src/core/discord/resolvePlayerFromCommand.ts b/src/core/discord/resolvePlayerFromCommand.ts index 084b8c52f..7c30bfa27 100644 --- a/src/core/discord/resolvePlayerFromCommand.ts +++ b/src/core/discord/resolvePlayerFromCommand.ts @@ -1,9 +1,9 @@ import { CacheType, ChatInputCommandInteraction } from 'discord.js'; -import { BlitzServer } from '../../constants/servers.js'; +import { BlitzServer } from '../../constants/servers'; import listPlayers, { usernamePatternWithoutPosition, -} from '../blitz/listPlayers.js'; -import throwError from '../node/throwError.js'; +} from '../blitz/listPlayers'; +import throwError from '../node/throwError'; export const serverAndIdPattern = /(com|eu|asia)\/[0-9]+/; diff --git a/src/core/express/normalizeRouteReturnable.ts b/src/core/express/normalizeRouteReturnable.ts index f630232da..8f3ecfd1c 100644 --- a/src/core/express/normalizeRouteReturnable.ts +++ b/src/core/express/normalizeRouteReturnable.ts @@ -1,5 +1,5 @@ -import { RouteReturnable } from '../../server.js'; -import jsxToSvg from '../node/jsxToSvg.js'; +import { RouteReturnable } from '../../server'; +import jsxToSvg from '../node/jsxToSvg'; export default async function normalizeRouteReturnable( returnable: RouteReturnable, diff --git a/src/core/express/resolvePeriodFromRequest.ts b/src/core/express/resolvePeriodFromRequest.ts index 8695d79a0..b0e964ad8 100644 --- a/src/core/express/resolvePeriodFromRequest.ts +++ b/src/core/express/resolvePeriodFromRequest.ts @@ -1,7 +1,11 @@ import { Request } from 'express'; -import { CYCLIC_API } from '../../constants/cyclic.js'; -import resolvePeriodFromURL from './resolvePeriodFromURL.js'; +import { CYCLIC_API } from '../../constants/cyclic'; +import { BlitzServer } from '../../constants/servers'; +import resolvePeriodFromURL from './resolvePeriodFromURL'; -export default function resolvePeriodFromRequest(request: Request) { - return resolvePeriodFromURL(`${CYCLIC_API}${request.path}`); +export default function resolvePeriodFromRequest( + server: BlitzServer, + request: Request, +) { + return resolvePeriodFromURL(server, `${CYCLIC_API}${request.path}`); } diff --git a/src/core/express/resolvePeriodFromURL.ts b/src/core/express/resolvePeriodFromURL.ts index 2799f2f39..d478126c6 100644 --- a/src/core/express/resolvePeriodFromURL.ts +++ b/src/core/express/resolvePeriodFromURL.ts @@ -1,14 +1,18 @@ -import getPeriodNow from '../blitzstars/getPeriodNow.js'; -import getPeriodStart from '../blitzstars/getPeriodStart.js'; -import getTimeDaysAgo from '../blitzstars/getTimeDaysAgo.js'; -import { Period } from '../discord/addPeriodSubCommands.js'; +import { BlitzServer } from '../../constants/servers'; +import getPeriodNow from '../blitzstars/getPeriodNow'; +import getPeriodStart from '../blitzstars/getPeriodStart'; +import getTimeDaysAgo from '../blitzstars/getTimeDaysAgo'; +import { Period } from '../discord/addPeriodSubCommands'; import { EVOLUTION_PERIOD_NAMES, PERIOD_NAMES, ResolvedPeriod, -} from '../discord/resolvePeriodFromCommand.js'; +} from '../discord/resolvePeriodFromCommand'; -export default function resolvePeriodFromURL(urlString: string) { +export default function resolvePeriodFromURL( + server: BlitzServer, + urlString: string, +) { let statsName: string; let evolutionName: string; let start: number; @@ -25,12 +29,12 @@ export default function resolvePeriodFromURL(urlString: string) { statsName = `${startMin} to ${endMax} days' statistics`; evolutionName = `${startMin} to ${endMax} days' evolution`; - start = getTimeDaysAgo(startMin); - end = getTimeDaysAgo(endMax); + start = getTimeDaysAgo(server, startMin); + end = getTimeDaysAgo(server, endMax); } else { statsName = PERIOD_NAMES[period]; evolutionName = EVOLUTION_PERIOD_NAMES[period]; - start = getPeriodStart(period); + start = getPeriodStart(server, period); end = getPeriodNow(); } diff --git a/src/core/express/resolvePlayerFromRequest.ts b/src/core/express/resolvePlayerFromRequest.ts index 2fe5bd578..600c39fdb 100644 --- a/src/core/express/resolvePlayerFromRequest.ts +++ b/src/core/express/resolvePlayerFromRequest.ts @@ -1,6 +1,6 @@ import { Request } from 'express'; -import { CYCLIC_API } from '../../constants/cyclic.js'; -import resolvePlayerFromURL from './resolvePlayerFromURL.js'; +import { CYCLIC_API } from '../../constants/cyclic'; +import resolvePlayerFromURL from './resolvePlayerFromURL'; export default function resolvePlayerFromRequest(request: Request) { return resolvePlayerFromURL(`${CYCLIC_API}${request.originalUrl}`); diff --git a/src/core/express/resolvePlayerFromURL.ts b/src/core/express/resolvePlayerFromURL.ts index 81608ab13..983fa11b8 100644 --- a/src/core/express/resolvePlayerFromURL.ts +++ b/src/core/express/resolvePlayerFromURL.ts @@ -1,5 +1,5 @@ -import { BlitzServer } from '../../constants/servers.js'; -import { ResolvedPlayer } from '../discord/resolvePlayerFromCommand.js'; +import { BlitzServer } from '../../constants/servers'; +import { ResolvedPlayer } from '../discord/resolvePlayerFromCommand'; export default function resolvePlayerFromURL(urlString: string) { const url = new URL(urlString); diff --git a/src/core/node/getClientId.ts b/src/core/node/getClientId.ts index bbc2506ed..4f292d07e 100644 --- a/src/core/node/getClientId.ts +++ b/src/core/node/getClientId.ts @@ -1,5 +1,5 @@ import discord from '../../../discord.json' assert { type: 'json' }; -import isDev from './isDev.js'; +import isDev from './isDev'; export default function getClientId() { return isDev() ? discord.client_id_dev : discord.client_id; diff --git a/src/core/node/jsxToPng.ts b/src/core/node/jsxToPng.ts index e48d3b1f5..b558dcd2b 100644 --- a/src/core/node/jsxToPng.ts +++ b/src/core/node/jsxToPng.ts @@ -1,5 +1,5 @@ -import jsxToSvg from './jsxToSvg.js'; -import svgToPng from './svgToPng.js'; +import jsxToSvg from './jsxToSvg'; +import svgToPng from './svgToPng'; export default async function jsxToPng(jsx: JSX.Element) { const svg = await jsxToSvg(jsx); diff --git a/src/core/node/svgToPng.ts b/src/core/node/svgToPng.ts index 3a221097d..601c8648c 100644 --- a/src/core/node/svgToPng.ts +++ b/src/core/node/svgToPng.ts @@ -1,5 +1,5 @@ -import { Resvg } from "@resvg/resvg-js"; +import { Resvg } from '@resvg/resvg-js'; export default function svgToPng(svg: string) { - return new Resvg(svg).render().asPng() -} \ No newline at end of file + return new Resvg(svg).render().asPng(); +} diff --git a/src/events/error.ts b/src/events/error.ts index 8dec386de..c4104b5ff 100644 --- a/src/events/error.ts +++ b/src/events/error.ts @@ -1,8 +1,8 @@ import { EmbedBuilder, TextChannel } from 'discord.js'; import discord from '../../discord.json' assert { type: 'json' }; -import { client } from '../bot.js'; -import { NEGATIVE_COLOR } from '../constants/colors.js'; -import isDev from '../core/node/isDev.js'; +import { client } from '../bot'; +import { NEGATIVE_COLOR } from '../constants/colors'; +import isDev from '../core/node/isDev'; const PROCESS_ERROR_EVENTS = ['uncaughtException']; const CLIENT_ERROR_EVENTS = ['error']; diff --git a/src/events/guildMemberAdd.ts b/src/events/guildMemberAdd.ts index 60b0abb3a..4e57ef9d9 100644 --- a/src/events/guildMemberAdd.ts +++ b/src/events/guildMemberAdd.ts @@ -1,6 +1,6 @@ import { EmbedBuilder, GuildMember, TextChannel } from 'discord.js'; import discord from '../../discord.json' assert { type: 'json' }; -import { ACCENT_COLOR } from '../constants/colors.js'; +import { ACCENT_COLOR } from '../constants/colors'; export default async function (member: GuildMember) { if (member.guild.id === discord.sklld_guild_id) { diff --git a/src/events/interactionCreate/handlers/autocomplete.ts b/src/events/interactionCreate/handlers/autocomplete.ts index 0a9383bed..af4b81bed 100644 --- a/src/events/interactionCreate/handlers/autocomplete.ts +++ b/src/events/interactionCreate/handlers/autocomplete.ts @@ -1,5 +1,5 @@ import { AutocompleteInteraction, CacheType } from 'discord.js'; -import { commands } from '../index.js'; +import { commands } from '..'; export default function handleAutocomplete( interaction: AutocompleteInteraction, diff --git a/src/events/interactionCreate/handlers/button.tsx b/src/events/interactionCreate/handlers/button.tsx index 657a0bcc8..7fc55ae21 100644 --- a/src/events/interactionCreate/handlers/button.tsx +++ b/src/events/interactionCreate/handlers/button.tsx @@ -1,8 +1,8 @@ import { ButtonInteraction, CacheType } from 'discord.js'; -import { CYCLIC_API } from '../../../constants/cyclic.js'; -import normalizeInteractionReturnable from '../../../core/discord/normalizeInteractionReturnable.js'; -import throwError from '../../../core/node/throwError.js'; -import { commands } from '../index.js'; +import { commands } from '..'; +import { CYCLIC_API } from '../../../constants/cyclic'; +import normalizeInteractionReturnable from '../../../core/discord/normalizeInteractionReturnable'; +import throwError from '../../../core/node/throwError'; export default async function handleButton( interaction: ButtonInteraction, diff --git a/src/events/interactionCreate/handlers/chatInputCommand.ts b/src/events/interactionCreate/handlers/chatInputCommand.ts index 3422b8121..f7b1a855d 100644 --- a/src/events/interactionCreate/handlers/chatInputCommand.ts +++ b/src/events/interactionCreate/handlers/chatInputCommand.ts @@ -5,12 +5,12 @@ import { CacheType, ChatInputCommandInteraction, } from 'discord.js'; -import embedNegative from '../../../core/discord/embedNegative.js'; -import normalizeInteractionReturnable from '../../../core/discord/normalizeInteractionReturnable.js'; -import embedWarning from '../../../core/discord/embedWarning.js'; -import { psa } from '../../../core/discord/psa.js'; -import { handleError } from '../../error.js'; -import { commands } from '../index.js'; +import { commands } from '..'; +import embedNegative from '../../../core/discord/embedNegative'; +import embedWarning from '../../../core/discord/embedWarning'; +import normalizeInteractionReturnable from '../../../core/discord/normalizeInteractionReturnable'; +import { psa } from '../../../core/discord/psa'; +import { handleError } from '../../error'; export default async function handleChatInputCommand( interaction: ChatInputCommandInteraction, diff --git a/src/events/interactionCreate/index.ts b/src/events/interactionCreate/index.ts index 49b3ec1e7..af2ba8d27 100644 --- a/src/events/interactionCreate/index.ts +++ b/src/events/interactionCreate/index.ts @@ -13,30 +13,31 @@ import { SlashCommandSubcommandsOnlyBuilder, } from 'discord.js'; import discord from '../../../discord.json' assert { type: 'json' }; -import { debugCommand } from '../../commands/debug.js'; -import { eligibleCommand } from '../../commands/eligible.js'; -import { evolutionCommand } from '../../commands/evolution.js'; -import { helpCommand } from '../../commands/help.js'; -import { inactiveCommand } from '../../commands/inactive.js'; -import { ownedTanksCommand } from '../../commands/ownedTanks.js'; -import { pingCommand } from '../../commands/ping.js'; -import { playerAchievementsCommand } from '../../commands/playerAchievements.js'; -import { playerInfoCommand } from '../../commands/playerInfo.js'; -import { searchClansCommand } from '../../commands/searchClans.js'; -import { searchPlayersCommand } from '../../commands/searchPlayers.js'; -import { searchTanksCommand } from '../../commands/searchTanks.js'; -import { statsCommand } from '../../commands/stats.js'; -import { statsFullCommand } from '../../commands/statsFull.js'; -import { todayCommand } from '../../commands/today.js'; -import { verifyCommand } from '../../commands/verify.js'; -import { DISCORD_TOKEN } from '../../core/node/arguments.js'; -import getClientId from '../../core/node/getClientId.js'; -import isDev from '../../core/node/isDev.js'; -import handleAutocomplete from './handlers/autocomplete.js'; -import handleButton from './handlers/button.js'; -import handleChatInputCommand from './handlers/chatInputCommand.js'; +import { debugCommand } from '../../commands/debug'; +import { eligibleCommand } from '../../commands/eligible'; +import { evolutionCommand } from '../../commands/evolution'; +import { fullStatsCommand } from '../../commands/fullStats'; +import { helpCommand } from '../../commands/help'; +import { inactiveCommand } from '../../commands/inactive'; +import { ownedTanksCommand } from '../../commands/ownedTanks'; +import { pingCommand } from '../../commands/ping'; +import { playerAchievementsCommand } from '../../commands/playerAchievements'; +import { playerInfoCommand } from '../../commands/playerInfo'; +import { searchClansCommand } from '../../commands/searchClans'; +import { searchPlayersCommand } from '../../commands/searchPlayers'; +import { searchTanksCommand } from '../../commands/searchTanks'; +import { statsCommand } from '../../commands/stats'; +import { todayCommand } from '../../commands/today'; +import { verifyCommand } from '../../commands/verify'; +import { DISCORD_TOKEN } from '../../core/node/arguments'; +import getClientId from '../../core/node/getClientId'; +import isDev from '../../core/node/isDev'; +import handleAutocomplete from './handlers/autocomplete'; +import handleButton from './handlers/button'; +import handleChatInputCommand from './handlers/chatInputCommand'; export type InteractionRawReturnable = + | string | EmbedBuilder | ButtonBuilder | JSX.Element; @@ -70,29 +71,31 @@ export interface CommandRegistry const rest = new REST().setToken(DISCORD_TOKEN); -export const commands: Record = ( - [ - debugCommand, - eligibleCommand, - helpCommand, - inactiveCommand, - ownedTanksCommand, - playerAchievementsCommand, - playerInfoCommand, - searchClansCommand, - searchPlayersCommand, - searchTanksCommand, - statsFullCommand, - todayCommand, - verifyCommand, - pingCommand, - evolutionCommand, - statsCommand, - ] as CommandRegistry[] -).reduce((accumulator, registry) => { - if (isDev()) registry.command.setDefaultMemberPermissions(0); - return { ...accumulator, [registry.command.name]: registry }; -}, {}); +export const COMMANDS_RAW: CommandRegistry[] = [ + debugCommand, + eligibleCommand, + helpCommand, + inactiveCommand, + ownedTanksCommand, + playerAchievementsCommand, + playerInfoCommand, + searchClansCommand, + searchPlayersCommand, + searchTanksCommand, + fullStatsCommand, + todayCommand, + verifyCommand, + pingCommand, + evolutionCommand, + statsCommand, +]; +export const commands: Record = COMMANDS_RAW.reduce( + (accumulator, registry) => { + if (isDev()) registry.command.setDefaultMemberPermissions(0); + return { ...accumulator, [registry.command.name]: registry }; + }, + {}, +); export const guildCommands: RESTPostAPIChatInputApplicationCommandsJSONBody[] = []; diff --git a/src/events/ready.ts b/src/events/ready.ts index e84016295..cf6996d04 100644 --- a/src/events/ready.ts +++ b/src/events/ready.ts @@ -1,5 +1,5 @@ import { Client } from 'discord.js'; -import isDev from '../core/node/isDev.js'; +import isDev from '../core/node/isDev'; export default function ready(client: Client) { console.log( diff --git a/src/renderers/evolution.tsx b/src/renderers/evolution.tsx index 26dec06bd..19d4f85e6 100644 --- a/src/renderers/evolution.tsx +++ b/src/renderers/evolution.tsx @@ -1,21 +1,21 @@ -import * as Graph from '../components/Graph/index.js'; -import NoData, { NoDataType } from '../components/NoData.js'; -import PoweredBy, { PoweredByType } from '../components/PoweredBy.js'; -import TitleBar from '../components/TitleBar.js'; -import Wrapper from '../components/Wrapper.js'; -import { BLITZ_SERVERS } from '../constants/servers.js'; -import getWargamingResponse from '../core/blitz/getWargamingResponse.js'; -import resolveTankName from '../core/blitz/resolveTankName.js'; -import { tankopedia } from '../core/blitz/tankopedia.js'; -import getPlayerHistories from '../core/blitzstars/getPlayerHistories.js'; -import getTankHistories from '../core/blitzstars/getTankHistories.js'; -import { ResolvedPeriod } from '../core/discord/resolvePeriodFromCommand.js'; -import { ResolvedPlayer } from '../core/discord/resolvePlayerFromCommand.js'; -import { WARGAMING_APPLICATION_ID } from '../core/node/arguments.js'; -import { AccountInfo } from '../types/accountInfo.js'; -import { Histories } from '../types/histories.js'; -import { PlayerClanData } from '../types/playerClanData.js'; -import { StatType } from './statsfull.js'; +import * as Graph from '../components/Graph'; +import NoData, { NoDataType } from '../components/NoData'; +import PoweredBy, { PoweredByType } from '../components/PoweredBy'; +import TitleBar from '../components/TitleBar'; +import Wrapper from '../components/Wrapper'; +import { BLITZ_SERVERS } from '../constants/servers'; +import getWargamingResponse from '../core/blitz/getWargamingResponse'; +import resolveTankName from '../core/blitz/resolveTankName'; +import { tankopedia } from '../core/blitz/tankopedia'; +import getPlayerHistories from '../core/blitzstars/getPlayerHistories'; +import getTankHistories from '../core/blitzstars/getTankHistories'; +import { ResolvedPeriod } from '../core/discord/resolvePeriodFromCommand'; +import { ResolvedPlayer } from '../core/discord/resolvePlayerFromCommand'; +import { WARGAMING_APPLICATION_ID } from '../core/node/arguments'; +import { AccountInfo } from '../types/accountInfo'; +import { Histories } from '../types/histories'; +import { PlayerClanData } from '../types/playerClanData'; +import { StatType } from './fullStats'; export default async function evolution( type: Type, @@ -96,6 +96,7 @@ export default async function evolution( {/* goofy ahh bug forces me to call them as functions */} {plot.length > 0 && ( ( +export default async function fullStats( type: Type, { start, end, statsName }: ResolvedPeriod, { server, id }: ResolvedPlayer, @@ -50,13 +50,13 @@ export default async function statsfull( image = (await tankopedia)[tankId!]?.images.normal; } - const tankStats = await getTankStatsDiffed(server, id, start, end); + const { diffed } = await getDiffedTankStats(server, id, start, end); let stats: AllStats; let supplementaryStats: SupplementaryStats; let tierWeights: TierWeightsRecord; if (type === 'player') { - const entries = Object.entries(tankStats); + const entries = Object.entries(diffed); stats = sumStats(entries.map(([, stats]) => stats)); const battlesOfTanksWithAverages = await entries.reduce< PossiblyPromise @@ -124,10 +124,10 @@ export default async function statsfull( {}, ); } else { - stats = tankStats[tankId!]; + stats = diffed[tankId!]; supplementaryStats = { - WN8: calculateWN8((await tankAverages)[tankId!].all, tankStats[tankId!]), + WN8: calculateWN8((await tankAverages)[tankId!].all, diffed[tankId!]), tier: (await tankopedia)[tankId!]?.tier, }; } diff --git a/src/renderers/stats.tsx b/src/renderers/stats.tsx index 6027869e1..9fe5e2629 100644 --- a/src/renderers/stats.tsx +++ b/src/renderers/stats.tsx @@ -1,28 +1,28 @@ -import AllStatsOverview from '../components/AllStatsOverview/index.js'; -import NoData, { NoDataType } from '../components/NoData.js'; -import PoweredBy, { PoweredByType } from '../components/PoweredBy.js'; -import { TierWeightsRecord } from '../components/TierWeights.js'; -import TitleBar from '../components/TitleBar.js'; -import Wrapper from '../components/Wrapper.js'; -import { BLITZ_SERVERS } from '../constants/servers.js'; -import calculateWN8 from '../core/blitz/calculateWN8.js'; -import getWargamingResponse from '../core/blitz/getWargamingResponse.js'; -import resolveTankName from '../core/blitz/resolveTankName.js'; -import sumStats from '../core/blitz/sumStats.js'; -import { Tier, tankopedia } from '../core/blitz/tankopedia.js'; -import getTankStatsDiffed from '../core/blitzstars/getTankStatsDiffed.js'; -import { tankAverages } from '../core/blitzstars/tankAverages.js'; -import { ResolvedPeriod } from '../core/discord/resolvePeriodFromCommand.js'; -import { ResolvedPlayer } from '../core/discord/resolvePlayerFromCommand.js'; -import { WARGAMING_APPLICATION_ID } from '../core/node/arguments.js'; -import { theme } from '../stitches.config.js'; +import AllStatsOverview from '../components/AllStatsOverview'; +import NoData, { NoDataType } from '../components/NoData'; +import PoweredBy, { PoweredByType } from '../components/PoweredBy'; +import { TierWeightsRecord } from '../components/TierWeights'; +import TitleBar from '../components/TitleBar'; +import Wrapper from '../components/Wrapper'; +import { BLITZ_SERVERS } from '../constants/servers'; +import calculateWN8 from '../core/blitz/calculateWN8'; +import getWargamingResponse from '../core/blitz/getWargamingResponse'; +import resolveTankName from '../core/blitz/resolveTankName'; +import sumStats from '../core/blitz/sumStats'; +import { Tier, tankopedia } from '../core/blitz/tankopedia'; +import getDiffedTankStats from '../core/blitzstars/getDiffedTankStats'; +import { tankAverages } from '../core/blitzstars/tankAverages'; +import { ResolvedPeriod } from '../core/discord/resolvePeriodFromCommand'; +import { ResolvedPlayer } from '../core/discord/resolvePlayerFromCommand'; +import { WARGAMING_APPLICATION_ID } from '../core/node/arguments'; +import { theme } from '../stitches.config'; import { AccountInfo, AllStats, SupplementaryStats, -} from '../types/accountInfo.js'; -import { PlayerClanData } from '../types/playerClanData.js'; -import { PossiblyPromise } from '../types/possiblyPromise.js'; +} from '../types/accountInfo'; +import { PlayerClanData } from '../types/playerClanData'; +import { PossiblyPromise } from '../types/possiblyPromise'; export type StatType = 'player' | 'tank'; @@ -52,13 +52,13 @@ export default async function stats( image = (await tankopedia)[tankId!]?.images.normal; } - const tankStats = await getTankStatsDiffed(server, id, start, end); + const { diffed } = await getDiffedTankStats(server, id, start, end); let stats: AllStats | undefined; let supplementaryStats: SupplementaryStats; let tierWeights: TierWeightsRecord; if (type === 'player') { - const entries = Object.entries(tankStats); + const entries = Object.entries(diffed); stats = sumStats(entries.map(([, stats]) => stats)); const battlesOfTanksWithAverages = await entries.reduce< PossiblyPromise @@ -126,11 +126,11 @@ export default async function stats( {}, ); } else { - stats = tankStats[tankId!]; + stats = diffed[tankId!]; supplementaryStats = { WN8: stats - ? calculateWN8((await tankAverages)[tankId!].all, tankStats[tankId!]) + ? calculateWN8((await tankAverages)[tankId!].all, diffed[tankId!]) : undefined, tier: (await tankopedia)[tankId!]?.tier, }; @@ -156,7 +156,7 @@ export default async function stats( ); return naked ? ( - overview + {overview} ) : ( ( @@ -39,7 +43,7 @@ export default async function today({ server, id }: ResolvedPlayer) { }; const allStatsToAccumulate: AllStats[] = []; - Object.entries(tankStatsOverTime).forEach(([, tankStats]) => { + Object.entries(diffed).forEach(([, tankStats]) => { allStatsToAccumulate.push(tankStats); }); Object.entries(careerTankStatsRaw).forEach(([, tankStats]) => { @@ -48,11 +52,11 @@ export default async function today({ server, id }: ResolvedPlayer) { const accumulatedStats = sumStats(allStatsToAccumulate); - if (Object.keys(tankStatsOverTime).length > 0) { - tankStatsOverTime[0] = accumulatedStats; + if (Object.keys(diffed).length > 0) { + diffed[0] = accumulatedStats; } - const tankStatsOverTimeEntries = Object.entries(tankStatsOverTime); + const tankStatsOverTimeEntries = Object.entries(diffed); const todayWN8s = await tankStatsOverTimeEntries.reduce< PossiblyPromise> >(async (accumulator, [tankIdString, tankStats]) => { @@ -85,12 +89,12 @@ export default async function today({ server, id }: ResolvedPlayer) { todayWN8s[0] = todayWN8sEntries.reduce( (accumulator, [tankIdString, wn8]) => - accumulator + wn8 * tankStatsOverTime[Number(tankIdString)].battles, + accumulator + wn8 * diffed[Number(tankIdString)].battles, 0, ) / todayWN8sEntries.reduce( (accumulator, [tankIdString]) => - accumulator + tankStatsOverTime[Number(tankIdString)].battles, + accumulator + diffed[Number(tankIdString)].battles, 0, ); careerWN8s[0] = @@ -110,38 +114,42 @@ export default async function today({ server, id }: ResolvedPlayer) { ); const rows = await Promise.all( - tankStatsOverTimeEntries.map(async ([tankIdString, tankStats]) => { - const tankId = Number(tankIdString); + [ + // this code unreadable on god will forget tomorrow no cap + ...(order.length === 0 ? [] : [0]), + ...(isFinite(limit) ? order.slice(0, limit) : order), + ].map(async (tankId, index) => { + const tankStats = diffed[tankId]; const career = careerStats[tankId]; return ( 4} key={tankId} - name={ - tankId === 0 ? 'Total' : await resolveTankName(Number(tankIdString)) - } + name={tankId === 0 ? 'Total' : await resolveTankName(tankId)} winrate={tankStats.wins / tankStats.battles} careerWinrate={career.wins / career.battles} WN8={isNaN(todayWN8s[tankId]) ? undefined : todayWN8s[tankId]} careerWN8={isNaN(careerWN8s[tankId]) ? undefined : careerWN8s[tankId]} damage={tankStats.damage_dealt / tankStats.battles} careerDamage={career.damage_dealt / career.battles} - survival={tankStats.survived_battles / tankStats.battles} - careerSurvival={career.survived_battles / career.battles} battles={tankStats.battles} careerBattles={career.battles} icon={ - tankId === 0 - ? undefined - : (await tankopedia)[tankIdString as unknown as number]?.images - .normal + tankId === 0 ? undefined : (await tankopedia)[tankId]?.images.normal } /> ); }), ); - return ( + return naked ? ( + + {rows.slice(1)} + + ) : ( ; @@ -20,7 +21,7 @@ export interface RouteRegistry const app = express(); -([statsRoute] as RouteRegistry[]).forEach((registry) => { +([statsRoute, todayRoute] as RouteRegistry[]).forEach((registry) => { if (!(isDev() ? registry.inDevelopment : registry.inProduction)) return; app.get(registry.route, async (req, res) => { diff --git a/src/stitches.config.ts b/src/stitches.config.ts index c7f27bdff..30036ab13 100644 --- a/src/stitches.config.ts +++ b/src/stitches.config.ts @@ -1,9 +1,15 @@ -import { slate, slateDark } from '@radix-ui/colors'; +import { amberDark, blueDark, slate, slateDark } from '@radix-ui/colors'; import { createBorderStyles, createColors } from 'bepaint'; // this isn't really a stitches config, but the object is compliant export const theme = { - colors: createColors(slateDark), + colors: { + ...createColors(slateDark), + ...createColors(slate, 'light'), + + ...createColors(amberDark, 'amber'), + ...createColors(blueDark, 'blue'), + }, + borderStyles: createBorderStyles(slateDark), }; -export const themeLight = { colors: createColors(slate) }; diff --git a/src/types/clanInfo.ts b/src/types/clanInfo.ts index db1f412f6..7ac8e8d5e 100644 --- a/src/types/clanInfo.ts +++ b/src/types/clanInfo.ts @@ -1,28 +1,30 @@ export interface ClanInfo { - [clanId:number]:{recruiting_options: { - vehicles_level: number; - wins_ratio: number; - average_battles_per_day: number; - battles: number; - average_damage: number; + [clanId: number]: { + recruiting_options: { + vehicles_level: number; + wins_ratio: number; + average_battles_per_day: number; + battles: number; + average_damage: number; + }; + members_count: number; + name: string; + creator_name: string; + clan_id: number; + created_at: number; + updated_at: number; + leader_name: string; + members_ids: number[]; + recruiting_policy: string; + tag: string; + is_clan_disbanded: boolean; + old_name: string; + emblem_set_id: number; + creator_id: number; + motto: string; + renamed_at: number; + old_tag: null; + leader_id: number; + description: string; }; - members_count: number; - name: string; - creator_name: string; - clan_id: number; - created_at: number; - updated_at: number; - leader_name: string; - members_ids: number[]; - recruiting_policy: string; - tag: string; - is_clan_disbanded: boolean; - old_name: string; - emblem_set_id: number; - creator_id: number; - motto: string; - renamed_at: number; - old_tag: null; - leader_id: number; - description: string;} } diff --git a/src/types/histories.ts b/src/types/histories.ts index 9258831f7..d2c5ae8be 100644 --- a/src/types/histories.ts +++ b/src/types/histories.ts @@ -1,6 +1,6 @@ // a cleaner simplification of Blitz Stars' histories because it's a mess there -import { AllStats } from './accountInfo.js'; +import { AllStats } from './accountInfo'; export interface History { all: AllStats; diff --git a/src/types/possiblyPromise.ts b/src/types/possiblyPromise.ts index 005f03fa7..a4658c873 100644 --- a/src/types/possiblyPromise.ts +++ b/src/types/possiblyPromise.ts @@ -1 +1 @@ -export type PossiblyPromise = Promise | Type; \ No newline at end of file +export type PossiblyPromise = Promise | Type; diff --git a/src/types/tanksStats.ts b/src/types/tanksStats.ts index 6cce62112..493f2c75f 100644 --- a/src/types/tanksStats.ts +++ b/src/types/tanksStats.ts @@ -1,4 +1,4 @@ -import { AllStats } from './accountInfo.js'; +import { AllStats } from './accountInfo'; export interface IndividualTankStats { all: AllStats; diff --git a/tsconfig.json b/tsconfig.json index d47b9a960..5f2ace03b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,8 +2,7 @@ "compilerOptions": { "target": "ESNext", "module": "ESNext", - // "moduleResolution": "Node", TODO: implement Node module resolution - "moduleResolution": "nodenext", + "moduleResolution": "Node", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, @@ -12,5 +11,8 @@ "resolveJsonModule": true, "jsx": "react-jsx" }, - "include": ["src/**/*.ts", "src/components/AllStatsOverview/components/WN8SurroundingStat.tsx"] + "include": [ + "src/**/*.ts", + "src/components/AllStatsOverview/components/WN8SurroundingStat.tsx" + ] } diff --git a/yarn.lock b/yarn.lock index bcaab4082..e74fb6d32 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1447,6 +1447,11 @@ preact@^10.0.0: resolved "https://registry.yarnpkg.com/preact/-/preact-10.15.1.tgz#a1de60c9fc0c79a522d969c65dcaddc5d994eede" integrity sha512-qs2ansoQEwzNiV5eAcRT1p1EC/dmEzaATVDJNiB3g2sRDWdA7b7MurXdJjB2+/WQktGWZwxvDrnuRFbWuIr64g== +prettier@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.0.0.tgz#e7b19f691245a21d618c68bc54dc06122f6105ae" + integrity sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g== + proxy-addr@~2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"