Compare commits

...
220 Commits
Author SHA1 Message Date
TrèsAbhi eed9d23646 fix: use root node modules 2025-12-01 16:58:18 -06:00
TrèsAbhi 2101dba7c6 New Crowdin updates (#184)
* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Russian)
2025-11-28 18:12:19 -06:00
TrèsAbhi 1b398a5f66 fix: initialize Tankopedia with model definitions for improved data handling 2025-11-28 12:55:55 -06:00
TrèsAbhi d8f9a2633d New Crowdin updates (#183)
* New translations en.json (French)

* New translations en.json (Spanish)

* New translations en.json (Japanese)

* New translations en.json (Polish)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (Ukrainian)

* New translations en.json (Chinese Simplified)
2025-11-27 21:53:42 -06:00
TrèsAbhi 2e79ccf7eb feat: update ts-proto executable handling to support multiple locations 2025-11-27 17:59:34 -06:00
TrèsAbhi be778acf39 feat: logs 2025-11-27 17:56:28 -06:00
TrèsAbhi 8d10d7cffa feat: integrate CompareCell component into TankRow and Tanks for improved performance metrics display 2025-11-27 17:48:58 -06:00
TrèsAbhi 5b9041a972 feat: performance tank table occupy more real estate 2025-11-27 17:34:29 -06:00
TrèsAbhi 1e60b1f61f fix: remove unused PerformanceInfo component from performance page 2025-11-27 17:33:44 -06:00
TrèsAbhi 13a636c023 fix: update playerCountPeriod to use "total" value 2025-11-27 17:33:05 -06:00
TrèsAbhi 8bc0693f3b fix: compare table occupy minimum width 2025-11-27 17:31:28 -06:00
TrèsAbhi 9b842c501c feat: update guessing game text for clarity 2025-11-27 17:26:10 -06:00
TrèsAbhi 8aadda4fb8 feat: add ToolOpenGraph component to guess page 2025-11-27 17:25:54 -06:00
TrèsAbhi 58c70f98e1 refactor: remove 'naked' prop from Controls and TankSandbox components 2025-11-27 17:24:12 -06:00
TrèsAbhi 54b73d11ce Merge branch 'main' into dev 2025-11-27 17:16:12 -06:00
TrèsAbhi d1ac5644a4 feat: logs 2025-11-27 14:14:18 -06:00
TrèsAbhi ae35c1516c Merge branch 'main' into fix-lighting 2025-11-27 14:12:38 -06:00
TrèsAbhi fd364d434f feat: hemispehre 2025-11-27 14:10:57 -06:00
TrèsAbhi fd37f72f8c feat: some simplification 2025-11-27 13:54:36 -06:00
TrèsAbhi 1427f61c52 feat: complete transition 2025-11-27 13:42:37 -06:00
TrèsAbhi cd4fa366a0 feat: requestedDisplay 2025-11-27 13:20:06 -06:00
TrèsAbhi 344e7caa81 feat: overhead lighting 2025-11-27 13:05:08 -06:00
TrèsAbhi fe7b2aea02 Add files via upload 2025-11-27 15:57:34 +00:00
TrèsAbhi fced1e96ef feat: remove transition skeleton 2025-11-26 21:10:54 -06:00
TrèsAbhi 778efb2372 feat: transiton and reveal 2025-11-26 21:10:03 -06:00
TrèsAbhi b67fd5b58c feat: new lighting 2025-11-26 20:48:25 -06:00
TrèsAbhi 2559782a28 fix: ground phasing through 2025-11-26 20:25:45 -06:00
TrèsAbhi efb479fece fix: vfs in core 2025-11-26 19:27:58 -06:00
TrèsAbhi fb829d4b81 Merge branch 'main' into dev 2025-11-26 19:22:48 -06:00
TrèsAbhi 91f4f2edcf Comment out unchanged change log statement
Comment out the log statement for unchanged changes.
2025-11-26 18:23:25 -06:00
TrèsAbhi a692d1ee0b Merge branch 'main' into dev 2025-11-26 13:58:44 -06:00
TrèsAbhi 31d2ab4746 feat: workflow 2025-11-26 13:44:45 -06:00
TrèsAbhi e4e2c30748 feat: logs 2025-11-26 13:42:43 -06:00
TrèsAbhi d633874d90 fix: path normalization 2025-11-26 13:38:40 -06:00
TrèsAbhi 9cb1d0cb3f fix: read dvpl files 2025-11-26 13:25:46 -06:00
TrèsAbhi a0bccc7973 feat: update all 2025-11-26 13:18:44 -06:00
TrèsAbhi 3efe807e35 feat: migrate definitiosn 2025-11-26 13:07:53 -06:00
TrèsAbhi 1cc15f64b0 feat: augment 2025-11-26 12:58:19 -06:00
TrèsAbhi f70864a0db feat: vfs file 2025-11-26 12:46:03 -06:00
TrèsAbhi 5de40b220f feat: rename 2025-11-26 12:34:26 -06:00
TrèsAbhi bd19c9aac9 Merge branch 'main' into fix-mangler 2025-11-26 12:28:32 -06:00
TrèsAbhi 6bc5b7dcb6 feat: .well-known dir 2025-11-26 12:22:15 -06:00
TrèsAbhi 00795eecdd feat: sort 2025-11-25 22:21:58 -06:00
TrèsAbhi f4e7b2ed98 feat: normalize path 2025-11-25 22:20:46 -06:00
TrèsAbhi 81612793e3 feat: manifest storing 2025-11-25 22:16:39 -06:00
TrèsAbhi d3bdbc39d2 feat: unified abstraction 2025-11-25 22:14:09 -06:00
TrèsAbhi 7cb67bd44b feat: steam client 2025-11-25 21:37:31 -06:00
TrèsAbhi 9df09c9a53 rm nm 2025-11-24 13:01:07 -06:00
TrèsAbhi a9f1d9ebec disable patches 2025-11-24 12:58:36 -06:00
TrèsAbhi 1b834eb848 just remove it 2025-11-24 12:46:32 -06:00
TrèsAbhi c5518ec9bc hide others 2025-11-24 12:45:39 -06:00
TrèsAbhi 8c705d7e2e root reserve 2025-11-24 12:45:05 -06:00
TrèsAbhi efe9f91a2d hide lvm 2025-11-24 12:44:33 -06:00
TrèsAbhi e84bf6b31c str 2025-11-24 12:44:01 -06:00
TrèsAbhi 46234b6cbb disable all 2025-11-24 12:43:02 -06:00
TrèsAbhi 0bda743a91 feat: reintroduce 2025-11-24 12:38:50 -06:00
TrèsAbhi b19866a1f5 feat: remove maximize-build-space 2025-11-24 12:35:45 -06:00
TrèsAbhi 9ca329e53f feat: start vfs 2025-11-24 11:02:18 -06:00
TrèsAbhi 47b1eb9ab0 feat: always use max build space 2025-11-20 12:45:57 -06:00
TrèsAbhi 6c8fbf7245 fix: use package node modules 2025-11-13 12:53:19 -06:00
TrèsAbhi 2ea22512d4 feat: new protos 2025-11-13 12:47:51 -06:00
TrèsAbhi 697d1fa1b4 Merge branch 'main' into dev 2025-11-13 12:40:59 -06:00
TrèsAbhi e4429f15bd feat: moar!! 2025-11-12 14:18:54 -06:00
TrèsAbhi cd8083ae30 feat: more patchnotes 2025-11-12 14:10:02 -06:00
TrèsAbhi 9dcf7441c9 feat: start of patch notes 2025-11-12 13:50:17 -06:00
TrèsAbhi d4e1f99ce2 fix: mk dir 2025-11-12 11:00:02 -06:00
TrèsAbhi 98de246578 mooooooove 2025-11-12 10:24:38 -06:00
TrèsAbhi 45097f198c debug 2025-11-12 10:23:22 -06:00
TrèsAbhi e063719caa debug 2025-11-12 10:22:27 -06:00
TrèsAbhi 975d579d21 New translations en.json (Russian) (#181) 2025-11-07 16:39:13 -06:00
TrèsAbhi b963ef0981 fix: name 2025-11-06 09:54:06 -06:00
TrèsAbhi 49fab21113 fix: use relative script 2025-11-06 09:52:47 -06:00
TrèsAbhi cbde48d41b fix: PUBLIC_ASSET_BASE 2025-11-06 09:40:11 -06:00
TrèsAbhi 1210baa545 fix: env type 2025-11-06 09:39:27 -06:00
TrèsAbhi 2c03b530d2 feat: group array 2025-11-06 09:37:14 -06:00
TrèsAbhi ec977899fb New translations en.json (Russian) (#180) 2025-11-05 14:44:13 -06:00
TrèsAbhi e5fc2faad7 Update youtubers.ts 2025-11-05 14:43:21 -06:00
TrèsAbhi 541dfc186b New Crowdin updates (#179)
* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)
2025-11-03 09:45:21 -06:00
TrèsAbhi 182471c4d6 feat: buildCatalogItemAccessor 2025-11-02 18:10:11 -06:00
TrèsAbhi e5c66cf232 Update isHalloween.ts 2025-10-31 15:07:27 -05:00
TrèsAbhi fbbc0b82b4 Update Lighting.tsx 2025-10-31 08:06:11 -05:00
TrèsAbhi aecc2adb07 feat: generalize colored delta cells 2025-10-26 22:01:42 -05:00
TrèsAbhi f8ee2b64a6 feat: simplify ownership and test filters 2025-10-26 21:49:48 -05:00
TrèsAbhi 7c4e28bd09 fix: show remove played even when logged out 2025-10-26 21:43:23 -05:00
TrèsAbhi 80d61d9e04 fix: fallback 2025-10-26 21:19:42 -05:00
TrèsAbhi 0c22ac3a97 fix: step title 2025-10-26 21:06:38 -05:00
TrèsAbhi efad6e61f7 Update assets.yml 2025-10-23 22:15:56 -05:00
TrèsAbhi 3563aaecd5 fix: move depots 2025-10-23 21:49:54 -05:00
TrèsAbhi 5294de848f fix: manual flag 2025-10-23 21:29:44 -05:00
TrèsAbhi e82aa56cb2 feat: logs 2025-10-23 21:28:27 -05:00
TrèsAbhi e1b2b130a5 feat: hide uploader 2025-10-23 21:26:51 -05:00
TrèsAbhi 28f22f1d40 fix: include missing module 2025-10-23 21:22:34 -05:00
TrèsAbhi a3bb023309 feat: migrate to asset uploader 2025-10-23 21:22:21 -05:00
TrèsAbhi 797307fb81 feat: asset uploader 2025-10-23 20:29:30 -05:00
TrèsAbhi f9802b44ff feat: update readme 2025-10-23 19:48:23 -05:00
TrèsAbhi e26b9c78a3 feat: remove unnecessary submodules 2025-10-23 19:46:31 -05:00
TrèsAbhi 925ae1a0b7 fix: type 2025-10-23 19:37:14 -05:00
TrèsAbhi 9584ecf38b feat: logs 2025-10-23 19:36:15 -05:00
TrèsAbhi 338168d7e4 remove url echos 2025-10-23 15:36:28 -05:00
TrèsAbhi 711d92887e fix: naming 2025-10-23 15:27:50 -05:00
TrèsAbhi 78160041e8 feat: parallel 2025-10-23 15:18:12 -05:00
TrèsAbhi 1b884a52f9 name 2025-10-23 15:09:54 -05:00
TrèsAbhi f13c658ef4 no spaced 2025-10-23 15:09:19 -05:00
TrèsAbhi b72d4058be feat: unarchive 2025-10-23 15:01:18 -05:00
TrèsAbhi 029a781c50 fix: hardcode app path 2025-10-23 10:31:26 -05:00
TrèsAbhi adc1791f56 feat: game archive mounting 2025-10-23 10:30:21 -05:00
TrèsAbhi 15b6ff43c5 feat: handle different asset repors 2025-10-23 10:01:17 -05:00
TrèsAbhi e2c02f3d86 fix: GH_TOKEN 2025-10-22 22:10:16 -05:00
TrèsAbhi 3ce47f6ed0 fix: token 2025-10-22 20:54:18 -05:00
TrèsAbhi fa91b37d92 Merge branch 'main' of https://github.com/blitzkit/blitzkit 2025-10-22 20:51:29 -05:00
TrèsAbhi 108644bd44 fix: protoc 2025-10-22 20:51:27 -05:00
TrèsAbhi 413436376b New translations en.json (Russian) (#178) 2025-10-22 20:49:39 -05:00
TrèsAbhi 7d118015d0 fix: merge commands 2025-10-22 19:17:09 -05:00
TrèsAbhi 92e7390a5d fix: merge commands 2025-10-22 19:16:07 -05:00
TrèsAbhi 475e25a0d8 fix: prisma client 2025-10-22 18:57:03 -05:00
TrèsAbhi 17115af0d8 fix: protos 2025-10-22 18:52:45 -05:00
TrèsAbhi a9894ade78 fix: prisma, protoc 2025-10-22 18:47:34 -05:00
TrèsAbhi f5b09873a9 feat: logs 2025-10-22 18:45:13 -05:00
TrèsAbhi ac768d0897 fix: the last of the errors 2025-10-22 18:42:51 -05:00
TrèsAbhi f802b997c0 feat: cleanup 2025-10-22 18:27:37 -05:00
TrèsAbhi db94edea69 fix: even more locale props 2025-10-22 17:55:17 -05:00
TrèsAbhi 0809c9ee0c fix: some locale props 2025-10-22 17:52:01 -05:00
TrèsAbhi 0326e8d1d7 Merge branch 'main' into fix-modules 2025-10-22 17:31:21 -05:00
TrèsAbhi 912c1fb125 no message 2025-10-20 22:13:14 -05:00
TrèsAbhi d07383a722 New Crowdin updates (#176)
* New translations en.json (Russian)

* New translations en.json (Russian)
2025-10-17 12:08:12 -05:00
TrèsAbhi 582e608203 fix: @bufbuild/protobuf 2025-10-15 18:26:14 -05:00
TrèsAbhi 501a618d5f Merge branch 'main' into fix-modules 2025-10-15 18:17:21 -05:00
TrèsAbhi 7c7f34d899 Merge branch 'main' of https://github.com/blitzkit/blitzkit 2025-10-15 18:14:22 -05:00
TrèsAbhi 864d34f344 feat: remove checked 2025-10-15 18:14:20 -05:00
TrèsAbhi 9201fc72ce New Crowdin updates (#175)
* New translations en.json (French)

* New translations en.json (Spanish)

* New translations en.json (Japanese)

* New translations en.json (Polish)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (Ukrainian)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)
2025-10-15 09:02:02 -05:00
TrèsAbhi 50f82d68b4 feat: logs 2025-10-14 22:34:11 -05:00
TrèsAbhi b81f664379 Merge branch 'main' into dev 2025-10-14 22:28:12 -05:00
TrèsAbhi 389707d0c8 fix: keys 2025-10-14 22:28:00 -05:00
TrèsAbhi 9fbd623d91 feat: enable playlist 2025-10-14 22:27:12 -05:00
TrèsAbhi f36df9e902 feat: landing page for mobile 2025-10-14 22:26:13 -05:00
TrèsAbhi 437bbfdc89 feat: large warning 2025-10-14 22:21:22 -05:00
TrèsAbhi 7becb7b6ad fix: modify only when needed 2025-10-14 22:06:01 -05:00
TrèsAbhi 94e8a07f90 feat: hide playlist 2025-10-14 22:03:51 -05:00
TrèsAbhi 4ae5283ac4 feat: landing page cleanup 2025-10-14 22:02:47 -05:00
TrèsAbhi ade238966b feat: some cleanup 2025-10-14 21:53:58 -05:00
TrèsAbhi ea048da7a2 feat: tracking 2025-10-14 21:36:37 -05:00
TrèsAbhi 263f292a59 feat: shuffle button 2025-10-14 20:19:52 -05:00
TrèsAbhi ff4a87e5b8 perf: reactive updates 2025-10-14 20:15:00 -05:00
TrèsAbhi 222b690fba feat: action buttons 2025-10-14 12:19:28 -05:00
TrèsAbhi e1a6e4e38d feat: spotify like table 2025-10-14 12:11:31 -05:00
TrèsAbhi 37adc04cda feat: drag handles 2025-10-14 11:32:43 -05:00
TrèsAbhi 3fc7279f0e feat: main package 2025-10-13 23:22:58 -05:00
TrèsAbhi bd852b9368 feat: some more type prefixes 2025-10-13 23:20:41 -05:00
TrèsAbhi 26304fc281 feat: some more type prefixes 2025-10-13 23:20:26 -05:00
TrèsAbhi 0d8cb5c1ae feat: some type prefixes 2025-10-13 23:15:32 -05:00
TrèsAbhi dc60cffdff i made things worse 2025-10-13 23:10:48 -05:00
TrèsAbhi a3e74de277 fix: missing percentage 2025-10-13 22:55:24 -05:00
TrèsAbhi 49b5d4f7dc fix: script imports 2025-10-13 22:26:11 -05:00
TrèsAbhi 2c55455cd7 fix: types and lodash-es 2025-10-13 16:59:31 -05:00
TrèsAbhi 648ac57424 fix: build proxy client 2025-10-13 16:58:16 -05:00
TrèsAbhi 5e3cf816ee fix: build protos in workflows 2025-10-13 16:54:32 -05:00
TrèsAbhi 98ea42c7b0 fix: protos 2025-10-13 16:53:37 -05:00
TrèsAbhi 873c8a54d3 feat: start fixing modules 2025-10-13 16:48:04 -05:00
TrèsAbhi 927495ca16 New translations en.json (Chinese Simplified) (#174) 2025-10-13 16:41:50 -05:00
TrèsAbhi cc957466fc feat: action button 2025-10-12 23:10:09 -05:00
TrèsAbhi ef173758aa feat: better landing screen 2025-10-12 22:48:14 -05:00
TrèsAbhi bfceacdf3c feat: playlist table 2025-10-12 19:37:44 -05:00
TrèsAbhi ed0dde37d2 feat: playlist 2025-10-12 18:51:28 -05:00
TrèsAbhi 5ba64f231a fix: trimmer pagewrapper min height 2025-10-12 18:20:00 -05:00
TrèsAbhi 1f06b1fef4 fix: checkout 2025-10-12 18:00:51 -05:00
TrèsAbhi 7b0625fc8d fix: @protos path 2025-10-12 17:57:49 -05:00
TrèsAbhi 5dce234447 fix: dvp 2025-10-12 14:12:47 -05:00
TrèsAbhi 0f609717d0 adjacent 2025-10-12 13:40:32 -05:00
TrèsAbhi 3958709348 fix: adjacent submodule 2025-10-12 13:39:03 -05:00
TrèsAbhi 2d869c9fac Remove submodule submodules/blitzkit-closed 2025-10-12 13:36:15 -05:00
TrèsAbhi 82afaf9494 fix: lz4js 2025-10-12 13:24:03 -05:00
TrèsAbhi b1d82e624a fix: crc 2025-10-12 13:12:10 -05:00
TrèsAbhi 9b60d56a36 fix: immer 2025-10-12 12:26:19 -05:00
TrèsAbhi ee0d4f6996 fix: varuna 2025-10-12 17:22:26 +00:00
TrèsAbhi 2d43408822 fix: remove closed 2025-10-12 17:21:18 +00:00
TrèsAbhi 868f132105 fix: core and closed dep 2025-10-12 17:20:33 +00:00
TrèsAbhi ce428e093b fix: i18n dep 2025-10-12 17:18:39 +00:00
TrèsAbhi ab430dc0ca fix: bot imports 2025-10-12 17:17:05 +00:00
TrèsAbhi 03a51c47bb fix: self core imports 2025-10-12 15:05:58 +00:00
TrèsAbhi 1790443698 feat: rename 2025-10-12 09:51:21 -05:00
TrèsAbhi a60f95340c fix: deps 2025-10-12 09:49:31 -05:00
TrèsAbhi e407463ca7 New Crowdin updates (#173) 2025-10-12 09:47:24 -05:00
TrèsAbhi 61cc15aafc Update package.json 2025-10-12 09:38:02 -05:00
TrèsAbhi 1a947a6300 feat: logs 2025-10-11 21:54:11 -05:00
TrèsAbhi 75262647f7 Merge branch 'main' into dev 2025-10-11 21:50:24 -05:00
TrèsAbhi 9914371dd8 fix: tier list filters 2025-10-11 21:49:20 -05:00
TrèsAbhi a4e48a2feb fix: performance filters 2025-10-11 21:45:18 -05:00
TrèsAbhi 4ab03e56a2 feat: support transparency 2025-10-11 21:22:48 -05:00
TrèsAbhi 06a3791ddd fix: settings title 2025-10-11 20:51:12 -05:00
TrèsAbhi 36cce4b0c1 fix: make settings page responsive again 2025-10-11 20:48:33 -05:00
TrèsAbhi 158141fdc7 Update index.ts 2025-10-06 20:58:45 -05:00
TrèsAbhi 1d319551e7 Update literals.ts 2025-10-06 18:24:48 -05:00
TrèsAbhi 0552ac790e Update assets.yml 2025-10-06 11:10:39 -05:00
TrèsAbhi 5ad7566874 fix: show controls in all display modes 2025-10-05 18:34:03 -05:00
TrèsAbhi 638f178fbf fix: trimmer heading 2025-10-05 18:31:07 -05:00
TrèsAbhi 1e996cb970 New Crowdin updates (#172) 2025-10-04 22:15:25 -05:00
TrèsAbhi 8639b31b8f feat: logs 2025-10-04 22:13:50 -05:00
TrèsAbhi 889a92e98b fix: lighting toggler no longer fires on unload 2025-10-04 22:05:36 -05:00
TrèsAbhi e7fe3a4baf fix: signs of tankiopedia transofmr controls 2025-10-04 22:02:24 -05:00
TrèsAbhi ab10343704 fix: clearer controls of tankoepdia 2025-10-04 21:53:18 -05:00
TrèsAbhi 9412be2d7c feat: filter owned tanks 2025-10-04 21:35:12 -05:00
TrèsAbhi 66cf590f1c fix: hook call outside parent hook 2025-10-04 21:33:28 -05:00
TrèsAbhi 7c18549c00 fix: full screen table 2025-10-02 10:39:36 -05:00
TrèsAbhi 9b97d3e27d feat: guesser flag icons 2025-10-02 10:29:43 -05:00
TrèsAbhi aadc252e47 fix: use new quicklime controls enabled 2025-10-02 10:19:50 -05:00
TrèsAbhi 320a764d45 feat: migrate all 2025-10-02 10:15:38 -05:00
TrèsAbhi 5594c0ec3b feat: some progress 2025-10-01 22:45:21 -05:00
TrèsAbhi 3d69d464f5 feat: start on amending slugs 2025-10-01 22:41:23 -05:00
TrèsAbhi dd0b2bade7 feat: named slugs 2025-10-01 22:41:15 -05:00
TrèsAbhi 99c90cfa9e Merge branch 'dev' of https://github.com/blitzkit/blitzkit into dev 2025-10-01 22:24:10 -05:00
TrèsAbhi dd3ce21508 feat: gun rotation 2025-10-01 22:24:08 -05:00
TrèsAbhi b512cb2292 Update README.md 2025-09-30 20:22:06 -05:00
TrèsAbhi 907e71ce03 feat: turret rotation 2025-09-30 16:48:37 -05:00
TrèsAbhi 10942879a6 feat: start of rotation 2025-09-30 16:34:08 -05:00
TrèsAbhi d60551d30a fix: change blitzkit icons to big icons 2025-09-29 13:41:52 -05:00
TrèsAbhi b8c8528e1c Merge branch 'main' into dev 2025-09-29 13:39:13 -05:00
TrèsAbhi 0c9643016a feat: logs 2025-09-29 13:37:00 -05:00
TrèsAbhi 61d1ebbad6 fix: redirect 2025-09-29 13:35:47 -05:00
TrèsAbhi e9b7059cc0 feat: some linking 2025-09-29 13:30:10 -05:00
TrèsAbhi a359897a2a New Crowdin updates (#171) 2025-09-29 13:29:58 -05:00
372 changed files with 13475 additions and 6934 deletions

No files matched your search

+26 -98
View File
@@ -1,4 +1,4 @@
name: Build Assets
name: Assets
on:
push:
@@ -6,30 +6,30 @@ on:
workflow_dispatch:
inputs:
cdn_branch:
asset_repo:
type: choice
required: false
description: What asset repo to write to
options:
- blitzkit/assets
- blitzkit/assets-preview
- blitzkit/assets-dev
default: blitzkit/assets
archive_url:
type: string
required: false
default: main
description: What CDN branch to write to
zip_url:
type: string
required: false
description: ZIP URL to download instead of Steam client
zip_root:
type: string
required: false
description: Root dir of ZIP which houses the executable and Data dir
description: Archive URL to download instead of Steam client
schedule:
- cron: '0 2 * * *'
- cron: "0 2 * * *"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event.inputs.cdn_branch }}
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event.inputs.asset_repo }}
cancel-in-progress: true
env:
DISCORD_CLIENT_ID: ${{ secrets.DISCORD_CLIENT_ID }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
VIDEOS_SHEET: ${{ secrets.VIDEOS_SHEET }}
@@ -37,8 +37,8 @@ env:
WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }}
WOTB_DLC_CDN: ${{ secrets.WOTB_DLC_CDN }}
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
PUBLIC_ASSET_REPO: blitzkit/assets
PUBLIC_ASSET_BRANCH: main
PUBLIC_ASSET_REPO: ${{ github.event.inputs.asset_repo != '' && github.event.inputs.asset_repo || secrets.PUBLIC_ASSET_REPO }}
PUBLIC_ASSET_BRANCH: ${{ secrets.PUBLIC_ASSET_BRANCH }}
WOTB_GLOSSARY_DOMAIN: ${{ secrets.WOTB_GLOSSARY_DOMAIN }}
WOTB_GLOSSARY_PATH: ${{ secrets.WOTB_GLOSSARY_PATH }}
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
@@ -54,100 +54,28 @@ env:
PUBLIC_ASSET_FAST_USER: blitzkit
PUBLIC_ASSET_FAST_REPO: assets
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
STEAM_USERNAME: ${{ secrets.STEAM_USERNAME }}
STEAM_PASSWORD: ${{ secrets.STEAM_PASSWORD }}
jobs:
download_client:
name: Download client
runs-on: ubuntu-latest
steps:
- name: (ZIP) Maximize build space
if: github.event.inputs.zip_url != ''
uses: easimon/maximize-build-space@master
with:
root-reserve-mb: 512
swap-size-mb: 1024
remove-dotnet: 'true'
- uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ secrets.GH_TOKEN }}
- name: (Steam) Setup bun
uses: oven-sh/setup-bun@v1
if: github.event.inputs.zip_url == ''
- name: (Steam) Install dependencies
if: github.event.inputs.zip_url == ''
run: bun install
- name: (Steam) Setup DepotDownloader
if: github.event.inputs.zip_url == ''
run: bun run setup:depotdownloader
- name: (Steam) Download client
if: github.event.inputs.zip_url == ''
run: |
chmod +x ./temp/depotDownloader/DepotDownloader
./temp/depotDownloader/DepotDownloader -q -os windows -app 444200 -username ${{ secrets.DEPOT_DOWNLOADER_USERNAME }} -password "${{ secrets.DEPOT_DOWNLOADER_PASSWORD }}"
- name: (Steam) Apply patches
if: github.event.inputs.zip_url == ''
run: bun setup:patches --depot
- name: (ZIP) Download client
if: github.event.inputs.zip_url != ''
env:
ZIP_URL: ${{ github.event.inputs.zip_url }}
ZIP_ROOT: ${{ github.event.inputs.zip_root }}
run: |
wget -q -O client.zip $ZIP_URL
unzip -q client.zip -d client
rm client.zip
mkdir -p depots/444202/0
mv client/$ZIP_ROOT/* depots/444202/0
- name: Archive depots
uses: actions/upload-artifact@v4
with:
name: depots
path: depots
build_assets:
name: Build assets
runs-on: ubuntu-latest
needs: download_client
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ secrets.GH_TOKEN }}
- name: Setup bun
uses: oven-sh/setup-bun@v1
- name: Install dependencies
run: bun install
- name: Lint
run: bun run lint:website
- name: Download depots
uses: actions/download-artifact@v4
- uses: arduino/setup-protoc@v3
with:
name: depots
path: depots
repo-token: ${{ secrets.GH_TOKEN }}
- uses: oven-sh/setup-bun@v1
- run: bun run setup
- name: AppArmor hack
run: echo 0 | sudo tee /proc/sys/kernel/apparmor_restrict_unprivileged_userns
- name: Start server and build assets
run: |
bun dev:website --verbosepa &
SERVER_PID=$!
bun run build:assets --all-targets --depot --production
kill $SERVER_PID || true
- name: Build assets
run: bun run build:assets --all-targets --manual
+11 -6
View File
@@ -3,7 +3,7 @@ name: Averages
on:
workflow_dispatch:
schedule:
- cron: '0 8 * * *'
- cron: "0 8 * * *"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -15,7 +15,7 @@ env:
PUBLIC_ASSET_FAST_USER: ${{ secrets.PUBLIC_ASSET_FAST_USER }}
PUBLIC_ASSET_FAST_REPO: ${{ secrets.PUBLIC_ASSET_FAST_REPO }}
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
ALLOW_ROBOTS: 'true'
ALLOW_ROBOTS: "true"
WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }}
PUBLIC_GOOGLE_TAG_MANAGER_ID: ${{ secrets.PUBLIC_GOOGLE_TAG_MANAGER_ID }}
PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ${{ secrets.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID }}
@@ -25,6 +25,7 @@ env:
PUBLIC_DISCORD_SERVER_ID: ${{ secrets.PUBLIC_DISCORD_SERVER_ID }}
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
PUBLIC_BRANCH: ${{ secrets.PUBLIC_BRANCH }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
jobs:
build:
@@ -32,16 +33,20 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ secrets.GH_TOKEN }}
- uses: oven-sh/setup-bun@v1
- name: Install dependencies
run: bun install
- uses: arduino/setup-protoc@v3
with:
repo-token: ${{ secrets.GH_TOKEN }}
- run: bun run setup
- name: Build averages
env:
PUBLIC_ASSET_BRANCH: main
DISCORD_CLIENT_ID: ${{ secrets.DISCORD_CLIENT_ID }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
VIDEOS_SHEET: ${{ secrets.VIDEOS_SHEET }}
+8 -7
View File
@@ -1,4 +1,4 @@
name: Build Bot
name: Bot
on:
push:
@@ -15,13 +15,14 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ secrets.GH_TOKEN }}
- uses: oven-sh/setup-bun@v1
- name: Install dependencies
run: bun install
- name: Build Prisma
run: bun run build:prisma
- uses: arduino/setup-protoc@v3
with:
repo-token: ${{ secrets.GH_TOKEN }}
- run: bun run setup
- name: Build bot
run: bun run build:bot
+10 -6
View File
@@ -1,9 +1,9 @@
name: Discover IDs
name: IDs
on:
workflow_dispatch:
schedule:
- cron: '0 0 * * 1'
- cron: "0 0 * * 1"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -11,11 +11,11 @@ concurrency:
env:
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
PUBLIC_ASSET_BRANCH: ${{ secrets.PUBLIC_ASSET_BRANCH }}
PUBLIC_ASSET_REPO: ${{ secrets.PUBLIC_ASSET_REPO }}
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
jobs:
build:
@@ -23,10 +23,14 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ secrets.GH_TOKEN }}
- uses: oven-sh/setup-bun@v1
- name: Install dependencies
run: bun install
- uses: arduino/setup-protoc@v3
with:
repo-token: ${{ secrets.GH_TOKEN }}
- run: bun run setup
- name: Discover IDs
run: bun build:ids --production
+6 -4
View File
@@ -1,10 +1,10 @@
name: Host Bot
name: Host
on:
push:
branches: main
schedule:
- cron: '0 */5 * * *'
- cron: "0 */5 * * *"
workflow_dispatch:
release:
types: [created, published, released, edited]
@@ -35,6 +35,7 @@ env:
PUBLIC_BRANCH: ${{ secrets.PUBLIC_BRANCH }}
PUBLIC_GUIDE_WEBSITE: ${{ secrets.PUBLIC_GUIDE_WEBSITE }}
SERVICING: ${{ secrets.SERVICING }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
jobs:
host:
@@ -42,8 +43,9 @@ jobs:
steps:
- uses: oven-sh/setup-bun@v1
# with:
# bun-version: 1.1.33
- uses: arduino/setup-protoc@v3
with:
repo-token: ${{ secrets.GH_TOKEN }}
- name: Download release assets
run: |
+30 -25
View File
@@ -3,7 +3,29 @@ name: Rating Asia
on:
workflow_dispatch:
schedule:
- cron: '0 17 * * *'
- cron: "0 17 * * *"
env:
PUBLIC_ASSET_BRANCH: main
PUBLIC_ASSET_REPO: ${{ secrets.PUBLIC_ASSET_REPO }}
DISCORD_CLIENT_ID: ${{ secrets.DISCORD_CLIENT_ID }}
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
PUBLIC_ASSET_FAST_USER: ${{ secrets.PUBLIC_ASSET_FAST_USER }}
PUBLIC_ASSET_FAST_REPO: ${{ secrets.PUBLIC_ASSET_FAST_REPO }}
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
ALLOW_ROBOTS: "true"
WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }}
PUBLIC_GOOGLE_TAG_MANAGER_ID: ${{ secrets.PUBLIC_GOOGLE_TAG_MANAGER_ID }}
PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ${{ secrets.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID }}
GOOGLE_APPLICATION_CREDENTIALS: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }}
WOTB_GLOSSARY_DOMAIN: ${{ secrets.WOTB_GLOSSARY_DOMAIN }}
WOTB_GLOSSARY_PATH: ${{ secrets.WOTB_GLOSSARY_PATH }}
PUBLIC_DISCORD_SERVER_ID: ${{ secrets.PUBLIC_DISCORD_SERVER_ID }}
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
PUBLIC_BRANCH: ${{ secrets.PUBLIC_BRANCH }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
jobs:
host:
@@ -11,31 +33,14 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ secrets.GH_TOKEN }}
- uses: oven-sh/setup-bun@v1
- name: Install dependencies
run: bun install
- uses: arduino/setup-protoc@v3
with:
repo-token: ${{ secrets.GH_TOKEN }}
- run: bun run setup
- name: Generate leaderboard
env:
PUBLIC_ASSET_BRANCH: main
PUBLIC_ASSET_REPO: ${{ secrets.PUBLIC_ASSET_REPO }}
DISCORD_CLIENT_ID: ${{ secrets.DISCORD_CLIENT_ID }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
PUBLIC_ASSET_FAST_USER: ${{ secrets.PUBLIC_ASSET_FAST_USER }}
PUBLIC_ASSET_FAST_REPO: ${{ secrets.PUBLIC_ASSET_FAST_REPO }}
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
ALLOW_ROBOTS: 'true'
WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }}
PUBLIC_GOOGLE_TAG_MANAGER_ID: ${{ secrets.PUBLIC_GOOGLE_TAG_MANAGER_ID }}
PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ${{ secrets.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID }}
GOOGLE_APPLICATION_CREDENTIALS: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }}
WOTB_GLOSSARY_DOMAIN: ${{ secrets.WOTB_GLOSSARY_DOMAIN }}
WOTB_GLOSSARY_PATH: ${{ secrets.WOTB_GLOSSARY_PATH }}
PUBLIC_DISCORD_SERVER_ID: ${{ secrets.PUBLIC_DISCORD_SERVER_ID }}
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
PUBLIC_BRANCH: ${{ secrets.PUBLIC_BRANCH }}
run: bun build:ratingleaderboard --region=asia
+30 -25
View File
@@ -3,7 +3,29 @@ name: Rating Europe
on:
workflow_dispatch:
schedule:
- cron: '0 23 * * *'
- cron: "0 23 * * *"
env:
PUBLIC_ASSET_BRANCH: main
PUBLIC_ASSET_REPO: ${{ secrets.PUBLIC_ASSET_REPO }}
DISCORD_CLIENT_ID: ${{ secrets.DISCORD_CLIENT_ID }}
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
PUBLIC_ASSET_FAST_USER: ${{ secrets.PUBLIC_ASSET_FAST_USER }}
PUBLIC_ASSET_FAST_REPO: ${{ secrets.PUBLIC_ASSET_FAST_REPO }}
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
ALLOW_ROBOTS: "true"
WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }}
PUBLIC_GOOGLE_TAG_MANAGER_ID: ${{ secrets.PUBLIC_GOOGLE_TAG_MANAGER_ID }}
PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ${{ secrets.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID }}
GOOGLE_APPLICATION_CREDENTIALS: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }}
WOTB_GLOSSARY_DOMAIN: ${{ secrets.WOTB_GLOSSARY_DOMAIN }}
WOTB_GLOSSARY_PATH: ${{ secrets.WOTB_GLOSSARY_PATH }}
PUBLIC_DISCORD_SERVER_ID: ${{ secrets.PUBLIC_DISCORD_SERVER_ID }}
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
PUBLIC_BRANCH: ${{ secrets.PUBLIC_BRANCH }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
jobs:
host:
@@ -11,31 +33,14 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ secrets.GH_TOKEN }}
- uses: oven-sh/setup-bun@v1
- name: Install dependencies
run: bun install
- uses: arduino/setup-protoc@v3
with:
repo-token: ${{ secrets.GH_TOKEN }}
- run: bun run setup
- name: Generate leaderboard
env:
PUBLIC_ASSET_BRANCH: main
PUBLIC_ASSET_REPO: ${{ secrets.PUBLIC_ASSET_REPO }}
DISCORD_CLIENT_ID: ${{ secrets.DISCORD_CLIENT_ID }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
PUBLIC_ASSET_FAST_USER: ${{ secrets.PUBLIC_ASSET_FAST_USER }}
PUBLIC_ASSET_FAST_REPO: ${{ secrets.PUBLIC_ASSET_FAST_REPO }}
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
ALLOW_ROBOTS: 'true'
WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }}
PUBLIC_GOOGLE_TAG_MANAGER_ID: ${{ secrets.PUBLIC_GOOGLE_TAG_MANAGER_ID }}
PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ${{ secrets.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID }}
GOOGLE_APPLICATION_CREDENTIALS: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }}
WOTB_GLOSSARY_DOMAIN: ${{ secrets.WOTB_GLOSSARY_DOMAIN }}
WOTB_GLOSSARY_PATH: ${{ secrets.WOTB_GLOSSARY_PATH }}
PUBLIC_DISCORD_SERVER_ID: ${{ secrets.PUBLIC_DISCORD_SERVER_ID }}
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
PUBLIC_BRANCH: ${{ secrets.PUBLIC_BRANCH }}
run: bun build:ratingleaderboard --region=eu
+30 -25
View File
@@ -3,7 +3,29 @@ name: Rating North America
on:
workflow_dispatch:
schedule:
- cron: '0 5 * * *'
- cron: "0 5 * * *"
env:
PUBLIC_ASSET_BRANCH: main
PUBLIC_ASSET_REPO: ${{ secrets.PUBLIC_ASSET_REPO }}
DISCORD_CLIENT_ID: ${{ secrets.DISCORD_CLIENT_ID }}
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
PUBLIC_ASSET_FAST_USER: ${{ secrets.PUBLIC_ASSET_FAST_USER }}
PUBLIC_ASSET_FAST_REPO: ${{ secrets.PUBLIC_ASSET_FAST_REPO }}
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
ALLOW_ROBOTS: "true"
WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }}
PUBLIC_GOOGLE_TAG_MANAGER_ID: ${{ secrets.PUBLIC_GOOGLE_TAG_MANAGER_ID }}
PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ${{ secrets.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID }}
GOOGLE_APPLICATION_CREDENTIALS: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }}
WOTB_GLOSSARY_DOMAIN: ${{ secrets.WOTB_GLOSSARY_DOMAIN }}
WOTB_GLOSSARY_PATH: ${{ secrets.WOTB_GLOSSARY_PATH }}
PUBLIC_DISCORD_SERVER_ID: ${{ secrets.PUBLIC_DISCORD_SERVER_ID }}
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
PUBLIC_BRANCH: ${{ secrets.PUBLIC_BRANCH }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
jobs:
host:
@@ -11,31 +33,14 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ secrets.GH_TOKEN }}
- uses: oven-sh/setup-bun@v1
- name: Install dependencies
run: bun install
- uses: arduino/setup-protoc@v3
with:
repo-token: ${{ secrets.GH_TOKEN }}
- run: bun run setup
- name: Generate leaderboard
env:
PUBLIC_ASSET_BRANCH: main
PUBLIC_ASSET_REPO: ${{ secrets.PUBLIC_ASSET_REPO }}
DISCORD_CLIENT_ID: ${{ secrets.DISCORD_CLIENT_ID }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
PUBLIC_ASSET_FAST_USER: ${{ secrets.PUBLIC_ASSET_FAST_USER }}
PUBLIC_ASSET_FAST_REPO: ${{ secrets.PUBLIC_ASSET_FAST_REPO }}
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
ALLOW_ROBOTS: 'true'
WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }}
PUBLIC_GOOGLE_TAG_MANAGER_ID: ${{ secrets.PUBLIC_GOOGLE_TAG_MANAGER_ID }}
PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ${{ secrets.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID }}
GOOGLE_APPLICATION_CREDENTIALS: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }}
WOTB_GLOSSARY_DOMAIN: ${{ secrets.WOTB_GLOSSARY_DOMAIN }}
WOTB_GLOSSARY_PATH: ${{ secrets.WOTB_GLOSSARY_PATH }}
PUBLIC_DISCORD_SERVER_ID: ${{ secrets.PUBLIC_DISCORD_SERVER_ID }}
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
PUBLIC_BRANCH: ${{ secrets.PUBLIC_BRANCH }}
run: bun build:ratingleaderboard --region=com
+7 -6
View File
@@ -11,12 +11,13 @@ concurrency:
env:
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
PUBLIC_ASSET_BRANCH: ${{ secrets.PUBLIC_ASSET_BRANCH }}
PUBLIC_ASSET_REPO: ${{ secrets.PUBLIC_ASSET_REPO }}
GOOGLE_APPLICATION_CREDENTIALS: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }}
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
jobs:
build:
@@ -27,11 +28,11 @@ jobs:
with:
submodules: recursive
token: ${{ secrets.GH_TOKEN }}
- uses: oven-sh/setup-bun@v1
- name: Install dependencies
run: bun install
- uses: arduino/setup-protoc@v3
with:
repo-token: ${{ secrets.GH_TOKEN }}
- run: bun run setup
- name: Find reviews
run: bun packages/scripts/src/reviews.ts
run: bun run build:reviews
+6 -5
View File
@@ -5,7 +5,7 @@ on:
workflow_dispatch:
schedule:
- cron: '0 4 * * *'
- cron: "0 4 * * *"
permissions:
contents: read
@@ -31,6 +31,7 @@ env:
PUBLIC_BRANCH: ${{ secrets.PUBLIC_BRANCH }}
PUBLIC_GUIDE_WEBSITE: ${{ secrets.PUBLIC_GUIDE_WEBSITE }}
SERVICING: ${{ secrets.SERVICING }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
jobs:
build:
@@ -41,12 +42,12 @@ jobs:
with:
submodules: recursive
token: ${{ secrets.GH_TOKEN }}
- uses: oven-sh/setup-bun@v1
- uses: arduino/setup-protoc@v3
with:
repo-token: ${{ secrets.GH_TOKEN }}
- uses: actions/configure-pages@v5
- name: Install dependencies
run: bun install
- run: bun run setup
- name: Build website
run: bun run build:website
+25 -1
View File
@@ -28,4 +28,28 @@ target
.vercel
/src
/packages/bot/src/prisma
dev-dist
dev-dist
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
+3 -9
View File
@@ -1,9 +1,3 @@
[submodule "submodules/blitzkit-closed"]
path = submodules/blitzkit-closed
url = https://github.com/tresabhi/blitzkit-closed
[submodule "submodules/CUE4Parse"]
path = submodules/CUE4Parse
url = https://github.com/FabianFG/CUE4Parse
[submodule "submodules/blitzkit-internal"]
path = submodules/blitzkit-internal
url = https://github.com/blitzkit/internal
[submodule "packages/closed"]
path = packages/closed
url = https://github.com/blitzkit/closed.git
+18
View File
@@ -11,6 +11,7 @@
"analyticsreporting",
"APAC",
"apcr",
"appinfo",
"applypatches",
"artefacts",
"astrojs",
@@ -34,12 +35,14 @@
"blitzkit",
"blitzrinth",
"blitzstars",
"bufbuild",
"cacheable",
"Callouts",
"Camo",
"Carro",
"CCPAOPTOUT",
"CDON",
"clanid",
"classname",
"cleandist",
"closedkrieg",
@@ -62,8 +65,10 @@
"deburr",
"decalmask",
"decompressor",
"dedup",
"defence",
"degressive",
"depotdownloader",
"devkrieg",
"diacriticless",
"DIDS",
@@ -74,6 +79,7 @@
"DVPLCRC",
"dvpm",
"dxgi",
"easimon",
"echarts",
"Everytime",
"FASTNAME",
@@ -86,11 +92,13 @@
"Fullscreen",
"fullstats",
"fuzzysort",
"gamemode",
"gameplay",
"glsl",
"GLTF",
"gtag",
"haha",
"Herdbreaker",
"highp",
"iife",
"imgur",
@@ -104,6 +112,7 @@
"leaderboards",
"lerp",
"LINEARSIZE",
"linktarget",
"lmao",
"loadouts",
"loddist",
@@ -126,6 +135,7 @@
"NEGATIVEX",
"NEGATIVEY",
"NEGATIVEZ",
"networkidle",
"NITROPAY",
"nivo",
"nmatname",
@@ -136,11 +146,13 @@
"Oopsy",
"opentest",
"OPTML",
"otemp",
"outdir",
"outfile",
"ownedtanks",
"padder",
"partytown",
"patchnotes",
"PIXELFORMAT",
"playerachievements",
"playerinfo",
@@ -153,11 +165,15 @@
"Promisable",
"proto",
"protobuf",
"protoc",
"protos",
"proxyclient",
"prunestrings",
"Pyogenics",
"ratingleaderboard",
"raycaster",
"Redirector",
"Relogin",
"researchable",
"Resvg",
"rizz",
@@ -206,12 +222,14 @@
"Unregistering",
"unrotate",
"unwrapper",
"userns",
"vercel",
"vickers",
"vitepress",
"Vovko",
"wargaming",
"webp",
"wgpkg",
"whitespaces",
"WHSS",
"winrate",
+10 -10
View File
@@ -1,8 +1,10 @@
# BlitzKit
Tools for everything and anything World of Tanks Blitz.
Tools for everything and anything, World of Tanks Blitz.
This GitHub repository contains the code for BlitzKit and all its services. You may need to checkout sensitive closed-source submodules in order to locally develop some aspects of BlitzKit. Communicate with `@tresabhi` on Discord to get started or talk about sensitive content like environment variables or submodules.
This GitHub repository contains the code for BlitzKit and all its services. If you are here, looking to use BlitzKit, go to https://blitzkit.app/.
If you intend to contribute to the code, note that you may need to check out sensitive, closed-source submodules to locally develop certain aspects of BlitzKit. Communicate with `@tresabhi` on Discord to get started or discuss sensitive content, such as environment variables or submodules.
## Packages
@@ -13,23 +15,21 @@ This GitHub repository contains the code for BlitzKit and all its services. You
| [packages/core](packages/core) | | | Mutual internal code shared between all other packages. |
| [packages/i18n](packages/i18n) | | [Crowdin](https://crowdin.com/project/blitzkrieg) | Translations served as JSON shared across all other packages, including some utility code. |
| [packages/scripts](packages/scripts) | | | CLI utilities for the development and deployment of BlitzKit services. |
| [packages/varuna](packages/varuna) | Varuna | [NPM](https://www.npmjs.com/package/varuna) | React and Astro oriented state management library, awaiting separation from BlitzKit. Unavailable from NPM at this time. |
| [packages/website](packages/website) | BlitzKit | https://blitzkit.app | The BlitzKit website providing a wide range of tools, APIs, and interconnecting all other services. |
| [packages/varuna](packages/varuna) | Varuna | [NPM](https://www.npmjs.com/package/varuna) | React and Astro-oriented state management library, awaiting separation from BlitzKit. Unavailable from NPM at this time. |
| [packages/website](packages/website) | BlitzKit | https://blitzkit.app | The BlitzKit website provides a wide range of tools, APIs, and interconnects all other services. |
## Submodules
| Submodule | Title | Source | Memo |
| ------------------------------------------------------------ | ----------- | ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [submodules/blitzkit-closed](submodules/blitzkit-closed) | ClosedKit | [blitzkit/closed](https://github.com/blitzkit/closed) | Closed source, sensitive code hidden from the public to prevent bad actors from abusing the game. Discuss with `@tresabhi` on Discord for **special permission** to access. |
| [submodules/blitzkit-internal](submodules/blitzkit-internal) | InternalKit | [blitzkit/internal](https://github.com/blitzkit/internal) | Closed source, sensitive internal documentation hidden from the public to prevent bad actors from abusing the game. Access to this submodule is **not negotiable**. |
| [submodules/CUE4Parse](submodules/CUE4Parse) | CUE4Parse | [FabianFG/CUE4Parse](https://github.com/FabianFG/CUE4Parse) | An Unreal Engine archives and packages parsing library in C#. |
| Submodule | Title | Source | Memo |
| ------------------------------------------------------ | --------- | ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [submodules/blitzkit-closed](packages/blitzkit-closed) | ClosedKit | [blitzkit/closed](https://github.com/blitzkit/closed) | Closed source, sensitive code hidden from the public to prevent bad actors from abusing the game. Discuss with `@tresabhi` on Discord for **special permission** to access. |
## Environment Variables
| Name | Source | BlitzKit Default | Memo |
| ------------------------------------------------------------- | -------------------------------------------------------------------------------- | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `PUBLIC_WARGAMING_APPLICATION_ID` | [Wargaming Developer's Room](https://developers.wargaming.net/) | | Application ID needed to communicate with Wargaming APIs. |
| `PUBLIC_PROMOTE_OPENTEST` | | `false` | Optional boolean dictating wether the website advertises [the opentest subdomain of BlitzKit](https://opentest.blitzkit.app/). |
| `PUBLIC_PROMOTE_OPENTEST` | | `false` | Optional boolean dictating whether the website advertises [the opentest subdomain of BlitzKit](https://opentest.blitzkit.app/). |
| `ALLOW_ROBOTS` | | `true` | Toggles between `Allow: /` and `Disallow: /` in the website's `/robots.txt`. This is usually `false` for all BlitzKit's subdomains. |
| `WOTB_GLOSSARY`, `WOTB_GLOSSARY_DOMAIN`, `WOTB_GLOSSARY_PATH` | | | |
| `PUBLIC_GOOGLE_TAG_MANAGER_ID` | [Google Tag Manager](https://tagmanager.google.com/) | `G-GL2JVHCGPQ` | Google Tag Manager's unique identifier for analytical purposes. |
+271 -331
View File
File diff suppressed because it is too large. Load diff
+5
View File
@@ -0,0 +1,5 @@
# BlitzKit 1.28.2
## Fixes
- BlitzKit no longer tries to redirect translated pages to the same page, over and over again
+18
View File
@@ -0,0 +1,18 @@
# BlitzKit 1.28.3
## Features
- Turrets and guns can now be rotated in Mixer without any pitch or yaw limits
- Added nation icons to the search results for Guesser
- The table now occupies the whole screen in Compare
- Added the ability to log in with a Wargaming account in the navbar
- Added owned/unowned filters in the Tankopedia filters
- Revamped Tankopedia quick controls to be more accessible and obvious
## Fixes
- Missing tank icons across the website have been fixed
- Events of starting and stopping of the transform tanks now use an asynchronous event instead of a reactive state, making them faster
- Removed a hook call on root context, speeding up load times
- Tankopedia's pitch input now follows the same sign convention as the pitch and yaw visualizer
- Shadows are no longer disabled when the scene is unloaded when the user scrolls away from the canvas
+10
View File
@@ -0,0 +1,10 @@
# BlitzKit 1.28.4
## Fixes
- The tank names in the Tankopedia sandbox now have less margin from the navbar
- Controls in the Tankopedia sandbox are now shown in all modes, except for the advanced highlighting button which only appears in the dynamic armor mode
- The settings page now reacts to input again
- The settings page now has a tab title and description for search engines
- Added support for transparent textures, fixing tanks like the Herdbreaker and the WZ-132-1
- The tank filters in the Performance and Tier-List tools work once again
+12
View File
@@ -0,0 +1,12 @@
# BlitzKit 1.29.0
## Features
- Added the Playlist tool
- Don't know what to play today? Let BlitzKit do the thinking!
- Navigate to [the tool on BlitzKit](https://blitzkit.app/playlist)
- Make sure you login with your Wargaming account to unlock all features like live tracking and filtering by owned tanks
- Use filters to pick tiers, nations, etc. then hit Generate playlist
- BlitzKit will generate a checklist for you to go through as you play
- Don't like the order of tanks? Hit Shuffle, Sort by Last played and Least played, or straight up remove tanks from the list by using the (-) button
- If you're logged in, BlitzKit will automatically check off tanks as you play them
+5
View File
@@ -0,0 +1,5 @@
# BlitzKit 1.29.1
## Technical Changes
- Recreated the full dependency tree for BlitzKit
+5
View File
@@ -0,0 +1,5 @@
# BlitzKit 1.29.2
## Technical Changes
- Implemented new workflow capabilities to deploy BlitzKit OpenTest and Preview remotely, from any device
+5
View File
@@ -0,0 +1,5 @@
# BlitzKit 1.29.3
## Technical Changes
- Added a significantly more memory and network efficient AssetUploader abstraction
+5
View File
@@ -0,0 +1,5 @@
# BlitzKit 1.29.4
## Technical Changes
- BlitzKit now partially downloads game files instead of the entire game, allowing for faster updates
+11
View File
@@ -0,0 +1,11 @@
# BlitzKit 1.29.5
## Features
- Added new lighting to be a bit softer on tanks
- Added a button with the sun icon to toggle high graphics
- Added a new reveal and transition animation between tabs
## Fixes
- Track wheels on some tanks no longer look translucent
+18
View File
@@ -0,0 +1,18 @@
# BlitzKit 1.29.6
## Features
- Simplified the ownership and test tank filters in the tank search
- Renamed "Guess the tank" to simply "Guess"
- The player count value in the Performance tool is now set to Total by default
- Added Compare-like red/green highlighting to the Performance tool
## Changes
- Removed the last updated info bar in Performance for some more screen real estate
## Fixes
- The remove played tanks in the Playlist tool now shows even when logged out
- The Guess game now has a page title
- The compare table now occupies the minimum width possible instead of expanding all columns excessively
+16 -30
View File
@@ -1,23 +1,18 @@
{
"name": "blitzkit",
"version": "1.29.6",
"license": "BlitzKit License",
"private": true,
"version": "1.28.1",
"description": "🎉 Tools for everything World of Tanks Blitz",
"type": "module",
"types": "./types.d.ts",
"workspaces": [
"packages/*"
],
"scripts": {
"build:assets": "cd packages/scripts && bun run build:assets",
"build:averages": "bun --filter=@blitzkit/scripts run build:averages",
"build:bot": "cd packages/scripts && bun run build:bot",
"build:website": "bun build:prisma --no-hints && cd packages/website && bun run build",
"build:website": "cd packages/website && bun run build",
"build:prisma": "prisma generate",
"build:sitemap": "bun scripts/sitemap.ts",
"build:docs": "vitepress build docs",
"build:proxyclient": "cd packages/scripts && bun run build:proxyclient",
"build:catalogitemaccesssor": "cd packages/scripts && bun run build:catalogitemaccesssor",
"build:ratingleaderboard": "cd packages/scripts && bun run build:ratingleaderboard",
"build:app": "cd packages/app && bun run build",
"dev:bot": "bun run build:bot --dev && bun start:bot",
"start:bot": "bun dist/bot/main.js",
"dev:docs": "vitepress dev docs",
@@ -35,30 +30,21 @@
"update": "yarn upgrade --latest",
"tunnel": "cloudflared tunnel --url http://localhost:3000",
"discoverids": "scripts/discoverIds.ts",
"build:protos": "bun packages/scripts/src/protos.ts",
"build:protos": "cd packages/scripts && bun run build:protos",
"preview:website": "cd packages/website && bun run preview",
"build:reviews": "cd packages/scripts && bun build:reviews",
"build:ids": "cd packages/scripts && bun build:ids",
"prunestrings": "cd packages/scripts && bun prunestrings"
"prunestrings": "cd packages/scripts && bun prunestrings",
"setup": "bun install && bun run build:prisma && bun run build:protos && bun run build:proxyclient",
"patchnotes": "cd packages/scripts && bun run patchnotes"
},
"license": "MIT",
"type": "module",
"workspaces": [
"packages/*"
],
"dependencies": {
"@types/xmpp__client": "^0.13.3",
"@xmpp/client": "^0.13.6",
"astro-favicons": "^3.1.5",
"cheerio": "^1.1.2",
"googleapis": "^160.0.0",
"lodash-es": "^4.17.21",
"protobufjs": "^7.5.4",
"quicklime": "^1.2.0",
"sharp": "^0.34.4"
},
"devDependencies": {
"@types/bun": "^1.2.22",
"@types/lodash-es": "^4.17.12",
"@types/node": "^24.5.2",
"prettier": "^3.6.2",
"prisma": "^6.16.2",
"typescript": "^5.9.2"
"@prisma/client": "^6.18.0",
"prisma": "^6.18.0",
"typescript": "^5.9.3"
}
}
-24
View File
@@ -1,24 +0,0 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
-43
View File
@@ -1,43 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="stylesheet" href="/src/styles.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Tauri App</title>
<script type="module" src="/src/main.ts" defer></script>
</head>
<body>
<main class="container">
<h1>Welcome to Tauri</h1>
<div class="row">
<a href="https://vite.dev" target="_blank">
<img src="/src/assets/vite.svg" class="logo vite" alt="Vite logo" />
</a>
<a href="https://tauri.app" target="_blank">
<img
src="/src/assets/tauri.svg"
class="logo tauri"
alt="Tauri logo"
/>
</a>
<a href="https://www.typescriptlang.org/docs" target="_blank">
<img
src="/src/assets/typescript.svg"
class="logo typescript"
alt="typescript logo"
/>
</a>
</div>
<p>Click on the Tauri logo to learn more about the framework</p>
<form class="row" id="greet-form">
<input id="greet-input" placeholder="Enter a name..." />
<button type="submit">Greet</button>
</form>
<p id="greet-msg"></p>
</main>
</body>
</html>
+5286
View File
File diff suppressed because it is too large. Load diff
+23 -2
View File
@@ -1,6 +1,27 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use std::process::{Command, Stdio};
use tauri::Manager;
fn main() {
blitzkit_lib::run()
tauri::Builder::default()
.setup(|app| {
let app_handle = app.handle();
let entry = app_handle
.path_resolver()
.resolve_resource("../../dist/website/server/entry.mjs")
.expect("failed to resolve server.ts");
Command::new("bun")
.arg("run")
.arg(entry)
.stderr(Stdio::null())
.spawn()
.expect("failed to start Bun server");
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running Tauri app");
}
+9 -7
View File
@@ -4,18 +4,20 @@
"version": "0.1.0",
"identifier": "com.tresabhi.blitzkit",
"build": {
"beforeDevCommand": "bun run dev",
"devUrl": "http://localhost:1420",
"beforeBuildCommand": "bun run build",
"frontendDist": "../dist"
"beforeDevCommand": "cd ../website && bun run dev",
"_beforeBuildCommand": "cd ../website && bun run build",
"frontendDist": "../../../dist/website/client",
"devUrl": "http://localhost:4321"
},
"app": {
"withGlobalTauri": true,
"windows": [
{
"title": "blitzkit",
"width": 800,
"height": 600
"url": "http://localhost:4321",
"title": "BlitzKit",
"fullscreen": false,
"resizable": true,
"maximized": true
}
],
"security": {
-6
View File
@@ -1,6 +0,0 @@
<svg width="206" height="231" viewBox="0 0 206 231" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M143.143 84C143.143 96.1503 133.293 106 121.143 106C108.992 106 99.1426 96.1503 99.1426 84C99.1426 71.8497 108.992 62 121.143 62C133.293 62 143.143 71.8497 143.143 84Z" fill="#FFC131"/>
<ellipse cx="84.1426" cy="147" rx="22" ry="22" transform="rotate(180 84.1426 147)" fill="#24C8DB"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M166.738 154.548C157.86 160.286 148.023 164.269 137.757 166.341C139.858 160.282 141 153.774 141 147C141 144.543 140.85 142.121 140.558 139.743C144.975 138.204 149.215 136.139 153.183 133.575C162.73 127.404 170.292 118.608 174.961 108.244C179.63 97.8797 181.207 86.3876 179.502 75.1487C177.798 63.9098 172.884 53.4021 165.352 44.8883C157.82 36.3744 147.99 30.2165 137.042 27.1546C126.095 24.0926 114.496 24.2568 103.64 27.6274C92.7839 30.998 83.1319 37.4317 75.8437 46.1553C74.9102 47.2727 74.0206 48.4216 73.176 49.5993C61.9292 50.8488 51.0363 54.0318 40.9629 58.9556C44.2417 48.4586 49.5653 38.6591 56.679 30.1442C67.0505 17.7298 80.7861 8.57426 96.2354 3.77762C111.685 -1.01901 128.19 -1.25267 143.769 3.10474C159.348 7.46215 173.337 16.2252 184.056 28.3411C194.775 40.457 201.767 55.4101 204.193 71.404C206.619 87.3978 204.374 103.752 197.73 118.501C191.086 133.25 180.324 145.767 166.738 154.548ZM41.9631 74.275L62.5557 76.8042C63.0459 72.813 63.9401 68.9018 65.2138 65.1274C57.0465 67.0016 49.2088 70.087 41.9631 74.275Z" fill="#FFC131"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M38.4045 76.4519C47.3493 70.6709 57.2677 66.6712 67.6171 64.6132C65.2774 70.9669 64 77.8343 64 85.0001C64 87.1434 64.1143 89.26 64.3371 91.3442C60.0093 92.8732 55.8533 94.9092 51.9599 97.4256C42.4128 103.596 34.8505 112.392 30.1816 122.756C25.5126 133.12 23.9357 144.612 25.6403 155.851C27.3449 167.09 32.2584 177.598 39.7906 186.112C47.3227 194.626 57.153 200.784 68.1003 203.846C79.0476 206.907 90.6462 206.743 101.502 203.373C112.359 200.002 122.011 193.568 129.299 184.845C130.237 183.722 131.131 182.567 131.979 181.383C143.235 180.114 154.132 176.91 164.205 171.962C160.929 182.49 155.596 192.319 148.464 200.856C138.092 213.27 124.357 222.426 108.907 227.222C93.458 232.019 76.9524 232.253 61.3736 227.895C45.7948 223.538 31.8055 214.775 21.0867 202.659C10.3679 190.543 3.37557 175.59 0.949823 159.596C-1.47592 143.602 0.768139 127.248 7.41237 112.499C14.0566 97.7497 24.8183 85.2327 38.4045 76.4519ZM163.062 156.711L163.062 156.711C162.954 156.773 162.846 156.835 162.738 156.897C162.846 156.835 162.954 156.773 163.062 156.711Z" fill="#24C8DB"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

-25
View File
@@ -1,25 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
fill="#2D79C7" stroke="none">
<path d="M430 5109 c-130 -19 -248 -88 -325 -191 -53 -71 -83 -147 -96 -247
-6 -49 -9 -813 -7 -2166 l3 -2090 22 -65 c54 -159 170 -273 328 -323 l70 -22
2140 0 2140 0 66 23 c160 55 272 169 322 327 l22 70 0 2135 0 2135 -22 70
c-49 157 -155 265 -319 327 l-59 23 -2115 1 c-1163 1 -2140 -2 -2170 -7z
m3931 -2383 c48 -9 120 -26 160 -39 l74 -23 3 -237 c1 -130 0 -237 -2 -237 -3
0 -26 14 -53 30 -61 38 -197 84 -310 106 -110 20 -293 15 -368 -12 -111 -39
-175 -110 -175 -193 0 -110 97 -197 335 -300 140 -61 309 -146 375 -189 30
-20 87 -68 126 -107 119 -117 164 -234 164 -426 0 -310 -145 -518 -430 -613
-131 -43 -248 -59 -445 -60 -243 -1 -405 24 -577 90 l-68 26 0 242 c0 175 -3
245 -12 254 -9 9 -9 12 0 12 7 0 12 -4 12 -9 0 -17 139 -102 223 -138 136 -57
233 -77 382 -76 145 0 224 19 295 68 75 52 100 156 59 242 -41 84 -135 148
-374 253 -367 161 -522 300 -581 520 -23 86 -23 253 -1 337 73 275 312 448
682 492 109 13 401 6 506 -13z m-1391 -241 l0 -205 -320 0 -320 0 0 -915 0
-915 -255 0 -255 0 0 915 0 915 -320 0 -320 0 0 205 0 205 895 0 895 0 0 -205z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

-22
View File
@@ -1,22 +0,0 @@
import { invoke } from "@tauri-apps/api/core";
let greetInputEl: HTMLInputElement | null;
let greetMsgEl: HTMLElement | null;
async function greet() {
if (greetMsgEl && greetInputEl) {
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
greetMsgEl.textContent = await invoke("greet", {
name: greetInputEl.value,
});
}
}
window.addEventListener("DOMContentLoaded", () => {
greetInputEl = document.querySelector("#greet-input");
greetMsgEl = document.querySelector("#greet-msg");
document.querySelector("#greet-form")?.addEventListener("submit", (e) => {
e.preventDefault();
greet();
});
});
-116
View File
@@ -1,116 +0,0 @@
.logo.vite:hover {
filter: drop-shadow(0 0 2em #747bff);
}
.logo.typescript:hover {
filter: drop-shadow(0 0 2em #2d79c7);
}
:root {
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
font-size: 16px;
line-height: 24px;
font-weight: 400;
color: #0f0f0f;
background-color: #f6f6f6;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}
.container {
margin: 0;
padding-top: 10vh;
display: flex;
flex-direction: column;
justify-content: center;
text-align: center;
}
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: 0.75s;
}
.logo.tauri:hover {
filter: drop-shadow(0 0 2em #24c8db);
}
.row {
display: flex;
justify-content: center;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
h1 {
text-align: center;
}
input,
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
color: #0f0f0f;
background-color: #ffffff;
transition: border-color 0.25s;
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2);
}
button {
cursor: pointer;
}
button:hover {
border-color: #396cd8;
}
button:active {
border-color: #396cd8;
background-color: #e8e8e8;
}
input,
button {
outline: none;
}
#greet-input {
margin-right: 5px;
}
@media (prefers-color-scheme: dark) {
:root {
color: #f6f6f6;
background-color: #2f2f2f;
}
a:hover {
color: #24c8db;
}
input,
button {
color: #ffffff;
background-color: #0f0f0f98;
}
button:active {
background-color: #0f0f0f69;
}
}
+1 -2
View File
@@ -15,6 +15,5 @@
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"]
}
}
-30
View File
@@ -1,30 +0,0 @@
import { defineConfig } from "vite";
// @ts-expect-error process is a nodejs global
const host = process.env.TAURI_DEV_HOST;
// https://vite.dev/config/
export default defineConfig(async () => ({
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
//
// 1. prevent Vite from obscuring rust errors
clearScreen: false,
// 2. tauri expects a fixed port, fail if that port is not available
server: {
port: 1420,
strictPort: true,
host: host || false,
hmr: host
? {
protocol: "ws",
host,
port: 1421,
}
: undefined,
watch: {
// 3. tell Vite to ignore watching `src-tauri`
ignored: ["**/src-tauri/**"],
},
},
}));
+20 -12
View File
@@ -1,26 +1,34 @@
{
"name": "@blitzkit/bot",
"private": true,
"type": "module",
"types": "src/types.d.ts",
"dependencies": {
"@blitzkit/core": "workspace:*",
"@blitzkit/i18n": "workspace:*",
"satori": "^0.18.3",
"@resvg/resvg-js": "^2.6.2",
"discord.js": "^14.22.1",
"react": "^19.1.1",
"@radix-ui/colors": "^3.0.0",
"fuzzysort": "^3.1.0",
"@radix-ui/themes": "^3.2.1",
"@resvg/resvg-js": "^2.6.2",
"@types/adm-zip": "^0.5.7",
"@types/lodash-es": "^4.17.12",
"@types/markdown-escape": "^1.1.3",
"@types/react": "^19.2.2",
"adm-zip": "^0.5.16",
"bepaint": "^0.1.0-beta.3",
"discord.js": "^14.22.1",
"dotenv": "^17.2.2",
"markdown-escape": "^2.0.0"
"fuzzysort": "^3.1.0",
"lodash-es": "^4.17.21",
"markdown-escape": "^2.0.0",
"pickleparser": "^0.2.1",
"prisma": "^6.17.1",
"protobufjs": "^7.5.4",
"react": "^19.2.0",
"satori": "^0.18.3",
"sharp": "^0.34.4"
},
"private": true,
"scripts": {
"dev": "bun --watch src/main.ts",
"start": "bun src/main.ts"
},
"devDependencies": {
"@types/markdown-escape": "^1.1.3"
}
"type": "module",
"types": "src/types.d.ts"
}
+22 -19
View File
@@ -1,28 +1,31 @@
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
import { translator } from '../core/localization/translator';
import { COMMANDS_RAW, CommandRegistry } from '../events/interactionCreate';
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
import { translator } from "../core/localization/translator";
import {
COMMANDS_RAW,
type CommandRegistry,
} from "../events/interactionCreate";
const subcommands = [
'embeds',
'introduction',
'invite',
'timezones',
'credits',
"embeds",
"introduction",
"invite",
"timezones",
"credits",
] as const;
type Subcommand = (typeof subcommands)[number];
const DOCS: Record<Subcommand, string> = {
embeds: 'guide/embeds',
introduction: 'guide/introduction',
invite: 'guide/invite',
timezones: 'guide/timezones',
credits: 'about/credits',
embeds: "guide/embeds",
introduction: "guide/introduction",
invite: "guide/invite",
timezones: "guide/timezones",
credits: "about/credits",
};
export const aboutCommand = new Promise<CommandRegistry>(async (resolve) => {
resolve({
command: createLocalizedCommand('about', [
{ subcommand: 'commands' },
command: createLocalizedCommand("about", [
{ subcommand: "commands" },
...subcommands.map((subcommand) => ({ subcommand })),
]),
@@ -30,7 +33,7 @@ export const aboutCommand = new Promise<CommandRegistry>(async (resolve) => {
const subcommand = interaction.options.getSubcommand();
const { strings } = translator(interaction.locale);
if (subcommand === 'commands') {
if (subcommand === "commands") {
return `${strings.bot.commands.about.subcommands.commands.body}${(
await Promise.all(COMMANDS_RAW)
)
@@ -40,7 +43,7 @@ export const aboutCommand = new Promise<CommandRegistry>(async (resolve) => {
(b.command.name_localizations?.[interaction.locale] ??
b.command.name)
? -1
: 1,
: 1
)
.map(
(registry) =>
@@ -51,9 +54,9 @@ export const aboutCommand = new Promise<CommandRegistry>(async (resolve) => {
registry.command.description_localizations?.[
interaction.locale
] ?? registry.command.description
}`,
}`
)
.join('\n')}`;
.join("\n")}`;
}
const url = `https://raw.githubusercontent.com/tresabhi/blitzkit/main/docs/${DOCS[subcommand as Subcommand]}.md`;
-144
View File
@@ -1,144 +0,0 @@
import {
BlitzTankClass,
Region,
regionToRegionSubdomain,
} from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { tankDefinitions } from '../core/blitzkit/nonBlockingPromises';
import { chunkLines } from '../core/discord/chunkLines';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
import { translator } from '../core/localization/translator';
import { CommandRegistry } from '../events/interactionCreate';
type Auction = null | {
count: number;
has_next: boolean;
results: Result[];
};
type Result =
| {
id?: number;
type: 'vehicle';
countable?: boolean;
entity?: Entity;
initial_count: number;
current_count: number;
saleable?: boolean;
available_from?: Date;
available_before?: Date;
price?: Price;
next_price: Price | null;
available: true;
display?: boolean;
next_price_datetime: Date;
next_price_timestamp: number;
}
| {
available: false;
type: 'vehicle';
};
interface Entity {
id: number;
name: string;
nation: string;
subnation: string;
use_subnation_flag: boolean;
type_slug: BlitzTankClass;
level: number;
roman_level: string;
user_string: string;
image_url: string;
preview_image_url: string;
is_premium: boolean;
is_collectible: boolean;
}
interface Price {
currency: Currency;
value: number;
}
interface Currency {
name: 'gold';
count: number;
title: 'Gold';
image_url: string;
sizes: unknown;
type: 'currency';
}
const WARNING_COUNT = 500;
export const auctionCommand = new Promise<CommandRegistry>((resolve) => {
resolve({
command: createLocalizedCommand('auction'),
async handler(interaction) {
const { strings } = translator(interaction.locale);
const awaitedTankDefinitions = await tankDefinitions;
const region: Region = 'com';
const pageSize = 80;
const saleable = true;
const data = await fetch(
`https://${regionToRegionSubdomain(
region,
)}.wotblitz.com/en/api/events/items/auction/?page_size=${
pageSize
}&type[]=vehicle&saleable=${saleable}`,
).then((response) => response.json() as Promise<Auction>);
if (data === null) {
return strings.bot.commands.auction.errors.no_auction;
}
const saleableTanks = data.results.filter((result) => result.available);
const nextPricesTimestamp = saleableTanks[0].next_price_timestamp;
const title = `# ${strings.bot.commands.auction.body.title}`;
const subtitle = `${literals(strings.bot.commands.auction.body.subtitle, [
`${saleableTanks.length} / ${data.results.length}`,
`<t:${nextPricesTimestamp}:R>`,
'<https://wotblitz.com/auction/>',
])}`;
const body = saleableTanks.map((data, index) => {
const tank = awaitedTankDefinitions.tanks[data.entity!.id];
if (!tank) return '';
const name = `[${tank.name}](<https://blitzkit.app/tanks/${
tank.slug
}>)`;
const next =
data.next_price === null
? ''
: `\n-# ${literals(strings.bot.commands.auction.body.next, [
`<:gold:1317173197082333244> ${data.next_price.value.toLocaleString(
interaction.locale,
)}`,
])}`;
const available = literals(
strings.bot.commands.auction.body.available,
[
data.current_count.toLocaleString(interaction.locale),
data.initial_count.toLocaleString(interaction.locale),
],
);
const isOut = data.current_count === 0;
const outString = isOut ? '~~' : '';
const isLow = data.current_count <= WARNING_COUNT;
return `${index + 1}. ${
outString
}${name} <:gold:1317173197082333244> ${data.price!.value.toLocaleString(
interaction.locale,
)}${outString}\n-#${isLow ? ' ‼️ ' : ' '}${available}${isOut ? '' : next}`;
});
const header = `${title}\n${subtitle}`;
const lines = [header, ...body];
return chunkLines(lines, '\n\n');
},
});
});
@@ -13,7 +13,7 @@ import { resolvePeriodFromButton } from '../../core/discord/resolvePeriodFromBut
import { resolvePeriodFromCommand } from '../../core/discord/resolvePeriodFromCommand';
import { resolvePlayerFromButton } from '../../core/discord/resolvePlayerFromButton';
import { resolvePlayerFromCommand } from '../../core/discord/resolvePlayerFromCommand';
import { CommandRegistry } from '../../events/interactionCreate';
import type { CommandRegistry } from '../../events/interactionCreate';
import { renderBreakdown } from './render';
export const breakdownCommand = new Promise<CommandRegistry>(
@@ -1,5 +1,5 @@
import {
BlitzStats,
type BlitzStats,
calculateWN8,
getAccountInfo,
getClanAccountInfo,
@@ -17,11 +17,11 @@ import { TitleBar } from "../../components/TitleBar";
import { filtersToDescription } from "../../core/blitzkit/filtersToDescription";
import { tankDefinitions } from "../../core/blitzkit/nonBlockingPromises";
import { UserError } from "../../core/blitzkit/userError";
import { filterStats, StatFilters } from "../../core/blitzstars/filterStats";
import { filterStats, type StatFilters } from "../../core/blitzstars/filterStats";
import { getStatsInPeriod } from "../../core/blitzstars/getStatsInPeriod";
import { blitzStarsTankAverages } from "../../core/blitzstars/tankAverages";
import { ResolvedPeriod } from "../../core/discord/resolvePeriodFromCommand";
import { ResolvedPlayer } from "../../core/discord/resolvePlayerFromCommand";
import type { ResolvedPeriod } from "../../core/discord/resolvePeriodFromCommand";
import type { ResolvedPlayer } from "../../core/discord/resolvePlayerFromCommand";
import { translator } from "../../core/localization/translator";
const ROWS_PER_PAGE = 8;
@@ -193,9 +193,9 @@ export async function renderBreakdown(
title={
tankDefinition
? unwrap(tankDefinition.name)
: literals(strings.bot.commands.breakdown.body.unknown_tank, [
`${id}`,
])
: literals(strings.bot.commands.breakdown.body.unknown_tank, {
id,
})
}
stats={[
{
+9 -9
View File
@@ -1,14 +1,14 @@
import packageJSON from '../../../../package.json';
import { client } from '../core/discord/client';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
import { translator } from '../core/localization/translator';
import { CommandRegistry } from '../events/interactionCreate';
import packageJSON from "../../../../package.json";
import { client } from "../core/discord/client";
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
import { translator } from "../core/localization/translator";
import type { CommandRegistry } from "../events/interactionCreate";
const executionStart = new Date().getTime();
export const debugCommand = new Promise<CommandRegistry>((resolve) => {
resolve({
command: createLocalizedCommand('debug'),
command: createLocalizedCommand("debug"),
async handler(interaction) {
const { strings } = translator(interaction.locale);
@@ -23,15 +23,15 @@ export const debugCommand = new Promise<CommandRegistry>((resolve) => {
[
strings.bot.commands.debug.body.uptime,
`${Math.floor((uptime / 1000 / 60 / 60) % 24)}h ${Math.floor(
(uptime / 1000 / 60) % 60,
(uptime / 1000 / 60) % 60
)}m ${Math.floor((uptime / 1000) % 60)}s ${Math.floor(
uptime % 1000,
uptime % 1000
)}ms`,
],
];
return `${strings.bot.commands.debug.body.title}${list
.map(([key, value]) => `- ${key}: ${value}`)
.join('\n')}`;
.join("\n")}`;
},
});
});
+47 -47
View File
@@ -1,48 +1,48 @@
import { emblemURL, getAccountInfo, getClanAccountInfo } from '@blitzkit/core';
import { Locale, SlashCommandSubcommandGroupBuilder } from 'discord.js';
import { CommandWrapper } from '../components/CommandWrapper';
import * as Graph from '../components/Graph';
import { LineColor } from '../components/Graph/components/Line/constants';
import { NoData } from '../components/NoData';
import { TitleBar } from '../components/TitleBar';
import { resolveTankId } from '../core/blitz/resolveTankId';
import { getBlitzStarsLinkButton } from '../core/blitzstars/getBlitzStarsLinkButton';
import { getPlayerHistories } from '../core/blitzstars/getPlayerHistories';
import { getTankHistories } from '../core/blitzstars/getTankHistories';
import { addPeriodSubCommands } from '../core/discord/addPeriodSubCommands';
import { addTankChoices } from '../core/discord/addTankChoices';
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
import { autocompleteTanks } from '../core/discord/autocompleteTanks';
import { autocompleteUsername } from '../core/discord/autocompleteUsername';
import { buttonRefresh } from '../core/discord/buttonRefresh';
import { commandToURL } from '../core/discord/commandToURL';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
import { getCustomPeriodParams } from '../core/discord/getCustomPeriodParams';
import { resolvePeriodFromButton } from '../core/discord/resolvePeriodFromButton';
import { emblemURL, getAccountInfo, getClanAccountInfo } from "@blitzkit/core";
import { Locale, SlashCommandSubcommandGroupBuilder } from "discord.js";
import { CommandWrapper } from "../components/CommandWrapper";
import * as Graph from "../components/Graph";
import { LineColor } from "../components/Graph/components/Line/constants";
import { NoData } from "../components/NoData";
import { TitleBar } from "../components/TitleBar";
import { resolveTankId } from "../core/blitz/resolveTankId";
import { getBlitzStarsLinkButton } from "../core/blitzstars/getBlitzStarsLinkButton";
import { getPlayerHistories } from "../core/blitzstars/getPlayerHistories";
import { getTankHistories } from "../core/blitzstars/getTankHistories";
import { addPeriodSubCommands } from "../core/discord/addPeriodSubCommands";
import { addTankChoices } from "../core/discord/addTankChoices";
import { addUsernameChoices } from "../core/discord/addUsernameChoices";
import { autocompleteTanks } from "../core/discord/autocompleteTanks";
import { autocompleteUsername } from "../core/discord/autocompleteUsername";
import { buttonRefresh } from "../core/discord/buttonRefresh";
import { commandToURL } from "../core/discord/commandToURL";
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
import { getCustomPeriodParams } from "../core/discord/getCustomPeriodParams";
import { resolvePeriodFromButton } from "../core/discord/resolvePeriodFromButton";
import {
ResolvedPeriod,
type ResolvedPeriod,
resolvePeriodFromCommand,
} from '../core/discord/resolvePeriodFromCommand';
import { resolvePlayerFromButton } from '../core/discord/resolvePlayerFromButton';
} from "../core/discord/resolvePeriodFromCommand";
import { resolvePlayerFromButton } from "../core/discord/resolvePlayerFromButton";
import {
ResolvedPlayer,
type ResolvedPlayer,
resolvePlayerFromCommand,
} from '../core/discord/resolvePlayerFromCommand';
import { CommandRegistry } from '../events/interactionCreate';
} from "../core/discord/resolvePlayerFromCommand";
import type { CommandRegistry } from "../events/interactionCreate";
type EvolutionStatType = 'player' | 'tank';
type EvolutionStatType = "player" | "tank";
async function render(
{ region, id }: ResolvedPlayer,
{ start, end, name }: ResolvedPeriod,
type: 'player' | 'tank',
type: "player" | "tank",
tankId: number | null,
locale: Locale,
locale: Locale
) {
const accountInfo = await getAccountInfo(region, id);
const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan;
const clan = (await getClanAccountInfo(region, id, ["clan"]))?.clan;
const logo = clan ? emblemURL(clan.emblem_set_id) : undefined;
const histories = await (type === 'player'
const histories = await (type === "player"
? getPlayerHistories(region, id, {
start,
end,
@@ -61,7 +61,7 @@ async function render(
[
history.all.battles,
(history.all.wins / history.all.battles) * 100,
] as Graph.PlotItem,
] as Graph.PlotItem
);
const times = histories.map((history) => history.last_battle_time);
const minTime = Math.min(...times);
@@ -82,7 +82,7 @@ async function render(
verticalMargin={{
min: minY,
max: maxY,
suffix: '%',
suffix: "%",
precision: 2,
}}
horizontalMargin={{
@@ -110,22 +110,22 @@ async function render(
export const evolutionCommand = new Promise<CommandRegistry>(
async (resolve) => {
resolve({
command: createLocalizedCommand('evolution', [
command: createLocalizedCommand("evolution", [
{
group: 'player',
group: "player",
modify(option: SlashCommandSubcommandGroupBuilder) {
addPeriodSubCommands(option, (option) =>
option.addStringOption(addUsernameChoices),
option.addStringOption(addUsernameChoices)
);
},
},
{
group: 'tank',
group: "tank",
modify(option: SlashCommandSubcommandGroupBuilder) {
addPeriodSubCommands(option, (option) =>
option
.addStringOption(addTankChoices)
.addStringOption(addUsernameChoices),
.addStringOption(addUsernameChoices)
);
},
},
@@ -136,9 +136,9 @@ export const evolutionCommand = new Promise<CommandRegistry>(
interaction.options.getSubcommandGroup() as EvolutionStatType;
const player = await resolvePlayerFromCommand(interaction);
const period = resolvePeriodFromCommand(player.region, interaction);
const tankIdRaw = interaction.options.getString('tank')!;
const tankIdRaw = interaction.options.getString("tank")!;
const tankId =
commandGroup === 'tank'
commandGroup === "tank"
? await resolveTankId(tankIdRaw, interaction.locale)
: null;
const path = commandToURL(interaction, {
@@ -153,13 +153,13 @@ export const evolutionCommand = new Promise<CommandRegistry>(
period,
commandGroup,
tankId,
interaction.locale,
interaction.locale
),
buttonRefresh(interaction, path),
await getBlitzStarsLinkButton(
player.region,
player.id,
tankId ?? undefined,
tankId ?? undefined
),
];
},
@@ -171,7 +171,7 @@ export const evolutionCommand = new Promise<CommandRegistry>(
async button(interaction) {
const url = new URL(`https://example.com/${interaction.customId}`);
const path = url.pathname.split('/');
const path = url.pathname.split("/");
const commandGroup = path[2] as EvolutionStatType;
const player = await resolvePlayerFromButton(interaction);
const period = resolvePeriodFromButton(player.region, interaction);
@@ -180,10 +180,10 @@ export const evolutionCommand = new Promise<CommandRegistry>(
player,
period,
commandGroup,
parseInt(url.searchParams.get('tankId') ?? '0') || null,
interaction.locale,
parseInt(url.searchParams.get("tankId") ?? "0") || null,
interaction.locale
);
},
});
},
}
);
+4 -4
View File
@@ -11,7 +11,7 @@ import { NoData } from '../components/NoData';
import { TierWeights } from '../components/TierWeights';
import { TitleBar } from '../components/TitleBar';
import { filtersToDescription } from '../core/blitzkit/filtersToDescription';
import { filterStats, StatFilters } from '../core/blitzstars/filterStats';
import { filterStats, type StatFilters } from '../core/blitzstars/filterStats';
import { getBlitzStarsLinkButton } from '../core/blitzstars/getBlitzStarsLinkButton';
import { getStatsInPeriod } from '../core/blitzstars/getStatsInPeriod';
import { addPeriodicFilterOptions } from '../core/discord/addPeriodicFilterOptions';
@@ -26,15 +26,15 @@ import { getFiltersFromButton } from '../core/discord/getFiltersFromButton';
import { getFiltersFromCommand } from '../core/discord/getFiltersFromCommand';
import { resolvePeriodFromButton } from '../core/discord/resolvePeriodFromButton';
import {
ResolvedPeriod,
type ResolvedPeriod,
resolvePeriodFromCommand,
} from '../core/discord/resolvePeriodFromCommand';
import { resolvePlayerFromButton } from '../core/discord/resolvePlayerFromButton';
import {
ResolvedPlayer,
type ResolvedPlayer,
resolvePlayerFromCommand,
} from '../core/discord/resolvePlayerFromCommand';
import { CommandRegistry } from '../events/interactionCreate';
import type { CommandRegistry } from '../events/interactionCreate';
async function render(
{ region, id }: ResolvedPlayer,
+28 -28
View File
@@ -1,44 +1,44 @@
import { getAccountInfo, getClanInfo } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import { CommandWrapper } from '../components/CommandWrapper';
import { GenericStats } from '../components/GenericStats';
import { NoData } from '../components/NoData';
import { TitleBar } from '../components/TitleBar';
import { addClanChoices } from '../core/discord/addClanChoices';
import { autocompleteClan } from '../core/discord/autocompleteClan';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
import { localizationObject } from '../core/discord/localizationObject';
import { resolveClanFromCommand } from '../core/discord/resolveClanFromCommand';
import { translator } from '../core/localization/translator';
import { CommandRegistry } from '../events/interactionCreate';
import { getAccountInfo, getClanInfo } from "@blitzkit/core";
import { literals } from "@blitzkit/i18n";
import { Locale } from "discord.js";
import { CommandWrapper } from "../components/CommandWrapper";
import { GenericStats } from "../components/GenericStats";
import { NoData } from "../components/NoData";
import { TitleBar } from "../components/TitleBar";
import { addClanChoices } from "../core/discord/addClanChoices";
import { autocompleteClan } from "../core/discord/autocompleteClan";
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
import { localizationObject } from "../core/discord/localizationObject";
import { resolveClanFromCommand } from "../core/discord/resolveClanFromCommand";
import { translator } from "../core/localization/translator";
import type { CommandRegistry } from "../events/interactionCreate";
const DEFAULT_THRESHOLD = 7;
export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
resolve({
command: createLocalizedCommand('inactive')
command: createLocalizedCommand("inactive")
.addStringOption(addClanChoices)
.addNumberOption((option) => {
const { strings } = translator(Locale.EnglishUS);
return option
.setName('threshold')
.setName("threshold")
.setNameLocalizations(
localizationObject(
(strings) => strings.bot.commands.inactive.options.threshold.name,
undefined,
true,
),
true
)
)
.setDescription(
strings.bot.commands.inactive.options.threshold.description,
strings.bot.commands.inactive.options.threshold.description
)
.setDescriptionLocalizations(
localizationObject(
(strings) =>
strings.bot.commands.inactive.options.threshold.description,
),
strings.bot.commands.inactive.options.threshold.description
)
)
.setMinValue(0);
}),
@@ -47,7 +47,7 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
const { strings } = translator(interaction.locale);
const { region, id } = await resolveClanFromCommand(interaction);
const threshold =
interaction.options.getNumber('threshold')! ?? DEFAULT_THRESHOLD;
interaction.options.getNumber("threshold")! ?? DEFAULT_THRESHOLD;
const time = new Date().getTime() / 1000;
const clanInfo = await getClanInfo(region, id);
const accountInfo = await getAccountInfo(region, clanInfo.members_ids);
@@ -57,7 +57,7 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
[
account.nickname,
(time - account.last_battle_time) / 60 / 60 / 24,
] as [string, number],
] as [string, number]
)
.filter(([, inactiveDays]) => inactiveDays >= threshold)
.sort((a, b) => b[1] - a[1])
@@ -65,10 +65,10 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
([name, days]) =>
[
name,
literals(strings.bot.commands.inactive.body.listing, [
days.toFixed(0),
]),
] as [string, string],
literals(strings.bot.commands.inactive.body.listing, {
days: days.toFixed(0),
}),
] as [string, string]
);
const hasInactiveMembers = inactive.length > 0;
@@ -79,7 +79,7 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
image={`https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clanInfo.emblem_set_id}.png`}
description={`${literals(
strings.bot.commands.inactive.body.subtitle,
[`${threshold}`],
{ days: threshold }
)} • ${new Date().toLocaleDateString(interaction.locale)}`}
/>
+1 -1
View File
@@ -9,7 +9,7 @@ import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
import { embedInfo } from '../core/discord/embedInfo';
import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand';
import { translator } from '../core/localization/translator';
import { CommandRegistry } from '../events/interactionCreate';
import type { CommandRegistry } from '../events/interactionCreate';
import { theme } from '../stitches.config';
export const verifyCommand = new Promise<CommandRegistry>((resolve) => {
+22 -22
View File
@@ -5,31 +5,31 @@ import {
flags,
getAccountInfo,
getTankStats,
} from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import markdownEscape from 'markdown-escape';
} from "@blitzkit/core";
import { literals } from "@blitzkit/i18n";
import markdownEscape from "markdown-escape";
import {
gameDefinitions,
tankDefinitions,
} from '../core/blitzkit/nonBlockingPromises';
import { addTierChoices } from '../core/discord/addTierChoices';
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
import { autocompleteUsername } from '../core/discord/autocompleteUsername';
import { chunkLines } from '../core/discord/chunkLines';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand';
import { translator } from '../core/localization/translator';
import { CommandRegistry } from '../events/interactionCreate';
} from "../core/blitzkit/nonBlockingPromises";
import { addTierChoices } from "../core/discord/addTierChoices";
import { addUsernameChoices } from "../core/discord/addUsernameChoices";
import { autocompleteUsername } from "../core/discord/autocompleteUsername";
import { chunkLines } from "../core/discord/chunkLines";
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
import { resolvePlayerFromCommand } from "../core/discord/resolvePlayerFromCommand";
import { translator } from "../core/localization/translator";
import { type CommandRegistry } from "../events/interactionCreate";
export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
resolve({
command: createLocalizedCommand('owned-tanks')
command: createLocalizedCommand("owned-tanks")
.addStringOption(addTierChoices)
.addStringOption(addUsernameChoices),
async handler(interaction) {
const { strings, unwrap } = translator(interaction.locale);
const tier = Number(interaction.options.getString('tier'));
const tier = Number(interaction.options.getString("tier"));
const { id, region } = await resolvePlayerFromCommand(interaction);
const accountInfo = await getAccountInfo(region, id);
const tankStats = await getTankStats(region, id);
@@ -43,7 +43,7 @@ export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
tankStats.map(async (tankData) => ({
tankDefinitions: (await tankDefinitions).tanks[tankData.tank_id]!,
id: tankData.tank_id,
})),
}))
)
).filter((tank) => tank.tankDefinitions?.tier === tier);
const groupedTanks: Record<string, TankDefinition[]> = {};
@@ -60,25 +60,25 @@ export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
const awaitedTankDefinitions = await tankDefinitions;
const awaitedGameDefinitions = await gameDefinitions;
const title = literals(strings.bot.commands.owned_tanks.body.title, [
markdownEscape(accountInfo.nickname),
TIER_ROMAN_NUMERALS[tier],
]);
const title = literals(strings.bot.commands.owned_tanks.body.title, {
name: markdownEscape(accountInfo.nickname),
tier: TIER_ROMAN_NUMERALS[tier],
});
const tankList = tankStats
.map(({ tank_id }) => awaitedTankDefinitions.tanks[tank_id])
.filter((tank) => tank.tier === tier)
.sort(
(a, b) =>
TANK_CLASSES.indexOf(a.class) - TANK_CLASSES.indexOf(b.class),
TANK_CLASSES.indexOf(a.class) - TANK_CLASSES.indexOf(b.class)
)
.sort(
(a, b) =>
awaitedGameDefinitions.nations.indexOf(a.nation) -
awaitedGameDefinitions.nations.indexOf(b.nation),
awaitedGameDefinitions.nations.indexOf(b.nation)
)
.map(
(tank) =>
`[${flags[tank.nation]} ${markdownEscape(unwrap(tank.name))}](<https://blitzkit.app/tanks/${tank.slug}>)`,
`[${flags[tank.nation]} ${markdownEscape(unwrap(tank.name))}](<https://blitzkit.app/tanks/${tank.slug}>)`
);
const lines = [title, ...tankList];
+13 -13
View File
@@ -1,11 +1,11 @@
import { literals } from '@blitzkit/i18n';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
import { translator } from '../core/localization/translator';
import { CommandRegistry } from '../events/interactionCreate';
import { literals } from "@blitzkit/i18n";
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
import { translator } from "../core/localization/translator";
import { CommandRegistry } from "../events/interactionCreate";
export const permissionsCommand = new Promise<CommandRegistry>((resolve) => {
resolve({
command: createLocalizedCommand('permissions'),
command: createLocalizedCommand("permissions"),
async handler(interaction) {
const { strings } = translator(interaction.locale);
@@ -14,33 +14,33 @@ export const permissionsCommand = new Promise<CommandRegistry>((resolve) => {
new Date().getDay() === 5 && new Date().getDate() === 13;
const permissions = [
[
interaction.appPermissions?.has('ViewChannel'),
interaction.appPermissions?.has("ViewChannel"),
strings.bot.commands.permissions.body.view_channels,
],
[
interaction.appPermissions?.has('ReadMessageHistory'),
interaction.appPermissions?.has("ReadMessageHistory"),
strings.bot.commands.permissions.body.read_message_history,
],
[
interaction.appPermissions?.has('AttachFiles'),
interaction.appPermissions?.has("AttachFiles"),
strings.bot.commands.permissions.body.attach_files,
],
[
interaction.appPermissions?.has('UseExternalEmojis'),
interaction.appPermissions?.has("UseExternalEmojis"),
strings.bot.commands.permissions.body.external_emojis,
],
];
const body = permissions
.map(([hasPermission, description]) => {
if (hasPermission) {
return `${isFridayThe13th ? '😇' : '✅'} ${description}`;
return `${isFridayThe13th ? "😇" : "✅"} ${description}`;
} else {
return `${isFridayThe13th ? '💀' : '❌'} ${description}`;
return `${isFridayThe13th ? "💀" : "❌"} ${description}`;
}
})
.join('\n');
.join("\n");
return literals(strings.bot.commands.permissions.body.title, [body]);
return literals(strings.bot.commands.permissions.body.title, { body });
},
});
});
+12 -12
View File
@@ -1,15 +1,15 @@
import { literals } from '@blitzkit/i18n';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
import { translator } from '../core/localization/translator';
import { CommandRegistry } from '../events/interactionCreate';
import { literals } from "@blitzkit/i18n";
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
import { translator } from "../core/localization/translator";
import type { CommandRegistry } from "../events/interactionCreate";
export const pingCommand = new Promise<CommandRegistry>((resolve) => {
resolve({
handlesInteraction: true,
command: createLocalizedCommand('ping', [
{ subcommand: 'blitzkit' },
{ subcommand: 'wotb' },
command: createLocalizedCommand("ping", [
{ subcommand: "blitzkit" },
{ subcommand: "wotb" },
]),
async handler(interaction) {
@@ -17,17 +17,17 @@ export const pingCommand = new Promise<CommandRegistry>((resolve) => {
const subcommand = interaction.options.getSubcommand();
const executionStart = Date.now();
if (subcommand === 'blitzkit') {
if (subcommand === "blitzkit") {
await interaction.editReply(strings.bot.commands.ping.body.pong);
} else {
await fetch('https://api.wotblitz.com/');
await fetch("https://api.wotblitz.com/");
}
const executionTime = Date.now() - executionStart;
interaction.editReply(
literals(strings.bot.commands.ping.body.pong_with_time, [
`${Math.round(executionTime)}`,
]),
literals(strings.bot.commands.ping.body.pong_with_time, {
time: executionTime.toFixed(0),
})
);
},
});
+18 -18
View File
@@ -1,19 +1,19 @@
import { getAccountInfo } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import markdownEscape from 'markdown-escape';
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
import { autocompleteUsername } from '../core/discord/autocompleteUsername';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
import { embedInfo } from '../core/discord/embedInfo';
import { markdownTable } from '../core/discord/markdownTable';
import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand';
import { translator } from '../core/localization/translator';
import { CommandRegistry } from '../events/interactionCreate';
import { getAccountInfo } from "@blitzkit/core";
import { literals } from "@blitzkit/i18n";
import markdownEscape from "markdown-escape";
import { addUsernameChoices } from "../core/discord/addUsernameChoices";
import { autocompleteUsername } from "../core/discord/autocompleteUsername";
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
import { embedInfo } from "../core/discord/embedInfo";
import { markdownTable } from "../core/discord/markdownTable";
import { resolvePlayerFromCommand } from "../core/discord/resolvePlayerFromCommand";
import { translator } from "../core/localization/translator";
import { CommandRegistry } from "../events/interactionCreate";
export const playerInfoCommand = new Promise<CommandRegistry>((resolve) => {
resolve({
command:
createLocalizedCommand('player-info').addStringOption(addUsernameChoices),
createLocalizedCommand("player-info").addStringOption(addUsernameChoices),
async handler(interaction) {
const { strings } = translator(interaction.locale);
@@ -22,9 +22,9 @@ export const playerInfoCommand = new Promise<CommandRegistry>((resolve) => {
const accountInfo = await getAccountInfo(region, id);
return embedInfo(
literals(strings.bot.commands.player_info.body.title, [
markdownEscape(accountInfo.nickname),
]),
literals(strings.bot.commands.player_info.body.title, {
name: markdownEscape(accountInfo.nickname),
}),
markdownTable([
[
@@ -51,16 +51,16 @@ export const playerInfoCommand = new Promise<CommandRegistry>((resolve) => {
[
strings.bot.commands.player_info.body.created,
new Date(accountInfo.created_at * 1000).toLocaleDateString(
interaction.locale,
interaction.locale
),
],
[
strings.bot.commands.player_info.body.last_battle,
new Date(accountInfo.last_battle_time * 1000).toLocaleDateString(
interaction.locale,
interaction.locale
),
],
]),
])
);
},
+118 -118
View File
@@ -8,42 +8,42 @@ import {
getRatingInfo,
getRatingNeighbors,
imgur,
} from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { SlashCommandSubcommandBuilder } from 'discord.js';
import markdownEscape from 'markdown-escape';
import { Glow } from '../components/AllStatsOverview/components/HeroStat/components/Glow';
import { CommandWrapper } from '../components/CommandWrapper';
import { DeltaCaret } from '../components/DeltaCaret';
import { TitleBar } from '../components/TitleBar';
import { normalizeLeagueIcon } from '../core/blitz/normalizeLeagueIcon';
import { iconPng } from '../core/blitzkit/iconPng';
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand';
import { translator } from '../core/localization/translator';
import { CommandRegistry } from '../events/interactionCreate';
import { theme } from '../stitches.config';
} from "@blitzkit/core";
import { literals } from "@blitzkit/i18n";
import { SlashCommandSubcommandBuilder } from "discord.js";
import markdownEscape from "markdown-escape";
import { Glow } from "../components/AllStatsOverview/components/HeroStat/components/Glow";
import { CommandWrapper } from "../components/CommandWrapper";
import { DeltaCaret } from "../components/DeltaCaret";
import { TitleBar } from "../components/TitleBar";
import { normalizeLeagueIcon } from "../core/blitz/normalizeLeagueIcon";
import { iconPng } from "../core/blitzkit/iconPng";
import { addUsernameChoices } from "../core/discord/addUsernameChoices";
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
import { resolvePlayerFromCommand } from "../core/discord/resolvePlayerFromCommand";
import { translator } from "../core/localization/translator";
import type { CommandRegistry } from "../events/interactionCreate";
import { theme } from "../stitches.config";
const LEAGUE_ACCENT = [
'_purple',
'_sand',
'_amber',
'_mauve',
'_brown',
"_purple",
"_sand",
"_amber",
"_mauve",
"_brown",
] as const;
export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
resolve({
command: createLocalizedCommand('rating', [
command: createLocalizedCommand("rating", [
{
subcommand: 'today',
subcommand: "today",
modify(option: SlashCommandSubcommandBuilder) {
option.addStringOption(addUsernameChoices);
},
},
{
subcommand: 'season',
subcommand: "season",
modify(option: SlashCommandSubcommandBuilder) {
option.addStringOption(addUsernameChoices);
},
@@ -52,11 +52,11 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
async handler(interaction) {
const subcommand = interaction.options.getSubcommand(true) as
| 'season'
| 'today';
| "season"
| "today";
const { strings } = translator(interaction.locale);
const { id, region } = await resolvePlayerFromCommand(interaction);
const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan;
const clan = (await getClanAccountInfo(region, id, ["clan"]))?.clan;
const clanImage = clan ? emblemURL(clan.emblem_set_id) : undefined;
const ratingInfo = await getRatingInfo(region);
@@ -67,20 +67,20 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
const leaderboard = await getArchivedRatingLeaderboard(
region,
ratingInfo.current_season - (subcommand === 'today' ? 0 : 1),
ratingInfo.current_season - (subcommand === "today" ? 0 : 1)
);
if (leaderboard.version!.$case === 'v1') {
if (leaderboard.version!.$case === "v1") {
throw new Error(
'Encountered bkrl minimal format in latest season. Wait till next cron?',
"Encountered bkrl minimal format in latest season. Wait till next cron?"
);
}
const accountInfo = await getAccountInfo(region, id, [
'statistics.rating',
"statistics.rating",
]);
const statsIndex = leaderboard.version!.value.entries.findIndex(
(entry) => entry.id === id,
(entry) => entry.id === id
);
const statsA = leaderboard
? leaderboard.version!.value.entries[statsIndex!]
@@ -90,9 +90,9 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
const statsB2Array = (await getRatingNeighbors(region, id, 0)).neighbors;
if (statsB2Array === undefined) {
return literals(strings.bot.commands.rating.errors.no_participation, [
markdownEscape(accountInfo.nickname),
]);
return literals(strings.bot.commands.rating.errors.no_participation, {
name: markdownEscape(accountInfo.nickname),
});
}
const [statsB2] = statsB2Array;
@@ -102,15 +102,15 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
return strings.bot.commands.rating.errors.unrecorded_stats;
}
if (!statsB1) {
return literals(strings.bot.commands.rating.errors.no_participation, [
markdownEscape(accountInfo.nickname),
]);
return literals(strings.bot.commands.rating.errors.no_participation, {
name: markdownEscape(accountInfo.nickname),
});
}
const reward = ratingInfo.rewards.find(
(reward) =>
statsB2.number >= reward.from_position &&
statsB2.number <= reward.to_position,
statsB2.number <= reward.to_position
);
const positionDelta = leaderboard
? statsB2.number - positionA!
@@ -118,11 +118,11 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
const delta = deltaBkrlBlitzStats(
statsA,
accountInfo.statistics.rating!,
statsB2,
statsB2
);
const leagueTheme = LEAGUE_ACCENT[league.index];
let rewardImage = reward
? reward.type === 'vehicle'
? reward.type === "vehicle"
? reward.vehicle.preview_image_url
: reward.stuff.image_url
: undefined;
@@ -131,7 +131,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
const { ok } = await fetch(rewardImage);
if (!ok) {
rewardImage = imgur('kP88vqr');
rewardImage = imgur("kP88vqr");
}
}
@@ -147,24 +147,24 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
<div
style={{
position: 'relative',
display: 'flex',
overflow: 'hidden',
position: "relative",
display: "flex",
overflow: "hidden",
}}
>
<div
style={{
position: 'relative',
position: "relative",
padding: 16,
backgroundColor: theme.colors[`appBackground2${leagueTheme}`],
borderRadius: 4,
border: `1px solid ${
theme.colors[`componentInteractive${leagueTheme}`]
}`,
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
width: '100%',
display: "flex",
alignItems: "center",
justifyContent: "space-between",
width: "100%",
}}
>
{reward === undefined && (
@@ -172,17 +172,17 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
endOpacity={3 / 4}
color={theme.colors[`solidBackground${leagueTheme}`]}
style={{
transform: 'translateX(-16px) rotate(90deg)',
transform: "translateX(-16px) rotate(90deg)",
}}
/>
)}
<div
style={{
display: 'flex',
display: "flex",
gap: 8,
justifyContent: 'center',
alignItems: 'center',
justifyContent: "center",
alignItems: "center",
}}
>
<img
@@ -190,24 +190,24 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
style={{
width: 64,
height: 64,
objectFit: 'contain',
objectFit: "contain",
}}
src={normalizeLeagueIcon(
ratingInfo.leagues[league.index].big_icon,
ratingInfo.leagues[league.index].big_icon
)}
/>
<div
style={{
display: 'flex',
flexDirection: 'column',
display: "flex",
flexDirection: "column",
}}
>
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-start',
display: "flex",
alignItems: "center",
justifyContent: "flex-start",
}}
>
<span
@@ -232,7 +232,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
}}
>
{Math.abs(delta.score).toLocaleString(
interaction.locale,
interaction.locale
)}
</span>
</>
@@ -241,9 +241,9 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-start',
display: "flex",
alignItems: "center",
justifyContent: "flex-start",
}}
>
<span
@@ -267,7 +267,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
}}
>
{Math.abs(positionDelta).toLocaleString(
interaction.locale,
interaction.locale
)}
</span>
</>
@@ -279,23 +279,23 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
{reward && (
<div
style={{
display: 'flex',
display: "flex",
gap: 8,
justifyContent: 'center',
alignItems: 'center',
justifyContent: "center",
alignItems: "center",
}}
>
<div
style={{
display: 'flex',
flexDirection: 'column',
display: "flex",
flexDirection: "column",
}}
>
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
}}
>
<span
@@ -303,13 +303,13 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
color: theme.colors[`textHighContrast${leagueTheme}`],
fontSize: 16,
maxWidth: 136,
textAlign: 'right',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
overflow: 'hidden',
textAlign: "right",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
overflow: "hidden",
}}
>
{reward.type === 'vehicle'
{reward.type === "vehicle"
? reward.vehicle.user_string
: reward.stuff.title}
</span>
@@ -317,9 +317,9 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
}}
>
<span
@@ -329,7 +329,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
}}
>
Reward
{reward.type === 'stuff' ? ` x${reward.count}` : ''}
{reward.type === "stuff" ? ` x${reward.count}` : ""}
</span>
</div>
</div>
@@ -338,7 +338,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
style={{
width: 64,
height: 64,
objectFit: 'cover',
objectFit: "cover",
}}
src={await iconPng(rewardImage!)}
/>
@@ -350,7 +350,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
endOpacity={3 / 4}
color={theme.colors[`solidBackground${leagueTheme}`]}
style={{
transform: 'translateX(16px) rotate(-90deg)',
transform: "translateX(16px) rotate(-90deg)",
}}
/>
)}
@@ -362,10 +362,10 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
direction="reverse"
color={theme.colors[`solidBackground${leagueTheme}`]}
style={{
position: 'absolute',
position: "absolute",
top: -40,
left: '50%',
transform: 'translateX(-50%)',
left: "50%",
transform: "translateX(-50%)",
}}
/>
)}
@@ -373,32 +373,32 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
<Glow
color={theme.colors[`solidBackground${leagueTheme}`]}
style={{
position: 'absolute',
position: "absolute",
bottom: -40,
left: '50%',
transform: 'translateX(-50%)',
left: "50%",
transform: "translateX(-50%)",
}}
/>
)}
</div>
{delta.battles > 0 && (
<div style={{ padding: '8px 0', display: 'flex' }}>
<div style={{ padding: "8px 0", display: "flex" }}>
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
display: "flex",
alignItems: "center",
justifyContent: "center",
gap: 4,
flex: 1,
flexDirection: 'column',
flexDirection: "column",
}}
>
<span
style={{
fontSize: 24,
color: theme.colors.textHighContrast,
fontWeight: 'bold',
fontWeight: "bold",
}}
>
{delta.battles.toLocaleString(interaction.locale)}
@@ -414,19 +414,19 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
</div>
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
display: "flex",
alignItems: "center",
justifyContent: "center",
gap: 4,
flex: 1,
flexDirection: 'column',
flexDirection: "column",
}}
>
<span
style={{
fontSize: 24,
color: theme.colors.textHighContrast,
fontWeight: 'bold',
fontWeight: "bold",
}}
>
{(100 * (delta.wins / delta.battles)).toFixed(0)}%
@@ -442,23 +442,23 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
</div>
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
display: "flex",
alignItems: "center",
justifyContent: "center",
gap: 4,
flex: 1,
flexDirection: 'column',
flexDirection: "column",
}}
>
<span
style={{
fontSize: 24,
color: theme.colors.textHighContrast,
fontWeight: 'bold',
fontWeight: "bold",
}}
>
{Math.round(delta.damage / delta.battles).toLocaleString(
interaction.locale,
interaction.locale
)}
</span>
<span
@@ -472,19 +472,19 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
</div>
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
display: "flex",
alignItems: "center",
justifyContent: "center",
gap: 4,
flex: 1,
flexDirection: 'column',
flexDirection: "column",
}}
>
<span
style={{
fontSize: 24,
color: theme.colors.textHighContrast,
fontWeight: 'bold',
fontWeight: "bold",
}}
>
{(100 * (delta.survived / delta.battles)).toFixed(0)}%
@@ -500,19 +500,19 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
</div>
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
display: "flex",
alignItems: "center",
justifyContent: "center",
gap: 4,
flex: 1,
flexDirection: 'column',
flexDirection: "column",
}}
>
<span
style={{
fontSize: 24,
color: theme.colors.textHighContrast,
fontWeight: 'bold',
fontWeight: "bold",
}}
>
{(delta.kills / delta.battles).toFixed(2)}
+82 -83
View File
@@ -10,30 +10,30 @@ import {
imgur,
isOnGoingRatingSeason,
LEAGUES,
RatingInfo,
type RatingInfo,
RatingLeaderboard,
Region,
} from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
type Region,
} from "@blitzkit/core";
import { literals } from "@blitzkit/i18n";
import {
APIApplicationCommandOptionChoice,
type APIApplicationCommandOptionChoice,
Locale,
SlashCommandSubcommandBuilder,
} from 'discord.js';
import { range } from 'lodash-es';
import markdownEscape from 'markdown-escape';
import { CommandWrapper } from '../components/CommandWrapper';
import * as Leaderboard from '../components/Leaderboard';
import { TitleBar } from '../components/TitleBar';
import { UserError } from '../core/blitzkit/userError';
import { addRegionChoices } from '../core/discord/addRegionChoices';
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
import { autocompleteUsername } from '../core/discord/autocompleteUsername';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
import { localizationObject } from '../core/discord/localizationObject';
import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand';
import { translator } from '../core/localization/translator';
import { CommandRegistry } from '../events/interactionCreate';
} from "discord.js";
import { range } from "lodash-es";
import markdownEscape from "markdown-escape";
import { CommandWrapper } from "../components/CommandWrapper";
import * as Leaderboard from "../components/Leaderboard";
import { TitleBar } from "../components/TitleBar";
import { UserError } from "../core/blitzkit/userError";
import { addRegionChoices } from "../core/discord/addRegionChoices";
import { addUsernameChoices } from "../core/discord/addUsernameChoices";
import { autocompleteUsername } from "../core/discord/autocompleteUsername";
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
import { localizationObject } from "../core/discord/localizationObject";
import { resolvePlayerFromCommand } from "../core/discord/resolvePlayerFromCommand";
import { translator } from "../core/localization/translator";
import type { CommandRegistry } from "../events/interactionCreate";
interface SimplifiedPlayer {
id: number;
@@ -51,7 +51,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
const onGoingSeason = await isOnGoingRatingSeason();
const seasonNumbers = range(
FIRST_MINIMAL_ARCHIVED_RATING_SEASON,
latestArchivedSeasonNumber + (onGoingSeason ? 1 : 0) + 1,
latestArchivedSeasonNumber + (onGoingSeason ? 1 : 0) + 1
);
function addOptions(option: SlashCommandSubcommandBuilder) {
@@ -66,51 +66,50 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
(strings) =>
strings.bot.commands.rating_leaderboard.options.limit.name,
undefined,
true,
),
true
)
)
.setDescription(
literals(
strings.bot.commands.rating_leaderboard.options.limit
.description,
[`${DEFAULT_LIMIT}`],
),
{ count: DEFAULT_LIMIT }
)
)
.setDescriptionLocalizations(
localizationObject(
(strings) =>
strings.bot.commands.rating_leaderboard.options.limit
.description,
[`${DEFAULT_LIMIT}`],
),
{ count: DEFAULT_LIMIT }
)
)
.setRequired(false)
.setMinValue(5)
.setMaxValue(30),
.setMaxValue(30)
)
.addStringOption((option) =>
option
.setName(
strings.bot.commands.rating_leaderboard.options.season.name,
strings.bot.commands.rating_leaderboard.options.season.name
)
.setNameLocalizations(
localizationObject(
(strings) =>
strings.bot.commands.rating_leaderboard.options.season.name,
undefined,
true,
),
true
)
)
.setDescription(
strings.bot.commands.rating_leaderboard.options.season
.description,
strings.bot.commands.rating_leaderboard.options.season.description
)
.setDescriptionLocalizations(
localizationObject(
(strings) =>
strings.bot.commands.rating_leaderboard.options.season
.description,
),
.description
)
)
.setRequired(false)
.addChoices(
@@ -124,7 +123,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
? literals(
strings.bot.commands.rating_leaderboard.options.season
.choices.current,
[`${number}`],
{ season: number }
)
: `${number}`,
value: `${number}`,
@@ -133,27 +132,27 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
(strings) =>
strings.bot.commands.rating_leaderboard.options
.season.choices.current,
[`${number}`],
{ season: number }
)
: undefined,
} satisfies APIApplicationCommandOptionChoice<string>;
})
.reverse(),
),
.reverse()
)
);
}
resolve({
command: createLocalizedCommand('rating-leaderboard', [
command: createLocalizedCommand("rating-leaderboard", [
{
subcommand: 'neighbors',
subcommand: "neighbors",
modify(option: SlashCommandSubcommandBuilder) {
option.addStringOption(addUsernameChoices);
addOptions(option);
},
},
{
subcommand: 'league',
subcommand: "league",
modify(option: SlashCommandSubcommandBuilder) {
option
.addStringOption((option) => {
@@ -161,7 +160,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
return option
.setName(
strings.bot.commands.rating_leaderboard.options.league.name,
strings.bot.commands.rating_leaderboard.options.league.name
)
.setNameLocalizations(
localizationObject(
@@ -169,19 +168,19 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
strings.bot.commands.rating_leaderboard.options.league
.name,
undefined,
true,
),
true
)
)
.setDescription(
strings.bot.commands.rating_leaderboard.options.league
.description,
.description
)
.setDescriptionLocalizations(
localizationObject(
(strings) =>
strings.bot.commands.rating_leaderboard.options.league
.description,
),
.description
)
)
.addChoices(
...LEAGUES.map(
@@ -195,10 +194,10 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
(strings) =>
(
strings.common.leagues as Record<string, string>
)[name],
)[name]
),
}) satisfies APIApplicationCommandOptionChoice<string>,
),
}) satisfies APIApplicationCommandOptionChoice<string>
)
)
.setRequired(true);
})
@@ -213,10 +212,10 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
async handler(interaction) {
const { strings } = translator(interaction.locale);
const subcommand = interaction.options.getSubcommand(true) as
| 'league'
| 'neighbors';
const limit = interaction.options.getInteger('limit') ?? DEFAULT_LIMIT;
const seasonOption = interaction.options.getString('season');
| "league"
| "neighbors";
const limit = interaction.options.getInteger("limit") ?? DEFAULT_LIMIT;
const seasonOption = interaction.options.getString("season");
const season = seasonOption
? parseInt(seasonOption)
: onGoingSeason
@@ -232,11 +231,11 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
let playerId: number | undefined;
let regionRatingInfo: RatingInfo | null;
if (subcommand === 'league') {
if (subcommand === "league") {
const leagueIndex = parseInt(
interaction.options.getString('league')!,
interaction.options.getString("league")!
);
const region = interaction.options.getString('region') as Region;
const region = interaction.options.getString("region") as Region;
regionRatingInfo = await (isCurrentSeason
? getRatingInfo(region)
: null);
@@ -259,8 +258,8 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
position: player.number,
clan: player.clan_tag,
nickname: player.nickname,
}) satisfies SimplifiedPlayer,
),
}) satisfies SimplifiedPlayer
)
)
: await getArchivedRatingLeaderboard(region, season!).then(
async (players) => {
@@ -269,17 +268,17 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
? 0
: players.version!.value.entries.findIndex(
(player) =>
player.score < LEAGUES[leagueIndex - 1].minScore,
player.score < LEAGUES[leagueIndex - 1].minScore
);
const lastPlayerIndex = firstPlayerIndex + limit - 1;
const trimmed = players.version!.value.entries.slice(
firstPlayerIndex,
lastPlayerIndex + 1,
lastPlayerIndex + 1
);
const clanData = await getClanAccountInfo(
region,
trimmed.map(({ id }) => id),
['clan'],
["clan"]
);
return trimmed.map(
@@ -294,16 +293,16 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
literals(
strings.bot.commands.rating_leaderboard.body
.deleted_player,
[`${player.id}`],
{ id: player.id }
),
}) satisfies SimplifiedPlayer,
}) satisfies SimplifiedPlayer
);
},
}
);
const leagueInfo = LEAGUES[leagueIndex];
archivedLeaderboard = await getArchivedRatingLeaderboard(
region,
season,
season
);
players = result;
@@ -316,12 +315,12 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
? (strings.common.leagues as Record<string, string>)[
LEAGUES[leagueInfo.index].name
]
: ''
: ""
} - ${strings.common.regions.short[region]}`;
titleImage = imgur(leagueInfo.icon);
titleDescription = literals(
strings.bot.commands.rating_leaderboard.body.top_players,
[`${limit}`],
{ count: limit }
);
} else {
const { region, id } = await resolvePlayerFromCommand(interaction);
@@ -338,7 +337,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
}
const accountInfo = await getAccountInfo(region, id);
const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan;
const clan = (await getClanAccountInfo(region, id, ["clan"]))?.clan;
const neighbors = isCurrentSeason
? await getRatingNeighbors(region, id, Math.round(limit / 2)).then(
(players) =>
@@ -350,13 +349,13 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
score: player.score,
clan: player.clan_tag,
nickname: player.nickname,
}) satisfies SimplifiedPlayer,
),
}) satisfies SimplifiedPlayer
)
)
: await getArchivedRatingLeaderboard(region, season!).then(
async (players) => {
const playerIndex = players.version!.value.entries.findIndex(
(player) => player.id === id,
(player) => player.id === id
);
if (playerIndex === -1) {
@@ -364,20 +363,20 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
literals(
strings.bot.commands.rating_leaderboard.body
.no_participation,
[markdownEscape(accountInfo.nickname), `${season}`],
),
{ player: markdownEscape(accountInfo.nickname), season }
)
);
}
const halfRange = Math.round(limit / 2);
const trimmed = players.version!.value.entries.slice(
playerIndex - halfRange,
playerIndex + halfRange + 1,
playerIndex + halfRange + 1
);
const clanData = await getClanAccountInfo(
region,
trimmed.map((player) => player.id),
['clan'],
["clan"]
);
return trimmed.map((player, index) => ({
@@ -389,18 +388,18 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
? clanData[index]!.account_name
: `Deleted ${player.id}`,
}));
},
}
);
archivedLeaderboard = await getArchivedRatingLeaderboard(
region,
season,
season
);
players = neighbors;
titleImage = clan
? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clan.emblem_set_id}.png`
: undefined;
titleName = neighbors ? accountInfo.nickname : '';
titleName = neighbors ? accountInfo.nickname : "";
titleDescription =
strings.bot.commands.rating_leaderboard.body.neighbors;
playersAfter =
@@ -414,7 +413,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
const items = players.map((player) => {
const midnightIndex =
archivedLeaderboard?.version!.value.entries.findIndex(
(item) => item.id === player.id,
(item) => item.id === player.id
);
const midnightScore =
midnightIndex === undefined
@@ -453,7 +452,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
<Leaderboard.Gap
message={literals(
strings.bot.commands.rating_leaderboard.body.more,
[`${playersAfter.toLocaleString(interaction.locale)}`],
{ count: playersAfter.toLocaleString(interaction.locale) }
)}
/>
)}
@@ -465,5 +464,5 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
autocomplete: autocompleteUsername,
});
},
}
);
+1 -1
View File
@@ -20,7 +20,7 @@ import { blitzStarsTankAverages } from '../core/blitzstars/tankAverages';
import { buttonLink } from '../core/discord/buttonLink';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
import { translator } from '../core/localization/translator';
import { CommandRegistry } from '../events/interactionCreate';
import { type CommandRegistry } from '../events/interactionCreate';
import { theme } from '../stitches.config';
type WotInspectorReplaySubmission =
+136 -137
View File
@@ -8,31 +8,31 @@ import {
TANK_ICONS,
TankType,
TIER_ROMAN_NUMERALS,
} from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { escapeMarkdown, Locale } from 'discord.js';
import { CommandWrapper } from '../components/CommandWrapper';
import { TitleBar } from '../components/TitleBar';
import { resolveTankId } from '../core/blitz/resolveTankId';
import { buildTechTreeLine } from '../core/blitzkit/buildTechTreeLine';
import { iconPng } from '../core/blitzkit/iconPng';
import { tankDefinitions } from '../core/blitzkit/nonBlockingPromises';
import { resolveAncestry } from '../core/blitzkit/resolveAncestry';
import { tankIconPng } from '../core/blitzkit/tankIconPng';
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
import { autocompleteTanks } from '../core/discord/autocompleteTanks';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
import { localizationObject } from '../core/discord/localizationObject';
import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand';
import { translator } from '../core/localization/translator';
import { CommandRegistry } from '../events/interactionCreate';
import { theme } from '../stitches.config';
} from "@blitzkit/core";
import { literals } from "@blitzkit/i18n";
import { escapeMarkdown, Locale } from "discord.js";
import { CommandWrapper } from "../components/CommandWrapper";
import { TitleBar } from "../components/TitleBar";
import { resolveTankId } from "../core/blitz/resolveTankId";
import { buildTechTreeLine } from "../core/blitzkit/buildTechTreeLine";
import { iconPng } from "../core/blitzkit/iconPng";
import { tankDefinitions } from "../core/blitzkit/nonBlockingPromises";
import { resolveAncestry } from "../core/blitzkit/resolveAncestry";
import { tankIconPng } from "../core/blitzkit/tankIconPng";
import { addUsernameChoices } from "../core/discord/addUsernameChoices";
import { autocompleteTanks } from "../core/discord/autocompleteTanks";
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
import { localizationObject } from "../core/discord/localizationObject";
import { resolvePlayerFromCommand } from "../core/discord/resolvePlayerFromCommand";
import { translator } from "../core/localization/translator";
import { type CommandRegistry } from "../events/interactionCreate";
import { theme } from "../stitches.config";
export const researchCommand = new Promise<CommandRegistry>((resolve) => {
const { strings } = translator(Locale.EnglishUS);
resolve({
command: createLocalizedCommand('research')
command: createLocalizedCommand("research")
.addStringOption((option) =>
option
.setName(strings.bot.commands.research.options.target_tank.name)
@@ -41,20 +41,20 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
(strings) =>
strings.bot.commands.research.options.target_tank.name,
undefined,
true,
),
true
)
)
.setDescription(
strings.bot.commands.research.options.target_tank.description,
strings.bot.commands.research.options.target_tank.description
)
.setDescriptionLocalizations(
localizationObject(
(strings) =>
strings.bot.commands.research.options.target_tank.description,
),
strings.bot.commands.research.options.target_tank.description
)
)
.setAutocomplete(true)
.setRequired(true),
.setRequired(true)
)
.addStringOption((option) =>
option
@@ -64,20 +64,20 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
(strings) =>
strings.bot.commands.research.options.starting_tank.name,
undefined,
true,
),
true
)
)
.setDescription(
strings.bot.commands.research.options.starting_tank.description,
strings.bot.commands.research.options.starting_tank.description
)
.setDescriptionLocalizations(
localizationObject(
(strings) =>
strings.bot.commands.research.options.starting_tank.description,
),
strings.bot.commands.research.options.starting_tank.description
)
)
.setAutocomplete(true)
.setRequired(false),
.setRequired(false)
)
.addStringOption(addUsernameChoices),
@@ -85,11 +85,11 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
const { strings, unwrap } = translator(interaction.locale);
const awaitedTankDefinitions = await tankDefinitions;
const targetTankId = await resolveTankId(
interaction.options.getString('target-tank', true),
interaction.options.getString("target-tank", true),
interaction.locale,
true,
true
);
const startingTankRaw = interaction.options.getString('starting-tank');
const startingTankRaw = interaction.options.getString("starting-tank");
const targetTank = awaitedTankDefinitions.tanks[targetTankId];
const { id, region } = await resolvePlayerFromCommand(interaction);
let startingTankId: number;
@@ -99,46 +99,45 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
startingTankId = await resolveTankId(
startingTankRaw,
interaction.locale,
true,
true
);
if (targetTankId === startingTankId) {
return literals(
strings.bot.commands.research.errors.start_end_equal,
[escapeMarkdown(unwrap(targetTank.name))],
{ tank: escapeMarkdown(unwrap(targetTank.name)) }
);
}
const startingTank = awaitedTankDefinitions.tanks[startingTankId];
if (startingTank.tier > targetTank.tier) {
return literals(strings.bot.commands.research.errors.unordered, [
escapeMarkdown(unwrap(startingTank.name)),
escapeMarkdown(unwrap(targetTank.name)),
escapeMarkdown(unwrap(targetTank.name)),
]);
return literals(strings.bot.commands.research.errors.unordered, {
tank_1: escapeMarkdown(unwrap(startingTank.name)),
tank_2: escapeMarkdown(unwrap(targetTank.name)),
});
}
if (!targetTankAncestry.includes(startingTankId)) {
return literals(
strings.bot.commands.research.errors.tanks_not_on_line,
[
escapeMarkdown(unwrap(targetTank.name)),
escapeMarkdown(unwrap(startingTank.name)),
],
{
tank_1: escapeMarkdown(unwrap(targetTank.name)),
tank_2: escapeMarkdown(unwrap(startingTank.name)),
}
);
}
} else {
if (targetTank.type !== TankType.RESEARCHABLE) {
return literals(strings.bot.commands.research.errors.non_tech_tree, [
escapeMarkdown(unwrap(targetTank.name)),
]);
return literals(strings.bot.commands.research.errors.non_tech_tree, {
tank: escapeMarkdown(unwrap(targetTank.name)),
});
}
if (targetTank.tier === 1) {
return literals(strings.bot.commands.research.errors.tier_1, [
escapeMarkdown(unwrap(targetTank.name)),
]);
return literals(strings.bot.commands.research.errors.tier_1, {
tank: escapeMarkdown(unwrap(targetTank.name)),
});
}
const tankStats = await getTankStats(region, id);
@@ -150,18 +149,18 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
if (tankStats.some(({ tank_id }) => tank_id === targetTankId)) {
return literals(
strings.bot.commands.research.errors.already_researched,
[escapeMarkdown(unwrap(targetTank.name))],
{ tank: escapeMarkdown(unwrap(targetTank.name)) }
);
}
const foundAncestor = targetTankAncestry.find(
(id) =>
awaitedTankDefinitions.tanks[id].tier === 1 ||
tankStats.some(({ tank_id }) => tank_id === id),
tankStats.some(({ tank_id }) => tank_id === id)
);
if (foundAncestor === undefined) {
throw new Error('No first owned tech tree found.');
throw new Error("No first owned tech tree found.");
}
startingTankId = foundAncestor;
@@ -169,12 +168,12 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
const techTreeLine = await buildTechTreeLine(
startingTankId,
targetTankId,
targetTankId
);
const line = [...techTreeLine, startingTankId].reverse();
const { nickname } = await getAccountInfo(region, id);
const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan;
const clan = (await getClanAccountInfo(region, id, ["clan"]))?.clan;
const clanImage = clan ? emblemURL(clan.emblem_set_id) : undefined;
const costs = await Promise.all(
line.map(async (id) => {
@@ -187,18 +186,18 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
tank.turrets.forEach((turret) => {
if (
turret.research_cost !== undefined &&
typeof turret.research_cost.research_cost_type!.value === 'number'
typeof turret.research_cost.research_cost_type!.value === "number"
) {
turretXps.set(
turret.id,
turret.research_cost.research_cost_type!.value,
turret.research_cost.research_cost_type!.value
);
}
turret.guns.forEach((gun) => {
if (
gun.research_cost !== undefined &&
typeof gun.research_cost.research_cost_type!.value === 'number'
typeof gun.research_cost.research_cost_type!.value === "number"
) {
gunXps.set(gun.id, gun.research_cost.research_cost_type!.value);
}
@@ -208,11 +207,11 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
tank.engines.forEach((engine) => {
if (
engine.research_cost !== undefined &&
typeof engine.research_cost.research_cost_type!.value === 'number'
typeof engine.research_cost.research_cost_type!.value === "number"
) {
engineXps.set(
engine.id,
engine.research_cost.research_cost_type!.value,
engine.research_cost.research_cost_type!.value
);
}
});
@@ -220,11 +219,11 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
tank.tracks.forEach((track) => {
if (
track.research_cost !== undefined &&
typeof track.research_cost.research_cost_type!.value === 'number'
typeof track.research_cost.research_cost_type!.value === "number"
) {
trackXps.set(
track.id,
track.research_cost.research_cost_type!.value,
track.research_cost.research_cost_type!.value
);
}
});
@@ -249,10 +248,10 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
purchase: tank.price.value,
equipment: equipmentPriceMatrix[tank.tier].reduce(
(a, b) => a + 3 * b,
0,
0
),
};
}),
})
);
const image = (
@@ -267,14 +266,14 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
<div
style={{
display: 'flex',
flexDirection: 'column',
display: "flex",
flexDirection: "column",
gap: 8,
}}
>
<div
style={{
display: 'flex',
display: "flex",
borderRadius: 4,
backgroundColor: theme.colors.appBackground2,
border: theme.borderStyles.subtle,
@@ -285,10 +284,10 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
<div
style={{
flex: 1,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
position: 'relative',
display: "flex",
alignItems: "center",
justifyContent: "center",
position: "relative",
right: 24,
}}
>
@@ -304,23 +303,23 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
<div
style={{
flex: 1,
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
display: "flex",
flexDirection: "column",
justifyContent: "center",
gap: 8,
padding: '16px 0',
padding: "16px 0",
}}
>
<div
style={{
display: 'flex',
display: "flex",
gap: 4,
alignItems: 'center',
alignItems: "center",
}}
>
<img
alt="XP icon"
src={await iconPng(asset('icons/currencies/xp.webp'))}
src={await iconPng(asset("icons/currencies/xp.webp"))}
width={16}
height={16}
/>
@@ -330,7 +329,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
color: theme.colors.textHighContrast,
}}
>
Experience:{' '}
Experience:{" "}
{costs
.reduce((a, b) => a + (b.research ?? 0) + b.upgrades, 0)
.toLocaleString(interaction.locale)}
@@ -339,14 +338,14 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
<div
style={{
display: 'flex',
display: "flex",
gap: 4,
alignItems: 'center',
alignItems: "center",
}}
>
<img
alt="Silver icon"
src={await iconPng(asset('icons/currencies/silver.webp'))}
src={await iconPng(asset("icons/currencies/silver.webp"))}
width={16}
height={16}
/>
@@ -356,7 +355,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
color: theme.colors.textHighContrast,
}}
>
Purchase:{' '}
Purchase:{" "}
{costs
.reduce((a, b) => a + b.purchase, 0)
.toLocaleString(interaction.locale)}
@@ -365,14 +364,14 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
<div
style={{
display: 'flex',
display: "flex",
gap: 4,
alignItems: 'center',
alignItems: "center",
}}
>
<img
alt="Silver icon"
src={await iconPng(asset('icons/currencies/silver.webp'))}
src={await iconPng(asset("icons/currencies/silver.webp"))}
width={16}
height={16}
/>
@@ -382,7 +381,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
color: theme.colors.textHighContrast,
}}
>
Equipment:{' '}
Equipment:{" "}
{costs
.reduce((a, b) => a + b.equipment, 0)
.toLocaleString(interaction.locale)}
@@ -399,7 +398,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
<div
key={id}
style={{
display: 'flex',
display: "flex",
borderRadius: 4,
backgroundColor: theme.colors.appBackground2,
border: theme.borderStyles.subtle,
@@ -408,9 +407,9 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
<div
style={{
width: 48,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<span
@@ -426,9 +425,9 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
<div
style={{
flex: 1,
display: 'flex',
position: 'relative',
right: '5%',
display: "flex",
position: "relative",
right: "5%",
}}
>
<img
@@ -437,23 +436,23 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
style={{
height: 100,
width: 100,
objectFit: 'contain',
position: 'absolute',
left: '25%',
objectFit: "contain",
position: "absolute",
left: "25%",
top: 0,
}}
/>
<div
style={{
display: 'flex',
display: "flex",
gap: 4,
alignItems: 'center',
justifyContent: 'center',
position: 'absolute',
alignItems: "center",
justifyContent: "center",
position: "absolute",
bottom: 8,
left: '50%',
transform: 'translateX(-50%)',
left: "50%",
transform: "translateX(-50%)",
}}
>
<img
@@ -476,23 +475,23 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
<div
style={{
flex: 1,
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
display: "flex",
flexDirection: "column",
justifyContent: "center",
gap: 8,
padding: '16px 0',
padding: "16px 0",
}}
>
<div
style={{
display: 'flex',
display: "flex",
gap: 4,
alignItems: 'center',
alignItems: "center",
}}
>
<img
alt="XP icon"
src={await iconPng(asset('icons/currencies/xp.webp'))}
src={await iconPng(asset("icons/currencies/xp.webp"))}
width={16}
height={16}
/>
@@ -504,11 +503,11 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
>
{literals(
strings.bot.commands.research.body.research,
[
`${costs[index].research?.toLocaleString(
interaction.locale,
)}`,
],
{
cost: costs[index].research?.toLocaleString(
interaction.locale
),
}
)}
</span>
</div>
@@ -516,15 +515,15 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
{costs[index].upgrades > 0 && (
<div
style={{
display: 'flex',
display: "flex",
gap: 4,
alignItems: 'center',
alignItems: "center",
}}
>
<img
alt="Free XP icon"
src={await iconPng(
asset('icons/currencies/free-xp.webp'),
asset("icons/currencies/free-xp.webp")
)}
width={16}
height={16}
@@ -537,7 +536,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
>
{literals(
strings.bot.commands.research.body.upgrades,
[costs[index].upgrades.toLocaleString()],
{ cost: costs[index].upgrades.toLocaleString() }
)}
</span>
</div>
@@ -545,15 +544,15 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
<div
style={{
display: 'flex',
display: "flex",
gap: 4,
alignItems: 'center',
alignItems: "center",
}}
>
<img
alt="Silver icon"
src={await iconPng(
asset('icons/currencies/silver.webp'),
asset("icons/currencies/silver.webp")
)}
width={16}
height={16}
@@ -566,26 +565,26 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
>
{literals(
strings.bot.commands.research.body.purchase,
[
costs[index].purchase.toLocaleString(
interaction.locale,
{
cost: costs[index].purchase.toLocaleString(
interaction.locale
),
],
}
)}
</span>
</div>
<div
style={{
display: 'flex',
display: "flex",
gap: 4,
alignItems: 'center',
alignItems: "center",
}}
>
<img
alt="Silver icon"
src={await iconPng(
asset('icons/currencies/silver.webp'),
asset("icons/currencies/silver.webp")
)}
width={16}
height={16}
@@ -598,18 +597,18 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
>
{literals(
strings.bot.commands.research.body.equipment,
[
costs[index].equipment.toLocaleString(
interaction.locale,
{
cost: costs[index].equipment.toLocaleString(
interaction.locale
),
],
}
)}
</span>
</div>
</div>
</div>
);
}),
})
)}
</div>
</CommandWrapper>
@@ -621,7 +620,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
},
autocomplete(interaction) {
autocompleteTanks(interaction, true, ['target-tank', 'starting-tank']);
autocompleteTanks(interaction, true, ["target-tank", "starting-tank"]);
},
});
});
+28 -28
View File
@@ -1,23 +1,23 @@
import { ClanList, fetchBlitz, Region } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import markdownEscape from 'markdown-escape';
import { addClanChoices } from '../core/discord/addClanChoices';
import { addRegionChoices } from '../core/discord/addRegionChoices';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
import { embedInfo } from '../core/discord/embedInfo';
import { localizationObject } from '../core/discord/localizationObject';
import { translator } from '../core/localization/translator';
import { CommandRegistry } from '../events/interactionCreate';
import { type ClanList, fetchBlitz, type Region } from "@blitzkit/core";
import { literals } from "@blitzkit/i18n";
import { Locale } from "discord.js";
import markdownEscape from "markdown-escape";
import { addClanChoices } from "../core/discord/addClanChoices";
import { addRegionChoices } from "../core/discord/addRegionChoices";
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
import { embedInfo } from "../core/discord/embedInfo";
import { localizationObject } from "../core/discord/localizationObject";
import { translator } from "../core/localization/translator";
import type { CommandRegistry } from "../events/interactionCreate";
const DEFAULT_LIMIT = 25;
export const searchClansCommand = new Promise<CommandRegistry>((resolve) => {
resolve({
command: createLocalizedCommand('search-clans')
command: createLocalizedCommand("search-clans")
.addStringOption(addRegionChoices)
.addStringOption((option) =>
addClanChoices(option).setAutocomplete(false),
addClanChoices(option).setAutocomplete(false)
)
.addIntegerOption((option) => {
const { strings } = translator(Locale.EnglishUS);
@@ -28,21 +28,21 @@ export const searchClansCommand = new Promise<CommandRegistry>((resolve) => {
localizationObject(
(strings) => strings.bot.commands.search_clans.options.limit.name,
undefined,
true,
),
true
)
)
.setDescription(
literals(
strings.bot.commands.search_clans.options.limit.description,
[`${DEFAULT_LIMIT}`],
),
{ limit: DEFAULT_LIMIT }
)
)
.setDescriptionLocalizations(
localizationObject(
(strings) =>
strings.bot.commands.search_clans.options.limit.description,
[`${DEFAULT_LIMIT}`],
),
{ limit: DEFAULT_LIMIT }
)
)
.setMinValue(1)
.setMaxValue(100);
@@ -50,24 +50,24 @@ export const searchClansCommand = new Promise<CommandRegistry>((resolve) => {
async handler(interaction) {
const { strings } = translator(interaction.locale);
const region = interaction.options.getString('region') as Region;
const clan = interaction.options.getString('clan')!;
const limit = interaction.options.getInteger('limit') ?? DEFAULT_LIMIT;
const clanList = await fetchBlitz<ClanList>(region, 'clans/list', {
const region = interaction.options.getString("region") as Region;
const clan = interaction.options.getString("clan")!;
const limit = interaction.options.getInteger("limit") ?? DEFAULT_LIMIT;
const clanList = await fetchBlitz<ClanList>(region, "clans/list", {
search: clan,
limit,
});
return embedInfo(
literals(strings.bot.commands.search_clans.body.title, [
markdownEscape(clan),
strings.common.regions.normal[region],
]),
literals(strings.bot.commands.search_clans.body.title, {
search: markdownEscape(clan),
region: strings.common.regions.normal[region],
}),
clanList.length === 0
? strings.bot.commands.search_clans.body.no_results
: `\`\`\`\n${clanList
.map((clan) => `${clan.name} [${clan.tag}]`)
.join('\n')}\n\`\`\``,
.join("\n")}\n\`\`\``
);
},
});
+32 -32
View File
@@ -1,19 +1,19 @@
import {
AccountList,
type AccountList,
fetchBlitz,
Region,
type Region,
usernamePattern,
} from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import markdownEscape from 'markdown-escape';
import { addRegionChoices } from '../core/discord/addRegionChoices';
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
import { embedInfo } from '../core/discord/embedInfo';
import { localizationObject } from '../core/discord/localizationObject';
import { translator } from '../core/localization/translator';
import { CommandRegistry } from '../events/interactionCreate';
} from "@blitzkit/core";
import { literals } from "@blitzkit/i18n";
import { Locale } from "discord.js";
import markdownEscape from "markdown-escape";
import { addRegionChoices } from "../core/discord/addRegionChoices";
import { addUsernameChoices } from "../core/discord/addUsernameChoices";
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
import { embedInfo } from "../core/discord/embedInfo";
import { localizationObject } from "../core/discord/localizationObject";
import { translator } from "../core/localization/translator";
import type { CommandRegistry } from "../events/interactionCreate";
const DEFAULT_LIMIT = 25;
@@ -21,10 +21,10 @@ export const searchPlayersCommand = new Promise<CommandRegistry>((resolve) => {
const { strings } = translator(Locale.EnglishUS);
resolve({
command: createLocalizedCommand('search-players')
command: createLocalizedCommand("search-players")
.addStringOption(addRegionChoices)
.addStringOption((option) =>
addUsernameChoices(option).setAutocomplete(false).setRequired(true),
addUsernameChoices(option).setAutocomplete(false).setRequired(true)
)
.addIntegerOption((option) =>
option
@@ -34,49 +34,49 @@ export const searchPlayersCommand = new Promise<CommandRegistry>((resolve) => {
(strings) =>
strings.bot.commands.search_players.options.limit.name,
undefined,
true,
),
true
)
)
.setDescription(
literals(
strings.bot.commands.search_players.options.limit.description,
[`${DEFAULT_LIMIT}`],
),
{ limit: DEFAULT_LIMIT }
)
)
.setDescriptionLocalizations(
localizationObject(
(strings) =>
strings.bot.commands.search_players.options.limit.description,
[`${DEFAULT_LIMIT}`],
),
{ limit: DEFAULT_LIMIT }
)
)
.setMinValue(1)
.setMaxValue(100),
.setMaxValue(100)
),
async handler(interaction) {
const { strings } = translator(interaction.locale);
const region = interaction.options.getString('region') as Region;
const name = interaction.options.getString('username')!;
const limit = interaction.options.getInteger('limit') ?? 25;
const region = interaction.options.getString("region") as Region;
const name = interaction.options.getString("username")!;
const limit = interaction.options.getInteger("limit") ?? 25;
const trimmedSearch = name.trim();
const players = usernamePattern.test(trimmedSearch)
? await fetchBlitz<AccountList>(region, 'account/list', {
? await fetchBlitz<AccountList>(region, "account/list", {
search: trimmedSearch,
limit,
})
: [];
return embedInfo(
literals(strings.bot.commands.search_players.body.title, [
markdownEscape(trimmedSearch),
strings.common.regions.normal[region],
]),
literals(strings.bot.commands.search_players.body.title, {
search: markdownEscape(trimmedSearch),
region: strings.common.regions.normal[region],
}),
`\`\`\`${
players.length === 0
? strings.bot.commands.search_players.body.no_results
: players.map((player) => player.nickname).join('\n')
}\`\`\``,
: players.map((player) => player.nickname).join("\n")
}\`\`\``
);
},
});
+32 -32
View File
@@ -1,50 +1,50 @@
import { emblemURL, getAccountInfo, getClanAccountInfo } from '@blitzkit/core';
import { Locale } from 'discord.js';
import { AllStatsOverview } from '../components/AllStatsOverview';
import { CommandWrapper } from '../components/CommandWrapper';
import { NoData } from '../components/NoData';
import { TitleBar } from '../components/TitleBar';
import { filtersToDescription } from '../core/blitzkit/filtersToDescription';
import { filterStats, StatFilters } from '../core/blitzstars/filterStats';
import { getBlitzStarsLinkButton } from '../core/blitzstars/getBlitzStarsLinkButton';
import { getStatsInPeriod } from '../core/blitzstars/getStatsInPeriod';
import { addPeriodicFilterOptions } from '../core/discord/addPeriodicFilterOptions';
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
import { autocompleteTanks } from '../core/discord/autocompleteTanks';
import { autocompleteUsername } from '../core/discord/autocompleteUsername';
import { buttonRefresh } from '../core/discord/buttonRefresh';
import { commandToURL } from '../core/discord/commandToURL';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
import { getCustomPeriodParams } from '../core/discord/getCustomPeriodParams';
import { getFiltersFromButton } from '../core/discord/getFiltersFromButton';
import { getFiltersFromCommand } from '../core/discord/getFiltersFromCommand';
import { resolvePeriodFromButton } from '../core/discord/resolvePeriodFromButton';
import { emblemURL, getAccountInfo, getClanAccountInfo } from "@blitzkit/core";
import { Locale } from "discord.js";
import { AllStatsOverview } from "../components/AllStatsOverview";
import { CommandWrapper } from "../components/CommandWrapper";
import { NoData } from "../components/NoData";
import { TitleBar } from "../components/TitleBar";
import { filtersToDescription } from "../core/blitzkit/filtersToDescription";
import { filterStats, type StatFilters } from "../core/blitzstars/filterStats";
import { getBlitzStarsLinkButton } from "../core/blitzstars/getBlitzStarsLinkButton";
import { getStatsInPeriod } from "../core/blitzstars/getStatsInPeriod";
import { addPeriodicFilterOptions } from "../core/discord/addPeriodicFilterOptions";
import { addUsernameChoices } from "../core/discord/addUsernameChoices";
import { autocompleteTanks } from "../core/discord/autocompleteTanks";
import { autocompleteUsername } from "../core/discord/autocompleteUsername";
import { buttonRefresh } from "../core/discord/buttonRefresh";
import { commandToURL } from "../core/discord/commandToURL";
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
import { getCustomPeriodParams } from "../core/discord/getCustomPeriodParams";
import { getFiltersFromButton } from "../core/discord/getFiltersFromButton";
import { getFiltersFromCommand } from "../core/discord/getFiltersFromCommand";
import { resolvePeriodFromButton } from "../core/discord/resolvePeriodFromButton";
import {
ResolvedPeriod,
type ResolvedPeriod,
resolvePeriodFromCommand,
} from '../core/discord/resolvePeriodFromCommand';
import { resolvePlayerFromButton } from '../core/discord/resolvePlayerFromButton';
} from "../core/discord/resolvePeriodFromCommand";
import { resolvePlayerFromButton } from "../core/discord/resolvePlayerFromButton";
import {
ResolvedPlayer,
type ResolvedPlayer,
resolvePlayerFromCommand,
} from '../core/discord/resolvePlayerFromCommand';
import { CommandRegistry } from '../events/interactionCreate';
} from "../core/discord/resolvePlayerFromCommand";
import type { CommandRegistry } from "../events/interactionCreate";
async function render(
{ region, id }: ResolvedPlayer,
{ start, end, name }: ResolvedPeriod,
filters: StatFilters,
locale: Locale,
locale: Locale
) {
const { nickname } = await getAccountInfo(region, id);
const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan;
const clan = (await getClanAccountInfo(region, id, ["clan"]))?.clan;
const clanImage = clan ? emblemURL(clan.emblem_set_id) : undefined;
const diffedTankStats = await getStatsInPeriod(
region,
id,
start,
end,
locale,
locale
);
const { stats, supplementary } = await filterStats(diffedTankStats, filters);
const filterDescriptions = await filtersToDescription(filters, locale);
@@ -72,8 +72,8 @@ async function render(
export const statsCommand = new Promise<CommandRegistry>(async (resolve) => {
resolve({
command: await addPeriodicFilterOptions(
createLocalizedCommand('stats'),
(option) => option.addStringOption(addUsernameChoices),
createLocalizedCommand("stats"),
(option) => option.addStringOption(addUsernameChoices)
),
async handler(interaction) {
+1 -1
View File
@@ -14,7 +14,7 @@ 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 { CommandRegistry } from '../events/interactionCreate';
import { type CommandRegistry } from '../events/interactionCreate';
import { renderBreakdown } from './breakdown/render';
export const todayCommand = new Promise<CommandRegistry>(async (resolve) => {
@@ -1,8 +1,8 @@
import { ComponentProps } from 'react';
import { type ComponentProps } from "react";
export interface GlowProps extends ComponentProps<'div'> {
export interface GlowProps extends ComponentProps<"div"> {
color: string;
direction?: 'default' | 'reverse';
direction?: "default" | "reverse";
rotation?: number;
startOpacity?: number;
endOpacity?: number;
@@ -12,7 +12,7 @@ const width = 16;
export function Glow({
color,
direction = 'default',
direction = "default",
rotation = 0,
style,
startOpacity = 0,
@@ -21,20 +21,20 @@ export function Glow({
}: GlowProps) {
const color1 = `${color}${Math.round(endOpacity * 255)
.toString(16)
.padStart(2, '0')}`;
.padStart(2, "0")}`;
const color2 = `${color}${Math.round(startOpacity * 255)
.toString(16)
.padStart(2, '0')}`;
.padStart(2, "0")}`;
const background = `linear-gradient(${
direction === 'default' ? 0 : 180
direction === "default" ? 0 : 180
}deg, ${color1} 0%, ${color2} 100%)`;
return (
<div
style={{
display: 'flex',
alignItems: direction === 'default' ? 'flex-end' : 'flex-start',
justifyContent: 'center',
display: "flex",
alignItems: direction === "default" ? "flex-end" : "flex-start",
justifyContent: "center",
transform: `rotate(${rotation}deg)`,
...style,
}}
@@ -1,7 +1,7 @@
import {
getWN8Percentile,
Percentile,
SupplementaryStats,
type SupplementaryStats,
} from '@blitzkit/core';
import { Locale } from 'discord.js';
import { translator } from '../../core/localization/translator';
@@ -1,4 +1,4 @@
import { ReactNode } from 'react';
import { type ReactNode } from "react";
interface RootProps {
children: ReactNode;
@@ -8,7 +8,7 @@ export function Root({ children }: RootProps) {
return (
<div
className="session-tracker-root"
style={{ display: 'flex', flexDirection: 'column', gap: 8 }}
style={{ display: "flex", flexDirection: "column", gap: 8 }}
>
{children}
</div>
@@ -6,8 +6,8 @@ import {
} from '@blitzkit/core';
import { createColors } from 'bepaint';
import {
AccentColor,
GrayColor,
type AccentColor,
type GrayColor,
PALETTES,
} from '../../../core/radix/radixColors';
import { RowStat } from './RowStat';
@@ -1,11 +1,11 @@
import { Percentile } from '@blitzkit/core';
import { createColors } from 'bepaint';
import { Percentile } from "@blitzkit/core";
import { createColors } from "bepaint";
import {
AccentColor,
GrayColor,
type AccentColor,
type GrayColor,
PALETTES,
} from '../../../core/radix/radixColors';
import { PercentileIndicator } from '../../PercentileIndicator';
} from "../../../core/radix/radixColors";
import { PercentileIndicator } from "../../PercentileIndicator";
interface RowStatProps {
name: string;
@@ -22,7 +22,7 @@ export function RowStat({
delta,
percentile,
minimized = false,
color = 'slate',
color = "slate",
}: RowStatProps) {
const theme = createColors(PALETTES[`${color}Dark`]);
@@ -30,29 +30,29 @@ export function RowStat({
<div
className="session-tracker-row-stat"
style={{
display: 'flex',
flexDirection: 'column',
display: "flex",
flexDirection: "column",
gap: 4,
alignItems: 'center',
justifyContent: 'center',
alignItems: "center",
justifyContent: "center",
flex: 1,
}}
>
<div
style={{
display: 'flex',
display: "flex",
gap: 4,
alignItems: 'center',
justifyContent: 'center',
alignItems: "center",
justifyContent: "center",
}}
>
{percentile === undefined && delta !== undefined && delta !== 0 && (
<img
alt={delta > 0 ? 'Increase' : 'Decrease'}
alt={delta > 0 ? "Increase" : "Decrease"}
src={
(delta ?? 0) > 0
? 'https://i.imgur.com/qbjiXa1.png'
: 'https://i.imgur.com/3uyNhun.png'
? "https://i.imgur.com/qbjiXa1.png"
: "https://i.imgur.com/3uyNhun.png"
}
style={{ width: 12, height: 12 }}
/>
@@ -66,7 +66,7 @@ export function RowStat({
color: theme.textHighContrast,
fontWeight: 700,
fontSize: 16,
textAlign: 'center',
textAlign: "center",
}}
>
{value}
@@ -77,7 +77,7 @@ export function RowStat({
<span
style={{
color: theme.textLowContrast,
textAlign: 'center',
textAlign: "center",
fontSize: 12,
}}
>
@@ -1,5 +1,5 @@
import { ReactNode } from 'react';
import { theme } from '../stitches.config';
import { type ReactNode } from "react";
import { theme } from "../stitches.config";
interface CommandWrapperProps {
children: ReactNode;
@@ -10,14 +10,14 @@ export function CommandWrapper({ children, fat }: CommandWrapperProps) {
return (
<div
style={{
display: 'flex',
flexDirection: 'column',
display: "flex",
flexDirection: "column",
padding: `16px ${fat ? 0 : 16}px`,
gap: 16,
width: '100%',
width: "100%",
color: theme.colors.textHighContrast,
backgroundColor: theme.colors.appBackground1,
position: 'relative',
position: "relative",
}}
>
{children}
@@ -1,8 +1,8 @@
import {
BlitzStats,
type BlitzStats,
getWN8Percentile,
isNumber,
SupplementaryStats,
type SupplementaryStats,
} from '@blitzkit/core';
import { Locale } from 'discord.js';
import { translator } from '../../core/localization/translator';
@@ -1,5 +1,5 @@
import { GenericStat } from '..';
import { GenericStatsRow } from './GenericStatsRow';
import { type GenericStat } from "..";
import { GenericStatsRow } from "./GenericStatsRow";
interface GenericStatsColumnProps {
stats: GenericStat[];
@@ -7,7 +7,7 @@ interface GenericStatsColumnProps {
export function GenericStatsColumn({ stats }: GenericStatsColumnProps) {
return (
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 8 }}>
<div style={{ flex: 1, display: "flex", flexDirection: "column", gap: 8 }}>
{stats.map((stat) => (
<GenericStatsRow key={stat[0]} stat={stat} />
))}
@@ -1,6 +1,6 @@
import { GenericStat } from '..';
import { theme } from '../../../stitches.config';
import { PercentileIndicator } from '../../PercentileIndicator';
import { type GenericStat } from "..";
import { theme } from "../../../stitches.config";
import { PercentileIndicator } from "../../PercentileIndicator";
interface GenericStatsRowProps {
stat: GenericStat;
@@ -10,18 +10,18 @@ export function GenericStatsRow({ stat }: GenericStatsRowProps) {
return (
<div
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
display: "flex",
justifyContent: "space-between",
alignItems: "center",
}}
>
<span
style={{
color: theme.colors.textLowContrast,
fontSize: 16,
whiteSpace: 'nowrap',
whiteSpace: "nowrap",
maxWidth: 128,
overflow: 'hidden',
overflow: "hidden",
}}
>
{stat[0]}
@@ -32,9 +32,9 @@ export function GenericStatsRow({ stat }: GenericStatsRowProps) {
color: theme.colors.textHighContrast,
fontWeight: 700,
fontSize: 16,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
display: "flex",
alignItems: "center",
justifyContent: "center",
gap: 4,
}}
>
@@ -1,6 +1,6 @@
import { times } from 'lodash-es';
import { theme } from '../../../../stitches.config';
import { MarginInputProps } from '../Root';
import { type MarginInputProps } from '../Root';
import { MarginOrientation } from './constants';
interface MarginProps extends MarginInputProps {
@@ -1,9 +1,9 @@
import { times } from 'lodash-es';
import { ReactNode } from 'react';
import { theme } from '../../../../stitches.config';
import { Margin } from '../Margin';
import { MarginOrientation } from '../Margin/constants';
import { GRAPH_HEIGHT, GRAPH_WIDTH } from './constants';
import { times } from "lodash-es";
import { type ReactNode } from "react";
import { theme } from "../../../../stitches.config";
import { Margin } from "../Margin";
import { MarginOrientation } from "../Margin/constants";
import { GRAPH_HEIGHT, GRAPH_WIDTH } from "./constants";
export interface MarginInputProps {
min: number;
@@ -32,12 +32,12 @@ export function Root({
xTitle,
}: RootProps) {
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
<div
style={{
paddingLeft: 64,
display: 'flex',
justifyContent: 'space-between',
display: "flex",
justifyContent: "space-between",
}}
>
<span style={{ fontSize: 16, color: theme.colors.textHighContrast }}>
@@ -48,7 +48,7 @@ export function Root({
</span>
</div>
<div style={{ display: 'flex', gap: 16 }}>
<div style={{ display: "flex", gap: 16 }}>
{verticalMargin && (
<Margin {...verticalMargin} verticalSeparations={separations} />
)}
@@ -94,9 +94,9 @@ export function Root({
<div
style={{
paddingLeft: 64,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<span style={{ fontSize: 16, color: theme.colors.textLowContrast }}>
@@ -1,7 +1,7 @@
import { ComponentProps } from 'react';
import { theme } from '../../../stitches.config';
import { type ComponentProps } from "react";
import { theme } from "../../../stitches.config";
interface ItemProps extends ComponentProps<'div'> {
interface ItemProps extends ComponentProps<"div"> {
position: number;
deltaPosition?: number;
score?: number;
@@ -25,15 +25,15 @@ export function Item({
<div
{...props}
style={{
cursor: 'pointer',
display: 'flex',
cursor: "pointer",
display: "flex",
padding: 8,
backgroundColor: highlight
? theme.colors.componentInteractive_blue
: theme.colors.componentInteractive,
borderRadius: 4,
alignItems: 'center',
justifyContent: 'center',
alignItems: "center",
justifyContent: "center",
gap: 8,
}}
>
@@ -50,9 +50,9 @@ export function Item({
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
display: "flex",
alignItems: "center",
justifyContent: "center",
gap: 4,
}}
>
@@ -83,22 +83,22 @@ export function Item({
{deltaPosition !== undefined && deltaPosition !== 0 && (
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
display: "flex",
alignItems: "center",
justifyContent: "center",
gap: 4,
}}
>
<img
alt={deltaPosition > 0 ? 'Increase' : 'Decrease'}
alt={deltaPosition > 0 ? "Increase" : "Decrease"}
style={{
width: 8,
height: 8,
}}
src={
deltaPosition > 0
? 'https://i.imgur.com/qbjiXa1.png'
: 'https://i.imgur.com/3uyNhun.png'
? "https://i.imgur.com/qbjiXa1.png"
: "https://i.imgur.com/3uyNhun.png"
}
/>
<span
@@ -119,11 +119,11 @@ export function Item({
<div
style={{
display: 'flex',
display: "flex",
gap: 4,
alignItems: 'center',
alignItems: "center",
width: 96,
justifyContent: 'flex-end',
justifyContent: "flex-end",
}}
>
{deltaScore !== 0 && deltaScore !== undefined && (
@@ -140,15 +140,15 @@ export function Item({
)}
{deltaScore !== 0 && deltaScore !== undefined && (
<img
alt={deltaScore > 0 ? 'Increase' : 'Decrease'}
alt={deltaScore > 0 ? "Increase" : "Decrease"}
style={{
width: 8,
height: 8,
}}
src={
deltaScore > 0
? 'https://i.imgur.com/qbjiXa1.png'
: 'https://i.imgur.com/3uyNhun.png'
? "https://i.imgur.com/qbjiXa1.png"
: "https://i.imgur.com/3uyNhun.png"
}
/>
)}
@@ -1,4 +1,4 @@
import { ReactNode } from 'react';
import { type ReactNode } from 'react';
interface RootProps {
children: ReactNode;
@@ -1,4 +1,4 @@
import { ReactNode } from 'react';
import { type ReactNode } from 'react';
interface ColumnProps {
children: ReactNode;
@@ -1,4 +1,4 @@
import { ReactNode } from 'react';
import { type ReactNode } from "react";
interface RootProps {
children: ReactNode;
@@ -8,8 +8,8 @@ export function Root({ children }: RootProps) {
return (
<div
style={{
display: 'flex',
flexDirection: 'column',
display: "flex",
flexDirection: "column",
gap: 16,
}}
>
@@ -1,4 +1,4 @@
import { ReactNode } from 'react';
import { type ReactNode } from "react";
interface RowProps {
children: ReactNode;
@@ -8,7 +8,7 @@ export function Row({ children }: RowProps) {
return (
<div
style={{
display: 'flex',
display: "flex",
gap: 8,
}}
>
+7 -7
View File
@@ -1,5 +1,5 @@
import { TierWeightsRecord } from '@blitzkit/core';
import { theme } from '../stitches.config';
import type { TierWeightsRecord } from "@blitzkit/core";
import { theme } from "../stitches.config";
interface TierWeightsProps {
weights: TierWeightsRecord;
@@ -7,7 +7,7 @@ interface TierWeightsProps {
export function TierWeights({ weights }: TierWeightsProps) {
return (
<div style={{ display: 'flex', gap: 8 }}>
<div style={{ display: "flex", gap: 8 }}>
{Object.entries(weights).map(([tier, weight]) => (
<div
key={tier}
@@ -15,11 +15,11 @@ export function TierWeights({ weights }: TierWeightsProps) {
backgroundColor: theme.colors.appBackground1_light,
flex: weight,
color: theme.colors.textHighContrast_light,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
display: "flex",
alignItems: "center",
justifyContent: "center",
fontSize: 16,
overflow: 'hidden',
overflow: "hidden",
borderRadius: 8,
minWidth: 16,
}}
+18 -16
View File
@@ -1,33 +1,33 @@
import { SEARCH_KEYS } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import { go } from 'fuzzysort';
import markdownEscape from 'markdown-escape';
import { tankDefinitions, tankNames } from '../blitzkit/nonBlockingPromises';
import { UserError } from '../blitzkit/userError';
import { tankNamesTechTreeOnly } from '../discord/autocompleteTanks';
import { translator } from '../localization/translator';
import { SEARCH_KEYS } from "@blitzkit/core";
import { literals } from "@blitzkit/i18n";
import { Locale } from "discord.js";
import { go } from "fuzzysort";
import markdownEscape from "markdown-escape";
import { tankDefinitions, tankNames } from "../blitzkit/nonBlockingPromises";
import { UserError } from "../blitzkit/userError";
import { tankNamesTechTreeOnly } from "../discord/autocompleteTanks";
import { translator } from "../localization/translator";
export async function resolveTankId(
tank: string | number,
locale: Locale,
techTreeOnly = false,
techTreeOnly = false
) {
const { strings } = translator(locale);
const number = typeof tank === 'string' ? Number(tank) : tank;
const number = typeof tank === "string" ? Number(tank) : tank;
if (Number.isNaN(number)) {
const searchResult = go(
`${tank}`,
await (techTreeOnly ? tankNamesTechTreeOnly : tankNames),
{ keys: SEARCH_KEYS, limit: 1 },
{ keys: SEARCH_KEYS, limit: 1 }
);
if (searchResult.length === 0) {
throw new UserError(
literals(strings.bot.common.errors.tank_name_not_found, [
markdownEscape(`${tank}`),
]),
literals(strings.bot.common.errors.tank_name_not_found, {
search: markdownEscape(`${tank}`),
})
);
} else {
return searchResult[0].obj.id;
@@ -39,7 +39,9 @@ export async function resolveTankId(
return number;
} else {
throw new UserError(
literals(strings.bot.common.errors.tank_id_not_found, [`${number}`]),
literals(strings.bot.common.errors.tank_id_not_found, {
search: number,
})
);
}
}
@@ -1,12 +1,12 @@
import { fetchTankDefinitions, TIER_ROMAN_NUMERALS } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import { StatFilters } from '../blitzstars/filterStats';
import { translator } from '../localization/translator';
import { fetchTankDefinitions, TIER_ROMAN_NUMERALS } from "@blitzkit/core";
import { literals } from "@blitzkit/i18n";
import { Locale } from "discord.js";
import { type StatFilters } from "../blitzstars/filterStats";
import { translator } from "../localization/translator";
export async function filtersToDescription(
{ nation, tier, type, class: tankClass, tank }: StatFilters,
locale: Locale,
locale: Locale
) {
const { strings, bkLocale } = translator(locale);
const awaitedTankDefinitions = await fetchTankDefinitions();
@@ -18,11 +18,13 @@ export async function filtersToDescription(
info.push((strings.common.nations as Record<string, string>)[nation]);
if (tier) {
info.push(
literals(strings.bot.common.filters.tier, [TIER_ROMAN_NUMERALS[tier]]),
literals(strings.bot.common.filters.tier, {
tier: TIER_ROMAN_NUMERALS[tier],
})
);
}
if (tankClass) info.push(strings.common.tank_class_short[tankClass]);
if (type) info.push(strings.common.tree_type[type]);
return info.length === 0 ? strings.bot.common.filters.none : info.join(', ');
return info.length === 0 ? strings.bot.common.filters.none : info.join(", ");
}
@@ -1,5 +1,5 @@
import { getTimeDaysAgo, Region } from '@blitzkit/core';
import { PeriodSize } from '../discord/addPeriodSubCommands';
import { getTimeDaysAgo, type Region } from '@blitzkit/core';
import type { PeriodSize } from '../discord/addPeriodSubCommands';
export function getPeriodStart(region: Region, period: PeriodSize) {
if (period === 'career') return 0;
+3 -3
View File
@@ -1,9 +1,9 @@
import sharp from 'sharp';
import sharp from "sharp";
const cache: Record<string, Buffer> = {};
export async function iconPng(url: string) {
if (!url.endsWith('.webp')) return url;
if (!url.endsWith(".webp")) return url;
if (!cache[url]) {
const imageResponse = await fetch(url);
@@ -12,5 +12,5 @@ export async function iconPng(url: string) {
cache[url] = imageBufferPNG;
}
return `data:image/png;base64,${cache[url].toString('base64')}`;
return `data:image/png;base64,${cache[url].toString("base64")}`;
}
+12 -12
View File
@@ -1,10 +1,10 @@
import { TankClass, TankType } from '@blitzkit/core';
import { SupplementaryStats } from '@blitzkit/core/src/blitz/getAccountInfo';
import { calculateWN8 } from '@blitzkit/core/src/statistics/calculateWN8';
import { BlitzStats } from '@blitzkit/core/src/statistics/compositeStats';
import { sumAllStats } from '@blitzkit/core/src/statistics/sumAllStats';
import { tankDefinitions } from '../blitzkit/nonBlockingPromises';
import { blitzStarsTankAverages } from './tankAverages';
import { TankClass, TankType } from "@blitzkit/core";
import type { SupplementaryStats } from "@blitzkit/core/src/blitz/getAccountInfo";
import { calculateWN8 } from "@blitzkit/core/src/statistics/calculateWN8";
import type { BlitzStats } from "@blitzkit/core/src/statistics/compositeStats";
import { sumAllStats } from "@blitzkit/core/src/statistics/sumAllStats";
import { tankDefinitions } from "../blitzkit/nonBlockingPromises";
import { blitzStarsTankAverages } from "./tankAverages";
export interface StatFilters {
nation?: string;
@@ -16,7 +16,7 @@ export interface StatFilters {
export async function filterStats(
{ diff, order }: DiffedTankStats,
filters: StatFilters,
filters: StatFilters
) {
// tank, if provided, takes priority over all other filters
if (filters.tank) filters = { tank: filters.tank };
@@ -45,14 +45,14 @@ export async function filterStats(
const battlesOfTanksWithAverages = filteredOrder.reduce<number>(
(accumulator, id) =>
awaitedTankAverages[id] ? accumulator + diff[id].battles : accumulator,
0,
0
);
const battlesOfTanksWithTankDefinition = filteredOrder.reduce<number>(
(accumulator, id) =>
awaitedTankDefinitions.tanks[id]
? accumulator + diff[id].battles
: accumulator,
0,
0
);
const supplementary = {
WN8:
@@ -63,7 +63,7 @@ export async function filterStats(
calculateWN8(awaitedTankAverages[id].all, diff[id]) *
diff[id].battles
: accumulator,
0,
0
) / battlesOfTanksWithAverages,
tier:
filteredOrder.reduce<number>(
@@ -72,7 +72,7 @@ export async function filterStats(
? accumulator +
awaitedTankDefinitions.tanks[id]!.tier * diff[id].battles
: accumulator,
0,
0
) / battlesOfTanksWithTankDefinition,
} satisfies SupplementaryStats;
@@ -1,17 +1,17 @@
import { getAccountInfo, Region } from '@blitzkit/core';
import { buttonLink } from '../discord/buttonLink';
import { getAccountInfo, type Region } from "@blitzkit/core";
import { buttonLink } from "../discord/buttonLink";
export async function getBlitzStarsLinkButton(
region: Region,
id: number,
tankId?: number,
tankId?: number
) {
const { nickname } = await getAccountInfo(region, id);
return buttonLink(
`https://www.blitzstars.com/player/${region}/${nickname}${
tankId ? `/tank/${tankId!}` : ''
tankId ? `/tank/${tankId!}` : ""
}`,
'BlitzStars',
"BlitzStars"
);
}
@@ -1,11 +1,11 @@
import { Region, emptyAllStats, getAccountInfo } from '@blitzkit/core';
import { type Region, emptyAllStats, getAccountInfo } from '@blitzkit/core';
import {
GetHistoriesOptions,
Histories,
History,
type GetHistoriesOptions,
type Histories,
type History,
getHistoriesDefaultOptions,
} from '../../types/histories';
import { PlayerStats } from './getPlayerStats';
import type { PlayerStats } from './getPlayerStats';
export async function getPlayerHistories(
server: Region,
@@ -1,4 +1,4 @@
import { BlitzStats, Region } from '@blitzkit/core';
import type { BlitzStats, Region } from "@blitzkit/core";
interface IndividualPlayerStats {
clan: { clan_id: number; name: string; tag: string };
@@ -25,13 +25,13 @@ export async function getPlayerStats<
ReturnType = Ids extends number ? PlayerStats : PlayerStats[],
>(region: Region, ids: Ids) {
const stats = await Promise.all(
((typeof ids === 'number' ? [ids] : ids) as number[]).map((id) =>
((typeof ids === "number" ? [ids] : ids) as number[]).map((id) =>
fetch(`https://www.blitzstars.com/api/playerstats/${id}`).then(
(response) => response.json() as Promise<PlayerStats>,
),
),
(response) => response.json() as Promise<PlayerStats>
)
)
);
if (typeof ids === 'number') return stats[0] as ReturnType;
if (typeof ids === "number") return stats[0] as ReturnType;
return stats as ReturnType;
}
@@ -1,11 +1,11 @@
import { BlitzStats, emptyAllStats, Region } from '@blitzkit/core';
import { Locale } from 'discord.js';
import { DiffedTankStats } from './filterStats';
import { type BlitzStats, emptyAllStats, type Region } from "@blitzkit/core";
import { Locale } from "discord.js";
import type { DiffedTankStats } from "./filterStats";
import {
getTankHistories,
TankHistories,
TankHistoryRaw,
} from './getTankHistories';
type TankHistories,
type TankHistoryRaw,
} from "./getTankHistories";
const emptyTankHistoryNode: TankHistoryRaw = {
account_id: 0,
@@ -22,7 +22,7 @@ export async function getStatsInPeriod(
id: number,
start: number,
end: number,
locale: Locale,
locale: Locale
) {
const history = await getTankHistories(server, id, locale, {
includeLatestHistories: true,
@@ -43,8 +43,8 @@ export async function getStatsInPeriod(
// find the ones played within the range
const inRangeTanks = tanks.filter((tankId) =>
tankSortedHistory[tankId].some(
(node) => node.last_battle_time > start && node.last_battle_time < end,
),
(node) => node.last_battle_time > start && node.last_battle_time < end
)
);
const playedTanks: number[] = [];
@@ -1,13 +1,13 @@
import { Region, emptyAllStats, getTankStats } from '@blitzkit/core';
import { Locale } from 'discord.js';
import { type Region, emptyAllStats, getTankStats } from "@blitzkit/core";
import { Locale } from "discord.js";
import {
GetHistoriesOptions,
Histories,
History,
type GetHistoriesOptions,
type Histories,
type History,
getHistoriesDefaultOptions,
} from '../../types/histories';
import { UserError } from '../blitzkit/userError';
import { translator } from '../localization/translator';
} from "../../types/histories";
import { UserError } from "../blitzkit/userError";
import { translator } from "../localization/translator";
export interface TankHistory extends History {
tank_id: number;
@@ -36,12 +36,12 @@ export async function getTankHistories(
server: Region,
id: number,
locale: Locale,
options?: Partial<GetTankHistoriesOptions>,
options?: Partial<GetTankHistoriesOptions>
) {
const { strings } = translator(locale);
const mergedOptions = { ...getTankHistoriesDefaultOptions, ...options };
const tankHistoriesResponse = await fetch(
`https://www.blitzstars.com/api/tankhistories/for/${id}/`,
`https://www.blitzstars.com/api/tankhistories/for/${id}/`
);
const tankHistories = (
(await tankHistoriesResponse.json()) as TankHistoriesRaw
@@ -51,7 +51,7 @@ export 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<number, number> = {};
@@ -73,7 +73,7 @@ export async function getTankHistories(
lastBattles[tank_id] = battles;
return complies;
},
}
);
const latestNodes: TankHistories = [];
@@ -112,7 +112,7 @@ export async function getTankHistories(
all: emptyAllStats,
last_battle_time: -Infinity,
tank_id: tankId,
} satisfies TankHistory),
} satisfies TankHistory)
);
}
});
@@ -4,7 +4,7 @@ import {
TANK_TYPES,
} from '@blitzkit/core';
import {
APIApplicationCommandOptionChoice,
type APIApplicationCommandOptionChoice,
Locale,
SlashCommandBuilder,
SlashCommandSubcommandBuilder,
@@ -1,11 +1,11 @@
import { REGIONS } from '@blitzkit/core';
import { REGIONS } from "@blitzkit/core";
import {
APIApplicationCommandOptionChoice,
type APIApplicationCommandOptionChoice,
Locale,
SlashCommandStringOption,
} from 'discord.js';
import { translator } from '../localization/translator';
import { localizationObject } from './localizationObject';
} from "discord.js";
import { translator } from "../localization/translator";
import { localizationObject } from "./localizationObject";
export function addRegionChoices(option: SlashCommandStringOption) {
const { strings } = translator(Locale.EnglishUS);
@@ -16,14 +16,14 @@ export function addRegionChoices(option: SlashCommandStringOption) {
localizationObject(
(strings) => strings.bot.common.options.region.name,
undefined,
true,
),
true
)
)
.setDescription(strings.bot.common.options.region.description)
.setDescriptionLocalizations(
localizationObject(
(strings) => strings.bot.common.options.region.description,
),
(strings) => strings.bot.common.options.region.description
)
)
.addChoices(
...REGIONS.map(
@@ -32,10 +32,10 @@ export function addRegionChoices(option: SlashCommandStringOption) {
value: region,
name: strings.common.regions.normal[region],
name_localizations: localizationObject(
(strings) => strings.common.regions.normal[region],
(strings) => strings.common.regions.normal[region]
),
}) satisfies APIApplicationCommandOptionChoice,
),
}) satisfies APIApplicationCommandOptionChoice
)
)
.setRequired(true);
}
+17 -17
View File
@@ -1,13 +1,13 @@
import { numberToRomanNumeral } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { numberToRomanNumeral } from "@blitzkit/core";
import { literals } from "@blitzkit/i18n";
import {
APIApplicationCommandOptionChoice,
Locale,
SlashCommandStringOption,
} from 'discord.js';
import { range } from 'lodash-es';
import { translator } from '../localization/translator';
import { localizationObject } from './localizationObject';
} from "discord.js";
import { range } from "lodash-es";
import { translator } from "../localization/translator";
import { localizationObject } from "./localizationObject";
export function addTierChoices(option: SlashCommandStringOption) {
const { strings } = translator(Locale.EnglishUS);
@@ -18,30 +18,30 @@ export function addTierChoices(option: SlashCommandStringOption) {
localizationObject(
(strings) => strings.bot.common.options.tier.name,
undefined,
true,
),
true
)
)
.setDescription(strings.bot.common.options.tier.description)
.setDescriptionLocalizations(
localizationObject(
(strings) => strings.bot.common.options.tier.description,
),
(strings) => strings.bot.common.options.tier.description
)
)
.setChoices(
...range(10, 0).map(
(tier) =>
({
name: literals(strings.bot.common.options.tier.choices.tier, [
`${tier}`,
numberToRomanNumeral(tier),
]),
name: literals(strings.bot.common.options.tier.choices.tier, {
tier_1: tier,
tier_2: numberToRomanNumeral(tier),
}),
value: `${tier}`,
name_localizations: localizationObject(
(strings) => strings.bot.common.options.tier.choices.tier,
[`${tier}`, numberToRomanNumeral(tier)],
{ tier_1: tier, tier_2: numberToRomanNumeral(tier) }
),
}) satisfies APIApplicationCommandOptionChoice,
),
}) satisfies APIApplicationCommandOptionChoice
)
)
.setRequired(true);
}
@@ -1,25 +1,25 @@
import { fetchTankNames, SEARCH_KEYS, TankType } from '@blitzkit/core';
import { fetchTankNames, SEARCH_KEYS, TankType } from "@blitzkit/core";
import {
ApplicationCommandOptionChoiceData,
type ApplicationCommandOptionChoiceData,
AutocompleteInteraction,
CacheType,
} from 'discord.js';
import { go } from 'fuzzysort';
import { tankNames } from '../blitzkit/nonBlockingPromises';
import { translator } from '../localization/translator';
type CacheType,
} from "discord.js";
import { go } from "fuzzysort";
import { tankNames } from "../blitzkit/nonBlockingPromises";
import { translator } from "../localization/translator";
import {
DISCORD_CHOICES_MAX_NAME_SIZE,
OVERFLOW_SUFFIX,
} from './autocompleteClan/constants';
} from "./autocompleteClan/constants";
export const tankNamesTechTreeOnly = fetchTankNames().then((names) =>
names.filter((tank) => tank.treeType === TankType.RESEARCHABLE),
names.filter((tank) => tank.treeType === TankType.RESEARCHABLE)
);
export async function autocompleteTanks(
interaction: AutocompleteInteraction<CacheType>,
techTreeOnly = false,
fields = ['tank'],
fields = ["tank"]
) {
const { unwrap } = translator(interaction.locale);
const focusedOption = interaction.options.getFocused(true);
@@ -31,7 +31,7 @@ export async function autocompleteTanks(
go(
focusedOption.value,
await (techTreeOnly ? tankNamesTechTreeOnly : tankNames),
{ keys: SEARCH_KEYS, limit: 10 },
{ keys: SEARCH_KEYS, limit: 10 }
).map(async (item) => {
let name = unwrap(item.obj.searchableName);
@@ -42,7 +42,7 @@ export async function autocompleteTanks(
0,
unwrap(item.obj.searchableName).length -
overSize -
OVERFLOW_SUFFIX.length,
OVERFLOW_SUFFIX.length
)}${OVERFLOW_SUFFIX}`;
}
@@ -50,8 +50,8 @@ export async function autocompleteTanks(
name,
value: `${item.obj.id}`,
} satisfies ApplicationCommandOptionChoiceData<string>;
}),
})
)
: [],
: []
);
}
+3 -3
View File
@@ -1,13 +1,13 @@
import {
ButtonBuilder,
ButtonStyle,
ComponentEmojiResolvable,
} from 'discord.js';
type ComponentEmojiResolvable,
} from "discord.js";
export function buttonLink(
url: string,
label: string,
emoji?: ComponentEmojiResolvable,
emoji?: ComponentEmojiResolvable
) {
const button = new ButtonBuilder()
.setStyle(ButtonStyle.Link)
@@ -1,13 +1,13 @@
import {
ButtonBuilder,
ButtonStyle,
ComponentEmojiResolvable,
} from 'discord.js';
type ComponentEmojiResolvable,
} from "discord.js";
export function buttonPrimary(
id: string,
label: string,
emoji?: ComponentEmojiResolvable,
emoji?: ComponentEmojiResolvable
) {
const button = new ButtonBuilder()
.setCustomId(id)
@@ -1,4 +1,4 @@
import { CacheType, ChatInputCommandInteraction } from 'discord.js';
import { type CacheType, ChatInputCommandInteraction } from "discord.js";
export interface Parameters {
[key: string]: string | number | boolean | undefined | null;
@@ -9,7 +9,7 @@ export interface Parameters {
export function commandToURL(
interaction: ChatInputCommandInteraction<CacheType>,
parameters?: Parameters,
parameters?: Parameters
) {
const pathname = [
interaction.commandName,
@@ -17,11 +17,11 @@ export function commandToURL(
interaction.options.getSubcommand(false),
]
.filter(Boolean)
.join('/');
.join("/");
const params = (parameters ? Object.entries(parameters) : [])
.filter(([, value]) => value !== undefined && value !== null)
.map(([key, value]) => `${key}=${value}`)
.join('&');
.join("&");
return `${pathname}${params.length > 0 ? `?${params}` : ''}`;
return `${pathname}${params.length > 0 ? `?${params}` : ""}`;
}
@@ -1,12 +1,12 @@
import { BlitzKitStrings } from '@blitzkit/i18n';
import { type BlitzKitStrings } from "@blitzkit/i18n";
import {
Locale,
SlashCommandBuilder,
SlashCommandSubcommandBuilder,
SlashCommandSubcommandGroupBuilder,
} from 'discord.js';
import { translator } from '../localization/translator';
import { localizationObject } from './localizationObject';
} from "discord.js";
import { translator } from "../localization/translator";
import { localizationObject } from "./localizationObject";
interface CreateLocalizedCommandSubcommand {
subcommand: string;
@@ -22,12 +22,12 @@ export function createLocalizedCommand(
command: string,
sub?:
| CreateLocalizedCommandSubcommand[]
| CreateLocalizedCommandSubcommandGroup[],
| CreateLocalizedCommandSubcommandGroup[]
) {
const commandPathItem = command.replaceAll(
'-',
'_',
) as keyof BlitzKitStrings['bot']['commands'];
"-",
"_"
) as keyof BlitzKitStrings["bot"]["commands"];
const { strings } = translator(Locale.EnglishUS);
const slashCommand = new SlashCommandBuilder()
.setName(command)
@@ -35,19 +35,19 @@ export function createLocalizedCommand(
localizationObject(
(strings) => strings.bot.commands[commandPathItem].name,
undefined,
true,
),
true
)
)
.setDescription(strings.bot.commands[commandPathItem].description)
.setDescriptionLocalizations(
localizationObject(
(strings) => strings.bot.commands[commandPathItem].description,
),
(strings) => strings.bot.commands[commandPathItem].description
)
);
if (sub) {
sub.map((subItem) => {
if ('subcommand' in subItem) {
if ("subcommand" in subItem) {
const subcommandNameLocalizations = localizationObject(
(strings) =>
(
@@ -56,7 +56,7 @@ export function createLocalizedCommand(
}
).subcommands[subItem.subcommand].name,
undefined,
true,
true
);
const subcommandDescriptionLocalizations = localizationObject(
(strings) =>
@@ -64,7 +64,7 @@ export function createLocalizedCommand(
strings.bot.commands[commandPathItem] as unknown as {
subcommands: Record<string, { description: string }>;
}
).subcommands[subItem.subcommand].description,
).subcommands[subItem.subcommand].description
);
slashCommand.addSubcommand((option) => {
@@ -78,7 +78,7 @@ export function createLocalizedCommand(
strings.bot.commands[commandPathItem] as unknown as {
subcommands: Record<string, { description: string }>;
}
).subcommands[subItem.subcommand].description,
).subcommands[subItem.subcommand].description
)
.setDescriptionLocalizations(subcommandDescriptionLocalizations);
});
@@ -91,7 +91,7 @@ export function createLocalizedCommand(
}
).groups[subItem.group].name,
undefined,
true,
true
);
const subcommandDescriptionLocalizations = localizationObject(
(strings) =>
@@ -99,7 +99,7 @@ export function createLocalizedCommand(
strings.bot.commands[commandPathItem] as unknown as {
groups: Record<string, { description: string }>;
}
).groups[subItem.group].description,
).groups[subItem.group].description
);
slashCommand.addSubcommandGroup((option) => {
@@ -113,7 +113,7 @@ export function createLocalizedCommand(
strings.bot.commands[commandPathItem] as unknown as {
groups: Record<string, { description: string }>;
}
).groups[subItem.group].description,
).groups[subItem.group].description
)
.setDescriptionLocalizations(subcommandDescriptionLocalizations);
});
@@ -1,17 +1,17 @@
import { ChatInputCommandInteraction } from 'discord.js';
import { PeriodType } from './addPeriodSubCommands';
import { ChatInputCommandInteraction } from "discord.js";
import type { PeriodType } from "./addPeriodSubCommands";
export function getCustomPeriodParams(
interaction: ChatInputCommandInteraction,
forcedNonCustomPeriod = false,
forcedNonCustomPeriod = false
) {
if (
!forcedNonCustomPeriod &&
(interaction.options.getSubcommand() as PeriodType) === 'custom'
(interaction.options.getSubcommand() as PeriodType) === "custom"
) {
return {
start: interaction.options.getInteger('start', true),
end: interaction.options.getInteger('end', true),
start: interaction.options.getInteger("start", true),
end: interaction.options.getInteger("end", true),
};
}
@@ -1,19 +1,19 @@
import { TankClass, TankType } from '@blitzkit/core';
import { ButtonInteraction } from 'discord.js';
import { StatFilters } from '../blitzstars/filterStats';
import { TankClass, TankType } from "@blitzkit/core";
import { ButtonInteraction } from "discord.js";
import type { StatFilters } from "../blitzstars/filterStats";
export function getFiltersFromButton(interaction: ButtonInteraction) {
const url = new URL(`https://example.com/${interaction.customId}`);
return {
nation: url.searchParams.get('nation') ?? undefined,
tank: parseInt(url.searchParams.get('tank') ?? '0') || undefined,
nation: url.searchParams.get("nation") ?? undefined,
tank: parseInt(url.searchParams.get("tank") ?? "0") || undefined,
class:
(Number(url.searchParams.get('class')) as TankClass | undefined) ??
(Number(url.searchParams.get("class")) as TankClass | undefined) ??
undefined,
type:
(Number(url.searchParams.get('type')) as TankType | undefined) ??
(Number(url.searchParams.get("type")) as TankType | undefined) ??
undefined,
tier: parseInt(url.searchParams.get('tier') ?? '0') || undefined,
tier: parseInt(url.searchParams.get("tier") ?? "0") || undefined,
} satisfies StatFilters;
}
@@ -1,6 +1,6 @@
import { ChatInputCommandInteraction } from 'discord.js';
import { resolveTankId } from '../blitz/resolveTankId';
import { StatFilters } from '../blitzstars/filterStats';
import type { StatFilters } from '../blitzstars/filterStats';
export async function getFiltersFromCommand(
interaction: ChatInputCommandInteraction,
@@ -1,11 +1,11 @@
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import { translator } from '../localization/translator';
import { PeriodSize } from './addPeriodSubCommands';
import { literals } from "@blitzkit/i18n";
import { Locale } from "discord.js";
import { translator } from "../localization/translator";
import type { PeriodSize } from "./addPeriodSubCommands";
export function getPeriodOptionName(period: PeriodSize, locale: Locale) {
const { strings } = translator(locale);
if (period === 'career') return strings.bot.common.periods.career;
if (period === 'today') return strings.bot.common.periods.today;
return literals(strings.bot.common.periods.days, [period]);
if (period === "career") return strings.bot.common.periods.career;
if (period === "today") return strings.bot.common.periods.today;
return literals(strings.bot.common.periods.days, { days: period });
}
@@ -1,15 +1,15 @@
import { BlitzKitStrings, literals as literalsFunc } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import { SUPPORTED_LOCALES_DISCORD } from '../localization/strings/constants';
import { translator } from '../localization/translator';
import { type BlitzKitStrings, literals as literalsFunc } from "@blitzkit/i18n";
import { Locale } from "discord.js";
import { SUPPORTED_LOCALES_DISCORD } from "../localization/strings/constants";
import { translator } from "../localization/translator";
const validNameRegex =
/^[\p{Ll}\p{Lm}\p{Lo}\p{N}\p{sc=Devanagari}\p{sc=Thai}_-]+$/u;
export function localizationObject(
path: (strings: BlitzKitStrings) => string,
literals: string[] = [],
strict = false,
literals?: Record<string, string | number>,
strict = false
) {
return SUPPORTED_LOCALES_DISCORD.reduce<Partial<Record<Locale, string>>>(
(localizations, locale) => {
@@ -20,7 +20,7 @@ export function localizationObject(
if (!isValid) {
console.warn(
`Invalid localization for ${locale} (${path}): ${translation}; skipping...`,
`Invalid localization for ${locale} (${path}): ${translation}; skipping...`
);
return localizations;
}
@@ -30,6 +30,6 @@ export function localizationObject(
[locale]: translation,
};
},
{},
{}
);
}
Loaded 100 of 372 files, more files were not shown because too many files have changed in this diff. Show more