diff --git a/.github/workflows/assets.yml b/.github/workflows/assets.yml index b4f0e5770..1c197cf22 100644 --- a/.github/workflows/assets.yml +++ b/.github/workflows/assets.yml @@ -64,14 +64,14 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: recursive token: ${{ secrets.GH_TOKEN }} - uses: arduino/setup-protoc@v3 with: repo-token: ${{ secrets.GH_TOKEN }} - - uses: oven-sh/setup-bun@v1 + - uses: oven-sh/setup-bun@v2 - run: bun run setup - name: AppArmor hack diff --git a/.github/workflows/averages.yml b/.github/workflows/averages.yml index 7a0c69162..167edc311 100644 --- a/.github/workflows/averages.yml +++ b/.github/workflows/averages.yml @@ -32,11 +32,11 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: recursive token: ${{ secrets.GH_TOKEN }} - - uses: oven-sh/setup-bun@v1 + - uses: oven-sh/setup-bun@v2 - uses: arduino/setup-protoc@v3 with: repo-token: ${{ secrets.GH_TOKEN }} diff --git a/.github/workflows/build-bot.yml b/.github/workflows/build-bot.yml index 5ed433e66..4fdf182a0 100644 --- a/.github/workflows/build-bot.yml +++ b/.github/workflows/build-bot.yml @@ -14,11 +14,11 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: recursive token: ${{ secrets.GH_TOKEN }} - - uses: oven-sh/setup-bun@v1 + - uses: oven-sh/setup-bun@v2 - uses: arduino/setup-protoc@v3 with: repo-token: ${{ secrets.GH_TOKEN }} @@ -28,7 +28,7 @@ jobs: run: bun run build:bot - name: Archive build - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: bot path: dist/bot diff --git a/.github/workflows/discover-ids.yml b/.github/workflows/discover-ids.yml index 157b80304..3d5d76a9b 100644 --- a/.github/workflows/discover-ids.yml +++ b/.github/workflows/discover-ids.yml @@ -22,11 +22,11 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: recursive token: ${{ secrets.GH_TOKEN }} - - uses: oven-sh/setup-bun@v1 + - uses: oven-sh/setup-bun@v2 - uses: arduino/setup-protoc@v3 with: repo-token: ${{ secrets.GH_TOKEN }} diff --git a/.github/workflows/host-bot.yml b/.github/workflows/host-bot.yml index a369d4811..8503fb8b2 100644 --- a/.github/workflows/host-bot.yml +++ b/.github/workflows/host-bot.yml @@ -42,7 +42,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: oven-sh/setup-bun@v1 + - uses: oven-sh/setup-bun@v2 - uses: arduino/setup-protoc@v3 with: repo-token: ${{ secrets.GH_TOKEN }} diff --git a/.github/workflows/rating-asia.yml b/.github/workflows/rating-asia.yml index b9e7c2df3..1c85cb67b 100644 --- a/.github/workflows/rating-asia.yml +++ b/.github/workflows/rating-asia.yml @@ -32,11 +32,11 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: recursive token: ${{ secrets.GH_TOKEN }} - - uses: oven-sh/setup-bun@v1 + - uses: oven-sh/setup-bun@v2 - uses: arduino/setup-protoc@v3 with: repo-token: ${{ secrets.GH_TOKEN }} diff --git a/.github/workflows/rating-eu.yml b/.github/workflows/rating-eu.yml index 6fc45f045..e8c27e389 100644 --- a/.github/workflows/rating-eu.yml +++ b/.github/workflows/rating-eu.yml @@ -32,11 +32,11 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: recursive token: ${{ secrets.GH_TOKEN }} - - uses: oven-sh/setup-bun@v1 + - uses: oven-sh/setup-bun@v2 - uses: arduino/setup-protoc@v3 with: repo-token: ${{ secrets.GH_TOKEN }} diff --git a/.github/workflows/rating-na.yml b/.github/workflows/rating-na.yml index 4dcf4ce9a..1c3ff2a45 100644 --- a/.github/workflows/rating-na.yml +++ b/.github/workflows/rating-na.yml @@ -32,11 +32,11 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: recursive token: ${{ secrets.GH_TOKEN }} - - uses: oven-sh/setup-bun@v1 + - uses: oven-sh/setup-bun@v2 - uses: arduino/setup-protoc@v3 with: repo-token: ${{ secrets.GH_TOKEN }} diff --git a/.github/workflows/reviews.yml b/.github/workflows/reviews.yml index d3fd70594..efc7bb923 100644 --- a/.github/workflows/reviews.yml +++ b/.github/workflows/reviews.yml @@ -24,11 +24,11 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: recursive token: ${{ secrets.GH_TOKEN }} - - uses: oven-sh/setup-bun@v1 + - uses: oven-sh/setup-bun@v2 - uses: arduino/setup-protoc@v3 with: repo-token: ${{ secrets.GH_TOKEN }} diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml index f39f89bab..4b952f98c 100644 --- a/.github/workflows/website.yml +++ b/.github/workflows/website.yml @@ -38,11 +38,11 @@ jobs: name: Build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: recursive token: ${{ secrets.GH_TOKEN }} - - uses: oven-sh/setup-bun@v1 + - uses: oven-sh/setup-bun@v2 - uses: arduino/setup-protoc@v3 with: repo-token: ${{ secrets.GH_TOKEN }} @@ -53,7 +53,7 @@ jobs: run: bun run build:website - name: Upload artifact - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@v4 with: path: dist/website diff --git a/.vscode/settings.json b/.vscode/settings.json index 6cfc450ba..5b0cbc0bd 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -64,6 +64,7 @@ "deburr", "decalmask", "decompressor", + "décor", "dedup", "defence", "degressive", @@ -95,6 +96,7 @@ "gameplay", "glsl", "GLTF", + "GSOR", "gtag", "haha", "Herdbreaker", diff --git a/docs/guides/christmas-2025.md b/docs/guides/christmas-2025.md new file mode 100644 index 000000000..08cd06d4d --- /dev/null +++ b/docs/guides/christmas-2025.md @@ -0,0 +1,57 @@ +# Christmas Garage Guide 2025 + +This content was pre-recorded on a test server and the version you may see may not be the same. Please see [the official WoTB portal](https://wotblitz.com/) for the most up-to-date information. + +The holiday season has arrived, and this year the celebrations spill directly into your garage, transforming the familiar space into a festive hub filled with activities, rewards, and seasonal flair. Instead of just offering decorations or a visual makeover, this event brings a full set of mechanics that turn everyday battles into a steady stream of progress. Whether you’re aiming for bonuses, collectibles, or new tanks, the garage is designed to make every match feel like part of a larger holiday journey. + +![](https://i.imgur.com/HmbDw0x.jpeg) + +## The Music Box + +The familiar Snow Globes are absent this year, replaced entirely by **Music Boxes**, which act as the new way to try your luck for seasonal rewards. They behave similarly to the Snow Globes and give you a chance to obtain various items, including a shot at the brand-new **[GSOR the TANK](https://blitzkit.app/tanks/gsor-the-tank/)**. But this time around, **they do not guarantee a tank every single time.** But as a bonus, the Music Box guarantees you will not receive duplicate tanks. Once you have a tank, the Music Box will try its best to give you a different one each time. + +![](https://i.imgur.com/sxYV77L.jpeg) + +## Win Battles, Earn Rewards + +Each victory during the event rewards you with **3 Hot Coffees**, a seasonal currency that serves as the backbone of the progression system. These coffees can be exchanged for a variety of Festive Town rewards, giving you reasons to keep pushing through battles even if you’re just casually playing. + +![](https://i.imgur.com/MXGxejz.jpeg) + +With the coffees, you’ll also be able to claim **2 tank certificates**, one for **tier VII** vehicles and another covering **tier IX–X** options. + +The tier VII pool includes the [E 25](https://blitzkit.app/tanks/e-25), [CS-52 LIS](https://blitzkit.app/tanks/cs-52-lis), [AMX 13 57](https://blitzkit.app/tanks/amx-13-57), [Steyr WT](https://blitzkit.app/tanks/steyr-wt), [Kunze Panzer](https://blitzkit.app/tanks/kunze-panzer), and [Super Hellcat](https://blitzkit.app/tanks/super-hellcat). Meanwhile, the tier IX–X certificate gives access to higher-tier picks like the [121B](https://blitzkit.app/tanks/121b), [AMX 30 B](https://blitzkit.app/tanks/amx-30-b), [Obj. 260](https://blitzkit.app/tanks/obj-260), [Char Futur 4](https://blitzkit.app/tanks/char-futur-4), [TL-7-120](https://blitzkit.app/tanks/tl-7-120), and [Object 452K](https://blitzkit.app/tanks/object-452k). + +![](https://i.imgur.com/lxr7PE9.png) + +## Upgrade Your Christmas Tree + +Golden tokens allow you to upgrade your Christmas Tree through **four distinct stages**, each bringing your garage closer to its fully decorated holiday form. + +![](https://i.imgur.com/bcmQE8T.jpeg) + +As the tree grows, additional ornament slots open up, eight in total, which appear on the left side of your screen. These slots give you more room to customize the look of your garage and make the tree feel like something you’ve built up piece by piece over the course of the event. Each new ornament adds to the atmosphere, making the progression feel both cosmetic and rewarding. + +A fully decorated tree grants up to **+25% silver** per battle, turning your regular gameplay into a more profitable experience without requiring extra effort. Ensure all your ornaments come from the same set to unlock the full potential of the tree (all left, all middle, or all right). + +![](https://i.imgur.com/MMbyYLn.jpeg) + +## Upgrade Your Yuletide Stall + +Your golden tokens can also be invested into upgrading the **Yuletide Stall**, another element of the garage that evolves as you level it up. Each upgrade unlocks new rewards, giving you incentives to distribute your tokens across different attractions instead of focusing on only one. The stall begins in a simple state, but after upgrades it transforms visually and functionally, becoming a more vibrant part of your garage. + +![](https://i.imgur.com/AONZN5O.jpeg) + +Each upgrade unlocks a tier worth of rewards that you can claim in exchange for Free XP, Gold, Silver, and more Hot Coffees! + +![](https://i.imgur.com/8fNy0Yj.png) + +## Upgrade Your Shooting Gallery + +Another upgradeable area is the **Shooting Gallery**, which contributes its own set of rewards as it levels up. Enhancing the gallery expands your options for exchanging mastery badges, making those badges more useful during the event than they usually are. + +![](https://i.imgur.com/Rj1WNCE.jpeg) + +![](https://i.imgur.com/GAUd2uR.png) + +Consider yourselves debriefed. Happy holidays! diff --git a/docs/guide/dpm.md b/docs/guides/dpm.md similarity index 100% rename from docs/guide/dpm.md rename to docs/guides/dpm.md diff --git a/docs/guide/embeds.md b/docs/guides/embeds.md similarity index 100% rename from docs/guide/embeds.md rename to docs/guides/embeds.md diff --git a/docs/guide/introduction.md b/docs/guides/introduction.md similarity index 100% rename from docs/guide/introduction.md rename to docs/guides/introduction.md diff --git a/docs/guide/invite.md b/docs/guides/invite.md similarity index 100% rename from docs/guide/invite.md rename to docs/guides/invite.md diff --git a/docs/guide/timezones.md b/docs/guides/timezones.md similarity index 100% rename from docs/guide/timezones.md rename to docs/guides/timezones.md diff --git a/packages/bot/src/commands/ratingLeaderboard.tsx b/packages/bot/src/commands/ratingLeaderboard.tsx index 3a9e8eadc..458e70d52 100644 --- a/packages/bot/src/commands/ratingLeaderboard.tsx +++ b/packages/bot/src/commands/ratingLeaderboard.tsx @@ -52,7 +52,7 @@ export const ratingLeaderboardCommand = new Promise( const seasonNumbers = range( FIRST_MINIMAL_ARCHIVED_RATING_SEASON, latestArchivedSeasonNumber + (onGoingSeason ? 1 : 0) + 1 - ); + ).slice(-25); function addOptions(option: SlashCommandSubcommandBuilder) { const { strings } = translator(Locale.EnglishUS); @@ -196,7 +196,7 @@ export const ratingLeaderboardCommand = new Promise( strings.common.leagues as Record )[name] ), - }) satisfies APIApplicationCommandOptionChoice + } satisfies APIApplicationCommandOptionChoice) ) ) .setRequired(true); @@ -219,8 +219,8 @@ export const ratingLeaderboardCommand = new Promise( const season = seasonOption ? parseInt(seasonOption) : onGoingSeason - ? latestArchivedSeasonNumber + 1 - : latestArchivedSeasonNumber; + ? latestArchivedSeasonNumber + 1 + : latestArchivedSeasonNumber; const isCurrentSeason = season === latestArchivedSeasonNumber + 1; let titleName: string; let titleImage: string | undefined; @@ -258,7 +258,7 @@ export const ratingLeaderboardCommand = new Promise( position: player.number, clan: player.clan_tag, nickname: player.nickname, - }) satisfies SimplifiedPlayer + } satisfies SimplifiedPlayer) ) ) : await getArchivedRatingLeaderboard(region, season!).then( @@ -295,7 +295,7 @@ export const ratingLeaderboardCommand = new Promise( .deleted_player, { id: player.id } ), - }) satisfies SimplifiedPlayer + } satisfies SimplifiedPlayer) ); } ); @@ -349,7 +349,7 @@ export const ratingLeaderboardCommand = new Promise( score: player.score, clan: player.clan_tag, nickname: player.nickname, - }) satisfies SimplifiedPlayer + } satisfies SimplifiedPlayer) ) ) : await getArchivedRatingLeaderboard(region, season!).then( diff --git a/packages/bot/src/core/discord/localizationObject.ts b/packages/bot/src/core/discord/localizationObject.ts index 2decec43e..637a6d604 100644 --- a/packages/bot/src/core/discord/localizationObject.ts +++ b/packages/bot/src/core/discord/localizationObject.ts @@ -19,9 +19,9 @@ export function localizationObject( (!strict || validNameRegex.test(translation)) && translation.length > 0; if (!isValid) { - console.warn( - `Invalid localization for ${locale} (${path}): ${translation}; skipping...` - ); + // console.warn( + // `Invalid localization for ${locale} (${path}): ${translation}; skipping...` + // ); return localizations; } diff --git a/packages/core/src/blitzkit/getArchivedLatestSeasonNumber.ts b/packages/core/src/blitzkit/getArchivedLatestSeasonNumber.ts index 1b9d60c90..460a81f01 100644 --- a/packages/core/src/blitzkit/getArchivedLatestSeasonNumber.ts +++ b/packages/core/src/blitzkit/getArchivedLatestSeasonNumber.ts @@ -17,7 +17,7 @@ export async function getArchivedLatestSeasonNumber() { if (ratingInfo.detail) { const regionsURL = ( (await fetch( - `https://api.github.com/repos/tresabhi/${assertSecret( + `https://api.github.com/repos/${assertSecret( import.meta.env.PUBLIC_ASSET_REPO )}/git/trees/main` ).then((response) => response.json())) as GitHubTrees diff --git a/packages/scripts/src/buildBot.ts b/packages/scripts/src/buildBot.ts index 527fc7292..25641a4d3 100644 --- a/packages/scripts/src/buildBot.ts +++ b/packages/scripts/src/buildBot.ts @@ -2,12 +2,15 @@ import { build } from "esbuild"; import { cp, readdir, rm } from "fs/promises"; import { argv } from "process"; +const dev = argv.includes("--dev"); + const projectDir = "../.."; const srcRoot = `${projectDir}/packages/bot`; const distRoot = `${projectDir}/dist/bot`; -const modulesRoot = `${projectDir}/packages/bot/node_modules`; +const modulesRoot = dev + ? "../../packages/bot/node_modules" + : "../../node_modules"; const workers = await readdir(`${srcRoot}/src/workers`); -const dev = argv.includes("--dev"); await rm(distRoot, { recursive: true, force: true }); diff --git a/packages/website/src/components/ArticlePlug.astro b/packages/website/src/components/ArticlePlug.astro new file mode 100644 index 000000000..4ef838462 --- /dev/null +++ b/packages/website/src/components/ArticlePlug.astro @@ -0,0 +1,38 @@ +--- +import { tankIcon } from "@blitzkit/core"; +import { Flex, Heading, Link, Strong, Text } from "@radix-ui/themes"; +--- + + + + + + + + Christmas 2025 + + Win the GSOR the TANK and more! + + + + + diff --git a/packages/website/src/components/Giveaway.tsx b/packages/website/src/components/Giveaway.tsx new file mode 100644 index 000000000..edd64c67a --- /dev/null +++ b/packages/website/src/components/Giveaway.tsx @@ -0,0 +1,89 @@ +import { Code, Flex, Heading, Link, Text } from "@radix-ui/themes"; +import { useEffect, useState, type ReactNode } from "react"; +import type { MaybeSkeletonComponentProps } from "../types/maybeSkeletonComponentProps"; +import { InlineSkeleton } from "./InlineSkeleton"; + +const TIME = 1764898200 * 1000; + +export function Giveaway({ skeleton }: MaybeSkeletonComponentProps) { + const [timeLeft, setTimeLeft] = useState(TIME - Date.now()); + + useEffect(() => { + const interval = setInterval(() => { + setTimeLeft(TIME - Date.now()); + }, 1000); + + return () => { + clearInterval(interval); + }; + }, []); + + let string: ReactNode; + + if (timeLeft > 0) { + const hours = Math.floor(timeLeft / (1000 * 60 * 60)); + const minutes = Math.floor((timeLeft / (1000 * 60)) % 60) + .toString() + .padStart(2, "0"); + const seconds = Math.floor((timeLeft / 1000) % 60) + .toString() + .padStart(2, "0"); + + string = ( + <> + in{" "} + + {hours}h {minutes}m {seconds}s + + + ); + } else { + string = "has ended. Come again later!"; + } + + return ( + + + + + + + + Music Box giveaway {!skeleton && string}{" "} + {skeleton && } + + + Join the BlitzKit Discord server event + + + + + + ); +} diff --git a/packages/website/src/pages/[...locale]/docs/[directory]/[document]/_index.tsx b/packages/website/src/pages/[...locale]/docs/[directory]/[document]/_index.tsx index 921bf79b2..5c1415000 100644 --- a/packages/website/src/pages/[...locale]/docs/[directory]/[document]/_index.tsx +++ b/packages/website/src/pages/[...locale]/docs/[directory]/[document]/_index.tsx @@ -1,5 +1,5 @@ -import { Markdown } from '../../../../../components/Markdown'; -import { PageWrapper } from '../../../../../components/PageWrapper'; +import { Markdown } from "../../../../../components/Markdown"; +import { PageWrapper } from "../../../../../components/PageWrapper"; interface PageProps { content: string; @@ -7,7 +7,7 @@ interface PageProps { export function Page({ content }: PageProps) { return ( - + {content} ); diff --git a/packages/website/src/pages/[...locale]/index.astro b/packages/website/src/pages/[...locale]/index.astro index f8e4ffbd4..9dba8b0c5 100644 --- a/packages/website/src/pages/[...locale]/index.astro +++ b/packages/website/src/pages/[...locale]/index.astro @@ -6,6 +6,7 @@ import { literals } from "@blitzkit/i18n/src/literals"; import { Box, Flex, Link } from "@radix-ui/themes"; import type { GetStaticPaths } from "astro"; import { AesonPlug } from "../../components/AesonPlug"; +import { Giveaway } from "../../components/Giveaway"; import HomeHero from "../../components/HomeHero.astro"; import { Plugs } from "../../components/Plugs"; import ToolCard from "../../components/ToolCard.astro";