Compare commits

...
404 Commits
Author SHA1 Message Date
TrèsAbhi 22f42117df feat: more hot garbage 2025-05-26 18:30:00 -05:00
TrèsAbhi 788edf5bdf feat: more hot garbage 2025-05-26 18:29:29 -05:00
TrèsAbhi d0170d53e4 feat: hot garbage 2025-05-26 14:23:00 -05:00
TrèsAbhi ae5e38139b feat: some set info in meta section 2025-05-25 18:49:00 -05:00
TrèsAbhi e38713f365 feat: meta section wallpapers 2025-05-25 18:08:00 -05:00
TrèsAbhi 5903b8130b feat: some of meta section 2025-05-25 18:03:55 -05:00
TrèsAbhi 179ce62a55 feat: strings 2025-05-25 15:18:22 -05:00
TrèsAbhi a2f1593a0e feat: add accessors 2025-05-24 18:41:03 -05:00
TrèsAbhi 6305db7ad6 feat: update to correct branch 2025-05-24 12:28:57 -05:00
TrèsAbhi 5082e1b82b feat: new protos 2025-05-24 12:24:53 -05:00
TrèsAbhi e7ca8f1b10 feat: remove UAssetAPI 2025-05-23 20:05:08 -05:00
tresabhi 0d8a677ac5 Merge branch 'main' into dev 2025-05-21 13:25:49 -05:00
tresabhi a8dc271e0a feat: update to imported base creators 2025-05-21 13:24:02 -05:00
TrèsAbhi f3d6ba68a7 New Crowdin updates (#154)
* New translations en.json (Ukrainian)

* New translations en.json (French)

* New translations en.json (Spanish)

* New translations en.json (Japanese)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (Chinese Simplified)

* New translations en.json (Polish)

* New translations en.json (Ukrainian)

* New translations en.json (French)

* New translations en.json (Spanish)

* New translations en.json (Japanese)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (Chinese Simplified)

* New translations en.json (Polish)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Ukrainian)

* New translations en.json (French)

* New translations en.json (Spanish)

* New translations en.json (Japanese)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (Chinese Simplified)

* New translations en.json (Polish)

* New translations en.json (Polish)

* New translations en.json (Russian)

* New translations en.json (Ukrainian)

* New translations en.json (French)

* New translations en.json (Spanish)

* New translations en.json (Japanese)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (Chinese Simplified)

* New translations en.json (Polish)

* New translations en.json (Japanese)

* New translations en.json (Japanese)

* New translations en.json (Chinese Simplified)
2025-05-20 09:04:31 -05:00
TrèsAbhi e68b27bac4 fix: don't return on bot 2025-05-19 17:39:02 -05:00
tresabhi d49eef7308 feat: update uasset 2025-05-19 12:47:49 -05:00
tresabhi 6e630a91aa Merge branch 'main' of https://github.com/tresabhi/blitzkit 2025-05-19 12:47:37 -05:00
tresabhi 2c66e79daf Merge branch 'preview' 2025-05-19 12:47:32 -05:00
TrèsAbhi 9a94a4e371 Update rating-na.yml 2025-05-18 19:00:33 -05:00
TrèsAbhi b1372c998d Update rating-eu.yml 2025-05-18 18:59:44 -05:00
TrèsAbhi 4ed93cb38f Update rating-asia.yml 2025-05-18 18:59:15 -05:00
TrèsAbhi 4d1b175c07 Update averages.yml 2025-05-18 18:58:37 -05:00
TrèsAbhi ecfedc32ad Update assets.yml 2025-05-18 18:58:01 -05:00
TrèsAbhi 9da514c329 Update assets.yml 2025-05-18 11:38:41 -05:00
tresabhi ee5386c925 fix: secrets 2025-05-18 09:58:38 -05:00
tresabhi 1a90e9312d feat: logs 2025-05-18 09:55:02 -05:00
tresabhi 2c7082fe04 feat: fast assets 2025-05-18 09:54:12 -05:00
tresabhi 518a83ee73 fix: bruh what 2025-05-16 23:03:56 -05:00
tresabhi 25c40bcea2 feat: logs 2025-05-16 22:33:31 -05:00
tresabhi f88ac8daf7 feat: more heuristic formats 2025-05-16 22:23:58 -05:00
tresabhi c4be6b78b6 feat: heuristics 2025-05-16 22:23:34 -05:00
tresabhi 2cfc65a990 feat: migrate tanks 2025-05-16 22:11:01 -05:00
tresabhi a8eb751048 feat: migrate tanks search page 2025-05-16 19:20:29 -05:00
tresabhi f030fd53b1 feat: slugs 2025-05-16 19:14:56 -05:00
tresabhi da506083c9 feat: migrate tier list 2025-05-16 18:15:27 -05:00
tresabhi 8bdc4d9b1b fix: sessio 2025-05-16 18:13:57 -05:00
tresabhi e0718d9ebb fix: migrate performance 2025-05-16 18:12:19 -05:00
tresabhi dbf1718d39 fix: migrate guess 2025-05-16 18:09:33 -05:00
tresabhi 84dd13c0ac feat: rename to path 2025-05-16 18:07:15 -05:00
tresabhi fdecef5f9c feat: migrate gallery 2025-05-16 18:06:04 -05:00
tresabhi 198e800958 feat: remove rybbit 2025-05-16 18:02:08 -05:00
tresabhi 29bb8ada06 feat: reduced navbar shadow 2025-05-16 18:01:10 -05:00
tresabhi 662f116afe fix: move guess components to src/components 2025-05-16 18:00:08 -05:00
tresabhi 02f0c078a5 fix: legacy redirect embed pages 2025-05-16 00:07:43 -05:00
tresabhi 4dd1113b6d feat: migrate embed and fix embed not rendering because of no context 2025-05-16 00:03:47 -05:00
tresabhi e656b83719 feat: migrate compare 2025-05-15 23:54:07 -05:00
tresabhi b90b355f8f feat: migrate urls 2025-05-15 23:44:20 -05:00
tresabhi 165a9cd719 feat: logs 2025-05-15 23:31:26 -05:00
tresabhi 5b8da36fb8 feat: effective and persistent angle 2025-05-15 23:21:01 -05:00
tresabhi bbe682fa73 feat: bigger icons 2025-05-15 23:03:10 -05:00
tresabhi af08df1526 feat: ricochet visualizer shell switcher 2025-05-15 23:02:15 -05:00
tresabhi 47a60adac7 feat: disable reload visualizer 2025-05-15 22:46:17 -05:00
tresabhi 10f3bc0026 fix: compare labels 2025-05-15 22:45:09 -05:00
tresabhi e7da1d582f fix: use pitch angle of transition of 15deg 2025-05-15 22:40:09 -05:00
tresabhi df70816f7e fix: use svh 2025-05-15 22:37:58 -05:00
tresabhi 091364d7f3 Merge branch 'main' into dev 2025-05-15 22:35:42 -05:00
tresabhi 753c06eee8 feat: logs 2025-05-07 20:54:24 -05:00
tresabhi 17713243b5 feat: final stuff 2025-05-07 20:51:52 -05:00
tresabhi 4e4f070368 feat: shinier 2025-05-07 19:43:40 -05:00
tresabhi 4064f163ec feat: remove plug 2025-05-07 19:42:49 -05:00
tresabhi b911f64847 feat: out animation 2025-05-07 19:41:47 -05:00
tresabhi b86b6e09d0 feat: font 2025-05-07 19:32:18 -05:00
tresabhi d2e7a477bb feat: fullscreen 2025-05-07 19:16:10 -05:00
tresabhi f9c182e68c feat: hide guess 2025-05-06 21:30:35 -05:00
tresabhi 7c497ab681 feat: more tintgs 2025-05-06 13:59:41 -05:00
tresabhi c840086c11 feat: tints 2025-05-06 13:56:56 -05:00
tresabhi 8649d09a94 feat: shadow 2025-05-06 13:39:07 -05:00
tresabhi c764564a96 feat: outline title 2025-05-06 13:29:40 -05:00
tresabhi ad8cbb96b7 feat: nerd time 2025-05-05 22:54:39 -05:00
tresabhi b409e26ca5 feat: reveal titles 2025-05-05 21:41:45 -05:00
tresabhi fbbfa5307c feat: skip 2025-05-05 21:22:05 -05:00
tresabhi 4165533838 feat: simplier hiding 2025-05-05 21:05:46 -05:00
tresabhi fb47ab3e64 feat: new auto rotate animation 2025-05-04 22:11:41 -05:00
tresabhi 57971453ad feat: stats 2025-05-04 21:45:38 -05:00
tresabhi c8cb490b48 feat: full logic 2025-05-04 21:39:09 -05:00
tresabhi 232f24181f feat: some guessing mechanics 2025-05-04 21:02:50 -05:00
tresabhi 46441d74d8 feat: some baswork 2025-05-04 13:08:28 -05:00
tresabhi a2274a6f0c idk man 2025-05-04 11:24:33 -05:00
tresabhi 0eb2827e7b Merge branch 'main' into dev 2025-05-02 21:28:54 -05:00
TrèsAbhi 41cb3f8511 Update Party3.astro 2025-05-02 09:32:22 -05:00
tresabhi 9405dccee9 feat: even more robust 2025-04-29 16:36:59 -05:00
tresabhi 83b0c6abc3 feat: persistent shells 2025-04-29 16:23:22 -05:00
tresabhi bf1621001b feat: more glow 2025-04-29 00:11:27 -05:00
tresabhi 8a5a4bf696 feat: glow and shells 2025-04-29 00:10:42 -05:00
tresabhi a99cce07dc feat: background 2025-04-28 17:43:29 -05:00
tresabhi d8c26ba24a feat: begennings of reload visuals 2025-04-28 17:21:42 -05:00
imgbot[bot]andImgBotApp f0b393c48f [ImgBot] Optimize images (#152)
*Total -- 1,028.23kb -> 900.50kb (12.42%)

/packages/website/public/assets/images/3d/grid.png -- 610.88kb -> 490.28kb (19.74%)
/packages/website/public/assets/images/icons/aim-caret.png -- 1.93kb -> 1.84kb (4.6%)
/packages/website/public/assets/images/icons/xp-multiplier.png -- 4.54kb -> 4.43kb (2.28%)
/packages/website/public/assets/images/third-party/wotb-news.png -- 404.33kb -> 397.41kb (1.71%)
/packages/website/public/assets/images/icons/custom-shell.png -- 6.55kb -> 6.54kb (0.18%)

Signed-off-by: ImgBotApp <[email protected]>
Co-authored-by: ImgBotApp <[email protected]>
2025-04-26 13:32:03 -05:00
TrèsAbhi d0d205bb93 New Crowdin updates (#150)
* New translations en.json (Ukrainian)

* New translations en.json (Ukrainian)

* New translations en.json (Russian)

* New translations en.json (Ukrainian)

* 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 (Chinese Simplified)

* New translations en.json (Russian)

* New translations en.json (Ukrainian)

* New translations en.json (Ukrainian)

* New translations en.json (Ukrainian)

* New translations en.json (Ukrainian)

* 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 (Chinese Simplified)

* New translations en.json (Russian)

* New translations en.json (French)
2025-04-26 13:31:52 -05:00
tresabhi aeaf52d3ac feat: darker view range 2025-04-24 22:06:28 -05:00
tresabhi e05358952a feat: stat search 2025-04-24 22:04:33 -05:00
tresabhi c1000e1608 feat: more responsive 2025-04-24 20:54:24 -05:00
tresabhi 46c86c5352 feat: more decimal places for dimensios 2025-04-24 20:37:31 -05:00
tresabhi 507b503560 feat: remove units and make ranks softer 2025-04-24 20:35:58 -05:00
tresabhi 37ca981d33 feat: more responsive 2025-04-24 20:24:12 -05:00
tresabhi efe6d2b1a5 feat: better navbar background 2025-04-24 20:07:17 -05:00
tresabhi 9f79f9c1e8 fix: no draggable images 2025-04-22 22:01:36 -05:00
tresabhi 38b4c62777 feat: new maps 2025-04-22 21:56:39 -05:00
tresabhi df1bc56a4c feat: isolate stat sections 2025-04-22 21:22:26 -05:00
tresabhi c3d2b063bc feat: new ricochet visualizer 2025-04-22 21:10:22 -05:00
tresabhi 1b94748668 feat: start of ricochet visual 2 2025-04-22 16:35:49 -05:00
tresabhi f4f804727f feat: slicker design 2025-04-22 14:42:04 -05:00
tresabhi 3729072594 feat: prettier 2025-04-22 14:36:31 -05:00
tresabhi c174f32e15 feat: view range visualizer 2025-04-22 14:35:34 -05:00
tresabhi 2443ecde2f feat: turretless support 2025-04-22 13:10:01 -05:00
tresabhi 9c027b3dde feat: puncture hole 2025-04-22 01:06:38 -05:00
tresabhi 22d794ac65 feat: animations 2025-04-22 00:50:14 -05:00
tresabhi 35073c184e test 2025-04-21 18:39:28 -05:00
tresabhi 4741406e4a fix: include prisma only in bot 2025-04-21 18:30:35 -05:00
tresabhi 24107433c6 fix: use built prisma 2025-04-21 18:24:19 -05:00
tresabhi 05c6fcfb34 fix: inline 2025-04-21 18:21:12 -05:00
tresabhi be7a16d399 feat: lower contrast 2025-04-21 18:19:05 -05:00
tresabhi c7f781dbb2 feat: ricochet visualizer 2025-04-21 18:09:57 -05:00
tresabhi a66c6ebb27 feat: classic styled components 2025-04-21 17:01:32 -05:00
tresabhi 83aeca2e5e Merge branch 'main' into dev 2025-04-21 16:39:49 -05:00
tresabhi 89a3db05d8 Merge branch 'main' of https://github.com/tresabhi/blitzkit 2025-04-21 16:39:08 -05:00
tresabhi 6050145b16 fix: home page hot tanks 2025-04-21 16:39:06 -05:00
tresabhi 77c78741b3 fix: remove unused type 2025-04-21 16:37:29 -05:00
TrèsAbhi f35670d6a8 Update Party3.astro 2025-04-21 14:36:24 -05:00
tresabhi 3392a57480 feat: rotation visualize 2025-04-20 23:00:08 -05:00
tresabhi ed64ee8228 feat: hull traverse visualizer 2025-04-20 22:51:53 -05:00
tresabhi 4f47d6ce85 Merge branch 'main' into dev 2025-04-20 21:35:06 -05:00
tresabhi 92d56ac086 fix: onPointerDown 2025-04-20 21:34:57 -05:00
tresabhi 671b8b60b9 feat: update prisma with custom output 2025-04-20 21:31:15 -05:00
tresabhi ba5a5245d9 feat: update prisma 2025-04-20 21:27:00 -05:00
tresabhi eee262add7 fix: submodules 2025-04-20 21:18:54 -05:00
tresabhi e5eaf3fd75 fix: submodules 2025-04-20 21:18:02 -05:00
tresabhi 5fdc9044ca fix: hero interactibility 2025-04-20 21:16:50 -05:00
tresabhi 0924a77776 fix: mas width for meta section 2025-04-20 21:13:40 -05:00
tresabhi 1a46e176f5 fix: tankopedia title 2025-04-20 21:11:16 -05:00
tresabhi efb8df9769 feat: logs 2025-04-20 21:00:57 -05:00
tresabhi 04e09b84ff feat: skeleton and proper file extensions 2025-04-20 20:49:54 -05:00
tresabhi b52c107af9 feat: parrelel api 2025-04-19 00:48:29 -05:00
tresabhi c5e6f5e2eb feat: glossary cron 2025-04-19 00:47:28 -05:00
tresabhi 365c68aa4d feat: move popular tanks to cron job 2025-04-18 23:57:04 -05:00
tresabhi ff9ec55da6 feat: increase title gap 2025-04-17 13:06:40 -05:00
tresabhi 2a2994ae69 feat: more responsive 2025-04-17 13:00:53 -05:00
tresabhi e3c6497d29 feat: better meta 2025-04-17 00:23:01 -05:00
tresabhi bdc013b4c8 fix: cleaner clear button 2025-04-16 23:34:30 -05:00
tresabhi e20d65d218 feat: darker 2025-04-16 23:22:12 -05:00
tresabhi 8b726c12d9 fix: invalidate frams on reveal 2025-04-16 23:20:44 -05:00
tresabhi da704e6181 feat: transition skeleton 2025-04-16 23:18:24 -05:00
tresabhi 64d346ccb6 feat: in-hero controls 2025-04-15 16:54:36 -05:00
tresabhi c090ad6492 feat: disturb on tab change and no title select 2025-04-15 16:37:10 -05:00
tresabhi 8b6610151e fix: keys 2025-04-15 16:29:14 -05:00
tresabhi 545b655d4e feat: slicker loading 2025-04-15 00:35:12 -05:00
tresabhi 0e6aa734cb feat: advanced jsxTree 2025-04-15 00:11:49 -05:00
tresabhi 2bef96475c feat: quick controls 2025-04-14 23:56:29 -05:00
tresabhi 45938c875f feat: skeleton search 2025-04-14 23:28:35 -05:00
tresabhi 9d916a588d feat: cooler header 2025-04-14 19:26:41 -05:00
tresabhi f0295079cc feat: quiz input angles animaiton 2025-04-14 18:56:02 -05:00
tresabhi 6d8387fa9d feat: foggy transition 2025-04-14 18:52:49 -05:00
tresabhi 7a18de6650 fix: don't reset camera on tab change 2025-04-14 17:30:51 -05:00
tresabhi cc6c4faa2d feat: migration 2025-04-06 22:57:35 -05:00
TrèsAbhi 4253cd3e1b fix: allow 2025-04-06 22:04:01 -05:00
TrèsAbhi 75cf827082 Update website.yml 2025-04-06 19:59:27 -05:00
TrèsAbhi 73c8807f14 no, no, no, NOW all hail pyogenics i messed up the last time 2025-04-06 16:03:04 -05:00
TrèsAbhi 9fac6dd40d all hail pyogenics 2025-04-06 15:53:23 -05:00
TrèsAbhi c06c338536 fix: add env and remove compressors 2025-04-06 10:48:08 -05:00
TrèsAbhi 9258623931 Update astro.config.mjs 2025-04-06 00:12:01 -05:00
tresabhi 3b02c25973 fix: disable playformCompress 2025-04-05 22:02:26 -05:00
tresabhi f90768d155 fix: env 2025-04-05 17:08:55 -05:00
tresabhi d77bd6a5f9 feat: logs 2025-04-05 16:15:55 -05:00
tresabhi 6836cb327f feat: more images 2025-04-05 16:07:42 -05:00
tresabhi 4373efd0cd feat: move images to blitzkit 2025-04-05 16:00:52 -05:00
tresabhi f13165e77d feat: better ui 2025-04-05 15:09:15 -05:00
tresabhi 8a5b0c1170 fix: gun-specific shell discrimination 2025-04-05 15:09:12 -05:00
tresabhi f16511a72e Merge branch 'main' into dev 2025-04-05 14:57:41 -05:00
TrèsAbhi 76f7ae45ce Update index.astro 2025-04-04 21:27:42 -05:00
TresAbhi ad2aaa7bf8 feat: some sketchy code 2025-04-05 02:07:35 +00:00
TresAbhi 2a4375c718 Merge branch 'main' into preview 2025-04-04 20:26:22 +00:00
tresabhi df0dc4f118 feat: shell filters 2025-04-03 16:33:48 -05:00
tresabhi 92399de5e2 fix: navbar interact 2025-04-03 13:00:23 -05:00
tresabhi 8d9dff8d73 feat: inline 2025-04-03 12:53:54 -05:00
tresabhi f91744f2f0 feat: compressor again 2025-04-03 12:51:24 -05:00
tresabhi e946dfdb69 feat: compressor 2025-04-03 12:48:17 -05:00
tresabhi 6283ddc57a feat: astro seo 2025-04-03 12:45:33 -05:00
tresabhi 2d66e0ab28 fix: missing padding backgrond 2025-04-03 12:34:02 -05:00
tresabhi c3d8f15a2a feat: better robots.txt 2025-04-03 12:23:53 -05:00
tresabhi 21a37ec6cc feat: new nav bar 2025-04-03 12:15:08 -05:00
tresabhi de471400ad feat: partytown 2025-04-03 11:34:57 -05:00
tresabhi 0b16295853 fix: keys 2025-04-03 11:31:31 -05:00
TrèsAbhi 63eea81856 Update stitches.config.ts 2025-04-02 21:55:08 -05:00
tresabhi ba57d61af5 feat: skeleton 2025-04-02 17:59:23 -05:00
tresabhi 3d940f4b47 feat: discord plug 2025-04-02 17:48:51 -05:00
tresabhi 8ef355c574 feat: we don't talk about it 2025-04-02 16:11:09 -05:00
tresabhi 71e1ab1362 aaaaaaaaaaaaaaaa 2025-04-01 19:38:58 -05:00
tresabhi 53dc108356 Merge branch 'main' of https://github.com/tresabhi/blitzkit 2025-04-01 19:38:01 -05:00
tresabhi 57b3218c5a undo cue4 2025-04-01 19:38:00 -05:00
tresabhi 7472e39faf fix: gifs 2025-04-01 19:34:03 -05:00
tresabhi 034417d19f fix: remove cue4 2025-03-31 17:49:23 -05:00
tresabhi 0368e9ceda feat: pink bot 2025-03-31 17:44:08 -05:00
tresabhi 28f365f762 fix: no class list 2025-03-31 17:39:42 -05:00
tresabhi 40a81f6b1b fix: class list 2025-03-31 17:39:01 -05:00
tresabhi 9475d22472 feat: uh hahaha you'll see ... again! 2025-03-31 17:36:40 -05:00
tresabhi 9538a03afb feat: uh hahaha you'll see 2025-03-31 16:54:32 -05:00
tresabhi d5d171bea6 4 2025-03-30 14:37:40 -05:00
tresabhi a9c9a90ac1 fix: disable /auction 2025-03-30 12:53:14 -05:00
tresabhi f7c562f8b6 fix: detect invalid 0-length translations 2025-03-30 12:51:34 -05:00
TrèsAbhi 836cf69f01 New Crowdin updates (#149)
* New translations en.json (Russian)

* New translations en.json (Ukrainian)

* New translations en.json (Spanish)

* New translations en.json (Chinese Simplified)
2025-03-26 14:37:29 -05:00
tresabhi ba17aae706 feat: bump int size 2025-03-25 15:29:41 -05:00
TrèsAbhi 30ca5c053a Update tankNames.ts 2025-03-14 12:14:55 -05:00
tresabhi c36419f5b6 fix: don't include cue4 2025-03-11 10:40:02 -05:00
tresabhi 1879895db2 feat: add 124 support 2025-03-11 10:13:02 -05:00
tresabhi c964e3c8ec feat: google crawler 2025-03-03 17:49:45 -06:00
tresabhi 2ef6d18f82 lol why cue4??? 2025-03-03 16:19:43 -06:00
tresabhi 10b672572b feat: try appending dvpl? 2025-03-03 16:15:58 -06:00
TrèsAbhi 2a4efa8cd2 fix: check for changes for real 2025-03-03 12:29:25 -06:00
TrèsAbhi 5a8435da29 Update Footer.astro 2025-03-03 12:15:05 -06:00
TrèsAbhi 3461349a96 Update Footer.astro 2025-03-03 12:13:06 -06:00
TrèsAbhi 3791e9df12 New Crowdin updates (#147)
* New translations en.json (French)

* New translations en.json (French)

* New translations en.json (Ukrainian)

* New translations en.json (Ukrainian)

* New translations en.json (Portuguese)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (French)

* New translations en.json (French)

* New translations en.json (Ukrainian)

* New translations en.json (French)

* New translations en.json (French)

* New translations en.json (Ukrainian)

* New translations en.json (Ukrainian)

* New translations en.json (Japanese)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Portuguese)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Russian)

* New translations en.json (Ukrainian)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Russian)
2025-03-03 11:10:15 -06:00
TrèsAbhi b3c36aac93 New Crowdin updates (#146)
* New translations en.json (Polish)

* New translations en.json (Polish)

* New translations en.json (Japanese)

* New translations en.json (Japanese)

* New translations en.json (Japanese)

* New translations en.json (Japanese)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Ukrainian)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Spanish)

* New translations en.json (Ukrainian)

* New translations en.json (Ukrainian)

* New translations en.json (Portuguese)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (Polish)

* New translations en.json (Polish)

* New translations en.json (Spanish)

* New translations en.json (Japanese)

* New translations en.json (Chinese Simplified)

* New translations en.json (Japanese)

* New translations en.json (Japanese)

* New translations en.json (Ukrainian)

* New translations en.json (Polish)

* New translations en.json (Chinese Simplified)

* New translations en.json (Ukrainian)

* New translations en.json (Ukrainian)

* New translations en.json (Russian)

* New translations en.json (French)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Ukrainian)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Ukrainian)

* New translations en.json (Ukrainian)

* New translations en.json (Portuguese)

* New translations en.json (Portuguese)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Portuguese)

* New translations en.json (Portuguese)

* New translations en.json (Portuguese)

