Compare commits

...
330 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
TrèsAbhi e5f7d72df3 feat: rearrange 2025-09-28 14:11:33 -05:00
TrèsAbhi 57092d8df0 feat: env vars 2025-09-28 14:10:34 -05:00
TrèsAbhi 86508d5413 feat: submodules and packages 2025-09-28 13:44:15 -05:00
TrèsAbhi 33373c286f feat: logs 2025-09-28 13:13:08 -05:00
TrèsAbhi 7443f75347 fix: less aggressive light manager 2025-09-28 13:12:16 -05:00
TrèsAbhi d09f934b80 feat: start app 2025-09-28 13:09:03 -05:00
TrèsAbhi 7f919ebb87 feat: remove pwa 2025-09-28 13:02:48 -05:00
TrèsAbhi 0929825555 fix: glob and cache 2025-09-27 19:13:29 -05:00
TrèsAbhi 4de0fc5ec1 feat: concurrency 2025-09-27 18:37:51 -05:00
TrèsAbhi e66610f86e fix: side and out 2025-09-27 18:32:18 -05:00
TrèsAbhi 674abc4923 fix: dead code 2025-09-27 18:28:35 -05:00
TrèsAbhi 25cee8a36b fix: icons 2025-09-27 18:26:37 -05:00
TrèsAbhi dcbac5e017 fix: missing package 2025-09-27 18:16:05 -05:00
TrèsAbhi 0c94d42f3f fix: missing package 2025-09-27 18:15:00 -05:00
TrèsAbhi 7ab5e94323 fix: missing package 2025-09-27 18:13:59 -05:00
TrèsAbhi f9ea5ba74a fix: missing package 2025-09-27 18:13:00 -05:00
TrèsAbhi 1e7162d2da fix: missing package 2025-09-27 18:11:49 -05:00
TrèsAbhi cfba53a655 fix: missing package 2025-09-27 18:10:20 -05:00
TrèsAbhi 77a85641f0 fix: missing package 2025-09-27 18:09:10 -05:00
TrèsAbhi 5330a27e96 feat: logs 2025-09-27 18:07:43 -05:00
TrèsAbhi a791aff4a0 Merge branch 'main' into dev 2025-09-27 18:03:37 -05:00
TrèsAbhi df8a5967f0 feat: start of logs 2025-09-27 18:03:20 -05:00
TrèsAbhi 6261dfc9cd fix: more linting 2025-09-27 18:01:20 -05:00
TrèsAbhi 766801826c fix: canvases 2025-09-27 17:51:29 -05:00
TrèsAbhi 7b728596ca feat: storeDeferred 2025-09-27 17:32:20 -05:00
TrèsAbhi 0bf6ac33a5 feat: more promotion 2025-09-27 17:29:12 -05:00
TrèsAbhi 9a33b84b1c feat: install button 2025-09-27 17:05:06 -05:00
TrèsAbhi c14efe86cc fix: update errors 2025-09-27 16:07:45 -05:00
TrèsAbhi 4ff9a2ec7f feat: more pwa entries 2025-09-27 15:51:46 -05:00
TrèsAbhi 75d7b4554b feat: start of pew 2025-09-27 15:38:37 -05:00
TrèsAbhi 27c8b348bb feat: favicons 2025-09-27 13:39:08 -05:00
TrèsAbhi a55ab5e7ee feat: redo base setup 2025-09-27 13:34:11 -05:00
TrèsAbhi 12e90cc6ee feat: update dependencies 2025-09-26 14:51:49 -05:00
TrèsAbhi 01755a2a64 feat: start adding pwa 2025-09-26 14:46:07 -05:00
TrèsAbhi 080f0a0eb7 fix: make tankopedia hero section a bit smaller 2025-09-22 13:52:31 -05:00
TrèsAbhi a646cc1e57 feat: mixer buttons 2025-09-22 13:50:46 -05:00
TrèsAbhi d107813c37 feat: update closed 2025-09-22 13:27:38 -05:00
TrèsAbhi 27d3bbf346 feat: update closed 2025-09-22 13:20:47 -05:00
TrèsAbhi 675a613a0e New translations en.json (Russian) (#170) 2025-09-22 10:06:08 -05:00
TrèsAbhi 50c77e0ae1 fix: seo 2025-09-18 10:43:38 -05:00
TrèsAbhi 6e0429332d feat: mixer tank selector 2025-09-18 10:41:42 -05:00
TrèsAbhi 7afce6cbcb fix: keys 2025-09-18 10:19:41 -05:00
TrèsAbhi 17c7a5c7f5 feat: start of selector 2025-09-13 23:57:10 -05:00
TrèsAbhi f5aa2ff9de feat: reenable mixer 2025-09-13 23:42:26 -05:00
TrèsAbhi 96a8f225fc feat: start on api 2025-09-13 23:07:58 -05:00
TrèsAbhi d15a5aee69 fix: no overflow navbar on certain devices 2025-09-13 22:54:45 -05:00
TrèsAbhi 84dc5e4960 feat: data-driven tools 2025-09-13 22:53:29 -05:00
TrèsAbhi 58b5ee7f83 feat: data-driven tools 2025-09-13 21:54:43 -05:00
TrèsAbhi 8a99d34af9 fix: check intialization this mount 2025-09-13 21:33:16 -05:00
TrèsAbhi 7cd7bd83dc Merge branch 'main' into dev 2025-09-13 21:28:58 -05:00
TrèsAbhi c29d36a4b7 feat: update to main 2025-09-13 10:03:22 -05:00
TrèsAbhi 7d3b6221d7 feat: remake decodeAny 2025-09-13 10:02:46 -05:00
TrèsAbhi fd66f9ab17 feat: compiler based client 2025-09-12 16:49:26 -05:00
TrèsAbhi 6fbb0e81cb simplify error handling 2025-09-12 14:08:53 -05:00
TrèsAbhi 6dc82588c1 feat: cleanup 2025-09-12 13:42:33 -05:00
TrèsAbhi c6eae9ad51 feat: update closed 2025-09-12 13:29:10 -05:00
TrèsAbhi ebdd7629ff update 2025-09-12 13:02:43 -05:00
TrèsAbhi 646bccf370 feat: logs 2025-09-07 16:38:07 -05:00
TrèsAbhi 86206f2d89 Merge branch 'main' into dev 2025-09-07 16:29:28 -05:00
TrèsAbhi bc0dd87e31 Merge branch 'main' into dev 2025-09-07 16:29:09 -05:00
TrèsAbhi e26bb4efe0 feat: some changes to lighting... again 2025-09-06 12:40:35 -05:00
TrèsAbhi 66cf1fd223 feat: baked ao 2025-09-06 00:48:39 -05:00
TrèsAbhi 335cd4d699 feat: rewrite some systems 2025-09-05 23:34:59 -05:00
TrèsAbhi 95fa5bc1a5 fix: use base color map 2025-09-05 20:45:04 -05:00
TrèsAbhi 50eac9eba6 New translations en.json (French) (#169) 2025-09-04 08:04:17 -05:00
TrèsAbhi 1598ae973f feat: auto framerate 2025-09-02 22:44:46 -05:00
TrèsAbhi 69f3a95379 feat: use armor model 2025-09-02 22:16:48 -05:00
TrèsAbhi 3846cbab4a fix: save persistent data 2025-09-02 16:31:11 -05:00
TrèsAbhi 30e87743fc feat: upgrade rest of them 2025-09-02 16:24:13 -05:00
TrèsAbhi fe4afdcbf0 feat: upgrade CompareEphemeral 2025-09-01 23:05:22 -05:00
TrèsAbhi ef2a47e756 feat: upgrade ComparePersistent 2025-09-01 23:02:20 -05:00
TrèsAbhi c2d625e1ed feat: upgrade EmbedBreakdown 2025-09-01 23:00:44 -05:00
TrèsAbhi d33990bdb4 feat: upgrade Gallery 2025-09-01 22:58:26 -05:00
TrèsAbhi 96482d4268 feat: renaming 2025-09-01 22:56:39 -05:00
TrèsAbhi 8b527f2333 fix: last and next checking 2025-09-01 22:55:31 -05:00
TrèsAbhi f56c31fb48 feat: upgrade Guess 2025-09-01 19:22:38 -05:00
TrèsAbhi 7d164e415a feat: upgrade Mixer 2025-09-01 19:19:44 -05:00
TrèsAbhi b3a9f0889e feat: upgrade Session 2025-09-01 19:16:36 -05:00
TrèsAbhi 6d24ed92d0 feat: upgrade TankFilters 2025-09-01 19:13:08 -05:00
TrèsAbhi d287e66265 feat: upgrade tankopediaEphemeral 2025-09-01 19:01:01 -05:00
TrèsAbhi ac29d1ead0 feat: upgrade tankopediaEphemeral 2025-09-01 18:51:04 -05:00
TrèsAbhi b6c3fdc4e9 feat: upgrade performance 2025-09-01 18:16:41 -05:00
TrèsAbhi 4efdaaf7c9 feat: upgrade TankSort 2025-09-01 18:13:41 -05:00
TrèsAbhi 87b6a36520 feat: use custom state manager 2025-09-01 18:03:07 -05:00
TrèsAbhi cf38cb3a6e feat: optimize legacy redirect pages 2025-09-01 17:27:42 -05:00
TrèsAbhi 97fc386bc7 New Crowdin updates (#168)
* New translations en.json (Russian)

* 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 (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 (Russian)

* New translations en.json (Russian)
2025-08-31 09:54:49 -05:00
TrèsAbhi abfb3f8ddb fix: typo 2025-08-29 23:58:47 -05:00
TrèsAbhi 9cb5113f6e feat: haha 2025-08-29 23:58:14 -05:00
TrèsAbhi 7dd1c0c32a feat: logs 2025-08-29 23:57:33 -05:00
TrèsAbhi 03233f3fdc fix: align antagonist selector filters correctly 2025-08-29 23:46:57 -05:00
TrèsAbhi 377159c298 fix: consider tank models 2025-08-29 23:41:35 -05:00
TrèsAbhi d8431fb620 fix: gap alignemnt 2025-08-29 23:28:49 -05:00
TrèsAbhi 0546ddefbd fix: better shot card alignment 2025-08-29 23:24:12 -05:00
TrèsAbhi 26f6889012 feat: revert subtler advanced highlighting coloring 2025-08-29 23:10:20 -05:00
TrèsAbhi 3ad6088565 feat: skip button for reload visualizer 2025-08-29 23:08:44 -05:00
TrèsAbhi b6fd568cc6 fix: screenshot props 2025-08-29 23:00:02 -05:00
TrèsAbhi 3861b07aef fix: remove "show grid" 2025-08-29 22:53:04 -05:00
TrèsAbhi 2bf57e2c72 feat: better framing 2025-08-29 22:51:24 -05:00
TrèsAbhi 744c6654cf feat: cooler initial position 2025-08-29 22:39:34 -05:00
TrèsAbhi b82e905757 fix: dead code 2025-08-29 13:42:44 -05:00
TrèsAbhi c42e5b7dc3 feat: logs 2025-08-29 13:32:46 -05:00
TrèsAbhi b716218ecd fix: loading bar 2025-08-29 13:31:48 -05:00
TrèsAbhi 511346e4bb fix: select 2025-08-29 13:26:34 -05:00
TrèsAbhi 5c2e49453e feat: more lighting 2025-08-29 13:21:41 -05:00
TrèsAbhi 5d397de766 feat: more progress 2025-08-28 18:23:02 -05:00
TrèsAbhi aa763f65d1 feat: more progress 2025-08-28 17:59:31 -05:00
TrèsAbhi 94cbd95509 feat: start of new lighting 2025-08-28 16:52:23 -05:00
TrèsAbhi f3762e356f feat: start of mixer 2025-08-28 16:12:40 -05:00
TrèsAbhi 5747d7549f feat: tweak a few things and enable guess game 2025-08-27 17:43:42 -05:00
TrèsAbhi aea4905d20 fix: vars 2025-08-27 17:22:06 -05:00
521 changed files with 21395 additions and 11471 deletions

No files matched your search

+26 -98
View File
@@ -1,4 +1,4 @@
name: Build Assets name: Assets
on: on:
push: push:
@@ -6,30 +6,30 @@ on:
workflow_dispatch: workflow_dispatch:
inputs: 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 type: string
required: false required: false
default: main description: Archive URL to download instead of Steam client
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
schedule: schedule:
- cron: '0 2 * * *' - cron: "0 2 * * *"
concurrency: 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 cancel-in-progress: true
env: env:
DISCORD_CLIENT_ID: ${{ secrets.DISCORD_CLIENT_ID }} DISCORD_CLIENT_ID: ${{ secrets.DISCORD_CLIENT_ID }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }} DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
DATABASE_URL: ${{ secrets.DATABASE_URL }} DATABASE_URL: ${{ secrets.DATABASE_URL }}
VIDEOS_SHEET: ${{ secrets.VIDEOS_SHEET }} VIDEOS_SHEET: ${{ secrets.VIDEOS_SHEET }}
@@ -37,8 +37,8 @@ env:
WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }} WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }}
WOTB_DLC_CDN: ${{ secrets.WOTB_DLC_CDN }} WOTB_DLC_CDN: ${{ secrets.WOTB_DLC_CDN }}
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }} PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
PUBLIC_ASSET_REPO: blitzkit/assets PUBLIC_ASSET_REPO: ${{ github.event.inputs.asset_repo != '' && github.event.inputs.asset_repo || secrets.PUBLIC_ASSET_REPO }}
PUBLIC_ASSET_BRANCH: main PUBLIC_ASSET_BRANCH: ${{ secrets.PUBLIC_ASSET_BRANCH }}
WOTB_GLOSSARY_DOMAIN: ${{ secrets.WOTB_GLOSSARY_DOMAIN }} WOTB_GLOSSARY_DOMAIN: ${{ secrets.WOTB_GLOSSARY_DOMAIN }}
WOTB_GLOSSARY_PATH: ${{ secrets.WOTB_GLOSSARY_PATH }} WOTB_GLOSSARY_PATH: ${{ secrets.WOTB_GLOSSARY_PATH }}
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }} PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
@@ -54,100 +54,28 @@ env:
PUBLIC_ASSET_FAST_USER: blitzkit PUBLIC_ASSET_FAST_USER: blitzkit
PUBLIC_ASSET_FAST_REPO: assets PUBLIC_ASSET_FAST_REPO: assets
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }} PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
STEAM_USERNAME: ${{ secrets.STEAM_USERNAME }}
STEAM_PASSWORD: ${{ secrets.STEAM_PASSWORD }}
jobs: 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: build_assets:
name: Build assets name: Build assets
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: download_client
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
submodules: recursive submodules: recursive
token: ${{ secrets.GH_TOKEN }} token: ${{ secrets.GH_TOKEN }}
- uses: arduino/setup-protoc@v3
- 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
with: with:
name: depots repo-token: ${{ secrets.GH_TOKEN }}
path: depots - uses: oven-sh/setup-bun@v1
- run: bun run setup
- name: AppArmor hack - name: AppArmor hack
run: echo 0 | sudo tee /proc/sys/kernel/apparmor_restrict_unprivileged_userns run: echo 0 | sudo tee /proc/sys/kernel/apparmor_restrict_unprivileged_userns
- name: Start server and build assets - name: Build assets
run: | run: bun run build:assets --all-targets --manual
bun dev:website --verbosepa &
SERVER_PID=$!
bun run build:assets --all-targets --depot --production
kill $SERVER_PID || true
+11 -6
View File
@@ -3,7 +3,7 @@ name: Averages
on: on:
workflow_dispatch: workflow_dispatch:
schedule: schedule:
- cron: '0 8 * * *' - cron: "0 8 * * *"
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.ref }} group: ${{ github.workflow }}-${{ github.ref }}
@@ -15,7 +15,7 @@ env:
PUBLIC_ASSET_FAST_USER: ${{ secrets.PUBLIC_ASSET_FAST_USER }} PUBLIC_ASSET_FAST_USER: ${{ secrets.PUBLIC_ASSET_FAST_USER }}
PUBLIC_ASSET_FAST_REPO: ${{ secrets.PUBLIC_ASSET_FAST_REPO }} PUBLIC_ASSET_FAST_REPO: ${{ secrets.PUBLIC_ASSET_FAST_REPO }}
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }} PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
ALLOW_ROBOTS: 'true' ALLOW_ROBOTS: "true"
WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }} WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }}
PUBLIC_GOOGLE_TAG_MANAGER_ID: ${{ secrets.PUBLIC_GOOGLE_TAG_MANAGER_ID }} PUBLIC_GOOGLE_TAG_MANAGER_ID: ${{ secrets.PUBLIC_GOOGLE_TAG_MANAGER_ID }}
PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ${{ secrets.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_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_DISCORD_SERVER_ID: ${{ secrets.PUBLIC_DISCORD_SERVER_ID }}
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }} PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
PUBLIC_BRANCH: ${{ secrets.PUBLIC_BRANCH }} PUBLIC_BRANCH: ${{ secrets.PUBLIC_BRANCH }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
jobs: jobs:
build: build:
@@ -32,16 +33,20 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ secrets.GH_TOKEN }}
- uses: oven-sh/setup-bun@v1 - uses: oven-sh/setup-bun@v1
- uses: arduino/setup-protoc@v3
- name: Install dependencies with:
run: bun install repo-token: ${{ secrets.GH_TOKEN }}
- run: bun run setup
- name: Build averages - name: Build averages
env: env:
PUBLIC_ASSET_BRANCH: main PUBLIC_ASSET_BRANCH: main
DISCORD_CLIENT_ID: ${{ secrets.DISCORD_CLIENT_ID }} DISCORD_CLIENT_ID: ${{ secrets.DISCORD_CLIENT_ID }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }} DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
DATABASE_URL: ${{ secrets.DATABASE_URL }} DATABASE_URL: ${{ secrets.DATABASE_URL }}
VIDEOS_SHEET: ${{ secrets.VIDEOS_SHEET }} VIDEOS_SHEET: ${{ secrets.VIDEOS_SHEET }}
+8 -7
View File
@@ -1,4 +1,4 @@
name: Build Bot name: Bot
on: on:
push: push:
@@ -15,13 +15,14 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ secrets.GH_TOKEN }}
- uses: oven-sh/setup-bun@v1 - uses: oven-sh/setup-bun@v1
- uses: arduino/setup-protoc@v3
- name: Install dependencies with:
run: bun install repo-token: ${{ secrets.GH_TOKEN }}
- run: bun run setup
- name: Build Prisma
run: bun run build:prisma
- name: Build bot - name: Build bot
run: bun run build:bot run: bun run build:bot
+10 -6
View File
@@ -1,9 +1,9 @@
name: Discover IDs name: IDs
on: on:
workflow_dispatch: workflow_dispatch:
schedule: schedule:
- cron: '0 0 * * 1' - cron: "0 0 * * 1"
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.ref }} group: ${{ github.workflow }}-${{ github.ref }}
@@ -11,11 +11,11 @@ concurrency:
env: env:
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }} DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
DATABASE_URL: ${{ secrets.DATABASE_URL }} DATABASE_URL: ${{ secrets.DATABASE_URL }}
PUBLIC_ASSET_BRANCH: ${{ secrets.PUBLIC_ASSET_BRANCH }} PUBLIC_ASSET_BRANCH: ${{ secrets.PUBLIC_ASSET_BRANCH }}
PUBLIC_ASSET_REPO: ${{ secrets.PUBLIC_ASSET_REPO }} PUBLIC_ASSET_REPO: ${{ secrets.PUBLIC_ASSET_REPO }}
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }} PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
jobs: jobs:
build: build:
@@ -23,10 +23,14 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ secrets.GH_TOKEN }}
- uses: oven-sh/setup-bun@v1 - uses: oven-sh/setup-bun@v1
- uses: arduino/setup-protoc@v3
- name: Install dependencies with:
run: bun install repo-token: ${{ secrets.GH_TOKEN }}
- run: bun run setup
- name: Discover IDs - name: Discover IDs
run: bun build:ids --production run: bun build:ids --production
+19 -4
View File
@@ -1,10 +1,10 @@
name: Host Bot name: Host
on: on:
push: push:
branches: main branches: main
schedule: schedule:
- cron: '0 */5 * * *' - cron: "0 */5 * * *"
workflow_dispatch: workflow_dispatch:
release: release:
types: [created, published, released, edited] types: [created, published, released, edited]
@@ -22,6 +22,20 @@ env:
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }} PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
PUBLIC_ASSET_FAST_USER: ${{ secrets.PUBLIC_ASSET_FAST_USER }} PUBLIC_ASSET_FAST_USER: ${{ secrets.PUBLIC_ASSET_FAST_USER }}
PUBLIC_ASSET_FAST_REPO: ${{ secrets.PUBLIC_ASSET_FAST_REPO }} PUBLIC_ASSET_FAST_REPO: ${{ secrets.PUBLIC_ASSET_FAST_REPO }}
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
ALLOW_ROBOTS: ${{ secrets.ALLOW_ROBOTS }}
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 }}
PUBLIC_GUIDE_WEBSITE: ${{ secrets.PUBLIC_GUIDE_WEBSITE }}
SERVICING: ${{ secrets.SERVICING }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
jobs: jobs:
host: host:
@@ -29,8 +43,9 @@ jobs:
steps: steps:
- uses: oven-sh/setup-bun@v1 - uses: oven-sh/setup-bun@v1
# with: - uses: arduino/setup-protoc@v3
# bun-version: 1.1.33 with:
repo-token: ${{ secrets.GH_TOKEN }}
- name: Download release assets - name: Download release assets
run: | run: |
+30 -25
View File
@@ -3,7 +3,29 @@ name: Rating Asia
on: on:
workflow_dispatch: workflow_dispatch:
schedule: 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: jobs:
host: host:
@@ -11,31 +33,14 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ secrets.GH_TOKEN }}
- uses: oven-sh/setup-bun@v1 - uses: oven-sh/setup-bun@v1
- uses: arduino/setup-protoc@v3
- name: Install dependencies with:
run: bun install repo-token: ${{ secrets.GH_TOKEN }}
- run: bun run setup
- name: Generate leaderboard - 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 run: bun build:ratingleaderboard --region=asia
+30 -25
View File
@@ -3,7 +3,29 @@ name: Rating Europe
on: on:
workflow_dispatch: workflow_dispatch:
schedule: 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: jobs:
host: host:
@@ -11,31 +33,14 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ secrets.GH_TOKEN }}
- uses: oven-sh/setup-bun@v1 - uses: oven-sh/setup-bun@v1
- uses: arduino/setup-protoc@v3
- name: Install dependencies with:
run: bun install repo-token: ${{ secrets.GH_TOKEN }}
- run: bun run setup
- name: Generate leaderboard - 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 run: bun build:ratingleaderboard --region=eu
+30 -25
View File
@@ -3,7 +3,29 @@ name: Rating North America
on: on:
workflow_dispatch: workflow_dispatch:
schedule: 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: jobs:
host: host:
@@ -11,31 +33,14 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ secrets.GH_TOKEN }}
- uses: oven-sh/setup-bun@v1 - uses: oven-sh/setup-bun@v1
- uses: arduino/setup-protoc@v3
- name: Install dependencies with:
run: bun install repo-token: ${{ secrets.GH_TOKEN }}
- run: bun run setup
- name: Generate leaderboard - 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 run: bun build:ratingleaderboard --region=com
+7 -6
View File
@@ -11,12 +11,13 @@ concurrency:
env: env:
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }} DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
DATABASE_URL: ${{ secrets.DATABASE_URL }} DATABASE_URL: ${{ secrets.DATABASE_URL }}
PUBLIC_ASSET_BRANCH: ${{ secrets.PUBLIC_ASSET_BRANCH }} PUBLIC_ASSET_BRANCH: ${{ secrets.PUBLIC_ASSET_BRANCH }}
PUBLIC_ASSET_REPO: ${{ secrets.PUBLIC_ASSET_REPO }} PUBLIC_ASSET_REPO: ${{ secrets.PUBLIC_ASSET_REPO }}
GOOGLE_APPLICATION_CREDENTIALS: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }} GOOGLE_APPLICATION_CREDENTIALS: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }}
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }} PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
jobs: jobs:
build: build:
@@ -27,11 +28,11 @@ jobs:
with: with:
submodules: recursive submodules: recursive
token: ${{ secrets.GH_TOKEN }} token: ${{ secrets.GH_TOKEN }}
- uses: oven-sh/setup-bun@v1 - uses: oven-sh/setup-bun@v1
- uses: arduino/setup-protoc@v3
- name: Install dependencies with:
run: bun install repo-token: ${{ secrets.GH_TOKEN }}
- run: bun run setup
- name: Find reviews - 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: workflow_dispatch:
schedule: schedule:
- cron: '0 4 * * *' - cron: "0 4 * * *"
permissions: permissions:
contents: read contents: read
@@ -31,6 +31,7 @@ env:
PUBLIC_BRANCH: ${{ secrets.PUBLIC_BRANCH }} PUBLIC_BRANCH: ${{ secrets.PUBLIC_BRANCH }}
PUBLIC_GUIDE_WEBSITE: ${{ secrets.PUBLIC_GUIDE_WEBSITE }} PUBLIC_GUIDE_WEBSITE: ${{ secrets.PUBLIC_GUIDE_WEBSITE }}
SERVICING: ${{ secrets.SERVICING }} SERVICING: ${{ secrets.SERVICING }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
jobs: jobs:
build: build:
@@ -41,12 +42,12 @@ jobs:
with: with:
submodules: recursive submodules: recursive
token: ${{ secrets.GH_TOKEN }} token: ${{ secrets.GH_TOKEN }}
- uses: oven-sh/setup-bun@v1 - uses: oven-sh/setup-bun@v1
- uses: arduino/setup-protoc@v3
with:
repo-token: ${{ secrets.GH_TOKEN }}
- uses: actions/configure-pages@v5 - uses: actions/configure-pages@v5
- run: bun run setup
- name: Install dependencies
run: bun install
- name: Build website - name: Build website
run: bun run build:website run: bun run build:website
+27 -2
View File
@@ -24,7 +24,32 @@ public/sitemap*
target target
# packages/core/src/protos/*.ts # packages/core/src/protos/*.ts
/.vercel /.vercel
/.astro .astro
.vercel .vercel
/src /src
/packages/bot/src/prisma /packages/bot/src/prisma
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"] [submodule "packages/closed"]
path = submodules/blitzkit-closed path = packages/closed
url = https://github.com/tresabhi/blitzkit-closed url = https://github.com/blitzkit/closed.git
[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
-7
View File
@@ -1,7 +0,0 @@
{
"trailingComma": "all",
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"printWidth": 80
}
+26 -7
View File
@@ -11,6 +11,7 @@
"analyticsreporting", "analyticsreporting",
"APAC", "APAC",
"apcr", "apcr",
"appinfo",
"applypatches", "applypatches",
"artefacts", "artefacts",
"astrojs", "astrojs",
@@ -21,6 +22,7 @@
"AYUV", "AYUV",
"battleresults", "battleresults",
"bbox", "bbox",
"beforeinstallprompt",
"bepaint", "bepaint",
"betakrieg", "betakrieg",
"BINORMAL", "BINORMAL",
@@ -33,12 +35,14 @@
"blitzkit", "blitzkit",
"blitzrinth", "blitzrinth",
"blitzstars", "blitzstars",
"bufbuild",
"cacheable", "cacheable",
"Callouts", "Callouts",
"Camo", "Camo",
"Carro", "Carro",
"CCPAOPTOUT", "CCPAOPTOUT",
"CDON", "CDON",
"clanid",
"classname", "classname",
"cleandist", "cleandist",
"closedkrieg", "closedkrieg",
@@ -61,8 +65,10 @@
"deburr", "deburr",
"decalmask", "decalmask",
"decompressor", "decompressor",
"dedup",
"defence", "defence",
"degressive", "degressive",
"depotdownloader",
"devkrieg", "devkrieg",
"diacriticless", "diacriticless",
"DIDS", "DIDS",
@@ -73,21 +79,26 @@
"DVPLCRC", "DVPLCRC",
"dvpm", "dvpm",
"dxgi", "dxgi",
"easimon",
"echarts", "echarts",
"Everytime", "Everytime",
"FASTNAME", "FASTNAME",
"favicons",
"flexdirection", "flexdirection",
"fontsource",
"FOURCC", "FOURCC",
"frameloop", "frameloop",
"fsformat", "fsformat",
"Fullscreen", "Fullscreen",
"fullstats", "fullstats",
"fuzzysort", "fuzzysort",
"gamemode",
"gameplay", "gameplay",
"glsl", "glsl",
"GLTF", "GLTF",
"gtag", "gtag",
"haha", "haha",
"Herdbreaker",
"highp", "highp",
"iife", "iife",
"imgur", "imgur",
@@ -101,6 +112,7 @@
"leaderboards", "leaderboards",
"lerp", "lerp",
"LINEARSIZE", "LINEARSIZE",
"linktarget",
"lmao", "lmao",
"loadouts", "loadouts",
"loddist", "loddist",
@@ -123,6 +135,7 @@
"NEGATIVEX", "NEGATIVEX",
"NEGATIVEY", "NEGATIVEY",
"NEGATIVEZ", "NEGATIVEZ",
"networkidle",
"NITROPAY", "NITROPAY",
"nivo", "nivo",
"nmatname", "nmatname",
@@ -133,11 +146,13 @@
"Oopsy", "Oopsy",
"opentest", "opentest",
"OPTML", "OPTML",
"otemp",
"outdir", "outdir",
"outfile", "outfile",
"ownedtanks", "ownedtanks",
"padder", "padder",
"partytown", "partytown",
"patchnotes",
"PIXELFORMAT", "PIXELFORMAT",
"playerachievements", "playerachievements",
"playerinfo", "playerinfo",
@@ -150,10 +165,15 @@
"Promisable", "Promisable",
"proto", "proto",
"protobuf", "protobuf",
"protoc",
"protos", "protos",
"proxyclient",
"prunestrings",
"Pyogenics", "Pyogenics",
"ratingleaderboard", "ratingleaderboard",
"raycaster", "raycaster",
"Redirector",
"Relogin",
"researchable", "researchable",
"Resvg", "Resvg",
"rizz", "rizz",
@@ -167,6 +187,7 @@
"searchtanks", "searchtanks",
"SHAREDEXP", "SHAREDEXP",
"SKLLD", "SKLLD",
"smoothstep",
"Smri", "Smri",
"Specularity", "Specularity",
"SSAO", "SSAO",
@@ -186,6 +207,7 @@
"tankopedia", "tankopedia",
"tankopedias", "tankopedias",
"tankstats", "tankstats",
"tauri",
"texconv", "texconv",
"TEXCOORD", "TEXCOORD",
"tresabhi", "tresabhi",
@@ -200,12 +222,14 @@
"Unregistering", "Unregistering",
"unrotate", "unrotate",
"unwrapper", "unwrapper",
"userns",
"vercel", "vercel",
"vickers", "vickers",
"vitepress", "vitepress",
"Vovko", "Vovko",
"wargaming", "wargaming",
"webp", "webp",
"wgpkg",
"whitespaces", "whitespaces",
"WHSS", "WHSS",
"winrate", "winrate",
@@ -222,12 +246,7 @@
"yolo", "yolo",
"youtuber", "youtuber",
"youtubers", "youtubers",
"zoomable", "zoomable"
"zustand"
], ],
"deno.enable": false, "typescript.tsdk": "node_modules\\typescript\\lib"
"deno.unstable": false,
"cSpell.enableFiletypes": ["glsl", "proto3"],
"typescript.tsdk": "node_modules\\typescript\\lib",
"liveServer.settings.multiRootWorkspaceName": "blitzkit"
} }
+41 -7
View File
@@ -1,13 +1,47 @@
## BlitzKit # BlitzKit
Your premier source for all things Blitz. Tools for everything and anything, World of Tanks Blitz.
### https://blitzkit.app/ 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/.
![BlitzKit website](https://i.imgur.com/KaxUxmt.png) 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.
## Invite the bot ## Packages
Go to [application directory for BlitzKit](https://discord.com/application-directory/1097673957865443370) and click "Add to Server." Voila! You're ready to go. | Package | Title | Source | Memo |
| ------------------------------------ | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ |
| [packages/app](packages/app) | BlitzKit | [Play Store](https://play.google.com/store/apps/details?id=com.tresabhi.blitzkit), [App Store](https://apps.apple.com/app/4321), [Microsoft Store](https://apps.microsoft.com/detail/zyxw) | The native app of BlitzKit for all platforms. Unavailable from stores at this time. |
| [packages/bot](packages/bot) | BlitzKit Discord Bot | [Discord Application Directory](https://discord.com/discovery/applications/1097673957865443370) | The one-in-all Discord bot for statistics. |
| [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 provides a wide range of tools, APIs, and interconnects all other services. |
![BlitzKit application directory](https://i.imgur.com/YWCtOuJ.png) ## Submodules
| 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 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. |
| `PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID` | [Google Analytics](https://analytics.google.com/) | `430711767` | Google Analytics' unique property ID for [BlitzKit's apex domain](https://blitzkit.app/) and subdomains. |
| `GOOGLE_APPLICATION_CREDENTIALS` | [Google Cloud](https://cloud.google.com/) | | Google Cloud's API key, primarily used for ranking popular tanks and finding relevant tank review videos in Tankopedia. |
| `PUBLIC_DISCORD_SERVER_ID` | [BlitzKit Discord Server](https://discord.gg/nDt7AjGJQH) | `734786591205359697` | BlitzKit Discord Server's id. |
| `PUBLIC_ASSET_BASE` | [blitzkit/assets](https://github.com/blitzkit/assets) | `https://blitzkit.github.io/assets` | The root domain and path that serves the external assets like tank models and icons. |
| `PUBLIC_BRANCH` | | `main` | The branch that the website is operating on. This is completely nominal. |
| `PUBLIC_GUIDE_WEBSITE` | [GuidesBlitz](https://guidesblitz.com/) | `https://guidesblitz.com` | The website that serves the encyclopedia-like content in BlitzKit. |
| `SERVICING` | | `false` | Wether the website is down for servicing. |
| `DISCORD_CLIENT_ID` | [Discord Developer Portal](https://discord.com/developers/applications) | `1097673957865443370` | The BlitzKit Discord Bot's user ID. |
| `GH_TOKEN` | [GitHub Fine-Grained Tokens](https://github.com/settings/personal-access-tokens) | | The GitHub API key used for checking out private submodules and pushing content to the asset repository. |
| `DISCORD_TOKEN` | [Discord Developer Portal](https://discord.com/developers/applications) | | The Discord API Key for the BlitzKit Discord Bot. |
| `DATABASE_URL` | [CockroachDB](https://www.cockroachlabs.com/) | | The CockroachDB-SQL database URL used to link Wargaming and Discord users. |
| `WOTB_DLC_CDN` | | | |
+2884
View File
File diff suppressed because it is too large. Load diff
BIN
View File
Binary file not shown.
+11
View File
@@ -0,0 +1,11 @@
# BlitzKit 1.27.18
## Features
- Added a mini-game to BlitzKit similar to "Guess Who's That Pokemon" but with tanks: http://blitzkit.vercel.app/guess
- Switched to a new renderer for better lighting for the Tankopedia
## Fixes
- The loading bar for the Tankopedia sandbox now shows up again
- Removed a lot of dead code, improving performance
+18
View File
@@ -0,0 +1,18 @@
# BlitzKit 1.27.19
Two updates in a day? I just got a little excited by the new Tankopedia lighting.
## Features
- The camera is now lower down in the Tankopedia, giving the tank a more heroic feel
- Tanks are better framed across all devices, minimizing whitespace
- Added a skip button for the reload visualizer to skip to the next event
## Fixes
- Removed the useless "show grid" setting
- Taking screenshots now no longer includes the scene props like the floor
- The subtler color contrast for the advanced highlighting has now been reverted back to the higher contrast
- The shot display card is now properly aligned as a proper table again
- Tank modules are once again considered in the penetration tester
- Tank filters in the antagonist selector in the Tankopedia sandbox are now properly aligned
+13
View File
@@ -0,0 +1,13 @@
# BlitzKit 1.27.20
It's all technical updates! The HD tanks will be rolled out over the span of few days.
## Technical Changes
- Shadows are disabled if your framerate drops below 30fps
- Pages that redirect to other pages (like the old /tools/tankopedia) now load a few hundred times faster
- Replaced Zustand with a custom state manager called Varuna with the goal to rewrite a significant portion of the website to not require any React for rendering increasing performance
- The transition wireframe in the Tankopedia sandbox now uses a lower quality model for performance reasons
- Tanks with PBR now use the correct maximum resolution textures
- This is a significant update as you can now literally see individual splats of dust on tanks and scratches haha
- Baked the ambient occlusion maps into the textures, hence reducing the overall model load times even with higher resolution textures
+16
View File
@@ -0,0 +1,16 @@
# BlitzKit 1.28.0
BlitzKit, as an app and the new Mixer tool!
## Features
- Created the BlitzKit app that can be downloaded on most devices
- Re-did the BlitzKit logo to be a bit warmer
- Added the Mixer tool which lets you combine tank models into Frankensteins
- Updated all dependencies to the latest version for a decent performance boost
## Fixes
- BlitzKit no longer forgets persistent data if the app crashes without properly closing
- The tools list in the navbar no longer overflows into the neighboring elements on certain devices
- Added keying to a few places, speeding up re-rendering
+5
View File
@@ -0,0 +1,5 @@
# BlitzKit 1.28.1
## Fixes
- The lights no longer crash the Mixer tool
+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
+18 -27
View File
@@ -1,26 +1,23 @@
{ {
"name": "blitzkit", "name": "blitzkit",
"version": "1.29.6",
"license": "BlitzKit License",
"private": true, "private": true,
"version": "1.27.17",
"description": "🎉 Tools for everything World of Tanks Blitz",
"type": "module",
"types": "./types.d.ts",
"workspaces": [
"packages/*"
],
"scripts": { "scripts": {
"build:assets": "cd packages/scripts && bun run build:assets", "build:assets": "cd packages/scripts && bun run build:assets",
"build:averages": "bun --filter=@blitzkit/scripts run build:averages", "build:averages": "bun --filter=@blitzkit/scripts run build:averages",
"build:bot": "cd packages/scripts && bun run build:bot", "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:prisma": "prisma generate",
"build:sitemap": "bun scripts/sitemap.ts", "build:proxyclient": "cd packages/scripts && bun run build:proxyclient",
"build:docs": "vitepress build docs", "build:catalogitemaccesssor": "cd packages/scripts && bun run build:catalogitemaccesssor",
"build:ratingleaderboard": "cd packages/scripts && bun run build:ratingleaderboard", "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", "dev:bot": "bun run build:bot --dev && bun start:bot",
"start:bot": "bun dist/bot/main.js", "start:bot": "bun dist/bot/main.js",
"dev:docs": "vitepress dev docs", "dev:docs": "vitepress dev docs",
"dev:website": "cd packages/website && bun run dev", "dev:website": "cd packages/website && bun run dev",
"dev:app": "cd packages/app && bun run dev",
"dev": "bun run dev:website", "dev": "bun run dev:website",
"dev:api": "bun --filter=@blitzkit/api run dev", "dev:api": "bun --filter=@blitzkit/api run dev",
"setup:depotdownloader": "bun --filter=@blitzkit/scripts run setup:depotdownloader", "setup:depotdownloader": "bun --filter=@blitzkit/scripts run setup:depotdownloader",
@@ -33,27 +30,21 @@
"update": "yarn upgrade --latest", "update": "yarn upgrade --latest",
"tunnel": "cloudflared tunnel --url http://localhost:3000", "tunnel": "cloudflared tunnel --url http://localhost:3000",
"discoverids": "scripts/discoverIds.ts", "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", "preview:website": "cd packages/website && bun run preview",
"build:reviews": "cd packages/scripts && bun build:reviews", "build:reviews": "cd packages/scripts && bun build:reviews",
"build:ids": "cd packages/scripts && bun build:ids", "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": { "dependencies": {
"cheerio": "^1.1.2", "@prisma/client": "^6.18.0",
"googleapis": "^144.0.0", "prisma": "^6.18.0",
"lodash-es": "^4.17.21", "typescript": "^5.9.3"
"protobufjs": "^7.5.2",
"quicklime": "^1.2.0",
"sharp": "^0.33.5"
},
"devDependencies": {
"@types/bun": "latest",
"@types/lodash-es": "^4.17.12",
"@types/node": "^22.14.1",
"prettier": "^3.5.3",
"prisma": "^6.6.0",
"typescript": "^5.8.3"
} }
} }
+18
View File
@@ -0,0 +1,18 @@
{
"name": "@blitzkit/app",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"tauri": "tauri"
},
"dependencies": {
"@tauri-apps/api": "^2",
"@tauri-apps/plugin-opener": "^2",
"@tauri-apps/cli": "^2",
"vite": "^6.0.3",
"typescript": "~5.6.2"
}
}
+7
View File
@@ -0,0 +1,7 @@
# Generated by Cargo
# will have compiled files and executables
/target/
# Generated by Tauri
# will have schema files for capabilities auto-completion
/gen/schemas
+5286
View File
File diff suppressed because it is too large. Load diff
+25
View File
@@ -0,0 +1,25 @@
[package]
name = "blitzkit"
version = "0.1.0"
description = "A Tauri App"
authors = ["you"]
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
# The `_lib` suffix may seem redundant but it is necessary
# to make the lib name unique and wouldn't conflict with the bin name.
# This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519
name = "blitzkit_lib"
crate-type = ["staticlib", "cdylib", "rlib"]
[build-dependencies]
tauri-build = { version = "2", features = [] }
[dependencies]
tauri = { version = "2", features = [] }
tauri-plugin-opener = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
+3
View File
@@ -0,0 +1,3 @@
fn main() {
tauri_build::build()
}
@@ -0,0 +1,10 @@
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
"core:default",
"opener:default"
]
}
+14
View File
@@ -0,0 +1,14 @@
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
#[tauri::command]
fn greet(name: &str) -> String {
format!("Hello, {}! You've been greeted from Rust!", name)
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_opener::init())
.invoke_handler(tauri::generate_handler![greet])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
+27
View File
@@ -0,0 +1,27 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use std::process::{Command, Stdio};
use tauri::Manager;
fn main() {
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");
}
+38
View File
@@ -0,0 +1,38 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "blitzkit",
"version": "0.1.0",
"identifier": "com.tresabhi.blitzkit",
"build": {
"beforeDevCommand": "cd ../website && bun run dev",
"_beforeBuildCommand": "cd ../website && bun run build",
"frontendDist": "../../../dist/website/client",
"devUrl": "http://localhost:4321"
},
"app": {
"withGlobalTauri": true,
"windows": [
{
"url": "http://localhost:4321",
"title": "BlitzKit",
"fullscreen": false,
"resizable": true,
"maximized": true
}
],
"security": {
"csp": null
}
},
"bundle": {
"active": true,
"targets": "all",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/[email protected]",
"icons/icon.icns",
"icons/icon.ico"
]
}
}
+19
View File
@@ -0,0 +1,19 @@
{
"extends": ["../../tsconfig.json"],
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
}
}
+21 -13
View File
@@ -1,26 +1,34 @@
{ {
"name": "@blitzkit/bot", "name": "@blitzkit/bot",
"private": true,
"type": "module",
"types": "src/types.d.ts",
"dependencies": { "dependencies": {
"@blitzkit/core": "workspace:*", "@blitzkit/core": "workspace:*",
"@blitzkit/i18n": "workspace:*", "@blitzkit/i18n": "workspace:*",
"satori": "^0.10.14",
"@resvg/resvg-js": "^2.6.2",
"discord.js": "^14.16.3",
"react": "^18.3.1",
"@radix-ui/colors": "^3.0.0", "@radix-ui/colors": "^3.0.0",
"fuzzysort": "^2.0.4", "@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", "bepaint": "^0.1.0-beta.3",
"dotenv": "^16.4.5", "discord.js": "^14.22.1",
"markdown-escape": "^2.0.0" "dotenv": "^17.2.2",
"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": { "scripts": {
"dev": "bun --watch src/main.ts", "dev": "bun --watch src/main.ts",
"start": "bun src/main.ts" "start": "bun src/main.ts"
}, },
"devDependencies": { "type": "module",
"@types/markdown-escape": "^1.1.3" "types": "src/types.d.ts"
}
} }
+22 -19
View File
@@ -1,28 +1,31 @@
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand'; import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
import { translator } from '../core/localization/translator'; import { translator } from "../core/localization/translator";
import { COMMANDS_RAW, CommandRegistry } from '../events/interactionCreate'; import {
COMMANDS_RAW,
type CommandRegistry,
} from "../events/interactionCreate";
const subcommands = [ const subcommands = [
'embeds', "embeds",
'introduction', "introduction",
'invite', "invite",
'timezones', "timezones",
'credits', "credits",
] as const; ] as const;
type Subcommand = (typeof subcommands)[number]; type Subcommand = (typeof subcommands)[number];
const DOCS: Record<Subcommand, string> = { const DOCS: Record<Subcommand, string> = {
embeds: 'guide/embeds', embeds: "guide/embeds",
introduction: 'guide/introduction', introduction: "guide/introduction",
invite: 'guide/invite', invite: "guide/invite",
timezones: 'guide/timezones', timezones: "guide/timezones",
credits: 'about/credits', credits: "about/credits",
}; };
export const aboutCommand = new Promise<CommandRegistry>(async (resolve) => { export const aboutCommand = new Promise<CommandRegistry>(async (resolve) => {
resolve({ resolve({
command: createLocalizedCommand('about', [ command: createLocalizedCommand("about", [
{ subcommand: 'commands' }, { subcommand: "commands" },
...subcommands.map((subcommand) => ({ subcommand })), ...subcommands.map((subcommand) => ({ subcommand })),
]), ]),
@@ -30,7 +33,7 @@ export const aboutCommand = new Promise<CommandRegistry>(async (resolve) => {
const subcommand = interaction.options.getSubcommand(); const subcommand = interaction.options.getSubcommand();
const { strings } = translator(interaction.locale); const { strings } = translator(interaction.locale);
if (subcommand === 'commands') { if (subcommand === "commands") {
return `${strings.bot.commands.about.subcommands.commands.body}${( return `${strings.bot.commands.about.subcommands.commands.body}${(
await Promise.all(COMMANDS_RAW) 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_localizations?.[interaction.locale] ??
b.command.name) b.command.name)
? -1 ? -1
: 1, : 1
) )
.map( .map(
(registry) => (registry) =>
@@ -51,9 +54,9 @@ export const aboutCommand = new Promise<CommandRegistry>(async (resolve) => {
registry.command.description_localizations?.[ registry.command.description_localizations?.[
interaction.locale interaction.locale
] ?? registry.command.description ] ?? registry.command.description
}`, }`
) )
.join('\n')}`; .join("\n")}`;
} }
const url = `https://raw.githubusercontent.com/tresabhi/blitzkit/main/docs/${DOCS[subcommand as Subcommand]}.md`; 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 { resolvePeriodFromCommand } from '../../core/discord/resolvePeriodFromCommand';
import { resolvePlayerFromButton } from '../../core/discord/resolvePlayerFromButton'; import { resolvePlayerFromButton } from '../../core/discord/resolvePlayerFromButton';
import { resolvePlayerFromCommand } from '../../core/discord/resolvePlayerFromCommand'; import { resolvePlayerFromCommand } from '../../core/discord/resolvePlayerFromCommand';
import { CommandRegistry } from '../../events/interactionCreate'; import type { CommandRegistry } from '../../events/interactionCreate';
import { renderBreakdown } from './render'; import { renderBreakdown } from './render';
export const breakdownCommand = new Promise<CommandRegistry>( export const breakdownCommand = new Promise<CommandRegistry>(
+41 -41
View File
@@ -1,28 +1,28 @@
import { import {
BlitzStats, type BlitzStats,
calculateWN8, calculateWN8,
getAccountInfo, getAccountInfo,
getClanAccountInfo, getClanAccountInfo,
getTankStats, getTankStats,
getWN8Percentile, getWN8Percentile,
TankDefinition, TankDefinition,
} from '@blitzkit/core'; } from "@blitzkit/core";
import { literals } from '@blitzkit/i18n'; import { literals } from "@blitzkit/i18n";
import { Locale } from 'discord.js'; import { Locale } from "discord.js";
import { chunk } from 'lodash-es'; import { chunk } from "lodash-es";
import * as Breakdown from '../../components/Breakdown'; import * as Breakdown from "../../components/Breakdown";
import { CommandWrapper } from '../../components/CommandWrapper'; import { CommandWrapper } from "../../components/CommandWrapper";
import { NoData } from '../../components/NoData'; import { NoData } from "../../components/NoData";
import { TitleBar } from '../../components/TitleBar'; import { TitleBar } from "../../components/TitleBar";
import { filtersToDescription } from '../../core/blitzkit/filtersToDescription'; import { filtersToDescription } from "../../core/blitzkit/filtersToDescription";
import { tankDefinitions } from '../../core/blitzkit/nonBlockingPromises'; import { tankDefinitions } from "../../core/blitzkit/nonBlockingPromises";
import { UserError } from '../../core/blitzkit/userError'; 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 { getStatsInPeriod } from "../../core/blitzstars/getStatsInPeriod";
import { blitzStarsTankAverages } from '../../core/blitzstars/tankAverages'; import { blitzStarsTankAverages } from "../../core/blitzstars/tankAverages";
import { ResolvedPeriod } from '../../core/discord/resolvePeriodFromCommand'; import type { ResolvedPeriod } from "../../core/discord/resolvePeriodFromCommand";
import { ResolvedPlayer } from '../../core/discord/resolvePlayerFromCommand'; import type { ResolvedPlayer } from "../../core/discord/resolvePlayerFromCommand";
import { translator } from '../../core/localization/translator'; import { translator } from "../../core/localization/translator";
const ROWS_PER_PAGE = 8; const ROWS_PER_PAGE = 8;
const MAX_PAGES = 9; const MAX_PAGES = 9;
@@ -31,7 +31,7 @@ export async function renderBreakdown(
{ region, id }: ResolvedPlayer, { region, id }: ResolvedPlayer,
{ start, end, name }: ResolvedPeriod, { start, end, name }: ResolvedPeriod,
filters: StatFilters, filters: StatFilters,
locale: Locale, locale: Locale
) { ) {
const { strings, unwrap } = translator(locale); const { strings, unwrap } = translator(locale);
const awaitedTankDefinitions = await tankDefinitions; const awaitedTankDefinitions = await tankDefinitions;
@@ -39,7 +39,7 @@ export async function renderBreakdown(
const statsInPeriod = await getStatsInPeriod(region, id, start, end, locale); const statsInPeriod = await getStatsInPeriod(region, id, start, end, locale);
const { filteredOrder } = await filterStats(statsInPeriod, filters); const { filteredOrder } = await filterStats(statsInPeriod, filters);
const accountInfo = await getAccountInfo(region, id); const accountInfo = await getAccountInfo(region, id);
const clanData = await getClanAccountInfo(region, id, ['clan']); const clanData = await getClanAccountInfo(region, id, ["clan"]);
const tankStats = await getTankStats(region, id); const tankStats = await getTankStats(region, id);
const filterDescriptions = await filtersToDescription(filters, locale); const filterDescriptions = await filtersToDescription(filters, locale);
const orderedCurrentStats: BlitzStats[] = []; const orderedCurrentStats: BlitzStats[] = [];
@@ -74,63 +74,63 @@ export async function renderBreakdown(
orderedCareerWN8Full.push( orderedCareerWN8Full.push(
tankAveragesEntry tankAveragesEntry
? calculateWN8(tankAveragesEntry.all, tank.all) ? calculateWN8(tankAveragesEntry.all, tank.all)
: undefined, : undefined
); );
}); });
const currentBattles = orderedCurrentStats.reduce( const currentBattles = orderedCurrentStats.reduce(
(accumulator, current) => accumulator + current.battles, (accumulator, current) => accumulator + current.battles,
0, 0
); );
const careerBattles = tankStats.reduce( const careerBattles = tankStats.reduce(
(accumulator, current) => accumulator + current.all.battles, (accumulator, current) => accumulator + current.all.battles,
0, 0
); );
const currentWinrate = const currentWinrate =
orderedCurrentStats.reduce( orderedCurrentStats.reduce(
(accumulator, current) => accumulator + current.wins, (accumulator, current) => accumulator + current.wins,
0, 0
) / currentBattles; ) / currentBattles;
const careerWinrate = const careerWinrate =
tankStats.reduce( tankStats.reduce(
(accumulator, current) => accumulator + current.all.wins, (accumulator, current) => accumulator + current.all.wins,
0, 0
) / careerBattles; ) / careerBattles;
const currentWN8 = const currentWN8 =
orderedCurrentWN8.reduce<number>( orderedCurrentWN8.reduce<number>(
(accumulator, current, index) => (accumulator, current, index) =>
accumulator + (current ?? 0) * orderedCurrentStats[index].battles, accumulator + (current ?? 0) * orderedCurrentStats[index].battles,
0, 0
) / ) /
orderedCurrentStats.reduce( orderedCurrentStats.reduce(
(accumulator, current, index) => (accumulator, current, index) =>
accumulator + accumulator +
(orderedCurrentWN8[index] === undefined ? 0 : current.battles), (orderedCurrentWN8[index] === undefined ? 0 : current.battles),
0, 0
); );
const careerWN8 = const careerWN8 =
orderedCareerWN8Full.reduce<number>( orderedCareerWN8Full.reduce<number>(
(accumulator, current) => accumulator + (current ?? 0), (accumulator, current) => accumulator + (current ?? 0),
0, 0
) / ) /
tankStats.reduce( tankStats.reduce(
(accumulator, current, index) => (accumulator, current, index) =>
accumulator + accumulator +
(orderedCareerWN8Full[index] === undefined ? 0 : current.all.battles), (orderedCareerWN8Full[index] === undefined ? 0 : current.all.battles),
0, 0
); );
const currentDamage = const currentDamage =
orderedCurrentStats.reduce( orderedCurrentStats.reduce(
(accumulator, current) => accumulator + current.damage_dealt, (accumulator, current) => accumulator + current.damage_dealt,
0, 0
) / currentBattles; ) / currentBattles;
const careerDamage = const careerDamage =
tankStats.reduce( tankStats.reduce(
(accumulator, current) => accumulator + current.all.damage_dealt, (accumulator, current) => accumulator + current.all.damage_dealt,
0, 0
) / careerBattles; ) / careerBattles;
const children: JSX.Element[] = []; const children: React.JSX.Element[] = [];
if (filteredOrder.length > 0) { if (filteredOrder.length > 0) {
children.push( children.push(
@@ -172,7 +172,7 @@ export async function renderBreakdown(
delta: currentDamage - careerDamage, delta: currentDamage - careerDamage,
}, },
]} ]}
/>, />
); );
filteredOrder.forEach((id, index) => { filteredOrder.forEach((id, index) => {
@@ -193,9 +193,9 @@ export async function renderBreakdown(
title={ title={
tankDefinition tankDefinition
? unwrap(tankDefinition.name) ? unwrap(tankDefinition.name)
: literals(strings.bot.commands.breakdown.body.unknown_tank, [ : literals(strings.bot.commands.breakdown.body.unknown_tank, {
`${id}`, id,
]) })
} }
stats={[ stats={[
{ {
@@ -206,7 +206,7 @@ export async function renderBreakdown(
{ {
title: strings.bot.commands.breakdown.body.winrate, title: strings.bot.commands.breakdown.body.winrate,
current: `${(100 * (current.wins / current.battles)).toFixed( current: `${(100 * (current.wins / current.battles)).toFixed(
2, 2
)}%`, )}%`,
career: `${(100 * (career.wins / career.battles)).toFixed(2)}%`, career: `${(100 * (career.wins / career.battles)).toFixed(2)}%`,
delta: delta:
@@ -230,24 +230,24 @@ export async function renderBreakdown(
{ {
title: strings.bot.commands.breakdown.body.damage, title: strings.bot.commands.breakdown.body.damage,
current: Math.round( current: Math.round(
current.damage_dealt / current.battles, current.damage_dealt / current.battles
).toLocaleString(), ).toLocaleString(),
career: Math.round( career: Math.round(
career.damage_dealt / career.battles, career.damage_dealt / career.battles
).toLocaleString(), ).toLocaleString(),
delta: delta:
current.damage_dealt / current.battles - current.damage_dealt / current.battles -
career.damage_dealt / career.battles, career.damage_dealt / career.battles,
}, },
]} ]}
/>, />
); );
}); });
} }
const pages = chunk( const pages = chunk(
children, children,
Math.max(Math.ceil(children.length / MAX_PAGES), ROWS_PER_PAGE), Math.max(Math.ceil(children.length / MAX_PAGES), ROWS_PER_PAGE)
); );
if (filteredOrder.length > 0) { if (filteredOrder.length > 0) {
+9 -9
View File
@@ -1,14 +1,14 @@
import packageJSON from '../../../../package.json'; import packageJSON from "../../../../package.json";
import { client } from '../core/discord/client'; import { client } from "../core/discord/client";
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand'; import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
import { translator } from '../core/localization/translator'; import { translator } from "../core/localization/translator";
import { CommandRegistry } from '../events/interactionCreate'; import type { CommandRegistry } from "../events/interactionCreate";
const executionStart = new Date().getTime(); const executionStart = new Date().getTime();
export const debugCommand = new Promise<CommandRegistry>((resolve) => { export const debugCommand = new Promise<CommandRegistry>((resolve) => {
resolve({ resolve({
command: createLocalizedCommand('debug'), command: createLocalizedCommand("debug"),
async handler(interaction) { async handler(interaction) {
const { strings } = translator(interaction.locale); const { strings } = translator(interaction.locale);
@@ -23,15 +23,15 @@ export const debugCommand = new Promise<CommandRegistry>((resolve) => {
[ [
strings.bot.commands.debug.body.uptime, strings.bot.commands.debug.body.uptime,
`${Math.floor((uptime / 1000 / 60 / 60) % 24)}h ${Math.floor( `${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( )}m ${Math.floor((uptime / 1000) % 60)}s ${Math.floor(
uptime % 1000, uptime % 1000
)}ms`, )}ms`,
], ],
]; ];
return `${strings.bot.commands.debug.body.title}${list return `${strings.bot.commands.debug.body.title}${list
.map(([key, value]) => `- ${key}: ${value}`) .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 { emblemURL, getAccountInfo, getClanAccountInfo } from "@blitzkit/core";
import { Locale, SlashCommandSubcommandGroupBuilder } from 'discord.js'; import { Locale, SlashCommandSubcommandGroupBuilder } from "discord.js";
import { CommandWrapper } from '../components/CommandWrapper'; import { CommandWrapper } from "../components/CommandWrapper";
import * as Graph from '../components/Graph'; import * as Graph from "../components/Graph";
import { LineColor } from '../components/Graph/components/Line/constants'; import { LineColor } from "../components/Graph/components/Line/constants";
import { NoData } from '../components/NoData'; import { NoData } from "../components/NoData";
import { TitleBar } from '../components/TitleBar'; import { TitleBar } from "../components/TitleBar";
import { resolveTankId } from '../core/blitz/resolveTankId'; import { resolveTankId } from "../core/blitz/resolveTankId";
import { getBlitzStarsLinkButton } from '../core/blitzstars/getBlitzStarsLinkButton'; import { getBlitzStarsLinkButton } from "../core/blitzstars/getBlitzStarsLinkButton";
import { getPlayerHistories } from '../core/blitzstars/getPlayerHistories'; import { getPlayerHistories } from "../core/blitzstars/getPlayerHistories";
import { getTankHistories } from '../core/blitzstars/getTankHistories'; import { getTankHistories } from "../core/blitzstars/getTankHistories";
import { addPeriodSubCommands } from '../core/discord/addPeriodSubCommands'; import { addPeriodSubCommands } from "../core/discord/addPeriodSubCommands";
import { addTankChoices } from '../core/discord/addTankChoices'; import { addTankChoices } from "../core/discord/addTankChoices";
import { addUsernameChoices } from '../core/discord/addUsernameChoices'; import { addUsernameChoices } from "../core/discord/addUsernameChoices";
import { autocompleteTanks } from '../core/discord/autocompleteTanks'; import { autocompleteTanks } from "../core/discord/autocompleteTanks";
import { autocompleteUsername } from '../core/discord/autocompleteUsername'; import { autocompleteUsername } from "../core/discord/autocompleteUsername";
import { buttonRefresh } from '../core/discord/buttonRefresh'; import { buttonRefresh } from "../core/discord/buttonRefresh";
import { commandToURL } from '../core/discord/commandToURL'; import { commandToURL } from "../core/discord/commandToURL";
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand'; import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
import { getCustomPeriodParams } from '../core/discord/getCustomPeriodParams'; import { getCustomPeriodParams } from "../core/discord/getCustomPeriodParams";
import { resolvePeriodFromButton } from '../core/discord/resolvePeriodFromButton'; import { resolvePeriodFromButton } from "../core/discord/resolvePeriodFromButton";
import { import {
ResolvedPeriod, type ResolvedPeriod,
resolvePeriodFromCommand, resolvePeriodFromCommand,
} from '../core/discord/resolvePeriodFromCommand'; } from "../core/discord/resolvePeriodFromCommand";
import { resolvePlayerFromButton } from '../core/discord/resolvePlayerFromButton'; import { resolvePlayerFromButton } from "../core/discord/resolvePlayerFromButton";
import { import {
ResolvedPlayer, type ResolvedPlayer,
resolvePlayerFromCommand, resolvePlayerFromCommand,
} from '../core/discord/resolvePlayerFromCommand'; } from "../core/discord/resolvePlayerFromCommand";
import { CommandRegistry } from '../events/interactionCreate'; import type { CommandRegistry } from "../events/interactionCreate";
type EvolutionStatType = 'player' | 'tank'; type EvolutionStatType = "player" | "tank";
async function render( async function render(
{ region, id }: ResolvedPlayer, { region, id }: ResolvedPlayer,
{ start, end, name }: ResolvedPeriod, { start, end, name }: ResolvedPeriod,
type: 'player' | 'tank', type: "player" | "tank",
tankId: number | null, tankId: number | null,
locale: Locale, locale: Locale
) { ) {
const accountInfo = await getAccountInfo(region, id); const accountInfo = await getAccountInfo(region, id);
const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan; const clan = (await getClanAccountInfo(region, id, ["clan"]))?.clan;
const logo = clan ? emblemURL(clan.emblem_set_id) : undefined; const logo = clan ? emblemURL(clan.emblem_set_id) : undefined;
const histories = await (type === 'player' const histories = await (type === "player"
? getPlayerHistories(region, id, { ? getPlayerHistories(region, id, {
start, start,
end, end,
@@ -61,7 +61,7 @@ async function render(
[ [
history.all.battles, history.all.battles,
(history.all.wins / history.all.battles) * 100, (history.all.wins / history.all.battles) * 100,
] as Graph.PlotItem, ] as Graph.PlotItem
); );
const times = histories.map((history) => history.last_battle_time); const times = histories.map((history) => history.last_battle_time);
const minTime = Math.min(...times); const minTime = Math.min(...times);
@@ -82,7 +82,7 @@ async function render(
verticalMargin={{ verticalMargin={{
min: minY, min: minY,
max: maxY, max: maxY,
suffix: '%', suffix: "%",
precision: 2, precision: 2,
}} }}
horizontalMargin={{ horizontalMargin={{
@@ -110,22 +110,22 @@ async function render(
export const evolutionCommand = new Promise<CommandRegistry>( export const evolutionCommand = new Promise<CommandRegistry>(
async (resolve) => { async (resolve) => {
resolve({ resolve({
command: createLocalizedCommand('evolution', [ command: createLocalizedCommand("evolution", [
{ {
group: 'player', group: "player",
modify(option: SlashCommandSubcommandGroupBuilder) { modify(option: SlashCommandSubcommandGroupBuilder) {
addPeriodSubCommands(option, (option) => addPeriodSubCommands(option, (option) =>
option.addStringOption(addUsernameChoices), option.addStringOption(addUsernameChoices)
); );
}, },
}, },
{ {
group: 'tank', group: "tank",
modify(option: SlashCommandSubcommandGroupBuilder) { modify(option: SlashCommandSubcommandGroupBuilder) {
addPeriodSubCommands(option, (option) => addPeriodSubCommands(option, (option) =>
option option
.addStringOption(addTankChoices) .addStringOption(addTankChoices)
.addStringOption(addUsernameChoices), .addStringOption(addUsernameChoices)
); );
}, },
}, },
@@ -136,9 +136,9 @@ export const evolutionCommand = new Promise<CommandRegistry>(
interaction.options.getSubcommandGroup() as EvolutionStatType; interaction.options.getSubcommandGroup() as EvolutionStatType;
const player = await resolvePlayerFromCommand(interaction); const player = await resolvePlayerFromCommand(interaction);
const period = resolvePeriodFromCommand(player.region, interaction); const period = resolvePeriodFromCommand(player.region, interaction);
const tankIdRaw = interaction.options.getString('tank')!; const tankIdRaw = interaction.options.getString("tank")!;
const tankId = const tankId =
commandGroup === 'tank' commandGroup === "tank"
? await resolveTankId(tankIdRaw, interaction.locale) ? await resolveTankId(tankIdRaw, interaction.locale)
: null; : null;
const path = commandToURL(interaction, { const path = commandToURL(interaction, {
@@ -153,13 +153,13 @@ export const evolutionCommand = new Promise<CommandRegistry>(
period, period,
commandGroup, commandGroup,
tankId, tankId,
interaction.locale, interaction.locale
), ),
buttonRefresh(interaction, path), buttonRefresh(interaction, path),
await getBlitzStarsLinkButton( await getBlitzStarsLinkButton(
player.region, player.region,
player.id, player.id,
tankId ?? undefined, tankId ?? undefined
), ),
]; ];
}, },
@@ -171,7 +171,7 @@ export const evolutionCommand = new Promise<CommandRegistry>(
async button(interaction) { async button(interaction) {
const url = new URL(`https://example.com/${interaction.customId}`); 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 commandGroup = path[2] as EvolutionStatType;
const player = await resolvePlayerFromButton(interaction); const player = await resolvePlayerFromButton(interaction);
const period = resolvePeriodFromButton(player.region, interaction); const period = resolvePeriodFromButton(player.region, interaction);
@@ -180,10 +180,10 @@ export const evolutionCommand = new Promise<CommandRegistry>(
player, player,
period, period,
commandGroup, commandGroup,
parseInt(url.searchParams.get('tankId') ?? '0') || null, parseInt(url.searchParams.get("tankId") ?? "0") || null,
interaction.locale, interaction.locale
); );
}, },
}); });
}, }
); );
+4 -4
View File
@@ -11,7 +11,7 @@ import { NoData } from '../components/NoData';
import { TierWeights } from '../components/TierWeights'; import { TierWeights } from '../components/TierWeights';
import { TitleBar } from '../components/TitleBar'; import { TitleBar } from '../components/TitleBar';
import { filtersToDescription } from '../core/blitzkit/filtersToDescription'; 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 { getBlitzStarsLinkButton } from '../core/blitzstars/getBlitzStarsLinkButton';
import { getStatsInPeriod } from '../core/blitzstars/getStatsInPeriod'; import { getStatsInPeriod } from '../core/blitzstars/getStatsInPeriod';
import { addPeriodicFilterOptions } from '../core/discord/addPeriodicFilterOptions'; import { addPeriodicFilterOptions } from '../core/discord/addPeriodicFilterOptions';
@@ -26,15 +26,15 @@ import { getFiltersFromButton } from '../core/discord/getFiltersFromButton';
import { getFiltersFromCommand } from '../core/discord/getFiltersFromCommand'; import { getFiltersFromCommand } from '../core/discord/getFiltersFromCommand';
import { resolvePeriodFromButton } from '../core/discord/resolvePeriodFromButton'; import { resolvePeriodFromButton } from '../core/discord/resolvePeriodFromButton';
import { import {
ResolvedPeriod, type ResolvedPeriod,
resolvePeriodFromCommand, resolvePeriodFromCommand,
} from '../core/discord/resolvePeriodFromCommand'; } from '../core/discord/resolvePeriodFromCommand';
import { resolvePlayerFromButton } from '../core/discord/resolvePlayerFromButton'; import { resolvePlayerFromButton } from '../core/discord/resolvePlayerFromButton';
import { import {
ResolvedPlayer, type ResolvedPlayer,
resolvePlayerFromCommand, resolvePlayerFromCommand,
} from '../core/discord/resolvePlayerFromCommand'; } from '../core/discord/resolvePlayerFromCommand';
import { CommandRegistry } from '../events/interactionCreate'; import type { CommandRegistry } from '../events/interactionCreate';
async function render( async function render(
{ region, id }: ResolvedPlayer, { region, id }: ResolvedPlayer,
+28 -28
View File
@@ -1,44 +1,44 @@
import { getAccountInfo, getClanInfo } from '@blitzkit/core'; import { getAccountInfo, getClanInfo } from "@blitzkit/core";
import { literals } from '@blitzkit/i18n'; import { literals } from "@blitzkit/i18n";
import { Locale } from 'discord.js'; import { Locale } from "discord.js";
import { CommandWrapper } from '../components/CommandWrapper'; import { CommandWrapper } from "../components/CommandWrapper";
import { GenericStats } from '../components/GenericStats'; import { GenericStats } from "../components/GenericStats";
import { NoData } from '../components/NoData'; import { NoData } from "../components/NoData";
import { TitleBar } from '../components/TitleBar'; import { TitleBar } from "../components/TitleBar";
import { addClanChoices } from '../core/discord/addClanChoices'; import { addClanChoices } from "../core/discord/addClanChoices";
import { autocompleteClan } from '../core/discord/autocompleteClan'; import { autocompleteClan } from "../core/discord/autocompleteClan";
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand'; import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
import { localizationObject } from '../core/discord/localizationObject'; import { localizationObject } from "../core/discord/localizationObject";
import { resolveClanFromCommand } from '../core/discord/resolveClanFromCommand'; import { resolveClanFromCommand } from "../core/discord/resolveClanFromCommand";
import { translator } from '../core/localization/translator'; import { translator } from "../core/localization/translator";
import { CommandRegistry } from '../events/interactionCreate'; import type { CommandRegistry } from "../events/interactionCreate";
const DEFAULT_THRESHOLD = 7; const DEFAULT_THRESHOLD = 7;
export const inactiveCommand = new Promise<CommandRegistry>((resolve) => { export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
resolve({ resolve({
command: createLocalizedCommand('inactive') command: createLocalizedCommand("inactive")
.addStringOption(addClanChoices) .addStringOption(addClanChoices)
.addNumberOption((option) => { .addNumberOption((option) => {
const { strings } = translator(Locale.EnglishUS); const { strings } = translator(Locale.EnglishUS);
return option return option
.setName('threshold') .setName("threshold")
.setNameLocalizations( .setNameLocalizations(
localizationObject( localizationObject(
(strings) => strings.bot.commands.inactive.options.threshold.name, (strings) => strings.bot.commands.inactive.options.threshold.name,
undefined, undefined,
true, true
), )
) )
.setDescription( .setDescription(
strings.bot.commands.inactive.options.threshold.description, strings.bot.commands.inactive.options.threshold.description
) )
.setDescriptionLocalizations( .setDescriptionLocalizations(
localizationObject( localizationObject(
(strings) => (strings) =>
strings.bot.commands.inactive.options.threshold.description, strings.bot.commands.inactive.options.threshold.description
), )
) )
.setMinValue(0); .setMinValue(0);
}), }),
@@ -47,7 +47,7 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
const { strings } = translator(interaction.locale); const { strings } = translator(interaction.locale);
const { region, id } = await resolveClanFromCommand(interaction); const { region, id } = await resolveClanFromCommand(interaction);
const threshold = const threshold =
interaction.options.getNumber('threshold')! ?? DEFAULT_THRESHOLD; interaction.options.getNumber("threshold")! ?? DEFAULT_THRESHOLD;
const time = new Date().getTime() / 1000; const time = new Date().getTime() / 1000;
const clanInfo = await getClanInfo(region, id); const clanInfo = await getClanInfo(region, id);
const accountInfo = await getAccountInfo(region, clanInfo.members_ids); const accountInfo = await getAccountInfo(region, clanInfo.members_ids);
@@ -57,7 +57,7 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
[ [
account.nickname, account.nickname,
(time - account.last_battle_time) / 60 / 60 / 24, (time - account.last_battle_time) / 60 / 60 / 24,
] as [string, number], ] as [string, number]
) )
.filter(([, inactiveDays]) => inactiveDays >= threshold) .filter(([, inactiveDays]) => inactiveDays >= threshold)
.sort((a, b) => b[1] - a[1]) .sort((a, b) => b[1] - a[1])
@@ -65,10 +65,10 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
([name, days]) => ([name, days]) =>
[ [
name, name,
literals(strings.bot.commands.inactive.body.listing, [ literals(strings.bot.commands.inactive.body.listing, {
days.toFixed(0), days: days.toFixed(0),
]), }),
] as [string, string], ] as [string, string]
); );
const hasInactiveMembers = inactive.length > 0; 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`} image={`https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clanInfo.emblem_set_id}.png`}
description={`${literals( description={`${literals(
strings.bot.commands.inactive.body.subtitle, strings.bot.commands.inactive.body.subtitle,
[`${threshold}`], { days: threshold }
)} • ${new Date().toLocaleDateString(interaction.locale)}`} )} • ${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 { embedInfo } from '../core/discord/embedInfo';
import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand'; import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand';
import { translator } from '../core/localization/translator'; import { translator } from '../core/localization/translator';
import { CommandRegistry } from '../events/interactionCreate'; import type { CommandRegistry } from '../events/interactionCreate';
import { theme } from '../stitches.config'; import { theme } from '../stitches.config';
export const verifyCommand = new Promise<CommandRegistry>((resolve) => { export const verifyCommand = new Promise<CommandRegistry>((resolve) => {
+22 -22
View File
@@ -5,31 +5,31 @@ import {
flags, flags,
getAccountInfo, getAccountInfo,
getTankStats, getTankStats,
} from '@blitzkit/core'; } from "@blitzkit/core";
import { literals } from '@blitzkit/i18n'; import { literals } from "@blitzkit/i18n";
import markdownEscape from 'markdown-escape'; import markdownEscape from "markdown-escape";
import { import {
gameDefinitions, gameDefinitions,
tankDefinitions, tankDefinitions,
} from '../core/blitzkit/nonBlockingPromises'; } from "../core/blitzkit/nonBlockingPromises";
import { addTierChoices } from '../core/discord/addTierChoices'; import { addTierChoices } from "../core/discord/addTierChoices";
import { addUsernameChoices } from '../core/discord/addUsernameChoices'; import { addUsernameChoices } from "../core/discord/addUsernameChoices";
import { autocompleteUsername } from '../core/discord/autocompleteUsername'; import { autocompleteUsername } from "../core/discord/autocompleteUsername";
import { chunkLines } from '../core/discord/chunkLines'; import { chunkLines } from "../core/discord/chunkLines";
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand'; import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand'; import { resolvePlayerFromCommand } from "../core/discord/resolvePlayerFromCommand";
import { translator } from '../core/localization/translator'; import { translator } from "../core/localization/translator";
import { CommandRegistry } from '../events/interactionCreate'; import { type CommandRegistry } from "../events/interactionCreate";
export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => { export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
resolve({ resolve({
command: createLocalizedCommand('owned-tanks') command: createLocalizedCommand("owned-tanks")
.addStringOption(addTierChoices) .addStringOption(addTierChoices)
.addStringOption(addUsernameChoices), .addStringOption(addUsernameChoices),
async handler(interaction) { async handler(interaction) {
const { strings, unwrap } = translator(interaction.locale); 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 { id, region } = await resolvePlayerFromCommand(interaction);
const accountInfo = await getAccountInfo(region, id); const accountInfo = await getAccountInfo(region, id);
const tankStats = await getTankStats(region, id); const tankStats = await getTankStats(region, id);
@@ -43,7 +43,7 @@ export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
tankStats.map(async (tankData) => ({ tankStats.map(async (tankData) => ({
tankDefinitions: (await tankDefinitions).tanks[tankData.tank_id]!, tankDefinitions: (await tankDefinitions).tanks[tankData.tank_id]!,
id: tankData.tank_id, id: tankData.tank_id,
})), }))
) )
).filter((tank) => tank.tankDefinitions?.tier === tier); ).filter((tank) => tank.tankDefinitions?.tier === tier);
const groupedTanks: Record<string, TankDefinition[]> = {}; const groupedTanks: Record<string, TankDefinition[]> = {};
@@ -60,25 +60,25 @@ export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
const awaitedTankDefinitions = await tankDefinitions; const awaitedTankDefinitions = await tankDefinitions;
const awaitedGameDefinitions = await gameDefinitions; const awaitedGameDefinitions = await gameDefinitions;
const title = literals(strings.bot.commands.owned_tanks.body.title, [ const title = literals(strings.bot.commands.owned_tanks.body.title, {
markdownEscape(accountInfo.nickname), name: markdownEscape(accountInfo.nickname),
TIER_ROMAN_NUMERALS[tier], tier: TIER_ROMAN_NUMERALS[tier],
]); });
const tankList = tankStats const tankList = tankStats
.map(({ tank_id }) => awaitedTankDefinitions.tanks[tank_id]) .map(({ tank_id }) => awaitedTankDefinitions.tanks[tank_id])
.filter((tank) => tank.tier === tier) .filter((tank) => tank.tier === tier)
.sort( .sort(
(a, b) => (a, b) =>
TANK_CLASSES.indexOf(a.class) - TANK_CLASSES.indexOf(b.class), TANK_CLASSES.indexOf(a.class) - TANK_CLASSES.indexOf(b.class)
) )
.sort( .sort(
(a, b) => (a, b) =>
awaitedGameDefinitions.nations.indexOf(a.nation) - awaitedGameDefinitions.nations.indexOf(a.nation) -
awaitedGameDefinitions.nations.indexOf(b.nation), awaitedGameDefinitions.nations.indexOf(b.nation)
) )
.map( .map(
(tank) => (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]; const lines = [title, ...tankList];
+13 -13
View File
@@ -1,11 +1,11 @@
import { literals } from '@blitzkit/i18n'; import { literals } from "@blitzkit/i18n";
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand'; import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
import { translator } from '../core/localization/translator'; import { translator } from "../core/localization/translator";
import { CommandRegistry } from '../events/interactionCreate'; import { CommandRegistry } from "../events/interactionCreate";
export const permissionsCommand = new Promise<CommandRegistry>((resolve) => { export const permissionsCommand = new Promise<CommandRegistry>((resolve) => {
resolve({ resolve({
command: createLocalizedCommand('permissions'), command: createLocalizedCommand("permissions"),
async handler(interaction) { async handler(interaction) {
const { strings } = translator(interaction.locale); const { strings } = translator(interaction.locale);
@@ -14,33 +14,33 @@ export const permissionsCommand = new Promise<CommandRegistry>((resolve) => {
new Date().getDay() === 5 && new Date().getDate() === 13; new Date().getDay() === 5 && new Date().getDate() === 13;
const permissions = [ const permissions = [
[ [
interaction.appPermissions?.has('ViewChannel'), interaction.appPermissions?.has("ViewChannel"),
strings.bot.commands.permissions.body.view_channels, strings.bot.commands.permissions.body.view_channels,
], ],
[ [
interaction.appPermissions?.has('ReadMessageHistory'), interaction.appPermissions?.has("ReadMessageHistory"),
strings.bot.commands.permissions.body.read_message_history, strings.bot.commands.permissions.body.read_message_history,
], ],
[ [
interaction.appPermissions?.has('AttachFiles'), interaction.appPermissions?.has("AttachFiles"),
strings.bot.commands.permissions.body.attach_files, strings.bot.commands.permissions.body.attach_files,
], ],
[ [
interaction.appPermissions?.has('UseExternalEmojis'), interaction.appPermissions?.has("UseExternalEmojis"),
strings.bot.commands.permissions.body.external_emojis, strings.bot.commands.permissions.body.external_emojis,
], ],
]; ];
const body = permissions const body = permissions
.map(([hasPermission, description]) => { .map(([hasPermission, description]) => {
if (hasPermission) { if (hasPermission) {
return `${isFridayThe13th ? '😇' : '✅'} ${description}`; return `${isFridayThe13th ? "😇" : "✅"} ${description}`;
} else { } 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 { literals } from "@blitzkit/i18n";
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand'; import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
import { translator } from '../core/localization/translator'; import { translator } from "../core/localization/translator";
import { CommandRegistry } from '../events/interactionCreate'; import type { CommandRegistry } from "../events/interactionCreate";
export const pingCommand = new Promise<CommandRegistry>((resolve) => { export const pingCommand = new Promise<CommandRegistry>((resolve) => {
resolve({ resolve({
handlesInteraction: true, handlesInteraction: true,
command: createLocalizedCommand('ping', [ command: createLocalizedCommand("ping", [
{ subcommand: 'blitzkit' }, { subcommand: "blitzkit" },
{ subcommand: 'wotb' }, { subcommand: "wotb" },
]), ]),
async handler(interaction) { async handler(interaction) {
@@ -17,17 +17,17 @@ export const pingCommand = new Promise<CommandRegistry>((resolve) => {
const subcommand = interaction.options.getSubcommand(); const subcommand = interaction.options.getSubcommand();
const executionStart = Date.now(); const executionStart = Date.now();
if (subcommand === 'blitzkit') { if (subcommand === "blitzkit") {
await interaction.editReply(strings.bot.commands.ping.body.pong); await interaction.editReply(strings.bot.commands.ping.body.pong);
} else { } else {
await fetch('https://api.wotblitz.com/'); await fetch("https://api.wotblitz.com/");
} }
const executionTime = Date.now() - executionStart; const executionTime = Date.now() - executionStart;
interaction.editReply( interaction.editReply(
literals(strings.bot.commands.ping.body.pong_with_time, [ literals(strings.bot.commands.ping.body.pong_with_time, {
`${Math.round(executionTime)}`, time: executionTime.toFixed(0),
]), })
); );
}, },
}); });
+18 -18
View File
@@ -1,19 +1,19 @@
import { getAccountInfo } from '@blitzkit/core'; import { getAccountInfo } from "@blitzkit/core";
import { literals } from '@blitzkit/i18n'; import { literals } from "@blitzkit/i18n";
import markdownEscape from 'markdown-escape'; import markdownEscape from "markdown-escape";
import { addUsernameChoices } from '../core/discord/addUsernameChoices'; import { addUsernameChoices } from "../core/discord/addUsernameChoices";
import { autocompleteUsername } from '../core/discord/autocompleteUsername'; import { autocompleteUsername } from "../core/discord/autocompleteUsername";
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand'; import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
import { embedInfo } from '../core/discord/embedInfo'; import { embedInfo } from "../core/discord/embedInfo";
import { markdownTable } from '../core/discord/markdownTable'; import { markdownTable } from "../core/discord/markdownTable";
import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand'; import { resolvePlayerFromCommand } from "../core/discord/resolvePlayerFromCommand";
import { translator } from '../core/localization/translator'; import { translator } from "../core/localization/translator";
import { CommandRegistry } from '../events/interactionCreate'; import { CommandRegistry } from "../events/interactionCreate";
export const playerInfoCommand = new Promise<CommandRegistry>((resolve) => { export const playerInfoCommand = new Promise<CommandRegistry>((resolve) => {
resolve({ resolve({
command: command:
createLocalizedCommand('player-info').addStringOption(addUsernameChoices), createLocalizedCommand("player-info").addStringOption(addUsernameChoices),
async handler(interaction) { async handler(interaction) {
const { strings } = translator(interaction.locale); const { strings } = translator(interaction.locale);
@@ -22,9 +22,9 @@ export const playerInfoCommand = new Promise<CommandRegistry>((resolve) => {
const accountInfo = await getAccountInfo(region, id); const accountInfo = await getAccountInfo(region, id);
return embedInfo( return embedInfo(
literals(strings.bot.commands.player_info.body.title, [ literals(strings.bot.commands.player_info.body.title, {
markdownEscape(accountInfo.nickname), name: markdownEscape(accountInfo.nickname),
]), }),
markdownTable([ markdownTable([
[ [
@@ -51,16 +51,16 @@ export const playerInfoCommand = new Promise<CommandRegistry>((resolve) => {
[ [
strings.bot.commands.player_info.body.created, strings.bot.commands.player_info.body.created,
new Date(accountInfo.created_at * 1000).toLocaleDateString( new Date(accountInfo.created_at * 1000).toLocaleDateString(
interaction.locale, interaction.locale
), ),
], ],
[ [
strings.bot.commands.player_info.body.last_battle, strings.bot.commands.player_info.body.last_battle,
new Date(accountInfo.last_battle_time * 1000).toLocaleDateString( new Date(accountInfo.last_battle_time * 1000).toLocaleDateString(
interaction.locale, interaction.locale
), ),
], ],
]), ])
); );
}, },
+118 -118
View File
@@ -8,42 +8,42 @@ import {
getRatingInfo, getRatingInfo,
getRatingNeighbors, getRatingNeighbors,
imgur, imgur,
} from '@blitzkit/core'; } from "@blitzkit/core";
import { literals } from '@blitzkit/i18n'; import { literals } from "@blitzkit/i18n";
import { SlashCommandSubcommandBuilder } from 'discord.js'; import { SlashCommandSubcommandBuilder } from "discord.js";
import markdownEscape from 'markdown-escape'; import markdownEscape from "markdown-escape";
import { Glow } from '../components/AllStatsOverview/components/HeroStat/components/Glow'; import { Glow } from "../components/AllStatsOverview/components/HeroStat/components/Glow";
import { CommandWrapper } from '../components/CommandWrapper'; import { CommandWrapper } from "../components/CommandWrapper";
import { DeltaCaret } from '../components/DeltaCaret'; import { DeltaCaret } from "../components/DeltaCaret";
import { TitleBar } from '../components/TitleBar'; import { TitleBar } from "../components/TitleBar";
import { normalizeLeagueIcon } from '../core/blitz/normalizeLeagueIcon'; import { normalizeLeagueIcon } from "../core/blitz/normalizeLeagueIcon";
import { iconPng } from '../core/blitzkit/iconPng'; import { iconPng } from "../core/blitzkit/iconPng";
import { addUsernameChoices } from '../core/discord/addUsernameChoices'; import { addUsernameChoices } from "../core/discord/addUsernameChoices";
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand'; import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand'; import { resolvePlayerFromCommand } from "../core/discord/resolvePlayerFromCommand";
import { translator } from '../core/localization/translator'; import { translator } from "../core/localization/translator";
import { CommandRegistry } from '../events/interactionCreate'; import type { CommandRegistry } from "../events/interactionCreate";
import { theme } from '../stitches.config'; import { theme } from "../stitches.config";
const LEAGUE_ACCENT = [ const LEAGUE_ACCENT = [
'_purple', "_purple",
'_sand', "_sand",
'_amber', "_amber",
'_mauve', "_mauve",
'_brown', "_brown",
] as const; ] as const;
export const ratingCommand = new Promise<CommandRegistry>((resolve) => { export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
resolve({ resolve({
command: createLocalizedCommand('rating', [ command: createLocalizedCommand("rating", [
{ {
subcommand: 'today', subcommand: "today",
modify(option: SlashCommandSubcommandBuilder) { modify(option: SlashCommandSubcommandBuilder) {
option.addStringOption(addUsernameChoices); option.addStringOption(addUsernameChoices);
}, },
}, },
{ {
subcommand: 'season', subcommand: "season",
modify(option: SlashCommandSubcommandBuilder) { modify(option: SlashCommandSubcommandBuilder) {
option.addStringOption(addUsernameChoices); option.addStringOption(addUsernameChoices);
}, },
@@ -52,11 +52,11 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
async handler(interaction) { async handler(interaction) {
const subcommand = interaction.options.getSubcommand(true) as const subcommand = interaction.options.getSubcommand(true) as
| 'season' | "season"
| 'today'; | "today";
const { strings } = translator(interaction.locale); const { strings } = translator(interaction.locale);
const { id, region } = await resolvePlayerFromCommand(interaction); 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 clanImage = clan ? emblemURL(clan.emblem_set_id) : undefined;
const ratingInfo = await getRatingInfo(region); const ratingInfo = await getRatingInfo(region);
@@ -67,20 +67,20 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
const leaderboard = await getArchivedRatingLeaderboard( const leaderboard = await getArchivedRatingLeaderboard(
region, 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( 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, [ const accountInfo = await getAccountInfo(region, id, [
'statistics.rating', "statistics.rating",
]); ]);
const statsIndex = leaderboard.version!.value.entries.findIndex( const statsIndex = leaderboard.version!.value.entries.findIndex(
(entry) => entry.id === id, (entry) => entry.id === id
); );
const statsA = leaderboard const statsA = leaderboard
? leaderboard.version!.value.entries[statsIndex!] ? leaderboard.version!.value.entries[statsIndex!]
@@ -90,9 +90,9 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
const statsB2Array = (await getRatingNeighbors(region, id, 0)).neighbors; const statsB2Array = (await getRatingNeighbors(region, id, 0)).neighbors;
if (statsB2Array === undefined) { if (statsB2Array === undefined) {
return literals(strings.bot.commands.rating.errors.no_participation, [ return literals(strings.bot.commands.rating.errors.no_participation, {
markdownEscape(accountInfo.nickname), name: markdownEscape(accountInfo.nickname),
]); });
} }
const [statsB2] = statsB2Array; const [statsB2] = statsB2Array;
@@ -102,15 +102,15 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
return strings.bot.commands.rating.errors.unrecorded_stats; return strings.bot.commands.rating.errors.unrecorded_stats;
} }
if (!statsB1) { if (!statsB1) {
return literals(strings.bot.commands.rating.errors.no_participation, [ return literals(strings.bot.commands.rating.errors.no_participation, {
markdownEscape(accountInfo.nickname), name: markdownEscape(accountInfo.nickname),
]); });
} }
const reward = ratingInfo.rewards.find( const reward = ratingInfo.rewards.find(
(reward) => (reward) =>
statsB2.number >= reward.from_position && statsB2.number >= reward.from_position &&
statsB2.number <= reward.to_position, statsB2.number <= reward.to_position
); );
const positionDelta = leaderboard const positionDelta = leaderboard
? statsB2.number - positionA! ? statsB2.number - positionA!
@@ -118,11 +118,11 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
const delta = deltaBkrlBlitzStats( const delta = deltaBkrlBlitzStats(
statsA, statsA,
accountInfo.statistics.rating!, accountInfo.statistics.rating!,
statsB2, statsB2
); );
const leagueTheme = LEAGUE_ACCENT[league.index]; const leagueTheme = LEAGUE_ACCENT[league.index];
let rewardImage = reward let rewardImage = reward
? reward.type === 'vehicle' ? reward.type === "vehicle"
? reward.vehicle.preview_image_url ? reward.vehicle.preview_image_url
: reward.stuff.image_url : reward.stuff.image_url
: undefined; : undefined;
@@ -131,7 +131,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
const { ok } = await fetch(rewardImage); const { ok } = await fetch(rewardImage);
if (!ok) { if (!ok) {
rewardImage = imgur('kP88vqr'); rewardImage = imgur("kP88vqr");
} }
} }
@@ -147,24 +147,24 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
<div <div
style={{ style={{
position: 'relative', position: "relative",
display: 'flex', display: "flex",
overflow: 'hidden', overflow: "hidden",
}} }}
> >
<div <div
style={{ style={{
position: 'relative', position: "relative",
padding: 16, padding: 16,
backgroundColor: theme.colors[`appBackground2${leagueTheme}`], backgroundColor: theme.colors[`appBackground2${leagueTheme}`],
borderRadius: 4, borderRadius: 4,
border: `1px solid ${ border: `1px solid ${
theme.colors[`componentInteractive${leagueTheme}`] theme.colors[`componentInteractive${leagueTheme}`]
}`, }`,
display: 'flex', display: "flex",
alignItems: 'center', alignItems: "center",
justifyContent: 'space-between', justifyContent: "space-between",
width: '100%', width: "100%",
}} }}
> >
{reward === undefined && ( {reward === undefined && (
@@ -172,17 +172,17 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
endOpacity={3 / 4} endOpacity={3 / 4}
color={theme.colors[`solidBackground${leagueTheme}`]} color={theme.colors[`solidBackground${leagueTheme}`]}
style={{ style={{
transform: 'translateX(-16px) rotate(90deg)', transform: "translateX(-16px) rotate(90deg)",
}} }}
/> />
)} )}
<div <div
style={{ style={{
display: 'flex', display: "flex",
gap: 8, gap: 8,
justifyContent: 'center', justifyContent: "center",
alignItems: 'center', alignItems: "center",
}} }}
> >
<img <img
@@ -190,24 +190,24 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
style={{ style={{
width: 64, width: 64,
height: 64, height: 64,
objectFit: 'contain', objectFit: "contain",
}} }}
src={normalizeLeagueIcon( src={normalizeLeagueIcon(
ratingInfo.leagues[league.index].big_icon, ratingInfo.leagues[league.index].big_icon
)} )}
/> />
<div <div
style={{ style={{
display: 'flex', display: "flex",
flexDirection: 'column', flexDirection: "column",
}} }}
> >
<div <div
style={{ style={{
display: 'flex', display: "flex",
alignItems: 'center', alignItems: "center",
justifyContent: 'flex-start', justifyContent: "flex-start",
}} }}
> >
<span <span
@@ -232,7 +232,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
}} }}
> >
{Math.abs(delta.score).toLocaleString( {Math.abs(delta.score).toLocaleString(
interaction.locale, interaction.locale
)} )}
</span> </span>
</> </>
@@ -241,9 +241,9 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
<div <div
style={{ style={{
display: 'flex', display: "flex",
alignItems: 'center', alignItems: "center",
justifyContent: 'flex-start', justifyContent: "flex-start",
}} }}
> >
<span <span
@@ -267,7 +267,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
}} }}
> >
{Math.abs(positionDelta).toLocaleString( {Math.abs(positionDelta).toLocaleString(
interaction.locale, interaction.locale
)} )}
</span> </span>
</> </>
@@ -279,23 +279,23 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
{reward && ( {reward && (
<div <div
style={{ style={{
display: 'flex', display: "flex",
gap: 8, gap: 8,
justifyContent: 'center', justifyContent: "center",
alignItems: 'center', alignItems: "center",
}} }}
> >
<div <div
style={{ style={{
display: 'flex', display: "flex",
flexDirection: 'column', flexDirection: "column",
}} }}
> >
<div <div
style={{ style={{
display: 'flex', display: "flex",
alignItems: 'center', alignItems: "center",
justifyContent: 'flex-end', justifyContent: "flex-end",
}} }}
> >
<span <span
@@ -303,13 +303,13 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
color: theme.colors[`textHighContrast${leagueTheme}`], color: theme.colors[`textHighContrast${leagueTheme}`],
fontSize: 16, fontSize: 16,
maxWidth: 136, maxWidth: 136,
textAlign: 'right', textAlign: "right",
whiteSpace: 'nowrap', whiteSpace: "nowrap",
textOverflow: 'ellipsis', textOverflow: "ellipsis",
overflow: 'hidden', overflow: "hidden",
}} }}
> >
{reward.type === 'vehicle' {reward.type === "vehicle"
? reward.vehicle.user_string ? reward.vehicle.user_string
: reward.stuff.title} : reward.stuff.title}
</span> </span>
@@ -317,9 +317,9 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
<div <div
style={{ style={{
display: 'flex', display: "flex",
alignItems: 'center', alignItems: "center",
justifyContent: 'flex-end', justifyContent: "flex-end",
}} }}
> >
<span <span
@@ -329,7 +329,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
}} }}
> >
Reward Reward
{reward.type === 'stuff' ? ` x${reward.count}` : ''} {reward.type === "stuff" ? ` x${reward.count}` : ""}
</span> </span>
</div> </div>
</div> </div>
@@ -338,7 +338,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
style={{ style={{
width: 64, width: 64,
height: 64, height: 64,
objectFit: 'cover', objectFit: "cover",
}} }}
src={await iconPng(rewardImage!)} src={await iconPng(rewardImage!)}
/> />
@@ -350,7 +350,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
endOpacity={3 / 4} endOpacity={3 / 4}
color={theme.colors[`solidBackground${leagueTheme}`]} color={theme.colors[`solidBackground${leagueTheme}`]}
style={{ style={{
transform: 'translateX(16px) rotate(-90deg)', transform: "translateX(16px) rotate(-90deg)",
}} }}
/> />
)} )}
@@ -362,10 +362,10 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
direction="reverse" direction="reverse"
color={theme.colors[`solidBackground${leagueTheme}`]} color={theme.colors[`solidBackground${leagueTheme}`]}
style={{ style={{
position: 'absolute', position: "absolute",
top: -40, top: -40,
left: '50%', left: "50%",
transform: 'translateX(-50%)', transform: "translateX(-50%)",
}} }}
/> />
)} )}
@@ -373,32 +373,32 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
<Glow <Glow
color={theme.colors[`solidBackground${leagueTheme}`]} color={theme.colors[`solidBackground${leagueTheme}`]}
style={{ style={{
position: 'absolute', position: "absolute",
bottom: -40, bottom: -40,
left: '50%', left: "50%",
transform: 'translateX(-50%)', transform: "translateX(-50%)",
}} }}
/> />
)} )}
</div> </div>
{delta.battles > 0 && ( {delta.battles > 0 && (
<div style={{ padding: '8px 0', display: 'flex' }}> <div style={{ padding: "8px 0", display: "flex" }}>
<div <div
style={{ style={{
display: 'flex', display: "flex",
alignItems: 'center', alignItems: "center",
justifyContent: 'center', justifyContent: "center",
gap: 4, gap: 4,
flex: 1, flex: 1,
flexDirection: 'column', flexDirection: "column",
}} }}
> >
<span <span
style={{ style={{
fontSize: 24, fontSize: 24,
color: theme.colors.textHighContrast, color: theme.colors.textHighContrast,
fontWeight: 'bold', fontWeight: "bold",
}} }}
> >
{delta.battles.toLocaleString(interaction.locale)} {delta.battles.toLocaleString(interaction.locale)}
@@ -414,19 +414,19 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
</div> </div>
<div <div
style={{ style={{
display: 'flex', display: "flex",
alignItems: 'center', alignItems: "center",
justifyContent: 'center', justifyContent: "center",
gap: 4, gap: 4,
flex: 1, flex: 1,
flexDirection: 'column', flexDirection: "column",
}} }}
> >
<span <span
style={{ style={{
fontSize: 24, fontSize: 24,
color: theme.colors.textHighContrast, color: theme.colors.textHighContrast,
fontWeight: 'bold', fontWeight: "bold",
}} }}
> >
{(100 * (delta.wins / delta.battles)).toFixed(0)}% {(100 * (delta.wins / delta.battles)).toFixed(0)}%
@@ -442,23 +442,23 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
</div> </div>
<div <div
style={{ style={{
display: 'flex', display: "flex",
alignItems: 'center', alignItems: "center",
justifyContent: 'center', justifyContent: "center",
gap: 4, gap: 4,
flex: 1, flex: 1,
flexDirection: 'column', flexDirection: "column",
}} }}
> >
<span <span
style={{ style={{
fontSize: 24, fontSize: 24,
color: theme.colors.textHighContrast, color: theme.colors.textHighContrast,
fontWeight: 'bold', fontWeight: "bold",
}} }}
> >
{Math.round(delta.damage / delta.battles).toLocaleString( {Math.round(delta.damage / delta.battles).toLocaleString(
interaction.locale, interaction.locale
)} )}
</span> </span>
<span <span
@@ -472,19 +472,19 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
</div> </div>
<div <div
style={{ style={{
display: 'flex', display: "flex",
alignItems: 'center', alignItems: "center",
justifyContent: 'center', justifyContent: "center",
gap: 4, gap: 4,
flex: 1, flex: 1,
flexDirection: 'column', flexDirection: "column",
}} }}
> >
<span <span
style={{ style={{
fontSize: 24, fontSize: 24,
color: theme.colors.textHighContrast, color: theme.colors.textHighContrast,
fontWeight: 'bold', fontWeight: "bold",
}} }}
> >
{(100 * (delta.survived / delta.battles)).toFixed(0)}% {(100 * (delta.survived / delta.battles)).toFixed(0)}%
@@ -500,19 +500,19 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
</div> </div>
<div <div
style={{ style={{
display: 'flex', display: "flex",
alignItems: 'center', alignItems: "center",
justifyContent: 'center', justifyContent: "center",
gap: 4, gap: 4,
flex: 1, flex: 1,
flexDirection: 'column', flexDirection: "column",
}} }}
> >
<span <span
style={{ style={{
fontSize: 24, fontSize: 24,
color: theme.colors.textHighContrast, color: theme.colors.textHighContrast,
fontWeight: 'bold', fontWeight: "bold",
}} }}
> >
{(delta.kills / delta.battles).toFixed(2)} {(delta.kills / delta.battles).toFixed(2)}
+82 -83
View File
@@ -10,30 +10,30 @@ import {
imgur, imgur,
isOnGoingRatingSeason, isOnGoingRatingSeason,
LEAGUES, LEAGUES,
RatingInfo, type RatingInfo,
RatingLeaderboard, RatingLeaderboard,
Region, type Region,
} from '@blitzkit/core'; } from "@blitzkit/core";
import { literals } from '@blitzkit/i18n'; import { literals } from "@blitzkit/i18n";
import { import {
APIApplicationCommandOptionChoice, type APIApplicationCommandOptionChoice,
Locale, Locale,
SlashCommandSubcommandBuilder, SlashCommandSubcommandBuilder,
} from 'discord.js'; } from "discord.js";
import { range } from 'lodash-es'; import { range } from "lodash-es";
import markdownEscape from 'markdown-escape'; import markdownEscape from "markdown-escape";
import { CommandWrapper } from '../components/CommandWrapper'; import { CommandWrapper } from "../components/CommandWrapper";
import * as Leaderboard from '../components/Leaderboard'; import * as Leaderboard from "../components/Leaderboard";
import { TitleBar } from '../components/TitleBar'; import { TitleBar } from "../components/TitleBar";
import { UserError } from '../core/blitzkit/userError'; import { UserError } from "../core/blitzkit/userError";
import { addRegionChoices } from '../core/discord/addRegionChoices'; import { addRegionChoices } from "../core/discord/addRegionChoices";
import { addUsernameChoices } from '../core/discord/addUsernameChoices'; import { addUsernameChoices } from "../core/discord/addUsernameChoices";
import { autocompleteUsername } from '../core/discord/autocompleteUsername'; import { autocompleteUsername } from "../core/discord/autocompleteUsername";
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand'; import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
import { localizationObject } from '../core/discord/localizationObject'; import { localizationObject } from "../core/discord/localizationObject";
import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand'; import { resolvePlayerFromCommand } from "../core/discord/resolvePlayerFromCommand";
import { translator } from '../core/localization/translator'; import { translator } from "../core/localization/translator";
import { CommandRegistry } from '../events/interactionCreate'; import type { CommandRegistry } from "../events/interactionCreate";
interface SimplifiedPlayer { interface SimplifiedPlayer {
id: number; id: number;
@@ -51,7 +51,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
const onGoingSeason = await isOnGoingRatingSeason(); const onGoingSeason = await isOnGoingRatingSeason();
const seasonNumbers = range( const seasonNumbers = range(
FIRST_MINIMAL_ARCHIVED_RATING_SEASON, FIRST_MINIMAL_ARCHIVED_RATING_SEASON,
latestArchivedSeasonNumber + (onGoingSeason ? 1 : 0) + 1, latestArchivedSeasonNumber + (onGoingSeason ? 1 : 0) + 1
); );
function addOptions(option: SlashCommandSubcommandBuilder) { function addOptions(option: SlashCommandSubcommandBuilder) {
@@ -66,51 +66,50 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
(strings) => (strings) =>
strings.bot.commands.rating_leaderboard.options.limit.name, strings.bot.commands.rating_leaderboard.options.limit.name,
undefined, undefined,
true, true
), )
) )
.setDescription( .setDescription(
literals( literals(
strings.bot.commands.rating_leaderboard.options.limit strings.bot.commands.rating_leaderboard.options.limit
.description, .description,
[`${DEFAULT_LIMIT}`], { count: DEFAULT_LIMIT }
), )
) )
.setDescriptionLocalizations( .setDescriptionLocalizations(
localizationObject( localizationObject(
(strings) => (strings) =>
strings.bot.commands.rating_leaderboard.options.limit strings.bot.commands.rating_leaderboard.options.limit
.description, .description,
[`${DEFAULT_LIMIT}`], { count: DEFAULT_LIMIT }
), )
) )
.setRequired(false) .setRequired(false)
.setMinValue(5) .setMinValue(5)
.setMaxValue(30), .setMaxValue(30)
) )
.addStringOption((option) => .addStringOption((option) =>
option option
.setName( .setName(
strings.bot.commands.rating_leaderboard.options.season.name, strings.bot.commands.rating_leaderboard.options.season.name
) )
.setNameLocalizations( .setNameLocalizations(
localizationObject( localizationObject(
(strings) => (strings) =>
strings.bot.commands.rating_leaderboard.options.season.name, strings.bot.commands.rating_leaderboard.options.season.name,
undefined, undefined,
true, true
), )
) )
.setDescription( .setDescription(
strings.bot.commands.rating_leaderboard.options.season strings.bot.commands.rating_leaderboard.options.season.description
.description,
) )
.setDescriptionLocalizations( .setDescriptionLocalizations(
localizationObject( localizationObject(
(strings) => (strings) =>
strings.bot.commands.rating_leaderboard.options.season strings.bot.commands.rating_leaderboard.options.season
.description, .description
), )
) )
.setRequired(false) .setRequired(false)
.addChoices( .addChoices(
@@ -124,7 +123,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
? literals( ? literals(
strings.bot.commands.rating_leaderboard.options.season strings.bot.commands.rating_leaderboard.options.season
.choices.current, .choices.current,
[`${number}`], { season: number }
) )
: `${number}`, : `${number}`,
value: `${number}`, value: `${number}`,
@@ -133,27 +132,27 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
(strings) => (strings) =>
strings.bot.commands.rating_leaderboard.options strings.bot.commands.rating_leaderboard.options
.season.choices.current, .season.choices.current,
[`${number}`], { season: number }
) )
: undefined, : undefined,
} satisfies APIApplicationCommandOptionChoice<string>; } satisfies APIApplicationCommandOptionChoice<string>;
}) })
.reverse(), .reverse()
), )
); );
} }
resolve({ resolve({
command: createLocalizedCommand('rating-leaderboard', [ command: createLocalizedCommand("rating-leaderboard", [
{ {
subcommand: 'neighbors', subcommand: "neighbors",
modify(option: SlashCommandSubcommandBuilder) { modify(option: SlashCommandSubcommandBuilder) {
option.addStringOption(addUsernameChoices); option.addStringOption(addUsernameChoices);
addOptions(option); addOptions(option);
}, },
}, },
{ {
subcommand: 'league', subcommand: "league",
modify(option: SlashCommandSubcommandBuilder) { modify(option: SlashCommandSubcommandBuilder) {
option option
.addStringOption((option) => { .addStringOption((option) => {
@@ -161,7 +160,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
return option return option
.setName( .setName(
strings.bot.commands.rating_leaderboard.options.league.name, strings.bot.commands.rating_leaderboard.options.league.name
) )
.setNameLocalizations( .setNameLocalizations(
localizationObject( localizationObject(
@@ -169,19 +168,19 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
strings.bot.commands.rating_leaderboard.options.league strings.bot.commands.rating_leaderboard.options.league
.name, .name,
undefined, undefined,
true, true
), )
) )
.setDescription( .setDescription(
strings.bot.commands.rating_leaderboard.options.league strings.bot.commands.rating_leaderboard.options.league
.description, .description
) )
.setDescriptionLocalizations( .setDescriptionLocalizations(
localizationObject( localizationObject(
(strings) => (strings) =>
strings.bot.commands.rating_leaderboard.options.league strings.bot.commands.rating_leaderboard.options.league
.description, .description
), )
) )
.addChoices( .addChoices(
...LEAGUES.map( ...LEAGUES.map(
@@ -195,10 +194,10 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
(strings) => (strings) =>
( (
strings.common.leagues as Record<string, string> strings.common.leagues as Record<string, string>
)[name], )[name]
), ),
}) satisfies APIApplicationCommandOptionChoice<string>, }) satisfies APIApplicationCommandOptionChoice<string>
), )
) )
.setRequired(true); .setRequired(true);
}) })
@@ -213,10 +212,10 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
async handler(interaction) { async handler(interaction) {
const { strings } = translator(interaction.locale); const { strings } = translator(interaction.locale);
const subcommand = interaction.options.getSubcommand(true) as const subcommand = interaction.options.getSubcommand(true) as
| 'league' | "league"
| 'neighbors'; | "neighbors";
const limit = interaction.options.getInteger('limit') ?? DEFAULT_LIMIT; const limit = interaction.options.getInteger("limit") ?? DEFAULT_LIMIT;
const seasonOption = interaction.options.getString('season'); const seasonOption = interaction.options.getString("season");
const season = seasonOption const season = seasonOption
? parseInt(seasonOption) ? parseInt(seasonOption)
: onGoingSeason : onGoingSeason
@@ -232,11 +231,11 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
let playerId: number | undefined; let playerId: number | undefined;
let regionRatingInfo: RatingInfo | null; let regionRatingInfo: RatingInfo | null;
if (subcommand === 'league') { if (subcommand === "league") {
const leagueIndex = parseInt( 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 regionRatingInfo = await (isCurrentSeason
? getRatingInfo(region) ? getRatingInfo(region)
: null); : null);
@@ -259,8 +258,8 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
position: player.number, position: player.number,
clan: player.clan_tag, clan: player.clan_tag,
nickname: player.nickname, nickname: player.nickname,
}) satisfies SimplifiedPlayer, }) satisfies SimplifiedPlayer
), )
) )
: await getArchivedRatingLeaderboard(region, season!).then( : await getArchivedRatingLeaderboard(region, season!).then(
async (players) => { async (players) => {
@@ -269,17 +268,17 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
? 0 ? 0
: players.version!.value.entries.findIndex( : players.version!.value.entries.findIndex(
(player) => (player) =>
player.score < LEAGUES[leagueIndex - 1].minScore, player.score < LEAGUES[leagueIndex - 1].minScore
); );
const lastPlayerIndex = firstPlayerIndex + limit - 1; const lastPlayerIndex = firstPlayerIndex + limit - 1;
const trimmed = players.version!.value.entries.slice( const trimmed = players.version!.value.entries.slice(
firstPlayerIndex, firstPlayerIndex,
lastPlayerIndex + 1, lastPlayerIndex + 1
); );
const clanData = await getClanAccountInfo( const clanData = await getClanAccountInfo(
region, region,
trimmed.map(({ id }) => id), trimmed.map(({ id }) => id),
['clan'], ["clan"]
); );
return trimmed.map( return trimmed.map(
@@ -294,16 +293,16 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
literals( literals(
strings.bot.commands.rating_leaderboard.body strings.bot.commands.rating_leaderboard.body
.deleted_player, .deleted_player,
[`${player.id}`], { id: player.id }
), ),
}) satisfies SimplifiedPlayer, }) satisfies SimplifiedPlayer
); );
}, }
); );
const leagueInfo = LEAGUES[leagueIndex]; const leagueInfo = LEAGUES[leagueIndex];
archivedLeaderboard = await getArchivedRatingLeaderboard( archivedLeaderboard = await getArchivedRatingLeaderboard(
region, region,
season, season
); );
players = result; players = result;
@@ -316,12 +315,12 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
? (strings.common.leagues as Record<string, string>)[ ? (strings.common.leagues as Record<string, string>)[
LEAGUES[leagueInfo.index].name LEAGUES[leagueInfo.index].name
] ]
: '' : ""
} - ${strings.common.regions.short[region]}`; } - ${strings.common.regions.short[region]}`;
titleImage = imgur(leagueInfo.icon); titleImage = imgur(leagueInfo.icon);
titleDescription = literals( titleDescription = literals(
strings.bot.commands.rating_leaderboard.body.top_players, strings.bot.commands.rating_leaderboard.body.top_players,
[`${limit}`], { count: limit }
); );
} else { } else {
const { region, id } = await resolvePlayerFromCommand(interaction); const { region, id } = await resolvePlayerFromCommand(interaction);
@@ -338,7 +337,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
} }
const accountInfo = await getAccountInfo(region, id); const accountInfo = await getAccountInfo(region, id);
const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan; const clan = (await getClanAccountInfo(region, id, ["clan"]))?.clan;
const neighbors = isCurrentSeason const neighbors = isCurrentSeason
? await getRatingNeighbors(region, id, Math.round(limit / 2)).then( ? await getRatingNeighbors(region, id, Math.round(limit / 2)).then(
(players) => (players) =>
@@ -350,13 +349,13 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
score: player.score, score: player.score,
clan: player.clan_tag, clan: player.clan_tag,
nickname: player.nickname, nickname: player.nickname,
}) satisfies SimplifiedPlayer, }) satisfies SimplifiedPlayer
), )
) )
: await getArchivedRatingLeaderboard(region, season!).then( : await getArchivedRatingLeaderboard(region, season!).then(
async (players) => { async (players) => {
const playerIndex = players.version!.value.entries.findIndex( const playerIndex = players.version!.value.entries.findIndex(
(player) => player.id === id, (player) => player.id === id
); );
if (playerIndex === -1) { if (playerIndex === -1) {
@@ -364,20 +363,20 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
literals( literals(
strings.bot.commands.rating_leaderboard.body strings.bot.commands.rating_leaderboard.body
.no_participation, .no_participation,
[markdownEscape(accountInfo.nickname), `${season}`], { player: markdownEscape(accountInfo.nickname), season }
), )
); );
} }
const halfRange = Math.round(limit / 2); const halfRange = Math.round(limit / 2);
const trimmed = players.version!.value.entries.slice( const trimmed = players.version!.value.entries.slice(
playerIndex - halfRange, playerIndex - halfRange,
playerIndex + halfRange + 1, playerIndex + halfRange + 1
); );
const clanData = await getClanAccountInfo( const clanData = await getClanAccountInfo(
region, region,
trimmed.map((player) => player.id), trimmed.map((player) => player.id),
['clan'], ["clan"]
); );
return trimmed.map((player, index) => ({ return trimmed.map((player, index) => ({
@@ -389,18 +388,18 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
? clanData[index]!.account_name ? clanData[index]!.account_name
: `Deleted ${player.id}`, : `Deleted ${player.id}`,
})); }));
}, }
); );
archivedLeaderboard = await getArchivedRatingLeaderboard( archivedLeaderboard = await getArchivedRatingLeaderboard(
region, region,
season, season
); );
players = neighbors; players = neighbors;
titleImage = clan titleImage = clan
? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clan.emblem_set_id}.png` ? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clan.emblem_set_id}.png`
: undefined; : undefined;
titleName = neighbors ? accountInfo.nickname : ''; titleName = neighbors ? accountInfo.nickname : "";
titleDescription = titleDescription =
strings.bot.commands.rating_leaderboard.body.neighbors; strings.bot.commands.rating_leaderboard.body.neighbors;
playersAfter = playersAfter =
@@ -414,7 +413,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
const items = players.map((player) => { const items = players.map((player) => {
const midnightIndex = const midnightIndex =
archivedLeaderboard?.version!.value.entries.findIndex( archivedLeaderboard?.version!.value.entries.findIndex(
(item) => item.id === player.id, (item) => item.id === player.id
); );
const midnightScore = const midnightScore =
midnightIndex === undefined midnightIndex === undefined
@@ -453,7 +452,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
<Leaderboard.Gap <Leaderboard.Gap
message={literals( message={literals(
strings.bot.commands.rating_leaderboard.body.more, 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, autocomplete: autocompleteUsername,
}); });
}, }
); );
+1 -1
View File
@@ -20,7 +20,7 @@ import { blitzStarsTankAverages } from '../core/blitzstars/tankAverages';
import { buttonLink } from '../core/discord/buttonLink'; import { buttonLink } from '../core/discord/buttonLink';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand'; import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
import { translator } from '../core/localization/translator'; import { translator } from '../core/localization/translator';
import { CommandRegistry } from '../events/interactionCreate'; import { type CommandRegistry } from '../events/interactionCreate';
import { theme } from '../stitches.config'; import { theme } from '../stitches.config';
type WotInspectorReplaySubmission = type WotInspectorReplaySubmission =
+136 -137
View File
@@ -8,31 +8,31 @@ import {
TANK_ICONS, TANK_ICONS,
TankType, TankType,
TIER_ROMAN_NUMERALS, TIER_ROMAN_NUMERALS,
} from '@blitzkit/core'; } from "@blitzkit/core";
import { literals } from '@blitzkit/i18n'; import { literals } from "@blitzkit/i18n";
import { escapeMarkdown, Locale } from 'discord.js'; import { escapeMarkdown, Locale } from "discord.js";
import { CommandWrapper } from '../components/CommandWrapper'; import { CommandWrapper } from "../components/CommandWrapper";
import { TitleBar } from '../components/TitleBar'; import { TitleBar } from "../components/TitleBar";
import { resolveTankId } from '../core/blitz/resolveTankId'; import { resolveTankId } from "../core/blitz/resolveTankId";
import { buildTechTreeLine } from '../core/blitzkit/buildTechTreeLine'; import { buildTechTreeLine } from "../core/blitzkit/buildTechTreeLine";
import { iconPng } from '../core/blitzkit/iconPng'; import { iconPng } from "../core/blitzkit/iconPng";
import { tankDefinitions } from '../core/blitzkit/nonBlockingPromises'; import { tankDefinitions } from "../core/blitzkit/nonBlockingPromises";
import { resolveAncestry } from '../core/blitzkit/resolveAncestry'; import { resolveAncestry } from "../core/blitzkit/resolveAncestry";
import { tankIconPng } from '../core/blitzkit/tankIconPng'; import { tankIconPng } from "../core/blitzkit/tankIconPng";
import { addUsernameChoices } from '../core/discord/addUsernameChoices'; import { addUsernameChoices } from "../core/discord/addUsernameChoices";
import { autocompleteTanks } from '../core/discord/autocompleteTanks'; import { autocompleteTanks } from "../core/discord/autocompleteTanks";
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand'; import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
import { localizationObject } from '../core/discord/localizationObject'; import { localizationObject } from "../core/discord/localizationObject";
import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand'; import { resolvePlayerFromCommand } from "../core/discord/resolvePlayerFromCommand";
import { translator } from '../core/localization/translator'; import { translator } from "../core/localization/translator";
import { CommandRegistry } from '../events/interactionCreate'; import { type CommandRegistry } from "../events/interactionCreate";
import { theme } from '../stitches.config'; import { theme } from "../stitches.config";
export const researchCommand = new Promise<CommandRegistry>((resolve) => { export const researchCommand = new Promise<CommandRegistry>((resolve) => {
const { strings } = translator(Locale.EnglishUS); const { strings } = translator(Locale.EnglishUS);
resolve({ resolve({
command: createLocalizedCommand('research') command: createLocalizedCommand("research")
.addStringOption((option) => .addStringOption((option) =>
option option
.setName(strings.bot.commands.research.options.target_tank.name) .setName(strings.bot.commands.research.options.target_tank.name)
@@ -41,20 +41,20 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
(strings) => (strings) =>
strings.bot.commands.research.options.target_tank.name, strings.bot.commands.research.options.target_tank.name,
undefined, undefined,
true, true
), )
) )
.setDescription( .setDescription(
strings.bot.commands.research.options.target_tank.description, strings.bot.commands.research.options.target_tank.description
) )
.setDescriptionLocalizations( .setDescriptionLocalizations(
localizationObject( localizationObject(
(strings) => (strings) =>
strings.bot.commands.research.options.target_tank.description, strings.bot.commands.research.options.target_tank.description
), )
) )
.setAutocomplete(true) .setAutocomplete(true)
.setRequired(true), .setRequired(true)
) )
.addStringOption((option) => .addStringOption((option) =>
option option
@@ -64,20 +64,20 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
(strings) => (strings) =>
strings.bot.commands.research.options.starting_tank.name, strings.bot.commands.research.options.starting_tank.name,
undefined, undefined,
true, true
), )
) )
.setDescription( .setDescription(
strings.bot.commands.research.options.starting_tank.description, strings.bot.commands.research.options.starting_tank.description
) )
.setDescriptionLocalizations( .setDescriptionLocalizations(
localizationObject( localizationObject(
(strings) => (strings) =>
strings.bot.commands.research.options.starting_tank.description, strings.bot.commands.research.options.starting_tank.description
), )
) )
.setAutocomplete(true) .setAutocomplete(true)
.setRequired(false), .setRequired(false)
) )
.addStringOption(addUsernameChoices), .addStringOption(addUsernameChoices),
@@ -85,11 +85,11 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
const { strings, unwrap } = translator(interaction.locale); const { strings, unwrap } = translator(interaction.locale);
const awaitedTankDefinitions = await tankDefinitions; const awaitedTankDefinitions = await tankDefinitions;
const targetTankId = await resolveTankId( const targetTankId = await resolveTankId(
interaction.options.getString('target-tank', true), interaction.options.getString("target-tank", true),
interaction.locale, interaction.locale,
true, true
); );
const startingTankRaw = interaction.options.getString('starting-tank'); const startingTankRaw = interaction.options.getString("starting-tank");
const targetTank = awaitedTankDefinitions.tanks[targetTankId]; const targetTank = awaitedTankDefinitions.tanks[targetTankId];
const { id, region } = await resolvePlayerFromCommand(interaction); const { id, region } = await resolvePlayerFromCommand(interaction);
let startingTankId: number; let startingTankId: number;
@@ -99,46 +99,45 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
startingTankId = await resolveTankId( startingTankId = await resolveTankId(
startingTankRaw, startingTankRaw,
interaction.locale, interaction.locale,
true, true
); );
if (targetTankId === startingTankId) { if (targetTankId === startingTankId) {
return literals( return literals(
strings.bot.commands.research.errors.start_end_equal, strings.bot.commands.research.errors.start_end_equal,
[escapeMarkdown(unwrap(targetTank.name))], { tank: escapeMarkdown(unwrap(targetTank.name)) }
); );
} }
const startingTank = awaitedTankDefinitions.tanks[startingTankId]; const startingTank = awaitedTankDefinitions.tanks[startingTankId];
if (startingTank.tier > targetTank.tier) { if (startingTank.tier > targetTank.tier) {
return literals(strings.bot.commands.research.errors.unordered, [ return literals(strings.bot.commands.research.errors.unordered, {
escapeMarkdown(unwrap(startingTank.name)), tank_1: escapeMarkdown(unwrap(startingTank.name)),
escapeMarkdown(unwrap(targetTank.name)), tank_2: escapeMarkdown(unwrap(targetTank.name)),
escapeMarkdown(unwrap(targetTank.name)), });
]);
} }
if (!targetTankAncestry.includes(startingTankId)) { if (!targetTankAncestry.includes(startingTankId)) {
return literals( return literals(
strings.bot.commands.research.errors.tanks_not_on_line, strings.bot.commands.research.errors.tanks_not_on_line,
[ {
escapeMarkdown(unwrap(targetTank.name)), tank_1: escapeMarkdown(unwrap(targetTank.name)),
escapeMarkdown(unwrap(startingTank.name)), tank_2: escapeMarkdown(unwrap(startingTank.name)),
], }
); );
} }
} else { } else {
if (targetTank.type !== TankType.RESEARCHABLE) { if (targetTank.type !== TankType.RESEARCHABLE) {
return literals(strings.bot.commands.research.errors.non_tech_tree, [ return literals(strings.bot.commands.research.errors.non_tech_tree, {
escapeMarkdown(unwrap(targetTank.name)), tank: escapeMarkdown(unwrap(targetTank.name)),
]); });
} }
if (targetTank.tier === 1) { if (targetTank.tier === 1) {
return literals(strings.bot.commands.research.errors.tier_1, [ return literals(strings.bot.commands.research.errors.tier_1, {
escapeMarkdown(unwrap(targetTank.name)), tank: escapeMarkdown(unwrap(targetTank.name)),
]); });
} }
const tankStats = await getTankStats(region, id); 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)) { if (tankStats.some(({ tank_id }) => tank_id === targetTankId)) {
return literals( return literals(
strings.bot.commands.research.errors.already_researched, strings.bot.commands.research.errors.already_researched,
[escapeMarkdown(unwrap(targetTank.name))], { tank: escapeMarkdown(unwrap(targetTank.name)) }
); );
} }
const foundAncestor = targetTankAncestry.find( const foundAncestor = targetTankAncestry.find(
(id) => (id) =>
awaitedTankDefinitions.tanks[id].tier === 1 || awaitedTankDefinitions.tanks[id].tier === 1 ||
tankStats.some(({ tank_id }) => tank_id === id), tankStats.some(({ tank_id }) => tank_id === id)
); );
if (foundAncestor === undefined) { if (foundAncestor === undefined) {
throw new Error('No first owned tech tree found.'); throw new Error("No first owned tech tree found.");
} }
startingTankId = foundAncestor; startingTankId = foundAncestor;
@@ -169,12 +168,12 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
const techTreeLine = await buildTechTreeLine( const techTreeLine = await buildTechTreeLine(
startingTankId, startingTankId,
targetTankId, targetTankId
); );
const line = [...techTreeLine, startingTankId].reverse(); const line = [...techTreeLine, startingTankId].reverse();
const { nickname } = await getAccountInfo(region, id); 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 clanImage = clan ? emblemURL(clan.emblem_set_id) : undefined;
const costs = await Promise.all( const costs = await Promise.all(
line.map(async (id) => { line.map(async (id) => {
@@ -187,18 +186,18 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
tank.turrets.forEach((turret) => { tank.turrets.forEach((turret) => {
if ( if (
turret.research_cost !== undefined && turret.research_cost !== undefined &&
typeof turret.research_cost.research_cost_type!.value === 'number' typeof turret.research_cost.research_cost_type!.value === "number"
) { ) {
turretXps.set( turretXps.set(
turret.id, turret.id,
turret.research_cost.research_cost_type!.value, turret.research_cost.research_cost_type!.value
); );
} }
turret.guns.forEach((gun) => { turret.guns.forEach((gun) => {
if ( if (
gun.research_cost !== undefined && 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); 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) => { tank.engines.forEach((engine) => {
if ( if (
engine.research_cost !== undefined && engine.research_cost !== undefined &&
typeof engine.research_cost.research_cost_type!.value === 'number' typeof engine.research_cost.research_cost_type!.value === "number"
) { ) {
engineXps.set( engineXps.set(
engine.id, 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) => { tank.tracks.forEach((track) => {
if ( if (
track.research_cost !== undefined && track.research_cost !== undefined &&
typeof track.research_cost.research_cost_type!.value === 'number' typeof track.research_cost.research_cost_type!.value === "number"
) { ) {
trackXps.set( trackXps.set(
track.id, 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, purchase: tank.price.value,
equipment: equipmentPriceMatrix[tank.tier].reduce( equipment: equipmentPriceMatrix[tank.tier].reduce(
(a, b) => a + 3 * b, (a, b) => a + 3 * b,
0, 0
), ),
}; };
}), })
); );
const image = ( const image = (
@@ -267,14 +266,14 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
<div <div
style={{ style={{
display: 'flex', display: "flex",
flexDirection: 'column', flexDirection: "column",
gap: 8, gap: 8,
}} }}
> >
<div <div
style={{ style={{
display: 'flex', display: "flex",
borderRadius: 4, borderRadius: 4,
backgroundColor: theme.colors.appBackground2, backgroundColor: theme.colors.appBackground2,
border: theme.borderStyles.subtle, border: theme.borderStyles.subtle,
@@ -285,10 +284,10 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
<div <div
style={{ style={{
flex: 1, flex: 1,
display: 'flex', display: "flex",
alignItems: 'center', alignItems: "center",
justifyContent: 'center', justifyContent: "center",
position: 'relative', position: "relative",
right: 24, right: 24,
}} }}
> >
@@ -304,23 +303,23 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
<div <div
style={{ style={{
flex: 1, flex: 1,
display: 'flex', display: "flex",
flexDirection: 'column', flexDirection: "column",
justifyContent: 'center', justifyContent: "center",
gap: 8, gap: 8,
padding: '16px 0', padding: "16px 0",
}} }}
> >
<div <div
style={{ style={{
display: 'flex', display: "flex",
gap: 4, gap: 4,
alignItems: 'center', alignItems: "center",
}} }}
> >
<img <img
alt="XP icon" alt="XP icon"
src={await iconPng(asset('icons/currencies/xp.webp'))} src={await iconPng(asset("icons/currencies/xp.webp"))}
width={16} width={16}
height={16} height={16}
/> />
@@ -330,7 +329,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
color: theme.colors.textHighContrast, color: theme.colors.textHighContrast,
}} }}
> >
Experience:{' '} Experience:{" "}
{costs {costs
.reduce((a, b) => a + (b.research ?? 0) + b.upgrades, 0) .reduce((a, b) => a + (b.research ?? 0) + b.upgrades, 0)
.toLocaleString(interaction.locale)} .toLocaleString(interaction.locale)}
@@ -339,14 +338,14 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
<div <div
style={{ style={{
display: 'flex', display: "flex",
gap: 4, gap: 4,
alignItems: 'center', alignItems: "center",
}} }}
> >
<img <img
alt="Silver icon" alt="Silver icon"
src={await iconPng(asset('icons/currencies/silver.webp'))} src={await iconPng(asset("icons/currencies/silver.webp"))}
width={16} width={16}
height={16} height={16}
/> />
@@ -356,7 +355,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
color: theme.colors.textHighContrast, color: theme.colors.textHighContrast,
}} }}
> >
Purchase:{' '} Purchase:{" "}
{costs {costs
.reduce((a, b) => a + b.purchase, 0) .reduce((a, b) => a + b.purchase, 0)
.toLocaleString(interaction.locale)} .toLocaleString(interaction.locale)}
@@ -365,14 +364,14 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
<div <div
style={{ style={{
display: 'flex', display: "flex",
gap: 4, gap: 4,
alignItems: 'center', alignItems: "center",
}} }}
> >
<img <img
alt="Silver icon" alt="Silver icon"
src={await iconPng(asset('icons/currencies/silver.webp'))} src={await iconPng(asset("icons/currencies/silver.webp"))}
width={16} width={16}
height={16} height={16}
/> />
@@ -382,7 +381,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
color: theme.colors.textHighContrast, color: theme.colors.textHighContrast,
}} }}
> >
Equipment:{' '} Equipment:{" "}
{costs {costs
.reduce((a, b) => a + b.equipment, 0) .reduce((a, b) => a + b.equipment, 0)
.toLocaleString(interaction.locale)} .toLocaleString(interaction.locale)}
@@ -399,7 +398,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
<div <div
key={id} key={id}
style={{ style={{
display: 'flex', display: "flex",
borderRadius: 4, borderRadius: 4,
backgroundColor: theme.colors.appBackground2, backgroundColor: theme.colors.appBackground2,
border: theme.borderStyles.subtle, border: theme.borderStyles.subtle,
@@ -408,9 +407,9 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
<div <div
style={{ style={{
width: 48, width: 48,
display: 'flex', display: "flex",
alignItems: 'center', alignItems: "center",
justifyContent: 'center', justifyContent: "center",
}} }}
> >
<span <span
@@ -426,9 +425,9 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
<div <div
style={{ style={{
flex: 1, flex: 1,
display: 'flex', display: "flex",
position: 'relative', position: "relative",
right: '5%', right: "5%",
}} }}
> >
<img <img
@@ -437,23 +436,23 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
style={{ style={{
height: 100, height: 100,
width: 100, width: 100,
objectFit: 'contain', objectFit: "contain",
position: 'absolute', position: "absolute",
left: '25%', left: "25%",
top: 0, top: 0,
}} }}
/> />
<div <div
style={{ style={{
display: 'flex', display: "flex",
gap: 4, gap: 4,
alignItems: 'center', alignItems: "center",
justifyContent: 'center', justifyContent: "center",
position: 'absolute', position: "absolute",
bottom: 8, bottom: 8,
left: '50%', left: "50%",
transform: 'translateX(-50%)', transform: "translateX(-50%)",
}} }}
> >
<img <img
@@ -476,23 +475,23 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
<div <div
style={{ style={{
flex: 1, flex: 1,
display: 'flex', display: "flex",
flexDirection: 'column', flexDirection: "column",
justifyContent: 'center', justifyContent: "center",
gap: 8, gap: 8,
padding: '16px 0', padding: "16px 0",
}} }}
> >
<div <div
style={{ style={{
display: 'flex', display: "flex",
gap: 4, gap: 4,
alignItems: 'center', alignItems: "center",
}} }}
> >
<img <img
alt="XP icon" alt="XP icon"
src={await iconPng(asset('icons/currencies/xp.webp'))} src={await iconPng(asset("icons/currencies/xp.webp"))}
width={16} width={16}
height={16} height={16}
/> />
@@ -504,11 +503,11 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
> >
{literals( {literals(
strings.bot.commands.research.body.research, strings.bot.commands.research.body.research,
[ {
`${costs[index].research?.toLocaleString( cost: costs[index].research?.toLocaleString(
interaction.locale, interaction.locale
)}`, ),
], }
)} )}
</span> </span>
</div> </div>
@@ -516,15 +515,15 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
{costs[index].upgrades > 0 && ( {costs[index].upgrades > 0 && (
<div <div
style={{ style={{
display: 'flex', display: "flex",
gap: 4, gap: 4,
alignItems: 'center', alignItems: "center",
}} }}
> >
<img <img
alt="Free XP icon" alt="Free XP icon"
src={await iconPng( src={await iconPng(
asset('icons/currencies/free-xp.webp'), asset("icons/currencies/free-xp.webp")
)} )}
width={16} width={16}
height={16} height={16}
@@ -537,7 +536,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
> >
{literals( {literals(
strings.bot.commands.research.body.upgrades, strings.bot.commands.research.body.upgrades,
[costs[index].upgrades.toLocaleString()], { cost: costs[index].upgrades.toLocaleString() }
)} )}
</span> </span>
</div> </div>
@@ -545,15 +544,15 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
<div <div
style={{ style={{
display: 'flex', display: "flex",
gap: 4, gap: 4,
alignItems: 'center', alignItems: "center",
}} }}
> >
<img <img
alt="Silver icon" alt="Silver icon"
src={await iconPng( src={await iconPng(
asset('icons/currencies/silver.webp'), asset("icons/currencies/silver.webp")
)} )}
width={16} width={16}
height={16} height={16}
@@ -566,26 +565,26 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
> >
{literals( {literals(
strings.bot.commands.research.body.purchase, strings.bot.commands.research.body.purchase,
[ {
costs[index].purchase.toLocaleString( cost: costs[index].purchase.toLocaleString(
interaction.locale, interaction.locale
), ),
], }
)} )}
</span> </span>
</div> </div>
<div <div
style={{ style={{
display: 'flex', display: "flex",
gap: 4, gap: 4,
alignItems: 'center', alignItems: "center",
}} }}
> >
<img <img
alt="Silver icon" alt="Silver icon"
src={await iconPng( src={await iconPng(
asset('icons/currencies/silver.webp'), asset("icons/currencies/silver.webp")
)} )}
width={16} width={16}
height={16} height={16}
@@ -598,18 +597,18 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
> >
{literals( {literals(
strings.bot.commands.research.body.equipment, strings.bot.commands.research.body.equipment,
[ {
costs[index].equipment.toLocaleString( cost: costs[index].equipment.toLocaleString(
interaction.locale, interaction.locale
), ),
], }
)} )}
</span> </span>
</div> </div>
</div> </div>
</div> </div>
); );
}), })
)} )}
</div> </div>
</CommandWrapper> </CommandWrapper>
@@ -621,7 +620,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
}, },
autocomplete(interaction) { 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 { type ClanList, fetchBlitz, type Region } from "@blitzkit/core";
import { literals } from '@blitzkit/i18n'; import { literals } from "@blitzkit/i18n";
import { Locale } from 'discord.js'; import { Locale } from "discord.js";
import markdownEscape from 'markdown-escape'; import markdownEscape from "markdown-escape";
import { addClanChoices } from '../core/discord/addClanChoices'; import { addClanChoices } from "../core/discord/addClanChoices";
import { addRegionChoices } from '../core/discord/addRegionChoices'; import { addRegionChoices } from "../core/discord/addRegionChoices";
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand'; import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
import { embedInfo } from '../core/discord/embedInfo'; import { embedInfo } from "../core/discord/embedInfo";
import { localizationObject } from '../core/discord/localizationObject'; import { localizationObject } from "../core/discord/localizationObject";
import { translator } from '../core/localization/translator'; import { translator } from "../core/localization/translator";
import { CommandRegistry } from '../events/interactionCreate'; import type { CommandRegistry } from "../events/interactionCreate";
const DEFAULT_LIMIT = 25; const DEFAULT_LIMIT = 25;
export const searchClansCommand = new Promise<CommandRegistry>((resolve) => { export const searchClansCommand = new Promise<CommandRegistry>((resolve) => {
resolve({ resolve({
command: createLocalizedCommand('search-clans') command: createLocalizedCommand("search-clans")
.addStringOption(addRegionChoices) .addStringOption(addRegionChoices)
.addStringOption((option) => .addStringOption((option) =>
addClanChoices(option).setAutocomplete(false), addClanChoices(option).setAutocomplete(false)
) )
.addIntegerOption((option) => { .addIntegerOption((option) => {
const { strings } = translator(Locale.EnglishUS); const { strings } = translator(Locale.EnglishUS);
@@ -28,21 +28,21 @@ export const searchClansCommand = new Promise<CommandRegistry>((resolve) => {
localizationObject( localizationObject(
(strings) => strings.bot.commands.search_clans.options.limit.name, (strings) => strings.bot.commands.search_clans.options.limit.name,
undefined, undefined,
true, true
), )
) )
.setDescription( .setDescription(
literals( literals(
strings.bot.commands.search_clans.options.limit.description, strings.bot.commands.search_clans.options.limit.description,
[`${DEFAULT_LIMIT}`], { limit: DEFAULT_LIMIT }
), )
) )
.setDescriptionLocalizations( .setDescriptionLocalizations(
localizationObject( localizationObject(
(strings) => (strings) =>
strings.bot.commands.search_clans.options.limit.description, strings.bot.commands.search_clans.options.limit.description,
[`${DEFAULT_LIMIT}`], { limit: DEFAULT_LIMIT }
), )
) )
.setMinValue(1) .setMinValue(1)
.setMaxValue(100); .setMaxValue(100);
@@ -50,24 +50,24 @@ export const searchClansCommand = new Promise<CommandRegistry>((resolve) => {
async handler(interaction) { async handler(interaction) {
const { strings } = translator(interaction.locale); const { strings } = translator(interaction.locale);
const region = interaction.options.getString('region') as Region; const region = interaction.options.getString("region") as Region;
const clan = interaction.options.getString('clan')!; const clan = interaction.options.getString("clan")!;
const limit = interaction.options.getInteger('limit') ?? DEFAULT_LIMIT; const limit = interaction.options.getInteger("limit") ?? DEFAULT_LIMIT;
const clanList = await fetchBlitz<ClanList>(region, 'clans/list', { const clanList = await fetchBlitz<ClanList>(region, "clans/list", {
search: clan, search: clan,
limit, limit,
}); });
return embedInfo( return embedInfo(
literals(strings.bot.commands.search_clans.body.title, [ literals(strings.bot.commands.search_clans.body.title, {
markdownEscape(clan), search: markdownEscape(clan),
strings.common.regions.normal[region], region: strings.common.regions.normal[region],
]), }),
clanList.length === 0 clanList.length === 0
? strings.bot.commands.search_clans.body.no_results ? strings.bot.commands.search_clans.body.no_results
: `\`\`\`\n${clanList : `\`\`\`\n${clanList
.map((clan) => `${clan.name} [${clan.tag}]`) .map((clan) => `${clan.name} [${clan.tag}]`)
.join('\n')}\n\`\`\``, .join("\n")}\n\`\`\``
); );
}, },
}); });
+32 -32
View File
@@ -1,19 +1,19 @@
import { import {
AccountList, type AccountList,
fetchBlitz, fetchBlitz,
Region, type Region,
usernamePattern, usernamePattern,
} from '@blitzkit/core'; } from "@blitzkit/core";
import { literals } from '@blitzkit/i18n'; import { literals } from "@blitzkit/i18n";
import { Locale } from 'discord.js'; import { Locale } from "discord.js";
import markdownEscape from 'markdown-escape'; import markdownEscape from "markdown-escape";
import { addRegionChoices } from '../core/discord/addRegionChoices'; import { addRegionChoices } from "../core/discord/addRegionChoices";
import { addUsernameChoices } from '../core/discord/addUsernameChoices'; import { addUsernameChoices } from "../core/discord/addUsernameChoices";
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand'; import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
import { embedInfo } from '../core/discord/embedInfo'; import { embedInfo } from "../core/discord/embedInfo";
import { localizationObject } from '../core/discord/localizationObject'; import { localizationObject } from "../core/discord/localizationObject";
import { translator } from '../core/localization/translator'; import { translator } from "../core/localization/translator";
import { CommandRegistry } from '../events/interactionCreate'; import type { CommandRegistry } from "../events/interactionCreate";
const DEFAULT_LIMIT = 25; const DEFAULT_LIMIT = 25;
@@ -21,10 +21,10 @@ export const searchPlayersCommand = new Promise<CommandRegistry>((resolve) => {
const { strings } = translator(Locale.EnglishUS); const { strings } = translator(Locale.EnglishUS);
resolve({ resolve({
command: createLocalizedCommand('search-players') command: createLocalizedCommand("search-players")
.addStringOption(addRegionChoices) .addStringOption(addRegionChoices)
.addStringOption((option) => .addStringOption((option) =>
addUsernameChoices(option).setAutocomplete(false).setRequired(true), addUsernameChoices(option).setAutocomplete(false).setRequired(true)
) )
.addIntegerOption((option) => .addIntegerOption((option) =>
option option
@@ -34,49 +34,49 @@ export const searchPlayersCommand = new Promise<CommandRegistry>((resolve) => {
(strings) => (strings) =>
strings.bot.commands.search_players.options.limit.name, strings.bot.commands.search_players.options.limit.name,
undefined, undefined,
true, true
), )
) )
.setDescription( .setDescription(
literals( literals(
strings.bot.commands.search_players.options.limit.description, strings.bot.commands.search_players.options.limit.description,
[`${DEFAULT_LIMIT}`], { limit: DEFAULT_LIMIT }
), )
) )
.setDescriptionLocalizations( .setDescriptionLocalizations(
localizationObject( localizationObject(
(strings) => (strings) =>
strings.bot.commands.search_players.options.limit.description, strings.bot.commands.search_players.options.limit.description,
[`${DEFAULT_LIMIT}`], { limit: DEFAULT_LIMIT }
), )
) )
.setMinValue(1) .setMinValue(1)
.setMaxValue(100), .setMaxValue(100)
), ),
async handler(interaction) { async handler(interaction) {
const { strings } = translator(interaction.locale); const { strings } = translator(interaction.locale);
const region = interaction.options.getString('region') as Region; const region = interaction.options.getString("region") as Region;
const name = interaction.options.getString('username')!; const name = interaction.options.getString("username")!;
const limit = interaction.options.getInteger('limit') ?? 25; const limit = interaction.options.getInteger("limit") ?? 25;
const trimmedSearch = name.trim(); const trimmedSearch = name.trim();
const players = usernamePattern.test(trimmedSearch) const players = usernamePattern.test(trimmedSearch)
? await fetchBlitz<AccountList>(region, 'account/list', { ? await fetchBlitz<AccountList>(region, "account/list", {
search: trimmedSearch, search: trimmedSearch,
limit, limit,
}) })
: []; : [];
return embedInfo( return embedInfo(
literals(strings.bot.commands.search_players.body.title, [ literals(strings.bot.commands.search_players.body.title, {
markdownEscape(trimmedSearch), search: markdownEscape(trimmedSearch),
strings.common.regions.normal[region], region: strings.common.regions.normal[region],
]), }),
`\`\`\`${ `\`\`\`${
players.length === 0 players.length === 0
? strings.bot.commands.search_players.body.no_results ? 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 { emblemURL, getAccountInfo, getClanAccountInfo } from "@blitzkit/core";
import { Locale } from 'discord.js'; import { Locale } from "discord.js";
import { AllStatsOverview } from '../components/AllStatsOverview'; import { AllStatsOverview } from "../components/AllStatsOverview";
import { CommandWrapper } from '../components/CommandWrapper'; import { CommandWrapper } from "../components/CommandWrapper";
import { NoData } from '../components/NoData'; import { NoData } from "../components/NoData";
import { TitleBar } from '../components/TitleBar'; import { TitleBar } from "../components/TitleBar";
import { filtersToDescription } from '../core/blitzkit/filtersToDescription'; 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 { getBlitzStarsLinkButton } from "../core/blitzstars/getBlitzStarsLinkButton";
import { getStatsInPeriod } from '../core/blitzstars/getStatsInPeriod'; import { getStatsInPeriod } from "../core/blitzstars/getStatsInPeriod";
import { addPeriodicFilterOptions } from '../core/discord/addPeriodicFilterOptions'; import { addPeriodicFilterOptions } from "../core/discord/addPeriodicFilterOptions";
import { addUsernameChoices } from '../core/discord/addUsernameChoices'; import { addUsernameChoices } from "../core/discord/addUsernameChoices";
import { autocompleteTanks } from '../core/discord/autocompleteTanks'; import { autocompleteTanks } from "../core/discord/autocompleteTanks";
import { autocompleteUsername } from '../core/discord/autocompleteUsername'; import { autocompleteUsername } from "../core/discord/autocompleteUsername";
import { buttonRefresh } from '../core/discord/buttonRefresh'; import { buttonRefresh } from "../core/discord/buttonRefresh";
import { commandToURL } from '../core/discord/commandToURL'; import { commandToURL } from "../core/discord/commandToURL";
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand'; import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
import { getCustomPeriodParams } from '../core/discord/getCustomPeriodParams'; import { getCustomPeriodParams } from "../core/discord/getCustomPeriodParams";
import { getFiltersFromButton } from '../core/discord/getFiltersFromButton'; import { getFiltersFromButton } from "../core/discord/getFiltersFromButton";
import { getFiltersFromCommand } from '../core/discord/getFiltersFromCommand'; import { getFiltersFromCommand } from "../core/discord/getFiltersFromCommand";
import { resolvePeriodFromButton } from '../core/discord/resolvePeriodFromButton'; import { resolvePeriodFromButton } from "../core/discord/resolvePeriodFromButton";
import { import {
ResolvedPeriod, type ResolvedPeriod,
resolvePeriodFromCommand, resolvePeriodFromCommand,
} from '../core/discord/resolvePeriodFromCommand'; } from "../core/discord/resolvePeriodFromCommand";
import { resolvePlayerFromButton } from '../core/discord/resolvePlayerFromButton'; import { resolvePlayerFromButton } from "../core/discord/resolvePlayerFromButton";
import { import {
ResolvedPlayer, type ResolvedPlayer,
resolvePlayerFromCommand, resolvePlayerFromCommand,
} from '../core/discord/resolvePlayerFromCommand'; } from "../core/discord/resolvePlayerFromCommand";
import { CommandRegistry } from '../events/interactionCreate'; import type { CommandRegistry } from "../events/interactionCreate";
async function render( async function render(
{ region, id }: ResolvedPlayer, { region, id }: ResolvedPlayer,
{ start, end, name }: ResolvedPeriod, { start, end, name }: ResolvedPeriod,
filters: StatFilters, filters: StatFilters,
locale: Locale, locale: Locale
) { ) {
const { nickname } = await getAccountInfo(region, id); 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 clanImage = clan ? emblemURL(clan.emblem_set_id) : undefined;
const diffedTankStats = await getStatsInPeriod( const diffedTankStats = await getStatsInPeriod(
region, region,
id, id,
start, start,
end, end,
locale, locale
); );
const { stats, supplementary } = await filterStats(diffedTankStats, filters); const { stats, supplementary } = await filterStats(diffedTankStats, filters);
const filterDescriptions = await filtersToDescription(filters, locale); const filterDescriptions = await filtersToDescription(filters, locale);
@@ -72,8 +72,8 @@ async function render(
export const statsCommand = new Promise<CommandRegistry>(async (resolve) => { export const statsCommand = new Promise<CommandRegistry>(async (resolve) => {
resolve({ resolve({
command: await addPeriodicFilterOptions( command: await addPeriodicFilterOptions(
createLocalizedCommand('stats'), createLocalizedCommand("stats"),
(option) => option.addStringOption(addUsernameChoices), (option) => option.addStringOption(addUsernameChoices)
), ),
async handler(interaction) { async handler(interaction) {
+1 -1
View File
@@ -14,7 +14,7 @@ import { resolvePeriodFromButton } from '../core/discord/resolvePeriodFromButton
import { resolvePeriodFromCommand } from '../core/discord/resolvePeriodFromCommand'; import { resolvePeriodFromCommand } from '../core/discord/resolvePeriodFromCommand';
import { resolvePlayerFromButton } from '../core/discord/resolvePlayerFromButton'; import { resolvePlayerFromButton } from '../core/discord/resolvePlayerFromButton';
import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand'; import { resolvePlayerFromCommand } from '../core/discord/resolvePlayerFromCommand';
import { CommandRegistry } from '../events/interactionCreate'; import { type CommandRegistry } from '../events/interactionCreate';
import { renderBreakdown } from './breakdown/render'; import { renderBreakdown } from './breakdown/render';
export const todayCommand = new Promise<CommandRegistry>(async (resolve) => { 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; color: string;
direction?: 'default' | 'reverse'; direction?: "default" | "reverse";
rotation?: number; rotation?: number;
startOpacity?: number; startOpacity?: number;
endOpacity?: number; endOpacity?: number;
@@ -12,7 +12,7 @@ const width = 16;
export function Glow({ export function Glow({
color, color,
direction = 'default', direction = "default",
rotation = 0, rotation = 0,
style, style,
startOpacity = 0, startOpacity = 0,
@@ -21,20 +21,20 @@ export function Glow({
}: GlowProps) { }: GlowProps) {
const color1 = `${color}${Math.round(endOpacity * 255) const color1 = `${color}${Math.round(endOpacity * 255)
.toString(16) .toString(16)
.padStart(2, '0')}`; .padStart(2, "0")}`;
const color2 = `${color}${Math.round(startOpacity * 255) const color2 = `${color}${Math.round(startOpacity * 255)
.toString(16) .toString(16)
.padStart(2, '0')}`; .padStart(2, "0")}`;
const background = `linear-gradient(${ const background = `linear-gradient(${
direction === 'default' ? 0 : 180 direction === "default" ? 0 : 180
}deg, ${color1} 0%, ${color2} 100%)`; }deg, ${color1} 0%, ${color2} 100%)`;
return ( return (
<div <div
style={{ style={{
display: 'flex', display: "flex",
alignItems: direction === 'default' ? 'flex-end' : 'flex-start', alignItems: direction === "default" ? "flex-end" : "flex-start",
justifyContent: 'center', justifyContent: "center",
transform: `rotate(${rotation}deg)`, transform: `rotate(${rotation}deg)`,
...style, ...style,
}} }}
@@ -1,7 +1,7 @@
import { import {
getWN8Percentile, getWN8Percentile,
Percentile, Percentile,
SupplementaryStats, type SupplementaryStats,
} from '@blitzkit/core'; } from '@blitzkit/core';
import { Locale } from 'discord.js'; import { Locale } from 'discord.js';
import { translator } from '../../core/localization/translator'; import { translator } from '../../core/localization/translator';
@@ -1,4 +1,4 @@
import { ReactNode } from 'react'; import { type ReactNode } from "react";
interface RootProps { interface RootProps {
children: ReactNode; children: ReactNode;
@@ -8,7 +8,7 @@ export function Root({ children }: RootProps) {
return ( return (
<div <div
className="session-tracker-root" className="session-tracker-root"
style={{ display: 'flex', flexDirection: 'column', gap: 8 }} style={{ display: "flex", flexDirection: "column", gap: 8 }}
> >
{children} {children}
</div> </div>
@@ -6,8 +6,8 @@ import {
} from '@blitzkit/core'; } from '@blitzkit/core';
import { createColors } from 'bepaint'; import { createColors } from 'bepaint';
import { import {
AccentColor, type AccentColor,
GrayColor, type GrayColor,
PALETTES, PALETTES,
} from '../../../core/radix/radixColors'; } from '../../../core/radix/radixColors';
import { RowStat } from './RowStat'; import { RowStat } from './RowStat';
@@ -1,11 +1,11 @@
import { Percentile } from '@blitzkit/core'; import { Percentile } from "@blitzkit/core";
import { createColors } from 'bepaint'; import { createColors } from "bepaint";
import { import {
AccentColor, type AccentColor,
GrayColor, type GrayColor,
PALETTES, PALETTES,
} from '../../../core/radix/radixColors'; } from "../../../core/radix/radixColors";
import { PercentileIndicator } from '../../PercentileIndicator'; import { PercentileIndicator } from "../../PercentileIndicator";
interface RowStatProps { interface RowStatProps {
name: string; name: string;
@@ -22,7 +22,7 @@ export function RowStat({
delta, delta,
percentile, percentile,
minimized = false, minimized = false,
color = 'slate', color = "slate",
}: RowStatProps) { }: RowStatProps) {
const theme = createColors(PALETTES[`${color}Dark`]); const theme = createColors(PALETTES[`${color}Dark`]);
@@ -30,29 +30,29 @@ export function RowStat({
<div <div
className="session-tracker-row-stat" className="session-tracker-row-stat"
style={{ style={{
display: 'flex', display: "flex",
flexDirection: 'column', flexDirection: "column",
gap: 4, gap: 4,
alignItems: 'center', alignItems: "center",
justifyContent: 'center', justifyContent: "center",
flex: 1, flex: 1,
}} }}
> >
<div <div
style={{ style={{
display: 'flex', display: "flex",
gap: 4, gap: 4,
alignItems: 'center', alignItems: "center",
justifyContent: 'center', justifyContent: "center",
}} }}
> >
{percentile === undefined && delta !== undefined && delta !== 0 && ( {percentile === undefined && delta !== undefined && delta !== 0 && (
<img <img
alt={delta > 0 ? 'Increase' : 'Decrease'} alt={delta > 0 ? "Increase" : "Decrease"}
src={ src={
(delta ?? 0) > 0 (delta ?? 0) > 0
? 'https://i.imgur.com/qbjiXa1.png' ? "https://i.imgur.com/qbjiXa1.png"
: 'https://i.imgur.com/3uyNhun.png' : "https://i.imgur.com/3uyNhun.png"
} }
style={{ width: 12, height: 12 }} style={{ width: 12, height: 12 }}
/> />
@@ -66,7 +66,7 @@ export function RowStat({
color: theme.textHighContrast, color: theme.textHighContrast,
fontWeight: 700, fontWeight: 700,
fontSize: 16, fontSize: 16,
textAlign: 'center', textAlign: "center",
}} }}
> >
{value} {value}
@@ -77,7 +77,7 @@ export function RowStat({
<span <span
style={{ style={{
color: theme.textLowContrast, color: theme.textLowContrast,
textAlign: 'center', textAlign: "center",
fontSize: 12, fontSize: 12,
}} }}
> >
@@ -1,5 +1,5 @@
import { ReactNode } from 'react'; import { type ReactNode } from "react";
import { theme } from '../stitches.config'; import { theme } from "../stitches.config";
interface CommandWrapperProps { interface CommandWrapperProps {
children: ReactNode; children: ReactNode;
@@ -10,14 +10,14 @@ export function CommandWrapper({ children, fat }: CommandWrapperProps) {
return ( return (
<div <div
style={{ style={{
display: 'flex', display: "flex",
flexDirection: 'column', flexDirection: "column",
padding: `16px ${fat ? 0 : 16}px`, padding: `16px ${fat ? 0 : 16}px`,
gap: 16, gap: 16,
width: '100%', width: "100%",
color: theme.colors.textHighContrast, color: theme.colors.textHighContrast,
backgroundColor: theme.colors.appBackground1, backgroundColor: theme.colors.appBackground1,
position: 'relative', position: "relative",
}} }}
> >
{children} {children}
@@ -1,8 +1,8 @@
import { import {
BlitzStats, type BlitzStats,
getWN8Percentile, getWN8Percentile,
isNumber, isNumber,
SupplementaryStats, type SupplementaryStats,
} from '@blitzkit/core'; } from '@blitzkit/core';
import { Locale } from 'discord.js'; import { Locale } from 'discord.js';
import { translator } from '../../core/localization/translator'; import { translator } from '../../core/localization/translator';
@@ -1,5 +1,5 @@
import { GenericStat } from '..'; import { type GenericStat } from "..";
import { GenericStatsRow } from './GenericStatsRow'; import { GenericStatsRow } from "./GenericStatsRow";
interface GenericStatsColumnProps { interface GenericStatsColumnProps {
stats: GenericStat[]; stats: GenericStat[];
@@ -7,7 +7,7 @@ interface GenericStatsColumnProps {
export function GenericStatsColumn({ stats }: GenericStatsColumnProps) { export function GenericStatsColumn({ stats }: GenericStatsColumnProps) {
return ( return (
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 8 }}> <div style={{ flex: 1, display: "flex", flexDirection: "column", gap: 8 }}>
{stats.map((stat) => ( {stats.map((stat) => (
<GenericStatsRow key={stat[0]} stat={stat} /> <GenericStatsRow key={stat[0]} stat={stat} />
))} ))}
@@ -1,6 +1,6 @@
import { GenericStat } from '..'; import { type GenericStat } from "..";
import { theme } from '../../../stitches.config'; import { theme } from "../../../stitches.config";
import { PercentileIndicator } from '../../PercentileIndicator'; import { PercentileIndicator } from "../../PercentileIndicator";
interface GenericStatsRowProps { interface GenericStatsRowProps {
stat: GenericStat; stat: GenericStat;
@@ -10,18 +10,18 @@ export function GenericStatsRow({ stat }: GenericStatsRowProps) {
return ( return (
<div <div
style={{ style={{
display: 'flex', display: "flex",
justifyContent: 'space-between', justifyContent: "space-between",
alignItems: 'center', alignItems: "center",
}} }}
> >
<span <span
style={{ style={{
color: theme.colors.textLowContrast, color: theme.colors.textLowContrast,
fontSize: 16, fontSize: 16,
whiteSpace: 'nowrap', whiteSpace: "nowrap",
maxWidth: 128, maxWidth: 128,
overflow: 'hidden', overflow: "hidden",
}} }}
> >
{stat[0]} {stat[0]}
@@ -32,9 +32,9 @@ export function GenericStatsRow({ stat }: GenericStatsRowProps) {
color: theme.colors.textHighContrast, color: theme.colors.textHighContrast,
fontWeight: 700, fontWeight: 700,
fontSize: 16, fontSize: 16,
display: 'flex', display: "flex",
alignItems: 'center', alignItems: "center",
justifyContent: 'center', justifyContent: "center",
gap: 4, gap: 4,
}} }}
> >
@@ -1,6 +1,6 @@
import { times } from 'lodash-es'; import { times } from 'lodash-es';
import { theme } from '../../../../stitches.config'; import { theme } from '../../../../stitches.config';
import { MarginInputProps } from '../Root'; import { type MarginInputProps } from '../Root';
import { MarginOrientation } from './constants'; import { MarginOrientation } from './constants';
interface MarginProps extends MarginInputProps { interface MarginProps extends MarginInputProps {
@@ -1,9 +1,9 @@
import { times } from 'lodash-es'; import { times } from "lodash-es";
import { ReactNode } from 'react'; import { type ReactNode } from "react";
import { theme } from '../../../../stitches.config'; import { theme } from "../../../../stitches.config";
import { Margin } from '../Margin'; import { Margin } from "../Margin";
import { MarginOrientation } from '../Margin/constants'; import { MarginOrientation } from "../Margin/constants";
import { GRAPH_HEIGHT, GRAPH_WIDTH } from './constants'; import { GRAPH_HEIGHT, GRAPH_WIDTH } from "./constants";
export interface MarginInputProps { export interface MarginInputProps {
min: number; min: number;
@@ -32,12 +32,12 @@ export function Root({
xTitle, xTitle,
}: RootProps) { }: RootProps) {
return ( return (
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}> <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
<div <div
style={{ style={{
paddingLeft: 64, paddingLeft: 64,
display: 'flex', display: "flex",
justifyContent: 'space-between', justifyContent: "space-between",
}} }}
> >
<span style={{ fontSize: 16, color: theme.colors.textHighContrast }}> <span style={{ fontSize: 16, color: theme.colors.textHighContrast }}>
@@ -48,7 +48,7 @@ export function Root({
</span> </span>
</div> </div>
<div style={{ display: 'flex', gap: 16 }}> <div style={{ display: "flex", gap: 16 }}>
{verticalMargin && ( {verticalMargin && (
<Margin {...verticalMargin} verticalSeparations={separations} /> <Margin {...verticalMargin} verticalSeparations={separations} />
)} )}
@@ -94,9 +94,9 @@ export function Root({
<div <div
style={{ style={{
paddingLeft: 64, paddingLeft: 64,
display: 'flex', display: "flex",
alignItems: 'center', alignItems: "center",
justifyContent: 'center', justifyContent: "center",
}} }}
> >
<span style={{ fontSize: 16, color: theme.colors.textLowContrast }}> <span style={{ fontSize: 16, color: theme.colors.textLowContrast }}>
@@ -1,7 +1,7 @@
import { ComponentProps } from 'react'; import { type ComponentProps } from "react";
import { theme } from '../../../stitches.config'; import { theme } from "../../../stitches.config";
interface ItemProps extends ComponentProps<'div'> { interface ItemProps extends ComponentProps<"div"> {
position: number; position: number;
deltaPosition?: number; deltaPosition?: number;
score?: number; score?: number;
@@ -25,15 +25,15 @@ export function Item({
<div <div
{...props} {...props}
style={{ style={{
cursor: 'pointer', cursor: "pointer",
display: 'flex', display: "flex",
padding: 8, padding: 8,
backgroundColor: highlight backgroundColor: highlight
? theme.colors.componentInteractive_blue ? theme.colors.componentInteractive_blue
: theme.colors.componentInteractive, : theme.colors.componentInteractive,
borderRadius: 4, borderRadius: 4,
alignItems: 'center', alignItems: "center",
justifyContent: 'center', justifyContent: "center",
gap: 8, gap: 8,
}} }}
> >
@@ -50,9 +50,9 @@ export function Item({
<div <div
style={{ style={{
display: 'flex', display: "flex",
alignItems: 'center', alignItems: "center",
justifyContent: 'center', justifyContent: "center",
gap: 4, gap: 4,
}} }}
> >
@@ -83,22 +83,22 @@ export function Item({
{deltaPosition !== undefined && deltaPosition !== 0 && ( {deltaPosition !== undefined && deltaPosition !== 0 && (
<div <div
style={{ style={{
display: 'flex', display: "flex",
alignItems: 'center', alignItems: "center",
justifyContent: 'center', justifyContent: "center",
gap: 4, gap: 4,
}} }}
> >
<img <img
alt={deltaPosition > 0 ? 'Increase' : 'Decrease'} alt={deltaPosition > 0 ? "Increase" : "Decrease"}
style={{ style={{
width: 8, width: 8,
height: 8, height: 8,
}} }}
src={ src={
deltaPosition > 0 deltaPosition > 0
? 'https://i.imgur.com/qbjiXa1.png' ? "https://i.imgur.com/qbjiXa1.png"
: 'https://i.imgur.com/3uyNhun.png' : "https://i.imgur.com/3uyNhun.png"
} }
/> />
<span <span
@@ -119,11 +119,11 @@ export function Item({
<div <div
style={{ style={{
display: 'flex', display: "flex",
gap: 4, gap: 4,
alignItems: 'center', alignItems: "center",
width: 96, width: 96,
justifyContent: 'flex-end', justifyContent: "flex-end",
}} }}
> >
{deltaScore !== 0 && deltaScore !== undefined && ( {deltaScore !== 0 && deltaScore !== undefined && (
@@ -140,15 +140,15 @@ export function Item({
)} )}
{deltaScore !== 0 && deltaScore !== undefined && ( {deltaScore !== 0 && deltaScore !== undefined && (
<img <img
alt={deltaScore > 0 ? 'Increase' : 'Decrease'} alt={deltaScore > 0 ? "Increase" : "Decrease"}
style={{ style={{
width: 8, width: 8,
height: 8, height: 8,
}} }}
src={ src={
deltaScore > 0 deltaScore > 0
? 'https://i.imgur.com/qbjiXa1.png' ? "https://i.imgur.com/qbjiXa1.png"
: 'https://i.imgur.com/3uyNhun.png' : "https://i.imgur.com/3uyNhun.png"
} }
/> />
)} )}
@@ -1,4 +1,4 @@
import { ReactNode } from 'react'; import { type ReactNode } from 'react';
interface RootProps { interface RootProps {
children: ReactNode; children: ReactNode;
@@ -1,4 +1,4 @@
import { ReactNode } from 'react'; import { type ReactNode } from 'react';
interface ColumnProps { interface ColumnProps {
children: ReactNode; children: ReactNode;
@@ -1,4 +1,4 @@
import { ReactNode } from 'react'; import { type ReactNode } from "react";
interface RootProps { interface RootProps {
children: ReactNode; children: ReactNode;
@@ -8,8 +8,8 @@ export function Root({ children }: RootProps) {
return ( return (
<div <div
style={{ style={{
display: 'flex', display: "flex",
flexDirection: 'column', flexDirection: "column",
gap: 16, gap: 16,
}} }}
> >
@@ -1,4 +1,4 @@
import { ReactNode } from 'react'; import { type ReactNode } from "react";
interface RowProps { interface RowProps {
children: ReactNode; children: ReactNode;
@@ -8,7 +8,7 @@ export function Row({ children }: RowProps) {
return ( return (
<div <div
style={{ style={{
display: 'flex', display: "flex",
gap: 8, gap: 8,
}} }}
> >
+7 -7
View File
@@ -1,5 +1,5 @@
import { TierWeightsRecord } from '@blitzkit/core'; import type { TierWeightsRecord } from "@blitzkit/core";
import { theme } from '../stitches.config'; import { theme } from "../stitches.config";
interface TierWeightsProps { interface TierWeightsProps {
weights: TierWeightsRecord; weights: TierWeightsRecord;
@@ -7,7 +7,7 @@ interface TierWeightsProps {
export function TierWeights({ weights }: TierWeightsProps) { export function TierWeights({ weights }: TierWeightsProps) {
return ( return (
<div style={{ display: 'flex', gap: 8 }}> <div style={{ display: "flex", gap: 8 }}>
{Object.entries(weights).map(([tier, weight]) => ( {Object.entries(weights).map(([tier, weight]) => (
<div <div
key={tier} key={tier}
@@ -15,11 +15,11 @@ export function TierWeights({ weights }: TierWeightsProps) {
backgroundColor: theme.colors.appBackground1_light, backgroundColor: theme.colors.appBackground1_light,
flex: weight, flex: weight,
color: theme.colors.textHighContrast_light, color: theme.colors.textHighContrast_light,
display: 'flex', display: "flex",
alignItems: 'center', alignItems: "center",
justifyContent: 'center', justifyContent: "center",
fontSize: 16, fontSize: 16,
overflow: 'hidden', overflow: "hidden",
borderRadius: 8, borderRadius: 8,
minWidth: 16, minWidth: 16,
}} }}
+18 -16
View File
@@ -1,33 +1,33 @@
import { SEARCH_KEYS } from '@blitzkit/core'; import { SEARCH_KEYS } from "@blitzkit/core";
import { literals } from '@blitzkit/i18n'; import { literals } from "@blitzkit/i18n";
import { Locale } from 'discord.js'; import { Locale } from "discord.js";
import { go } from 'fuzzysort'; import { go } from "fuzzysort";
import markdownEscape from 'markdown-escape'; import markdownEscape from "markdown-escape";
import { tankDefinitions, tankNames } from '../blitzkit/nonBlockingPromises'; import { tankDefinitions, tankNames } from "../blitzkit/nonBlockingPromises";
import { UserError } from '../blitzkit/userError'; import { UserError } from "../blitzkit/userError";
import { tankNamesTechTreeOnly } from '../discord/autocompleteTanks'; import { tankNamesTechTreeOnly } from "../discord/autocompleteTanks";
import { translator } from '../localization/translator'; import { translator } from "../localization/translator";
export async function resolveTankId( export async function resolveTankId(
tank: string | number, tank: string | number,
locale: Locale, locale: Locale,
techTreeOnly = false, techTreeOnly = false
) { ) {
const { strings } = translator(locale); const { strings } = translator(locale);
const number = typeof tank === 'string' ? Number(tank) : tank; const number = typeof tank === "string" ? Number(tank) : tank;
if (Number.isNaN(number)) { if (Number.isNaN(number)) {
const searchResult = go( const searchResult = go(
`${tank}`, `${tank}`,
await (techTreeOnly ? tankNamesTechTreeOnly : tankNames), await (techTreeOnly ? tankNamesTechTreeOnly : tankNames),
{ keys: SEARCH_KEYS, limit: 1 }, { keys: SEARCH_KEYS, limit: 1 }
); );
if (searchResult.length === 0) { if (searchResult.length === 0) {
throw new UserError( throw new UserError(
literals(strings.bot.common.errors.tank_name_not_found, [ literals(strings.bot.common.errors.tank_name_not_found, {
markdownEscape(`${tank}`), search: markdownEscape(`${tank}`),
]), })
); );
} else { } else {
return searchResult[0].obj.id; return searchResult[0].obj.id;
@@ -39,7 +39,9 @@ export async function resolveTankId(
return number; return number;
} else { } else {
throw new UserError( 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 { fetchTankDefinitions, TIER_ROMAN_NUMERALS } from "@blitzkit/core";
import { literals } from '@blitzkit/i18n'; import { literals } from "@blitzkit/i18n";
import { Locale } from 'discord.js'; import { Locale } from "discord.js";
import { StatFilters } from '../blitzstars/filterStats'; import { type StatFilters } from "../blitzstars/filterStats";
import { translator } from '../localization/translator'; import { translator } from "../localization/translator";
export async function filtersToDescription( export async function filtersToDescription(
{ nation, tier, type, class: tankClass, tank }: StatFilters, { nation, tier, type, class: tankClass, tank }: StatFilters,
locale: Locale, locale: Locale
) { ) {
const { strings, bkLocale } = translator(locale); const { strings, bkLocale } = translator(locale);
const awaitedTankDefinitions = await fetchTankDefinitions(); const awaitedTankDefinitions = await fetchTankDefinitions();
@@ -18,11 +18,13 @@ export async function filtersToDescription(
info.push((strings.common.nations as Record<string, string>)[nation]); info.push((strings.common.nations as Record<string, string>)[nation]);
if (tier) { if (tier) {
info.push( 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 (tankClass) info.push(strings.common.tank_class_short[tankClass]);
if (type) info.push(strings.common.tree_type[type]); 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 { getTimeDaysAgo, type Region } from '@blitzkit/core';
import { PeriodSize } from '../discord/addPeriodSubCommands'; import type { PeriodSize } from '../discord/addPeriodSubCommands';
export function getPeriodStart(region: Region, period: PeriodSize) { export function getPeriodStart(region: Region, period: PeriodSize) {
if (period === 'career') return 0; 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> = {}; const cache: Record<string, Buffer> = {};
export async function iconPng(url: string) { export async function iconPng(url: string) {
if (!url.endsWith('.webp')) return url; if (!url.endsWith(".webp")) return url;
if (!cache[url]) { if (!cache[url]) {
const imageResponse = await fetch(url); const imageResponse = await fetch(url);
@@ -12,5 +12,5 @@ export async function iconPng(url: string) {
cache[url] = imageBufferPNG; cache[url] = imageBufferPNG;
} }
return `data:image/png;base64,${cache[url].toString('base64')}`; return `data:image/png;base64,${cache[url].toString("base64")}`;
} }
+3 -3
View File
@@ -1,7 +1,7 @@
import { jsxToSvg } from './jsxToSvg'; import { jsxToSvg } from "./jsxToSvg";
import { svgToPng } from './svgToPng'; import { svgToPng } from "./svgToPng";
export async function jsxToPng(jsx: JSX.Element) { export async function jsxToPng(jsx: React.JSX.Element) {
const svg = await jsxToSvg(jsx); const svg = await jsxToSvg(jsx);
const png = svgToPng(svg); const png = svgToPng(svg);
@@ -1,10 +1,10 @@
import { jsxToSvg } from './jsxToSvg'; import { jsxToSvg } from "./jsxToSvg";
import { RenderConfiguration } from './renderConfiguration'; import { RenderConfiguration } from "./renderConfiguration";
import { svgToPngThreaded } from './svgToPngThreaded'; import { svgToPngThreaded } from "./svgToPngThreaded";
export async function jsxToPngThreaded( export async function jsxToPngThreaded(
jsx: JSX.Element, jsx: React.JSX.Element,
renderConfiguration: RenderConfiguration, renderConfiguration: RenderConfiguration
) { ) {
const svg = await jsxToSvg(jsx, renderConfiguration); const svg = await jsxToSvg(jsx, renderConfiguration);
const png = await svgToPngThreaded(svg); const png = await svgToPngThreaded(svg);
@@ -1,9 +1,9 @@
import { readFile } from 'fs/promises'; import { readFile } from "fs/promises";
import satori from 'satori'; import satori from "satori";
import { RenderConfiguration } from '../renderConfiguration'; import { RenderConfiguration } from "../renderConfiguration";
import robotoBlack from './Roboto-Black.ttf'; import robotoBlack from "./Roboto-Black.ttf";
import robotoBold from './Roboto-Bold.ttf'; import robotoBold from "./Roboto-Bold.ttf";
import roboto from './Roboto.ttf'; import roboto from "./Roboto.ttf";
const fontsPromise = [ const fontsPromise = [
{ file: roboto, weight: 400 as const }, { file: roboto, weight: 400 as const },
@@ -11,12 +11,12 @@ const fontsPromise = [
{ file: robotoBlack, weight: 900 as const }, { file: robotoBlack, weight: 900 as const },
].map(async ({ file, weight }) => { ].map(async ({ file, weight }) => {
const data = await readFile(`${__dirname}/${file}`); const data = await readFile(`${__dirname}/${file}`);
return { name: 'Roboto', weight, data }; return { name: "Roboto", weight, data };
}); });
export async function jsxToSvg( export async function jsxToSvg(
jsx: JSX.Element, jsx: React.JSX.Element,
renderConfiguration = new RenderConfiguration(), renderConfiguration = new RenderConfiguration()
) { ) {
const fonts = await Promise.all(fontsPromise); const fonts = await Promise.all(fontsPromise);
return await satori(jsx, { width: renderConfiguration.width, fonts }); return await satori(jsx, { width: renderConfiguration.width, fonts });
+12 -12
View File
@@ -1,10 +1,10 @@
import { TankClass, TankType } from '@blitzkit/core'; import { TankClass, TankType } from "@blitzkit/core";
import { SupplementaryStats } from '@blitzkit/core/src/blitz/getAccountInfo'; import type { SupplementaryStats } from "@blitzkit/core/src/blitz/getAccountInfo";
import { calculateWN8 } from '@blitzkit/core/src/statistics/calculateWN8'; import { calculateWN8 } from "@blitzkit/core/src/statistics/calculateWN8";
import { BlitzStats } from '@blitzkit/core/src/statistics/compositeStats'; import type { BlitzStats } from "@blitzkit/core/src/statistics/compositeStats";
import { sumAllStats } from '@blitzkit/core/src/statistics/sumAllStats'; import { sumAllStats } from "@blitzkit/core/src/statistics/sumAllStats";
import { tankDefinitions } from '../blitzkit/nonBlockingPromises'; import { tankDefinitions } from "../blitzkit/nonBlockingPromises";
import { blitzStarsTankAverages } from './tankAverages'; import { blitzStarsTankAverages } from "./tankAverages";
export interface StatFilters { export interface StatFilters {
nation?: string; nation?: string;
@@ -16,7 +16,7 @@ export interface StatFilters {
export async function filterStats( export async function filterStats(
{ diff, order }: DiffedTankStats, { diff, order }: DiffedTankStats,
filters: StatFilters, filters: StatFilters
) { ) {
// tank, if provided, takes priority over all other filters // tank, if provided, takes priority over all other filters
if (filters.tank) filters = { tank: filters.tank }; if (filters.tank) filters = { tank: filters.tank };
@@ -45,14 +45,14 @@ export async function filterStats(
const battlesOfTanksWithAverages = filteredOrder.reduce<number>( const battlesOfTanksWithAverages = filteredOrder.reduce<number>(
(accumulator, id) => (accumulator, id) =>
awaitedTankAverages[id] ? accumulator + diff[id].battles : accumulator, awaitedTankAverages[id] ? accumulator + diff[id].battles : accumulator,
0, 0
); );
const battlesOfTanksWithTankDefinition = filteredOrder.reduce<number>( const battlesOfTanksWithTankDefinition = filteredOrder.reduce<number>(
(accumulator, id) => (accumulator, id) =>
awaitedTankDefinitions.tanks[id] awaitedTankDefinitions.tanks[id]
? accumulator + diff[id].battles ? accumulator + diff[id].battles
: accumulator, : accumulator,
0, 0
); );
const supplementary = { const supplementary = {
WN8: WN8:
@@ -63,7 +63,7 @@ export async function filterStats(
calculateWN8(awaitedTankAverages[id].all, diff[id]) * calculateWN8(awaitedTankAverages[id].all, diff[id]) *
diff[id].battles diff[id].battles
: accumulator, : accumulator,
0, 0
) / battlesOfTanksWithAverages, ) / battlesOfTanksWithAverages,
tier: tier:
filteredOrder.reduce<number>( filteredOrder.reduce<number>(
@@ -72,7 +72,7 @@ export async function filterStats(
? accumulator + ? accumulator +
awaitedTankDefinitions.tanks[id]!.tier * diff[id].battles awaitedTankDefinitions.tanks[id]!.tier * diff[id].battles
: accumulator, : accumulator,
0, 0
) / battlesOfTanksWithTankDefinition, ) / battlesOfTanksWithTankDefinition,
} satisfies SupplementaryStats; } satisfies SupplementaryStats;
@@ -1,17 +1,17 @@
import { getAccountInfo, Region } from '@blitzkit/core'; import { getAccountInfo, type Region } from "@blitzkit/core";
import { buttonLink } from '../discord/buttonLink'; import { buttonLink } from "../discord/buttonLink";
export async function getBlitzStarsLinkButton( export async function getBlitzStarsLinkButton(
region: Region, region: Region,
id: number, id: number,
tankId?: number, tankId?: number
) { ) {
const { nickname } = await getAccountInfo(region, id); const { nickname } = await getAccountInfo(region, id);
return buttonLink( return buttonLink(
`https://www.blitzstars.com/player/${region}/${nickname}${ `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 { import {
GetHistoriesOptions, type GetHistoriesOptions,
Histories, type Histories,
History, type History,
getHistoriesDefaultOptions, getHistoriesDefaultOptions,
} from '../../types/histories'; } from '../../types/histories';
import { PlayerStats } from './getPlayerStats'; import type { PlayerStats } from './getPlayerStats';
export async function getPlayerHistories( export async function getPlayerHistories(
server: Region, server: Region,
@@ -1,4 +1,4 @@
import { BlitzStats, Region } from '@blitzkit/core'; import type { BlitzStats, Region } from "@blitzkit/core";
interface IndividualPlayerStats { interface IndividualPlayerStats {
clan: { clan_id: number; name: string; tag: string }; clan: { clan_id: number; name: string; tag: string };
@@ -25,13 +25,13 @@ export async function getPlayerStats<
ReturnType = Ids extends number ? PlayerStats : PlayerStats[], ReturnType = Ids extends number ? PlayerStats : PlayerStats[],
>(region: Region, ids: Ids) { >(region: Region, ids: Ids) {
const stats = await Promise.all( 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( 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; return stats as ReturnType;
} }
@@ -1,11 +1,11 @@
import { BlitzStats, emptyAllStats, Region } from '@blitzkit/core'; import { type BlitzStats, emptyAllStats, type Region } from "@blitzkit/core";
import { Locale } from 'discord.js'; import { Locale } from "discord.js";
import { DiffedTankStats } from './filterStats'; import type { DiffedTankStats } from "./filterStats";
import { import {
getTankHistories, getTankHistories,
TankHistories, type TankHistories,
TankHistoryRaw, type TankHistoryRaw,
} from './getTankHistories'; } from "./getTankHistories";
const emptyTankHistoryNode: TankHistoryRaw = { const emptyTankHistoryNode: TankHistoryRaw = {
account_id: 0, account_id: 0,
@@ -22,7 +22,7 @@ export async function getStatsInPeriod(
id: number, id: number,
start: number, start: number,
end: number, end: number,
locale: Locale, locale: Locale
) { ) {
const history = await getTankHistories(server, id, locale, { const history = await getTankHistories(server, id, locale, {
includeLatestHistories: true, includeLatestHistories: true,
@@ -43,8 +43,8 @@ export async function getStatsInPeriod(
// find the ones played within the range // find the ones played within the range
const inRangeTanks = tanks.filter((tankId) => const inRangeTanks = tanks.filter((tankId) =>
tankSortedHistory[tankId].some( tankSortedHistory[tankId].some(
(node) => node.last_battle_time > start && node.last_battle_time < end, (node) => node.last_battle_time > start && node.last_battle_time < end
), )
); );
const playedTanks: number[] = []; const playedTanks: number[] = [];
@@ -1,13 +1,13 @@
import { Region, emptyAllStats, getTankStats } from '@blitzkit/core'; import { type Region, emptyAllStats, getTankStats } from "@blitzkit/core";
import { Locale } from 'discord.js'; import { Locale } from "discord.js";
import { import {
GetHistoriesOptions, type GetHistoriesOptions,
Histories, type Histories,
History, type History,
getHistoriesDefaultOptions, getHistoriesDefaultOptions,
} from '../../types/histories'; } from "../../types/histories";
import { UserError } from '../blitzkit/userError'; import { UserError } from "../blitzkit/userError";
import { translator } from '../localization/translator'; import { translator } from "../localization/translator";
export interface TankHistory extends History { export interface TankHistory extends History {
tank_id: number; tank_id: number;
@@ -36,12 +36,12 @@ export async function getTankHistories(
server: Region, server: Region,
id: number, id: number,
locale: Locale, locale: Locale,
options?: Partial<GetTankHistoriesOptions>, options?: Partial<GetTankHistoriesOptions>
) { ) {
const { strings } = translator(locale); const { strings } = translator(locale);
const mergedOptions = { ...getTankHistoriesDefaultOptions, ...options }; const mergedOptions = { ...getTankHistoriesDefaultOptions, ...options };
const tankHistoriesResponse = await fetch( const tankHistoriesResponse = await fetch(
`https://www.blitzstars.com/api/tankhistories/for/${id}/`, `https://www.blitzstars.com/api/tankhistories/for/${id}/`
); );
const tankHistories = ( const tankHistories = (
(await tankHistoriesResponse.json()) as TankHistoriesRaw (await tankHistoriesResponse.json()) as TankHistoriesRaw
@@ -51,7 +51,7 @@ export async function getTankHistories(
all: history.all, all: history.all,
last_battle_time: history.last_battle_time, last_battle_time: history.last_battle_time,
tank_id: history.tank_id, tank_id: history.tank_id,
}) satisfies TankHistory, }) satisfies TankHistory
); );
const compliantTanks: number[] = []; const compliantTanks: number[] = [];
const lastBattles: Record<number, number> = {}; const lastBattles: Record<number, number> = {};
@@ -73,7 +73,7 @@ export async function getTankHistories(
lastBattles[tank_id] = battles; lastBattles[tank_id] = battles;
return complies; return complies;
}, }
); );
const latestNodes: TankHistories = []; const latestNodes: TankHistories = [];
@@ -112,7 +112,7 @@ export async function getTankHistories(
all: emptyAllStats, all: emptyAllStats,
last_battle_time: -Infinity, last_battle_time: -Infinity,
tank_id: tankId, tank_id: tankId,
} satisfies TankHistory), } satisfies TankHistory)
); );
} }
}); });
@@ -4,7 +4,7 @@ import {
TANK_TYPES, TANK_TYPES,
} from '@blitzkit/core'; } from '@blitzkit/core';
import { import {
APIApplicationCommandOptionChoice, type APIApplicationCommandOptionChoice,
Locale, Locale,
SlashCommandBuilder, SlashCommandBuilder,
SlashCommandSubcommandBuilder, SlashCommandSubcommandBuilder,
@@ -1,11 +1,11 @@
import { REGIONS } from '@blitzkit/core'; import { REGIONS } from "@blitzkit/core";
import { import {
APIApplicationCommandOptionChoice, type APIApplicationCommandOptionChoice,
Locale, Locale,
SlashCommandStringOption, SlashCommandStringOption,
} from 'discord.js'; } from "discord.js";
import { translator } from '../localization/translator'; import { translator } from "../localization/translator";
import { localizationObject } from './localizationObject'; import { localizationObject } from "./localizationObject";
export function addRegionChoices(option: SlashCommandStringOption) { export function addRegionChoices(option: SlashCommandStringOption) {
const { strings } = translator(Locale.EnglishUS); const { strings } = translator(Locale.EnglishUS);
@@ -16,14 +16,14 @@ export function addRegionChoices(option: SlashCommandStringOption) {
localizationObject( localizationObject(
(strings) => strings.bot.common.options.region.name, (strings) => strings.bot.common.options.region.name,
undefined, undefined,
true, true
), )
) )
.setDescription(strings.bot.common.options.region.description) .setDescription(strings.bot.common.options.region.description)
.setDescriptionLocalizations( .setDescriptionLocalizations(
localizationObject( localizationObject(
(strings) => strings.bot.common.options.region.description, (strings) => strings.bot.common.options.region.description
), )
) )
.addChoices( .addChoices(
...REGIONS.map( ...REGIONS.map(
@@ -32,10 +32,10 @@ export function addRegionChoices(option: SlashCommandStringOption) {
value: region, value: region,
name: strings.common.regions.normal[region], name: strings.common.regions.normal[region],
name_localizations: localizationObject( name_localizations: localizationObject(
(strings) => strings.common.regions.normal[region], (strings) => strings.common.regions.normal[region]
), ),
}) satisfies APIApplicationCommandOptionChoice, }) satisfies APIApplicationCommandOptionChoice
), )
) )
.setRequired(true); .setRequired(true);
} }
+17 -17
View File
@@ -1,13 +1,13 @@
import { numberToRomanNumeral } from '@blitzkit/core'; import { numberToRomanNumeral } from "@blitzkit/core";
import { literals } from '@blitzkit/i18n'; import { literals } from "@blitzkit/i18n";
import { import {
APIApplicationCommandOptionChoice, APIApplicationCommandOptionChoice,
Locale, Locale,
SlashCommandStringOption, SlashCommandStringOption,
} from 'discord.js'; } from "discord.js";
import { range } from 'lodash-es'; import { range } from "lodash-es";
import { translator } from '../localization/translator'; import { translator } from "../localization/translator";
import { localizationObject } from './localizationObject'; import { localizationObject } from "./localizationObject";
export function addTierChoices(option: SlashCommandStringOption) { export function addTierChoices(option: SlashCommandStringOption) {
const { strings } = translator(Locale.EnglishUS); const { strings } = translator(Locale.EnglishUS);
@@ -18,30 +18,30 @@ export function addTierChoices(option: SlashCommandStringOption) {
localizationObject( localizationObject(
(strings) => strings.bot.common.options.tier.name, (strings) => strings.bot.common.options.tier.name,
undefined, undefined,
true, true
), )
) )
.setDescription(strings.bot.common.options.tier.description) .setDescription(strings.bot.common.options.tier.description)
.setDescriptionLocalizations( .setDescriptionLocalizations(
localizationObject( localizationObject(
(strings) => strings.bot.common.options.tier.description, (strings) => strings.bot.common.options.tier.description
), )
) )
.setChoices( .setChoices(
...range(10, 0).map( ...range(10, 0).map(
(tier) => (tier) =>
({ ({
name: literals(strings.bot.common.options.tier.choices.tier, [ name: literals(strings.bot.common.options.tier.choices.tier, {
`${tier}`, tier_1: tier,
numberToRomanNumeral(tier), tier_2: numberToRomanNumeral(tier),
]), }),
value: `${tier}`, value: `${tier}`,
name_localizations: localizationObject( name_localizations: localizationObject(
(strings) => strings.bot.common.options.tier.choices.tier, (strings) => strings.bot.common.options.tier.choices.tier,
[`${tier}`, numberToRomanNumeral(tier)], { tier_1: tier, tier_2: numberToRomanNumeral(tier) }
), ),
}) satisfies APIApplicationCommandOptionChoice, }) satisfies APIApplicationCommandOptionChoice
), )
) )
.setRequired(true); .setRequired(true);
} }
@@ -1,25 +1,25 @@
import { fetchTankNames, SEARCH_KEYS, TankType } from '@blitzkit/core'; import { fetchTankNames, SEARCH_KEYS, TankType } from "@blitzkit/core";
import { import {
ApplicationCommandOptionChoiceData, type ApplicationCommandOptionChoiceData,
AutocompleteInteraction, AutocompleteInteraction,
CacheType, type CacheType,
} from 'discord.js'; } from "discord.js";
import { go } from 'fuzzysort'; import { go } from "fuzzysort";
import { tankNames } from '../blitzkit/nonBlockingPromises'; import { tankNames } from "../blitzkit/nonBlockingPromises";
import { translator } from '../localization/translator'; import { translator } from "../localization/translator";
import { import {
DISCORD_CHOICES_MAX_NAME_SIZE, DISCORD_CHOICES_MAX_NAME_SIZE,
OVERFLOW_SUFFIX, OVERFLOW_SUFFIX,
} from './autocompleteClan/constants'; } from "./autocompleteClan/constants";
export const tankNamesTechTreeOnly = fetchTankNames().then((names) => export const tankNamesTechTreeOnly = fetchTankNames().then((names) =>
names.filter((tank) => tank.treeType === TankType.RESEARCHABLE), names.filter((tank) => tank.treeType === TankType.RESEARCHABLE)
); );
export async function autocompleteTanks( export async function autocompleteTanks(
interaction: AutocompleteInteraction<CacheType>, interaction: AutocompleteInteraction<CacheType>,
techTreeOnly = false, techTreeOnly = false,
fields = ['tank'], fields = ["tank"]
) { ) {
const { unwrap } = translator(interaction.locale); const { unwrap } = translator(interaction.locale);
const focusedOption = interaction.options.getFocused(true); const focusedOption = interaction.options.getFocused(true);
@@ -31,7 +31,7 @@ export async function autocompleteTanks(
go( go(
focusedOption.value, focusedOption.value,
await (techTreeOnly ? tankNamesTechTreeOnly : tankNames), await (techTreeOnly ? tankNamesTechTreeOnly : tankNames),
{ keys: SEARCH_KEYS, limit: 10 }, { keys: SEARCH_KEYS, limit: 10 }
).map(async (item) => { ).map(async (item) => {
let name = unwrap(item.obj.searchableName); let name = unwrap(item.obj.searchableName);
@@ -42,7 +42,7 @@ export async function autocompleteTanks(
0, 0,
unwrap(item.obj.searchableName).length - unwrap(item.obj.searchableName).length -
overSize - overSize -
OVERFLOW_SUFFIX.length, OVERFLOW_SUFFIX.length
)}${OVERFLOW_SUFFIX}`; )}${OVERFLOW_SUFFIX}`;
} }
@@ -50,8 +50,8 @@ export async function autocompleteTanks(
name, name,
value: `${item.obj.id}`, value: `${item.obj.id}`,
} satisfies ApplicationCommandOptionChoiceData<string>; } satisfies ApplicationCommandOptionChoiceData<string>;
}), })
) )
: [], : []
); );
} }
+3 -3
View File
@@ -1,13 +1,13 @@
import { import {
ButtonBuilder, ButtonBuilder,
ButtonStyle, ButtonStyle,
ComponentEmojiResolvable, type ComponentEmojiResolvable,
} from 'discord.js'; } from "discord.js";
export function buttonLink( export function buttonLink(
url: string, url: string,
label: string, label: string,
emoji?: ComponentEmojiResolvable, emoji?: ComponentEmojiResolvable
) { ) {
const button = new ButtonBuilder() const button = new ButtonBuilder()
.setStyle(ButtonStyle.Link) .setStyle(ButtonStyle.Link)
Loaded 100 of 521 files, more files were not shown because too many files have changed in this diff. Show more