* New translations en.json (Chinese Simplified)
2025-02-03 11:07:41 -06:00
TrèsAbhi c0895409bc Update index.ts 2025-01-24 11:27:45 -06:00
TrèsAbhi 438b565cbb New Crowdin updates (#145) 2025-01-19 13:00:15 -06:00
tresabhi cf1a82e89b fix: remove default 2025-01-19 12:51:26 -06:00
tresabhi 0506e361b2 feat: more prune 2025-01-19 12:46:57 -06:00
tresabhi d6a1357a82 Merge branch 'main' into dev 2025-01-19 12:41:06 -06:00
tresabhi a5a1dbd51b fix: [Oo] for /debug 2025-01-18 14:16:49 -06:00
tresabhi 5201350e44 fix: [Oo] for /stats 2025-01-18 14:16:26 -06:00
tresabhi 6a84b9d5ae fix: stricter pattern 2025-01-18 14:07:47 -06:00
tresabhi cf04fe21b3 fix: logs 2025-01-18 13:59:14 -06:00
tresabhi 84eb3c2da3 fix: version 2025-01-18 13:49:37 -06:00
tresabhi 22bf2dc1d8 feat: logs 2025-01-18 13:49:18 -06:00
tresabhi b9c339daa7 fix: search keys 2025-01-18 13:47:31 -06:00
tresabhi ef3006cac1 fix: double full stops 2025-01-18 13:42:52 -06:00
TrèsAbhi e482fd40c1 Update applyPatches.ts 2025-01-18 11:41:45 -06:00
TrèsAbhi cc4569966e Update applyPatches.ts 2025-01-18 09:19:38 -06:00
tresabhi 3708a3ced7 fix: handle deleted tanks 2025-01-17 20:29:33 -06:00
tresabhi 0ae59c2fd7 fix: check dir exists 2025-01-17 20:15:09 -06:00
tresabhi 7027f63236 feat: prune 2025-01-17 19:56:46 -06:00
TrèsAbhi 6725e79021 New Crowdin updates (#140) 2025-01-17 19:36:39 -06:00
tresabhi 5ca3cee9da feat: verbose 2025-01-17 19:22:02 -06:00
tresabhi 602a5a1e99 fix: move env 2025-01-17 19:19:45 -06:00
tresabhi bfbf3bdc39 fix: no double dvpl 2025-01-17 19:17:39 -06:00
tresabhi 644686582e fix: icon api 2025-01-17 19:06:07 -06:00
TrèsAbhi 1c00bc81f8 New Crowdin updates (#139)
* 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 (Portuguese)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Chinese Simplified)
2025-01-17 12:46:05 -06:00
TrèsAbhi ac3b69bec5 Update assets.yml 2025-01-17 12:45:12 -06:00
TrèsAbhi 3e14c943b6 Update assets.yml 2025-01-16 23:05:13 -06:00
TrèsAbhi d33633be2c Update assets.yml 2025-01-16 22:15:24 -06:00
TrèsAbhi 4bf70d02d0 Update Crowdin configuration file 2025-01-16 20:48:29 -06:00
tresabhi 377bcf0e38 fix: env 2025-01-16 20:44:55 -06:00
tresabhi 884b6d4318 fix: run tests even on dev 2025-01-16 20:36:49 -06:00
tresabhi ee94d762d0 fix: paths 2025-01-16 20:34:28 -06:00
tresabhi bcde319ac1 feat: move docs 2025-01-16 20:23:39 -06:00
tresabhi a4828774ca feat: translate compare 2025-01-16 20:14:34 -06:00
tresabhi e59725d763 feat: logs 2025-01-16 19:59:24 -06:00
tresabhi a62179a853 Merge branch 'main' into dev 2025-01-16 19:58:31 -06:00
TrèsAbhi 38ba940a98 New Crowdin updates (#138) 2025-01-16 19:56:42 -06:00
tresabhi a07d6c775e fix: remove writefile 2025-01-16 19:55:18 -06:00
tresabhi f9f7468544 feat: replace $ 2025-01-16 14:22:30 -06:00
tresabhi 98724df711 feat: update bot chunk 4 2025-01-16 14:10:28 -06:00
tresabhi 5499cb5cca feat: update bot chunk 3 2025-01-16 13:53:29 -06:00
tresabhi 122b3f6478 feat: update bot chunk 2 2025-01-16 13:48:13 -06:00
tresabhi d2b24fc953 feat: update bot chunk 1 2025-01-15 20:14:05 -06:00
tresabhi bb557a7dd7 feat: use thing 2025-01-15 14:21:27 -06:00
tresabhi f1612e8fe3 feat: path 2025-01-15 14:16:39 -06:00
tresabhi d12f4fb00e feat: logs 2025-01-15 14:06:28 -06:00
tresabhi ad555ece9a fix: warning 2025-01-15 14:00:06 -06:00
tresabhi 9aee8feaf4 feat: translate embeds 2025-01-15 13:55:24 -06:00
tresabhi baa16840df feat: footer lang 2025-01-15 13:10:57 -06:00
tresabhi 4e80490fd4 feat: localize sessin 2025-01-14 20:32:17 -06:00
tresabhi 6233c16b88 feat: fixed localize tankopedia chars 2025-01-14 20:19:37 -06:00
tresabhi c4f1d9f3b0 feat: localize tankopedia chars 2025-01-14 20:13:38 -06:00
tresabhi 2c365cdea0 feat: localize tankopedia config 2025-01-14 19:37:03 -06:00
tresabhi 18afa4cd28 feat: localize tankopedia reviews 2025-01-14 19:28:14 -06:00
tresabhi ef9778ea96 feat: localize game assets 2025-01-14 19:22:41 -06:00
tresabhi d8459d373c feat: start of localized proto strings 2025-01-14 14:38:41 -06:00
tresabhi 7900c61a9b feat: localize tankopedia tech tree 2025-01-14 14:13:00 -06:00
tresabhi ceb7842d49 feat: localize tankopedia meta section 2025-01-14 14:04:46 -06:00
tresabhi e0c897025e feat: localize tankopedia sandbox 2025-01-13 20:56:30 -06:00
tresabhi ade6ec8af1 feat: localize tank search 2025-01-13 20:06:43 -06:00
tresabhi 06cbe1e733 feat: localize language 2025-01-10 20:46:15 -06:00
tresabhi cb3e759f75 feat: remove redundant /en 2025-01-10 20:38:28 -06:00
tresabhi e4558d978c fix: tool og strings 2025-01-10 20:30:08 -06:00
tresabhi 260d819c77 feat: auto prefixor 2025-01-10 14:37:29 -06:00
tresabhi ffe58de179 Merge branch 'main' into dev 2025-01-10 14:04:54 -06:00
tresabhi 6fbe1d6339 feat: locales 2025-01-10 14:04:42 -06:00
TrèsAbhi 835d03d12c Delete packages/core/lang/hr.json 2025-01-10 14:00:38 -06:00
TrèsAbhi 2b208fa6a0 New Crowdin updates (#137) 2025-01-10 13:59:59 -06:00
tresabhi 66d63d5c60 Merge branch 'main' into dev 2025-01-10 13:49:28 -06:00
tresabhi 134566f1d8 feat: hide version... correctly 2025-01-10 12:06:20 -06:00
tresabhi d592a95cae feat: hide on preview 2025-01-10 11:56:00 -06:00
tresabhi 4f74556eb6 feat: hide version 2025-01-10 11:53:14 -06:00
tresabhi 1342f434a3 feat: desc 2025-01-10 11:14:43 -06:00
TrèsAbhi 6110471990 Update index.astro 2025-01-10 11:03:55 -06:00
TrèsAbhi 359eec5209 Update package.json 2025-01-10 10:46:57 -06:00
tresabhi 6ccffa0458 feat: enable vercel 2025-01-10 10:38:02 -06:00
tresabhi 4c0c10e39e feat: hide hot tanks 2025-01-10 10:12:31 -06:00
tresabhi 3d41c56c73 fix: use en 2025-01-10 10:10:59 -06:00
TrèsAbhi e0dda567f4 Update credits.md 2025-01-07 23:16:07 -06:00
tresabhi 91b367cd70 feat: start of tankopedia translation 2025-01-07 13:59:33 -06:00
tresabhi 5a65fee1d1 feat: translate tank search 2025-01-07 13:50:06 -06:00
tresabhi c7856364ad fix: some linting 2025-01-07 13:20:20 -06:00
tresabhi 65899a06c4 feat: move docs up 2025-01-07 13:12:22 -06:00
tresabhi 571ae81306 feat: translate tier list 2025-01-07 13:09:05 -06:00
tresabhi 6b2dda0f9d feat: translate gallery 2025-01-07 13:00:43 -06:00
tresabhi 9de41529d1 feat: localize performance 2025-01-07 12:47:55 -06:00
tresabhi 7dfdf4c11b feat: start of perforamnce 2025-01-06 20:31:21 -06:00
tresabhi db4132f666 feat: better lang selector 2025-01-06 20:14:33 -06:00
tresabhi d9688f6051 feat: homepage 2025-01-06 20:09:11 -06:00
TrèsAbhi 12e89a6b65 Update credits.md 2025-01-05 11:39:25 -06:00
TrèsAbhi 2d524cd273 Update Crowdin configuration file 2025-01-05 09:14:29 -06:00
tresabhi 1d44ecab18 Merge branch 'main' into dev 2025-01-03 12:36:02 -06:00
tresabhi bf9addf589 feat: iteration 2025-01-03 12:32:03 -06:00
tresabhi 4deee2d709 feat: update locales 2025-01-03 12:15:14 -06:00
tresabhi 309fda1ed1 feat: translators 2025-01-03 12:08:03 -06:00
tresabhi d6e7f35214 feat: translators 2025-01-02 19:43:13 -06:00
TrèsAbhi 7cd9131b60 Update assets.yml 2024-12-30 11:42:48 -06:00
tresabhi 599b81934e feat: simplification 2024-12-29 20:19:51 -06:00
tresabhi 3f595d0d8a Merge branch 'main' into dev 2024-12-29 20:07:12 -06:00
TrèsAbhi 9afed23bb8 feat: simplify 2024-12-29 20:06:34 -06:00
TrèsAbhi fc869f8b45 New Crowdin updates (#136) 2024-12-29 20:05:52 -06:00
TrèsAbhi cf7ba1c6b5 New translations en-us.json (Portuguese) (#135) 2024-12-29 20:04:04 -06:00
TrèsAbhi 07ca41e82a Merge pull request #134 from tresabhi/l10n_main
New Crowdin updates
2024-12-28 09:07:30 -06:00
tresabhi c487d9af61 feat: disable navbar account 2024-12-27 14:04:14 -06:00
tresabhi 9be6725043 feat: some more localized links 2024-12-27 14:03:43 -06:00
tresabhi e0daf85561 feat: some more localized links 2024-12-27 14:01:59 -06:00
tresabhi 8203db3dc2 feat: some locale links 2024-12-27 13:43:25 -06:00
tresabhi eb5527706e fix: remove dead code 2024-12-27 13:34:39 -06:00
tresabhi 1f577c2317 feat: i18n paths 2024-12-27 13:16:51 -06:00
tresabhi 68362c22b1 fix: imports 2024-12-27 12:59:56 -06:00
tresabhi 844965bcb0 feat: locale redirect 2024-12-27 12:55:52 -06:00
tresabhi 1843eb3bcf feat: move to locale 2024-12-27 12:38:38 -06:00
tresabhi 60ab845326 feat: set up locales 2024-12-26 14:36:48 -06:00
tresabhi 3bbc083b2a feat: logs 2024-12-26 14:10:41 -06:00
tresabhi 8df159b182 feat: i will do this later lol 2024-12-26 14:07:36 -06:00
tresabhi c7238b0342 feat: comments to ease users 2024-12-26 13:56:54 -06:00
tresabhi ba6006a320 feat: smaller title in header 2024-12-26 13:52:04 -06:00
tresabhi 43b9252f21 feat: hide chars 2024-12-26 13:47:51 -06:00
tresabhi d727ceaaf6 feat: more charts! 2024-12-26 13:47:06 -06:00
tresabhi eb95230a5f feat: start of charts 2024-12-23 12:48:52 -06:00
tresabhi b6fd5eaa66 feat: update readme 2024-12-23 12:37:12 -06:00
tresabhi 117f9892d6 feat: start of charts 2024-12-22 20:34:16 -06:00
tresabhi c20caf45a2 feat: rounding in blitzkit embeds 2024-12-22 20:30:08 -06:00
tresabhi 670b678348 feat: increase brightness of pbr tanks 2024-12-22 20:28:07 -06:00
tresabhi b1f0d4d844 feat: readd lobby lighting for static mode 2024-12-22 20:27:36 -06:00
tresabhi e9d1c82a60 feat: added option to hide tank under dynamic armor 2024-12-22 20:25:05 -06:00
tresabhi cc4345f1a3 fix: only show blue if 3rc saves the day 2024-12-22 20:21:23 -06:00
tresabhi 6d8326861b feat: background 2024-12-19 18:10:00 -06:00
tresabhi 8517ecfdb6 fix: app armor? 2024-12-19 13:26:25 -06:00
tresabhi a38b5d7ddd fix: remove show environment 2024-12-19 11:32:53 -06:00
tresabhi f726793819 the worst code i have ever written in my life 2024-12-19 11:27:35 -06:00
tresabhi 19531aa9ae feat: bring back good ol' 2024-12-19 11:22:38 -06:00
tresabhi 6d6bae847b feat: crowdin 2024-12-19 11:18:05 -06:00
tresabhi cc70b3ccc3 feat: logs 2024-12-19 11:16:27 -06:00
tresabhi e3bf619808 fix: remove comments 2024-12-19 11:09:32 -06:00
tresabhi f6cb4f2518 more adrenaline 2024-12-19 11:09:13 -06:00
tresabhi f396d6c4a1 fix: use stricter localizaiton 2024-12-19 11:04:48 -06:00
tresabhi 768c420ae4 Merge branch 'main' into dev 2024-12-19 10:41:46 -06:00
TrèsAbhi 7fba197bef Merge pull request #133 from tresabhi/l10n_main
New Crowdin updates
2024-12-19 10:41:26 -06:00
tresabhi b074481e1a fix: performance tool use performance 2024-12-19 10:29:32 -06:00
tresabhi 987e35ef8f feat: sticky headers for performance 2024-12-19 10:28:44 -06:00
tresabhi 83c77b5945 fix: make tank loader icon smaller 2024-12-19 10:20:09 -06:00
tresabhi e4f0120d67 feat: use blitzkit icons by default 2024-12-19 10:19:26 -06:00
tresabhi 2055c00154 Merge branch 'main' into dev 2024-12-17 21:49:58 -06:00
tresabhi c814230dcb feat: better loader 2024-12-17 21:49:55 -06:00
tresabhi 2f31aadb29 feat: better text in header 2024-12-17 21:46:44 -06:00
tresabhi 3113bf3a47 fix: filters 2024-12-17 21:06:11 -06:00
tresabhi 70bd54e03b feat: remove naked lighting 2024-12-17 19:45:48 -06:00
tresabhi 8863ac5fde feat: some icon only lighting 2024-12-17 11:12:04 -06:00
TrèsAbhi b62009ce2b Update Party3.astro 2024-12-17 09:58:49 -06:00
tresabhi 0f3d230f42 feat: better sizing for new icons 2024-12-16 23:42:38 -06:00
tresabhi 9db6b997c3 fix: use tanks key 2024-12-16 22:44:02 -06:00
tresabhi d9fcbd028b fix: re add blitzkit tank icons 2024-12-16 22:42:27 -06:00
tresabhi db74058075 feat: new angle 2024-12-16 22:40:36 -06:00
tresabhi 4180736d37 feat: neater code 2024-12-16 13:32:31 -06:00
tresabhi 382f04229e feat: better icons and sizing 2024-12-16 13:30:35 -06:00
tresabhi 3c9ba9ec53 feat: increased max recently viewed tank count to 14 2024-12-16 13:21:06 -06:00
tresabhi 4848234264 feat: custom tank icons 2024-12-16 12:41:24 -06:00
tresabhi 2ad0fcfdf9 feat: better hdr 2024-12-16 12:24:37 -06:00
tresabhi 8f0894364b fix: remove nitro ads 2024-12-16 11:01:07 -06:00
tresabhi 3695b8f7f4 fix: tag 2024-12-16 10:59:26 -06:00
tresabhi 4f8c1d416f feat: delete rando 2024-12-16 10:50:52 -06:00
tresabhi 453286677a Merge branch 'l10n_main' into dev 2024-12-16 10:49:54 -06:00
TrèsAbhi 53a49f199a New translations en-us.json (Croatian) 2024-12-16 10:49:28 -06:00
TrèsAbhi fdfde46342 New translations en-us.json (Ukrainian) 2024-12-16 10:49:26 -06:00
TrèsAbhi 1d836b2053 New translations en-us.json (Russian) 2024-12-16 10:49:25 -06:00
tresabhi 6d448dc5c4 feat: new languages 2024-12-16 10:49:18 -06:00
tresabhi 75d4d60b30 Merge branch 'main' into l10n_main 2024-12-16 10:44:05 -06:00
tresabhi e7f47fd2fd feat: handle no next price 2024-12-16 10:41:10 -06:00
tresabhi 50a3d1e922 feat: logs 2024-12-16 10:34:14 -06:00
tresabhi e296267ccd feat: add cpr 2024-12-16 10:30:05 -06:00
tresabhi d9aca6a3ed fix: include .git 2024-12-15 19:35:25 -06:00
tresabhi d07a6e954e fix: remove dead code 2024-12-15 19:30:22 -06:00
tresabhi 6bcaf685f8 fix: names 2024-12-15 19:26:58 -06:00
tresabhi 066f17a2f3 fix: concurrent only on main 2024-12-15 19:26:36 -06:00
tresabhi 374d6c22f9 feat: use artifact 2024-12-15 19:25:13 -06:00
tresabhi 55927258ca feat: use better emoji 2024-12-15 19:08:31 -06:00
tresabhi bb99220497 feat: /auction strikethrough and warning 2024-12-15 19:06:05 -06:00
tresabhi 4f6b41e264 feat: start work on website deployment 2024-12-15 18:59:20 -06:00
tresabhi f9c4cee30c feat: disable vercel deployments 2024-12-15 18:46:59 -06:00
tresabhi d865043b34 fix: invalidate on advanced highlighting change 2024-12-15 18:39:24 -06:00
tresabhi a4080f21b9 feat: optimize using claude lol 2024-12-15 18:33:15 -06:00
tresabhi 853c520d8a feat: optimize depth sampling 2024-12-15 18:26:34 -06:00
tresabhi d78f8c6ec4 feat: optimize constants 2024-12-15 18:21:03 -06:00
tresabhi 318a71bd32 feat: advanced mode 2024-12-15 18:13:23 -06:00
TrèsAbhi 2458d2c80c New translations en-us.json (Chinese Simplified) 2024-12-15 01:07:40 -06:00
TrèsAbhi 37a72e3bb5 New translations en-us.json (Chinese Simplified) 2024-12-15 00:08:24 -06:00
tresabhi bc9c9ad4e7 feat: start of custom renders 2024-12-14 22:32:28 -06:00
tresabhi b38c732983 fix: missing image backup 2024-12-14 15:46:47 -06:00
TrèsAbhi 415861f6e8 New translations en-us.json (Chinese Simplified) 2024-12-14 15:26:03 -06:00
TrèsAbhi 3124afa253 New translations en-us.json (Ukrainian) 2024-12-14 15:26:02 -06:00
TrèsAbhi 8523b9f44f New translations en-us.json (Russian) 2024-12-14 15:26:01 -06:00
TrèsAbhi 71e0404890 New translations en-us.json (Croatian (disambiguation)) 2024-12-14 15:26:00 -06:00
TrèsAbhi c01fd1f4ff New translations en-us.json (Spanish) 2024-12-14 15:25:59 -06:00
487 changed files with 23840 additions and 9593 deletions

No files matched your search

+32 -3
View File
@@ -29,7 +29,7 @@ concurrency:
env:
DISCORD_CLIENT_ID: ${{ secrets.DISCORD_CLIENT_ID }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
GH_TOKEN: ${{ secrets.ASSET_BOT }}
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
VIDEOS_SHEET: ${{ secrets.VIDEOS_SHEET }}
@@ -39,6 +39,20 @@ env:
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
PUBLIC_ASSET_REPO: ${{ secrets.PUBLIC_ASSET_REPO }}
PUBLIC_ASSET_BRANCH: ${{ github.event.inputs.cdn_branch || secrets.PUBLIC_ASSET_BRANCH }}
WOTB_GLOSSARY_DOMAIN: ${{ secrets.WOTB_GLOSSARY_DOMAIN }}
WOTB_GLOSSARY_PATH: ${{ secrets.WOTB_GLOSSARY_PATH }}
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
ALLOW_ROBOTS: true
PUBLIC_GOOGLE_TAG_MANAGER_ID: ${{ secrets.PUBLIC_GOOGLE_TAG_MANAGER_ID }}
PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ${{ secrets.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID }}
PUBLIC_PATREON_CLIENT_ID: ${{ secrets.PUBLIC_PATREON_CLIENT_ID }}
PUBLIC_PATREON_REDIRECT_URI: ${{ secrets.PUBLIC_PATREON_REDIRECT_URI }}
PATREON_CLIENT_SECRET: ${{ secrets.PATREON_CLIENT_SECRET }}
PUBLIC_NITROPAY_ID: ${{ secrets.PUBLIC_NITROPAY_ID }}
PUBLIC_DEBUG_MISSING_I18N: false
PUBLIC_DISCORD_SERVER_ID: ${{ secrets.PUBLIC_DISCORD_SERVER_ID }}
PUBLIC_ASSET_FAST_USER: ${{ secrets.PUBLIC_ASSET_FAST_USER }}
PUBLIC_ASSET_FAST_REPO: ${{ secrets.PUBLIC_ASSET_FAST_REPO }}
jobs:
download_client:
@@ -106,6 +120,9 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ secrets.GH_TOKEN }}
- name: Setup bun
uses: oven-sh/setup-bun@v1
@@ -113,11 +130,23 @@ jobs:
- name: Install dependencies
run: bun install
- name: Lint
run: bun run lint:website
- name: Download depots
uses: actions/download-artifact@v4
with:
name: depots
path: depots
- name: Build assets
run: bun run build:assets --all-targets --depot --production
- name: AppArmor hack
run: echo 0 | sudo tee /proc/sys/kernel/apparmor_restrict_unprivileged_userns
- name: Start server and build assets
run: |
bun dev:website --verbosepa &
SERVER_PID=$!
bun run build:assets --all-targets --depot --production
kill $SERVER_PID || true
+2
View File
@@ -12,6 +12,8 @@ concurrency:
env:
PUBLIC_ASSET_REPO: ${{ secrets.PUBLIC_ASSET_REPO }}
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 }}
jobs:
build:
+4 -2
View File
@@ -20,6 +20,8 @@ env:
PUBLIC_ASSET_REPO: ${{ secrets.PUBLIC_ASSET_REPO }}
PUBLIC_ASSET_BRANCH: ${{ secrets.PUBLIC_ASSET_BRANCH }}
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 }}
jobs:
host:
@@ -27,8 +29,8 @@ jobs:
steps:
- uses: oven-sh/setup-bun@v1
# with:
# bun-version: 1.1.33
# with:
# bun-version: 1.1.33
- name: Download release assets
run: |
+2
View File
@@ -25,4 +25,6 @@ jobs:
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 }}
run: bun build:ratingleaderboard --region=asia
+2
View File
@@ -25,4 +25,6 @@ jobs:
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 }}
run: bun build:ratingleaderboard --region=eu
+2
View File
@@ -25,4 +25,6 @@ jobs:
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 }}
run: bun build:ratingleaderboard --region=com
@@ -1,8 +1,7 @@
name: Build Website
name: Website
on:
push:
branches: main
workflow_dispatch:
schedule:
@@ -15,18 +14,23 @@ permissions:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
cancel-in-progress: ${{ github.ref == 'refs/heads/main' }}
env:
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
PUBLIC_ASSET_REPO: ${{ secrets.PUBLIC_ASSET_REPO }}
PUBLIC_ASSET_BRANCH: ${{ secrets.PUBLIC_ASSET_BRANCH }}
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
ALLOW_ROBOTS: true
ALLOW_ROBOTS: 'true'
WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }}
PUBLIC_GOOGLE_TAG_MANAGER_ID: ${{ secrets.PUBLIC_GOOGLE_TAG_MANAGER_ID }}
PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ${{ secrets.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID }}
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_FAST_USER: ${{ secrets.PUBLIC_ASSET_FAST_USER }}
PUBLIC_ASSET_FAST_REPO: ${{ secrets.PUBLIC_ASSET_FAST_REPO }}
jobs:
build:
@@ -52,13 +56,15 @@ jobs:
with:
path: dist/website
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
needs: build
deploy-github-pages:
if: ${{ github.ref == 'refs/heads/main' }}
name: Deploy to GitHub Pages
runs-on: ubuntu-latest
name: Deploy
needs: build
environment:
name: Production
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
+5 -3
View File
@@ -8,7 +8,7 @@ package-lock.json
dist/
dist.*
yarn-error.log
/test.*
test.*
/docs/.vitepress/dist
/docs/.vitepress/cache
servers.csv
@@ -22,7 +22,9 @@ depots
public/sitemap*
/firebase.json
target
# packages/core/src/protos/*
# !packages/core/src/protos/*.proto
# *.ts
/.vercel
/.astro
.vercel
/src
/packages/bot/src/prisma
+3
View File
@@ -1,3 +1,6 @@
[submodule "submodules/blitzkit-closed"]
path = submodules/blitzkit-closed
url = https://github.com/tresabhi/blitzkit-closed
[submodule "submodules/CUE4Parse"]
path = submodules/CUE4Parse
url = https://github.com/FabianFG/CUE4Parse
+16
View File
@@ -14,6 +14,7 @@
"applypatches",
"artefacts",
"astrojs",
"astrolib",
"atgm",
"Autoloader",
"Autoreloader",
@@ -32,9 +33,11 @@
"blitzkit",
"blitzrinth",
"blitzstars",
"bufbuild",
"cacheable",
"Callouts",
"Camo",
"Carro",
"CCPAOPTOUT",
"CDON",
"classname",
@@ -42,6 +45,7 @@
"closedkrieg",
"cockroackdb",
"Coef",
"Combattimento",
"cooldown",
"corelations",
"corsproxy",
@@ -53,6 +57,7 @@
"dbid",
"Ddpf",
"Ddsd",
"deadrail",
"debugflags",
"deburr",
"decalmask",
@@ -71,6 +76,7 @@
"dxgi",
"echarts",
"Everytime",
"FABLEGEAR",
"FASTNAME",
"flexdirection",
"FOURCC",
@@ -104,6 +110,7 @@
"Maddoxkkm",
"malum",
"mantlets",
"MECHA",
"metallicness",
"metalness",
"minitelrose",
@@ -130,17 +137,22 @@
"outdir",
"outfile",
"ownedtanks",
"padder",
"partytown",
"PIXELFORMAT",
"playerachievements",
"playerinfo",
"playform",
"pngjs",
"POSITIVEX",
"POSITIVEY",
"POSITIVEZ",
"precompute",
"PROGRESSTON",
"Promisable",
"proto",
"protobuf",
"protoc",
"protos",
"Pyogenics",
"ratingleaderboard",
@@ -181,6 +193,7 @@
"TEXCOORD",
"tresabhi",
"tungstentip",
"Turretless",
"TYPELESS",
"ubjson",
"Unicum",
@@ -189,10 +202,13 @@
"unpickled",
"Unregistering",
"unrotate",
"unwrapper",
"vercel",
"vickers",
"vitepress",
"Vovko",
"wargaming",
"WARHAMMER",
"webp",
"whitespaces",
"WHSS",
+4 -14
View File
@@ -1,23 +1,13 @@
## Visit the website
## BlitzKit
Your premier source for all things Blitz.
### https://blitzkit.app/
![BlitzKit website](https://i.imgur.com/m8e39Rv.png)
# BlitzKit
[![built with Codeium](https://codeium.com/badges/main)](https://codeium.com)
Tools for everything World of Tanks Blitz
![BlitzKit website](https://i.imgur.com/KaxUxmt.png)
## Invite the bot
Go to [application directory for BlitzKit](https://discord.com/application-directory/1097673957865443370) and click "Add to Server." Voila! You're ready to go.
![BlitzKit application directory](https://i.imgur.com/YWCtOuJ.png)
## Visit the documentation website
### https://tresabhi.github.io/blitzkit/
![BlitzKit documentation website](https://i.imgur.com/hm3KTsF.png)
BIN
View File
Binary file not shown.
+1 -1
View File
@@ -1,3 +1,3 @@
files:
- source: /packages/core/lang/en-US.json
- source: /packages/i18n/strings/en.json
translation: /%original_path%/%locale%.json
+2 -1
View File
@@ -5,6 +5,7 @@ BlitzKit is a one man band **TrèsAbhi** but I am far from solo.
| Role | People |
| ------------------------------ | ------------------------------ |
| Bot verification | S0AP |
| General testing | Prince_NA |
| Bot advertisement platform | S0AP |
| Bot rendering pipeline | Vovko |
| Game asset decompression | Maddox |
@@ -12,7 +13,7 @@ BlitzKit is a one man band **TrèsAbhi** but I am far from solo.
| Game file layout | Aeson |
| 3-caliber rule edge cases | Minitelrose, ChickenMan7777 |
| Embed UX | \_34, HisRoyalFatness, Synx |
| Tankopedia pre-release testing | [OPTML], Aeson, \_34 |
| Tankopedia pre-release testing | [OPTML], Aeson, \_34, xkdkw |
| Website advertisement platform | Aeson |
| Compare pre-release testing | [OPTML] |
| Redacted | GonnaHetzMe |
+15
View File
@@ -0,0 +1,15 @@
# BlitzKit 1.26.4
## Features
- `/auction` now warns if a tank stock is at or below 500 and now strikes through the tank name if its out
- Added advanced armor highlighting mode
- Green: penetration
- Red: blocked
- Pink: ricochet
- Cyan: penetration with 3 calibers rule
- Optimized the armor highlighting for about a 15% increase in frame rate
## Fixes
- Added 5000 doses of adrenaline, incase the BOT DECIDES TO DIE AGAIN FOR NO REASON
+19
View File
@@ -0,0 +1,19 @@
# BlitzKit 1.26.5
¡Ahora en 6 idiomas! This version of BlitzKit adds support for 4 new languages totalling up to 6! [Please help translate on Crowdin](https://crowdin.com/project/blitzkrieg).
## Features
- Added Croatian, Russian, Ukrainian, and Chinese
- Pre-existing languages: English, Spanish
- Added better lighting making tanks a little less yellow and a bit more realistic
- Added custom tank icons (some may be missing when the update drops; just give them an hour or so to update)
- Increased the maximum recently viewed tanks list size to 14
- Tweaked the tank name in the hero section of the Tankopedia to allow the tank to be in the center of the page
- The headers in the Performance tool are now sticky just like the Compare tool
## Fixes
- Removed unnecessary third party script that did absolutely nothing
- Localizations are now stricter in what they allow, reducing errors in the Discord bot
- Added 50 gallons of tomato soup to the bot in an attempt to keep it alive
+15
View File
@@ -0,0 +1,15 @@
# BlitzKit 1.26.6
This update only affects the website.
## Features
- Added an option to hide the tank model beneath the armor
## Fixes
- 3RC is now only display when it is used to avoid a ricochet in the advanced armor highlighting mode
- Fixed the excessively sharp lighting in static mode
- Brightness for non-PBR tanks is a bit better now
- Decimal places are now properly rounded in BlitzKit link embeds
- The titles in the Tankopedia pages aren't too massive now
+14
View File
@@ -0,0 +1,14 @@
# BlitzKit 1.27.0
PLEASE HELP TRANSLATE BLITZKIT: https://crowdin.com/project/blitzkrieg
This update will be the last major update before the release of World of Tanks Blitz Unreal Engine.
## Features
- Added support for English, Spanish, French, Japanese, Polish, Portuguese, Russian, Ukrainian, and Chinese to the website and bot
- You can switch languages through the home page, the footer of any page, or the settings page
## Fixes
- Fixed the units for shell capacity
+7
View File
@@ -0,0 +1,7 @@
# BlitzKit 1.27.1
## Fixes
- Fixed a crash that occurs if tanks are deleted in Blitz and were once available in BlitzKit
- All tank icons are now back
- The tank search once again returns results
+23
View File
@@ -0,0 +1,23 @@
# BlitzKit 1.27.10
This update changes the URLs across the whole website, removing the "tools" prefix and simplifying other aspects too.
## Changes
- Removed "full" tank names
- For example, tanks like the "Carro 45t" had full names like "Carro Combattimento 45t" which remained pretty much unused, so they have been removed
- Reduced the shadow below the navigation bar
- Removed the "tools" prefix from the URLs; here are the paths:
- `/tools/tankopedia` -> `/tanks`
- `/tools/tankopedia/12345` -> `/tanks/tank-name`
- `/tools/compare` -> `/compare`
- `/tools/embed` -> `/embed`
- `/tools/embed/embed-name` -> `/embed/embed-name`
- `/tools/gallery` -> `/gallery`
- `/tools/performance` -> `/performance`
- `/tools/session` -> `/session`
- `/tools/tier-list` -> `/tier-list`
## Technical Features
- The asset mangler now uses http headers and even more heuristics to minimize re-creating unchanged assets
+7
View File
@@ -0,0 +1,7 @@
# BlitzKit 1.27.11
This update affects both the bot and the website.
## Technical Changes
- Added a proper CDN domain to avoid error 429s and any slowdowns
+7
View File
@@ -0,0 +1,7 @@
# BlitzKit 1.27.2
This update only affects the website.
## Fixes
- The new Improved Vertical Stabilizer equipment which only affects the gun depression found on the Type 63 now works
+9
View File
@@ -0,0 +1,9 @@
# BlitzKit 1.27.3
This update only affects the bot.
## Fixes
- Added support for 64-bit IDs allowing users with new accounts to link with BlitzKit
- This comes at the added cost of resetting the links of all other accounts
- Just re-link and you're good to go
+3
View File
@@ -0,0 +1,3 @@
# FreakKit 1.27.4
It has come to our attention that tanks are very harsh objects. To even it out, we made the website a pretty pretty princess <3
+11
View File
@@ -0,0 +1,11 @@
# BlitzKit 1.27.5
This update only affects the bot.
## Changes
- Removed `/auction`
## Fixes
- Fixed a bug that caused `/rating` to disappear
+23
View File
@@ -0,0 +1,23 @@
# BlitzKit 1.27.6
This update only affects the website.
## Features
- Added a promotional card for [the official Discord server](https://discord.gg/nDt7AjGJQH) to the homepage
- Added a better navbar
- The added spaces and padding helps add more room for error when clicking, especially on mobile
- The dropdown menu is now a lost smaller, disposing of the need to scroll at all
- Added shell filters to the tank search
- Reworked the filter buttons and their positions to allow for more space
## Fixes
- Moved most images from imgur to BlitzKit allowing for users in certain countries to load BlitzKit without getting blocked
## Technical Features
- Added Partytown to offload some workload off the main thread, allowing for a more responsive experience
- Reworked `robots.txt` to disallow indexing of BlitzKit variants
- Improved the SEO further for most pages
- Added a few compressor library to speed up load times
+25
View File
@@ -0,0 +1,25 @@
# BlitzKit 1.27.7
![](https://i.imgur.com/qFepOaT.png)
This update only affects the website.
## Features
- Added a new foggy-wireframe transition between tabs to smooth out the jarring jumps
- Added animations pop-in and out animations on a few elements
- The tank name title now shrinks in size as soon as a disturbance is caused
- The shell selector, antagonist switcher, and thicknesses range now ease into frame
- The Tankopedia search page now loads as a skeleton by default before populating with tanks, allowing for the recently viewed tanks to load without cumulative shift
- Something similar was done to the Gallery tool
- Gave the meta section of the website some zest with nation-based backdrops
- The compare tabs in the Tankopedia are now centered
## Fixes
- The camera no longer snaps to the initial position when switching between tabs
## Technical Changes
- Backported some technologies from BlitzKit Reforged; you will not notice any differences in usage
- The popular tanks and gallery indexings have been moved to the asynchronous cron job
+12
View File
@@ -0,0 +1,12 @@
# BlitzKit 1.27.8
![](https://i.imgur.com/WO5MS94.png)
## Features
- The website now uses Inter as the font
- Added a cool new non-disturbed state for the Tankopedia because why not
## Changes
- Removed the Discord plug and language changer from the homepage
+27
View File
@@ -0,0 +1,27 @@
# BlitzKit 1.27.9
BlitzKit's great, but there's far too many numbers. This update adds a few visualizers to help new players understand how things relate numerically.
![](https://i.imgur.com/OV5ak4T.png)
## Features
- Added the turret and hull traverse speed visualizer
- Added the shell ricochet and normalization angles visualizer
- Added the view-range visualizer
- Added a search bar to the Tankopedia characteristics
- Bumped the decimal places for vehicle dimensions and volume
- Made tank ranks softer (the bars beneath the tank characteristics)
- Gave the navigation bar better distinction from the rest of the page
- Increased the number of characteristics rows to 3 on select devices
- Switched to a more contrasting classic style of components across the website (it's subtle, but beautiful)
## Changes
- Removed units
## Fixes
- Clarified a few confusing row labels in the Compare tool
- Made the Tankopedia hero section dynamically smaller on mobile phones to allow scrolling
- Tank ranks no longer reach 0 or go above the number of vehicles in the comparison
+23 -9
View File
@@ -1,12 +1,13 @@
{
"name": "blitzkit",
"name": "@blitzkit",
"private": true,
"version": "1.26.3",
"version": "1.27.11",
"description": "🎉 Tools for everything World of Tanks Blitz",
"type": "module",
"types": "./types.d.ts",
"workspaces": [
"packages/*"
"packages/*",
"submodules/*"
],
"scripts": {
"build:assets": "cd packages/scripts && bun run build:assets",
@@ -35,18 +36,31 @@
"build:protos": "bun packages/scripts/src/protos.ts",
"preview:website": "cd packages/website && bun run preview",
"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"
},
"license": "MIT",
"dependencies": {
"lodash-es": "^4.17.21"
"googleapis": "^144.0.0",
"js-yaml": "^4.1.0",
"lodash-es": "^4.17.21",
"protobufjs": "^7.5.2",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"quicklime": "^1.2.0",
"sharp": "^0.33.5"
},
"devDependencies": {
"@astrojs/react": "^4.3.0",
"@types/bun": "latest",
"@types/js-yaml": "^4.0.9",
"@types/lodash-es": "^4.17.12",
"@types/node": "^22.5.4",
"prettier": "^3.3.3",
"prisma": "^6.0.0",
"typescript": "^5.5.4"
"@types/react": "^19.1.5",
"@types/react-dom": "^19.1.5",
"@types/node": "^22.14.1",
"astro": "^5.8.0",
"prettier": "^3.5.3",
"prisma": "^6.6.0",
"typescript": "^5.8.3"
}
}
+1 -1
View File
@@ -5,10 +5,10 @@
"types": "src/types.d.ts",
"dependencies": {
"@blitzkit/core": "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",
"fuzzysort": "^2.0.4",
"bepaint": "^0.1.0-beta.3",
+2 -2
View File
@@ -28,10 +28,10 @@ export const aboutCommand = new Promise<CommandRegistry>(async (resolve) => {
async handler(interaction) {
const subcommand = interaction.options.getSubcommand();
const { t } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
if (subcommand === 'commands') {
return t`bot.commands.about.subcommands.commands.body${(
return `${strings.bot.commands.about.subcommands.commands.body}${(
await Promise.all(COMMANDS_RAW)
)
.sort((a, b) =>
+36 -24
View File
@@ -3,17 +3,18 @@ import {
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';
interface Auction {
type Auction = null | {
count: number;
has_next: boolean;
results: Result[];
}
};
type Result =
| {
@@ -27,7 +28,7 @@ type Result =
available_from?: Date;
available_before?: Date;
price?: Price;
next_price?: Price | null;
next_price: Price | null;
available: true;
display?: boolean;
next_price_datetime: Date;
@@ -68,12 +69,14 @@ interface Currency {
type: 'currency';
}
const WARNING_COUNT = 500;
export const auctionCommand = new Promise<CommandRegistry>((resolve) => {
resolve({
command: createLocalizedCommand('auction'),
async handler(interaction) {
const { t, translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const awaitedTankDefinitions = await tankDefinitions;
const region: Region = 'com';
const pageSize = 80;
@@ -85,16 +88,16 @@ export const auctionCommand = new Promise<CommandRegistry>((resolve) => {
pageSize
}&type[]=vehicle&saleable=${saleable}`,
).then((response) => response.json() as Promise<Auction>);
const saleableTanks = data.results.filter((result) => result.available);
if (saleableTanks.length === 0) {
return t`bot.commands.auction.errors.no_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 = `# ${t`bot.commands.auction.body.title`}`;
const subtitle = `${translate('bot.commands.auction.body.subtitle', [
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/>',
@@ -104,24 +107,33 @@ export const auctionCommand = new Promise<CommandRegistry>((resolve) => {
if (!tank) return '';
const name = `[${tank.name}](<https://blitzkit.app/tools/tankopedia/${
tank.id
const name = `[${tank.name}](<https://blitzkit.app/tanks/${
tank.slug
}>)`;
const next = translate('bot.commands.auction.body.next', [
`<:gold:1317173197082333244> ${data.next_price!.value.toLocaleString(
interaction.locale,
)}`,
]);
const available = translate('bot.commands.auction.body.available', [
data.current_count.toLocaleString(interaction.locale),
data.initial_count.toLocaleString(interaction.locale),
]);
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
}. ${name} <:gold:1317173197082333244> ${data.price!.value.toLocaleString(
return `${index + 1}. ${
outString
}${name} <:gold:1317173197082333244> ${data.price!.value.toLocaleString(
interaction.locale,
)}\n-# ${available}\n-# ${next}`;
)}${outString}\n-#${isLow ? ' ‼️ ' : ' '}${available}${isOut ? '' : next}`;
});
const header = `${title}\n${subtitle}`;
const lines = [header, ...body];
+17 -13
View File
@@ -7,6 +7,7 @@ import {
getWN8Percentile,
TankDefinition,
} from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import { chunk } from 'lodash-es';
import * as Breakdown from '../../components/Breakdown';
@@ -32,7 +33,7 @@ export async function renderBreakdown(
filters: StatFilters,
locale: Locale,
) {
const { t, translate } = translator(locale);
const { strings, unwrap } = translator(locale);
const awaitedTankDefinitions = await tankDefinitions;
const awaitedTankAverages = await blitzStarsTankAverages;
const statsInPeriod = await getStatsInPeriod(region, id, start, end, locale);
@@ -48,7 +49,7 @@ export async function renderBreakdown(
const orderedCareerWN8Full: (number | undefined)[] = [];
if (tankStats === null) {
throw new UserError(t`bot.common.errors.no_tank_stats`);
throw new UserError(strings.bot.common.errors.no_tank_stats);
}
filteredOrder.forEach((id) => {
@@ -136,21 +137,21 @@ export async function renderBreakdown(
<Breakdown.Row
key={id}
displayType="summary"
title={t`bot.commands.breakdown.body.total`}
title={strings.bot.commands.breakdown.body.total}
stats={[
{
title: t`bot.commands.breakdown.body.battles`,
title: strings.bot.commands.breakdown.body.battles,
current: currentBattles,
career: careerBattles,
},
{
title: t`bot.commands.breakdown.body.winrate`,
title: strings.bot.commands.breakdown.body.winrate,
current: `${(100 * currentWinrate).toFixed(2)}%`,
career: `${(100 * careerWinrate).toFixed(2)}%`,
delta: currentWinrate - careerWinrate,
},
{
title: t`bot.commands.breakdown.body.wn8`,
title: strings.bot.commands.breakdown.body.wn8,
current:
currentWN8 === undefined
? undefined
@@ -165,7 +166,7 @@ export async function renderBreakdown(
: getWN8Percentile(currentWN8),
},
{
title: t`bot.commands.breakdown.body.damage`,
title: strings.bot.commands.breakdown.body.damage,
current: Math.round(currentDamage).toLocaleString(),
career: Math.round(careerDamage).toLocaleString(),
delta: currentDamage - careerDamage,
@@ -190,17 +191,20 @@ export async function renderBreakdown(
class={tankDefinition?.class}
type={tankDefinition?.type}
title={
tankDefinition?.name ??
translate('bot.commands.breakdown.body.unknown_tank', [`${id}`])
tankDefinition
? unwrap(tankDefinition.name)
: literals(strings.bot.commands.breakdown.body.unknown_tank, [
`${id}`,
])
}
stats={[
{
title: t`bot.commands.breakdown.body.battles`,
title: strings.bot.commands.breakdown.body.battles,
current: current.battles.toLocaleString(),
career: career.battles.toLocaleString(),
},
{
title: t`bot.commands.breakdown.body.winrate`,
title: strings.bot.commands.breakdown.body.winrate,
current: `${(100 * (current.wins / current.battles)).toFixed(
2,
)}%`,
@@ -209,7 +213,7 @@ export async function renderBreakdown(
current.wins / current.battles - career.wins / career.battles,
},
{
title: t`bot.commands.breakdown.body.wn8`,
title: strings.bot.commands.breakdown.body.wn8,
current:
currentWN8 === undefined
? undefined
@@ -224,7 +228,7 @@ export async function renderBreakdown(
: getWN8Percentile(currentWN8),
},
{
title: t`bot.commands.breakdown.body.damage`,
title: strings.bot.commands.breakdown.body.damage,
current: Math.round(
current.damage_dealt / current.battles,
).toLocaleString(),
+6 -6
View File
@@ -11,17 +11,17 @@ export const debugCommand = new Promise<CommandRegistry>((resolve) => {
command: createLocalizedCommand('debug'),
async handler(interaction) {
const { t } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const currentTime = new Date().getTime();
const uptime = currentTime - executionStart;
const list = [
[t`bot.commands.debug.body.version`, packageJSON.version],
[strings.bot.commands.debug.body.version, packageJSON.version],
[
t`bot.commands.debug.body.shard`,
client.shard?.ids[0] ?? t`bot.commands.debug.body.shard.default`,
strings.bot.commands.debug.body.shard.label,
client.shard?.ids[0] ?? strings.bot.commands.debug.body.shard.default,
],
[
t`bot.commands.debug.body.uptime`,
strings.bot.commands.debug.body.uptime,
`${Math.floor((uptime / 1000 / 60 / 60) % 24)}h ${Math.floor(
(uptime / 1000 / 60) % 60,
)}m ${Math.floor((uptime / 1000) % 60)}s ${Math.floor(
@@ -29,7 +29,7 @@ export const debugCommand = new Promise<CommandRegistry>((resolve) => {
)}ms`,
],
];
return t`bot.commands.debug.body.title${list
return `${strings.bot.commands.debug.body.title}${list
.map(([key, value]) => `- ${key}: ${value}`)
.join('\n')}`;
},
+16 -9
View File
@@ -1,4 +1,5 @@
import { getAccountInfo, getClanInfo } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import { CommandWrapper } from '../components/CommandWrapper';
import { GenericStats } from '../components/GenericStats';
@@ -19,26 +20,31 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
command: createLocalizedCommand('inactive')
.addStringOption(addClanChoices)
.addNumberOption((option) => {
const { translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return option
.setName('threshold')
.setNameLocalizations(
localizationObject('bot.commands.inactive.options.threshold'),
localizationObject(
(strings) => strings.bot.commands.inactive.options.threshold.name,
undefined,
true,
),
)
.setDescription(
translate('bot.commands.inactive.options.threshold.description'),
strings.bot.commands.inactive.options.threshold.description,
)
.setDescriptionLocalizations(
localizationObject(
'bot.commands.inactive.options.threshold.description',
(strings) =>
strings.bot.commands.inactive.options.threshold.description,
),
)
.setMinValue(0);
}),
async handler(interaction) {
const { translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const { region, id } = await resolveClanFromCommand(interaction);
const threshold =
interaction.options.getNumber('threshold')! ?? DEFAULT_THRESHOLD;
@@ -59,7 +65,7 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
([name, days]) =>
[
name,
translate('bot.commands.inactive.body.listing', [
literals(strings.bot.commands.inactive.body.listing, [
days.toFixed(0),
]),
] as [string, string],
@@ -71,9 +77,10 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
<TitleBar
title={clanInfo.name}
image={`https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clanInfo.emblem_set_id}.png`}
description={`${translate('bot.commands.inactive.body.subtitle', [
`${threshold}`,
])} • ${new Date().toLocaleDateString(interaction.locale)}`}
description={`${literals(
strings.bot.commands.inactive.body.subtitle,
[`${threshold}`],
)} • ${new Date().toLocaleDateString(interaction.locale)}`}
/>
{!hasInactiveMembers && (
+9 -12
View File
@@ -1,11 +1,8 @@
import {
getAccountInfo,
getClanAccountInfo,
linkBlitzAndDiscord,
} from '@blitzkit/core';
import { getAccountInfo, getClanAccountInfo } from '@blitzkit/core';
import { teal } from '@radix-ui/colors';
import { Glow } from '../components/AllStatsOverview/components/HeroStat/components/Glow';
import { CommandWrapper } from '../components/CommandWrapper';
import { linkBlitzAndDiscord } from '../core/db/linkBlitzAndDiscord';
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
import { autocompleteUsername } from '../core/discord/autocompleteUsername';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
@@ -22,7 +19,7 @@ export const verifyCommand = new Promise<CommandRegistry>((resolve) => {
),
async handler(interaction) {
const { t, translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const { id, region } = await resolvePlayerFromCommand(interaction);
const discordId = BigInt(interaction.user.id);
const accountInfo = await getAccountInfo(region, id);
@@ -120,9 +117,9 @@ export const verifyCommand = new Promise<CommandRegistry>((resolve) => {
height: 16,
}}
/>
<span
style={{ fontSize: 16 }}
>{t`bot.commands.link.body.accounts_linked`}</span>
<span style={{ fontSize: 16 }}>
{strings.bot.commands.link.body.accounts_linked}
</span>
</div>
<div
@@ -174,7 +171,7 @@ export const verifyCommand = new Promise<CommandRegistry>((resolve) => {
{clanAccountInfo?.clan
? `[${clanAccountInfo?.clan?.tag}] • `
: ''}
{translate(`common.regions.normal.${region}`)}
{strings.common.regions.normal[region]}
</span>
</div>
</div>
@@ -185,8 +182,8 @@ export const verifyCommand = new Promise<CommandRegistry>((resolve) => {
</CommandWrapper>,
embedInfo(
t`bot.commands.link.embed.title`,
t`bot.commands.link.embed.description`,
strings.bot.commands.link.embed.title,
strings.bot.commands.link.embed.description,
),
];
},
+5 -4
View File
@@ -6,6 +6,7 @@ import {
getAccountInfo,
getTankStats,
} from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import markdownEscape from 'markdown-escape';
import {
gameDefinitions,
@@ -27,14 +28,14 @@ export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
.addStringOption(addUsernameChoices),
async handler(interaction) {
const { t, translate } = translator(interaction.locale);
const { strings, unwrap } = translator(interaction.locale);
const tier = Number(interaction.options.getString('tier'));
const { id, region } = await resolvePlayerFromCommand(interaction);
const accountInfo = await getAccountInfo(region, id);
const tankStats = await getTankStats(region, id);
if (tankStats === null) {
return t`bot.common.errors.no_tank_stats`;
return strings.bot.common.errors.no_tank_stats;
}
const filteredTanks = (
@@ -59,7 +60,7 @@ export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
const awaitedTankDefinitions = await tankDefinitions;
const awaitedGameDefinitions = await gameDefinitions;
const title = translate('bot.commands.owned_tanks.body.title', [
const title = literals(strings.bot.commands.owned_tanks.body.title, [
markdownEscape(accountInfo.nickname),
TIER_ROMAN_NUMERALS[tier],
]);
@@ -77,7 +78,7 @@ export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
)
.map(
(tank) =>
`[${flags[tank.nation]} ${markdownEscape(tank.name)}](<https://blitzkit.app/tools/tankopedia/${tank.id}>)`,
`[${flags[tank.nation]} ${markdownEscape(unwrap(tank.name))}](<https://blitzkit.app/tanks/${tank.slug}>)`,
);
const lines = [title, ...tankList];
+7 -6
View File
@@ -1,3 +1,4 @@
import { literals } from '@blitzkit/i18n';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
import { translator } from '../core/localization/translator';
import { CommandRegistry } from '../events/interactionCreate';
@@ -7,26 +8,26 @@ export const permissionsCommand = new Promise<CommandRegistry>((resolve) => {
command: createLocalizedCommand('permissions'),
async handler(interaction) {
const { t, translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
// friday the 13th permissions easter egg
const isFridayThe13th =
new Date().getDay() === 5 && new Date().getDate() === 13;
const permissions = [
[
interaction.appPermissions?.has('ViewChannel'),
t`bot.commands.permissions.body.view_channels`,
strings.bot.commands.permissions.body.view_channels,
],
[
interaction.appPermissions?.has('ReadMessageHistory'),
t`bot.commands.permissions.body.read_message_history`,
strings.bot.commands.permissions.body.read_message_history,
],
[
interaction.appPermissions?.has('AttachFiles'),
t`bot.commands.permissions.body.attach_files`,
strings.bot.commands.permissions.body.attach_files,
],
[
interaction.appPermissions?.has('UseExternalEmojis'),
t`bot.commands.permissions.body.external_emojis`,
strings.bot.commands.permissions.body.external_emojis,
],
];
const body = permissions
@@ -39,7 +40,7 @@ export const permissionsCommand = new Promise<CommandRegistry>((resolve) => {
})
.join('\n');
return translate('bot.commands.permissions.body.title', [body]);
return literals(strings.bot.commands.permissions.body.title, [body]);
},
});
});
+4 -3
View File
@@ -1,3 +1,4 @@
import { literals } from '@blitzkit/i18n';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
import { translator } from '../core/localization/translator';
import { CommandRegistry } from '../events/interactionCreate';
@@ -12,19 +13,19 @@ export const pingCommand = new Promise<CommandRegistry>((resolve) => {
]),
async handler(interaction) {
const { t, translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const subcommand = interaction.options.getSubcommand();
const executionStart = Date.now();
if (subcommand === 'blitzkit') {
await interaction.editReply(t`bot.commands.ping.body.pong`);
await interaction.editReply(strings.bot.commands.ping.body.pong);
} else {
await fetch('https://api.wotblitz.com/');
}
const executionTime = Date.now() - executionStart;
interaction.editReply(
translate('bot.commands.ping.body.pong_with_time', [
literals(strings.bot.commands.ping.body.pong_with_time, [
`${Math.round(executionTime)}`,
]),
);
+9 -8
View File
@@ -1,4 +1,5 @@
import { getAccountInfo } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import markdownEscape from 'markdown-escape';
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
import { autocompleteUsername } from '../core/discord/autocompleteUsername';
@@ -15,27 +16,27 @@ export const playerInfoCommand = new Promise<CommandRegistry>((resolve) => {
createLocalizedCommand('player-info').addStringOption(addUsernameChoices),
async handler(interaction) {
const { t, translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const account = await resolvePlayerFromCommand(interaction);
const { id, region: region } = account;
const accountInfo = await getAccountInfo(region, id);
return embedInfo(
translate('bot.commands.player_info.body.title', [
literals(strings.bot.commands.player_info.body.title, [
markdownEscape(accountInfo.nickname),
]),
markdownTable([
[
t`bot.commands.player_info.body.nickname`,
strings.bot.commands.player_info.body.nickname,
`${accountInfo.nickname}`,
],
[
t`bot.commands.player_info.body.battles`,
strings.bot.commands.player_info.body.battles,
`${accountInfo.statistics.all.battles}`,
],
[
t`bot.commands.player_info.body.winrate`,
strings.bot.commands.player_info.body.winrate,
`${(
100 *
(accountInfo.statistics.all.wins /
@@ -44,17 +45,17 @@ export const playerInfoCommand = new Promise<CommandRegistry>((resolve) => {
],
[],
[
t`bot.commands.player_info.body.account_id`,
strings.bot.commands.player_info.body.account_id,
`${accountInfo.account_id}`,
],
[
t`bot.commands.player_info.body.created`,
strings.bot.commands.player_info.body.created,
new Date(accountInfo.created_at * 1000).toLocaleDateString(
interaction.locale,
),
],
[
t`bot.commands.player_info.body.last_battle`,
strings.bot.commands.player_info.body.last_battle,
new Date(accountInfo.last_battle_time * 1000).toLocaleDateString(
interaction.locale,
),
+23 -12
View File
@@ -7,7 +7,9 @@ import {
getLeagueFromScore,
getRatingInfo,
getRatingNeighbors,
imgur,
} from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { SlashCommandSubcommandBuilder } from 'discord.js';
import markdownEscape from 'markdown-escape';
import { Glow } from '../components/AllStatsOverview/components/HeroStat/components/Glow';
@@ -52,7 +54,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
const subcommand = interaction.options.getSubcommand(true) as
| 'season'
| 'today';
const { t, translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const { id, region } = await resolvePlayerFromCommand(interaction);
const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan;
const clanImage = clan ? emblemURL(clan.emblem_set_id) : undefined;
@@ -88,7 +90,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
const statsB2Array = (await getRatingNeighbors(region, id, 0)).neighbors;
if (statsB2Array === undefined) {
return translate('bot.commands.rating.errors.no_participation', [
return literals(strings.bot.commands.rating.errors.no_participation, [
markdownEscape(accountInfo.nickname),
]);
}
@@ -97,10 +99,10 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
const league = getLeagueFromScore(statsB2.score);
if (!statsA) {
return t`bot.commands.rating.errors.unrecorded_stats`;
return strings.bot.commands.rating.errors.unrecorded_stats;
}
if (!statsB1) {
return translate('bot.commands.rating.errors.no_participation', [
return literals(strings.bot.commands.rating.errors.no_participation, [
markdownEscape(accountInfo.nickname),
]);
}
@@ -119,15 +121,28 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
statsB2,
);
const leagueTheme = LEAGUE_ACCENT[league.index];
let rewardImage = reward
? reward.type === 'vehicle'
? reward.vehicle.preview_image_url
: reward.stuff.image_url
: undefined;
if (rewardImage) {
const { ok } = await fetch(rewardImage);
if (!ok) {
rewardImage = imgur('kP88vqr');
}
}
return (
<CommandWrapper>
<TitleBar
title={accountInfo.nickname}
image={clanImage}
description={`${translate(
`bot.commands.rating.body.subtitle.${subcommand}`,
)} • ${new Date().toLocaleDateString(interaction.locale)}`}
description={`${
strings.bot.commands.rating.body.subtitle[subcommand]
} • ${new Date().toLocaleDateString(interaction.locale)}`}
/>
<div
@@ -325,11 +340,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
height: 64,
objectFit: 'cover',
}}
src={await iconPng(
reward.type === 'vehicle'
? reward.vehicle.preview_image_url
: reward.stuff.image_url,
)}
src={await iconPng(rewardImage!)}
/>
</div>
)}
+73 -38
View File
@@ -14,6 +14,7 @@ import {
RatingLeaderboard,
Region,
} from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import {
APIApplicationCommandOptionChoice,
Locale,
@@ -54,26 +55,32 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
);
function addOptions(option: SlashCommandSubcommandBuilder) {
const { t, translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return option
.addIntegerOption((option) =>
option
.setName(t`bot.commands.rating_leaderboard.options.limit`)
.setName(strings.bot.commands.rating_leaderboard.options.limit.name)
.setNameLocalizations(
localizationObject(
'bot.commands.rating_leaderboard.options.limit',
(strings) =>
strings.bot.commands.rating_leaderboard.options.limit.name,
undefined,
true,
),
)
.setDescription(
translate(
'bot.commands.rating_leaderboard.options.limit.description',
literals(
strings.bot.commands.rating_leaderboard.options.limit
.description,
[`${DEFAULT_LIMIT}`],
),
)
.setDescriptionLocalizations(
localizationObject(
'bot.commands.rating_leaderboard.options.limit.description',
(strings) =>
strings.bot.commands.rating_leaderboard.options.limit
.description,
[`${DEFAULT_LIMIT}`],
),
)
@@ -83,20 +90,26 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
)
.addStringOption((option) =>
option
.setName(t`bot.commands.rating_leaderboard.options.season`)
.setName(
strings.bot.commands.rating_leaderboard.options.season.name,
)
.setNameLocalizations(
localizationObject(
'bot.commands.rating_leaderboard.options.season',
(strings) =>
strings.bot.commands.rating_leaderboard.options.season.name,
undefined,
true,
),
)
.setDescription(
translate(
'bot.commands.rating_leaderboard.options.season.description',
),
strings.bot.commands.rating_leaderboard.options.season
.description,
)
.setDescriptionLocalizations(
localizationObject(
'bot.commands.rating_leaderboard.options.season.description',
(strings) =>
strings.bot.commands.rating_leaderboard.options.season
.description,
),
)
.setRequired(false)
@@ -108,15 +121,18 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
return {
name: current
? translate(
'bot.commands.rating_leaderboard.options.season.choices.current',
? literals(
strings.bot.commands.rating_leaderboard.options.season
.choices.current,
[`${number}`],
)
: `${number}`,
value: `${number}`,
name_localizations: current
? localizationObject(
'bot.commands.rating_leaderboard.options.season.choices.current',
(strings) =>
strings.bot.commands.rating_leaderboard.options
.season.choices.current,
[`${number}`],
)
: undefined,
@@ -141,33 +157,45 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
modify(option: SlashCommandSubcommandBuilder) {
option
.addStringOption((option) => {
const { t, translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return option
.setName(t`bot.commands.rating_leaderboard.options.league`)
.setName(
strings.bot.commands.rating_leaderboard.options.league.name,
)
.setNameLocalizations(
localizationObject(
'bot.commands.rating_leaderboard.options.league',
(strings) =>
strings.bot.commands.rating_leaderboard.options.league
.name,
undefined,
true,
),
)
.setDescription(
translate(
'bot.commands.rating_leaderboard.options.league.description',
),
strings.bot.commands.rating_leaderboard.options.league
.description,
)
.setDescriptionLocalizations(
localizationObject(
'bot.commands.rating_leaderboard.options.league.description',
(strings) =>
strings.bot.commands.rating_leaderboard.options.league
.description,
),
)
.addChoices(
...LEAGUES.map(
({ name }, value) =>
({
name: translate(`common.leagues.${name}`),
name: (
strings.common.leagues as Record<string, string>
)[name],
value: `${value}`,
name_localizations: localizationObject(
`common.leagues.${name}`,
(strings) =>
(
strings.common.leagues as Record<string, string>
)[name],
),
}) satisfies APIApplicationCommandOptionChoice<string>,
),
@@ -183,7 +211,7 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
// BIG TODO: title seems to be in english
async handler(interaction) {
const { t, translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const subcommand = interaction.options.getSubcommand(true) as
| 'league'
| 'neighbors';
@@ -217,7 +245,8 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
regionRatingInfo !== null &&
regionRatingInfo.detail !== undefined
) {
return t`bot.commands.rating_leaderboard.body.no_ongoing_season`;
return strings.bot.commands.rating_leaderboard.body
.no_ongoing_season;
}
const result = isCurrentSeason
@@ -262,8 +291,9 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
clan: clanData[player.id]?.clan?.tag,
nickname:
clanData[index]?.account_name ??
translate(
'bot.commands.rating_leaderboard.body.deleted_player',
literals(
strings.bot.commands.rating_leaderboard.body
.deleted_player,
[`${player.id}`],
),
}) satisfies SimplifiedPlayer,
@@ -283,12 +313,14 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
: regionRatingInfo.count) - result[result.length - 1].position;
titleName = `${
leagueInfo
? translate(`common.leagues.${LEAGUES[leagueInfo.index].name}`)
? (strings.common.leagues as Record<string, string>)[
LEAGUES[leagueInfo.index].name
]
: ''
} - ${translate(`common.regions.short.${region}`)}`;
} - ${strings.common.regions.short[region]}`;
titleImage = imgur(leagueInfo.icon);
titleDescription = translate(
'bot.commands.rating_leaderboard.body.top_players',
titleDescription = literals(
strings.bot.commands.rating_leaderboard.body.top_players,
[`${limit}`],
);
} else {
@@ -301,7 +333,8 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
regionRatingInfo !== null &&
regionRatingInfo.detail !== undefined
) {
return t`bot.commands.rating_leaderboard.body.no_ongoing_season`;
return strings.bot.commands.rating_leaderboard.body
.no_ongoing_season;
}
const accountInfo = await getAccountInfo(region, id);
@@ -328,8 +361,9 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
if (playerIndex === -1) {
throw new UserError(
translate(
'bot.commands.rating_leaderboard.body.no_participation',
literals(
strings.bot.commands.rating_leaderboard.body
.no_participation,
[markdownEscape(accountInfo.nickname), `${season}`],
),
);
@@ -367,7 +401,8 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clan.emblem_set_id}.png`
: undefined;
titleName = neighbors ? accountInfo.nickname : '';
titleDescription = t`bot.commands.rating_leaderboard.body.neighbors`;
titleDescription =
strings.bot.commands.rating_leaderboard.body.neighbors;
playersAfter =
(regionRatingInfo === null
? archivedLeaderboard.version!.value.entries.length
@@ -416,8 +451,8 @@ export const ratingLeaderboardCommand = new Promise<CommandRegistry>(
{items}
{playersAfter > 0 && (
<Leaderboard.Gap
message={translate(
'bot.commands.rating_leaderboard.body.more',
message={literals(
strings.bot.commands.rating_leaderboard.body.more,
[`${playersAfter.toLocaleString(interaction.locale)}`],
)}
/>
+13 -3
View File
@@ -6,6 +6,7 @@ import {
tankIcon,
TIER_ROMAN_NUMERALS,
} from '@blitzkit/core';
import { Locale } from 'discord.js';
import { clamp } from 'lodash-es';
import { CommandWrapper } from '../components/CommandWrapper';
import { TitleBar } from '../components/TitleBar';
@@ -18,6 +19,7 @@ import { RenderConfiguration } from '../core/blitzkit/renderConfiguration';
import { blitzStarsTankAverages } from '../core/blitzstars/tankAverages';
import { buttonLink } from '../core/discord/buttonLink';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
import { translator } from '../core/localization/translator';
import { CommandRegistry } from '../events/interactionCreate';
import { theme } from '../stitches.config';
@@ -218,11 +220,13 @@ function stat(value: string | number, flex = 2) {
}
async function playerListing(
locale: Locale,
player: WotInspectorReplayPlayerData,
protagonistTeam: boolean,
winningTeam: number,
recordingPlayer = false,
) {
const { unwrap } = translator(locale);
const awaitedTankAverages = await blitzStarsTankAverages;
const awaitedTankDefinitions = await tankDefinitions;
const tank = awaitedTankDefinitions.tanks[player.vehicle_descr];
@@ -275,7 +279,7 @@ async function playerListing(
}}
>
<img
alt={tank.name}
alt={unwrap(tank.name)}
src={await iconPng(tankIcon(tank.id))}
style={{
width: 32,
@@ -312,7 +316,7 @@ async function playerListing(
fontSize: 16,
}}
>
{tank.name}
{unwrap(tank.name)}
</span>
</div>
@@ -530,6 +534,7 @@ export const replayCommand = new Promise<CommandRegistry>((resolve) => {
)
.map((player) =>
playerListing(
interaction.locale,
player,
true,
replayData.winner_team,
@@ -553,7 +558,12 @@ export const replayCommand = new Promise<CommandRegistry>((resolve) => {
players
.filter((player) => player.team === antagonistTeamId)
.map((player) =>
playerListing(player, false, replayData.winner_team),
playerListing(
interaction.locale,
player,
false,
replayData.winner_team,
),
),
)}
</div>
+80 -52
View File
@@ -9,6 +9,7 @@ import {
TankType,
TIER_ROMAN_NUMERALS,
} from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { escapeMarkdown, Locale } from 'discord.js';
import { CommandWrapper } from '../components/CommandWrapper';
import { TitleBar } from '../components/TitleBar';
@@ -28,22 +29,28 @@ import { CommandRegistry } from '../events/interactionCreate';
import { theme } from '../stitches.config';
export const researchCommand = new Promise<CommandRegistry>((resolve) => {
const { t } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
resolve({
command: createLocalizedCommand('research')
.addStringOption((option) =>
option
.setName(t`bot.commands.research.options.target_tank`)
.setName(strings.bot.commands.research.options.target_tank.name)
.setNameLocalizations(
localizationObject('bot.commands.research.options.target_tank'),
localizationObject(
(strings) =>
strings.bot.commands.research.options.target_tank.name,
undefined,
true,
),
)
.setDescription(
t`bot.commands.research.options.target_tank.description`,
strings.bot.commands.research.options.target_tank.description,
)
.setDescriptionLocalizations(
localizationObject(
'bot.commands.research.options.target_tank.description',
(strings) =>
strings.bot.commands.research.options.target_tank.description,
),
)
.setAutocomplete(true)
@@ -51,16 +58,22 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
)
.addStringOption((option) =>
option
.setName(t`bot.commands.research.options.starting_tank`)
.setName(strings.bot.commands.research.options.starting_tank.name)
.setNameLocalizations(
localizationObject('bot.commands.research.options.starting_tank'),
localizationObject(
(strings) =>
strings.bot.commands.research.options.starting_tank.name,
undefined,
true,
),
)
.setDescription(
t`bot.commands.research.options.starting_tank.description`,
strings.bot.commands.research.options.starting_tank.description,
)
.setDescriptionLocalizations(
localizationObject(
'bot.commands.research.options.starting_tank.description',
(strings) =>
strings.bot.commands.research.options.starting_tank.description,
),
)
.setAutocomplete(true)
@@ -69,7 +82,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
.addStringOption(addUsernameChoices),
async handler(interaction) {
const { t, translate } = translator(interaction.locale);
const { strings, unwrap } = translator(interaction.locale);
const awaitedTankDefinitions = await tankDefinitions;
const targetTankId = await resolveTankId(
interaction.options.getString('target-tank', true),
@@ -90,50 +103,55 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
);
if (targetTankId === startingTankId) {
return translate('bot.commands.research.errors.start_end_equal', [
escapeMarkdown(targetTank.name),
]);
return literals(
strings.bot.commands.research.errors.start_end_equal,
[escapeMarkdown(unwrap(targetTank.name))],
);
}
const startingTank = awaitedTankDefinitions.tanks[startingTankId];
if (startingTank.tier > targetTank.tier) {
return translate('bot.commands.research.errors.unordered', [
escapeMarkdown(startingTank.name),
escapeMarkdown(targetTank.name),
escapeMarkdown(targetTank.name),
return literals(strings.bot.commands.research.errors.unordered, [
escapeMarkdown(unwrap(startingTank.name)),
escapeMarkdown(unwrap(targetTank.name)),
escapeMarkdown(unwrap(targetTank.name)),
]);
}
if (!targetTankAncestry.includes(startingTankId)) {
return translate('bot.commands.research.errors.tanks_not_on_line', [
escapeMarkdown(targetTank.name),
escapeMarkdown(startingTank.name),
]);
return literals(
strings.bot.commands.research.errors.tanks_not_on_line,
[
escapeMarkdown(unwrap(targetTank.name)),
escapeMarkdown(unwrap(startingTank.name)),
],
);
}
} else {
if (targetTank.type !== TankType.RESEARCHABLE) {
return translate('bot.commands.research.errors.non_tech_tree', [
escapeMarkdown(targetTank.name),
return literals(strings.bot.commands.research.errors.non_tech_tree, [
escapeMarkdown(unwrap(targetTank.name)),
]);
}
if (targetTank.tier === 1) {
return translate('bot.commands.research.errors.tier_1', [
escapeMarkdown(targetTank.name),
return literals(strings.bot.commands.research.errors.tier_1, [
escapeMarkdown(unwrap(targetTank.name)),
]);
}
const tankStats = await getTankStats(region, id);
if (tankStats === null) {
return t`bot.common.errors.no_tank_stats`;
return strings.bot.common.errors.no_tank_stats;
}
if (tankStats.some(({ tank_id }) => tank_id === targetTankId)) {
return translate('bot.commands.research.errors.already_researched', [
escapeMarkdown(targetTank.name),
]);
return literals(
strings.bot.commands.research.errors.already_researched,
[escapeMarkdown(unwrap(targetTank.name))],
);
}
const foundAncestor = targetTankAncestry.find(
@@ -247,7 +265,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
<TitleBar
title={nickname}
image={clanImage}
description={`${t`bot.commands.research.body.subtitle`} • ${
description={`${strings.bot.commands.research.body.subtitle} • ${
targetTank.name
}`}
/>
@@ -284,7 +302,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
color: theme.colors.textHighContrast,
}}
>
{t`bot.commands.research.body.total`}
{strings.bot.commands.research.body.total}
</span>
</div>
@@ -444,7 +462,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
}}
>
<img
alt={tank.name}
alt={unwrap(tank.name)}
src={TANK_ICONS[tank.class]}
width={16}
height={16}
@@ -455,7 +473,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
color: theme.colors.textHighContrast,
}}
>
{tank.name}
{unwrap(tank.name)}
</span>
</div>
</div>
@@ -489,11 +507,14 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
color: theme.colors.textHighContrast,
}}
>
{translate('bot.commands.research.body.research', [
`${costs[index].research?.toLocaleString(
interaction.locale,
)}`,
])}
{literals(
strings.bot.commands.research.body.research,
[
`${costs[index].research?.toLocaleString(
interaction.locale,
)}`,
],
)}
</span>
</div>
@@ -519,9 +540,10 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
color: theme.colors.textHighContrast,
}}
>
{translate('bot.commands.research.body.upgrades', [
costs[index].upgrades.toLocaleString(),
])}
{literals(
strings.bot.commands.research.body.upgrades,
[costs[index].upgrades.toLocaleString()],
)}
</span>
</div>
)}
@@ -547,11 +569,14 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
color: theme.colors.textHighContrast,
}}
>
{translate('bot.commands.research.body.purchase', [
costs[index].purchase.toLocaleString(
interaction.locale,
),
])}
{literals(
strings.bot.commands.research.body.purchase,
[
costs[index].purchase.toLocaleString(
interaction.locale,
),
],
)}
</span>
</div>
@@ -576,11 +601,14 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
color: theme.colors.textHighContrast,
}}
>
{translate('bot.commands.research.body.equipment', [
costs[index].equipment.toLocaleString(
interaction.locale,
),
])}
{literals(
strings.bot.commands.research.body.equipment,
[
costs[index].equipment.toLocaleString(
interaction.locale,
),
],
)}
</span>
</div>
</div>
@@ -594,7 +622,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
return startingTankRaw
? image
: [t`bot.commands.research.body.estimation`, image];
: [strings.bot.commands.research.body.estimation, image];
},
autocomplete(interaction) {
+18 -11
View File
@@ -1,4 +1,5 @@
import { ClanList, fetchBlitz, Region } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import markdownEscape from 'markdown-escape';
import { addClanChoices } from '../core/discord/addClanChoices';
@@ -19,21 +20,27 @@ export const searchClansCommand = new Promise<CommandRegistry>((resolve) => {
addClanChoices(option).setAutocomplete(false),
)
.addIntegerOption((option) => {
const { t, translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return option
.setName(t`bot.commands.search_clans.options.limit`)
.setName(strings.bot.commands.search_clans.options.limit.name)
.setNameLocalizations(
localizationObject('bot.commands.search_clans.options.limit'),
localizationObject(
(strings) => strings.bot.commands.search_clans.options.limit.name,
undefined,
true,
),
)
.setDescription(
translate('bot.commands.search_clans.options.limit.description', [
`${DEFAULT_LIMIT}`,
]),
literals(
strings.bot.commands.search_clans.options.limit.description,
[`${DEFAULT_LIMIT}`],
),
)
.setDescriptionLocalizations(
localizationObject(
'bot.commands.search_clans.options.limit.description',
(strings) =>
strings.bot.commands.search_clans.options.limit.description,
[`${DEFAULT_LIMIT}`],
),
)
@@ -42,7 +49,7 @@ export const searchClansCommand = new Promise<CommandRegistry>((resolve) => {
}),
async handler(interaction) {
const { translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const region = interaction.options.getString('region') as Region;
const clan = interaction.options.getString('clan')!;
const limit = interaction.options.getInteger('limit') ?? DEFAULT_LIMIT;
@@ -52,12 +59,12 @@ export const searchClansCommand = new Promise<CommandRegistry>((resolve) => {
});
return embedInfo(
translate('bot.commands.search_clans.body.title', [
literals(strings.bot.commands.search_clans.body.title, [
markdownEscape(clan),
translate(`common.regions.normal.${region}`),
strings.common.regions.normal[region],
]),
clanList.length === 0
? translate('bot.commands.search_clans.body.no_results')
? strings.bot.commands.search_clans.body.no_results
: `\`\`\`\n${clanList
.map((clan) => `${clan.name} [${clan.tag}]`)
.join('\n')}\n\`\`\``,
+19 -11
View File
@@ -4,6 +4,7 @@ import {
Region,
usernamePattern,
} from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import markdownEscape from 'markdown-escape';
import { addRegionChoices } from '../core/discord/addRegionChoices';
@@ -17,7 +18,7 @@ import { CommandRegistry } from '../events/interactionCreate';
const DEFAULT_LIMIT = 25;
export const searchPlayersCommand = new Promise<CommandRegistry>((resolve) => {
const { t, translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
resolve({
command: createLocalizedCommand('search-players')
@@ -27,18 +28,25 @@ export const searchPlayersCommand = new Promise<CommandRegistry>((resolve) => {
)
.addIntegerOption((option) =>
option
.setName(t`bot.commands.search_players.options.limit`)
.setName(strings.bot.commands.search_players.options.limit.name)
.setNameLocalizations(
localizationObject('bot.commands.search_players.options.limit'),
localizationObject(
(strings) =>
strings.bot.commands.search_players.options.limit.name,
undefined,
true,
),
)
.setDescription(
translate('bot.commands.search_players.options.limit.description', [
`${DEFAULT_LIMIT}`,
]),
literals(
strings.bot.commands.search_players.options.limit.description,
[`${DEFAULT_LIMIT}`],
),
)
.setDescriptionLocalizations(
localizationObject(
'bot.commands.search_players.options.limit.description',
(strings) =>
strings.bot.commands.search_players.options.limit.description,
[`${DEFAULT_LIMIT}`],
),
)
@@ -47,7 +55,7 @@ export const searchPlayersCommand = new Promise<CommandRegistry>((resolve) => {
),
async handler(interaction) {
const { translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const region = interaction.options.getString('region') as Region;
const name = interaction.options.getString('username')!;
const limit = interaction.options.getInteger('limit') ?? 25;
@@ -60,13 +68,13 @@ export const searchPlayersCommand = new Promise<CommandRegistry>((resolve) => {
: [];
return embedInfo(
translate('bot.commands.search_players.body.title', [
literals(strings.bot.commands.search_players.body.title, [
markdownEscape(trimmedSearch),
translate(`common.regions.normal.${region}`),
strings.common.regions.normal[region],
]),
`\`\`\`${
players.length === 0
? translate('bot.commands.search_players.body.no_results')
? strings.bot.commands.search_players.body.no_results
: players.map((player) => player.nickname).join('\n')
}\`\`\``,
);
@@ -32,7 +32,7 @@ export function AllStatsOverview({
locale,
supplementaryStats,
}: AllStatsOverviewProps) {
const { t, translate } = translator(locale);
const { strings } = translator(locale);
const percentile =
typeof supplementaryStats.WN8 === 'number'
? getWN8Percentile(supplementaryStats.WN8)
@@ -63,18 +63,18 @@ export function AllStatsOverview({
}}
>
<WN8SurroundingStat
label={t`bot.common.hero_stats.battles`}
label={strings.bot.common.hero_stats.battles}
value={stats.battles.toLocaleString()}
align={WN8SurroundingStatAlign.Right}
/>
<WN8SurroundingStat
label={t`bot.common.hero_stats.winrate`}
label={strings.bot.common.hero_stats.winrate}
value={`${(100 * (stats.wins / stats.battles)).toFixed(2)}%`}
align={WN8SurroundingStatAlign.Right}
padded
/>
<WN8SurroundingStat
label={t`bot.common.hero_stats.damage`}
label={strings.bot.common.hero_stats.damage}
value={Math.round(
stats.damage_dealt / stats.battles,
).toLocaleString()}
@@ -82,7 +82,7 @@ export function AllStatsOverview({
padded
/>
<WN8SurroundingStat
label={t`bot.common.hero_stats.damage_ratio`}
label={strings.bot.common.hero_stats.damage_ratio}
value={(stats.damage_dealt / stats.damage_received).toFixed(2)}
align={WN8SurroundingStatAlign.Right}
/>
@@ -90,7 +90,7 @@ export function AllStatsOverview({
<HeroStat
value={heroStat}
subtitle={translate(`common.wn8_percentile.${percentile}`)}
subtitle={strings.common.wn8_percentile[percentile]}
color={color}
/>
@@ -103,25 +103,25 @@ export function AllStatsOverview({
}}
>
<WN8SurroundingStat
label={t`bot.common.hero_stats.tier`}
label={strings.bot.common.hero_stats.tier}
value={
supplementaryStats.tier ? supplementaryStats.tier.toFixed(2) : '--'
}
/>
<WN8SurroundingStat
label={t`bot.common.hero_stats.survival`}
label={strings.bot.common.hero_stats.survival}
value={`${(100 * (stats.survived_battles / stats.battles)).toFixed(
2,
)}%`}
padded
/>
<WN8SurroundingStat
label={t`bot.common.hero_stats.accuracy`}
label={strings.bot.common.hero_stats.accuracy}
value={`${(100 * (stats.hits / stats.shots)).toFixed(2)}%`}
padded
/>
<WN8SurroundingStat
label={t`bot.common.hero_stats.kills`}
label={strings.bot.common.hero_stats.kills}
value={(stats.frags / stats.battles).toFixed(2)}
/>
</div>
@@ -16,8 +16,8 @@ export function CommandWrapper({ children, fat }: CommandWrapperProps) {
gap: 16,
width: '100%',
color: theme.colors.textHighContrast,
backgroundColor: theme.colors.appBackground1,
position: 'relative',
background: theme.colors.appBackground1,
}}
>
{children}
@@ -21,7 +21,7 @@ export function GenericAllStats({
supplementaryStats,
locale,
}: GenericAllStatsProps) {
const { t } = translator(locale);
const { strings } = translator(locale);
const damageRatio = stats.damage_dealt / stats.damage_received;
const killsToDeathRatio =
stats.frags / (stats.battles - stats.survived_battles);
@@ -30,27 +30,28 @@ export function GenericAllStats({
<Root>
<Row>
<Card
title={t`bot.commands.full_stats.body.cards.games`}
title={strings.bot.commands.full_stats.body.cards.games.title}
items={[
{
label: t`bot.commands.full_stats.body.cards.games.winrate`,
label: strings.bot.commands.full_stats.body.cards.games.winrate,
value: `${(100 * (stats.wins / stats.battles)).toFixed(2)}%`,
},
{
label: t`bot.commands.full_stats.body.cards.games.battles`,
label: strings.bot.commands.full_stats.body.cards.games.battles,
value: stats.battles,
},
{
label: t`bot.commands.full_stats.body.cards.games.wins_losses`,
label:
strings.bot.commands.full_stats.body.cards.games.wins_losses,
value: `${stats.wins} / ${stats.losses}`,
},
]}
/>
<Card
title={t`bot.commands.full_stats.body.cards.efficiency`}
title={strings.bot.commands.full_stats.body.cards.efficiency.title}
items={[
{
label: t`bot.commands.full_stats.body.cards.efficiency.wn8`,
label: strings.bot.commands.full_stats.body.cards.efficiency.wn8,
value:
supplementaryStats && isNumber(supplementaryStats.WN8)
? supplementaryStats.WN8!.toFixed(0)
@@ -60,14 +61,16 @@ export function GenericAllStats({
: undefined,
},
{
label: t`bot.commands.full_stats.body.cards.efficiency.survival`,
label:
strings.bot.commands.full_stats.body.cards.efficiency.survival,
value: `${(
100 *
(stats.survived_battles / stats.battles)
).toFixed(2)}%`,
},
{
label: t`bot.commands.full_stats.body.cards.efficiency.accuracy`,
label:
strings.bot.commands.full_stats.body.cards.efficiency.accuracy,
value: `${((stats.hits / stats.shots) * 100).toFixed(2)}%`,
},
]}
@@ -75,14 +78,18 @@ export function GenericAllStats({
</Row>
<Row>
<Card
title={t`bot.commands.full_stats.body.cards.lethality`}
title={strings.bot.commands.full_stats.body.cards.lethality.title}
items={[
{
label: t`bot.commands.full_stats.body.cards.lethality.average_damage`,
label:
strings.bot.commands.full_stats.body.cards.lethality
.average_damage,
value: (stats.damage_dealt / stats.battles).toFixed(0),
},
{
label: t`bot.commands.full_stats.body.cards.lethality.damage_per_tier`,
label:
strings.bot.commands.full_stats.body.cards.lethality
.damage_per_tier,
value: supplementaryStats?.tier
? (
stats.damage_dealt /
@@ -92,32 +99,44 @@ export function GenericAllStats({
: undefined,
},
{
label: t`bot.commands.full_stats.body.cards.lethality.damage_ratio`,
label:
strings.bot.commands.full_stats.body.cards.lethality
.damage_ratio,
value: isFinite(damageRatio) ? damageRatio.toFixed(2) : undefined,
},
{
label: t`bot.commands.full_stats.body.cards.lethality.average_kills`,
label:
strings.bot.commands.full_stats.body.cards.lethality
.average_kills,
value: (stats.frags / stats.battles).toFixed(0),
},
]}
/>
<Card
title={t`bot.commands.full_stats.body.cards.miscellaneous`}
title={strings.bot.commands.full_stats.body.cards.miscellaneous.title}
items={[
{
label: t`bot.commands.full_stats.body.cards.miscellaneous.average_tier`,
label:
strings.bot.commands.full_stats.body.cards.miscellaneous
.average_tier,
value: supplementaryStats?.tier?.toFixed(2),
},
{
label: t`bot.commands.full_stats.body.cards.miscellaneous.average_spots`,
label:
strings.bot.commands.full_stats.body.cards.miscellaneous
.average_spots,
value: (stats.spotted / stats.battles).toFixed(2),
},
{
label: t`bot.commands.full_stats.body.cards.miscellaneous.average_xp`,
label:
strings.bot.commands.full_stats.body.cards.miscellaneous
.average_xp,
value: (stats.xp / stats.battles).toFixed(0),
},
{
label: t`bot.commands.full_stats.body.cards.miscellaneous.kills_to_death_ratio`,
label:
strings.bot.commands.full_stats.body.cards.miscellaneous
.kills_to_death_ratio,
value: isFinite(killsToDeathRatio)
? killsToDeathRatio.toFixed(2)
: undefined,
+2 -2
View File
@@ -12,7 +12,7 @@ interface NoDataProps {
}
export function NoData({ type, locale }: NoDataProps) {
const { translate } = translator(locale);
const { strings } = translator(locale);
return (
<div
@@ -23,7 +23,7 @@ export function NoData({ type, locale }: NoDataProps) {
}}
>
<span style={{ fontSize: 16, color: theme.colors.textLowContrast }}>
{translate(`bot.common.no_data.${type}`)}
{strings.bot.common.no_data[type]}
</span>
</div>
);
+6 -7
View File
@@ -1,3 +1,5 @@
import { SEARCH_KEYS } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import { go } from 'fuzzysort';
import markdownEscape from 'markdown-escape';
@@ -11,22 +13,19 @@ export async function resolveTankId(
locale: Locale,
techTreeOnly = false,
) {
const { translate } = translator(locale);
const { strings } = translator(locale);
const number = typeof tank === 'string' ? Number(tank) : tank;
if (Number.isNaN(number)) {
const searchResult = go(
`${tank}`,
await (techTreeOnly ? tankNamesTechTreeOnly : tankNames),
{
keys: ['searchableName', 'searchableNameDeburr', 'camouflages'],
limit: 1,
},
{ keys: SEARCH_KEYS, limit: 1 },
);
if (searchResult.length === 0) {
throw new UserError(
translate('bot.common.errors.tank_name_not_found', [
literals(strings.bot.common.errors.tank_name_not_found, [
markdownEscape(`${tank}`),
]),
);
@@ -40,7 +39,7 @@ export async function resolveTankId(
return number;
} else {
throw new UserError(
translate('bot.common.errors.tank_id_not_found', [`${number}`]),
literals(strings.bot.common.errors.tank_id_not_found, [`${number}`]),
);
}
}
@@ -8,15 +8,15 @@ export async function searchClansAcrossRegionsBotWrapper(
locale: Locale,
limit?: number,
) {
const { t } = translator(locale);
const { strings } = translator(locale);
const trimmed = search.trim();
if (trimmed.length < 3) {
throw new UserError(t`bot.common.errors.query_too_small`);
throw new UserError(strings.bot.common.errors.query_too_small);
}
if (trimmed.length > 100) {
throw new UserError(t`bot.common.errors.query_too_long`);
throw new UserError(strings.bot.common.errors.query_too_long);
}
return await searchClansAcrossRegions(trimmed, limit);
@@ -8,15 +8,15 @@ export async function searchPlayersAcrossRegionsBotWrapper(
locale: Locale,
limit?: number,
) {
const { t } = translator(locale);
const { strings } = translator(locale);
const trimmed = search.trim();
if (trimmed.length < 3) {
throw new UserError(t`bot.common.errors.query_too_small`);
throw new UserError(strings.bot.common.errors.query_too_small);
}
if (trimmed.length > 100) {
throw new UserError(t`bot.common.errors.query_too_long`);
throw new UserError(strings.bot.common.errors.query_too_long);
}
return await searchPlayersAcrossRegions(trimmed, limit);
@@ -1,4 +1,5 @@
import { fetchTankDefinitions, TIER_ROMAN_NUMERALS } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import { StatFilters } from '../blitzstars/filterStats';
import { translator } from '../localization/translator';
@@ -7,22 +8,21 @@ export async function filtersToDescription(
{ nation, tier, type, class: tankClass, tank }: StatFilters,
locale: Locale,
) {
const { translate } = translator(locale);
const { strings, bkLocale } = translator(locale);
const awaitedTankDefinitions = await fetchTankDefinitions();
const info: string[] = [];
// TODO: somehow localize tank names?
if (tank) return awaitedTankDefinitions.tanks[tank].name;
if (nation) info.push(translate(`common.nations.${nation}`));
if (tank) return awaitedTankDefinitions.tanks[tank].name.locales[bkLocale];
if (nation)
info.push((strings.common.nations as Record<string, string>)[nation]);
if (tier) {
info.push(
translate('bot.common.filters.tier', [TIER_ROMAN_NUMERALS[tier]]),
literals(strings.bot.common.filters.tier, [TIER_ROMAN_NUMERALS[tier]]),
);
}
if (tankClass) info.push(translate(`common.tank_class_short.${tankClass}`));
if (type) info.push(translate(`common.tree_type.${type}`));
if (tankClass) info.push(strings.common.tank_class_short[tankClass]);
if (type) info.push(strings.common.tree_type[type]);
return info.length === 0
? translate('bot.common.filters.none')
: info.join(', ');
return info.length === 0 ? strings.bot.common.filters.none : info.join(', ');
}
@@ -1,5 +1,6 @@
import { idToRegion, usersDatabase } from '@blitzkit/core';
import { flagUserActivity } from '@blitzkit/core/src/blitzkit/flagUserActivity';
import { idToRegion } from '@blitzkit/core';
import { flagUserActivity } from '../db/flagUserActivity';
import { usersDatabase } from '../db/users';
export async function getBlitzFromDiscord(discordId: bigint) {
const unique = await usersDatabase.discordUser.findUnique({
@@ -8,6 +9,8 @@ export async function getBlitzFromDiscord(discordId: bigint) {
if (unique === null) return null;
await flagUserActivity(unique.blitz_id);
return { id: unique.blitz_id, region: idToRegion(unique.blitz_id) };
const id = Number(unique.blitz_id);
await flagUserActivity(id);
return { id, region: idToRegion(id) };
}
+1 -1
View File
@@ -1,4 +1,4 @@
import { usersDatabase } from '@blitzkit/core';
import { usersDatabase } from '../db/users';
import { client } from '../discord/client';
const EXIT_EVENTS = [
@@ -38,7 +38,7 @@ export async function getTankHistories(
locale: Locale,
options?: Partial<GetTankHistoriesOptions>,
) {
const { t } = translator(locale);
const { strings } = translator(locale);
const mergedOptions = { ...getTankHistoriesDefaultOptions, ...options };
const tankHistoriesResponse = await fetch(
`https://www.blitzstars.com/api/tankhistories/for/${id}/`,
@@ -82,7 +82,7 @@ export async function getTankHistories(
const tankStats = await getTankStats(server, id);
if (tankStats === null) {
throw new UserError(t`bot.common.errors.no_tank_stats`);
throw new UserError(strings.bot.common.errors.no_tank_stats);
}
tankStats.forEach((tankHistory) => {
@@ -1,4 +1,4 @@
import { usersDatabase } from '@blitzkit/core';
import { usersDatabase } from './users';
export async function flagUserActivity(blitzId: number) {
await usersDatabase.user.upsert({
@@ -1,5 +1,5 @@
import { usersDatabase } from '@blitzkit/core';
import { flagUserActivity } from './flagUserActivity';
import { usersDatabase } from './users';
export async function linkBlitzAndDiscord(discordId: bigint, blitzId: number) {
await usersDatabase.discordUser.upsert({
@@ -1,3 +1,3 @@
import { PrismaClient } from '@prisma/client';
import { PrismaClient } from '../../prisma';
export const usersDatabase = new PrismaClient();
@@ -3,14 +3,22 @@ import { translator } from '../localization/translator';
import { localizationObject } from './localizationObject';
export function addClanChoices(option: SlashCommandStringOption) {
const { translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return option
.setName('clan')
.setNameLocalizations(localizationObject('bot.common.options.clan'))
.setDescription(translate('bot.common.options.clan.description'))
.setNameLocalizations(
localizationObject(
(strings) => strings.bot.common.options.clan.name,
undefined,
true,
),
)
.setDescription(strings.bot.common.options.clan.description)
.setDescriptionLocalizations(
localizationObject('bot.common.options.clan.description'),
localizationObject(
(strings) => strings.bot.common.options.clan.description,
),
)
.setAutocomplete(true)
.setMinLength(2)
@@ -17,30 +17,40 @@ import { localizationObject } from './localizationObject';
export function addFilterOptions<
Option extends SlashCommandBuilder | SlashCommandSubcommandBuilder,
>(option: Option, nations: string[]) {
const { translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return (
option
.addStringOption((option) =>
option
.setName(translate('bot.common.options.filters.nation'))
.setName(strings.bot.common.options.filters.nation.name)
.setNameLocalizations(
localizationObject('bot.common.options.filters.nation'),
)
.setDescription(
translate('bot.common.options.filters.nation.description'),
localizationObject(
(strings) => strings.bot.common.options.filters.nation.name,
undefined,
true,
),
)
.setDescription(strings.bot.common.options.filters.nation.description)
.setDescriptionLocalizations(
localizationObject('bot.common.options.filters.nation.description'),
localizationObject(
(strings) =>
strings.bot.common.options.filters.nation.description,
),
)
.addChoices(
...nations.map(
(nation) =>
({
value: nation,
name: translate(`common.nations.${nation}`),
name: (strings.common.nations as Record<string, string>)[
nation
],
name_localizations: localizationObject(
`common.nations.${nation}`,
(strings) =>
(strings.common.nations as Record<string, string>)[
nation
],
),
}) satisfies APIApplicationCommandOptionChoice,
),
@@ -50,24 +60,28 @@ export function addFilterOptions<
.addStringOption((option) => addTierChoices(option).setRequired(false))
.addStringOption((option) =>
option
.setName(translate('bot.common.options.filters.class'))
.setName(strings.bot.common.options.filters.class.name)
.setNameLocalizations(
localizationObject('bot.common.options.filters.class'),
)
.setDescription(
translate('bot.common.options.filters.class.description'),
localizationObject(
(strings) => strings.bot.common.options.filters.class.name,
undefined,
true,
),
)
.setDescription(strings.bot.common.options.filters.class.description)
.setDescriptionLocalizations(
localizationObject('bot.common.options.filters.class.description'),
localizationObject(
(strings) => strings.bot.common.options.filters.class.description,
),
)
.addChoices(
...TANK_CLASSES.map(
(tankType) =>
({
value: `${tankType}`,
name: translate(`common.tank_class_short.${tankType}`),
name: strings.common.tank_class_short[tankType],
name_localizations: localizationObject(
`common.tank_class_short.${tankType}`,
(strings) => strings.common.tank_class_short[tankType],
),
}) satisfies APIApplicationCommandOptionChoice,
),
@@ -76,24 +90,28 @@ export function addFilterOptions<
)
.addStringOption((option) =>
option
.setName(translate('bot.common.options.filters.type'))
.setName(strings.bot.common.options.filters.type.name)
.setNameLocalizations(
localizationObject('bot.common.options.filters.type'),
)
.setDescription(
translate('bot.common.options.filters.type.description'),
localizationObject(
(strings) => strings.bot.common.options.filters.type.name,
undefined,
true,
),
)
.setDescription(strings.bot.common.options.filters.type.description)
.setDescriptionLocalizations(
localizationObject('bot.common.options.filters.type.description'),
localizationObject(
(strings) => strings.bot.common.options.filters.type.description,
),
)
.addChoices(
...TANK_TYPES.map(
(treeType) =>
({
value: TANK_TYPE_COMMAND_NAMES[treeType],
name: translate(`common.tree_type.${treeType}`),
name: strings.common.tree_type[treeType],
name_localizations: localizationObject(
`common.tree_type.${treeType}`,
(strings) => strings.common.tree_type[treeType],
),
}) satisfies APIApplicationCommandOptionChoice,
),
@@ -16,31 +16,67 @@ export function addPeriodSubCommands(
option: SlashCommandSubcommandBuilder,
) => SlashCommandSubcommandBuilder = (option) => option,
) {
const { translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
const subcommandNameLocalizations = {
today: localizationObject('bot.common.subcommands.today'),
30: localizationObject('bot.common.subcommands.30'),
60: localizationObject('bot.common.subcommands.60'),
90: localizationObject('bot.common.subcommands.90'),
career: localizationObject('bot.common.subcommands.career'),
custom: localizationObject('bot.common.subcommands.custom'),
today: localizationObject(
(strings) => strings.bot.common.subcommands.today.name,
undefined,
true,
),
30: localizationObject(
(strings) => strings.bot.common.subcommands[30].name,
undefined,
true,
),
60: localizationObject(
(strings) => strings.bot.common.subcommands[60].name,
undefined,
true,
),
90: localizationObject(
(strings) => strings.bot.common.subcommands[90].name,
undefined,
true,
),
career: localizationObject(
(strings) => strings.bot.common.subcommands.career.name,
undefined,
true,
),
custom: localizationObject(
(strings) => strings.bot.common.subcommands.custom.name,
undefined,
true,
),
};
const subcommandDescriptionLocalizations = {
today: localizationObject('bot.common.subcommands.today.description'),
30: localizationObject('bot.common.subcommands.30.description'),
60: localizationObject('bot.common.subcommands.60.description'),
90: localizationObject('bot.common.subcommands.90.description'),
career: localizationObject('bot.common.subcommands.career.description'),
custom: localizationObject('bot.common.subcommands.custom.description'),
today: localizationObject(
(strings) => strings.bot.common.subcommands.today.description,
),
30: localizationObject(
(strings) => strings.bot.common.subcommands[30].description,
),
60: localizationObject(
(strings) => strings.bot.common.subcommands[60].description,
),
90: localizationObject(
(strings) => strings.bot.common.subcommands[90].description,
),
career: localizationObject(
(strings) => strings.bot.common.subcommands.career.description,
),
custom: localizationObject(
(strings) => strings.bot.common.subcommands.custom.description,
),
};
option
.addSubcommand((option) =>
extra(
option
.setName(translate('bot.common.subcommands.today'))
.setName(strings.bot.common.subcommands.today.name)
.setNameLocalizations(subcommandNameLocalizations.today)
.setDescription(translate('bot.common.subcommands.today.description'))
.setDescription(strings.bot.common.subcommands.today.description)
.setDescriptionLocalizations(
subcommandDescriptionLocalizations.today,
),
@@ -49,38 +85,36 @@ export function addPeriodSubCommands(
.addSubcommand((option) =>
extra(
option
.setName(translate('bot.common.subcommands.30'))
.setName(strings.bot.common.subcommands[30].name)
.setNameLocalizations(subcommandNameLocalizations[30])
.setDescription(translate('bot.common.subcommands.30.description'))
.setDescription(strings.bot.common.subcommands[30].description)
.setDescriptionLocalizations(subcommandDescriptionLocalizations[30]),
),
)
.addSubcommand((option) =>
extra(
option
.setName(translate('bot.common.subcommands.60'))
.setName(strings.bot.common.subcommands[60].name)
.setNameLocalizations(subcommandNameLocalizations[60])
.setDescription(translate('bot.common.subcommands.60.description'))
.setDescription(strings.bot.common.subcommands[60].description)
.setDescriptionLocalizations(subcommandDescriptionLocalizations[60]),
),
)
.addSubcommand((option) =>
extra(
option
.setName(translate('bot.common.subcommands.90'))
.setName(strings.bot.common.subcommands[90].name)
.setNameLocalizations(subcommandNameLocalizations[90])
.setDescription(translate('bot.common.subcommands.90.description'))
.setDescription(strings.bot.common.subcommands[90].description)
.setDescriptionLocalizations(subcommandDescriptionLocalizations[90]),
),
)
.addSubcommand((option) =>
extra(
option
.setName(translate('bot.common.subcommands.career'))
.setName(strings.bot.common.subcommands.career.name)
.setNameLocalizations(subcommandNameLocalizations.career)
.setDescription(
translate('bot.common.subcommands.career.description'),
)
.setDescription(strings.bot.common.subcommands.career.description)
.setDescriptionLocalizations(
subcommandDescriptionLocalizations.career,
),
@@ -89,31 +123,25 @@ export function addPeriodSubCommands(
.addSubcommand((option) =>
extra(
option
.setName(translate('bot.common.subcommands.custom'))
.setName(strings.bot.common.subcommands.custom.name)
.setNameLocalizations(subcommandNameLocalizations.custom)
.setDescription(
translate('bot.common.subcommands.custom.description'),
)
.setDescription(strings.bot.common.subcommands.custom.description)
.setDescriptionLocalizations(
subcommandDescriptionLocalizations.custom,
)
.addIntegerOption((option) =>
option
.setName(translate('bot.common.subcommands.custom.options.start'))
.setName(strings.bot.common.subcommands.custom.options.start.name)
.setDescription(
translate(
'bot.common.subcommands.custom.options.start.description',
),
strings.bot.common.subcommands.custom.options.start.description,
)
.setRequired(true),
)
.addIntegerOption((option) =>
option
.setName(translate('bot.common.subcommands.custom.options.end'))
.setName(strings.bot.common.subcommands.custom.options.end.name)
.setDescription(
translate(
'bot.common.subcommands.custom.options.end.description',
),
strings.bot.common.subcommands.custom.options.end.description,
)
.setRequired(true),
),
@@ -8,23 +8,31 @@ import { translator } from '../localization/translator';
import { localizationObject } from './localizationObject';
export function addRegionChoices(option: SlashCommandStringOption) {
const { t, translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return option
.setName(t`bot.common.options.region`)
.setNameLocalizations(localizationObject('bot.common.options.region'))
.setDescription(t`bot.common.options.region.description`)
.setName(strings.bot.common.options.region.name)
.setNameLocalizations(
localizationObject(
(strings) => strings.bot.common.options.region.name,
undefined,
true,
),
)
.setDescription(strings.bot.common.options.region.description)
.setDescriptionLocalizations(
localizationObject('bot.common.options.region.description'),
localizationObject(
(strings) => strings.bot.common.options.region.description,
),
)
.addChoices(
...REGIONS.map(
(region) =>
({
value: region,
name: translate(`common.regions.normal.${region}`),
name: strings.common.regions.normal[region],
name_localizations: localizationObject(
`common.regions.normal.${region}`,
(strings) => strings.common.regions.normal[region],
),
}) satisfies APIApplicationCommandOptionChoice,
),
@@ -3,14 +3,22 @@ import { translator } from '../localization/translator';
import { localizationObject } from './localizationObject';
export function addTankChoices(option: SlashCommandStringOption) {
const { translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return option
.setName(translate('bot.common.options.tank'))
.setNameLocalizations(localizationObject('bot.common.options.tank'))
.setDescription(translate('bot.common.options.tank.description'))
.setName(strings.bot.common.options.tank.name)
.setNameLocalizations(
localizationObject(
(strings) => strings.bot.common.options.tank.name,
undefined,
true,
),
)
.setDescription(strings.bot.common.options.tank.description)
.setDescriptionLocalizations(
localizationObject('bot.common.options.tank.description'),
localizationObject(
(strings) => strings.bot.common.options.tank.description,
),
)
.setAutocomplete(true)
.setRequired(true);
@@ -1,4 +1,5 @@
import { numberToRomanNumeral } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import {
APIApplicationCommandOptionChoice,
Locale,
@@ -9,26 +10,34 @@ import { translator } from '../localization/translator';
import { localizationObject } from './localizationObject';
export function addTierChoices(option: SlashCommandStringOption) {
const { t, translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return option
.setName(t`bot.common.options.tier`)
.setNameLocalizations(localizationObject('bot.common.options.tier'))
.setDescription(t`bot.common.options.tier.description`)
.setName(strings.bot.common.options.tier.name)
.setNameLocalizations(
localizationObject(
(strings) => strings.bot.common.options.tier.name,
undefined,
true,
),
)
.setDescription(strings.bot.common.options.tier.description)
.setDescriptionLocalizations(
localizationObject('bot.common.options.tier.description'),
localizationObject(
(strings) => strings.bot.common.options.tier.description,
),
)
.setChoices(
...range(10, 0).map(
(tier) =>
({
name: translate('bot.common.options.tier.choices.tier', [
name: literals(strings.bot.common.options.tier.choices.tier, [
`${tier}`,
numberToRomanNumeral(tier),
]),
value: `${tier}`,
name_localizations: localizationObject(
'bot.common.options.tier.choices.tier',
(strings) => strings.bot.common.options.tier.choices.tier,
[`${tier}`, numberToRomanNumeral(tier)],
),
}) satisfies APIApplicationCommandOptionChoice,
@@ -3,14 +3,22 @@ import { translator } from '../localization/translator';
import { localizationObject } from './localizationObject';
export function addUsernameChoices(option: SlashCommandStringOption) {
const { t } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return option
.setName(t`bot.common.options.username`)
.setNameLocalizations(localizationObject('bot.common.options.username'))
.setDescription(t`bot.common.options.username.description`)
.setName(strings.bot.common.options.username.name)
.setNameLocalizations(
localizationObject(
(strings) => strings.bot.common.options.username.name,
undefined,
true,
),
)
.setDescription(strings.bot.common.options.username.description)
.setDescriptionLocalizations(
localizationObject('bot.common.options.username.description'),
localizationObject(
(strings) => strings.bot.common.options.username.description,
),
)
.setAutocomplete(true);
}
@@ -10,7 +10,7 @@ import { DISCORD_CHOICES_MAX_NAME_SIZE, OVERFLOW_SUFFIX } from './constants';
export async function autocompleteClan(
interaction: AutocompleteInteraction<CacheType>,
) {
const { translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const focusedOption = interaction.options.getFocused(true);
if (focusedOption.name !== 'clan') return;
if (focusedOption.value.length < 2) return interaction.respond([]);
@@ -19,9 +19,7 @@ export async function autocompleteClan(
await interaction.respond(
clans
? clans.map((clan) => {
let name = `${clan.name} (${translate(
`common.regions.short.${clan.region}`,
)})`;
let name = `${clan.name} (${strings.common.regions.short[clan.region]})`;
if (name.length > DISCORD_CHOICES_MAX_NAME_SIZE) {
const overSize = name.length - DISCORD_CHOICES_MAX_NAME_SIZE;
@@ -29,9 +27,7 @@ export async function autocompleteClan(
name = `${clan.name.slice(
0,
clan.name.length - overSize - OVERFLOW_SUFFIX.length,
)}${OVERFLOW_SUFFIX} (${translate(
`common.regions.short.${clan.region}`,
)})`;
)}${OVERFLOW_SUFFIX} (${strings.common.regions.short[clan.region]})`;
}
return {
@@ -1,15 +1,16 @@
import { fetchTankNames, TankType } from '@blitzkit/core';
import { fetchTankNames, SEARCH_KEYS, TankType } from '@blitzkit/core';
import {
ApplicationCommandOptionChoiceData,
AutocompleteInteraction,
CacheType,
} from 'discord.js';
import { go } from 'fuzzysort';
import { tankNames } from '../blitzkit/nonBlockingPromises';
import { translator } from '../localization/translator';
import {
DISCORD_CHOICES_MAX_NAME_SIZE,
OVERFLOW_SUFFIX,
} from './autocompleteClan/constants';
import { tankNames } from '../blitzkit/nonBlockingPromises';
export const tankNamesTechTreeOnly = fetchTankNames().then((names) =>
names.filter((tank) => tank.treeType === TankType.RESEARCHABLE),
@@ -20,6 +21,7 @@ export async function autocompleteTanks(
techTreeOnly = false,
fields = ['tank'],
) {
const { unwrap } = translator(interaction.locale);
const focusedOption = interaction.options.getFocused(true);
if (!fields.includes(focusedOption.name)) return;
@@ -29,19 +31,16 @@ export async function autocompleteTanks(
go(
focusedOption.value,
await (techTreeOnly ? tankNamesTechTreeOnly : tankNames),
{
keys: ['searchableName', 'searchableNameDeburr', 'camouflages'],
limit: 10,
},
{ keys: SEARCH_KEYS, limit: 10 },
).map(async (item) => {
let name = item.obj.searchableName;
let name = unwrap(item.obj.searchableName);
if (name.length > DISCORD_CHOICES_MAX_NAME_SIZE) {
const overSize = name.length - DISCORD_CHOICES_MAX_NAME_SIZE;
name = `${item.obj.searchableName.slice(
name = `${unwrap(item.obj.searchableName).slice(
0,
item.obj.searchableName.length -
unwrap(item.obj.searchableName).length -
overSize -
OVERFLOW_SUFFIX.length,
)}${OVERFLOW_SUFFIX}`;
@@ -13,7 +13,7 @@ import {
export async function autocompleteUsername(
interaction: AutocompleteInteraction<CacheType>,
) {
const { translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const focusedOption = interaction.options.getFocused(true);
if (focusedOption.name !== 'username') return;
const players = await searchPlayersAcrossRegions(focusedOption.value);
@@ -22,9 +22,9 @@ export async function autocompleteUsername(
await interaction.respond(
players
? players.map((player) => {
let name = `${player.nickname} (${translate(
`common.regions.short.${player.region}`,
)})`;
let name = `${player.nickname} (${
strings.common.regions.short[player.region]
})`;
if (name.length > DISCORD_CHOICES_MAX_NAME_SIZE) {
const overSize = name.length - DISCORD_CHOICES_MAX_NAME_SIZE;
@@ -32,9 +32,9 @@ export async function autocompleteUsername(
name = `${player.nickname.slice(
0,
player.nickname.length - overSize - OVERFLOW_SUFFIX.length,
)}${OVERFLOW_SUFFIX} (${translate(
`common.regions.short.${player.region}`,
)})`;
)}${OVERFLOW_SUFFIX} (${
strings.common.regions.short[player.region]
})`;
}
return {
@@ -6,13 +6,13 @@ export function buttonRefresh(
interaction: ChatInputCommandInteraction,
path: string,
) {
const { t } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
if (
interaction.appPermissions?.has('ReadMessageHistory') &&
interaction.appPermissions?.has('ViewChannel')
) {
return buttonPrimary(path, t`bot.common.buttons.refresh`);
return buttonPrimary(path, strings.bot.common.buttons.refresh);
}
return null;
@@ -1,3 +1,4 @@
import { BlitzKitStrings } from '@blitzkit/i18n';
import {
Locale,
SlashCommandBuilder,
@@ -23,24 +24,47 @@ export function createLocalizedCommand(
| CreateLocalizedCommandSubcommand[]
| CreateLocalizedCommandSubcommandGroup[],
) {
const commandPathItem = command.replaceAll('-', '_');
const { translate } = translator(Locale.EnglishUS);
const commandPathItem = command.replaceAll(
'-',
'_',
) as keyof BlitzKitStrings['bot']['commands'];
const { strings } = translator(Locale.EnglishUS);
const slashCommand = new SlashCommandBuilder()
.setName(command)
.setNameLocalizations(localizationObject(`bot.commands.${commandPathItem}`))
.setDescription(translate(`bot.commands.${commandPathItem}.description`))
.setNameLocalizations(
localizationObject(
(strings) => strings.bot.commands[commandPathItem].name,
undefined,
true,
),
)
.setDescription(strings.bot.commands[commandPathItem].description)
.setDescriptionLocalizations(
localizationObject(`bot.commands.${commandPathItem}.description`),
localizationObject(
(strings) => strings.bot.commands[commandPathItem].description,
),
);
if (sub) {
sub.map((subItem) => {
if ('subcommand' in subItem) {
const subcommandNameLocalizations = localizationObject(
`bot.commands.${commandPathItem}.subcommands.${subItem.subcommand}`,
(strings) =>
(
strings.bot.commands[commandPathItem] as unknown as {
subcommands: Record<string, { name: string }>;
}
).subcommands[subItem.subcommand].name,
undefined,
true,
);
const subcommandDescriptionLocalizations = localizationObject(
`bot.commands.${commandPathItem}.subcommands.${subItem.subcommand}.description`,
(strings) =>
(
strings.bot.commands[commandPathItem] as unknown as {
subcommands: Record<string, { description: string }>;
}
).subcommands[subItem.subcommand].description,
);
slashCommand.addSubcommand((option) => {
@@ -50,18 +74,32 @@ export function createLocalizedCommand(
.setName(subItem.subcommand)
.setNameLocalizations(subcommandNameLocalizations)
.setDescription(
translate(
`bot.commands.${commandPathItem}.subcommands.${subItem.subcommand}.description`,
),
(
strings.bot.commands[commandPathItem] as unknown as {
subcommands: Record<string, { description: string }>;
}
).subcommands[subItem.subcommand].description,
)
.setDescriptionLocalizations(subcommandDescriptionLocalizations);
});
} else {
const subcommandNameLocalizations = localizationObject(
`bot.commands.${commandPathItem}.groups.${subItem.group}`,
(strings) =>
(
strings.bot.commands[commandPathItem] as unknown as {
groups: Record<string, { name: string }>;
}
).groups[subItem.group].name,
undefined,
true,
);
const subcommandDescriptionLocalizations = localizationObject(
`bot.commands.${commandPathItem}.groups.${subItem.group}.description`,
(strings) =>
(
strings.bot.commands[commandPathItem] as unknown as {
groups: Record<string, { description: string }>;
}
).groups[subItem.group].description,
);
slashCommand.addSubcommandGroup((option) => {
@@ -71,9 +109,11 @@ export function createLocalizedCommand(
.setName(subItem.group)
.setNameLocalizations(subcommandNameLocalizations)
.setDescription(
translate(
`bot.commands.${commandPathItem}.groups.${subItem.group}.description`,
),
(
strings.bot.commands[commandPathItem] as unknown as {
groups: Record<string, { description: string }>;
}
).groups[subItem.group].description,
)
.setDescriptionLocalizations(subcommandDescriptionLocalizations);
});
@@ -1,10 +1,11 @@
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import { translator } from '../localization/translator';
import { PeriodSize } from './addPeriodSubCommands';
export function getPeriodOptionName(period: PeriodSize, locale: Locale) {
const { translate } = translator(locale);
if (period === 'career') return translate('bot.common.periods.career');
if (period === 'today') return translate('bot.common.periods.today');
return translate('bot.common.periods.days', [period]);
const { strings } = translator(locale);
if (period === 'career') return strings.bot.common.periods.career;
if (period === 'today') return strings.bot.common.periods.today;
return literals(strings.bot.common.periods.days, [period]);
}
@@ -1,14 +1,33 @@
import { BlitzKitStrings, literals as literalsFunc } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import { SUPPORTED_LOCALES } from '../localization/strings/constants';
import { SUPPORTED_LOCALES_DISCORD } from '../localization/strings/constants';
import { translator } from '../localization/translator';
export function localizationObject(path: string, literals?: string[]) {
return SUPPORTED_LOCALES.reduce<Partial<Record<Locale, string>>>(
const validNameRegex =
/^[\p{Ll}\p{Lm}\p{Lo}\p{N}\p{sc=Devanagari}\p{sc=Thai}_-]+$/u;
export function localizationObject(
path: (strings: BlitzKitStrings) => string,
literals: string[] = [],
strict = false,
) {
return SUPPORTED_LOCALES_DISCORD.reduce<Partial<Record<Locale, string>>>(
(localizations, locale) => {
const { translate } = translator(locale);
const { strings } = translator(locale);
const translation = literalsFunc(path(strings), literals);
const isValid =
(!strict || validNameRegex.test(translation)) && translation.length > 0;
if (!isValid) {
console.warn(
`Invalid localization for ${locale} (${path}): ${translation}; skipping...`,
);
return localizations;
}
return {
...localizations,
[locale]: translate(path, literals),
[locale]: translation,
};
},
{},
-6
View File
@@ -1,6 +0,0 @@
import { assertSecret } from '@blitzkit/core';
import { ShardingManager } from 'discord.js';
export const manager = new ShardingManager('dist/bot/workers/bot.js', {
token: assertSecret(import.meta.env.DISCORD_TOKEN),
});
@@ -1,4 +1,5 @@
import { Region } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { ChatInputCommandInteraction } from 'discord.js';
import markdownEscape from 'markdown-escape';
import { searchClansAcrossRegionsBotWrapper } from '../blitz/searchClansAcrossRegionsBotWrapper';
@@ -10,7 +11,7 @@ export const serverAndIdPattern = /(com|eu|asia)\/[0-9]+/;
export async function resolveClanFromCommand(
interaction: ChatInputCommandInteraction,
) {
const { translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const clan = interaction.options.getString('clan', true);
if (serverAndIdPattern.test(clan)) {
@@ -26,7 +27,9 @@ export async function resolveClanFromCommand(
return { region: accounts[0].region, id: accounts[0].clan_id };
} else {
throw new UserError(
translate('bot.common.errors.clan_not_found', [markdownEscape(clan)]),
literals(strings.bot.common.errors.clan_not_found, [
markdownEscape(clan),
]),
);
}
}
@@ -1,4 +1,5 @@
import { getTimeDaysAgo, Region } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { CacheType, ChatInputCommandInteraction } from 'discord.js';
import { getPeriodNow } from '../blitzkit/getPeriodNow';
import { getPeriodStart } from '../blitzkit/getPeriodStart';
@@ -17,7 +18,7 @@ export function resolvePeriodFromCommand(
interaction: ChatInputCommandInteraction<CacheType>,
forcedPeriod?: PeriodType,
) {
const { translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
let name: string;
let start: number;
let end: number;
@@ -31,7 +32,7 @@ export function resolvePeriodFromCommand(
const startDaysAgoMin = Math.min(startOption, endOption);
const endDaysAgoMax = Math.max(startOption, endOption);
name = translate('bot.common.periods.custom', [
name = literals(strings.bot.common.periods.custom, [
`${startDaysAgoMin}`,
`${endDaysAgoMax}`,
]);
@@ -1,4 +1,5 @@
import { idToRegion, Region } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { CacheType, ChatInputCommandInteraction } from 'discord.js';
import markdownEscape from 'markdown-escape';
import { searchPlayersAcrossRegionsBotWrapper } from '../../blitz/searchPlayersAcrossRegionsBotWrapper';
@@ -14,7 +15,7 @@ export interface ResolvedPlayer {
export async function resolvePlayerFromCommand(
interaction: ChatInputCommandInteraction<CacheType>,
) {
const { t, translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const commandUsername = interaction.options.getString('username');
if (commandUsername !== null) {
@@ -36,7 +37,7 @@ export async function resolvePlayerFromCommand(
} satisfies ResolvedPlayer;
} else {
throw new UserError(
translate('bot.common.errors.player_not_found', [
literals(strings.bot.common.errors.player_not_found, [
markdownEscape(commandUsername),
]),
);
@@ -47,6 +48,6 @@ export async function resolvePlayerFromCommand(
if (account) return account;
throw new UserError(t`bot.common.errors.player_not_linked`);
throw new UserError(strings.bot.common.errors.player_not_linked);
}
}
@@ -1,3 +1,25 @@
import { DEFAULT_LOCALE, SupportedLocale } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
export const SUPPORTED_LOCALES: Locale[] = [Locale.EnglishUS, Locale.SpanishES];
const SUPPORTED_LOCALES_DISCORD_MAP: Record<SupportedLocale, Locale> = {
en: Locale.EnglishUS,
es: Locale.SpanishES,
pt: Locale.PortugueseBR,
ru: Locale.Russian,
uk: Locale.Ukrainian,
zh: Locale.ChineseCN,
fr: Locale.French,
ja: Locale.Japanese,
pl: Locale.Polish,
};
export const SUPPORTED_LOCALES_DISCORD_MAP_INVERSE = Object.fromEntries(
Object.entries(SUPPORTED_LOCALES_DISCORD_MAP).map(([k, v]) => [v, k]),
) as Record<Locale, SupportedLocale>;
export const SUPPORTED_LOCALES_DISCORD = Object.values(
SUPPORTED_LOCALES_DISCORD_MAP,
);
export const DEFAULT_LOCALE_DISCORD =
SUPPORTED_LOCALES_DISCORD_MAP[DEFAULT_LOCALE];
@@ -1,19 +0,0 @@
import { Locale } from 'discord.js';
import { SUPPORTED_LOCALES } from './constants';
export type TranslationFragmentTree = {
[key: string]: TranslationFragment;
} & {
$?: string;
};
export type TranslationFragment = string | TranslationFragmentTree;
export const translations = SUPPORTED_LOCALES.reduce<
Partial<Record<Locale, TranslationFragment>>
>(
(table, locale) => ({
...table,
[locale]: require(`../../../../../core/lang/${locale}.json`),
}),
{},
);
@@ -1,74 +1,20 @@
import { STRINGS, unwrapper } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import { translations } from './strings';
import { SUPPORTED_LOCALES } from './strings/constants';
import {
DEFAULT_LOCALE_DISCORD,
SUPPORTED_LOCALES_DISCORD,
SUPPORTED_LOCALES_DISCORD_MAP_INVERSE,
} from './strings/constants';
export function translator(localeRaw: Locale) {
const locale =
localeRaw !== Locale.EnglishUS && SUPPORTED_LOCALES.includes(localeRaw)
localeRaw !== DEFAULT_LOCALE_DISCORD &&
SUPPORTED_LOCALES_DISCORD.includes(localeRaw)
? localeRaw
: Locale.EnglishUS;
const strings = translations[locale]!;
: DEFAULT_LOCALE_DISCORD;
const bkLocale = SUPPORTED_LOCALES_DISCORD_MAP_INVERSE[locale];
const strings = STRINGS[bkLocale];
const unwrap = unwrapper(bkLocale);
function translate(path: string, literals?: string[]): string {
const pathArray = path.split('.');
let fragment = strings;
let resolvedString: string;
for (const pathItem of pathArray) {
if (typeof fragment === 'string') {
throw new SyntaxError(
`Attempted to access string with key "${pathItem}" in "${path}" from within a string instead of an object in locale "${locale}"`,
);
} else {
fragment = fragment[pathItem];
if (typeof fragment === 'undefined') {
const message = `Undefined translation at "${pathItem}" in "${path}" for locale "${locale}"`;
if (locale === Locale.EnglishUS) {
throw new Error(message);
} else {
console.warn(`${message} falling back to en-US`);
return translator(Locale.EnglishUS).translate(path, literals);
}
}
}
}
if (typeof fragment === 'string') {
resolvedString = fragment;
} else if (fragment.$) {
resolvedString = fragment.$!;
} else {
throw new Error(
`Unresolved tree ending for "${path}" in locale "${locale}"`,
);
}
if (literals) {
const chunks = resolvedString.split('%s');
let embeddedString = '';
chunks.forEach((chunk, index) => {
if (index !== 0) embeddedString += literals[index - 1];
embeddedString += chunk;
});
return embeddedString;
}
return resolvedString;
}
function t(paths: TemplateStringsArray, ...embeds: string[]) {
return paths
.map((path, index) =>
path.length === 0
? ''
: `${translate(path)}${index === embeds.length ? '' : embeds[index]}`,
)
.join('');
}
return { locale, translate, t };
return { locale, strings, unwrap, bkLocale };
}
@@ -8,22 +8,22 @@ import {
InteractionReplyOptions,
} from 'discord.js';
import { InteractionRawReturnable, commands } from '..';
import { UserError } from '../../../core/blitzkit/userError';
import { buttonLink } from '../../../core/discord/buttonLink';
import { embedWarning } from '../../../core/discord/embedWarning';
import { normalizeInteractionReturnable } from '../../../core/discord/normalizeInteractionReturnable';
import { psa } from '../../../core/discord/psa';
import { translator } from '../../../core/localization/translator';
import { Writeable } from '../../../types/writable';
import { UserError } from '../../../core/blitzkit/userError';
export async function handleChatInputCommand(
interaction: ChatInputCommandInteraction<CacheType>,
) {
await interaction.deferReply();
const awaitedCommands = await commands;
const registry = awaitedCommands[interaction.commandName];
await interaction.deferReply();
try {
const returnable = await registry.handler(interaction);
@@ -126,16 +126,16 @@ export async function handleChatInputCommand(
index++;
}
} catch (error) {
const { t } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const components = [
new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setLabel(t`bot.common.errors.get_help`)
.setLabel(strings.bot.common.errors.get_help)
.setURL('https://discord.gg/nDt7AjGJQH')
.setStyle(ButtonStyle.Link),
),
];
if (error instanceof UserError) {
interaction.editReply({
content: error.message,
@@ -143,9 +143,8 @@ export async function handleChatInputCommand(
});
} else {
console.error(interaction.commandName, error);
interaction.editReply({
content: t`bot.common.errors.uncaught_error`,
content: strings.bot.common.errors.uncaught_error,
components,
});
}
@@ -15,7 +15,6 @@ import {
SlashCommandSubcommandsOnlyBuilder,
} from 'discord.js';
import { aboutCommand } from '../../commands/about';
import { auctionCommand } from '../../commands/auction';
import { breakdownCommand } from '../../commands/breakdown';
import { debugCommand } from '../../commands/debug';
import { evolutionCommand } from '../../commands/evolution';
@@ -64,20 +63,23 @@ interface CommandRegistryBase {
autocomplete?: (interaction: AutocompleteInteraction<CacheType>) => void;
button?: (interaction: ButtonInteraction<CacheType>) => InteractionReturnable;
}
interface CommandRegistryImplicit extends CommandRegistryBase {
handlesInteraction: true;
handler: (interaction: ChatInputCommandInteraction<CacheType>) => void;
}
interface CommandRegistryExplicit extends CommandRegistryBase {
handlesInteraction?: false;
handler: (
interaction: ChatInputCommandInteraction<CacheType>,
) => InteractionReturnable;
}
export type CommandRegistry = CommandRegistryImplicit | CommandRegistryExplicit;
export const COMMANDS_RAW: Promise<CommandRegistry>[] = [
auctionCommand,
// auctionCommand,
permissionsCommand,
debugCommand,
aboutCommand,
@@ -107,10 +109,8 @@ export const commands = Promise.allSettled(COMMANDS_RAW).then((rawCommands) => {
`Command ${index} failed to load; skipping...`,
registry.reason,
);
return commands;
}
return { ...commands, [registry.value.command.name]: registry.value };
},
{},
@@ -126,9 +126,7 @@ commands.then((awaitedCommands) => {
rest.put(
Routes.applicationCommands(assertSecret(import.meta.env.DISCORD_CLIENT_ID)),
{
body,
},
{ body },
);
});
+68 -54
View File
@@ -1,67 +1,81 @@
import { ActivityType } from 'discord.js';
import { assertSecret } from '@blitzkit/core';
import { ActivityType, ShardingManager } from 'discord.js';
import {
discoveredIdsDefinitions,
tankDefinitions,
} from './core/blitzkit/nonBlockingPromises';
import { manager } from './core/discord/manager';
console.log('pre manager event listeners and spawn');
let iteration = 0;
const interval = setInterval(async () => {
iteration++;
const shards = await manager
.on('shardCreate', (shard) => {
console.log(`🟡 Launching shard ${shard.id}`);
shard.on('ready', () => console.log(`🟢 Launched shard ${shard.id}`));
})
.spawn();
console.log(`Attempting launch on iteration ${iteration}`);
console.log('post manager event listeners and spawn');
const manager = new ShardingManager('dist/bot/workers/bot.js', {
token: assertSecret(import.meta.env.DISCORD_TOKEN),
});
try {
let servers = 0;
let channels = 0;
let users = 0;
const shards = await manager
.on('shardCreate', (shard) => {
clearInterval(interval);
console.log(`🟡 Launching shard ${shard.id}`);
shard.on('ready', () => console.log(`🟢 Launched shard ${shard.id}`));
})
.spawn();
for (const [, shard] of shards) {
// wtf is this typing bro???
const guilds = (await shard.fetchClientValue('guilds.cache')) as any;
try {
let servers = 0;
let channels = 0;
let users = 0;
servers += guilds.length;
for (const [, shard] of shards) {
const guilds = (await shard.fetchClientValue('guilds.cache')) as any;
for (const guild of guilds) {
channels += guild.channels.length;
users += guild.memberCount;
servers += guilds.length;
for (const guild of guilds) {
channels += guild.channels.length;
users += guild.memberCount;
}
}
const awaitedTankDefinitions = await tankDefinitions;
const tanks = Object.values(awaitedTankDefinitions.tanks).length;
const awaitedDiscoveredIdsDefinitions = await discoveredIdsDefinitions;
const usernames = awaitedDiscoveredIdsDefinitions.count;
manager.broadcastEval(
async (client, { servers, users, channels, tanks, usernames }) => {
const bios = [
`Living in ${servers.toLocaleString()} servers`,
`Serving ${users.toLocaleString()} users`,
`Observing ${channels.toLocaleString()} channels`,
`Tinkering with ${tanks.toLocaleString()} tanks`,
`Memorizing ${usernames.toLocaleString()} usernames`,
];
client.user?.setPresence({
activities: [
{
type: ActivityType.Custom,
name: bios[Math.floor(Math.random() * bios.length)],
},
],
});
for (const guild of client.guilds.cache.values()) {
try {
const botMember = await guild.members.fetch(client.user.id);
await botMember.setNickname('BlitzKit');
} catch {}
}
},
{ context: { servers, channels, users, tanks, usernames } },
);
} catch (error) {
console.warn(
'An error occurred when setting up presence, ignoring to keep the process alive',
error,
);
}
const awaitedTankDefinitions = await tankDefinitions;
const tanks = Object.values(awaitedTankDefinitions.tanks).length;
const awaitedDiscoveredIdsDefinitions = await discoveredIdsDefinitions;
const usernames = awaitedDiscoveredIdsDefinitions.count;
manager.broadcastEval(
(client, { servers, users, channels, tanks, usernames }) => {
const bios = [
`Living in ${servers.toLocaleString()} servers`,
`Serving ${users.toLocaleString()} users`,
`Observing ${channels.toLocaleString()} channels`,
`Tinkering with ${tanks.toLocaleString()} tanks`,
`Memorizing ${usernames.toLocaleString()} usernames`,
];
client.user?.setPresence({
activities: [
{
type: ActivityType.Custom,
name: bios[Math.floor(Math.random() * bios.length)],
},
],
});
},
{ context: { servers, channels, users, tanks, usernames } },
);
} catch (error) {
console.warn(
'An error occurred when setting up presence, ignoring to keep the process alive',
error,
);
}
}, 5000);
+1 -1
View File
@@ -23,7 +23,7 @@ export const theme = {
spaces: createSpaces(),
colors: {
...createColors(slateDark),
...createColors(mauveDark),
...createColors(slateDarkA, 'alpha'),
...createColors(slate, 'light'),
+8 -1
View File
@@ -1,4 +1,11 @@
{
"extends": "../../tsconfig.json",
"include": ["src/**/*"]
"include": [
"src/**/*",
"../core/src/blitzkit/supportedLocales.ts",
"../i18n/src/unwrapper.ts"
],
"compilerOptions": {
"baseUrl": "../../"
}
}
-683
View File
@@ -1,683 +0,0 @@
{
"common": {
"wn8_percentile": {
"0": "Very bad",
"1": "Bad",
"2": "Below average",
"3": "Average",
"4": "Above average",
"5": "Good",
"6": "Very good",
"7": "Great",
"8": "Unicum",
"9": "Super unicum"
},
"bkni_percentile": {
"0": "Super malum",
"1": "Malum",
"2": "Very bad",
"3": "Bad",
"4": "Below average",
"5": "Average",
"6": "Above average",
"7": "Good",
"8": "Very good",
"9": "Unicum",
"10": "Super unicum"
},
"nations": {
"ussr": "U.S.S.R.",
"germany": "Germany",
"usa": "U.S.A.",
"china": "China",
"uk": "U.K.",
"france": "France",
"japan": "Japan",
"european": "European Nation",
"other": "Hybrid Nation"
},
"nations_adjectives": {
"ussr": "U.S.S.R.",
"germany": "German",
"usa": "U.S.A.",
"china": "Chinese",
"uk": "U.K.",
"france": "French",
"japan": "Japanese",
"european": "European",
"other": "Hybrid"
},
"tank_class_short": {
"-1": "unrecognized",
"0": "Light",
"1": "Medium",
"2": "Heavy",
"3": "TD"
},
"tank_class_short_plural_lowercase": {
"0": "lights",
"1": "mediums",
"2": "heavies",
"3": "TDs"
},
"tank_class_medium": {
"-1": "unrecognized",
"0": "Light",
"1": "Medium",
"2": "Heavy",
"3": "Tank destroyer"
},
"tree_type": {
"-1": "unrecognized",
"0": "Tech tree",
"1": "Premium",
"2": "Collector"
},
"regions": {
"normal": {
"com": "North America",
"eu": "Europe",
"asia": "Asia-Pacific"
},
"short": {
"com": "NA",
"eu": "EU",
"asia": "APAC"
}
},
"leagues": {
"diamond": "Diamond",
"platinum": "Platinum",
"gold": "Gold",
"silver": "Silver",
"bronze": "Bronze"
},
"composite_stats": {
"normalized_damage_dealt": "Damage",
"normalized_damage_received": "Damage received",
"normalized_deaths": "Deathrate",
"normalized_frags": "Kills",
"normalized_hits": "Hits",
"normalized_losses": "Lossrate",
"normalized_misses": "Misses",
"normalized_shots": "Shots",
"normalized_spotted": "Spots",
"normalized_survived_battles": "Survival rate",
"normalized_win_and_survived": "Winning and surviving rate",
"normalized_wins": "Winrate",
"normalized_xp": "XP",
"normalized_battle_life_time": "Battle time",
"cumulative_accuracy": "Accuracy",
"cumulative_battles": "Battles",
"cumulative_capture_points": "Total capture points",
"cumulative_damage_dealt": "Total damage",
"cumulative_damage_ratio": "Damage ratio",
"cumulative_damage_received": "Total damage received",
"cumulative_deaths": "Total deaths",
"cumulative_dropped_capture_points": "Total dropped capture points",
"cumulative_frags": "Total kills",
"cumulative_hits": "Total hits",
"cumulative_inaccuracy": "Inaccuracy",
"cumulative_kills_to_death_ratio": "Kills to death ratio",
"cumulative_losses": "Losses",
"cumulative_misses": "Total misses",
"cumulative_shots": "Total shots",
"cumulative_spotted": "Total spots",
"cumulative_survived_battles": "Survived battles",
"cumulative_win_and_survived": "Won and survived battles",
"cumulative_wins": "Wins",
"cumulative_xp": "Total XP",
"cumulative_wn8": "WN8",
"cumulative_battle_life_time": "Total battle time"
}
},
"bot": {
"common": {
"errors": {
"clan_not_found": "# Could not find clan\nCouldn't find \"%s\". Try selecting a clan from the search result.",
"player_not_found": "# Could not find user\nI couldn't find user \"%s\". Try picking an user from the search result, typing in a valid username, or using the `/link` command.",
"player_not_linked": "# Use `/link` to get started\nI don't know who you are but you can tell me today!",
"tank_name_not_found": "# Tank not found\nCould not find tank by the name \"%s\".",
"tank_id_not_found": "# Tank not found\nCould not find tank by the ID \"%s\". Try typing the name of the tank instead of a number.",
"no_tank_stats": "# No tank stats available\nThis player doesn't have any stats available. This may not be the player you are looking for.",
"get_help": "Help Discord server",
"uncaught_error": "# BlitzKit ran into an error!\nWe're so sorry about this. Feel free to [join the official Discord server](https://discord.gg/nDt7AjGJQH) to get help.",
"preview": "# This command is in preview\n\nYou can only access this command in [the official Discord server](https://discord.gg/nDt7AjGJQH). The command will be generally available soon.",
"query_too_small": "# Your search is too short\nYour search must be at least 3 characters long.",
"query_too_long": "# Your search is too long\nYour search must be at most 100 characters long."
},
"wn8": {
"$": "WN8",
"very_bad": "Very bad",
"bad": "Bad",
"below_average": "Below average",
"average": "Average",
"above_average": "Above average",
"good": "Good",
"very_good": "Very good",
"great": "Great",
"unicum": "Unicum",
"super_unicum": "Super unicum"
},
"subcommands": {
"today": {
"$": "today",
"description": "Today"
},
"30": {
"$": "30",
"description": "30 days"
},
"60": {
"$": "60",
"description": "60 days"
},
"90": {
"$": "90",
"description": "90 days"
},
"career": {
"$": "career",
"description": "Career"
},
"custom": {
"$": "custom",
"description": "Custom range",
"options": {
"start": {
"$": "start",
"description": "Start in number of days ago from today"
},
"end": {
"$": "end",
"description": "End in number of days ago from today"
}
}
}
},
"options": {
"clan": {
"$": "clan",
"description": "The clan name or tag"
},
"tank": {
"$": "tank",
"description": "The tank to get stats for"
},
"username": {
"$": "username",
"description": "The Blitz username of the player"
},
"filters": {
"nation": {
"$": "nation",
"description": "Nation"
},
"class": {
"$": "class",
"description": "Class"
},
"type": {
"$": "type",
"description": "Type"
}
},
"region": {
"$": "region",
"description": "Region"
},
"tier": {
"$": "tier",
"description": "Tier",
"choices": {
"tier": "Tier %s - %s"
}
}
},
"periods": {
"career": "Career",
"today": "Today",
"days": "%s days",
"custom": "%s - %s days"
},
"filters": {
"none": "No filters",
"tier": "Tier %s"
},
"no_data": {
"battles_in_period": "No battles played in this period",
"battles_in_season": "No battles played in this season",
"players_in_period": "No players in this period",
"tanks_found": "No tanks found"
},
"buttons": {
"refresh": "Refresh"
},
"hero_stats": {
"battles": "Battles",
"winrate": "Winrate",
"damage": "Damage",
"damage_ratio": "Damage ratio",
"tier": "Tier",
"survival": "Survival",
"accuracy": "Accuracy",
"kills": "Kills"
}
},
"commands": {
"auction": {
"$": "auction",
"description": "Tanks available right now in the auction",
"body": {
"title": "Blitz Auction",
"subtitle": "%s available • next prices %s\nBid now: %s",
"next": "Next price: %s",
"available": "Available: %s / %s"
},
"errors": {
"no_auction": "# No ongoing auction\n\nCurrently, there are no tanks available for purchase in the auction. If you think this is an error, please report this discrepancy in [the official Discord Server](https://discord.gg/nDt7AjGJQH)."
},
"actions": {
"bid": "Bid now"
}
},
"replay": {
"$": "replay",
"description": "Display replay results"
},
"research": {
"$": "research",
"description": "Lists the expenses in researching a line",
"options": {
"target_tank": {
"$": "target-tank",
"description": "The final tank to research in the line"
},
"starting_tank": {
"$": "starting-tank",
"description": "The starting tank in the line (inferred automatically if not provided)"
}
},
"body": {
"subtitle": "Research",
"total": "Total",
"research": "Research: %s",
"upgrades": "Upgrades: %s",
"purchase": "Purchase: %s",
"equipment": "Equipment: %s",
"estimation": "# This is an estimation!\n\nThis list may include already researched tanks because of the inaccurate information Wargaming provides. Try providing a `starting-tank` from the line when using the command if you can."
},
"errors": {
"non_tech_tree": "# %s is not on the tech tree\n\nYou cannot research a premium/collector tank.",
"tier_1": "# %s is a tier I\n\nTier I tanks are always unlocked for free for all players.",
"already_researched": "# %s is already researched\n\nTry providing a `starting-tank` when using the command to view the totals for a line you have already researched.",
"start_end_equal": "# %s was provided twice\n\nMake sure `starting-tank` and `target-tank` are different.",
"tanks_not_on_line": "# %s and %s are not on the same line\n\nMake sure `starting-tank` and `target-tank` are on the same tech-tree line.",
"unordered": "# %s higher tiered to %s\n\nMake sure to provide %s as the `starting-tank` instead as it is the lower tiered tank."
}
},
"about": {
"$": "about",
"description": "All the info you need about the bot",
"subcommands": {
"commands": {
"$": "commands",
"description": "List of all commands and their descriptions",
"body": "# Commands\n\nBlitzKit offers the following commands:\n\n"
},
"embeds": {
"$": "embeds",
"description": "How embeds work"
},
"introduction": {
"$": "introduction",
"description": "About the bot"
},
"invite": {
"$": "invite",
"description": "How to invite the bot"
},
"timezones": {
"$": "timezones",
"description": "How BlitzKit infers and uses your timezone"
},
"credits": {
"$": "credits",
"description": "People who have helped BlitzKit"
}
}
},
"breakdown": {
"$": "breakdown",
"description": "A period's breakdown by tanks played",
"body": {
"total": "Total",
"battles": "Battles",
"wn8": "WN8",
"winrate": "Winrate",
"damage": "Damage",
"unknown_tank": "Unknown tank %s"
}
},
"debug": {
"$": "debug",
"description": "Debug information about the bot",
"body": {
"title": "# Debug\n\n",
"version": "Version",
"shard": {
"$": "Shard",
"default": "default"
},
"uptime": "Uptime"
}
},
"evolution": {
"$": "evolution",
"description": "Evolution of statistics",
"groups": {
"player": {
"$": "player",
"description": "Player statistics"
},
"tank": {
"$": "tank",
"description": "Tank statistics"
}
}
},
"inactive": {
"$": "inactive",
"description": "Lists all inactive players in a clan above a threshold",
"options": {
"threshold": {
"$": "threshold",
"description": "The number of days inactive (default: %s days)"
}
},
"body": {
"subtitle": "Inactive for %s+ days",
"listing": "%s days"
}
},
"full_stats": {
"$": "full-stats",
"description": "Full in-game statistics",
"body": {
"cards": {
"games": {
"$": "Games",
"winrate": "Winrate",
"battles": "Battles",
"wins_losses": "Wins / losses"
},
"efficiency": {
"$": "Efficiency",
"wn8": "WN8",
"survival": "Survival",
"accuracy": "Accuracy"
},
"lethality": {
"$": "Lethality",
"average_damage": "Average damage",
"damage_per_tier": "Damage per tier",
"damage_ratio": "Damage ratio",
"average_kills": "Average kills"
},
"miscellaneous": {
"$": "Miscellaneous",
"average_tier": "Average tier",
"average_spots": "Average spots",
"average_xp": "Average XP",
"kills_to_death_ratio": "Kills to death ratio"
}
}
}
},
"link": {
"$": "link",
"description": "Links your Blitz and Discord account",
"body": {
"accounts_linked": "Accounts linked",
"no_role_permissions": {
"$": "%s failed to verify",
"description": "I don't have the permission to manage roles."
}
},
"embed": {
"title": "Incorrect account?",
"description": "Use the command again and select a username from the search results."
}
},
"search_clans": {
"$": "search-clans",
"description": "Search clans",
"options": {
"limit": {
"$": "limit",
"description": "The max number of clan results (default: %s)"
}
},
"body": {
"title": "Clan search for %s in region %s",
"no_results": "No clans found"
}
},
"search_players": {
"$": "search-players",
"description": "Search players",
"options": {
"limit": {
"$": "limit",
"description": "The max number of player results (default: %s)"
}
},
"body": {
"title": "Player search for %s in region %s",
"no_results": "No players found"
}
},
"rating_leaderboard": {
"$": "rating-leaderboard",
"description": "Rating battles statistics",
"subcommands": {
"neighbors": {
"$": "neighbors",
"description": "Neighboring players on the leaderboard"
},
"league": {
"$": "league",
"description": "Top players of a league"
}
},
"options": {
"limit": {
"$": "limit",
"description": "How many neighbors to display (default: %s)"
},
"season": {
"$": "season",
"description": "Season number (default: current)",
"choices": {
"current": "%s (current)"
}
},
"league": {
"$": "league",
"description": "The league to display"
}
},
"body": {
"top_players": "Top %s players",
"no_participation": "# %s didn't play rating in season %s\n\nThis player did not participate in this season or did not get past calibration.",
"no_ongoing_season": "# No ongoing season\n\nThere isn't a currently running rating season.",
"deleted_player": "Deleted %s",
"neighbors": "Rating neighbors",
"more": "+ %s more"
}
},
"today": {
"$": "today",
"description": "Today's played tanks and statistics"
},
"stats": {
"$": "stats",
"description": "Quick statistics for a period"
},
"owned_tanks": {
"$": "owned-tanks",
"description": "Shows all own tanks in a tier",
"body": {
"title": "# %s's owned tier %s tanks\n\n"
}
},
"permissions": {
"$": "permissions",
"description": "Checks wether the bot has the required permissions to function",
"body": {
"title": "# Permissions\n\n%s",
"view_channels": "View channels: needed to find the message the refresh button was used on",
"read_message_history": "Read message history: needed to find the message to refresh",
"attach_files": "Attach files: needed to add images to image-based commands",
"external_emojis": "External emojis: needed to add national flags and tank class emojis"
}
},
"ping": {
"$": "ping",
"description": "Check wether the bot is alive.",
"body": {
"pong": "Pong 🏓",
"pong_with_time": "Pong 🏓 - %sms"
},
"subcommands": {
"blitzkit": {
"$": "blitzkit",
"description": "Ping BlitzKit"
},
"wotb": {
"$": "wotb",
"description": "Ping World of Tanks Blitz"
}
}
},
"player_info": {
"$": "player-info",
"description": "Basic information about a player",
"body": {
"title": "%s's information",
"nickname": "Nickname",
"battles": "Battles",
"winrate": "Winrate",
"account_id": "Account ID",
"created": "Created",
"last_battle": "Last battle"
}
},
"rating": {
"$": "rating",
"description": "Rating player's statistics",
"subcommands": {
"today": {
"$": "today",
"description": "Today's rating statistics"
},
"season": {
"$": "season",
"description": "This season's rating statistics"
}
},
"body": {
"subtitle": {
"today": "Today's rating statistics",
"season": "This season's rating statistics"
}
},
"errors": {
"no_participation": "# %s hasn't played rating\n\nThis player hasn't participated in the rating gamemode this season.",
"unrecorded_stats": "# Statistics currently unavailable\n\nWargaming may be currently be facing server downtime. If there is an on-going season, statistics should be available withing 24 hours."
}
}
}
},
"website": {}
}
-601
View File
@@ -1,601 +0,0 @@
{
"common": {
"wn8_percentile": {
"0": "Muy malo",
"1": "Malo",
"2": "Inferior al promedio",
"3": "Promedio",
"4": "Superior al promedio",
"5": "Bueno",
"6": "Muy bueno",
"7": "Genial",
"8": "Unicum",
"9": "Super unicum"
},
"bkni_percentile": {
"0": "Super malum",
"1": "Malum",
"2": "Very bad",
"3": "Bad",
"4": "Below average",
"5": "Average",
"6": "Above average",
"7": "Good",
"8": "Very good",
"9": "Unicum",
"10": "Super unicum"
},
"nations": {
"ussr": "URSS",
"germany": "Alemania",
"usa": "EE. UU.",
"china": "China",
"uk": "Reino Unido",
"france": "Francia",
"japan": "Japón",
"european": "Nación europea",
"other": "Nación híbrida"
},
"nations_adjectives": {
"ussr": "U.S.S.R.",
"germany": "German",
"usa": "U.S.A.",
"china": "Chinese",
"uk": "U.K.",
"france": "French",
"japan": "Japanese",
"european": "European",
"other": "Hybrid"
},
"tank_class_short": {
"-1": "unrecognized",
"0": "Light",
"1": "Medium",
"2": "Heavy",
"3": "TD"
},
"tank_class_short_plural_lowercase": {
"0": "lights",
"1": "mediums",
"2": "heavies",
"3": "TDs"
},
"tank_class_medium": {
"-1": "unrecognized",
"0": "Light",
"1": "Medium",
"2": "Heavy",
"3": "Tank destroyer"
},
"tree_type": {
"-1": "unrecognized",
"0": "Tech tree",
"1": "Premium",
"2": "Collector"
},
"regions": {
"normal": {
"com": "Norteamérica",
"eu": "Europa",
"asia": "Asia-Pacífico"
},
"short": {
"com": "NA",
"eu": "Europa",
"asia": "APAC"
}
},
"leagues": {
"diamond": "Diamante",
"platinum": "Platino",
"gold": "Oro",
"silver": "Plata",
"bronze": "Bronce"
},
"composite_stats": {
"normalized_damage_dealt": "Damage",
"normalized_damage_received": "Damage received",
"normalized_deaths": "Deathrate",
"normalized_frags": "Kills",
"normalized_hits": "Hits",
"normalized_losses": "Lossrate",
"normalized_misses": "Misses",
"normalized_shots": "Shots",
"normalized_spotted": "Spots",
"normalized_survived_battles": "Survival rate",
"normalized_win_and_survived": "Winning and surviving rate",
"normalized_wins": "Winrate",
"normalized_xp": "XP",
"normalized_battle_life_time": "Battle time",
"cumulative_accuracy": "Accuracy",
"cumulative_battles": "Battles",
"cumulative_capture_points": "Total capture points",
"cumulative_damage_dealt": "Total damage",
"cumulative_damage_ratio": "Damage ratio",
"cumulative_damage_received": "Total damage received",
"cumulative_deaths": "Total deaths",
"cumulative_dropped_capture_points": "Total dropped capture points",
"cumulative_frags": "Total kills",
"cumulative_hits": "Total hits",
"cumulative_inaccuracy": "Inaccuracy",
"cumulative_kills_to_death_ratio": "Kills to death ratio",
"cumulative_losses": "Losses",
"cumulative_misses": "Total misses",
"cumulative_shots": "Total shots",
"cumulative_spotted": "Total spots",
"cumulative_survived_battles": "Survived battles",
"cumulative_win_and_survived": "Won and survived battles",
"cumulative_wins": "Wins",
"cumulative_xp": "Total XP",
"cumulative_wn8": "WN8",
"cumulative_battle_life_time": "Total battle time"
}
},
"bot": {
"common": {
"errors": {
"clan_not_found": "# No se pudo encontrar el clan\nNo se pudo encontrar \"%s\". Prueba seleccionando un clan de los resultados de búsqueda.",
"player_not_found": "# No se pudo encontrar el usuario\nNo pude encontrar al usuario \"%s\". Prueba eligiendo un usuario de los resultados de búsqueda, escribiendo un nombre de usuario aceptable o usando el comando `/vincular`.",
"player_not_linked": "# No sé quién eres\nUsa el comando `/vincular` para comenzar.",
"tank_name_not_found": "# No se pudo encontrar el tanque\nNo se pudo encontrar el tanque con el nombre \"%s\".",
"tank_id_not_found": "# Tanque no encontrado\nNo se pudo encontrar al tanque del ID \"%s\". Prueba escribiendo el nombre del tanque en vez de un número.",
"no_tank_stats": "# No hay estadísticas de tanque disponibles\nEste jugador no tiene estadísticas disponibles. Este podría no ser el jugador que estás buscando.",
"get_help": "Servidor de ayuda de Discord",
"uncaught_error": "# ¡Blitzkrieg sufrió un error!\nLo sentimos mucho. Siéntete libre de [unirte al servidor oficial de Discord](https://discord.gg/nDt7AjGJQH) para obtener ayuda.",
"preview": "# Este comando es una vista previa\n\nSolo puedes acceder a este comando en [el servidor oficial de Discord](https://discord.gg/nDt7AjGJQH). Generalmente este comando estará disponible pronto."
},
"wn8": {
"$": "WN8",
"very_bad": "Muy malo",
"bad": "Malo",
"below_average": "Inferior al promedio",
"average": "Promedio",
"above_average": "Superior al promedio",
"good": "Bueno",
"very_good": "Muy bueno",
"great": "Genial",
"unicum": "Unicum",
"super_unicum": "Super unicum"
},
"subcommands": {
"today": {
"$": "hoy",
"description": "Hoy"
},
"30": {
"$": "30",
"description": "30 días"
},
"60": {
"$": "60",
"description": "60 días"
},
"90": {
"$": "90",
"description": "90 días"
},
"career": {
"$": "carrera",
"description": "Carrera"
},
"custom": {
"$": "personalizado",
"description": "Rango personalizado",
"options": {
"start": {
"$": "comienzo",
"description": "Comenzar en cantidad de días pasados desde hoy"
},
"end": {
"$": "fin",
"description": "Terminar en cantidad de días pasados desde hoy"
}
}
}
},
"options": {
"clan": {
"$": "clan",
"description": "El nombre o etiqueta del clan"
},
"tank": {
"$": "tanque",
"description": "El tanque al cual conseguirle estadísticas"
},
"username": {
"$": "usuario",
"description": "El nombre de usuario de Blitz"
},
"filters": {
"nation": {
"$": "nación",
"description": "Nación"
},
"tank_class": {
"$": "clase-de-tanque",
"description": "Clase de tanque"
},
"tree_type": {
"$": "tipo-de-árbol",
"description": "Tipo de árbol"
}
},
"region": {
"$": "región",
"description": "Región"
},
"tier": {
"$": "nivel",
"description": "Nivel",
"choices": {
"tier": "Nivel %s - %s"
}
}
},
"periods": {
"career": "Carrera",
"today": "Hoy",
"days": "%s días",
"custom": "%s - %s días"
},
"filters": {
"none": "Sin filtros",
"tier": "Nivel %s"
},
"no_data": {
"battles_in_period": "No hay batallas jugadas en este período",
"battles_in_season": "No hay batallas jugadas en esta temporada",
"players_in_period": "No hay jugadores en este período",
"tanks_found": "No se encontraron tanques"
},
"buttons": {
"refresh": "Actualizar"
},
"hero_stats": {
"battles": "Batallas",
"winrate": "Tasa de victorias",
"damage": "Daño",
"damage_ratio": "Relación de daño",
"tier": "Nivel",
"survival": "Supervivencia",
"accuracy": "Precisión",
"kills": "Bajas"
}
},
"commands": {
"auction": {
"$": "auction",
"description": "Tanks available right now in the auction",
"body": {
"title": "Blitz Auction",
"subtitle": "%s available • next prices %s\nBid now: %s",
"next": "Next price: %s",
"available": "Available: %s / %s"
},
"errors": {
"no_auction": "# No ongoing auction\n\nCurrently, there are no tanks available for purchase in the auction. If you think this is an error, please report this discrepancy in [the official Discord Server](https://discord.gg/nDt7AjGJQH)."
},
"actions": {
"bid": "Bid now"
}
},
"replay": {
"$": "replay",
"description": "Display replay results"
},
"research": {
"$": "investigar",
"description": "Enumera los gastos por investigar una línea",
"options": {
"target_tank": {
"$": "tanque-objetivo",
"description": "El último tanque a investigar en la línea"
},
"starting_tank": {
"$": "tanque-inicial",
"description": "El tanque inicial en la línea (se deduce automáticamente si no se proporciona)"
}
},
"body": {
"subtitle": "Investigar",
"total": "Total",
"research": "Investigar: %s",
"upgrades": "Mejoras: %s",
"purchase": "Comprar: %s",
"equipment": "Equipamiento: %s",
"estimation": "# ¡Esto es una estimación!\n\nEsta lista podría incluir tanques ya investigados debido a la información errónea que proporciona Wargaming. Si puedes, prueba proporcionando un `tanque-inicial` de la línea al usar el comando."
},
"errors": {
"non_tech_tree": "# %s no está en el árbol tecnológico\n\nNo puedes investigar un tanque prémium o coleccionista.",
"tier_1": "# %s es un nivel I\n\nLos tanque de nivel I están siempre desbloqueados gratuitamente para todos los jugadores.",
"already_researched": "# %s ya está investigado\n\nPrueba proporcionando un `tanque-inicial` al usar el comando para ver los totales para una línea que ya has investigado.",
"start_end_equal": "# %s fue proporcionado dos veces\n\nAsegúrate de que `tanque-inicial` y `tanque-objetivo` son diferentes.",
"tanks_not_on_line": "# %s y %s no están en la misma línea\n\nAsegúrate de que `tanque-inicial` y `tanque-objetivo` están en la misma línea del árbol tecnológico.",
"unordered": "# %s de nivel superior a %s\n\nAsegúrate de proporcionar %s como el `tanque-inicial` en su lugar ya que es el tanque de nivel inferior."
}
},
"about": {
"$": "sobre",
"description": "Toda la información que necesitas sobre el bot",
"subcommands": {
"commands": {
"$": "comandos",
"description": "Lista de todos los comandos y sus descripciones",
"body": "# Comandos\n\nBlitzkrieg ofrece los siguientes comandos:\n\n"
},
"embeds": {
"$": "embeds",
"description": "Cómo funcionan los embeds"
},
"introduction": {
"$": "introducción",
"description": "Sobre el bot"
},
"invite": {
"$": "invitar",
"description": "Cómo invitar al bot"
},
"timezones": {
"$": "zonas-horarias",
"description": "Cómo Blitzkrieg infiere y utiliza tu zona horaria"
},
"credits": {
"$": "credits",
"description": "People who have helped BlitzKit"
}
}
},
"breakdown": {
"$": "resumen",
"description": "Resumen de un período por tanques jugados",
"body": {
"total": "Total",
"battles": "Batallas",
"wn8": "WN8",
"winrate": "Tasa de victorias",
"damage": "Daño",
"unknown_tank": "Tanque desconocido %s"
}
},
"debug": {
"$": "depurar",
"description": "Muestra más información sobre el bot",
"body": {
"title": "# Depuración\n\n",
"version": "Versión",
"shard": {
"$": "Partición",
"default": "predeterminado"
},
"uptime": "Tiempo en línea"
}
},
"evolution": {
"$": "evolución",
"description": "Evolución de estadísticas",
"groups": {
"player": {
"$": "jugador",
"description": "Estadísticas del jugador"
},
"tank": {
"$": "tanque",
"description": "Estadísticas del tanque"
}
}
},
"inactive": {
"$": "inactivo",
"description": "Enumera a todos los jugadores inactivos en un clan por encima de un umbral",
"options": {
"threshold": {
"$": "umbral",
"description": "El número de días inactivo (predeterminado: %s días)"
}
},
"body": {
"subtitle": "Inactivo por %s+ días",
"listing": "%s días"
}
},
"full_stats": {
"$": "estadísticas-completas",
"description": "Estadísticas completas del juego",
"body": {
"cards": {
"games": {
"$": "Partidas",
"winrate": "Tasa de victorias",
"battles": "Batallas",
"wins_losses": "Victorias / derrotas"
},
"efficiency": {
"$": "Eficiencia",
"wn8": "WN8",
"survival": "Supervivencia",
"accuracy": "Precisión"
},
"lethality": {
"$": "Letalidad",
"average_damage": "Daño promedio",
"damage_per_tier": "Daño por nivel",
"damage_ratio": "Relación de daño",
"average_kills": "Promedio de bajas"
},
"miscellaneous": {
"$": "Misceláneo",
"average_tier": "Nivel promedio",
"average_spots": "Promedio de detecciones",
"average_xp": "Promedio de XP",
"kills_to_death_ratio": "Relación bajas-muertes"
}
}
}
},
"link": {
"$": "vincular",
"description": "Vincula tu cuenta de Blitz y Discord",
"body": {
"accounts_linked": "Cuentas vinculadas",
"no_role_permissions": {
"$": "%s no se pudo verificar",
"description": "No tengo permiso para administrar roles."
}
},
"embed": {
"title": "¿Cuenta incorrecta?",
"description": "Usa el comando de nuevo y selecciona un usuario de los resultados de búsqueda."
}
},
"search_clans": {
"$": "buscar-clanes",
"description": "Buscar clanes",
"options": {
"limit": {
"$": "límite",
"description": "El número máximo de resultados de clanes (predeterminado: %s)"
}
},
"body": {
"title": "Búsqueda del clan %s en la región %s",
"no_results": "No se encontraron clanes"
}
},
"search_players": {
"$": "buscar-jugadores",
"description": "Buscar jugadores",
"options": {
"limit": {
"$": "límite",
"description": "El número máximo para resultados de jugador (predeterminado: %s)"
}
},
"body": {
"title": "Búsqueda del jugador %s en la región %s",
"no_results": "No se encontraron jugadores"
}
},
"rating_leaderboard": {
"$": "rating-leaderboard",
"description": "Rating battles statistics",
"subcommands": {
"neighbors": {
"$": "vecinos",
"description": "Neighboring players on the leaderboard"
},
"league": {
"$": "liga",
"description": "Mejores jugadores de una liga"
}
},
"options": {
"limit": {
"$": "límite",
"description": "Cuántos vecinos a mostrar (predeterminado: %s)"
},
"season": {
"$": "temporada",
"description": "Número de temporada (predeterminado: actual)",
"choices": {
"current": "%s (actual)"
}
},
"league": {
"$": "liga",
"description": "La liga a mostrar"
}
},
"body": {
"top_players": "Mejores %s jugadores",
"no_participation": "# %s didn't play rating in season %s\n\nThis player did not participate in this season or did not get past calibration.",
"no_ongoing_season": "# No ongoing season\n\nThere isn't a currently running rating season.",
"deleted_player": "%s eliminado",
"neighbors": "Rating neighbors",
"more": "+ %s más"
}
},
"today": {
"$": "hoy",
"description": "Tanques jugados hoy y estadísticas"
},
"stats": {
"$": "estadísticas",
"description": "Estadísticas rápidas para un período"
},
"owned_tanks": {
"$": "tanques-propios",
"description": "Muestra todos los tanques propios en un nivel",
"body": {
"title": "# Tanques de %s de nivel %s\n\n"
}
},
"permissions": {
"$": "permisos",
"description": "Comprueba si el bot tiene los permisos requeridos para funcionar",
"body": {
"title": "# Permisos\n\n%s",
"view_channels": "Ver canales: necesario para encontrar el mensaje en el cual el botón \"actualizar\" fue usado",
"read_message_history": "Leer historial de mensajes: necesario para encontrar el mensaje a actualizar",
"attach_files": "Adjuntar archivos: necesario para añadir imágenes a los comandos basados en estas",
"external_emojis": "Emoticonos externos: necesario para añadir emoticonos de banderas nacionales y clases de tanque"
}
},
"ping": {
"$": "ping",
"description": "Comprueba si el bot está vivo.",
"body": {
"pong": "Pong 🏓",
"pong_with_time": "Pong 🏓 - %sms"
},
"subcommands": {
"blitzkit": {
"$": "blitzkit",
"description": "Ping BlitzKit"
},
"wotb": {
"$": "wotb",
"description": "Hacerle un ping a World of Tanks Blitz"
}
}
},
"player_info": {
"$": "info-jugador",
"description": "Información básica sobre un jugador",
"body": {
"title": "Información de %s",
"nickname": "Apodo",
"battles": "Batallas",
"winrate": "Tasa de victorias",
"account_id": "ID de cuenta",
"created": "Creado",
"last_battle": "Última batalla"
}
},
"rating": {
"$": "rating-session",
"description": "Rating player's statistics",
"subcommands": {
"today": {
"$": "today",
"description": "Today's rating statistics"
},
"season": {
"$": "season",
"description": "This season's rating statistics"
}
},
"body": {
"subtitle": {
"today": "Today's rating statistics",
"season": "This season's rating statistics"
}
},
"errors": {
"no_participation": "# %s hasn't played rating\n\nThis player hasn't participated in the rating gamemode this season.",
"unrecorded_stats": "# Estadísticas no disponibles actualmente\n\nWargaming podría estar sufriendo una caída del servidor. Si hay alguna temporada en curso, las estadísticas deberían estar disponibles en 24 horas."
}
}
}
},
"website": {}
}
-601
View File
@@ -1,601 +0,0 @@
{
"common": {
"wn8_percentile": {
"0": "Jako loše",
"1": "Loše",
"2": "Ispod prosečnog",
"3": "Prosečno",
"4": "Nad prosečno",
"5": "Dobro",
"6": "Jako dobro",
"7": "Super",
"8": "Legendarno",
"9": "Super Legendarno"
},
"bkni_percentile": {
"0": "Super malum",
"1": "Malum",
"2": "Very bad",
"3": "Bad",
"4": "Below average",
"5": "Average",
"6": "Above average",
"7": "Good",
"8": "Very good",
"9": "Unicum",
"10": "Super unicum"
},
"nations": {
"ussr": "S.S.S.R.",
"germany": "Nemačka",
"usa": "S.A.D.",
"china": "Kina",
"uk": "Vel. Brit.",
"france": "Francuska",
"japan": "Japan",
"european": "Evropska Unija",
"other": "Hibridna nacija"
},
"nations_adjectives": {
"ussr": "U.S.S.R.",
"germany": "German",
"usa": "U.S.A.",
"china": "Chinese",
"uk": "U.K.",
"france": "French",
"japan": "Japanese",
"european": "European",
"other": "Hybrid"
},
"tank_class_short": {
"-1": "unrecognized",
"0": "Light",
"1": "Medium",
"2": "Heavy",
"3": "TD"
},
"tank_class_short_plural_lowercase": {
"0": "lights",
"1": "mediums",
"2": "heavies",
"3": "TDs"
},
"tank_class_medium": {
"-1": "unrecognized",
"0": "Light",
"1": "Medium",
"2": "Heavy",
"3": "Tank destroyer"
},
"tree_type": {
"-1": "unrecognized",
"0": "Tech tree",
"1": "Premium",
"2": "Collector"
},
"regions": {
"normal": {
"com": "Severna Amerika",
"eu": "Evropa",
"asia": "Azija-Pasifik"
},
"short": {
"com": "NA",
"eu": "EU",
"asia": "APAC"
}
},
"leagues": {
"diamond": "Dijamant",
"platinum": "Platina",
"gold": "Zlato",
"silver": "Srebro",
"bronze": "Bronza"
},
"composite_stats": {
"normalized_damage_dealt": "Damage",
"normalized_damage_received": "Damage received",
"normalized_deaths": "Deathrate",
"normalized_frags": "Kills",
"normalized_hits": "Hits",
"normalized_losses": "Lossrate",
"normalized_misses": "Misses",
"normalized_shots": "Shots",
"normalized_spotted": "Spots",
"normalized_survived_battles": "Survival rate",
"normalized_win_and_survived": "Winning and surviving rate",
"normalized_wins": "Winrate",
"normalized_xp": "XP",
"normalized_battle_life_time": "Battle time",
"cumulative_accuracy": "Accuracy",
"cumulative_battles": "Battles",
"cumulative_capture_points": "Total capture points",
"cumulative_damage_dealt": "Total damage",
"cumulative_damage_ratio": "Damage ratio",
"cumulative_damage_received": "Total damage received",
"cumulative_deaths": "Total deaths",
"cumulative_dropped_capture_points": "Total dropped capture points",
"cumulative_frags": "Total kills",
"cumulative_hits": "Total hits",
"cumulative_inaccuracy": "Inaccuracy",
"cumulative_kills_to_death_ratio": "Kills to death ratio",
"cumulative_losses": "Losses",
"cumulative_misses": "Total misses",
"cumulative_shots": "Total shots",
"cumulative_spotted": "Total spots",
"cumulative_survived_battles": "Survived battles",
"cumulative_win_and_survived": "Won and survived battles",
"cumulative_wins": "Wins",
"cumulative_xp": "Total XP",
"cumulative_wn8": "WN8",
"cumulative_battle_life_time": "Total battle time"
}
},
"bot": {
"common": {
"errors": {
"clan_not_found": "# Could not find clan\nCouldn't find \"%s\". Try selecting a clan from the search result.",
"player_not_found": "# Could not find user\nI couldn't find user \"%s\". Try picking an user from the search result, typing in a valid username, or using the `/link` command.",
"player_not_linked": "# I don't know who you are\nUse the `/link` command to get started.",
"tank_name_not_found": "# Tank not found\nCould not find tank by the name \"%s\".",
"tank_id_not_found": "# Tank not found\nCould not find tank by the ID \"%s\". Try typing the name of the tank instead of a number.",
"no_tank_stats": "# No tank stats available\nThis player doesn't have any stats available. This may not be the player you are looking for.",
"get_help": "Help Discord server",
"uncaught_error": "# Blitzkrieg ran into an error!\nWe're so sorry about this. Feel free to [join the official Discord server](https://discord.gg/nDt7AjGJQH) to get help.",
"preview": "# This command is in preview\n\nYou can only access this command in [the official Discord server](https://discord.gg/nDt7AjGJQH). The command will be generally available soon."
},
"wn8": {
"$": "WN8",
"very_bad": "Jako loše",
"bad": "Loše",
"below_average": "Ispod prosečnog",
"average": "Prosečno",
"above_average": "Nad prosečno",
"good": "Dobro",
"very_good": "Jako dobro",
"great": "Super",
"unicum": "Legendarno",
"super_unicum": "Super Legendarno"
},
"subcommands": {
"today": {
"$": "danas",
"description": "Danas"
},
"30": {
"$": "30",
"description": "30 dana"
},
"60": {
"$": "60",
"description": "60 dana"
},
"90": {
"$": "90",
"description": "90 dana"
},
"career": {
"$": "karijera",
"description": "Karijera"
},
"custom": {
"$": "proizvoljno",
"description": "Proizvoljna daljina",
"options": {
"start": {
"$": "kreni",
"description": "Start in number of days ago from today"
},
"end": {
"$": "kraj",
"description": "End in number of days ago from today"
}
}
}
},
"options": {
"clan": {
"$": "klan",
"description": "The clan name or tag"
},
"tank": {
"$": "tank",
"description": "The tank to get stats for"
},
"username": {
"$": "username",
"description": "The Blitz username of the player"
},
"filters": {
"nation": {
"$": "nation",
"description": "Nation"
},
"tank_class": {
"$": "tank-class",
"description": "Tank class"
},
"tree_type": {
"$": "tree-type",
"description": "Tree type"
}
},
"region": {
"$": "region",
"description": "Region"
},
"tier": {
"$": "tier",
"description": "Tier",
"choices": {
"tier": "Tier %s - %s"
}
}
},
"periods": {
"career": "Career",
"today": "Today",
"days": "%s days",
"custom": "%s - %s days"
},
"filters": {
"none": "No filters",
"tier": "Tier %s"
},
"no_data": {
"battles_in_period": "No battles played in this period",
"battles_in_season": "No battles played in this season",
"players_in_period": "No players in this period",
"tanks_found": "No tanks found"
},
"buttons": {
"refresh": "Refresh"
},
"hero_stats": {
"battles": "Battles",
"winrate": "Winrate",
"damage": "Damage",
"damage_ratio": "Damage ratio",
"tier": "Tier",
"survival": "Survival",
"accuracy": "Accuracy",
"kills": "Kills"
}
},
"commands": {
"auction": {
"$": "auction",
"description": "Tanks available right now in the auction",
"body": {
"title": "Blitz Auction",
"subtitle": "%s available • next prices %s\nBid now: %s",
"next": "Next price: %s",
"available": "Available: %s / %s"
},
"errors": {
"no_auction": "# No ongoing auction\n\nCurrently, there are no tanks available for purchase in the auction. If you think this is an error, please report this discrepancy in [the official Discord Server](https://discord.gg/nDt7AjGJQH)."
},
"actions": {
"bid": "Bid now"
}
},
"replay": {
"$": "replay",
"description": "Display replay results"
},
"research": {
"$": "research",
"description": "Lists the expenses in researching a line",
"options": {
"target_tank": {
"$": "target-tank",
"description": "The final tank to research in the line"
},
"starting_tank": {
"$": "starting-tank",
"description": "The starting tank in the line (inferred automatically if not provided)"
}
},
"body": {
"subtitle": "Research",
"total": "Total",
"research": "Research: %s",
"upgrades": "Upgrades: %s",
"purchase": "Purchase: %s",
"equipment": "Equipment: %s",
"estimation": "# This is an estimation!\n\nThis list may include already researched tanks because of the inaccurate information Wargaming provides. Try providing a `starting-tank` from the line when using the command if you can."
},
"errors": {
"non_tech_tree": "# %s is not on the tech tree\n\nYou cannot research a premium/collector tank.",
"tier_1": "# %s is a tier I\n\nTier I tanks are always unlocked for free for all players.",
"already_researched": "# %s is already researched\n\nTry providing a `starting-tank` when using the command to view the totals for a line you have already researched.",
"start_end_equal": "# %s was provided twice\n\nMake sure `starting-tank` and `target-tank` are different.",
"tanks_not_on_line": "# %s and %s are not on the same line\n\nMake sure `starting-tank` and `target-tank` are on the same tech-tree line.",
"unordered": "# %s higher tiered to %s\n\nMake sure to provide %s as the `starting-tank` instead as it is the lower tiered tank."
}
},
"about": {
"$": "about",
"description": "All the info you need about the bot",
"subcommands": {
"commands": {
"$": "commands",
"description": "List of all commands and their descriptions",
"body": "# Commands\n\nBlitzkrieg offers the following commands:\n\n"
},
"embeds": {
"$": "embeds",
"description": "Kako embed radi"
},
"introduction": {
"$": "uvod",
"description": "O bot-u"
},
"invite": {
"$": "pozovi",
"description": "Kako pozvati robota"
},
"timezones": {
"$": "vremenske zone",
"description": "Kako \"Blitzkrieg\" koristi podatke o tvojoj vremenskoj zoni"
},
"credits": {
"$": "credits",
"description": "People who have helped BlitzKit"
}
}
},
"breakdown": {
"$": "analiza",
"description": "A period's breakdown by tanks played",
"body": {
"total": "Total",
"battles": "Borbe",
"wn8": "WN8",
"winrate": "Procenat pobede",
"damage": "Šteta",
"unknown_tank": "Ne poznat tenk %s"
}
},
"debug": {
"$": "odklanjanje-grešaka",
"description": "Debug information about the bot",
"body": {
"title": "# Debug\n\n",
"version": "Version",
"shard": {
"$": "Shard",
"default": "default"
},
"uptime": "Uptime"
}
},
"evolution": {
"$": "evolution",
"description": "Evolution of statistics",
"groups": {
"player": {
"$": "player",
"description": "Player statistics"
},
"tank": {
"$": "tank",
"description": "Tank statistics"
}
}
},
"inactive": {
"$": "inactive",
"description": "Lists all inactive players in a clan above a threshold",
"options": {
"threshold": {
"$": "threshold",
"description": "The number of days inactive (default: %s days)"
}
},
"body": {
"subtitle": "Inactive for %s+ days",
"listing": "%s days"
}
},
"full_stats": {
"$": "full-stats",
"description": "Full in-game statistics",
"body": {
"cards": {
"games": {
"$": "Games",
"winrate": "Winrate",
"battles": "Battles",
"wins_losses": "Wins / losses"
},
"efficiency": {
"$": "Efficiency",
"wn8": "WN8",
"survival": "Survival",
"accuracy": "Accuracy"
},
"lethality": {
"$": "Lethality",
"average_damage": "Average damage",
"damage_per_tier": "Damage per tier",
"damage_ratio": "Damage ratio",
"average_kills": "Average kills"
},
"miscellaneous": {
"$": "Miscellaneous",
"average_tier": "Average tier",
"average_spots": "Average spots",
"average_xp": "Average XP",
"kills_to_death_ratio": "Kills to death ratio"
}
}
}
},
"link": {
"$": "link",
"description": "Links your Blitz and Discord account",
"body": {
"accounts_linked": "Accounts linked",
"no_role_permissions": {
"$": "%s failed to verify",
"description": "I don't have the permission to manage roles."
}
},
"embed": {
"title": "Incorrect account?",
"description": "Use the command again and select a username from the search results."
}
},
"search_clans": {
"$": "search-clans",
"description": "Search clans",
"options": {
"limit": {
"$": "limit",
"description": "The max number of clan results (default: %s)"
}
},
"body": {
"title": "Clan search for %s in region %s",
"no_results": "No clans found"
}
},
"search_players": {
"$": "search-players",
"description": "Search players",
"options": {
"limit": {
"$": "limit",
"description": "The max number of player results (default: %s)"
}
},
"body": {
"title": "Player search for %s in region %s",
"no_results": "No players found"
}
},
"rating_leaderboard": {
"$": "rating-leaderboard",
"description": "Rating battles statistics",
"subcommands": {
"neighbors": {
"$": "neighbors",
"description": "Neighboring players on the leaderboard"
},
"league": {
"$": "league",
"description": "Top players of a league"
}
},
"options": {
"limit": {
"$": "limit",
"description": "How many neighbors to display (default: %s)"
},
"season": {
"$": "season",
"description": "Season number (default: current)",
"choices": {
"current": "%s (current)"
}
},
"league": {
"$": "league",
"description": "The league to display"
}
},
"body": {
"top_players": "Top %s players",
"no_participation": "# %s didn't play rating in season %s\n\nThis player did not participate in this season or did not get past calibration.",
"no_ongoing_season": "# No ongoing season\n\nThere isn't a currently running rating season.",
"deleted_player": "Deleted %s",
"neighbors": "Rating neighbors",
"more": "+ %s more"
}
},
"today": {
"$": "today",
"description": "Today's played tanks and statistics"
},
"stats": {
"$": "stats",
"description": "Quick statistics for a period"
},
"owned_tanks": {
"$": "owned-tanks",
"description": "Shows all own tanks in a tier",
"body": {
"title": "# %s's owned tier %s tanks\n\n"
}
},
"permissions": {
"$": "permissions",
"description": "Checks wether the bot has the required permissions to function",
"body": {
"title": "# Permissions\n\n%s",
"view_channels": "View channels: needed to find the message the refresh button was used on",
"read_message_history": "Read message history: needed to find the message to refresh",
"attach_files": "Attach files: needed to add images to image-based commands",
"external_emojis": "External emojis: needed to add national flags and tank class emojis"
}
},
"ping": {
"$": "ping",
"description": "Check wether the bot is alive.",
"body": {
"pong": "Pong 🏓",
"pong_with_time": "Pong 🏓 - %sms"
},
"subcommands": {
"blitzkit": {
"$": "blitzkit",
"description": "Ping BlitzKit"
},
"wotb": {
"$": "wotb",
"description": "Ping World of Tanks Blitz"
}
}
},
"player_info": {
"$": "player-info",
"description": "Basic information about a player",
"body": {
"title": "%s's information",
"nickname": "Nickname",
"battles": "Battles",
"winrate": "Winrate",
"account_id": "Account ID",
"created": "Created",
"last_battle": "Last battle"
}
},
"rating": {
"$": "rating-session",
"description": "Rating player's statistics",
"subcommands": {
"today": {
"$": "today",
"description": "Today's rating statistics"
},
"season": {
"$": "season",
"description": "This season's rating statistics"
}
},
"body": {
"subtitle": {
"today": "Today's rating statistics",
"season": "This season's rating statistics"
}
},
"errors": {
"no_participation": "# %s hasn't played rating\n\nThis player hasn't participated in the rating gamemode this season.",
"unrecorded_stats": "# Statistics currently unavailable\n\nWargaming may be currently be facing server downtime. If there is an on-going season, statistics should be available withing 24 hours."
}
}
}
},
"website": {}
}
-601
View File
@@ -1,601 +0,0 @@
{
"common": {
"wn8_percentile": {
"0": "Very bad",
"1": "Bad",
"2": "Below average",
"3": "Average",
"4": "Above average",
"5": "Good",
"6": "Very good",
"7": "Great",
"8": "Unicum",
"9": "Super unicum"
},
"bkni_percentile": {
"0": "Super malum",
"1": "Malum",
"2": "Very bad",
"3": "Bad",
"4": "Below average",
"5": "Average",
"6": "Above average",
"7": "Good",
"8": "Very good",
"9": "Unicum",
"10": "Super unicum"
},
"nations": {
"ussr": "U.S.S.R.",
"germany": "Germany",
"usa": "U.S.A.",
"china": "China",
"uk": "U.K.",
"france": "France",
"japan": "Japan",
"european": "European Nation",
"other": "Hybrid Nation"
},
"nations_adjectives": {
"ussr": "U.S.S.R.",
"germany": "German",
"usa": "U.S.A.",
"china": "Chinese",
"uk": "U.K.",
"france": "French",
"japan": "Japanese",
"european": "European",
"other": "Hybrid"
},
"tank_class_short": {
"-1": "unrecognized",
"0": "Light",
"1": "Medium",
"2": "Heavy",
"3": "TD"
},
"tank_class_short_plural_lowercase": {
"0": "lights",
"1": "mediums",
"2": "heavies",
"3": "TDs"
},
"tank_class_medium": {
"-1": "unrecognized",
"0": "Light",
"1": "Medium",
"2": "Heavy",
"3": "Tank destroyer"
},
"tree_type": {
"-1": "unrecognized",
"0": "Tech tree",
"1": "Premium",
"2": "Collector"
},
"regions": {
"normal": {
"com": "North America",
"eu": "Europe",
"asia": "Asia-Pacific"
},
"short": {
"com": "NA",
"eu": "EU",
"asia": "APAC"
}
},
"leagues": {
"diamond": "Diamond",
"platinum": "Platinum",
"gold": "Gold",
"silver": "Silver",
"bronze": "Bronze"
},
"composite_stats": {
"normalized_damage_dealt": "Damage",
"normalized_damage_received": "Damage received",
"normalized_deaths": "Deathrate",
"normalized_frags": "Kills",
"normalized_hits": "Hits",
"normalized_losses": "Lossrate",
"normalized_misses": "Misses",
"normalized_shots": "Shots",
"normalized_spotted": "Spots",
"normalized_survived_battles": "Survival rate",
"normalized_win_and_survived": "Winning and surviving rate",
"normalized_wins": "Winrate",
"normalized_xp": "XP",
"normalized_battle_life_time": "Battle time",
"cumulative_accuracy": "Accuracy",
"cumulative_battles": "Battles",
"cumulative_capture_points": "Total capture points",
"cumulative_damage_dealt": "Total damage",
"cumulative_damage_ratio": "Damage ratio",
"cumulative_damage_received": "Total damage received",
"cumulative_deaths": "Total deaths",
"cumulative_dropped_capture_points": "Total dropped capture points",
"cumulative_frags": "Total kills",
"cumulative_hits": "Total hits",
"cumulative_inaccuracy": "Inaccuracy",
"cumulative_kills_to_death_ratio": "Kills to death ratio",
"cumulative_losses": "Losses",
"cumulative_misses": "Total misses",
"cumulative_shots": "Total shots",
"cumulative_spotted": "Total spots",
"cumulative_survived_battles": "Survived battles",
"cumulative_win_and_survived": "Won and survived battles",
"cumulative_wins": "Wins",
"cumulative_xp": "Total XP",
"cumulative_wn8": "WN8",
"cumulative_battle_life_time": "Total battle time"
}
},
"bot": {
"common": {
"errors": {
"clan_not_found": "# Could not find clan\nCouldn't find \"%s\". Try selecting a clan from the search result.",
"player_not_found": "# Could not find user\nI couldn't find user \"%s\". Try picking an user from the search result, typing in a valid username, or using the `/link` command.",
"player_not_linked": "# Use `/link` to get started\nI don't know who you are but you can tell me today!",
"tank_name_not_found": "# Tank not found\nCould not find tank by the name \"%s\".",
"tank_id_not_found": "# Tank not found\nCould not find tank by the ID \"%s\". Try typing the name of the tank instead of a number.",
"no_tank_stats": "# No tank stats available\nThis player doesn't have any stats available. This may not be the player you are looking for.",
"get_help": "Help Discord server",
"uncaught_error": "# BlitzKit ran into an error!\nWe're so sorry about this. Feel free to [join the official Discord server](https://discord.gg/nDt7AjGJQH) to get help.",
"preview": "# This command is in preview\n\nYou can only access this command in [the official Discord server](https://discord.gg/nDt7AjGJQH). The command will be generally available soon."
},
"wn8": {
"$": "WN8",
"very_bad": "Very bad",
"bad": "Bad",
"below_average": "Below average",
"average": "Average",
"above_average": "Above average",
"good": "Good",
"very_good": "Very good",
"great": "Great",
"unicum": "Unicum",
"super_unicum": "Super unicum"
},
"subcommands": {
"today": {
"$": "today",
"description": "Today"
},
"30": {
"$": "30",
"description": "30 days"
},
"60": {
"$": "60",
"description": "60 days"
},
"90": {
"$": "90",
"description": "90 days"
},
"career": {
"$": "career",
"description": "Career"
},
"custom": {
"$": "custom",
"description": "Custom range",
"options": {
"start": {
"$": "start",
"description": "Start in number of days ago from today"
},
"end": {
"$": "end",
"description": "End in number of days ago from today"
}
}
}
},
"options": {
"clan": {
"$": "clan",
"description": "The clan name or tag"
},
"tank": {
"$": "tank",
"description": "The tank to get stats for"
},
"username": {
"$": "username",
"description": "The Blitz username of the player"
},
"filters": {
"nation": {
"$": "nation",
"description": "Nation"
},
"tank_class": {
"$": "tank-class",
"description": "Tank class"
},
"tree_type": {
"$": "tree-type",
"description": "Tree type"
}
},
"region": {
"$": "region",
"description": "Region"
},
"tier": {
"$": "tier",
"description": "Tier",
"choices": {
"tier": "Tier %s - %s"
}
}
},
"periods": {
"career": "Career",
"today": "Today",
"days": "%s days",
"custom": "%s - %s days"
},
"filters": {
"none": "No filters",
"tier": "Tier %s"
},
"no_data": {
"battles_in_period": "No battles played in this period",
"battles_in_season": "No battles played in this season",
"players_in_period": "No players in this period",
"tanks_found": "No tanks found"
},
"buttons": {
"refresh": "Refresh"
},
"hero_stats": {
"battles": "Battles",
"winrate": "Winrate",
"damage": "Damage",
"damage_ratio": "Damage ratio",
"tier": "Tier",
"survival": "Survival",
"accuracy": "Accuracy",
"kills": "Kills"
}
},
"commands": {
"auction": {
"$": "auction",
"description": "Tanks available right now in the auction",
"body": {
"title": "Blitz Auction",
"subtitle": "%s available • next prices %s\nBid now: %s",
"next": "Next price: %s",
"available": "Available: %s / %s"
},
"errors": {
"no_auction": "# No ongoing auction\n\nCurrently, there are no tanks available for purchase in the auction. If you think this is an error, please report this discrepancy in [the official Discord Server](https://discord.gg/nDt7AjGJQH)."
},
"actions": {
"bid": "Bid now"
}
},
"replay": {
"$": "replay",
"description": "Display replay results"
},
"research": {
"$": "research",
"description": "Lists the expenses in researching a line",
"options": {
"target_tank": {
"$": "target-tank",
"description": "The final tank to research in the line"
},
"starting_tank": {
"$": "starting-tank",
"description": "The starting tank in the line (inferred automatically if not provided)"
}
},
"body": {
"subtitle": "Research",
"total": "Total",
"research": "Research: %s",
"upgrades": "Upgrades: %s",
"purchase": "Purchase: %s",
"equipment": "Equipment: %s",
"estimation": "# This is an estimation!\n\nThis list may include already researched tanks because of the inaccurate information Wargaming provides. Try providing a `starting-tank` from the line when using the command if you can."
},
"errors": {
"non_tech_tree": "# %s is not on the tech tree\n\nYou cannot research a premium/collector tank.",
"tier_1": "# %s is a tier I\n\nTier I tanks are always unlocked for free for all players.",
"already_researched": "# %s is already researched\n\nTry providing a `starting-tank` when using the command to view the totals for a line you have already researched.",
"start_end_equal": "# %s was provided twice\n\nMake sure `starting-tank` and `target-tank` are different.",
"tanks_not_on_line": "# %s and %s are not on the same line\n\nMake sure `starting-tank` and `target-tank` are on the same tech-tree line.",
"unordered": "# %s higher tiered to %s\n\nMake sure to provide %s as the `starting-tank` instead as it is the lower tiered tank."
}
},
"about": {
"$": "about",
"description": "All the info you need about the bot",
"subcommands": {
"commands": {
"$": "commands",
"description": "List of all commands and their descriptions",
"body": "# Commands\n\nBlitzKit offers the following commands:\n\n"
},
"embeds": {
"$": "embeds",
"description": "How embeds work"
},
"introduction": {
"$": "introduction",
"description": "About the bot"
},
"invite": {
"$": "invite",
"description": "How to invite the bot"
},
"timezones": {
"$": "timezones",
"description": "How BlitzKit infers and uses your timezone"
},
"credits": {
"$": "credits",
"description": "People who have helped BlitzKit"
}
}
},
"breakdown": {
"$": "breakdown",
"description": "A period's breakdown by tanks played",
"body": {
"total": "Total",
"battles": "Battles",
"wn8": "WN8",
"winrate": "Winrate",
"damage": "Damage",
"unknown_tank": "Unknown tank %s"
}
},
"debug": {
"$": "debug",
"description": "Debug information about the bot",
"body": {
"title": "# Debug\n\n",
"version": "Version",
"shard": {
"$": "Shard",
"default": "default"
},
"uptime": "Uptime"
}
},
"evolution": {
"$": "evolution",
"description": "Evolution of statistics",
"groups": {
"player": {
"$": "player",
"description": "Player statistics"
},
"tank": {
"$": "tank",
"description": "Tank statistics"
}
}
},
"inactive": {
"$": "inactive",
"description": "Lists all inactive players in a clan above a threshold",
"options": {
"threshold": {
"$": "threshold",
"description": "The number of days inactive (default: %s days)"
}
},
"body": {
"subtitle": "Inactive for %s+ days",
"listing": "%s days"
}
},
"full_stats": {
"$": "full-stats",
"description": "Full in-game statistics",
"body": {
"cards": {
"games": {
"$": "Games",
"winrate": "Winrate",
"battles": "Battles",
"wins_losses": "Wins / losses"
},
"efficiency": {
"$": "Efficiency",
"wn8": "WN8",
"survival": "Survival",
"accuracy": "Accuracy"
},
"lethality": {
"$": "Lethality",
"average_damage": "Average damage",
"damage_per_tier": "Damage per tier",
"damage_ratio": "Damage ratio",
"average_kills": "Average kills"
},
"miscellaneous": {
"$": "Miscellaneous",
"average_tier": "Average tier",
"average_spots": "Average spots",
"average_xp": "Average XP",
"kills_to_death_ratio": "Kills to death ratio"
}
}
}
},
"link": {
"$": "link",
"description": "Links your Blitz and Discord account",
"body": {
"accounts_linked": "Accounts linked",
"no_role_permissions": {
"$": "%s failed to verify",
"description": "I don't have the permission to manage roles."
}
},
"embed": {
"title": "Incorrect account?",
"description": "Use the command again and select a username from the search results."
}
},
"search_clans": {
"$": "search-clans",
"description": "Search clans",
"options": {
"limit": {
"$": "limit",
"description": "The max number of clan results (default: %s)"
}
},
"body": {
"title": "Clan search for %s in region %s",
"no_results": "No clans found"
}
},
"search_players": {
"$": "search-players",
"description": "Search players",
"options": {
"limit": {
"$": "limit",
"description": "The max number of player results (default: %s)"
}
},
"body": {
"title": "Player search for %s in region %s",
"no_results": "No players found"
}
},
"rating_leaderboard": {
"$": "rating-leaderboard",
"description": "Rating battles statistics",
"subcommands": {
"neighbors": {
"$": "neighbors",
"description": "Neighboring players on the leaderboard"
},
"league": {
"$": "league",
"description": "Top players of a league"
}
},
"options": {
"limit": {
"$": "limit",
"description": "How many neighbors to display (default: %s)"
},
"season": {
"$": "season",
"description": "Season number (default: current)",
"choices": {
"current": "%s (current)"
}
},
"league": {
"$": "league",
"description": "The league to display"
}
},
"body": {
"top_players": "Top %s players",
"no_participation": "# %s didn't play rating in season %s\n\nThis player did not participate in this season or did not get past calibration.",
"no_ongoing_season": "# No ongoing season\n\nThere isn't a currently running rating season.",
"deleted_player": "Deleted %s",
"neighbors": "Rating neighbors",
"more": "+ %s more"
}
},
"today": {
"$": "today",
"description": "Today's played tanks and statistics"
},
"stats": {
"$": "stats",
"description": "Quick statistics for a period"
},
"owned_tanks": {
"$": "owned-tanks",
"description": "Shows all own tanks in a tier",
"body": {
"title": "# %s's owned tier %s tanks\n\n"
}
},
"permissions": {
"$": "permissions",
"description": "Checks wether the bot has the required permissions to function",
"body": {
"title": "# Permissions\n\n%s",
"view_channels": "View channels: needed to find the message the refresh button was used on",
"read_message_history": "Read message history: needed to find the message to refresh",
"attach_files": "Attach files: needed to add images to image-based commands",
"external_emojis": "External emojis: needed to add national flags and tank class emojis"
}
},
"ping": {
"$": "ping",
"description": "Check wether the bot is alive.",
"body": {
"pong": "Pong 🏓",
"pong_with_time": "Pong 🏓 - %sms"
},
"subcommands": {
"blitzkit": {
"$": "blitzkit",
"description": "Ping BlitzKit"
},
"wotb": {
"$": "wotb",
"description": "Ping World of Tanks Blitz"
}
}
},
"player_info": {
"$": "player-info",
"description": "Basic information about a player",
"body": {
"title": "%s's information",
"nickname": "Nickname",
"battles": "Battles",
"winrate": "Winrate",
"account_id": "Account ID",
"created": "Created",
"last_battle": "Last battle"
}
},
"rating": {
"$": "rating",
"description": "Rating player's statistics",
"subcommands": {
"today": {
"$": "today",
"description": "Today's rating statistics"
},
"season": {
"$": "season",
"description": "This season's rating statistics"
}
},
"body": {
"subtitle": {
"today": "Today's rating statistics",
"season": "This season's rating statistics"
}
},
"errors": {
"no_participation": "# %s hasn't played rating\n\nThis player hasn't participated in the rating gamemode this season.",
"unrecorded_stats": "# Statistics currently unavailable\n\nWargaming may be currently be facing server downtime. If there is an on-going season, statistics should be available withing 24 hours."
}
}
}
},
"website": {}
}
-601
View File
@@ -1,601 +0,0 @@
{
"common": {
"wn8_percentile": {
"0": "Very bad",
"1": "Bad",
"2": "Below average",
"3": "Average",
"4": "Above average",
"5": "Good",
"6": "Very good",
"7": "Great",
"8": "Unicum",
"9": "Super unicum"
},
"bkni_percentile": {
"0": "Super malum",
"1": "Malum",
"2": "Very bad",
"3": "Bad",
"4": "Below average",
"5": "Average",
"6": "Above average",
"7": "Good",
"8": "Very good",
"9": "Unicum",
"10": "Super unicum"
},
"nations": {
"ussr": "U.S.S.R.",
"germany": "Germany",
"usa": "U.S.A.",
"china": "China",
"uk": "U.K.",
"france": "France",
"japan": "Japan",
"european": "European Nation",
"other": "Hybrid Nation"
},
"nations_adjectives": {
"ussr": "U.S.S.R.",
"germany": "German",
"usa": "U.S.A.",
"china": "Chinese",
"uk": "U.K.",
"france": "French",
"japan": "Japanese",
"european": "European",
"other": "Hybrid"
},
"tank_class_short": {
"-1": "unrecognized",
"0": "Light",
"1": "Medium",
"2": "Heavy",
"3": "TD"
},
"tank_class_short_plural_lowercase": {
"0": "lights",
"1": "mediums",
"2": "heavies",
"3": "TDs"
},
"tank_class_medium": {
"-1": "unrecognized",
"0": "Light",
"1": "Medium",
"2": "Heavy",
"3": "Tank destroyer"
},
"tree_type": {
"-1": "unrecognized",
"0": "Tech tree",
"1": "Premium",
"2": "Collector"
},
"regions": {
"normal": {
"com": "North America",
"eu": "Europe",
"asia": "Asia-Pacific"
},
"short": {
"com": "NA",
"eu": "EU",
"asia": "APAC"
}
},
"leagues": {
"diamond": "Diamond",
"platinum": "Platinum",
"gold": "Gold",
"silver": "Silver",
"bronze": "Bronze"
},
"composite_stats": {
"normalized_damage_dealt": "Damage",
"normalized_damage_received": "Damage received",
"normalized_deaths": "Deathrate",
"normalized_frags": "Kills",
"normalized_hits": "Hits",
"normalized_losses": "Lossrate",
"normalized_misses": "Misses",
"normalized_shots": "Shots",
"normalized_spotted": "Spots",
"normalized_survived_battles": "Survival rate",
"normalized_win_and_survived": "Winning and surviving rate",
"normalized_wins": "Winrate",
"normalized_xp": "XP",
"normalized_battle_life_time": "Battle time",
"cumulative_accuracy": "Accuracy",
"cumulative_battles": "Battles",
"cumulative_capture_points": "Total capture points",
"cumulative_damage_dealt": "Total damage",
"cumulative_damage_ratio": "Damage ratio",
"cumulative_damage_received": "Total damage received",
"cumulative_deaths": "Total deaths",
"cumulative_dropped_capture_points": "Total dropped capture points",
"cumulative_frags": "Total kills",
"cumulative_hits": "Total hits",
"cumulative_inaccuracy": "Inaccuracy",
"cumulative_kills_to_death_ratio": "Kills to death ratio",
"cumulative_losses": "Losses",
"cumulative_misses": "Total misses",
"cumulative_shots": "Total shots",
"cumulative_spotted": "Total spots",
"cumulative_survived_battles": "Survived battles",
"cumulative_win_and_survived": "Won and survived battles",
"cumulative_wins": "Wins",
"cumulative_xp": "Total XP",
"cumulative_wn8": "WN8",
"cumulative_battle_life_time": "Total battle time"
}
},
"bot": {
"common": {
"errors": {
"clan_not_found": "# Could not find clan\nCouldn't find \"%s\". Try selecting a clan from the search result.",
"player_not_found": "# Could not find user\nI couldn't find user \"%s\". Try picking an user from the search result, typing in a valid username, or using the `/link` command.",
"player_not_linked": "# Use `/link` to get started\nI don't know who you are but you can tell me today!",
"tank_name_not_found": "# Tank not found\nCould not find tank by the name \"%s\".",
"tank_id_not_found": "# Tank not found\nCould not find tank by the ID \"%s\". Try typing the name of the tank instead of a number.",
"no_tank_stats": "# No tank stats available\nThis player doesn't have any stats available. This may not be the player you are looking for.",
"get_help": "Help Discord server",
"uncaught_error": "# BlitzKit ran into an error!\nWe're so sorry about this. Feel free to [join the official Discord server](https://discord.gg/nDt7AjGJQH) to get help.",
"preview": "# This command is in preview\n\nYou can only access this command in [the official Discord server](https://discord.gg/nDt7AjGJQH). The command will be generally available soon."
},
"wn8": {
"$": "WN8",
"very_bad": "Very bad",
"bad": "Bad",
"below_average": "Below average",
"average": "Average",
"above_average": "Above average",
"good": "Good",
"very_good": "Very good",
"great": "Great",
"unicum": "Unicum",
"super_unicum": "Super unicum"
},
"subcommands": {
"today": {
"$": "today",
"description": "Today"
},
"30": {
"$": "30",
"description": "30 days"
},
"60": {
"$": "60",
"description": "60 days"
},
"90": {
"$": "90",
"description": "90 days"
},
"career": {
"$": "career",
"description": "Career"
},
"custom": {
"$": "custom",
"description": "Custom range",
"options": {
"start": {
"$": "start",
"description": "Start in number of days ago from today"
},
"end": {
"$": "end",
"description": "End in number of days ago from today"
}
}
}
},
"options": {
"clan": {
"$": "clan",
"description": "The clan name or tag"
},
"tank": {
"$": "tank",
"description": "The tank to get stats for"
},
"username": {
"$": "username",
"description": "The Blitz username of the player"
},
"filters": {
"nation": {
"$": "nation",
"description": "Nation"
},
"tank_class": {
"$": "tank-class",
"description": "Tank class"
},
"tree_type": {
"$": "tree-type",
"description": "Tree type"
}
},
"region": {
"$": "region",
"description": "Region"
},
"tier": {
"$": "tier",
"description": "Tier",
"choices": {
"tier": "Tier %s - %s"
}
}
},
"periods": {
"career": "Career",
"today": "Today",
"days": "%s days",
"custom": "%s - %s days"
},
"filters": {
"none": "No filters",
"tier": "Tier %s"
},
"no_data": {
"battles_in_period": "No battles played in this period",
"battles_in_season": "No battles played in this season",
"players_in_period": "No players in this period",
"tanks_found": "No tanks found"
},
"buttons": {
"refresh": "Refresh"
},
"hero_stats": {
"battles": "Battles",
"winrate": "Winrate",
"damage": "Damage",
"damage_ratio": "Damage ratio",
"tier": "Tier",
"survival": "Survival",
"accuracy": "Accuracy",
"kills": "Kills"
}
},
"commands": {
"auction": {
"$": "auction",
"description": "Tanks available right now in the auction",
"body": {
"title": "Blitz Auction",
"subtitle": "%s available • next prices %s\nBid now: %s",
"next": "Next price: %s",
"available": "Available: %s / %s"
},
"errors": {
"no_auction": "# No ongoing auction\n\nCurrently, there are no tanks available for purchase in the auction. If you think this is an error, please report this discrepancy in [the official Discord Server](https://discord.gg/nDt7AjGJQH)."
},
"actions": {
"bid": "Bid now"
}
},
"replay": {
"$": "replay",
"description": "Display replay results"
},
"research": {
"$": "research",
"description": "Lists the expenses in researching a line",
"options": {
"target_tank": {
"$": "target-tank",
"description": "The final tank to research in the line"
},
"starting_tank": {
"$": "starting-tank",
"description": "The starting tank in the line (inferred automatically if not provided)"
}
},
"body": {
"subtitle": "Research",
"total": "Total",
"research": "Research: %s",
"upgrades": "Upgrades: %s",
"purchase": "Purchase: %s",
"equipment": "Equipment: %s",
"estimation": "# This is an estimation!\n\nThis list may include already researched tanks because of the inaccurate information Wargaming provides. Try providing a `starting-tank` from the line when using the command if you can."
},
"errors": {
"non_tech_tree": "# %s is not on the tech tree\n\nYou cannot research a premium/collector tank.",
"tier_1": "# %s is a tier I\n\nTier I tanks are always unlocked for free for all players.",
"already_researched": "# %s is already researched\n\nTry providing a `starting-tank` when using the command to view the totals for a line you have already researched.",
"start_end_equal": "# %s was provided twice\n\nMake sure `starting-tank` and `target-tank` are different.",
"tanks_not_on_line": "# %s and %s are not on the same line\n\nMake sure `starting-tank` and `target-tank` are on the same tech-tree line.",
"unordered": "# %s higher tiered to %s\n\nMake sure to provide %s as the `starting-tank` instead as it is the lower tiered tank."
}
},
"about": {
"$": "about",
"description": "All the info you need about the bot",
"subcommands": {
"commands": {
"$": "commands",
"description": "List of all commands and their descriptions",
"body": "# Commands\n\nBlitzKit offers the following commands:\n\n"
},
"embeds": {
"$": "embeds",
"description": "How embeds work"
},
"introduction": {
"$": "introduction",
"description": "About the bot"
},
"invite": {
"$": "invite",
"description": "How to invite the bot"
},
"timezones": {
"$": "timezones",
"description": "How BlitzKit infers and uses your timezone"
},
"credits": {
"$": "credits",
"description": "People who have helped BlitzKit"
}
}
},
"breakdown": {
"$": "breakdown",
"description": "A period's breakdown by tanks played",
"body": {
"total": "Total",
"battles": "Battles",
"wn8": "WN8",
"winrate": "Winrate",
"damage": "Damage",
"unknown_tank": "Unknown tank %s"
}
},
"debug": {
"$": "debug",
"description": "Debug information about the bot",
"body": {
"title": "# Debug\n\n",
"version": "Version",
"shard": {
"$": "Shard",
"default": "default"
},
"uptime": "Uptime"
}
},
"evolution": {
"$": "evolution",
"description": "Evolution of statistics",
"groups": {
"player": {
"$": "player",
"description": "Player statistics"
},
"tank": {
"$": "tank",
"description": "Tank statistics"
}
}
},
"inactive": {
"$": "inactive",
"description": "Lists all inactive players in a clan above a threshold",
"options": {
"threshold": {
"$": "threshold",
"description": "The number of days inactive (default: %s days)"
}
},
"body": {
"subtitle": "Inactive for %s+ days",
"listing": "%s days"
}
},
"full_stats": {
"$": "full-stats",
"description": "Full in-game statistics",
"body": {
"cards": {
"games": {
"$": "Games",
"winrate": "Winrate",
"battles": "Battles",
"wins_losses": "Wins / losses"
},
"efficiency": {
"$": "Efficiency",
"wn8": "WN8",
"survival": "Survival",
"accuracy": "Accuracy"
},
"lethality": {
"$": "Lethality",
"average_damage": "Average damage",
"damage_per_tier": "Damage per tier",
"damage_ratio": "Damage ratio",
"average_kills": "Average kills"
},
"miscellaneous": {
"$": "Miscellaneous",
"average_tier": "Average tier",
"average_spots": "Average spots",
"average_xp": "Average XP",
"kills_to_death_ratio": "Kills to death ratio"
}
}
}
},
"link": {
"$": "link",
"description": "Links your Blitz and Discord account",
"body": {
"accounts_linked": "Accounts linked",
"no_role_permissions": {
"$": "%s failed to verify",
"description": "I don't have the permission to manage roles."
}
},
"embed": {
"title": "Incorrect account?",
"description": "Use the command again and select a username from the search results."
}
},
"search_clans": {
"$": "search-clans",
"description": "Search clans",
"options": {
"limit": {
"$": "limit",
"description": "The max number of clan results (default: %s)"
}
},
"body": {
"title": "Clan search for %s in region %s",
"no_results": "No clans found"
}
},
"search_players": {
"$": "search-players",
"description": "Search players",
"options": {
"limit": {
"$": "limit",
"description": "The max number of player results (default: %s)"
}
},
"body": {
"title": "Player search for %s in region %s",
"no_results": "No players found"
}
},
"rating_leaderboard": {
"$": "rating-leaderboard",
"description": "Rating battles statistics",
"subcommands": {
"neighbors": {
"$": "neighbors",
"description": "Neighboring players on the leaderboard"
},
"league": {
"$": "league",
"description": "Top players of a league"
}
},
"options": {
"limit": {
"$": "limit",
"description": "How many neighbors to display (default: %s)"
},
"season": {
"$": "season",
"description": "Season number (default: current)",
"choices": {
"current": "%s (current)"
}
},
"league": {
"$": "league",
"description": "The league to display"
}
},
"body": {
"top_players": "Top %s players",
"no_participation": "# %s didn't play rating in season %s\n\nThis player did not participate in this season or did not get past calibration.",
"no_ongoing_season": "# No ongoing season\n\nThere isn't a currently running rating season.",
"deleted_player": "Deleted %s",
"neighbors": "Rating neighbors",
"more": "+ %s more"
}
},
"today": {
"$": "today",
"description": "Today's played tanks and statistics"
},
"stats": {
"$": "stats",
"description": "Quick statistics for a period"
},
"owned_tanks": {
"$": "owned-tanks",
"description": "Shows all own tanks in a tier",
"body": {
"title": "# %s's owned tier %s tanks\n\n"
}
},
"permissions": {
"$": "permissions",
"description": "Checks wether the bot has the required permissions to function",
"body": {
"title": "# Permissions\n\n%s",
"view_channels": "View channels: needed to find the message the refresh button was used on",
"read_message_history": "Read message history: needed to find the message to refresh",
"attach_files": "Attach files: needed to add images to image-based commands",
"external_emojis": "External emojis: needed to add national flags and tank class emojis"
}
},
"ping": {
"$": "ping",
"description": "Check wether the bot is alive.",
"body": {
"pong": "Pong 🏓",
"pong_with_time": "Pong 🏓 - %sms"
},
"subcommands": {
"blitzkit": {
"$": "blitzkit",
"description": "Ping BlitzKit"
},
"wotb": {
"$": "wotb",
"description": "Ping World of Tanks Blitz"
}
}
},
"player_info": {
"$": "player-info",
"description": "Basic information about a player",
"body": {
"title": "%s's information",
"nickname": "Nickname",
"battles": "Battles",
"winrate": "Winrate",
"account_id": "Account ID",
"created": "Created",
"last_battle": "Last battle"
}
},
"rating": {
"$": "rating",
"description": "Rating player's statistics",
"subcommands": {
"today": {
"$": "today",
"description": "Today's rating statistics"
},
"season": {
"$": "season",
"description": "This season's rating statistics"
}
},
"body": {
"subtitle": {
"today": "Today's rating statistics",
"season": "This season's rating statistics"
}
},
"errors": {
"no_participation": "# %s hasn't played rating\n\nThis player hasn't participated in the rating gamemode this season.",
"unrecorded_stats": "# Statistics currently unavailable\n\nWargaming may be currently be facing server downtime. If there is an on-going season, statistics should be available withing 24 hours."
}
}
}
},
"website": {}
}
-601
View File
@@ -1,601 +0,0 @@
{
"common": {
"wn8_percentile": {
"0": "Very bad",
"1": "Bad",
"2": "Below average",
"3": "Average",
"4": "Above average",
"5": "Good",
"6": "Very good",
"7": "Great",
"8": "Unicum",
"9": "Super unicum"
},
"bkni_percentile": {
"0": "Super malum",
"1": "Malum",
"2": "Very bad",
"3": "Bad",
"4": "Below average",
"5": "Average",
"6": "Above average",
"7": "Good",
"8": "Very good",
"9": "Unicum",
"10": "Super unicum"
},
"nations": {
"ussr": "U.S.S.R.",
"germany": "Germany",
"usa": "U.S.A.",
"china": "China",
"uk": "U.K.",
"france": "France",
"japan": "Japan",
"european": "European Nation",
"other": "Hybrid Nation"
},
"nations_adjectives": {
"ussr": "U.S.S.R.",
"germany": "German",
"usa": "U.S.A.",
"china": "Chinese",
"uk": "U.K.",
"france": "French",
"japan": "Japanese",
"european": "European",
"other": "Hybrid"
},
"tank_class_short": {
"-1": "unrecognized",
"0": "Light",
"1": "Medium",
"2": "Heavy",
"3": "TD"
},
"tank_class_short_plural_lowercase": {
"0": "lights",
"1": "mediums",
"2": "heavies",
"3": "TDs"
},
"tank_class_medium": {
"-1": "unrecognized",
"0": "Light",
"1": "Medium",
"2": "Heavy",
"3": "Tank destroyer"
},
"tree_type": {
"-1": "unrecognized",
"0": "Tech tree",
"1": "Premium",
"2": "Collector"
},
"regions": {
"normal": {
"com": "North America",
"eu": "Europe",
"asia": "Asia-Pacific"
},
"short": {
"com": "NA",
"eu": "EU",
"asia": "APAC"
}
},
"leagues": {
"diamond": "Diamond",
"platinum": "Platinum",
"gold": "Gold",
"silver": "Silver",
"bronze": "Bronze"
},
"composite_stats": {
"normalized_damage_dealt": "Damage",
"normalized_damage_received": "Damage received",
"normalized_deaths": "Deathrate",
"normalized_frags": "Kills",
"normalized_hits": "Hits",
"normalized_losses": "Lossrate",
"normalized_misses": "Misses",
"normalized_shots": "Shots",
"normalized_spotted": "Spots",
"normalized_survived_battles": "Survival rate",
"normalized_win_and_survived": "Winning and surviving rate",
"normalized_wins": "Winrate",
"normalized_xp": "XP",
"normalized_battle_life_time": "Battle time",
"cumulative_accuracy": "Accuracy",
"cumulative_battles": "Battles",
"cumulative_capture_points": "Total capture points",
"cumulative_damage_dealt": "Total damage",
"cumulative_damage_ratio": "Damage ratio",
"cumulative_damage_received": "Total damage received",
"cumulative_deaths": "Total deaths",
"cumulative_dropped_capture_points": "Total dropped capture points",
"cumulative_frags": "Total kills",
"cumulative_hits": "Total hits",
"cumulative_inaccuracy": "Inaccuracy",
"cumulative_kills_to_death_ratio": "Kills to death ratio",
"cumulative_losses": "Losses",
"cumulative_misses": "Total misses",
"cumulative_shots": "Total shots",
"cumulative_spotted": "Total spots",
"cumulative_survived_battles": "Survived battles",
"cumulative_win_and_survived": "Won and survived battles",
"cumulative_wins": "Wins",
"cumulative_xp": "Total XP",
"cumulative_wn8": "WN8",
"cumulative_battle_life_time": "Total battle time"
}
},
"bot": {
"common": {
"errors": {
"clan_not_found": "# Could not find clan\nCouldn't find \"%s\". Try selecting a clan from the search result.",
"player_not_found": "# Could not find user\nI couldn't find user \"%s\". Try picking an user from the search result, typing in a valid username, or using the `/link` command.",
"player_not_linked": "# Use `/link` to get started\nI don't know who you are but you can tell me today!",
"tank_name_not_found": "# Tank not found\nCould not find tank by the name \"%s\".",
"tank_id_not_found": "# Tank not found\nCould not find tank by the ID \"%s\". Try typing the name of the tank instead of a number.",
"no_tank_stats": "# No tank stats available\nThis player doesn't have any stats available. This may not be the player you are looking for.",
"get_help": "Help Discord server",
"uncaught_error": "# BlitzKit ran into an error!\nWe're so sorry about this. Feel free to [join the official Discord server](https://discord.gg/nDt7AjGJQH) to get help.",
"preview": "# This command is in preview\n\nYou can only access this command in [the official Discord server](https://discord.gg/nDt7AjGJQH). The command will be generally available soon."
},
"wn8": {
"$": "WN8",
"very_bad": "Very bad",
"bad": "Bad",
"below_average": "Below average",
"average": "Average",
"above_average": "Above average",
"good": "Good",
"very_good": "Very good",
"great": "Great",
"unicum": "Unicum",
"super_unicum": "Super unicum"
},
"subcommands": {
"today": {
"$": "today",
"description": "Today"
},
"30": {
"$": "30",
"description": "30 days"
},
"60": {
"$": "60",
"description": "60 days"
},
"90": {
"$": "90",
"description": "90 days"
},
"career": {
"$": "career",
"description": "Career"
},
"custom": {
"$": "custom",
"description": "Custom range",
"options": {
"start": {
"$": "start",
"description": "Start in number of days ago from today"
},
"end": {
"$": "end",
"description": "End in number of days ago from today"
}
}
}
},
"options": {
"clan": {
"$": "clan",
"description": "The clan name or tag"
},
"tank": {
"$": "tank",
"description": "The tank to get stats for"
},
"username": {
"$": "username",
"description": "The Blitz username of the player"
},
"filters": {
"nation": {
"$": "nation",
"description": "Nation"
},
"tank_class": {
"$": "tank-class",
"description": "Tank class"
},
"tree_type": {
"$": "tree-type",
"description": "Tree type"
}
},
"region": {
"$": "region",
"description": "Region"
},
"tier": {
"$": "tier",
"description": "Tier",
"choices": {
"tier": "Tier %s - %s"
}
}
},
"periods": {
"career": "Career",
"today": "Today",
"days": "%s days",
"custom": "%s - %s days"
},
"filters": {
"none": "No filters",
"tier": "Tier %s"
},
"no_data": {
"battles_in_period": "No battles played in this period",
"battles_in_season": "No battles played in this season",
"players_in_period": "No players in this period",
"tanks_found": "No tanks found"
},
"buttons": {
"refresh": "Refresh"
},
"hero_stats": {
"battles": "Battles",
"winrate": "Winrate",
"damage": "Damage",
"damage_ratio": "Damage ratio",
"tier": "Tier",
"survival": "Survival",
"accuracy": "Accuracy",
"kills": "Kills"
}
},
"commands": {
"auction": {
"$": "auction",
"description": "Tanks available right now in the auction",
"body": {
"title": "Blitz Auction",
"subtitle": "%s available • next prices %s\nBid now: %s",
"next": "Next price: %s",
"available": "Available: %s / %s"
},
"errors": {
"no_auction": "# No ongoing auction\n\nCurrently, there are no tanks available for purchase in the auction. If you think this is an error, please report this discrepancy in [the official Discord Server](https://discord.gg/nDt7AjGJQH)."
},
"actions": {
"bid": "Bid now"
}
},
"replay": {
"$": "replay",
"description": "Display replay results"
},
"research": {
"$": "research",
"description": "Lists the expenses in researching a line",
"options": {
"target_tank": {
"$": "target-tank",
"description": "The final tank to research in the line"
},
"starting_tank": {
"$": "starting-tank",
"description": "The starting tank in the line (inferred automatically if not provided)"
}
},
"body": {
"subtitle": "Research",
"total": "Total",
"research": "Research: %s",
"upgrades": "Upgrades: %s",
"purchase": "Purchase: %s",
"equipment": "Equipment: %s",
"estimation": "# This is an estimation!\n\nThis list may include already researched tanks because of the inaccurate information Wargaming provides. Try providing a `starting-tank` from the line when using the command if you can."
},
"errors": {
"non_tech_tree": "# %s is not on the tech tree\n\nYou cannot research a premium/collector tank.",
"tier_1": "# %s is a tier I\n\nTier I tanks are always unlocked for free for all players.",
"already_researched": "# %s is already researched\n\nTry providing a `starting-tank` when using the command to view the totals for a line you have already researched.",
"start_end_equal": "# %s was provided twice\n\nMake sure `starting-tank` and `target-tank` are different.",
"tanks_not_on_line": "# %s and %s are not on the same line\n\nMake sure `starting-tank` and `target-tank` are on the same tech-tree line.",
"unordered": "# %s higher tiered to %s\n\nMake sure to provide %s as the `starting-tank` instead as it is the lower tiered tank."
}
},
"about": {
"$": "about",
"description": "All the info you need about the bot",
"subcommands": {
"commands": {
"$": "commands",
"description": "List of all commands and their descriptions",
"body": "# Commands\n\nBlitzKit offers the following commands:\n\n"
},
"embeds": {
"$": "embeds",
"description": "How embeds work"
},
"introduction": {
"$": "introduction",
"description": "About the bot"
},
"invite": {
"$": "invite",
"description": "How to invite the bot"
},
"timezones": {
"$": "timezones",
"description": "How BlitzKit infers and uses your timezone"
},
"credits": {
"$": "credits",
"description": "People who have helped BlitzKit"
}
}
},
"breakdown": {
"$": "breakdown",
"description": "A period's breakdown by tanks played",
"body": {
"total": "Total",
"battles": "Battles",
"wn8": "WN8",
"winrate": "Winrate",
"damage": "Damage",
"unknown_tank": "Unknown tank %s"
}
},
"debug": {
"$": "debug",
"description": "Debug information about the bot",
"body": {
"title": "# Debug\n\n",
"version": "Version",
"shard": {
"$": "Shard",
"default": "default"
},
"uptime": "Uptime"
}
},
"evolution": {
"$": "evolution",
"description": "Evolution of statistics",
"groups": {
"player": {
"$": "player",
"description": "Player statistics"
},
"tank": {
"$": "tank",
"description": "Tank statistics"
}
}
},
"inactive": {
"$": "inactive",
"description": "Lists all inactive players in a clan above a threshold",
"options": {
"threshold": {
"$": "threshold",
"description": "The number of days inactive (default: %s days)"
}
},
"body": {
"subtitle": "Inactive for %s+ days",
"listing": "%s days"
}
},
"full_stats": {
"$": "full-stats",
"description": "Full in-game statistics",
"body": {
"cards": {
"games": {
"$": "Games",
"winrate": "Winrate",
"battles": "Battles",
"wins_losses": "Wins / losses"
},
"efficiency": {
"$": "Efficiency",
"wn8": "WN8",
"survival": "Survival",
"accuracy": "Accuracy"
},
"lethality": {
"$": "Lethality",
"average_damage": "Average damage",
"damage_per_tier": "Damage per tier",
"damage_ratio": "Damage ratio",
"average_kills": "Average kills"
},
"miscellaneous": {
"$": "Miscellaneous",
"average_tier": "Average tier",
"average_spots": "Average spots",
"average_xp": "Average XP",
"kills_to_death_ratio": "Kills to death ratio"
}
}
}
},
"link": {
"$": "link",
"description": "Links your Blitz and Discord account",
"body": {
"accounts_linked": "Accounts linked",
"no_role_permissions": {
"$": "%s failed to verify",
"description": "I don't have the permission to manage roles."
}
},
"embed": {
"title": "Incorrect account?",
"description": "Use the command again and select a username from the search results."
}
},
"search_clans": {
"$": "search-clans",
"description": "Search clans",
"options": {
"limit": {
"$": "limit",
"description": "The max number of clan results (default: %s)"
}
},
"body": {
"title": "Clan search for %s in region %s",
"no_results": "No clans found"
}
},
"search_players": {
"$": "search-players",
"description": "Search players",
"options": {
"limit": {
"$": "limit",
"description": "The max number of player results (default: %s)"
}
},
"body": {
"title": "Player search for %s in region %s",
"no_results": "No players found"
}
},
"rating_leaderboard": {
"$": "rating-leaderboard",
"description": "Rating battles statistics",
"subcommands": {
"neighbors": {
"$": "neighbors",
"description": "Neighboring players on the leaderboard"
},
"league": {
"$": "league",
"description": "Top players of a league"
}
},
"options": {
"limit": {
"$": "limit",
"description": "How many neighbors to display (default: %s)"
},
"season": {
"$": "season",
"description": "Season number (default: current)",
"choices": {
"current": "%s (current)"
}
},
"league": {
"$": "league",
"description": "The league to display"
}
},
"body": {
"top_players": "Top %s players",
"no_participation": "# %s didn't play rating in season %s\n\nThis player did not participate in this season or did not get past calibration.",
"no_ongoing_season": "# No ongoing season\n\nThere isn't a currently running rating season.",
"deleted_player": "Deleted %s",
"neighbors": "Rating neighbors",
"more": "+ %s more"
}
},
"today": {
"$": "today",
"description": "Today's played tanks and statistics"
},
"stats": {
"$": "stats",
"description": "Quick statistics for a period"
},
"owned_tanks": {
"$": "owned-tanks",
"description": "Shows all own tanks in a tier",
"body": {
"title": "# %s's owned tier %s tanks\n\n"
}
},
"permissions": {
"$": "permissions",
"description": "Checks wether the bot has the required permissions to function",
"body": {
"title": "# Permissions\n\n%s",
"view_channels": "View channels: needed to find the message the refresh button was used on",
"read_message_history": "Read message history: needed to find the message to refresh",
"attach_files": "Attach files: needed to add images to image-based commands",
"external_emojis": "External emojis: needed to add national flags and tank class emojis"
}
},
"ping": {
"$": "ping",
"description": "Check wether the bot is alive.",
"body": {
"pong": "Pong 🏓",
"pong_with_time": "Pong 🏓 - %sms"
},
"subcommands": {
"blitzkit": {
"$": "blitzkit",
"description": "Ping BlitzKit"
},
"wotb": {
"$": "wotb",
"description": "Ping World of Tanks Blitz"
}
}
},
"player_info": {
"$": "player-info",
"description": "Basic information about a player",
"body": {
"title": "%s's information",
"nickname": "Nickname",
"battles": "Battles",
"winrate": "Winrate",
"account_id": "Account ID",
"created": "Created",
"last_battle": "Last battle"
}
},
"rating": {
"$": "rating",
"description": "Rating player's statistics",
"subcommands": {
"today": {
"$": "today",
"description": "Today's rating statistics"
},
"season": {
"$": "season",
"description": "This season's rating statistics"
}
},
"body": {
"subtitle": {
"today": "Today's rating statistics",
"season": "This season's rating statistics"
}
},
"errors": {
"no_participation": "# %s hasn't played rating\n\nThis player hasn't participated in the rating gamemode this season.",
"unrecorded_stats": "# Statistics currently unavailable\n\nWargaming may be currently be facing server downtime. If there is an on-going season, statistics should be available withing 24 hours."
}
}
}
},
"website": {}
}
+2 -2
View File
@@ -4,16 +4,16 @@
"type": "module",
"main": "src/index.ts",
"dependencies": {
"@blitzkit/closed": "workspace:*",
"@octokit/plugin-throttling": "^8.2.0",
"@octokit/rest": "^20.1.1",
"@prisma/client": "^5.19.1",
"@prisma/client": "^6.6.0",
"adm-zip": "^0.5.16",
"crc": "^4.3.2",
"dotenv": "^16.4.5",
"lz4js": "^0.2.0",
"pickleparser": "^0.2.1",
"protobufjs": "^7.4.0",
"sharp": "^0.33.5",
"ts-proto": "^2.2.2",
"yaml": "^2.5.0"
},
@@ -0,0 +1,28 @@
import { CatalogItem } from '@protos/blitz_items';
import { Any } from '@protos/google/protobuf/any';
import { MessageFns } from '../protos';
export class CatalogItemAccessor {
private components: Record<string, Any> = {};
constructor(item: CatalogItem) {
for (const component of item.components) {
if (
component.value === undefined ||
component.value.component === undefined
) {
continue;
}
this.components[component.key] = component.value.component;
}
}
get<Type>(Message: MessageFns<Type>, component: string) {
if (component in this.components) {
return Message.decode(this.components[component].value);
}
throw new Error(`Unknown component: ${component}`);
}
}
+4
View File
@@ -0,0 +1,4 @@
export function entityName(entity: string) {
const [_, ...name] = entity.split('.');
return name.join('.');
}
+11
View File
@@ -0,0 +1,11 @@
import { assertSecret } from '../blitzkit';
import { BlitzGlossary } from '../types';
export async function fetchGlossary(locale: string) {
const response = await fetch(
`${assertSecret(
import.meta.env.WOTB_GLOSSARY_DOMAIN,
)}/${locale}${assertSecret(import.meta.env.WOTB_GLOSSARY_PATH)}`,
);
return (await response.json()) as BlitzGlossary;
}
+4
View File
@@ -1,9 +1,12 @@
export * from './canSplash';
export * from './catalogItemAccessor';
export * from './clans';
export * from './classes';
export * from './constants';
export * from './entityName';
export * from './equipmentPrice';
export * from './fetchBlitz';
export * from './fetchGlossary';
export * from './generateStats';
export * from './getAccountAchievements';
export * from './getAccountInfo';
@@ -17,6 +20,7 @@ export * from './getTankStats';
export * from './idToRegion';
export * from './isOnGoingRatingSeason';
export * from './isValidBlitzId';
export * from './metadataAccessor';
export * from './nations';
export * from './normalizeExtra';
export * from './normalizeIds';
@@ -0,0 +1,38 @@
import { CatalogItem } from '@protos/blitz_items';
import { Metadata } from '@protos/blitz_metadata';
import { CatalogItemAccessor } from './catalogItemAccessor';
export class UnpackedMetadataAccessor {
private items: Record<string, CatalogItemAccessor> = {};
constructor(metadata: Metadata) {
for (const item of metadata.catalog_items) {
if (item.catalog_id in this.items) {
throw new Error(`Duplicate catalog ID: ${item.catalog_id}`);
}
this.items[item.catalog_id] = new CatalogItemAccessor(item);
}
}
get(item: string) {
if (item in this.items) return this.items[item];
throw new Error(`Unknown catalog ID: ${item}`);
}
}
export class DiscreteMetadataAccessor {
constructor(private base: string) {}
async get(item: string) {
const response = await fetch(`${this.base}/${item}.pb`);
if (!response.ok) throw new Error(`Unknown catalog ID: ${item}`);
const buffer = await response.arrayBuffer();
const catalogItem = CatalogItem.decode(new Uint8Array(buffer));
return new CatalogItemAccessor(catalogItem);
}
}
Loaded 100 of 487 files, more files were not shown because too many files have changed in this diff. Show more