Compare commits

...

218 Commits
main ... main

Author SHA1 Message Date
d794bd94ce fix: skip "prove yourself" text when unlocking all missions via cheat (#2965)
Even tho the PoE beginner bounty was skipped by the cheat, Konzu would still say "Gotta prove yourself first". Giving a non-zero standing value seems to bypass that (in this case I put ~~200~~ 250 which is identical to completing beginner bounty with bonus).

Reviewed-on: OpenWF/SpaceNinjaServer#2965
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: Animan8000 <animan8000@noreply.localhost>
Co-committed-by: Animan8000 <animan8000@noreply.localhost>
2025-11-01 02:52:15 -07:00
cecc65197b fix: set quest inactive when deleting it (#2963)
Closes #2958

Reviewed-on: OpenWF/SpaceNinjaServer#2963
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-11-01 02:52:05 -07:00
b1c1b56de3 feat: server-side conquest generation for U40 and above (#2962)
Closes #2932

Reviewed-on: OpenWF/SpaceNinjaServer#2962
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-11-01 02:51:58 -07:00
167da9c573 chore: don't splice quest stages when backtracking (#2961)
Inbox messages and items likely should not be given again

Reviewed-on: OpenWF/SpaceNinjaServer#2961
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-11-01 02:51:46 -07:00
5e6955ae32 chore: set nightwave activation to this week (#2960)
The weekly permanent challenges were already at 24

Reviewed-on: OpenWF/SpaceNinjaServer#2960
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-11-01 02:51:13 -07:00
f2145ed91b feat: remove quest related items (#2957)
if I haven't missed anything, this should close #1116

Reviewed-on: OpenWF/SpaceNinjaServer#2957
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-11-01 02:51:01 -07:00
20d9a699b4 chore(webui): rename inventory_maxPlexus to cheats_maxPlexus (#2956)
Reviewed-on: OpenWF/SpaceNinjaServer#2956
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: Animan8000 <animan8000@noreply.localhost>
Co-committed-by: Animan8000 <animan8000@noreply.localhost>
2025-10-30 23:07:50 -07:00
2b054d1728 chore(webui): update German translation (#2955)
Since German wf is a fkin mess with different terms being used for the exact same thing (for no reason at all I guess), I decided to stick with one term for WebUI instead of making the same mess that official German wf does:

`Enemy`, which uses "Feind" & "Gegner" in-game; I stick with Gegner.
`Health`, which uses "Gesundheit" & "Leben" in-game; I stick with Gesundheit.

Also includes some other small improvements.

Reviewed-on: OpenWF/SpaceNinjaServer#2955
Co-authored-by: Animan8000 <animan8000@noreply.localhost>
Co-committed-by: Animan8000 <animan8000@noreply.localhost>
2025-10-29 10:36:46 -07:00
5ac73528a0 chore: update inventory sync guidance to avoid confusiona (#2953)
Newer versions of the Bootstrapper do not require usage of /sync and other client patches might not have such a command.

Reviewed-on: OpenWF/SpaceNinjaServer#2953
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-29 06:25:22 -07:00
678ad0c4a1 fix(webui): display correct name for kuva weapons in detailedView (#2952)
Reviewed-on: OpenWF/SpaceNinjaServer#2952
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-10-29 06:25:14 -07:00
4bdb759463 fix: correct path for deepmind bounty reward tables (#2951)
Closes #2950

Reviewed-on: OpenWF/SpaceNinjaServer#2951
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-29 06:25:05 -07:00
71be8a2868 feat: nightwave dreams of the dead (#2949)
Reviewed-on: OpenWF/SpaceNinjaServer#2949
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-29 06:24:49 -07:00
f3072e84c9 feat: reverseQuestProgress (#2948)
Closes #2939

Reviewed-on: OpenWF/SpaceNinjaServer#2948
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-29 06:24:39 -07:00
b2749765a3 chore: fix nodejs deprecation warning in dev script (#2947)
Reviewed-on: OpenWF/SpaceNinjaServer#2947
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-29 06:24:32 -07:00
654652b889 chore: use bun instead of npm when running dev script under bun (#2946)
Reviewed-on: OpenWF/SpaceNinjaServer#2946
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-28 00:51:02 -07:00
e3048ea188 feat: ircExecutable config (#2945)
Reviewed-on: OpenWF/SpaceNinjaServer#2945
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-28 00:50:55 -07:00
bb1d6a98c5 chore: make docker setup compatible with regular mongodb data (#2944)
We still need to address the database as 'mongodb' instead of '127.0.0.1' inside of the container, but otherwise the MongoDB data folder can simply be copied over. Existing setups shouldn't be affected by this change.

Reviewed-on: OpenWF/SpaceNinjaServer#2944
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-28 00:50:01 -07:00
c3bf0ae7c7 ci: build multiplatform docker image 2025-10-27 11:56:54 +01:00
3a72617a0f feat: archgun arcane adapter (#2940)
Reviewed-on: OpenWF/SpaceNinjaServer#2940
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-10-27 00:21:32 -07:00
3ae535ccbc feat: deepmines bounties (#2933)
Closes #2936

Reviewed-on: OpenWF/SpaceNinjaServer#2933
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-10-26 06:37:43 -07:00
23abe5de02 fix: junction completion on steel path doesn't save (#2937)
Aka., an alternative approach to fixing the problem in #2866. Junctions don't have RewardInfo and therefore weren't reaching the new call to addMissionComplete.

Reviewed-on: OpenWF/SpaceNinjaServer#2937
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-25 00:27:02 -07:00
0d21c73ab7 fix: set ModQuestTeshinAccess when using cheats to complete mod quest (#2935)
This is required to go to Teshin's relay room after U40.

Reviewed-on: OpenWF/SpaceNinjaServer#2935
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-25 00:26:49 -07:00
482101ccd0 feat: nightcap syndicate (#2934)
Closes #2928
Closes #2931

Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Reviewed-on: OpenWF/SpaceNinjaServer#2934
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-10-25 00:26:36 -07:00
60e87543aa fixup: skipAllDialogue for the prince 2025-10-24 10:43:05 +02:00
c4c17f24d7 chore: add nightcap stuff for skipAllDialogues (#2930)
Reviewed-on: OpenWF/SpaceNinjaServer#2930
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-23 23:21:46 -07:00
43fa1978c0 feat(webui): remove IsNew (#2926)
Closes #2917

Reviewed-on: OpenWF/SpaceNinjaServer#2926
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-10-23 23:21:37 -07:00
18fafc38b5 feat: invasion additional credits (#2925)
Re #1097

Reviewed-on: OpenWF/SpaceNinjaServer#2925
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-10-23 07:42:18 -07:00
98a46e51de feat: complete Rising Tide with buying railjack (#2922)
Closes #2754

Reviewed-on: OpenWF/SpaceNinjaServer#2922
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-10-21 23:50:05 -07:00
2a7767ef4a chore(webui): update fr (#2924)
Reviewed-on: OpenWF/SpaceNinjaServer#2924
Co-authored-by: Vitruvio <vitruvio@noreply.localhost>
Co-committed-by: Vitruvio <vitruvio@noreply.localhost>
2025-10-21 12:23:12 -07:00
e867123f89 fix: correct multiplier for credit blessing (#2921)
Reviewed-on: OpenWF/SpaceNinjaServer#2921
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-21 00:43:25 -07:00
2322a994c6 feat: rewards for overriden enemy caches (#2919)
Closes #2913

Reviewed-on: OpenWF/SpaceNinjaServer#2919
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-10-21 00:43:15 -07:00
be8e2feae6 fix: exclude SolNode63 from archon hunts (#2918)
Reviewed-on: OpenWF/SpaceNinjaServer#2918
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-21 00:43:03 -07:00
4f8b07322e chore: move int cheats into account section (#2916)
Re #2361

Reviewed-on: OpenWF/SpaceNinjaServer#2916
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-10-21 00:42:56 -07:00
4b3b1969da chore(webui): disable browser autocompletion for datalist inputs (#2915)
It's just unnecessary clutter, especially if you switch languages in the webui

Reviewed-on: OpenWF/SpaceNinjaServer#2915
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-10-20 00:56:56 -07:00
a0ce110e7e chore: dont send messages with completeQuest (#2914)
Re #2754

Reviewed-on: OpenWF/SpaceNinjaServer#2914
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-10-20 00:56:45 -07:00
7fe00da2a4 fix: remove vors prize from questCompletionRewards (#2911)
Because this file overrides the public export, it means The Teacher quest would not be given.

Reviewed-on: OpenWF/SpaceNinjaServer#2911
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-18 23:44:48 -07:00
bac23a8465 fix(webui): use text type for email input (#2910)
We don't need the browser to validate the input because the game accepts emails with nothing before the @ which the browser may not.

Reviewed-on: OpenWF/SpaceNinjaServer#2910
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-18 23:44:39 -07:00
db112ee5ed chore: handle updateQuest request having CompletionDate (#2909)
The client date representation would produce a schema error

Reviewed-on: OpenWF/SpaceNinjaServer#2909
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-18 23:44:28 -07:00
86998b6760 fix: disallow infestation hijack missions in sorties (#2908)
Closes #2907

Reviewed-on: OpenWF/SpaceNinjaServer#2908
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-17 22:38:08 -07:00
be3dd7ab66 chore: use prettier instead of lint:fix for 'npm run fix' (#2906)
e.g., eslint can't fix prettier problems in .json files

Reviewed-on: OpenWF/SpaceNinjaServer#2906
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-17 22:37:59 -07:00
e6fb675e21 chore: update getSkuCatalog for U40 (#2905)
Reviewed-on: OpenWF/SpaceNinjaServer#2905
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-17 22:37:52 -07:00
fb4c42490e fix(webui): use optional chaining operator for maxLevelCap (#2904)
For items that not in itemMap

Reviewed-on: OpenWF/SpaceNinjaServer#2904
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-10-17 22:37:45 -07:00
96a15e25df chore(webui): update German translation (#2903)
Reviewed-on: OpenWF/SpaceNinjaServer#2903
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: Animan8000 <animan8000@noreply.localhost>
Co-committed-by: Animan8000 <animan8000@noreply.localhost>
2025-10-17 08:01:04 -07:00
ff234c9874 chore: update PE+ (#2902)
Reviewed-on: OpenWF/SpaceNinjaServer#2902
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-17 08:00:53 -07:00
7d3915fe05 feat: night of naberus and qtcc flashsales (#2901)
Reviewed-on: OpenWF/SpaceNinjaServer#2901
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-10-16 00:48:33 -07:00
4b3e2dfc62 chore: update typings for bootstrapper for 0.11.13 (#2900)
Reviewed-on: OpenWF/SpaceNinjaServer#2900
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-16 00:48:12 -07:00
737d013655 feat: focus 2.0 (#2898)
Implemented all the ops we handle for focus 3.0 + activating/deactivating upgrades + the pool mechanic

Reviewed-on: OpenWF/SpaceNinjaServer#2898
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-16 00:48:01 -07:00
1f8d437fad chore: fix unlock all focus schools cheat advising visiting navigation (#2899)
This doesn't work to sync inventory on pre-duviri or post-spider versions.

Reviewed-on: OpenWF/SpaceNinjaServer#2899
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-15 01:14:05 -07:00
9263b8f179 chore: forgot to add one removed/obsolete setting to configRemovedOptionsKeys (#2897)
One setting in the config used to have a typo before #291 and the whole thing here is case sensitive anyway, so I added it here as well.

Reviewed-on: OpenWF/SpaceNinjaServer#2897
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: Animan8000 <animan8000@noreply.localhost>
Co-committed-by: Animan8000 <animan8000@noreply.localhost>
2025-10-15 01:13:53 -07:00
875f4b9fa4 chore: more removed/obsolete settings put into configRemovedOptionsKeys (#2896)
Reviewed-on: OpenWF/SpaceNinjaServer#2896
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: Animan8000 <animan8000@noreply.localhost>
Co-committed-by: Animan8000 <animan8000@noreply.localhost>
2025-10-14 00:24:43 -07:00
fd7ddd9696 chore(webui): fix inconsistent strings in dropdown menu (#2895)
Reviewed-on: OpenWF/SpaceNinjaServer#2895
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: Animan8000 <animan8000@noreply.localhost>
Co-committed-by: Animan8000 <animan8000@noreply.localhost>
2025-10-13 05:21:22 -07:00
065afc0089 chore(webui): move wolf hunt 2025 option up for consistency (#2891)
Reviewed-on: OpenWF/SpaceNinjaServer#2891
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-11 23:35:53 -07:00
c1c14b2068 feat(webui): Vault MiscItems and ShipDecorations (#2889)
Closes #2874
Closes #2875

Reviewed-on: OpenWF/SpaceNinjaServer#2889
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-10-11 23:35:39 -07:00
9e66d22256 feat: wolf hunt 2019 (#2888)
Re #1103

Reviewed-on: OpenWF/SpaceNinjaServer#2888
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-10-11 05:17:08 -07:00
02f0935710 feat(webui): skins (#2816)
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Reviewed-on: OpenWF/SpaceNinjaServer#2816
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-10-09 23:02:09 -07:00
af4c3a93ce feat: forceRemoveItem.php (#2884)
Closes #2883

Reviewed-on: OpenWF/SpaceNinjaServer#2884
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-09 23:02:00 -07:00
4141970530 fix: use correct items for Hallowed Nightmares (#2885)
Reviewed-on: OpenWF/SpaceNinjaServer#2885
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-10-09 23:01:49 -07:00
ca589cb7cf feat: QTCC floofs alerts (#2886)
Re #2842

Reviewed-on: OpenWF/SpaceNinjaServer#2886
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-10-09 23:01:36 -07:00
d6ed22d1ff chore(webui): Void Corruption translations (#2887)
Reviewed-on: OpenWF/SpaceNinjaServer#2887
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-10-09 23:01:20 -07:00
610a432e46 fixup for 2ca895a5f88be3ab43943142e5d559823cac7387 2025-10-09 11:01:49 +02:00
2ca895a5f8 feat: Void Corruption 2025 (#2865)
Reviewed-on: OpenWF/SpaceNinjaServer#2865
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: Slayer55555 <slayer55555@noreply.localhost>
Co-committed-by: Slayer55555 <slayer55555@noreply.localhost>
2025-10-09 00:28:34 -07:00
fd2286c253 fix: send back entire dojo when a room build has been cancelled (#2879)
Fixes #2877

Reviewed-on: OpenWF/SpaceNinjaServer#2879
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-09 00:28:16 -07:00
5a582daa1a chore(webui): debounce guild tech actions (#2882)
Closes #2880

Reviewed-on: OpenWF/SpaceNinjaServer#2882
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-09 00:28:09 -07:00
6a571e5e78 chore: explicitly declare body-parser dependency for pnpm (#2873)
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Reviewed-on: OpenWF/SpaceNinjaServer#2873
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: Mind <1634300602@qq.com>
Co-committed-by: Mind <1634300602@qq.com>
2025-10-07 23:21:17 -07:00
0349c4a32c feat: increase BountyScore for additional stratos emblems earned (#2872)
Closes #2871

Reviewed-on: OpenWF/SpaceNinjaServer#2872
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-07 23:20:48 -07:00
e1563bf298 fix(webui): allow digits in itemtype for add items(raw) (#2870)
For items like `/Lotus/Types/Keys/TacAlertKeyAnniversary2023k`

Reviewed-on: OpenWF/SpaceNinjaServer#2870
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-10-07 23:18:03 -07:00
af6f422fec feat: nemesis mode t / LastNemesisAllySpawnTime (#2869)
Also some import stuff. Closes #2867

Reviewed-on: OpenWF/SpaceNinjaServer#2869
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-07 23:17:50 -07:00
f5c1b83598 fix: only commit 'Missions' on successful completion (#2866)
Fixes SP missions being marked as completed when failing/quitting.

Reviewed-on: OpenWF/SpaceNinjaServer#2866
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-06 22:57:18 -07:00
30f380f37e chore(webui): refresh when creating/deleting a clan in-game (#2864)
So the clan tab shows/hides instantly as expected.

Reviewed-on: OpenWF/SpaceNinjaServer#2864
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-06 22:57:08 -07:00
0f7a85db59 chore(webui): sync account cheats between different webui tabs (#2863)
Reviewed-on: OpenWF/SpaceNinjaServer#2863
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-06 22:56:51 -07:00
43bc12713a chore(webui): force account cheat element state after request is done (#2862)
There's a very slim chance we get an inventory response between sending the setAccountCheat request and receiving the response, in which case the element state would be ingruent.

Reviewed-on: OpenWF/SpaceNinjaServer#2862
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-06 22:56:35 -07:00
6022bf97b5 feat: nemesis mode d (#2860)
Reviewed-on: OpenWF/SpaceNinjaServer#2860
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-06 22:56:20 -07:00
159e151dc0 chore: check for xpBasedLevelCapDisabled in missionInventoryUpdate (#2859)
The bootstrapper provides this field since 0.8.2, so I think this field being absent is now more likely to mean that the patch is not in effect.

Reviewed-on: OpenWF/SpaceNinjaServer#2859
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-06 22:56:04 -07:00
56954260c8 chore(webui): debounce quest updates (#2858)
Closes #2855

Reviewed-on: OpenWF/SpaceNinjaServer#2858
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-06 22:55:46 -07:00
c535044af8 fix: use 1-based indexing for clan ranks for versions before U24 (#2857)
Reviewed-on: OpenWF/SpaceNinjaServer#2857
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-06 22:55:33 -07:00
f5146be129 fix: handle dojo room build request from old versions (#2854)
Reviewed-on: OpenWF/SpaceNinjaServer#2854
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-06 22:54:57 -07:00
d38ec06ed6 fix: disallow creating a clan from an account that's already in one (#2853)
Just a slight precaution to avoid snowballing problems.

Reviewed-on: OpenWF/SpaceNinjaServer#2853
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-06 22:54:35 -07:00
060f65900f fix: transform inventoryResponse.GuildId for older versions (#2852)
Reviewed-on: OpenWF/SpaceNinjaServer#2852
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-06 22:54:22 -07:00
66d3057d40 chore: fix typo 2025-10-06 08:33:44 +02:00
b14a5925df fix: setGuildMotd response for U29.3.1 (#2851)
Unsure which version introduced long descriptions exactly, but it surely wasn't this one. :^)

Reviewed-on: OpenWF/SpaceNinjaServer#2851
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-05 23:25:08 -07:00
9da47c406a fix: put CompletionTime of initial clan hall in the past (#2850)
For old versions, TimeRemaining of 0 would cause this to show in yellow, we need it to be negative.

Reviewed-on: OpenWF/SpaceNinjaServer#2850
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-05 23:24:59 -07:00
09065bdb4e chore: let webui know when client called updateQuest (#2849)
Reviewed-on: OpenWF/SpaceNinjaServer#2849
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-05 23:24:49 -07:00
8f04fc5fdf fix: default quest progress c to -1 (#2848)
Fixes #2846

Reviewed-on: OpenWF/SpaceNinjaServer#2848
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-05 23:24:38 -07:00
230ee5f638 fix: anniversary mission inbox messages showing unresolved |LOTUS_NAME| (#2845)
Reviewed-on: OpenWF/SpaceNinjaServer#2845
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-05 23:24:28 -07:00
21db6ce265 chore(webui): update uk & ru (#2844)
Reviewed-on: OpenWF/SpaceNinjaServer#2844
Co-authored-by: LoseFace <loseface@noreply.localhost>
Co-committed-by: LoseFace <loseface@noreply.localhost>
2025-10-05 23:24:14 -07:00
1ecf53c96b chore: don't add 'alwaysAvailable' skins with unlockAllSkins (#2843)
Reviewed-on: OpenWF/SpaceNinjaServer#2843
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-05 05:56:58 -07:00
e67ef63b77 fix: avoid using assassination node for an earlier sortie mission (#2838)
Closes #2837

Reviewed-on: OpenWF/SpaceNinjaServer#2838
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-04 04:18:21 -07:00
5772ebe746 feat(import): boosters (#2836)
Reviewed-on: OpenWF/SpaceNinjaServer#2836
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-03 06:46:07 -07:00
0136e4d152 chore(webui): clarify /sync command goes into chat (#2835)
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Reviewed-on: OpenWF/SpaceNinjaServer#2835
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: LoseFace <loseface@noreply.localhost>
Co-committed-by: LoseFace <loseface@noreply.localhost>
2025-10-03 06:45:57 -07:00
8b3ee4b4f5 chore: allow sortie image randomisation for most tilesets (#2834)
This should reduce the impact while we investigate #2833

Reviewed-on: OpenWF/SpaceNinjaServer#2834
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-02 05:27:56 -07:00
6e8800f048 chore(webui): fix typos (#2832)
also updated author credits

Reviewed-on: OpenWF/SpaceNinjaServer#2832
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: Animan8000 <animan8000@noreply.localhost>
Co-committed-by: Animan8000 <animan8000@noreply.localhost>
2025-10-01 01:23:08 -07:00
d65a667acd fix: ensure sorties show 'correct' image for corpus ice planet tileset (#2831)
Reviewed-on: OpenWF/SpaceNinjaServer#2831
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-30 00:00:13 -07:00
c6a3e86d2b fix(webui): invoke giveKeyChainStageTriggered for new stage (#2830)
Previously, this caused the old stage to just be reinitiated so we never went backwards.

Closes #2829

Reviewed-on: OpenWF/SpaceNinjaServer#2830
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-29 23:59:35 -07:00
a8e41c95e7 chore: move createNewEventMessages from inboxService to inboxController (#2828)
This function wasn't used anywhere else and caused a recursive include in inboxService.

Reviewed-on: OpenWF/SpaceNinjaServer#2828
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-29 23:59:26 -07:00
9426359370 feat: Nights of Naberus (#2817)
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Reviewed-on: OpenWF/SpaceNinjaServer#2817
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: Gian <gianplu55@gmail.com>
Co-committed-by: Gian <gianplu55@gmail.com>
2025-09-29 23:59:17 -07:00
e5247700df fix: use safe navigation to check for replay in giveKeyChainMessage (#2826)
Reviewed-on: OpenWF/SpaceNinjaServer#2826
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-29 02:00:32 -07:00
1c3f1e2276 feat: DeleteAllReadNonCin (#2824)
Closes #2822

Reviewed-on: OpenWF/SpaceNinjaServer#2824
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-29 02:00:17 -07:00
7710e7c13f feat: inbox message for relics cracked during an unfinished mission (#2823)
Closes #2821

Reviewed-on: OpenWF/SpaceNinjaServer#2823
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-29 02:00:05 -07:00
a64c5ea3c1 chore(webui): remove administratorNames entry when deleting account (#2820)
Closes #2819

Reviewed-on: OpenWF/SpaceNinjaServer#2820
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-28 01:10:45 -07:00
17e1eb86dd fix(webui): don't send off 2 addXp requests at once (#2815)
One would likely fail due to Mongoose's array versioning

Closes #2811

Reviewed-on: OpenWF/SpaceNinjaServer#2815
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-27 03:23:53 -07:00
de9dfb3d71 fix: show endless relic rewards in EOM screen (#2813)
Closes #2812

Reviewed-on: OpenWF/SpaceNinjaServer#2813
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-26 04:42:08 -07:00
fc38f818dd feat: nemesis henchmen kills multiplier cheat (#2806)
Co-authored-by: AlexisinGit <136088944+AlexisinGit@users.noreply.github.com>
Reviewed-on: OpenWF/SpaceNinjaServer#2806
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AlexisinGit <alexisingit@noreply.localhost>
Co-committed-by: AlexisinGit <alexisingit@noreply.localhost>
2025-09-26 04:41:54 -07:00
e76f08db89 chore(webui): update to Spanish translation (#2814)
Reviewed-on: OpenWF/SpaceNinjaServer#2814
Co-authored-by: hxedcl <hxedcl@noreply.localhost>
Co-committed-by: hxedcl <hxedcl@noreply.localhost>
2025-09-25 10:51:44 -07:00
7bcb5f21ce chore(webui): unify Invigoration code (#2809)
Reviewed-on: OpenWF/SpaceNinjaServer#2809
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-09-25 10:21:04 -07:00
3641d63f6f chore(webui): update to Spanish translation (#2810)
Reviewed-on: OpenWF/SpaceNinjaServer#2810
Co-authored-by: hxedcl <hxedcl@noreply.localhost>
Co-committed-by: hxedcl <hxedcl@noreply.localhost>
2025-09-24 23:49:10 -07:00
71c4835a69 chore(webui): unify Boosters code (#2808)
Reviewed-on: OpenWF/SpaceNinjaServer#2808
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-09-24 08:41:35 -07:00
86a63ace41 chore(webui): adjust checks for guild view requests (#2807)
Reviewed-on: OpenWF/SpaceNinjaServer#2807
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-09-24 08:41:04 -07:00
32c95b6715 fix: conditional in giveKeyChainItem (#2804)
Using safe navigation now and inverted the condition because i would be false when we have to give items, not true.

Closes #2803

Reviewed-on: OpenWF/SpaceNinjaServer#2804
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-22 04:42:54 -07:00
6f8b14fb2d chore(webui): stick from on top in acquire cards (#2802)
Also use `d-none` instead `style="display: none;"` in modular cards

Reviewed-on: OpenWF/SpaceNinjaServer#2802
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-09-22 04:42:46 -07:00
3d8aa60838 feat(webui): unlock level cap (#2799)
Closes #2620

Reviewed-on: OpenWF/SpaceNinjaServer#2799
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-09-21 02:53:16 -07:00
87da94658d fix: correct checks for quest replay (#2798)
Closes #2797

Reviewed-on: OpenWF/SpaceNinjaServer#2798
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-09-21 02:53:05 -07:00
05fbefa7f4 fix: faithful response to startCollectibleEntry (#2796)
Closes #2795

Reviewed-on: OpenWF/SpaceNinjaServer#2796
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-20 00:29:15 -07:00
a2abf6db8f fix(webui): get correct element for doAcquireCountItems (#2794)
Closes #2793

Reviewed-on: OpenWF/SpaceNinjaServer#2794
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-09-20 00:29:06 -07:00
64a1c8b276 chore(webui): update uk & ru (#2784)
Reviewed-on: OpenWF/SpaceNinjaServer#2784
Co-authored-by: LoseFace <loseface@noreply.localhost>
Co-committed-by: LoseFace <loseface@noreply.localhost>
2025-09-19 04:11:44 -07:00
4fa07a1319 chore(webui): give the user higher quantity of ShipDecorations (#2791)
100 is way too low. 999999 should be enough (was also the same number IIRC from the previous ShipDecorations cheat) for everything probably

Reviewed-on: OpenWF/SpaceNinjaServer#2791
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: Animan8000 <animan8000@noreply.localhost>
Co-committed-by: Animan8000 <animan8000@noreply.localhost>
2025-09-18 01:12:44 -07:00
a3cc7d9f92 fix: give host permission to highest clan ranks (#2790)
Re #2088, I must've assumed 16351 included this permission.

Reviewed-on: OpenWF/SpaceNinjaServer#2790
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-18 01:12:35 -07:00
c47c60fdcc fix: determine armor or shield based on sortie boss faction (#2787)
Closes #2785

Reviewed-on: OpenWF/SpaceNinjaServer#2787
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-18 01:12:25 -07:00
367455baaa chore: update German newsfeed worldState message (#2788)
I found "Trete" might fit slightly better due to sounding more natural than "Tritt". Also sounds slightly more welcoming this way

Reviewed-on: OpenWF/SpaceNinjaServer#2788
Co-authored-by: Animan8000 <animan8000@noreply.localhost>
Co-committed-by: Animan8000 <animan8000@noreply.localhost>
2025-09-17 08:32:27 -07:00
6c2b7a61e2 chore(webui): exclude always available items from datalist (#2783)
Reviewed-on: OpenWF/SpaceNinjaServer#2783
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-09-16 23:23:59 -07:00
6a6683fb25 chore(webui): stalker loc (#2781)
Reviewed-on: OpenWF/SpaceNinjaServer#2781
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-09-15 10:40:29 -07:00
e3b6accb5d feat(webui): ship decorations (#2780)
Re #2361

Reviewed-on: OpenWF/SpaceNinjaServer#2780
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-09-15 10:40:21 -07:00
7e437d75bf feat(webui): flavour Items (#2779)
Re #2361

Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Reviewed-on: OpenWF/SpaceNinjaServer#2779
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-09-14 23:31:43 -07:00
62570177b6 fix: handle quest replay (#2778)
Closes #2496

Reviewed-on: OpenWF/SpaceNinjaServer#2778
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-09-14 23:31:35 -07:00
d2aff211c6 fix: show conservation standing in progress screen, missing reward multiplications (#2776)
Closes #2774

Reviewed-on: OpenWF/SpaceNinjaServer#2776
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-13 23:50:08 -07:00
791ae389d8 fix: correct Activation/Expiry date for Ghoul Emergence (#2777)
Closes #2775

Reviewed-on: OpenWF/SpaceNinjaServer#2777
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-09-13 23:50:00 -07:00
d027e7f26e chore(webui): update fr (#2773)
Reviewed-on: OpenWF/SpaceNinjaServer#2773
Co-authored-by: Vitruvio <vitruvio@noreply.localhost>
Co-committed-by: Vitruvio <vitruvio@noreply.localhost>
2025-09-12 06:25:50 -07:00
cd6ce61b80 feat: conservation standing reward (#2772)
Closes #2763

Reviewed-on: OpenWF/SpaceNinjaServer#2772
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-12 00:41:05 -07:00
a5be29159f fix: handle lab conquest keeping RewardInfo from previous missions (#2771)
Closes #2768

Reviewed-on: OpenWF/SpaceNinjaServer#2771
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-12 00:40:48 -07:00
f099b64ef4 fix(webui): correct check for guild id (#2770)
Reviewed-on: OpenWF/SpaceNinjaServer#2770
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-09-11 01:10:32 -07:00
c4f348c252 chore: update PE+ (#2769)
Some more deprecations

Reviewed-on: OpenWF/SpaceNinjaServer#2769
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-11 01:10:24 -07:00
0d388b4b0f feat: support websocket connections from game client (#2735)
For bootstrapper v0.11.11, out now.

Reviewed-on: OpenWF/SpaceNinjaServer#2735
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-10 00:00:09 -07:00
d64531f4b2 feat(webui): guild view (#2752)
Also moves guild-specific cheats to a switch for each guild
Closes #1403

Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Reviewed-on: OpenWF/SpaceNinjaServer#2752
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-09-09 23:55:10 -07:00
01b8f7acf3 chore(webui): better locale support for relics (#2764)
Reviewed-on: OpenWF/SpaceNinjaServer#2764
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-09-09 23:55:01 -07:00
8a7db2cd85 chore: update PE+ (#2765)
Some things were deprecated in it

Reviewed-on: OpenWF/SpaceNinjaServer#2765
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-09 23:54:46 -07:00
5a9415ae0c feat: bindAddress (#2766)
so people can limit the server to only be reachable via 127.0.0.1 etc

Reviewed-on: OpenWF/SpaceNinjaServer#2766
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-09 23:54:33 -07:00
39f898cd30 chore: use inlineSourceMap instead of sourceMap (#2767)
Windows filesystem is pretty slow, so avoiding creating an extra file per file makes `npm run build` ~20% faster (~1600ms to ~1300ms on my machine)

Reviewed-on: OpenWF/SpaceNinjaServer#2767
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-09 23:54:26 -07:00
9c55a8a4aa chore: enable no-deprecated warning (#2762)
Reviewed-on: OpenWF/SpaceNinjaServer#2762
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-08 20:43:31 -07:00
253ae09f24 fix(webui): use excludeFromCodex to detect arcane imposters (#2761)
Closes #2760

Reviewed-on: OpenWF/SpaceNinjaServer#2761
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-08 20:43:15 -07:00
703e9007b0 fix: invasion reward message sender name (#2759)
Reviewed-on: OpenWF/SpaceNinjaServer#2759
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-08 20:43:02 -07:00
3e555b1753 feat: purchase additional conclave loadout slots (#2758)
Closes #2756. Also just in general simplified the logic around purchasing loadout slots.

Reviewed-on: OpenWF/SpaceNinjaServer#2758
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-08 20:42:53 -07:00
1066b4a983 chore(webui): quote display name for administrator requirement (#2753)
Reviewed-on: OpenWF/SpaceNinjaServer#2753
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-07 18:58:16 -07:00
b9a2cea862 chore(webui): update to Spanish translation (#2757)
Reviewed-on: OpenWF/SpaceNinjaServer#2757
Co-authored-by: hxedcl <hxedcl@noreply.localhost>
Co-committed-by: hxedcl <hxedcl@noreply.localhost>
2025-09-07 18:58:08 -07:00
0342f52359 chore(webui): inform users how to resync their client for certain cheats (#2750)
Reviewed-on: OpenWF/SpaceNinjaServer#2750
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-04 23:42:52 -07:00
ea9012bd56 chore: use raw running in update and start script if node is new enough (#2749)
Reviewed-on: OpenWF/SpaceNinjaServer#2749
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-04 23:42:45 -07:00
13400b6d83 chore(webui): update to Spanish translation (#2751)
Reviewed-on: OpenWF/SpaceNinjaServer#2751
Co-authored-by: hxedcl <hxedcl@noreply.localhost>
Co-committed-by: hxedcl <hxedcl@noreply.localhost>
2025-09-04 23:36:10 -07:00
8d57eda9d2 chore: make use of raw running when dev script is used with newer node (#2748)
Reviewed-on: OpenWF/SpaceNinjaServer#2748
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-03 22:46:03 -07:00
6b66cb495b chore: handle 'npm run raw' being used on node versions below 22.7.0 (#2747)
Reviewed-on: OpenWF/SpaceNinjaServer#2747
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-03 22:45:50 -07:00
f4f7ed00d1 chore: add consistent options for IRC, HUB, & NRS addresses (#2746)
Reviewed-on: OpenWF/SpaceNinjaServer#2746
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-03 22:45:22 -07:00
18556cb2f5 chore: rework AGENTS.md into a more generic CONTRIBUTING.md (#2745)
This should be useful for humans as well :)

Reviewed-on: OpenWF/SpaceNinjaServer#2745
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-03 22:45:02 -07:00
648af9ae18 chore(readme): note skipTutorial (#2744)
Reviewed-on: OpenWF/SpaceNinjaServer#2744
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-03 22:44:34 -07:00
e16da9da44 chore(readme): remove filter from issues link (#2743)
Reviewed-on: OpenWF/SpaceNinjaServer#2743
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-03 22:44:25 -07:00
4d8dbd99aa chore: update package-lock.json 2025-09-03 11:41:52 +02:00
0a3f9549a9 fix: include currency changes in purchase response (#2740)
Closes #2739

Reviewed-on: OpenWF/SpaceNinjaServer#2740
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-02 20:22:56 -07:00
2cfb21b98e chore: buttonify unlockAllScans, unlockAllShipFeatures, unlockAllCapturaScenes (#2738)
Re #2361. Mostly done via ChatGPT Codex.

Reviewed-on: OpenWF/SpaceNinjaServer#2738
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-02 20:22:47 -07:00
3fedc701f1 fix(webui): properly deselect active focus school so unlocking is free (#2737)
Reviewed-on: OpenWF/SpaceNinjaServer#2737
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-01 20:30:11 -07:00
ed596aa3f3 chore: respond to hub request with reflexive address (#2736)
Reviewed-on: OpenWF/SpaceNinjaServer#2736
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-01 20:30:03 -07:00
e2349b361e chore: add noDojoDecoBuildStage to default config (#2734)
Reviewed-on: OpenWF/SpaceNinjaServer#2734
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-08-31 18:50:08 -07:00
9221178522 fix(webui): switching routes resets active tab for "add items" (#2733)
Reviewed-on: OpenWF/SpaceNinjaServer#2733
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-31 18:49:55 -07:00
9a5c2ab4a4 chore: remove NRS config (#2732)
Reviewed-on: OpenWF/SpaceNinjaServer#2732
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-31 18:49:48 -07:00
d4c477769a fix: don't attempt to subtract a cost of 0 for unlocking focus school (#2731)
Closes #2730

Reviewed-on: OpenWF/SpaceNinjaServer#2731
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-31 18:47:14 -07:00
6de9f0dcdb fixup: remove unlockAllSimarisResearchEntries from config-vanilla.json 2025-08-31 18:35:24 +02:00
9662da00de chore(webui): update uk & ru (#2728)
Reviewed-on: OpenWF/SpaceNinjaServer#2728
Co-authored-by: LoseFace <loseface@noreply.localhost>
Co-committed-by: LoseFace <loseface@noreply.localhost>
2025-08-30 19:35:06 -07:00
662d824369 chore: move unlockAllSimarisResearchEntries to a per-account button (#2726)
Closes #2725. Re #2361.

Reviewed-on: OpenWF/SpaceNinjaServer#2726
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-30 19:34:55 -07:00
a0bac12e95 fix: put vault medallion into correct place (#2723)
Re #2719

Reviewed-on: OpenWF/SpaceNinjaServer#2723
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-08-30 19:34:30 -07:00
e98cb2ec24 feat(webui): add item by ItemType (#2704)
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Reviewed-on: OpenWF/SpaceNinjaServer#2704
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-08-30 19:34:22 -07:00
b5c6c3e485 fix: don't push thermal fractures event if it's not activated yet (#2722)
Closes #2721

Reviewed-on: OpenWF/SpaceNinjaServer#2722
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-29 17:37:34 -07:00
fa65ba3f25 chore: correct breaks in Thermia Fractures cycle (#2724)
Reviewed-on: OpenWF/SpaceNinjaServer#2724
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-08-29 17:37:23 -07:00
0c54c064eb fix: Pathos Clamps Upon Isleweave Clearing (#2718)
https://wiki.warframe.com/w/Isleweaver#Normal

![image.png](/attachments/ad46e7d2-e28c-47cf-8b13-237a29bc7cc6)

Co-authored-by: AlexisinGit <136088944+AlexisinGit@users.noreply.github.com>
Reviewed-on: OpenWF/SpaceNinjaServer#2718
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AlexisinGit <alexisingit@noreply.localhost>
Co-committed-by: AlexisinGit <alexisingit@noreply.localhost>
2025-08-29 17:36:42 -07:00
b4e789bf0d chore: move unlock all profit taker stages to a per-account button (#2717)
Re #2361, generated with OpenAI Codex.

Reviewed-on: OpenWF/SpaceNinjaServer#2717
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-29 17:36:31 -07:00
9add016d7b feat: finishInvasionsInOneMission (#2715)
#2646

Co-authored-by: AlexisinGit <136088944+AlexisinGit@users.noreply.github.com>
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Reviewed-on: OpenWF/SpaceNinjaServer#2715
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AlexisinGit <alexisingit@noreply.localhost>
Co-committed-by: AlexisinGit <alexisingit@noreply.localhost>
2025-08-29 17:36:11 -07:00
a2171c80a5 fix: CrewShipFusion bug with SubroutineIndex (#2714)
Crewship fusion didn't agree with player's choice.

Expected results: ![image.png](/attachments/a61c96c7-1624-4dd4-9e4c-c6199d5af95a)

What I actually get: ![image.png](/attachments/cf58aed4-18d9-4d97-8894-6c53762f366d)

```
export interface ICrewShipComponentFingerprint extends IInnateDamageFingerprint {
    SubroutineIndex?: number;
}
```

The interface already demand SubroutineIndex to exist, so it would be unnecessary to check.

Meanwhile, inferiorFingerprint.SubroutineIndex could be 0, 1, 2... and might be handled incorrectly as true/ false.

Co-authored-by: AlexisinGit <136088944+AlexisinGit@users.noreply.github.com>
Reviewed-on: OpenWF/SpaceNinjaServer#2714
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AlexisinGit <alexisingit@noreply.localhost>
Co-committed-by: AlexisinGit <alexisingit@noreply.localhost>
2025-08-28 05:50:28 -07:00
5a2fa2c2c3 chore: move a few more cheat toggles to be per-account (#2713)
Re #2361

Reviewed-on: OpenWF/SpaceNinjaServer#2713
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-28 05:50:21 -07:00
4b2b184b8f feat: additional operator appearance slots (#2712)
Closes #2710

Reviewed-on: OpenWF/SpaceNinjaServer#2712
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-28 05:50:13 -07:00
dc401de1e9 chore: use raw running in docker image (#2711)
We can rely on having up-to-date Node.js here, and reducing the size by like ~55 MiB seems decent.

Reviewed-on: OpenWF/SpaceNinjaServer#2711
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-28 05:50:06 -07:00
1439fdc083 chore(vscode): set typescript.preferences.preferTypeOnlyAutoImports 2025-08-27 23:44:12 +02:00
6771a129f5 fixup: remove fix-imports 2025-08-27 22:41:22 +02:00
f13de810e5 fixup: use eslint:fix instead of prettier in fix script 2025-08-27 22:33:55 +02:00
c52f7dcedc fixup: remove paths from tsconfig 2025-08-27 22:31:22 +02:00
0bf142ed50 fix: view profile on U39.1+ (#2709)
Reviewed-on: OpenWF/SpaceNinjaServer#2709
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-27 13:06:36 -07:00
0d791ad145 fix: incorrect ordering of relicQualitySuffixes (#2708)
Reviewed-on: OpenWF/SpaceNinjaServer#2708
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-27 13:06:28 -07:00
5396eefe75 chore: move dependencies only required for build to 'optional' (#2707)
This is a bit of a misnomer because npm installs them just like dev deps unless an appropriate --omit flag is added. Anyway, these deps are ~55 MB, so being able to omit that chunk for raw running might be good.

Reviewed-on: OpenWF/SpaceNinjaServer#2707
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-27 13:06:18 -07:00
a9a197b005 chore: add some missing entries to .dockerignore (#2706)
Reviewed-on: OpenWF/SpaceNinjaServer#2706
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-27 13:06:01 -07:00
1ade801e7c fix: 5 disinfection progress per mission until 95 for Technocyte Coda (#2705)
Co-authored-by: AlexisinGit <136088944+AlexisinGit@users.noreply.github.com>
Reviewed-on: OpenWF/SpaceNinjaServer#2705
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AlexisinGit <alexisingit@noreply.localhost>
Co-committed-by: AlexisinGit <alexisingit@noreply.localhost>
2025-08-27 13:05:49 -07:00
c9cc1fa089 chore: define node version constraints (#2703)
`>=20.18.1` is required for `npm i && npm run build && npm run start` to succeed.
`npm run raw` would require `>=22.7.0`.

Reviewed-on: OpenWF/SpaceNinjaServer#2703
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-27 01:30:36 -07:00
287acab892 chore: abort update and start scripts when .git folder is missing (#2702)
In that case, updating is obviously not possible.

Reviewed-on: OpenWF/SpaceNinjaServer#2702
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-27 01:30:19 -07:00
30398021b3 feat: claim all recipes (#2700)
Closes #2699

tried to make it a cleaner diff, but this is the best I could do

Reviewed-on: OpenWF/SpaceNinjaServer#2700
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-27 01:29:46 -07:00
15578b04d2 chore: move baro cheats to worldstate section (#2695)
Reviewed-on: OpenWF/SpaceNinjaServer#2695
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-08-27 01:29:38 -07:00
9d034824f7 chore: npm update 2025-08-26 23:25:20 +02:00
8b8d66ab2e chore: add worldState endpoint for U39.1 (#2701)
U39.1 wants world state from `/worldState.php` not from `/dynamic/worldState.php` as before.
Before bootstrapper update you need to replace `/static/data/buildConfig.json` to match latest version.

Reviewed-on: OpenWF/SpaceNinjaServer#2701
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-08-26 13:44:44 -07:00
78b8cf4c77 fix: bump DuviriInfo.NumCompletions when completing one of its modes (#2698)
Closes #2697

Reviewed-on: OpenWF/SpaceNinjaServer#2698
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-26 04:29:14 -07:00
1ead04ddc1 chore: switch to esm, support raw running via node (#2696)
Using ESM so the `import` syntax we use is actually valid without compilation. Now, Node can run the TypeScript code directly, albeit without typechecking and requiring use of an experimental feature.

Reviewed-on: OpenWF/SpaceNinjaServer#2696
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-26 04:29:06 -07:00
c97c22b434 chore: use relative imports with .ts (#2694)
Reviewed-on: OpenWF/SpaceNinjaServer#2694
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-25 13:37:14 -07:00
d7a93463c0 chore: enforce consistent-type-imports (#2693)
Reviewed-on: OpenWF/SpaceNinjaServer#2693
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-24 21:41:20 -07:00
f6b73e58da chore(webui): swap account and server cheat cards (#2691)
Reviewed-on: OpenWF/SpaceNinjaServer#2691
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-24 15:59:33 -07:00
28b8fb3e78 chore: move skipClanKeyCrafting back to config.json (#2690)
Reviewed-on: OpenWF/SpaceNinjaServer#2690
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-24 15:36:59 -07:00
895e76b45e chore: automatically remove deleted options from config.json (#2689)
Reviewed-on: OpenWF/SpaceNinjaServer#2689
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-24 15:30:31 -07:00
71d0c140ae chore: move more cheats to account section (#2680)
Re #2361

Reviewed-on: OpenWF/SpaceNinjaServer#2680
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-08-24 15:16:42 -07:00
f06a3b8187 fix: use netracell search pulse only when container item was picked up (#2687)
Reviewed-on: OpenWF/SpaceNinjaServer#2687
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-24 15:16:06 -07:00
ffcbbb480b fix(import): handle ship features being in inventory (#2688)
Closes #2686

Reviewed-on: OpenWF/SpaceNinjaServer#2688
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-24 15:15:50 -07:00
dfd1fb834b chore: avoid using client oid representation in database (#2685)
Reviewed-on: OpenWF/SpaceNinjaServer#2685
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-23 12:56:41 -07:00
2c43d897c0 fix(import): handle IOrbiterClient.ShipInterior being undefined (#2681)
Reviewed-on: OpenWF/SpaceNinjaServer#2681
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-22 23:17:53 -07:00
217eb1f61b chore(webui): update to Spanish translation (#2682)
Reviewed-on: OpenWF/SpaceNinjaServer#2682
Co-authored-by: hxedcl <hxedcl@noreply.localhost>
Co-committed-by: hxedcl <hxedcl@noreply.localhost>
2025-08-22 20:03:30 -07:00
5e1ff64cca fix: move getDialogue preconditions into the function itself (#2679)
Closes #2678

Reviewed-on: OpenWF/SpaceNinjaServer#2679
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-22 11:46:04 -07:00
9c232bfc1f chore: skip cetus intro job with complete all missions (#2677)
Closes #2647

Reviewed-on: OpenWF/SpaceNinjaServer#2677
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-22 11:45:56 -07:00
9e73fc7fb1 chore: update PE+ (#2676)
Closes #2673

Reviewed-on: OpenWF/SpaceNinjaServer#2676
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-22 11:45:47 -07:00
03b8b610db feat: consume search pulse for doing netracells (#2675)
Reviewed-on: OpenWF/SpaceNinjaServer#2675
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-22 11:45:39 -07:00
4b46938dab chore(webui): update uk (#2674)
Reviewed-on: OpenWF/SpaceNinjaServer#2674
Co-authored-by: LoseFace <loseface@noreply.localhost>
Co-committed-by: LoseFace <loseface@noreply.localhost>
2025-08-21 12:21:09 -07:00
6230f52f27 fix: incorrect log message 2025-08-21 20:58:57 +02:00
460cb20af7 fix: the circuit normal rewards rotation (#2672)
Reviewed-on: OpenWF/SpaceNinjaServer#2672
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: BanLanGen <banlangen@noreply.localhost>
Co-committed-by: BanLanGen <banlangen@noreply.localhost>
2025-08-21 11:27:36 -07:00
1e4007f6da fix(import): accept loadouts without GEAR field (#2671)
New inventories don't have that field

Reviewed-on: OpenWF/SpaceNinjaServer#2671
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-08-21 11:25:50 -07:00
7dc44e81ec feat: thermia fractures (#2670)
Re #1103

Reviewed-on: OpenWF/SpaceNinjaServer#2670
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-08-21 11:07:31 -07:00
bfc4048721 chore(webui): update to Spanish translation (#2669)
Reviewed-on: OpenWF/SpaceNinjaServer#2669
Co-authored-by: hxedcl <hxedcl@noreply.localhost>
Co-committed-by: hxedcl <hxedcl@noreply.localhost>
2025-08-20 00:37:59 -07:00
7174848588 chore: non-fatally handle missing LevelKey info (#2667)
Closes #2665

Reviewed-on: OpenWF/SpaceNinjaServer#2667
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-18 09:34:38 -07:00
a5b667c331 feat: protovyre armor challenge rewards (#2666)
Closes #2485

Reviewed-on: OpenWF/SpaceNinjaServer#2666
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-18 09:34:31 -07:00
9b6abff2be feat: dominus aureus rewards when operation eight claw is active (#2663)
Closes #2660

Reviewed-on: OpenWF/SpaceNinjaServer#2663
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-18 09:34:24 -07:00
7c7f37e46b chore(webui): update to Spanish translation (#2662)
Reviewed-on: OpenWF/SpaceNinjaServer#2662
Co-authored-by: hxedcl <hxedcl@noreply.localhost>
Co-committed-by: hxedcl <hxedcl@noreply.localhost>
2025-08-18 09:34:17 -07:00
52a560bef2 chore(webui): update uk & ru (#2664)
Reviewed-on: OpenWF/SpaceNinjaServer#2664
Co-authored-by: LoseFace <loseface@noreply.localhost>
Co-committed-by: LoseFace <loseface@noreply.localhost>
2025-08-18 09:31:58 -07:00
369794dcdb chore(webui): update zh (#2668)
Reviewed-on: OpenWF/SpaceNinjaServer#2668
Co-authored-by: BanLanGen <banlangen@noreply.localhost>
Co-committed-by: BanLanGen <banlangen@noreply.localhost>
2025-08-18 09:31:51 -07:00
345 changed files with 10534 additions and 5215 deletions

View File

@ -3,3 +3,6 @@
Dockerfile*
.*
docker-data/
node_modules/
static/data/
logs/

View File

@ -13,6 +13,7 @@
"node": true
},
"rules": {
"@typescript-eslint/consistent-type-imports": "error",
"@typescript-eslint/explicit-function-return-type": "error",
"@typescript-eslint/restrict-template-expressions": "error",
"@typescript-eslint/restrict-plus-operands": "error",
@ -30,7 +31,8 @@
"no-mixed-spaces-and-tabs": "error",
"@typescript-eslint/require-await": "error",
"import/no-named-as-default-member": "off",
"import/no-cycle": "warn"
"import/no-cycle": "warn",
"@typescript-eslint/no-deprecated": "warn"
},
"parser": "@typescript-eslint/parser",
"parserOptions": {

View File

@ -12,14 +12,13 @@ jobs:
- name: Setup Node.js environment
uses: actions/setup-node@v4.0.2
with:
node-version: ">=20.6.0"
node-version: ">=20.18.1"
- run: npm ci
- run: cp config-vanilla.json config.json
- run: npm run verify
- run: npm run lint:ci
- run: npm run prettier
- run: npm run update-translations
- run: npm run fix-imports
- name: Fail if there are uncommitted changes
run: |
if [[ -n "$(git status --porcelain)" ]]; then

View File

@ -4,9 +4,9 @@ on:
branches:
- main
jobs:
docker-amd64:
docker:
if: github.repository == 'OpenWF/SpaceNinjaServer'
runs-on: amd64
runs-on: ubuntu-latest
steps:
- name: Set up Docker buildx
uses: docker/setup-buildx-action@v3
@ -18,27 +18,10 @@ jobs:
- name: Build and push
uses: docker/build-push-action@v6
with:
platforms: linux/amd64
platforms: linux/arm64,linux/amd64
push: true
tags: |
openwf/spaceninjaserver:latest
openwf/spaceninjaserver:${{ github.sha }}
docker-arm64:
if: github.repository == 'OpenWF/SpaceNinjaServer'
runs-on: arm64
steps:
- name: Set up Docker buildx
uses: docker/setup-buildx-action@v3
- name: Log in to container registry
uses: docker/login-action@v3
with:
username: openwf
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v6
with:
platforms: linux/arm64
push: true
tags: |
openwf/spaceninjaserver:latest-arm64
openwf/spaceninjaserver:${{ github.sha }}
openwf/spaceninjaserver:${{ github.sha }}-arm64

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"typescript.preferences.preferTypeOnlyAutoImports": true
}

View File

@ -1,17 +0,0 @@
## In General
### Prerequisites
Use `npm i` or `npm ci` to install all dependencies.
### Testing
Use `npm run verify` to verify that your changes pass TypeScript's checks.
### Formatting
Use `npm run prettier` to ensure your formatting matches the expected format. Failing to do so will cause CI failure.
## WebUI Specific
The translation system is designed around additions being made to `static/webui/translations/en.js`. They are copied over for translation via `npm run update-translations`. DO NOT produce non-English strings; we want them to be translated by humans who can understand the full context.

19
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,19 @@
## In General
### Prerequisites
Use `npm i` or `npm ci` to install all dependencies, including dev dependencies.
## Development Process
Auto reloading is supported for server and WebUI development. Simply use `npm run dev` or `npm run dev:bun` to start the server and edit away.
### Testing
Before submitting a PR:
- Use `npm run verify` to verify that the code is type-safe.
- Use `npm run fix` to fix formatting issues as well as be informed of any unfixable issues. Avoid introducing new warnings.
## WebUI Specific
The translation system is designed around additions being made to `static/webui/translations/en.js`. They are copied over for translation via `npm run update-translations`. DO NOT provide translations generated by AI or other automated tools.

View File

@ -5,8 +5,7 @@ RUN apk add --no-cache bash jq
COPY . /app
WORKDIR /app
RUN npm i --omit=dev
RUN npm run build
RUN npm i --omit=dev --omit=optional
RUN date '+%d %B %Y' > BUILD_DATE
ENTRYPOINT ["/app/docker-entrypoint.sh"]

View File

@ -6,18 +6,21 @@ More information for the moment here: [https://discord.gg/PNNZ3asUuY](https://di
This project is in active development at <https://onlyg.it/OpenWF/SpaceNinjaServer>.
To get an idea of what functionality you can expect to be missing [have a look through the issues](https://onlyg.it/OpenWF/SpaceNinjaServer/issues?q=&type=all&state=open&labels=-4%2C-10&milestone=0&assignee=0&poster=). However, many things have been implemented and *should* work as expected. Please open an issue for anything where that's not the case and/or the server is reporting errors.
To get an idea of what functionality you can expect to be missing [have a look through the issues](https://onlyg.it/OpenWF/SpaceNinjaServer/issues). However, many things have been implemented and *should* work as expected. Please open an issue for anything where that's not the case and/or the server is reporting errors.
## config.json
SpaceNinjaServer requires a `config.json`. To set it up, you can copy the [config-vanilla.json](config-vanilla.json), which has most cheats disabled.
- `skipTutorial` affects only newly created accounts, so you may wish to change it before logging in for the first time.
- `logger.level` can be `fatal`, `error`, `warn`, `info`, `http`, `debug`, or `trace`.
- `myIrcAddresses` can be used to point to an IRC server. If not provided, defaults to `[ myAddress ]`.
- `ircExecutable` can be provided with a relative path to an EXE which will be ran as a child process of SpaceNinjaServer.
- `ircAddress`, `hubAddress`, and `nrsAddress` can be provided if these secondary servers are on a different machine.
- `worldState.eidolonOverride` can be set to `day` or `night` to lock the time to day/fass and night/vome on Plains of Eidolon/Cambion Drift.
- `worldState.vallisOverride` can be set to `warm` or `cold` to lock the temperature on Orb Vallis.
- `worldState.duviriOverride` can be set to `joy`, `anger`, `envy`, `sorrow`, or `fear` to lock the Duviri spiral.
- `worldState.nightwaveOverride` will lock the nightwave season, assuming the client is new enough for it. Valid values:
- `RadioLegionIntermission14Syndicate` for Nora's Mix: Dreams of the Dead
- `RadioLegionIntermission13Syndicate` for Nora's Mix Vol. 9
- `RadioLegionIntermission12Syndicate` for Nora's Mix Vol. 8
- `RadioLegionIntermission11Syndicate` for Nora's Mix Vol. 7
@ -34,5 +37,5 @@ SpaceNinjaServer requires a `config.json`. To set it up, you can copy the [confi
- `RadioLegion2Syndicate` for The Emissary
- `RadioLegionIntermissionSyndicate` for Intermission I
- `RadioLegionSyndicate` for The Wolf of Saturn Six
- `allTheFissures` can be set to `normal` or `hard` to enable all fissures either in normal or steel path, respectively.
- `worldState.allTheFissures` can be set to `normal` or `hard` to enable all fissures either in normal or steel path, respectively.
- `worldState.circuitGameModes` can be set to an array of game modes which will override the otherwise-random pattern in The Circuit. Valid element values are `Survival`, `VoidFlood`, `Excavation`, `Defense`, `Exterminate`, `Assassination`, and `Alchemy`.

View File

@ -2,24 +2,32 @@
echo Updating SpaceNinjaServer...
git fetch --prune
git stash
git checkout -f origin/main
if exist static\data\0\ (
echo Updating stripped assets...
cd static\data\0\
git pull
cd ..\..\..\
)
echo Updating dependencies...
call npm i --omit=dev
call npm run build
if %errorlevel% == 0 (
call npm run start
git stash
git checkout -f origin/main
if exist static\data\0\ (
echo Updating stripped assets...
cd static\data\0\
git pull
cd ..\..\..\
)
echo Updating dependencies...
node scripts/raw-precheck.js > NUL
if %errorlevel% == 0 (
call npm i --omit=dev --omit=optional
call npm run raw
) else (
call npm i --omit=dev
call npm run build
if %errorlevel% == 0 (
call npm run start
)
)
echo SpaceNinjaServer seems to have crashed.
)
:a
pause > nul
goto a

View File

@ -2,22 +2,28 @@
echo "Updating SpaceNinjaServer..."
git fetch --prune
git stash
git checkout -f origin/main
if [ -d "static/data/0/" ]; then
echo "Updating stripped assets..."
cd static/data/0/
git pull
cd ../../../
fi
echo "Updating dependencies..."
npm i --omit=dev
npm run build
if [ $? -eq 0 ]; then
npm run start
git stash
git checkout -f origin/main
if [ -d "static/data/0/" ]; then
echo "Updating stripped assets..."
cd static/data/0/
git pull
cd ../../../
fi
echo "Updating dependencies..."
node scripts/raw-precheck.js > /dev/null
if [ $? -eq 0 ]; then
npm i --omit=dev --omit=optional
npm run raw
else
npm i --omit=dev
npm run build
if [ $? -eq 0 ]; then
npm run start
fi
fi
echo "SpaceNinjaServer seems to have crashed."
fi

View File

@ -5,58 +5,19 @@
"level": "trace"
},
"myAddress": "localhost",
"bindAddress": "0.0.0.0",
"httpPort": 80,
"httpsPort": 443,
"NRS": ["localhost"],
"ircExecutable": null,
"ircAddress": null,
"hubAddress": null,
"nrsAddress": null,
"administratorNames": [],
"autoCreateAccount": true,
"skipTutorial": false,
"skipAllDialogue": false,
"unlockAllScans": false,
"claimingBlueprintRefundsIngredients": false,
"dontSubtractPurchaseCreditCost": false,
"dontSubtractPurchasePlatinumCost": false,
"dontSubtractPurchaseItemCost": false,
"dontSubtractPurchaseStandingCost": false,
"dontSubtractVoidTraces": false,
"dontSubtractConsumables": false,
"unlockAllShipFeatures": false,
"unlockAllShipDecorations": false,
"unlockAllFlavourItems": false,
"unlockAllSkins": false,
"unlockAllCapturaScenes": false,
"universalPolarityEverywhere": false,
"unlockDoubleCapacityPotatoesEverywhere": false,
"unlockExilusEverywhere": false,
"unlockArcanesEverywhere": false,
"noDailyStandingLimits": false,
"noDailyFocusLimit": false,
"noArgonCrystalDecay": false,
"noMasteryRankUpCooldown": false,
"noVendorPurchaseLimits": false,
"noDeathMarks": false,
"noKimCooldowns": false,
"fullyStockedVendors": false,
"baroAlwaysAvailable": false,
"baroFullyStocked": false,
"syndicateMissionsRepeatable": false,
"unlockAllProfitTakerStages": false,
"instantFinishRivenChallenge": false,
"instantResourceExtractorDrones": false,
"noResourceExtractorDronesDamage": false,
"skipClanKeyCrafting": false,
"noDojoRoomBuildStage": false,
"noDecoBuildStage": false,
"fastDojoRoomDestruction": false,
"noDojoResearchCosts": false,
"noDojoResearchTime": false,
"fastClanAscension": false,
"missionsCanGiveAllRelics": false,
"unlockAllSimarisResearchEntries": false,
"disableDailyTribute": false,
"spoofMasteryRank": -1,
"relicRewardItemCountMultiplier": 1,
"nightwaveStandingMultiplier": 1,
"unfaithfulBugFixes": {
"ignore1999LastRegionPlayed": false,
"fixXtraCheeseTimer": false,
@ -67,15 +28,25 @@
"affinityBoost": false,
"resourceBoost": false,
"tennoLiveRelay": false,
"wolfHunt": false,
"baroTennoConRelay": false,
"baroAlwaysAvailable": false,
"baroFullyStocked": false,
"varziaFullyStocked": false,
"wolfHunt": null,
"orphixVenom": false,
"longShadow": false,
"hallowedFlame": false,
"anniversary": null,
"hallowedNightmares": false,
"hallowedNightmaresRewardsOverride": 0,
"naberusNightsOverride": null,
"proxyRebellion": false,
"proxyRebellionRewardsOverride": 0,
"voidCorruption2025Week1": false,
"voidCorruption2025Week2": false,
"voidCorruption2025Week3": false,
"voidCorruption2025Week4": false,
"qtccAlerts": false,
"galleonOfGhouls": 0,
"ghoulEmergenceOverride": null,
"plagueStarOverride": null,
@ -86,15 +57,16 @@
"bellyOfTheBeastProgressOverride": 0,
"eightClaw": false,
"eightClawProgressOverride": 0,
"thermiaFracturesOverride": null,
"thermiaFracturesProgressOverride": 0,
"eidolonOverride": "",
"vallisOverride": "",
"duviriOverride": "",
"nightwaveOverride": "",
"allTheFissures": "",
"circuitGameModes": null,
"darvoStockMultiplier": 1,
"varziaOverride": "",
"varziaFullyStocked": false
"circuitGameModes": null,
"darvoStockMultiplier": 1
},
"dev": {
"keepVendorsExpired": false

View File

@ -1,6 +1,5 @@
services:
spaceninjaserver:
# The image to use. If you have an ARM CPU, replace 'latest' with 'latest-arm64'.
image: openwf/spaceninjaserver:latest
volumes:
@ -19,9 +18,6 @@ services:
- mongodb
mongodb:
image: docker.io/library/mongo:8.0.0-noble
environment:
MONGO_INITDB_ROOT_USERNAME: openwfagent
MONGO_INITDB_ROOT_PASSWORD: spaceninjaserver
volumes:
- ./docker-data/database:/data/db
command: mongod --quiet --logpath /dev/null

View File

@ -2,7 +2,7 @@
set -e
if [ ! -f conf/config.json ]; then
jq --arg value "mongodb://openwfagent:spaceninjaserver@mongodb:27017/" '.mongodbUrl = $value' /app/config-vanilla.json > /app/conf/config.json
jq --arg value "mongodb://mongodb:27017/openWF" '.mongodbUrl = $value' /app/config-vanilla.json > /app/conf/config.json
fi
exec npm run start -- --configPath conf/config.json
exec npm run raw -- --configPath conf/config.json

299
package-lock.json generated
View File

@ -9,11 +9,7 @@
"version": "0.1.0",
"license": "GNU",
"dependencies": {
"@types/express": "^5",
"@types/morgan": "^1.9.9",
"@types/websocket": "^1.0.10",
"@types/ws": "^8.18.1",
"@typescript/native-preview": "^7.0.0-dev.20250625.1",
"body-parser": "^2.2.0",
"chokidar": "^4.0.3",
"crc-32": "^1.2.2",
"express": "^5",
@ -21,9 +17,8 @@
"mongoose": "^8.11.0",
"morgan": "^1.10.0",
"ncp": "^2.0.0",
"typescript": "^5.5",
"undici": "^7.10.0",
"warframe-public-export-plus": "^0.5.81",
"warframe-public-export-plus": "^0.5.93",
"warframe-riven-info": "^0.1.2",
"winston": "^3.17.0",
"winston-daily-rotate-file": "^5.0.0",
@ -38,6 +33,18 @@
"eslint-plugin-prettier": "^5.2.5",
"prettier": "^3.5.3",
"tree-kill": "^1.2.2"
},
"engines": {
"node": ">=20.18.1"
},
"optionalDependencies": {
"@types/body-parser": "^1.19.6",
"@types/express": "^5",
"@types/morgan": "^1.9.9",
"@types/websocket": "^1.0.10",
"@types/ws": "^8.18.1",
"@typescript/native-preview": "^7.0.0-dev.20250625.1",
"typescript": "^5.7"
}
},
"node_modules/@colors/colors": {
@ -349,6 +356,7 @@
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz",
"integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==",
"license": "MIT",
"optional": true,
"dependencies": {
"@types/connect": "*",
"@types/node": "*"
@ -359,6 +367,7 @@
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
"integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
"license": "MIT",
"optional": true,
"dependencies": {
"@types/node": "*"
}
@ -368,6 +377,7 @@
"resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.3.tgz",
"integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==",
"license": "MIT",
"optional": true,
"dependencies": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "^5.0.0",
@ -379,6 +389,7 @@
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.7.tgz",
"integrity": "sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ==",
"license": "MIT",
"optional": true,
"dependencies": {
"@types/node": "*",
"@types/qs": "*",
@ -390,7 +401,8 @@
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz",
"integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==",
"license": "MIT"
"license": "MIT",
"optional": true
},
"node_modules/@types/json5": {
"version": "0.0.29",
@ -403,43 +415,49 @@
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
"license": "MIT"
"license": "MIT",
"optional": true
},
"node_modules/@types/morgan": {
"version": "1.9.10",
"resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.10.tgz",
"integrity": "sha512-sS4A1zheMvsADRVfT0lYbJ4S9lmsey8Zo2F7cnbYjWHP67Q0AwMYuuzLlkIM2N8gAbb9cubhIVFwcIN2XyYCkA==",
"license": "MIT",
"optional": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/node": {
"version": "24.0.15",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.15.tgz",
"integrity": "sha512-oaeTSbCef7U/z7rDeJA138xpG3NuKc64/rZ2qmUFkFJmnMsAPaluIifqyWd8hSSMxyP9oie3dLAqYPblag9KgA==",
"version": "24.3.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz",
"integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==",
"license": "MIT",
"optional": true,
"dependencies": {
"undici-types": "~7.8.0"
"undici-types": "~7.10.0"
}
},
"node_modules/@types/qs": {
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz",
"integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==",
"license": "MIT"
"license": "MIT",
"optional": true
},
"node_modules/@types/range-parser": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
"integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
"license": "MIT"
"license": "MIT",
"optional": true
},
"node_modules/@types/send": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz",
"integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==",
"license": "MIT",
"optional": true,
"dependencies": {
"@types/mime": "^1",
"@types/node": "*"
@ -450,6 +468,7 @@
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz",
"integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==",
"license": "MIT",
"optional": true,
"dependencies": {
"@types/http-errors": "*",
"@types/node": "*",
@ -473,6 +492,7 @@
"resolved": "https://registry.npmjs.org/@types/websocket/-/websocket-1.0.10.tgz",
"integrity": "sha512-svjGZvPB7EzuYS94cI7a+qhwgGU1y89wUgjT6E2wVUfmAGIvRfT7obBvRtnhXCSsoMdlG4gBFGE7MfkIXZLoww==",
"license": "MIT",
"optional": true,
"dependencies": {
"@types/node": "*"
}
@ -491,22 +511,23 @@
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
"integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
"license": "MIT",
"optional": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.37.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz",
"integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==",
"version": "8.41.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.41.0.tgz",
"integrity": "sha512-8fz6oa6wEKZrhXWro/S3n2eRJqlRcIa6SlDh59FXJ5Wp5XRZ8B9ixpJDcjadHq47hMx0u+HW6SNa6LjJQ6NLtw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "8.37.0",
"@typescript-eslint/type-utils": "8.37.0",
"@typescript-eslint/utils": "8.37.0",
"@typescript-eslint/visitor-keys": "8.37.0",
"@typescript-eslint/scope-manager": "8.41.0",
"@typescript-eslint/type-utils": "8.41.0",
"@typescript-eslint/utils": "8.41.0",
"@typescript-eslint/visitor-keys": "8.41.0",
"graphemer": "^1.4.0",
"ignore": "^7.0.0",
"natural-compare": "^1.4.0",
@ -520,22 +541,22 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"@typescript-eslint/parser": "^8.37.0",
"@typescript-eslint/parser": "^8.41.0",
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <5.9.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/parser": {
"version": "8.37.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz",
"integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==",
"version": "8.41.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.41.0.tgz",
"integrity": "sha512-gTtSdWX9xiMPA/7MV9STjJOOYtWwIJIYxkQxnSV1U3xcE+mnJSH3f6zI0RYP+ew66WSlZ5ed+h0VCxsvdC1jJg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/scope-manager": "8.37.0",
"@typescript-eslint/types": "8.37.0",
"@typescript-eslint/typescript-estree": "8.37.0",
"@typescript-eslint/visitor-keys": "8.37.0",
"@typescript-eslint/scope-manager": "8.41.0",
"@typescript-eslint/types": "8.41.0",
"@typescript-eslint/typescript-estree": "8.41.0",
"@typescript-eslint/visitor-keys": "8.41.0",
"debug": "^4.3.4"
},
"engines": {
@ -547,18 +568,18 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <5.9.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/project-service": {
"version": "8.37.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz",
"integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==",
"version": "8.41.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.41.0.tgz",
"integrity": "sha512-b8V9SdGBQzQdjJ/IO3eDifGpDBJfvrNTp2QD9P2BeqWTGrRibgfgIlBSw6z3b6R7dPzg752tOs4u/7yCLxksSQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.37.0",
"@typescript-eslint/types": "^8.37.0",
"@typescript-eslint/tsconfig-utils": "^8.41.0",
"@typescript-eslint/types": "^8.41.0",
"debug": "^4.3.4"
},
"engines": {
@ -569,18 +590,18 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <5.9.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "8.37.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz",
"integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==",
"version": "8.41.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.41.0.tgz",
"integrity": "sha512-n6m05bXn/Cd6DZDGyrpXrELCPVaTnLdPToyhBoFkLIMznRUQUEQdSp96s/pcWSQdqOhrgR1mzJ+yItK7T+WPMQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.37.0",
"@typescript-eslint/visitor-keys": "8.37.0"
"@typescript-eslint/types": "8.41.0",
"@typescript-eslint/visitor-keys": "8.41.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -591,9 +612,9 @@
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.37.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz",
"integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==",
"version": "8.41.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.41.0.tgz",
"integrity": "sha512-TDhxYFPUYRFxFhuU5hTIJk+auzM/wKvWgoNYOPcOf6i4ReYlOoYN8q1dV5kOTjNQNJgzWN3TUUQMtlLOcUgdUw==",
"dev": true,
"license": "MIT",
"engines": {
@ -604,19 +625,19 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <5.9.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.37.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz",
"integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==",
"version": "8.41.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.41.0.tgz",
"integrity": "sha512-63qt1h91vg3KsjVVonFJWjgSK7pZHSQFKH6uwqxAH9bBrsyRhO6ONoKyXxyVBzG1lJnFAJcKAcxLS54N1ee1OQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.37.0",
"@typescript-eslint/typescript-estree": "8.37.0",
"@typescript-eslint/utils": "8.37.0",
"@typescript-eslint/types": "8.41.0",
"@typescript-eslint/typescript-estree": "8.41.0",
"@typescript-eslint/utils": "8.41.0",
"debug": "^4.3.4",
"ts-api-utils": "^2.1.0"
},
@ -629,13 +650,13 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <5.9.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/types": {
"version": "8.37.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz",
"integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==",
"version": "8.41.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.41.0.tgz",
"integrity": "sha512-9EwxsWdVqh42afLbHP90n2VdHaWU/oWgbH2P0CfcNfdKL7CuKpwMQGjwev56vWu9cSKU7FWSu6r9zck6CVfnag==",
"dev": true,
"license": "MIT",
"engines": {
@ -647,16 +668,16 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "8.37.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz",
"integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==",
"version": "8.41.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.41.0.tgz",
"integrity": "sha512-D43UwUYJmGhuwHfY7MtNKRZMmfd8+p/eNSfFe6tH5mbVDto+VQCayeAt35rOx3Cs6wxD16DQtIKw/YXxt5E0UQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.37.0",
"@typescript-eslint/tsconfig-utils": "8.37.0",
"@typescript-eslint/types": "8.37.0",
"@typescript-eslint/visitor-keys": "8.37.0",
"@typescript-eslint/project-service": "8.41.0",
"@typescript-eslint/tsconfig-utils": "8.41.0",
"@typescript-eslint/types": "8.41.0",
"@typescript-eslint/visitor-keys": "8.41.0",
"debug": "^4.3.4",
"fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
@ -672,20 +693,20 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <5.9.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/utils": {
"version": "8.37.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz",
"integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==",
"version": "8.41.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.41.0.tgz",
"integrity": "sha512-udbCVstxZ5jiPIXrdH+BZWnPatjlYwJuJkDA4Tbo3WyYLh8NvB+h/bKeSZHDOFKfphsZYJQqaFtLeXEqurQn1A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.7.0",
"@typescript-eslint/scope-manager": "8.37.0",
"@typescript-eslint/types": "8.37.0",
"@typescript-eslint/typescript-estree": "8.37.0"
"@typescript-eslint/scope-manager": "8.41.0",
"@typescript-eslint/types": "8.41.0",
"@typescript-eslint/typescript-estree": "8.41.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -696,17 +717,17 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <5.9.0"
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "8.37.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz",
"integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==",
"version": "8.41.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.41.0.tgz",
"integrity": "sha512-+GeGMebMCy0elMNg67LRNoVnUFPIm37iu5CmHESVx56/9Jsfdpsvbv605DQ81Pi/x11IdKUsS5nzgTYbCQU9fg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.37.0",
"@typescript-eslint/types": "8.41.0",
"eslint-visitor-keys": "^4.2.1"
},
"engines": {
@ -731,10 +752,11 @@
}
},
"node_modules/@typescript/native-preview": {
"version": "7.0.0-dev.20250719.1",
"resolved": "https://registry.npmjs.org/@typescript/native-preview/-/native-preview-7.0.0-dev.20250719.1.tgz",
"integrity": "sha512-23wUnyj+BfVMSty9nsgTcVSlmI1ToS8P8qKS2g7ss0DUrnXN2tiJtbffDpmafMLEKEtfY42mVjmHPrZXHPdaOg==",
"version": "7.0.0-dev.20250826.1",
"resolved": "https://registry.npmjs.org/@typescript/native-preview/-/native-preview-7.0.0-dev.20250826.1.tgz",
"integrity": "sha512-+NuzOfk/lu6pLYSCio+R7uzJ9pfOasc1fshxVmLp6wgcB8yuUYYvBaT7CoHapUnNBYZXkJ9u0UOECnq3dbzgSQ==",
"license": "Apache-2.0",
"optional": true,
"bin": {
"tsgo": "bin/tsgo.js"
},
@ -742,19 +764,19 @@
"node": ">=20.6.0"
},
"optionalDependencies": {
"@typescript/native-preview-darwin-arm64": "7.0.0-dev.20250719.1",
"@typescript/native-preview-darwin-x64": "7.0.0-dev.20250719.1",
"@typescript/native-preview-linux-arm": "7.0.0-dev.20250719.1",
"@typescript/native-preview-linux-arm64": "7.0.0-dev.20250719.1",
"@typescript/native-preview-linux-x64": "7.0.0-dev.20250719.1",
"@typescript/native-preview-win32-arm64": "7.0.0-dev.20250719.1",
"@typescript/native-preview-win32-x64": "7.0.0-dev.20250719.1"
"@typescript/native-preview-darwin-arm64": "7.0.0-dev.20250826.1",
"@typescript/native-preview-darwin-x64": "7.0.0-dev.20250826.1",
"@typescript/native-preview-linux-arm": "7.0.0-dev.20250826.1",
"@typescript/native-preview-linux-arm64": "7.0.0-dev.20250826.1",
"@typescript/native-preview-linux-x64": "7.0.0-dev.20250826.1",
"@typescript/native-preview-win32-arm64": "7.0.0-dev.20250826.1",
"@typescript/native-preview-win32-x64": "7.0.0-dev.20250826.1"
}
},
"node_modules/@typescript/native-preview-darwin-arm64": {
"version": "7.0.0-dev.20250719.1",
"resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-arm64/-/native-preview-darwin-arm64-7.0.0-dev.20250719.1.tgz",
"integrity": "sha512-8BOaK/9MJx6qN8DSj7ooDQ56j1HTdGCgs5gzT+rOfv1dJZuhy96rFe1DDd3RwX3ufrNpuLE969GM1tmhaHGLag==",
"version": "7.0.0-dev.20250826.1",
"resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-arm64/-/native-preview-darwin-arm64-7.0.0-dev.20250826.1.tgz",
"integrity": "sha512-UTkmzj0+NpraW4GcgG5PECJZ25SASkiK7TP90lUx9/RZRidrClhHqyf/A3NPpI+1pqoKqGXxIt3+jV+BY15KyQ==",
"cpu": [
"arm64"
],
@ -768,9 +790,9 @@
}
},
"node_modules/@typescript/native-preview-darwin-x64": {
"version": "7.0.0-dev.20250719.1",
"resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-x64/-/native-preview-darwin-x64-7.0.0-dev.20250719.1.tgz",
"integrity": "sha512-cs+/yOFQlkbT5Z5SY8gr5TOk+mporqYAHgRBMXeWWOOStIA1gH3iLKO3aLw85GclgmFBEoFaOTm61RI/ugwD3w==",
"version": "7.0.0-dev.20250826.1",
"resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-x64/-/native-preview-darwin-x64-7.0.0-dev.20250826.1.tgz",
"integrity": "sha512-9UyLnKkJMW2T118FmS8Jj1djWDXgXGz2d9gUA/d+AdTAOaTBc1TmWhiUVcyKgIzG3+OAFIY+pVLeBWxNpFQu4Q==",
"cpu": [
"x64"
],
@ -784,9 +806,9 @@
}
},
"node_modules/@typescript/native-preview-linux-arm": {
"version": "7.0.0-dev.20250719.1",
"resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm/-/native-preview-linux-arm-7.0.0-dev.20250719.1.tgz",
"integrity": "sha512-KDRPvXXgks6JLf1zO4KCI4Zw1ZjAS3jM+YxF5EdTnw2v6+Hff+ritUo4FQHbgQOo2OLnmqity3Qsxxb5i4DaRg==",
"version": "7.0.0-dev.20250826.1",
"resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm/-/native-preview-linux-arm-7.0.0-dev.20250826.1.tgz",
"integrity": "sha512-CRH103rrFme+V917lftxtcttiT69fDNZPcnNzHeuj1z6/G3eIY6Hmon4xQb/Q4CjgblcO/BIEz6zDVphlas3dA==",
"cpu": [
"arm"
],
@ -800,9 +822,9 @@
}
},
"node_modules/@typescript/native-preview-linux-arm64": {
"version": "7.0.0-dev.20250719.1",
"resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm64/-/native-preview-linux-arm64-7.0.0-dev.20250719.1.tgz",
"integrity": "sha512-Yv8xeongAOJRy8UQaPH6TVbje+IXpu24KgIffjNW90ygMAJSrCNZXjnkRSXAelydD8RPCEas4O+qJR0Z2vElUQ==",
"version": "7.0.0-dev.20250826.1",
"resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm64/-/native-preview-linux-arm64-7.0.0-dev.20250826.1.tgz",
"integrity": "sha512-GFcWo//t482UhZW+K1Nqlp/eOO2NS1uAIR77UyPn1FK/NOQBaxg8w4AZk0OG5ZHzvut4iZwQa2Gp+8zbNnfAIA==",
"cpu": [
"arm64"
],
@ -816,9 +838,9 @@
}
},
"node_modules/@typescript/native-preview-linux-x64": {
"version": "7.0.0-dev.20250719.1",
"resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-x64/-/native-preview-linux-x64-7.0.0-dev.20250719.1.tgz",
"integrity": "sha512-GsGCL8KubbCOXfD9CyhHkMgZQVBLVXfeCnRID5UIgessmDBnd3Q158wbKIEZSLHVK2PffP9wKdfWMBGnU1X5BQ==",
"version": "7.0.0-dev.20250826.1",
"resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-x64/-/native-preview-linux-x64-7.0.0-dev.20250826.1.tgz",
"integrity": "sha512-ePQiUwa9YpUUc5g8lu77kCOgZyAseJn14B+5Uwz7n7CFPJn48L0mcKvjr3jVP8sER0r6rFmndEr2uyHnc+Qj2w==",
"cpu": [
"x64"
],
@ -832,9 +854,9 @@
}
},
"node_modules/@typescript/native-preview-win32-arm64": {
"version": "7.0.0-dev.20250719.1",
"resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-arm64/-/native-preview-win32-arm64-7.0.0-dev.20250719.1.tgz",
"integrity": "sha512-TP7ulNmZdAd2jgVLrIsmUoxwvQnpzsdWlRnTr0R6wEj+4Ob4Moyl75/1DrIkXO7yC6zaklhzLqgbHPz+H37ffg==",
"version": "7.0.0-dev.20250826.1",
"resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-arm64/-/native-preview-win32-arm64-7.0.0-dev.20250826.1.tgz",
"integrity": "sha512-kL3qH6CeOPdtAgZw1cuFgwpyup3VSkAM+ayZGk08SM42Zd90VMTDf736kXm/xxAcpvmCQ0YrH1FDIG0dPiRCAw==",
"cpu": [
"arm64"
],
@ -848,9 +870,9 @@
}
},
"node_modules/@typescript/native-preview-win32-x64": {
"version": "7.0.0-dev.20250719.1",
"resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-x64/-/native-preview-win32-x64-7.0.0-dev.20250719.1.tgz",
"integrity": "sha512-QIQAbcojUatTTxPriscWi7PBem5GsSKHS4lc1tosLXeC1o3jYITu0MHjFRtXyyO91I0xhJKE899Oh3Y/lqmn4A==",
"version": "7.0.0-dev.20250826.1",
"resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-x64/-/native-preview-win32-x64-7.0.0-dev.20250826.1.tgz",
"integrity": "sha512-WPiCnQDz64ZtwUEZ/ezXLHjhZTOetdfZI16ZbjZGkeZ5O3DNMaHrwzvvqqIq7gPxICEOK+6pM9eQ92I6wDq0qQ==",
"cpu": [
"x64"
],
@ -2287,9 +2309,9 @@
}
},
"node_modules/eslint-plugin-prettier": {
"version": "5.5.3",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.3.tgz",
"integrity": "sha512-NAdMYww51ehKfDyDhv59/eIItUVzU0Io9H2E8nHNGKEeeqlnci+1gCvrHib6EmZdf6GxF+LCV5K7UC65Ezvw7w==",
"version": "5.5.4",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz",
"integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -3833,9 +3855,9 @@
}
},
"node_modules/mongodb": {
"version": "6.17.0",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.17.0.tgz",
"integrity": "sha512-neerUzg/8U26cgruLysKEjJvoNSXhyID3RvzvdcpsIi2COYM3FS3o9nlH7fxFtefTb942dX3W9i37oPfCVj4wA==",
"version": "6.18.0",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.18.0.tgz",
"integrity": "sha512-fO5ttN9VC8P0F5fqtQmclAkgXZxbIkYRTUi1j8JO6IYwvamkhtYDilJr35jOPELR49zqCJgXZWwCtW7B+TM8vQ==",
"license": "Apache-2.0",
"dependencies": {
"@mongodb-js/saslprep": "^1.1.9",
@ -3889,14 +3911,14 @@
}
},
"node_modules/mongoose": {
"version": "8.16.4",
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.16.4.tgz",
"integrity": "sha512-jslgdQ8pY2vcNSKPv3Dbi5ogo/NT8zcvf6kPDyD8Sdsjsa1at3AFAF0F5PT+jySPGSPbvlNaQ49nT9h+Kx2UDA==",
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.18.0.tgz",
"integrity": "sha512-3TixPihQKBdyaYDeJqRjzgb86KbilEH07JmzV8SoSjgoskNTpa6oTBmDxeoF9p8YnWQoz7shnCyPkSV/48y3yw==",
"license": "MIT",
"dependencies": {
"bson": "^6.10.4",
"kareem": "2.6.3",
"mongodb": "~6.17.0",
"mongodb": "~6.18.0",
"mpath": "0.9.0",
"mquery": "5.0.0",
"ms": "2.1.3",
@ -3981,9 +4003,9 @@
"license": "MIT"
},
"node_modules/napi-postinstall": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.2.tgz",
"integrity": "sha512-tWVJxJHmBWLy69PvO96TZMZDrzmw5KeiZBz3RHmiM2XZ9grBJ2WgMAFVVg25nqp3ZjTFUs2Ftw1JhscL3Teliw==",
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.3.tgz",
"integrity": "sha512-uTp172LLXSxuSYHv/kou+f6KW3SMppU9ivthaVTXian9sOt3XM/zHYHpRZiLgQoxeWfYUnslNWQHF1+G71xcow==",
"dev": true,
"license": "MIT",
"bin": {
@ -5158,11 +5180,14 @@
}
},
"node_modules/tinyglobby/node_modules/fdir": {
"version": "6.4.6",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz",
"integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12.0.0"
},
"peerDependencies": {
"picomatch": "^3 || ^4"
},
@ -5391,9 +5416,10 @@
}
},
"node_modules/typescript": {
"version": "5.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"version": "5.9.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
"devOptional": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
@ -5423,19 +5449,20 @@
}
},
"node_modules/undici": {
"version": "7.12.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-7.12.0.tgz",
"integrity": "sha512-GrKEsc3ughskmGA9jevVlIOPMiiAHJ4OFUtaAH+NhfTUSiZ1wMPIQqQvAJUrJspFXJt3EBWgpAeoHEDVT1IBug==",
"version": "7.15.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-7.15.0.tgz",
"integrity": "sha512-7oZJCPvvMvTd0OlqWsIxTuItTpJBpU1tcbVl24FMn3xt3+VSunwUasmfPJRE57oNO1KsZ4PgA1xTdAX4hq8NyQ==",
"license": "MIT",
"engines": {
"node": ">=20.18.1"
}
},
"node_modules/undici-types": {
"version": "7.8.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz",
"integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==",
"license": "MIT"
"version": "7.10.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz",
"integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==",
"license": "MIT",
"optional": true
},
"node_modules/unpipe": {
"version": "1.0.0",
@ -5507,9 +5534,9 @@
}
},
"node_modules/warframe-public-export-plus": {
"version": "0.5.81",
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.81.tgz",
"integrity": "sha512-kh3e21XThVDSwdC3TJsMsXZnlZ4B/21HdeJkKcjuTygpCd842EPEKS3lRZl3mpXFOmdha744vAW1XEyHfiLofg=="
"version": "0.5.93",
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.93.tgz",
"integrity": "sha512-A8LSFJoyg7sU1n4L0zhLK1g0CREh8Fxvk7eXKoT8nMTroQg6YgEw02gK0MUi9U3rWTnlaGTsXZMp/tgC7HWUKw=="
},
"node_modules/warframe-riven-info": {
"version": "0.1.2",

View File

@ -4,33 +4,29 @@
"description": "WF Emulator",
"main": "index.ts",
"scripts": {
"start": "node --enable-source-maps --import ./build/src/pathman.js build/src/index.js",
"build": "tsgo --sourceMap && ncp static/webui build/static/webui",
"build:tsc": "tsc --incremental --sourceMap && ncp static/webui build/static/webui",
"build:dev": "tsgo --sourceMap",
"build:dev:tsc": "tsc --incremental --sourceMap",
"start": "node --enable-source-maps build/src/index.js",
"build": "tsgo --inlineSourceMap && ncp static/webui build/static/webui",
"build:tsc": "tsc --incremental --inlineSourceMap && ncp static/webui build/static/webui",
"build:dev": "tsgo --inlineSourceMap",
"build:dev:tsc": "tsc --incremental --inlineSourceMap",
"build-and-start": "npm run build && npm run start",
"build-and-start:bun": "npm run verify && npm run bun-run",
"dev": "node scripts/dev.js",
"dev:bun": "bun scripts/dev.js",
"dev": "node scripts/dev.cjs",
"dev:bun": "bun scripts/dev.cjs",
"verify": "tsgo --noEmit",
"verify:tsc": "tsc --noEmit",
"bun-run": "bun src/index.ts",
"raw": "node scripts/raw-precheck.js && node --experimental-transform-types src/index.ts",
"raw:bun": "bun src/index.ts",
"lint": "eslint --ext .ts .",
"lint:ci": "eslint --ext .ts --rule \"prettier/prettier: off\" .",
"lint:fix": "eslint --fix --ext .ts .",
"prettier": "prettier --write .",
"update-translations": "cd scripts && node update-translations.js",
"fix-imports": "cd scripts && node fix-imports.js",
"fix": "npm run update-translations && npm run fix-imports && npm run prettier"
"update-translations": "cd scripts && node update-translations.cjs",
"fix": "npm run update-translations && npm run prettier"
},
"license": "GNU",
"type": "module",
"dependencies": {
"@types/express": "^5",
"@types/morgan": "^1.9.9",
"@types/websocket": "^1.0.10",
"@types/ws": "^8.18.1",
"@typescript/native-preview": "^7.0.0-dev.20250625.1",
"body-parser": "^2.2.0",
"chokidar": "^4.0.3",
"crc-32": "^1.2.2",
"express": "^5",
@ -38,14 +34,22 @@
"mongoose": "^8.11.0",
"morgan": "^1.10.0",
"ncp": "^2.0.0",
"typescript": "^5.5",
"undici": "^7.10.0",
"warframe-public-export-plus": "^0.5.81",
"warframe-public-export-plus": "^0.5.93",
"warframe-riven-info": "^0.1.2",
"winston": "^3.17.0",
"winston-daily-rotate-file": "^5.0.0",
"ws": "^8.18.2"
},
"optionalDependencies": {
"@types/body-parser": "^1.19.6",
"@types/express": "^5",
"@types/morgan": "^1.9.9",
"@types/websocket": "^1.0.10",
"@types/ws": "^8.18.1",
"@typescript/native-preview": "^7.0.0-dev.20250625.1",
"typescript": "^5.7"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^8.28.0",
"@typescript-eslint/parser": "^8.28.0",
@ -55,5 +59,8 @@
"eslint-plugin-prettier": "^5.2.5",
"prettier": "^3.5.3",
"tree-kill": "^1.2.2"
},
"engines": {
"node": ">=20.18.1"
}
}

View File

@ -13,6 +13,17 @@ args.push("--dev");
args.push("--secret");
args.push(secret);
const cangoraw = (() => {
if (process.versions.bun) {
return true;
}
const [major, minor] = process.versions.node.split(".").map(x => parseInt(x));
if (major > 22 || (major == 22 && minor >= 7)) {
return true;
}
return false;
})();
let buildproc, runproc;
const spawnopts = { stdio: "inherit", shell: true };
function run(changedFile) {
@ -29,7 +40,10 @@ function run(changedFile) {
runproc = undefined;
}
const thisbuildproc = spawn("npm", ["run", process.versions.bun ? "verify" : "build:dev"], spawnopts);
const thisbuildproc = spawn(
[process.versions.bun ? "bun" : "npm", "run", cangoraw ? "verify" : "build:dev"].join(" "),
spawnopts
);
const thisbuildstart = Date.now();
buildproc = thisbuildproc;
buildproc.on("exit", code => {
@ -38,8 +52,17 @@ function run(changedFile) {
}
buildproc = undefined;
if (code === 0) {
console.log(`${process.versions.bun ? "Verified" : "Built"} in ${Date.now() - thisbuildstart} ms`);
runproc = spawn("npm", ["run", process.versions.bun ? "bun-run" : "start", "--", ...args], spawnopts);
console.log(`${cangoraw ? "Verified" : "Built"} in ${Date.now() - thisbuildstart} ms`);
runproc = spawn(
[
process.versions.bun ? "bun" : "npm",
"run",
cangoraw ? (process.versions.bun ? "raw:bun" : "raw") : "start",
"--",
...args
].join(" "),
spawnopts
);
runproc.on("exit", () => {
runproc = undefined;
});

View File

@ -1,46 +0,0 @@
/* eslint-disable */
const fs = require("fs");
const path = require("path");
const root = path.join(process.cwd(), "..");
function listFiles(dir) {
const entries = fs.readdirSync(dir, { withFileTypes: true });
let results = [];
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
results = results.concat(listFiles(fullPath));
} else {
results.push(fullPath);
}
}
return results;
}
const files = listFiles(path.join(root, "src"));
for (const file of files) {
let content;
try {
content = fs.readFileSync(file, "utf8");
} catch (e) {
continue;
}
const dir = path.dirname(file);
const fixedContent = content.replaceAll(/} from "([^"]+)";/g, (sub, importPath) => {
if (!importPath.startsWith("@/")) {
const fullImportPath = path.resolve(dir, importPath);
if (fs.existsSync(fullImportPath + ".ts")) {
const relative = path.relative(root, fullImportPath).replace(/\\/g, "/");
const fixedPath = "@/" + relative;
console.log(`${importPath} -> ${fixedPath}`);
return sub.split(importPath).join(fixedPath);
}
}
return sub;
});
if (content != fixedContent) {
fs.writeFileSync(file, fixedContent, "utf8");
}
}

9
scripts/raw-precheck.js Normal file
View File

@ -0,0 +1,9 @@
const [major, minor] = process.versions.node.split(".").map(x => parseInt(x));
if (major > 22 || (major == 22 && minor >= 7)) {
// ok
} else {
console.log("Sorry, your Node version is a bit too old for this. You have 2 options:");
console.log("- Update Node.js.");
console.log("- Use 'npm run build && npm run start'. Optional libraries must be installed for this.");
process.exit(1);
}

View File

@ -31,7 +31,7 @@ fs.readdirSync("../static/webui/translations").forEach(file => {
const strings = extractStrings(line);
if (Object.keys(strings).length > 0) {
Object.entries(strings).forEach(([key, value]) => {
if (targetStrings.hasOwnProperty(key) && !targetStrings[key].startsWith("[UNTRANSLATED] ")) {
if (targetStrings.hasOwnProperty(key) && !targetStrings[key].startsWith("[UNTRANSLATED]")) {
fs.writeSync(fileHandle, ` ${key}: \`${targetStrings[key]}\`,\n`);
} else {
fs.writeSync(fileHandle, ` ${key}: \`[UNTRANSLATED] ${value}\`,\n`);

View File

@ -1,23 +1,23 @@
import express from "express";
import bodyParser from "body-parser";
import { unknownEndpointHandler } from "@/src/middleware/middleware";
import { requestLogger } from "@/src/middleware/morgenMiddleware";
import { errorHandler } from "@/src/middleware/errorHandler";
import { unknownEndpointHandler } from "./middleware/middleware.ts";
import { requestLogger } from "./middleware/morgenMiddleware.ts";
import { errorHandler } from "./middleware/errorHandler.ts";
import { apiRouter } from "@/src/routes/api";
import { cacheRouter } from "@/src/routes/cache";
import { customRouter } from "@/src/routes/custom";
import { dynamicController } from "@/src/routes/dynamic";
import { payRouter } from "@/src/routes/pay";
import { statsRouter } from "@/src/routes/stats";
import { webuiRouter } from "@/src/routes/webui";
import { apiRouter } from "./routes/api.ts";
import { cacheRouter } from "./routes/cache.ts";
import { customRouter } from "./routes/custom.ts";
import { dynamicController } from "./routes/dynamic.ts";
import { payRouter } from "./routes/pay.ts";
import { statsRouter } from "./routes/stats.ts";
import { webuiRouter } from "./routes/webui.ts";
const app = express();
app.use((req, _res, next) => {
// 38.5.0 introduced "ezip" for encrypted body blobs and "e" for request verification only (encrypted body blobs with no application data).
// The bootstrapper decrypts it for us but having an unsupported Content-Encoding here would still be an issue for Express, so removing it.
// The client patch is expected to decrypt it for us but having an unsupported Content-Encoding here would still be an issue for Express, so removing it.
if (req.headers["content-encoding"] == "ezip" || req.headers["content-encoding"] == "e") {
req.headers["content-encoding"] = undefined;
}

View File

@ -1,3 +1,5 @@
export const EPOCH = 1734307200_000; // Monday, Dec 16, 2024 @ 00:00 UTC+0; should logically be the start of winter in 1999 iteration 0
const millisecondsPerSecond = 1000;
const secondsPerMinute = 60;
const minutesPerHour = 60;

View File

@ -1,6 +1,6 @@
import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { RequestHandler } from "express";
import { getInventory } from "../../services/inventoryService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import type { RequestHandler } from "express";
export const abandonLibraryDailyTaskController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);

View File

@ -5,11 +5,11 @@ import {
hasGuildPermission,
removeDojoDeco,
removeDojoRoom
} from "@/src/services/guildService";
import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { GuildPermission } from "@/src/types/guildTypes";
import { RequestHandler } from "express";
} from "../../services/guildService.ts";
import { getInventory } from "../../services/inventoryService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { GuildPermission } from "../../types/guildTypes.ts";
import type { RequestHandler } from "express";
export const abortDojoComponentController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
@ -31,12 +31,13 @@ export const abortDojoComponentController: RequestHandler = async (req, res) =>
if (request.DecoId) {
removeDojoDeco(guild, request.ComponentId, request.DecoId);
await guild.save();
res.json(await getDojoClient(guild, 0, request.ComponentId));
} else {
await removeDojoRoom(guild, request.ComponentId);
await guild.save();
res.json(await getDojoClient(guild, 0));
}
await guild.save();
res.json(await getDojoClient(guild, 0, request.ComponentId));
};
interface IAbortDojoComponentRequest {

View File

@ -1,8 +1,13 @@
import { getDojoClient, getGuildForRequestEx, hasAccessToDojo, hasGuildPermission } from "@/src/services/guildService";
import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { GuildPermission } from "@/src/types/guildTypes";
import { RequestHandler } from "express";
import {
getDojoClient,
getGuildForRequestEx,
hasAccessToDojo,
hasGuildPermission
} from "../../services/guildService.ts";
import { getInventory } from "../../services/inventoryService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { GuildPermission } from "../../types/guildTypes.ts";
import type { RequestHandler } from "express";
export const abortDojoComponentDestructionController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);

View File

@ -1,20 +1,19 @@
import { toOid } from "@/src/helpers/inventoryHelpers";
import { toOid } from "../../helpers/inventoryHelpers.ts";
import {
createVeiledRivenFingerprint,
createUnveiledRivenFingerprint,
rivenRawToRealWeighted
} from "@/src/helpers/rivenHelper";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { addMods, getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { getRandomElement } from "@/src/services/rngService";
import { RequestHandler } from "express";
} from "../../helpers/rivenHelper.ts";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import { addMods, getInventory } from "../../services/inventoryService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { getRandomElement } from "../../services/rngService.ts";
import type { RequestHandler } from "express";
import { ExportUpgrades } from "warframe-public-export-plus";
import { config } from "@/src/services/configService";
export const activateRandomModController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const inventory = await getInventory(accountId);
const inventory = await getInventory(accountId, "RawUpgrades Upgrades instantFinishRivenChallenge");
const request = getJSONfromString<IActiveRandomModRequest>(String(req.body));
addMods(inventory, [
{
@ -23,7 +22,7 @@ export const activateRandomModController: RequestHandler = async (req, res) => {
}
]);
const rivenType = getRandomElement(rivenRawToRealWeighted[request.ItemType])!;
const fingerprint = config.instantFinishRivenChallenge
const fingerprint = inventory.instantFinishRivenChallenge
? createUnveiledRivenFingerprint(ExportUpgrades[rivenType])
: createVeiledRivenFingerprint(ExportUpgrades[rivenType]);
const upgradeIndex =

View File

@ -1,10 +1,10 @@
import { toOid } from "@/src/helpers/inventoryHelpers";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { Friendship } from "@/src/models/friendModel";
import { addAccountDataToFriendInfo, addInventoryDataToFriendInfo } from "@/src/services/friendService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { IFriendInfo } from "@/src/types/friendTypes";
import { RequestHandler } from "express";
import { toOid } from "../../helpers/inventoryHelpers.ts";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import { Friendship } from "../../models/friendModel.ts";
import { addAccountDataToFriendInfo, addInventoryDataToFriendInfo } from "../../services/friendService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import type { IFriendInfo } from "../../types/friendTypes.ts";
import type { RequestHandler } from "express";
export const addFriendController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);

View File

@ -1,7 +1,7 @@
import { RequestHandler } from "express";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { Inventory } from "@/src/models/inventoryModels/inventoryModel";
import type { RequestHandler } from "express";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { Inventory } from "../../models/inventoryModels/inventoryModel.ts";
export const addFriendImageController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);

View File

@ -1,9 +1,9 @@
import { toOid } from "@/src/helpers/inventoryHelpers";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { Account, Ignore } from "@/src/models/loginModel";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { IFriendInfo } from "@/src/types/friendTypes";
import { RequestHandler } from "express";
import { toOid } from "../../helpers/inventoryHelpers.ts";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import { Account, Ignore } from "../../models/loginModel.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import type { IFriendInfo } from "../../types/friendTypes.ts";
import type { RequestHandler } from "express";
export const addIgnoredUserController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);

View File

@ -1,12 +1,12 @@
import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { Friendship } from "@/src/models/friendModel";
import { Account } from "@/src/models/loginModel";
import { addInventoryDataToFriendInfo, areFriendsOfFriends } from "@/src/services/friendService";
import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { IFriendInfo } from "@/src/types/friendTypes";
import { RequestHandler } from "express";
import { toMongoDate, toOid } from "../../helpers/inventoryHelpers.ts";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import { Friendship } from "../../models/friendModel.ts";
import { Account } from "../../models/loginModel.ts";
import { addInventoryDataToFriendInfo, areFriendsOfFriends } from "../../services/friendService.ts";
import { getInventory } from "../../services/inventoryService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import type { IFriendInfo } from "../../types/friendTypes.ts";
import type { RequestHandler } from "express";
export const addPendingFriendController: RequestHandler = async (req, res) => {
const payload = getJSONfromString<IAddPendingFriendRequest>(String(req.body));

View File

@ -1,11 +1,11 @@
import { getJSONfromString, regexEscape } from "@/src/helpers/stringHelpers";
import { Alliance, AllianceMember, Guild, GuildMember } from "@/src/models/guildModel";
import { createMessage } from "@/src/services/inboxService";
import { getEffectiveAvatarImageType, getInventory } from "@/src/services/inventoryService";
import { getAccountForRequest, getSuffixedName } from "@/src/services/loginService";
import { GuildPermission } from "@/src/types/guildTypes";
import { logger } from "@/src/utils/logger";
import { RequestHandler } from "express";
import { getJSONfromString, regexEscape } from "../../helpers/stringHelpers.ts";
import { Alliance, AllianceMember, Guild, GuildMember } from "../../models/guildModel.ts";
import { createMessage } from "../../services/inboxService.ts";
import { getEffectiveAvatarImageType, getInventory } from "../../services/inventoryService.ts";
import { getAccountForRequest, getSuffixedName } from "../../services/loginService.ts";
import { GuildPermission } from "../../types/guildTypes.ts";
import { logger } from "../../utils/logger.ts";
import type { RequestHandler } from "express";
import { ExportFlavour } from "warframe-public-export-plus";
export const addToAllianceController: RequestHandler = async (req, res) => {

View File

@ -1,15 +1,16 @@
import { toMongoDate } from "@/src/helpers/inventoryHelpers";
import { Guild, GuildMember } from "@/src/models/guildModel";
import { Account } from "@/src/models/loginModel";
import { addInventoryDataToFriendInfo, areFriends } from "@/src/services/friendService";
import { hasGuildPermission } from "@/src/services/guildService";
import { createMessage } from "@/src/services/inboxService";
import { getEffectiveAvatarImageType, getInventory } from "@/src/services/inventoryService";
import { getAccountForRequest, getAccountIdForRequest, getSuffixedName } from "@/src/services/loginService";
import { IOid } from "@/src/types/commonTypes";
import { GuildPermission, IGuildMemberClient } from "@/src/types/guildTypes";
import { logger } from "@/src/utils/logger";
import { RequestHandler } from "express";
import { toMongoDate } from "../../helpers/inventoryHelpers.ts";
import { Guild, GuildMember } from "../../models/guildModel.ts";
import { Account } from "../../models/loginModel.ts";
import { addInventoryDataToFriendInfo, areFriends } from "../../services/friendService.ts";
import { hasGuildPermission } from "../../services/guildService.ts";
import { createMessage } from "../../services/inboxService.ts";
import { getEffectiveAvatarImageType, getInventory } from "../../services/inventoryService.ts";
import { getAccountForRequest, getAccountIdForRequest, getSuffixedName } from "../../services/loginService.ts";
import type { IOid } from "../../types/commonTypes.ts";
import type { IGuildMemberClient } from "../../types/guildTypes.ts";
import { GuildPermission } from "../../types/guildTypes.ts";
import { logger } from "../../utils/logger.ts";
import type { RequestHandler } from "express";
import { ExportFlavour } from "warframe-public-export-plus";
export const addToGuildController: RequestHandler = async (req, res) => {

View File

@ -1,7 +1,7 @@
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { RequestHandler } from "express";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import { getInventory } from "../../services/inventoryService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import type { RequestHandler } from "express";
export const adoptPetController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);

View File

@ -1,6 +1,6 @@
import { getAccountIdForRequest } from "@/src/services/loginService";
import { getPersonalRooms } from "@/src/services/personalRoomsService";
import { RequestHandler } from "express";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { getPersonalRooms } from "../../services/personalRoomsService.ts";
import type { RequestHandler } from "express";
export const apartmentController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);

View File

@ -1,8 +1,8 @@
import { RequestHandler } from "express";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { getInventory, addMods } from "@/src/services/inventoryService";
import { IOid } from "@/src/types/commonTypes";
import type { RequestHandler } from "express";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { getInventory, addMods } from "../../services/inventoryService.ts";
import type { IOid } from "../../types/commonTypes.ts";
export const arcaneCommonController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);

View File

@ -1,8 +1,8 @@
import { RequestHandler } from "express";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { addMiscItems, getInventory } from "@/src/services/inventoryService";
import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
import { colorToShard, combineColors, shardToColor } from "@/src/helpers/shardHelper";
import type { RequestHandler } from "express";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { addMiscItems, getInventory } from "../../services/inventoryService.ts";
import type { IMiscItem } from "../../types/inventoryTypes/inventoryTypes.ts";
import { colorToShard, combineColors, shardToColor } from "../../helpers/shardHelper.ts";
export const archonFusionController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);

View File

@ -1,11 +1,12 @@
import { fromOid, toOid } from "@/src/helpers/inventoryHelpers";
import { createVeiledRivenFingerprint, rivenRawToRealWeighted } from "@/src/helpers/rivenHelper";
import { addMiscItems, addMods, getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { getRandomElement, getRandomWeightedReward, getRandomWeightedRewardUc } from "@/src/services/rngService";
import { IUpgradeFromClient } from "@/src/types/inventoryTypes/inventoryTypes";
import { RequestHandler } from "express";
import { ExportBoosterPacks, ExportUpgrades, TRarity } from "warframe-public-export-plus";
import { fromOid, toOid } from "../../helpers/inventoryHelpers.ts";
import { createVeiledRivenFingerprint, rivenRawToRealWeighted } from "../../helpers/rivenHelper.ts";
import { addMiscItems, addMods, getInventory } from "../../services/inventoryService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { getRandomElement, getRandomWeightedReward, getRandomWeightedRewardUc } from "../../services/rngService.ts";
import type { IUpgradeFromClient } from "../../types/inventoryTypes/inventoryTypes.ts";
import type { RequestHandler } from "express";
import type { TRarity } from "warframe-public-export-plus";
import { ExportBoosterPacks, ExportUpgrades } from "warframe-public-export-plus";
export const artifactTransmutationController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);

View File

@ -1,8 +1,9 @@
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { RequestHandler } from "express";
import { IInventoryClient, IUpgradeClient } from "@/src/types/inventoryTypes/inventoryTypes";
import { addMods, getInventory } from "@/src/services/inventoryService";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import type { RequestHandler } from "express";
import type { IInventoryClient, IUpgradeClient } from "../../types/inventoryTypes/inventoryTypes.ts";
import { addMods, getInventory } from "../../services/inventoryService.ts";
import { broadcastInventoryUpdate } from "../../services/wsService.ts";
export const artifactsController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
@ -57,6 +58,7 @@ export const artifactsController: RequestHandler = async (req, res) => {
}
res.send(itemId);
broadcastInventoryUpdate(req);
};
interface IArtifactsRequest {

View File

@ -1,9 +1,9 @@
import { GuildAd } from "@/src/models/guildModel";
import { getGuildForRequestEx, hasGuildPermission } from "@/src/services/guildService";
import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { GuildPermission } from "@/src/types/guildTypes";
import { RequestHandler } from "express";
import { GuildAd } from "../../models/guildModel.ts";
import { getGuildForRequestEx, hasGuildPermission } from "../../services/guildService.ts";
import { getInventory } from "../../services/inventoryService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { GuildPermission } from "../../types/guildTypes.ts";
import type { RequestHandler } from "express";
export const cancelGuildAdvertisementController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);

View File

@ -1,10 +1,16 @@
import { RequestHandler } from "express";
import { getDojoClient, getGuildForRequestEx, hasAccessToDojo, hasGuildPermission } from "@/src/services/guildService";
import { logger } from "@/src/utils/logger";
import { GuildPermission, IDojoComponentDatabase } from "@/src/types/guildTypes";
import type { RequestHandler } from "express";
import {
getDojoClient,
getGuildForRequestEx,
hasAccessToDojo,
hasGuildPermission
} from "../../services/guildService.ts";
import { logger } from "../../utils/logger.ts";
import type { IDojoComponentDatabase } from "../../types/guildTypes.ts";
import { GuildPermission } from "../../types/guildTypes.ts";
import { Types } from "mongoose";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { getInventory } from "../../services/inventoryService.ts";
export const changeDojoRootController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);

View File

@ -1,8 +1,8 @@
import { GuildMember } from "@/src/models/guildModel";
import { getGuildForRequest, hasGuildPermissionEx } from "@/src/services/guildService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { GuildPermission } from "@/src/types/guildTypes";
import { RequestHandler } from "express";
import { GuildMember } from "../../models/guildModel.ts";
import { getGuildForRequest, hasGuildPermissionEx } from "../../services/guildService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { GuildPermission } from "../../types/guildTypes.ts";
import type { RequestHandler } from "express";
export const changeGuildRankController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);

View File

@ -1,5 +1,5 @@
import { getAccountForRequest } from "@/src/services/loginService";
import { RequestHandler } from "express";
import { getAccountForRequest } from "../../services/loginService.ts";
import type { RequestHandler } from "express";
export const checkDailyMissionBonusController: RequestHandler = async (req, res) => {
const account = await getAccountForRequest(req);

View File

@ -1,12 +1,13 @@
//this is a controller for the claimCompletedRecipe route
//it will claim a recipe for the user
import { RequestHandler } from "express";
import { logger } from "@/src/utils/logger";
import { getRecipe } from "@/src/services/itemDataService";
import { IOid, IOidWithLegacySupport } from "@/src/types/commonTypes";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { getAccountForRequest } from "@/src/services/loginService";
import type { RequestHandler } from "express";
import { logger } from "../../utils/logger.ts";
import { getRecipe } from "../../services/itemDataService.ts";
import type { IOidWithLegacySupport } from "../../types/commonTypes.ts";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import type { TAccountDocument } from "../../services/loginService.ts";
import { getAccountForRequest } from "../../services/loginService.ts";
import {
getInventory,
updateCurrency,
@ -17,243 +18,258 @@ import {
addKubrowPetPrint,
addPowerSuit,
addEquipment
} from "@/src/services/inventoryService";
import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { InventorySlot, IPendingRecipeDatabase } from "@/src/types/inventoryTypes/inventoryTypes";
import { toOid2 } from "@/src/helpers/inventoryHelpers";
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
import { IRecipe } from "warframe-public-export-plus";
import { config } from "@/src/services/configService";
import { EquipmentFeatures, IEquipmentClient, Status } from "@/src/types/equipmentTypes";
} from "../../services/inventoryService.ts";
import type { IInventoryChanges } from "../../types/purchaseTypes.ts";
import type { IPendingRecipeDatabase } from "../../types/inventoryTypes/inventoryTypes.ts";
import { InventorySlot } from "../../types/inventoryTypes/inventoryTypes.ts";
import { fromOid, toOid2 } from "../../helpers/inventoryHelpers.ts";
import type { TInventoryDatabaseDocument } from "../../models/inventoryModels/inventoryModel.ts";
import type { IRecipe } from "warframe-public-export-plus";
import type { IEquipmentClient } from "../../types/equipmentTypes.ts";
import { EquipmentFeatures, Status } from "../../types/equipmentTypes.ts";
interface IClaimCompletedRecipeRequest {
RecipeIds: IOid[];
RecipeIds: IOidWithLegacySupport[];
}
interface IClaimCompletedRecipeResponse {
InventoryChanges: IInventoryChanges;
BrandedSuits?: IOidWithLegacySupport[];
}
export const claimCompletedRecipeController: RequestHandler = async (req, res) => {
const claimCompletedRecipeRequest = getJSONfromString<IClaimCompletedRecipeRequest>(String(req.body));
const account = await getAccountForRequest(req);
const inventory = await getInventory(account._id.toString());
const pendingRecipe = inventory.PendingRecipes.id(claimCompletedRecipeRequest.RecipeIds[0].$oid);
if (!pendingRecipe) {
throw new Error(`no pending recipe found with id ${claimCompletedRecipeRequest.RecipeIds[0].$oid}`);
}
//check recipe is indeed ready to be completed
// if (pendingRecipe.CompletionDate > new Date()) {
// throw new Error(`recipe ${pendingRecipe._id} is not ready to be completed`);
// }
inventory.PendingRecipes.pull(pendingRecipe._id);
const recipe = getRecipe(pendingRecipe.ItemType);
if (!recipe) {
throw new Error(`no completed item found for recipe ${pendingRecipe._id.toString()}`);
}
if (req.query.cancel) {
const inventoryChanges: IInventoryChanges = {};
await refundRecipeIngredients(inventory, inventoryChanges, recipe, pendingRecipe);
await inventory.save();
res.json(inventoryChanges); // Not a bug: In the specific case of cancelling a recipe, InventoryChanges are expected to be the root.
} else {
logger.debug("Claiming Recipe", { recipe, pendingRecipe });
let BrandedSuits: undefined | IOidWithLegacySupport[];
if (recipe.secretIngredientAction == "SIA_SPECTRE_LOADOUT_COPY") {
inventory.PendingSpectreLoadouts ??= [];
inventory.SpectreLoadouts ??= [];
const pendingLoadoutIndex = inventory.PendingSpectreLoadouts.findIndex(
x => x.ItemType == recipe.resultType
);
if (pendingLoadoutIndex != -1) {
const loadoutIndex = inventory.SpectreLoadouts.findIndex(x => x.ItemType == recipe.resultType);
if (loadoutIndex != -1) {
inventory.SpectreLoadouts.splice(loadoutIndex, 1);
}
logger.debug(
"moving spectre loadout from pending to active",
inventory.toJSON().PendingSpectreLoadouts![pendingLoadoutIndex]
);
inventory.SpectreLoadouts.push(inventory.PendingSpectreLoadouts[pendingLoadoutIndex]);
inventory.PendingSpectreLoadouts.splice(pendingLoadoutIndex, 1);
}
} else if (recipe.secretIngredientAction == "SIA_UNBRAND") {
inventory.BrandedSuits!.splice(
inventory.BrandedSuits!.findIndex(x => x.equals(pendingRecipe.SuitToUnbrand)),
1
);
BrandedSuits = [toOid2(pendingRecipe.SuitToUnbrand!, account.BuildLabel)];
const resp: IClaimCompletedRecipeResponse = {
InventoryChanges: {}
};
for (const recipeId of claimCompletedRecipeRequest.RecipeIds) {
const pendingRecipe = inventory.PendingRecipes.id(fromOid(recipeId));
if (!pendingRecipe) {
throw new Error(`no pending recipe found with id ${fromOid(recipeId)}`);
}
let InventoryChanges: IInventoryChanges = {};
if (recipe.consumeOnUse) {
addRecipes(inventory, [
//check recipe is indeed ready to be completed
// if (pendingRecipe.CompletionDate > new Date()) {
// throw new Error(`recipe ${pendingRecipe._id} is not ready to be completed`);
// }
inventory.PendingRecipes.pull(pendingRecipe._id);
const recipe = getRecipe(pendingRecipe.ItemType);
if (!recipe) {
throw new Error(`no completed item found for recipe ${pendingRecipe._id.toString()}`);
}
if (req.query.cancel) {
const inventoryChanges: IInventoryChanges = {};
await refundRecipeIngredients(inventory, inventoryChanges, recipe, pendingRecipe);
await inventory.save();
res.json(inventoryChanges); // Not a bug: In the specific case of cancelling a recipe, InventoryChanges are expected to be the root.
return;
}
await claimCompletedRecipe(account, inventory, recipe, pendingRecipe, resp, req.query.rush);
}
await inventory.save();
res.json(resp);
};
const claimCompletedRecipe = async (
account: TAccountDocument,
inventory: TInventoryDatabaseDocument,
recipe: IRecipe,
pendingRecipe: IPendingRecipeDatabase,
resp: IClaimCompletedRecipeResponse,
rush: any
): Promise<void> => {
logger.debug("Claiming Recipe", { recipe, pendingRecipe });
if (recipe.secretIngredientAction == "SIA_SPECTRE_LOADOUT_COPY") {
inventory.PendingSpectreLoadouts ??= [];
inventory.SpectreLoadouts ??= [];
const pendingLoadoutIndex = inventory.PendingSpectreLoadouts.findIndex(x => x.ItemType == recipe.resultType);
if (pendingLoadoutIndex != -1) {
const loadoutIndex = inventory.SpectreLoadouts.findIndex(x => x.ItemType == recipe.resultType);
if (loadoutIndex != -1) {
inventory.SpectreLoadouts.splice(loadoutIndex, 1);
}
logger.debug(
"moving spectre loadout from pending to active",
inventory.toJSON().PendingSpectreLoadouts![pendingLoadoutIndex]
);
inventory.SpectreLoadouts.push(inventory.PendingSpectreLoadouts[pendingLoadoutIndex]);
inventory.PendingSpectreLoadouts.splice(pendingLoadoutIndex, 1);
}
} else if (recipe.secretIngredientAction == "SIA_UNBRAND") {
inventory.BrandedSuits!.splice(
inventory.BrandedSuits!.findIndex(x => x.equals(pendingRecipe.SuitToUnbrand)),
1
);
resp.BrandedSuits = [toOid2(pendingRecipe.SuitToUnbrand!, account.BuildLabel)];
}
if (recipe.consumeOnUse) {
addRecipes(inventory, [
{
ItemType: pendingRecipe.ItemType,
ItemCount: -1
}
]);
}
if (rush) {
const end = Math.trunc(pendingRecipe.CompletionDate.getTime() / 1000);
const start = end - recipe.buildTime;
const secondsElapsed = Math.trunc(Date.now() / 1000) - start;
const progress = secondsElapsed / recipe.buildTime;
logger.debug(`rushing recipe at ${Math.trunc(progress * 100)}% completion`);
const cost =
progress > 0.5 ? Math.round(recipe.skipBuildTimePrice * (1 - (progress - 0.5))) : recipe.skipBuildTimePrice;
combineInventoryChanges(resp.InventoryChanges, updateCurrency(inventory, cost, true));
}
if (recipe.secretIngredientAction == "SIA_CREATE_KUBROW") {
const pet = inventory.KubrowPets.id(pendingRecipe.KubrowPet!)!;
if (pet.Details!.HatchDate!.getTime() > Date.now()) {
pet.Details!.HatchDate = new Date();
}
let canSetActive = true;
for (const pet of inventory.KubrowPets) {
if (pet.Details!.Status == Status.StatusAvailable) {
canSetActive = false;
break;
}
}
pet.Details!.Status = canSetActive ? Status.StatusAvailable : Status.StatusStasis;
} else if (recipe.secretIngredientAction == "SIA_DISTILL_PRINT") {
const pet = inventory.KubrowPets.id(pendingRecipe.KubrowPet!)!;
addKubrowPetPrint(inventory, pet, resp.InventoryChanges);
} else if (recipe.secretIngredientAction != "SIA_UNBRAND") {
if (recipe.resultType == "/Lotus/Powersuits/Excalibur/ExcaliburUmbra") {
// Quite the special case here...
// We don't just get Umbra, but also Skiajati and Umbra Mods. Both items are max rank, potatoed, and with the mods are pre-installed.
// Source: https://wiki.warframe.com/w/The_Sacrifice, https://wiki.warframe.com/w/Excalibur/Umbra, https://wiki.warframe.com/w/Skiajati
const umbraModA = (
await addItem(
inventory,
"/Lotus/Upgrades/Mods/Sets/Umbra/WarframeUmbraModA",
1,
false,
undefined,
`{"lvl":5}`
)
).Upgrades![0];
const umbraModB = (
await addItem(
inventory,
"/Lotus/Upgrades/Mods/Sets/Umbra/WarframeUmbraModB",
1,
false,
undefined,
`{"lvl":5}`
)
).Upgrades![0];
const umbraModC = (
await addItem(
inventory,
"/Lotus/Upgrades/Mods/Sets/Umbra/WarframeUmbraModC",
1,
false,
undefined,
`{"lvl":5}`
)
).Upgrades![0];
const sacrificeModA = (
await addItem(
inventory,
"/Lotus/Upgrades/Mods/Sets/Sacrifice/MeleeSacrificeModA",
1,
false,
undefined,
`{"lvl":5}`
)
).Upgrades![0];
const sacrificeModB = (
await addItem(
inventory,
"/Lotus/Upgrades/Mods/Sets/Sacrifice/MeleeSacrificeModB",
1,
false,
undefined,
`{"lvl":5}`
)
).Upgrades![0];
resp.InventoryChanges.Upgrades ??= [];
resp.InventoryChanges.Upgrades.push(umbraModA, umbraModB, umbraModC, sacrificeModA, sacrificeModB);
await addPowerSuit(
inventory,
"/Lotus/Powersuits/Excalibur/ExcaliburUmbra",
{
ItemType: pendingRecipe.ItemType,
ItemCount: -1
}
]);
}
if (req.query.rush) {
const end = Math.trunc(pendingRecipe.CompletionDate.getTime() / 1000);
const start = end - recipe.buildTime;
const secondsElapsed = Math.trunc(Date.now() / 1000) - start;
const progress = secondsElapsed / recipe.buildTime;
logger.debug(`rushing recipe at ${Math.trunc(progress * 100)}% completion`);
const cost =
progress > 0.5
? Math.round(recipe.skipBuildTimePrice * (1 - (progress - 0.5)))
: recipe.skipBuildTimePrice;
InventoryChanges = {
...InventoryChanges,
...updateCurrency(inventory, cost, true)
};
}
Configs: [
{
Upgrades: [
"",
"",
"",
"",
"",
umbraModA.ItemId.$oid,
umbraModB.ItemId.$oid,
umbraModC.ItemId.$oid
]
}
],
XP: 900_000,
Features: EquipmentFeatures.DOUBLE_CAPACITY
},
resp.InventoryChanges
);
inventory.XPInfo.push({
ItemType: "/Lotus/Powersuits/Excalibur/ExcaliburUmbra",
XP: 900_000
});
if (recipe.secretIngredientAction == "SIA_CREATE_KUBROW") {
const pet = inventory.KubrowPets.id(pendingRecipe.KubrowPet!)!;
if (pet.Details!.HatchDate!.getTime() > Date.now()) {
pet.Details!.HatchDate = new Date();
}
let canSetActive = true;
for (const pet of inventory.KubrowPets) {
if (pet.Details!.Status == Status.StatusAvailable) {
canSetActive = false;
break;
}
}
pet.Details!.Status = canSetActive ? Status.StatusAvailable : Status.StatusStasis;
} else if (recipe.secretIngredientAction == "SIA_DISTILL_PRINT") {
const pet = inventory.KubrowPets.id(pendingRecipe.KubrowPet!)!;
addKubrowPetPrint(inventory, pet, InventoryChanges);
} else if (recipe.secretIngredientAction != "SIA_UNBRAND") {
if (recipe.resultType == "/Lotus/Powersuits/Excalibur/ExcaliburUmbra") {
// Quite the special case here...
// We don't just get Umbra, but also Skiajati and Umbra Mods. Both items are max rank, potatoed, and with the mods are pre-installed.
// Source: https://wiki.warframe.com/w/The_Sacrifice, https://wiki.warframe.com/w/Excalibur/Umbra, https://wiki.warframe.com/w/Skiajati
const umbraModA = (
await addItem(
inventory,
"/Lotus/Upgrades/Mods/Sets/Umbra/WarframeUmbraModA",
1,
false,
undefined,
`{"lvl":5}`
)
).Upgrades![0];
const umbraModB = (
await addItem(
inventory,
"/Lotus/Upgrades/Mods/Sets/Umbra/WarframeUmbraModB",
1,
false,
undefined,
`{"lvl":5}`
)
).Upgrades![0];
const umbraModC = (
await addItem(
inventory,
"/Lotus/Upgrades/Mods/Sets/Umbra/WarframeUmbraModC",
1,
false,
undefined,
`{"lvl":5}`
)
).Upgrades![0];
const sacrificeModA = (
await addItem(
inventory,
"/Lotus/Upgrades/Mods/Sets/Sacrifice/MeleeSacrificeModA",
1,
false,
undefined,
`{"lvl":5}`
)
).Upgrades![0];
const sacrificeModB = (
await addItem(
inventory,
"/Lotus/Upgrades/Mods/Sets/Sacrifice/MeleeSacrificeModB",
1,
false,
undefined,
`{"lvl":5}`
)
).Upgrades![0];
InventoryChanges.Upgrades ??= [];
InventoryChanges.Upgrades.push(umbraModA, umbraModB, umbraModC, sacrificeModA, sacrificeModB);
await addPowerSuit(
addEquipment(
inventory,
"Melee",
"/Lotus/Weapons/Tenno/Melee/Swords/UmbraKatana/UmbraKatana",
{
Configs: [
{ Upgrades: ["", "", "", "", "", "", sacrificeModA.ItemId.$oid, sacrificeModB.ItemId.$oid] }
],
XP: 450_000,
Features: EquipmentFeatures.DOUBLE_CAPACITY
},
resp.InventoryChanges
);
inventory.XPInfo.push({
ItemType: "/Lotus/Weapons/Tenno/Melee/Swords/UmbraKatana/UmbraKatana",
XP: 450_000
});
} else {
combineInventoryChanges(
resp.InventoryChanges,
await addItem(
inventory,
"/Lotus/Powersuits/Excalibur/ExcaliburUmbra",
{
Configs: [
{
Upgrades: [
"",
"",
"",
"",
"",
umbraModA.ItemId.$oid,
umbraModB.ItemId.$oid,
umbraModC.ItemId.$oid
]
}
],
XP: 900_000,
Features: EquipmentFeatures.DOUBLE_CAPACITY
},
InventoryChanges
);
inventory.XPInfo.push({
ItemType: "/Lotus/Powersuits/Excalibur/ExcaliburUmbra",
XP: 900_000
});
addEquipment(
inventory,
"Melee",
"/Lotus/Weapons/Tenno/Melee/Swords/UmbraKatana/UmbraKatana",
{
Configs: [
{ Upgrades: ["", "", "", "", "", "", sacrificeModA.ItemId.$oid, sacrificeModB.ItemId.$oid] }
],
XP: 450_000,
Features: EquipmentFeatures.DOUBLE_CAPACITY
},
InventoryChanges
);
inventory.XPInfo.push({
ItemType: "/Lotus/Weapons/Tenno/Melee/Swords/UmbraKatana/UmbraKatana",
XP: 450_000
});
} else {
InventoryChanges = {
...InventoryChanges,
...(await addItem(
inventory,
recipe.resultType,
recipe.num,
false,
undefined,
pendingRecipe.TargetFingerprint
))
};
}
recipe.resultType,
recipe.num,
false,
undefined,
pendingRecipe.TargetFingerprint
)
);
}
if (
config.claimingBlueprintRefundsIngredients &&
recipe.secretIngredientAction != "SIA_CREATE_KUBROW" // Can't refund the egg
) {
await refundRecipeIngredients(inventory, InventoryChanges, recipe, pendingRecipe);
}
await inventory.save();
res.json({ InventoryChanges, BrandedSuits });
}
if (
inventory.claimingBlueprintRefundsIngredients &&
recipe.secretIngredientAction != "SIA_CREATE_KUBROW" // Can't refund the egg
) {
await refundRecipeIngredients(inventory, resp.InventoryChanges, recipe, pendingRecipe);
}
};

View File

@ -1,8 +1,8 @@
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { combineInventoryChanges, getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
import { RequestHandler } from "express";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import { combineInventoryChanges, getInventory } from "../../services/inventoryService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { handleStoreItemAcquisition } from "../../services/purchaseService.ts";
import type { RequestHandler } from "express";
import { ExportChallenges } from "warframe-public-export-plus";
export const claimJunctionChallengeRewardController: RequestHandler = async (req, res) => {

View File

@ -1,6 +1,6 @@
import { addFusionPoints, getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { RequestHandler } from "express";
import { addFusionPoints, getInventory } from "../../services/inventoryService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import type { RequestHandler } from "express";
export const claimLibraryDailyTaskRewardController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);

View File

@ -1,6 +1,6 @@
import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { RequestHandler } from "express";
import { getInventory } from "../../services/inventoryService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import type { RequestHandler } from "express";
export const clearDialogueHistoryController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);

View File

@ -1,4 +1,4 @@
import { RequestHandler } from "express";
import type { RequestHandler } from "express";
// example req.body: {"NewEpisodeReward":true,"crossPlaySetting":"ENABLED"}
export const clearNewEpisodeRewardController: RequestHandler = (_req, res) => {

View File

@ -1,9 +1,9 @@
import { checkCalendarAutoAdvance, getCalendarProgress, getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
import { getWorldState } from "@/src/services/worldStateService";
import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { RequestHandler } from "express";
import { checkCalendarAutoAdvance, getCalendarProgress, getInventory } from "../../services/inventoryService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { handleStoreItemAcquisition } from "../../services/purchaseService.ts";
import { getWorldState } from "../../services/worldStateService.ts";
import type { IInventoryChanges } from "../../types/purchaseTypes.ts";
import type { RequestHandler } from "express";
// GET request; query parameters: CompletedEventIdx=0&Iteration=4&Version=19&Season=CST_SUMMER
export const completeCalendarEventController: RequestHandler = async (req, res) => {

View File

@ -1,10 +1,10 @@
import { RequestHandler } from "express";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { addMiscItems, getInventory, updateCurrency } from "@/src/services/inventoryService";
import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { IVeiledRivenFingerprint } from "@/src/helpers/rivenHelper";
import type { RequestHandler } from "express";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { addMiscItems, getInventory, updateCurrency } from "../../services/inventoryService.ts";
import type { IInventoryChanges } from "../../types/purchaseTypes.ts";
import type { IMiscItem } from "../../types/inventoryTypes/inventoryTypes.ts";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import type { IVeiledRivenFingerprint } from "../../helpers/rivenHelper.ts";
export const completeRandomModChallengeController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);

View File

@ -1,7 +1,7 @@
import { Alliance, AllianceMember, Guild, GuildMember } from "@/src/models/guildModel";
import { getAllianceClient } from "@/src/services/guildService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { RequestHandler } from "express";
import { Alliance, AllianceMember, Guild, GuildMember } from "../../models/guildModel.ts";
import { getAllianceClient } from "../../services/guildService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import type { RequestHandler } from "express";
export const confirmAllianceInvitationController: RequestHandler = async (req, res) => {
// Check requester is a warlord in their guild

View File

@ -1,18 +1,18 @@
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { Guild, GuildMember } from "@/src/models/guildModel";
import { Account } from "@/src/models/loginModel";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import { Guild, GuildMember } from "../../models/guildModel.ts";
import { Account } from "../../models/loginModel.ts";
import {
deleteGuild,
getGuildClient,
giveClanKey,
hasGuildPermission,
removeDojoKeyItems
} from "@/src/services/guildService";
import { getInventory } from "@/src/services/inventoryService";
import { getAccountForRequest, getAccountIdForRequest, getSuffixedName } from "@/src/services/loginService";
import { GuildPermission } from "@/src/types/guildTypes";
import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { RequestHandler } from "express";
} from "../../services/guildService.ts";
import { getInventory } from "../../services/inventoryService.ts";
import { getAccountForRequest, getAccountIdForRequest, getSuffixedName } from "../../services/loginService.ts";
import { GuildPermission } from "../../types/guildTypes.ts";
import type { IInventoryChanges } from "../../types/purchaseTypes.ts";
import type { RequestHandler } from "express";
import { Types } from "mongoose";
// GET request: A player accepting an invite they got in their inbox.

View File

@ -1,10 +1,10 @@
import { toMongoDate } from "@/src/helpers/inventoryHelpers";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { Guild } from "@/src/models/guildModel";
import { checkClanAscensionHasRequiredContributors } from "@/src/services/guildService";
import { addFusionPoints, getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { RequestHandler } from "express";
import { toMongoDate } from "../../helpers/inventoryHelpers.ts";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import { Guild } from "../../models/guildModel.ts";
import { checkClanAscensionHasRequiredContributors } from "../../services/guildService.ts";
import { addFusionPoints, getInventory } from "../../services/inventoryService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import type { RequestHandler } from "express";
import { Types } from "mongoose";
export const contributeGuildClassController: RequestHandler = async (req, res) => {

View File

@ -1,5 +1,6 @@
import { GuildMember, TGuildDatabaseDocument } from "@/src/models/guildModel";
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
import type { TGuildDatabaseDocument } from "../../models/guildModel.ts";
import { GuildMember } from "../../models/guildModel.ts";
import type { TInventoryDatabaseDocument } from "../../models/inventoryModels/inventoryModel.ts";
import {
addGuildMemberMiscItemContribution,
getDojoClient,
@ -8,14 +9,15 @@ import {
processDojoBuildMaterialsGathered,
scaleRequiredCount,
setDojoRoomLogFunded
} from "@/src/services/guildService";
import { addMiscItems, getInventory, updateCurrency } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { IDojoContributable, IGuildMemberDatabase } from "@/src/types/guildTypes";
import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { RequestHandler } from "express";
import { ExportDojoRecipes, IDojoBuild } from "warframe-public-export-plus";
} from "../../services/guildService.ts";
import { addMiscItems, getInventory, updateCurrency } from "../../services/inventoryService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import type { IDojoContributable, IGuildMemberDatabase } from "../../types/guildTypes.ts";
import type { IMiscItem } from "../../types/inventoryTypes/inventoryTypes.ts";
import type { IInventoryChanges } from "../../types/purchaseTypes.ts";
import type { RequestHandler } from "express";
import type { IDojoBuild } from "warframe-public-export-plus";
import { ExportDojoRecipes } from "warframe-public-export-plus";
interface IContributeToDojoComponentRequest {
ComponentId: string;

View File

@ -1,10 +1,5 @@
import {
Alliance,
Guild,
GuildMember,
TGuildDatabaseDocument,
TGuildMemberDatabaseDocument
} from "@/src/models/guildModel";
import type { TGuildDatabaseDocument, TGuildMemberDatabaseDocument } from "../../models/guildModel.ts";
import { Alliance, Guild, GuildMember } from "../../models/guildModel.ts";
import {
addGuildMemberMiscItemContribution,
addGuildMemberShipDecoContribution,
@ -12,18 +7,18 @@ import {
addVaultMiscItems,
addVaultShipDecos,
getGuildForRequestEx
} from "@/src/services/guildService";
} from "../../services/guildService.ts";
import {
addFusionTreasures,
addMiscItems,
addShipDecorations,
getInventory,
updateCurrency
} from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { ITypeCount } from "@/src/types/commonTypes";
import { IFusionTreasure, IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
import { RequestHandler } from "express";
} from "../../services/inventoryService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import type { ITypeCount } from "../../types/commonTypes.ts";
import type { IFusionTreasure, IMiscItem } from "../../types/inventoryTypes/inventoryTypes.ts";
import type { RequestHandler } from "express";
export const contributeToVaultController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);

View File

@ -1,10 +1,10 @@
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { Alliance, AllianceMember, Guild, GuildMember } from "@/src/models/guildModel";
import { getAllianceClient } from "@/src/services/guildService";
import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { GuildPermission } from "@/src/types/guildTypes";
import { RequestHandler } from "express";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import { Alliance, AllianceMember, Guild, GuildMember } from "../../models/guildModel.ts";
import { getAllianceClient } from "../../services/guildService.ts";
import { getInventory } from "../../services/inventoryService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { GuildPermission } from "../../types/guildTypes.ts";
import type { RequestHandler } from "express";
export const createAllianceController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);

View File

@ -1,15 +1,27 @@
import { RequestHandler } from "express";
import { getAccountForRequest } from "@/src/services/loginService";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { Guild, GuildMember } from "@/src/models/guildModel";
import { createUniqueClanName, getGuildClient, giveClanKey } from "@/src/services/guildService";
import { getInventory } from "@/src/services/inventoryService";
import { IInventoryChanges } from "@/src/types/purchaseTypes";
import type { RequestHandler } from "express";
import { getAccountForRequest } from "../../services/loginService.ts";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import { Guild, GuildMember } from "../../models/guildModel.ts";
import { createUniqueClanName, getGuildClient, giveClanKey } from "../../services/guildService.ts";
import { getInventory } from "../../services/inventoryService.ts";
import type { IInventoryChanges } from "../../types/purchaseTypes.ts";
import { sendWsBroadcastTo } from "../../services/wsService.ts";
export const createGuildController: RequestHandler = async (req, res) => {
const account = await getAccountForRequest(req);
const payload = getJSONfromString<ICreateGuildRequest>(String(req.body));
const inventory = await getInventory(account._id.toString(), "GuildId LevelKeys Recipes");
if (inventory.GuildId) {
const guild = await Guild.findById(inventory.GuildId);
if (guild) {
res.json({
...(await getGuildClient(guild, account))
});
return;
}
}
// Remove pending applications for this account
await GuildMember.deleteMany({ accountId: account._id, status: 1 });
@ -27,7 +39,6 @@ export const createGuildController: RequestHandler = async (req, res) => {
rank: 0
});
const inventory = await getInventory(account._id.toString(), "GuildId LevelKeys Recipes");
inventory.GuildId = guild._id;
const inventoryChanges: IInventoryChanges = {};
giveClanKey(inventory, inventoryChanges);
@ -37,6 +48,7 @@ export const createGuildController: RequestHandler = async (req, res) => {
...(await getGuildClient(guild, account)),
InventoryChanges: inventoryChanges
});
sendWsBroadcastTo(account._id.toString(), { update_inventory: true });
};
interface ICreateGuildRequest {

View File

@ -1,6 +1,6 @@
import { RequestHandler } from "express";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { getInventory } from "@/src/services/inventoryService";
import type { RequestHandler } from "express";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { getInventory } from "../../services/inventoryService.ts";
export const creditsController: RequestHandler = async (req, res) => {
const inventory = (

View File

@ -1,9 +1,9 @@
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { ICrewMemberClient } from "@/src/types/inventoryTypes/inventoryTypes";
import { RequestHandler } from "express";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import type { TInventoryDatabaseDocument } from "../../models/inventoryModels/inventoryModel.ts";
import { getInventory } from "../../services/inventoryService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import type { ICrewMemberClient } from "../../types/inventoryTypes/inventoryTypes.ts";
import type { RequestHandler } from "express";
import { Types } from "mongoose";
export const crewMembersController: RequestHandler = async (req, res) => {

View File

@ -1,10 +1,11 @@
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { addMiscItems, freeUpSlot, getInventory, updateCurrency } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { IOid } from "@/src/types/commonTypes";
import { ICrewShipComponentFingerprint, InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes";
import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { RequestHandler } from "express";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import { addMiscItems, freeUpSlot, getInventory, updateCurrency } from "../../services/inventoryService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import type { IOid } from "../../types/commonTypes.ts";
import type { ICrewShipComponentFingerprint } from "../../types/inventoryTypes/inventoryTypes.ts";
import { InventorySlot } from "../../types/inventoryTypes/inventoryTypes.ts";
import type { IInventoryChanges } from "../../types/purchaseTypes.ts";
import type { RequestHandler } from "express";
import { ExportCustoms, ExportDojoRecipes } from "warframe-public-export-plus";
export const crewShipFusionController: RequestHandler = async (req, res) => {
@ -80,7 +81,7 @@ export const crewShipFusionController: RequestHandler = async (req, res) => {
const newFval = (newPerc - rangeA[0]) / (rangeA[1] - rangeA[0]);
buffA.Value = Math.trunc(newFval * 0x3fffffff);
}
if (inferiorFingerprint.SubroutineIndex) {
if (inferiorFingerprint.SubroutineIndex !== undefined) {
const useSuperiorSubroutine = tierA < tierB ? !payload.UseSubroutineA : payload.UseSubroutineA;
if (!useSuperiorSubroutine) {
fingerprint.SubroutineIndex = inferiorFingerprint.SubroutineIndex;

View File

@ -3,16 +3,19 @@ import {
addCrewShipRawSalvage,
getInventory,
addEquipment
} from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { RequestHandler } from "express";
import { ICrewShipComponentFingerprint, IInnateDamageFingerprint } from "@/src/types/inventoryTypes/inventoryTypes";
} from "../../services/inventoryService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import type { RequestHandler } from "express";
import type {
ICrewShipComponentFingerprint,
IInnateDamageFingerprint
} from "../../types/inventoryTypes/inventoryTypes.ts";
import { ExportCustoms, ExportRailjackWeapons, ExportUpgrades } from "warframe-public-export-plus";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { getRandomInt } from "@/src/services/rngService";
import { IFingerprintStat } from "@/src/helpers/rivenHelper";
import { IEquipmentDatabase } from "@/src/types/equipmentTypes";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import type { IInventoryChanges } from "../../types/purchaseTypes.ts";
import { getRandomInt } from "../../services/rngService.ts";
import type { IFingerprintStat } from "../../helpers/rivenHelper.ts";
import type { IEquipmentDatabase } from "../../types/equipmentTypes.ts";
export const crewShipIdentifySalvageController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);

View File

@ -1,11 +1,11 @@
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { Guild } from "@/src/models/guildModel";
import { hasAccessToDojo, hasGuildPermission } from "@/src/services/guildService";
import { getInventory } from "@/src/services/inventoryService";
import { getAccountForRequest, getAccountIdForRequest } from "@/src/services/loginService";
import { GuildPermission } from "@/src/types/guildTypes";
import { logger } from "@/src/utils/logger";
import { RequestHandler } from "express";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import { Guild } from "../../models/guildModel.ts";
import { hasAccessToDojo, hasGuildPermission } from "../../services/guildService.ts";
import { getInventory } from "../../services/inventoryService.ts";
import { getAccountForRequest, getAccountIdForRequest } from "../../services/loginService.ts";
import { GuildPermission } from "../../types/guildTypes.ts";
import { logger } from "../../utils/logger.ts";
import type { RequestHandler } from "express";
export const customObstacleCourseLeaderboardController: RequestHandler = async (req, res) => {
const data = getJSONfromString<ICustomObstacleCourseLeaderboardRequest>(String(req.body));

View File

@ -1,7 +1,8 @@
import { getGuildForRequest, hasGuildPermission } from "@/src/services/guildService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { GuildPermission, IGuildRank } from "@/src/types/guildTypes";
import { RequestHandler } from "express";
import { getGuildForRequest, hasGuildPermission } from "../../services/guildService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import type { IGuildRank } from "../../types/guildTypes.ts";
import { GuildPermission } from "../../types/guildTypes.ts";
import type { RequestHandler } from "express";
export const customizeGuildRanksController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);

View File

@ -1,6 +1,6 @@
import { AllianceMember, GuildMember } from "@/src/models/guildModel";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { RequestHandler } from "express";
import { AllianceMember, GuildMember } from "../../models/guildModel.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import type { RequestHandler } from "express";
export const declineAllianceInviteController: RequestHandler = async (req, res) => {
// Check requester is a warlord in their guild

View File

@ -1,6 +1,6 @@
import { GuildMember } from "@/src/models/guildModel";
import { getAccountForRequest } from "@/src/services/loginService";
import { RequestHandler } from "express";
import { GuildMember } from "../../models/guildModel.ts";
import { getAccountForRequest } from "../../services/loginService.ts";
import type { RequestHandler } from "express";
export const declineGuildInviteController: RequestHandler = async (req, res) => {
const accountId = await getAccountForRequest(req);

View File

@ -1,5 +1,5 @@
import { RequestHandler } from "express";
import { deleteSession } from "@/src/managers/sessionManager";
import type { RequestHandler } from "express";
import { deleteSession } from "../../managers/sessionManager.ts";
const deleteSessionController: RequestHandler = (_req, res) => {
deleteSession(_req.query.sessionId as string);

View File

@ -5,12 +5,12 @@ import {
hasGuildPermission,
refundDojoDeco,
removeDojoDeco
} from "@/src/services/guildService";
import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { GuildPermission } from "@/src/types/guildTypes";
import { logger } from "@/src/utils/logger";
import { RequestHandler } from "express";
} from "../../services/guildService.ts";
import { getInventory } from "../../services/inventoryService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { GuildPermission } from "../../types/guildTypes.ts";
import { logger } from "../../utils/logger.ts";
import type { RequestHandler } from "express";
export const destroyDojoDecoController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);

View File

@ -1,9 +1,9 @@
import { Alliance, AllianceMember, Guild, GuildMember } from "@/src/models/guildModel";
import { getAccountForRequest } from "@/src/services/loginService";
import { GuildPermission } from "@/src/types/guildTypes";
import { parallelForeach } from "@/src/utils/async-utils";
import { logger } from "@/src/utils/logger";
import { RequestHandler } from "express";
import { Alliance, AllianceMember, Guild, GuildMember } from "../../models/guildModel.ts";
import { getAccountForRequest } from "../../services/loginService.ts";
import { GuildPermission } from "../../types/guildTypes.ts";
import { parallelForeach } from "../../utils/async-utils.ts";
import { logger } from "../../utils/logger.ts";
import type { RequestHandler } from "express";
export const divvyAllianceVaultController: RequestHandler = async (req, res) => {
// Afaict, there's no way to put anything other than credits in the alliance vault (anymore?), so just no-op if this is not a request to divvy credits.

View File

@ -1,10 +1,17 @@
import { GuildMember, TGuildDatabaseDocument } from "@/src/models/guildModel";
import { getDojoClient, getGuildForRequestEx, hasAccessToDojo, scaleRequiredCount } from "@/src/services/guildService";
import { getInventory, updateCurrency } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { IDojoContributable } from "@/src/types/guildTypes";
import { RequestHandler } from "express";
import { ExportDojoRecipes, IDojoBuild } from "warframe-public-export-plus";
import type { TGuildDatabaseDocument } from "../../models/guildModel.ts";
import { GuildMember } from "../../models/guildModel.ts";
import {
getDojoClient,
getGuildForRequestEx,
hasAccessToDojo,
scaleRequiredCount
} from "../../services/guildService.ts";
import { getInventory, updateCurrency } from "../../services/inventoryService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import type { IDojoContributable } from "../../types/guildTypes.ts";
import type { RequestHandler } from "express";
import type { IDojoBuild } from "warframe-public-export-plus";
import { ExportDojoRecipes } from "warframe-public-export-plus";
interface IDojoComponentRushRequest {
DecoType?: string;

View File

@ -1,4 +1,4 @@
import { RequestHandler } from "express";
import type { RequestHandler } from "express";
// Arbiter Dojo endpoints, not really used by us as we don't provide a ContentURL.

View File

@ -1,13 +1,12 @@
import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers";
import { config } from "@/src/services/configService";
import { addMiscItems, getInventory } from "@/src/services/inventoryService";
import { fromStoreItem } from "@/src/services/itemDataService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { getRandomInt, getRandomWeightedRewardUc } from "@/src/services/rngService";
import { IMongoDate, IOid } from "@/src/types/commonTypes";
import { IDroneClient } from "@/src/types/inventoryTypes/inventoryTypes";
import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { RequestHandler } from "express";
import { toMongoDate, toOid } from "../../helpers/inventoryHelpers.ts";
import { addMiscItems, getInventory } from "../../services/inventoryService.ts";
import { fromStoreItem } from "../../services/itemDataService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { getRandomInt, getRandomWeightedRewardUc } from "../../services/rngService.ts";
import type { IMongoDate, IOid } from "../../types/commonTypes.ts";
import type { IDroneClient } from "../../types/inventoryTypes/inventoryTypes.ts";
import type { IInventoryChanges } from "../../types/purchaseTypes.ts";
import type { RequestHandler } from "express";
import { ExportDrones, ExportResources, ExportSystems } from "warframe-public-export-plus";
export const dronesController: RequestHandler = async (req, res) => {
@ -39,10 +38,13 @@ export const dronesController: RequestHandler = async (req, res) => {
ActiveDrones: activeDrones
});
} else if ("droneId" in req.query && "systemIndex" in req.query) {
const inventory = await getInventory(accountId, "Drones");
const inventory = await getInventory(
accountId,
"Drones instantResourceExtractorDrones noResourceExtractorDronesDamage"
);
const drone = inventory.Drones.id(req.query.droneId as string)!;
const droneMeta = ExportDrones[drone.ItemType];
drone.DeployTime = config.instantResourceExtractorDrones ? new Date(0) : new Date();
drone.DeployTime = inventory.instantResourceExtractorDrones ? new Date(0) : new Date();
if (drone.RepairStart) {
const repairMinutes = (Date.now() - drone.RepairStart.getTime()) / 60_000;
const hpPerMinute = droneMeta.repairRate / 60;
@ -51,11 +53,11 @@ export const dronesController: RequestHandler = async (req, res) => {
}
drone.System = parseInt(req.query.systemIndex as string);
const system = ExportSystems[drone.System - 1];
drone.DamageTime = config.instantResourceExtractorDrones
drone.DamageTime = inventory.instantResourceExtractorDrones
? new Date()
: new Date(Date.now() + getRandomInt(3 * 3600 * 1000, 4 * 3600 * 1000));
drone.PendingDamage =
!config.noResourceExtractorDronesDamage && Math.random() < system.damageChance
!inventory.noResourceExtractorDronesDamage && Math.random() < system.damageChance
? getRandomInt(system.droneDamage.minValue, system.droneDamage.maxValue)
: 0;
const resource = getRandomWeightedRewardUc(system.resources, droneMeta.probabilities)!;

View File

@ -1,13 +1,18 @@
import { RequestHandler } from "express";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { combineInventoryChanges, getInventory } from "@/src/services/inventoryService";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { IEndlessXpReward, IInventoryClient, TEndlessXpCategory } from "@/src/types/inventoryTypes/inventoryTypes";
import { logger } from "@/src/utils/logger";
import { ExportRewards, ICountedStoreItem } from "warframe-public-export-plus";
import { getRandomElement } from "@/src/services/rngService";
import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
import { IInventoryChanges } from "@/src/types/purchaseTypes";
import type { RequestHandler } from "express";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { combineInventoryChanges, getInventory } from "../../services/inventoryService.ts";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import type {
IEndlessXpReward,
IInventoryClient,
TEndlessXpCategory
} from "../../types/inventoryTypes/inventoryTypes.ts";
import { logger } from "../../utils/logger.ts";
import type { ICountedStoreItem } from "warframe-public-export-plus";
import { ExportRewards } from "warframe-public-export-plus";
import { getRandomElement } from "../../services/rngService.ts";
import { handleStoreItemAcquisition } from "../../services/purchaseService.ts";
import type { IInventoryChanges } from "../../types/purchaseTypes.ts";
export const endlessXpController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);

View File

@ -1,8 +1,8 @@
import { toMongoDate } from "@/src/helpers/inventoryHelpers";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { RequestHandler } from "express";
import { toMongoDate } from "../../helpers/inventoryHelpers.ts";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import { getInventory, updateEntratiVault } from "../../services/inventoryService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import type { RequestHandler } from "express";
export const entratiLabConquestModeController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
@ -11,26 +11,7 @@ export const entratiLabConquestModeController: RequestHandler = async (req, res)
"EntratiVaultCountResetDate EntratiVaultCountLastPeriod EntratiLabConquestUnlocked EchoesHexConquestUnlocked EchoesHexConquestActiveFrameVariants EchoesHexConquestActiveStickers EntratiLabConquestActiveFrameVariants EntratiLabConquestCacheScoreMission EchoesHexConquestCacheScoreMission"
);
const body = getJSONfromString<IEntratiLabConquestModeRequest>(String(req.body));
if (!inventory.EntratiVaultCountResetDate || Date.now() >= inventory.EntratiVaultCountResetDate.getTime()) {
const EPOCH = 1734307200 * 1000; // Mondays, amirite?
const day = Math.trunc((Date.now() - EPOCH) / 86400000);
const week = Math.trunc(day / 7);
const weekStart = EPOCH + week * 604800000;
const weekEnd = weekStart + 604800000;
inventory.EntratiVaultCountLastPeriod = 0;
inventory.EntratiVaultCountResetDate = new Date(weekEnd);
if (inventory.EntratiLabConquestUnlocked) {
inventory.EntratiLabConquestUnlocked = 0;
inventory.EntratiLabConquestCacheScoreMission = 0;
inventory.EntratiLabConquestActiveFrameVariants = [];
}
if (inventory.EchoesHexConquestUnlocked) {
inventory.EchoesHexConquestUnlocked = 0;
inventory.EchoesHexConquestCacheScoreMission = 0;
inventory.EchoesHexConquestActiveFrameVariants = [];
inventory.EchoesHexConquestActiveStickers = [];
}
}
updateEntratiVault(inventory);
if (body.BuyMode) {
inventory.EntratiVaultCountLastPeriod! += 2;
if (body.IsEchoesDeepArchemedea) {
@ -51,7 +32,7 @@ export const entratiLabConquestModeController: RequestHandler = async (req, res)
}
await inventory.save();
res.json({
EntratiVaultCountResetDate: toMongoDate(inventory.EntratiVaultCountResetDate),
EntratiVaultCountResetDate: toMongoDate(inventory.EntratiVaultCountResetDate!),
EntratiVaultCountLastPeriod: inventory.EntratiVaultCountLastPeriod,
EntratiLabConquestUnlocked: inventory.EntratiLabConquestUnlocked,
EntratiLabConquestCacheScoreMission: inventory.EntratiLabConquestCacheScoreMission,

View File

@ -1,9 +1,10 @@
import { RequestHandler } from "express";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { addMiscItems, getInventory } from "@/src/services/inventoryService";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { getRecipe, WeaponTypeInternal } from "@/src/services/itemDataService";
import { EquipmentFeatures } from "@/src/types/equipmentTypes";
import type { RequestHandler } from "express";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { addMiscItems, getInventory } from "../../services/inventoryService.ts";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import type { WeaponTypeInternal } from "../../services/itemDataService.ts";
import { getRecipe } from "../../services/itemDataService.ts";
import { EquipmentFeatures } from "../../types/equipmentTypes.ts";
export const evolveWeaponController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);

View File

@ -0,0 +1,62 @@
import type { RequestHandler } from "express";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { addMiscItem, getInventory } from "../../services/inventoryService.ts";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import { logger } from "../../utils/logger.ts";
import type { IInventoryChanges } from "../../types/purchaseTypes.ts";
export const feedPrinceController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const inventory = await getInventory(accountId, "MiscItems NokkoColony NodeIntrosCompleted");
const payload = getJSONfromString<IFeedPrinceRequest>(String(req.body));
switch (payload.Mode) {
case "r": {
inventory.NokkoColony ??= {
FeedLevel: 0,
JournalEntries: []
};
const InventoryChanges: IInventoryChanges = {};
inventory.NokkoColony.FeedLevel += payload.Amount;
if (
(!inventory.NodeIntrosCompleted.includes("CompletedVision1") && inventory.NokkoColony.FeedLevel > 20) ||
(!inventory.NodeIntrosCompleted.includes("CompletedVision2") && inventory.NokkoColony.FeedLevel > 60)
) {
res.json({
FeedSucceeded: false,
FeedLevel: inventory.NokkoColony.FeedLevel - payload.Amount,
InventoryChanges
} satisfies IFeedPrinceResponse);
} else {
addMiscItem(
inventory,
"/Lotus/Types/Items/MiscItems/MushroomFood",
payload.Amount * -1,
InventoryChanges
);
await inventory.save();
res.json({
FeedSucceeded: true,
FeedLevel: inventory.NokkoColony.FeedLevel,
InventoryChanges
} satisfies IFeedPrinceResponse);
}
break;
}
default:
logger.debug(`data provided to ${req.path}: ${String(req.body)}`);
throw new Error(`unknown feedPrince mode: ${payload.Mode}`);
}
};
interface IFeedPrinceRequest {
Mode: string; // r
Amount: number;
}
interface IFeedPrinceResponse {
FeedSucceeded: boolean;
FeedLevel: number;
InventoryChanges: IInventoryChanges;
}

View File

@ -1,7 +1,7 @@
import { RequestHandler } from "express";
import { getSession } from "@/src/managers/sessionManager";
import { logger } from "@/src/utils/logger";
import { IFindSessionRequest } from "@/src/types/session";
import type { RequestHandler } from "express";
import { getSession } from "../../managers/sessionManager.ts";
import { logger } from "../../utils/logger.ts";
import type { IFindSessionRequest } from "../../types/session.ts";
export const findSessionsController: RequestHandler = (_req, res) => {
const req = JSON.parse(String(_req.body)) as IFindSessionRequest;

View File

@ -1,8 +1,8 @@
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { addMiscItems, addStanding, getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
import { RequestHandler } from "express";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import { addMiscItems, addStanding, getInventory } from "../../services/inventoryService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import type { IMiscItem } from "../../types/inventoryTypes/inventoryTypes.ts";
import type { RequestHandler } from "express";
import { ExportResources } from "warframe-public-export-plus";
export const fishmongerController: RequestHandler = async (req, res) => {

View File

@ -1,22 +1,91 @@
import { RequestHandler } from "express";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { getInventory, addMiscItems, addEquipment, occupySlot } from "@/src/services/inventoryService";
import { IMiscItem, TFocusPolarity, TEquipmentKey, InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes";
import { logger } from "@/src/utils/logger";
import type { RequestHandler } from "express";
import { getAccountForRequest } from "../../services/loginService.ts";
import { getInventory, addMiscItems, addEquipment, occupySlot } from "../../services/inventoryService.ts";
import type { IMiscItem, TFocusPolarity, TEquipmentKey } from "../../types/inventoryTypes/inventoryTypes.ts";
import { InventorySlot } from "../../types/inventoryTypes/inventoryTypes.ts";
import { logger } from "../../utils/logger.ts";
import { ExportFocusUpgrades } from "warframe-public-export-plus";
import { Inventory } from "@/src/models/inventoryModels/inventoryModel";
import { Inventory } from "../../models/inventoryModels/inventoryModel.ts";
import { version_compare } from "../../helpers/inventoryHelpers.ts";
export const focusController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
switch (req.query.op) {
const account = await getAccountForRequest(req);
let op = req.query.op as string;
const focus2 = account.BuildLabel && version_compare(account.BuildLabel, "2022.04.29.12.53") < 0;
if (focus2) {
// Focus 2.0
switch (req.query.op) {
case Focus2Operation.InstallLens:
op = "InstallLens";
break;
case Focus2Operation.UnlockWay:
op = "UnlockWay";
break;
case Focus2Operation.UnlockUpgrade:
op = "UnlockUpgrade";
break;
case Focus2Operation.IncreasePool:
op = "IncreasePool";
break;
case Focus2Operation.LevelUpUpgrade:
op = "LevelUpUpgrade";
break;
case Focus2Operation.ActivateWay:
op = "ActivateWay";
break;
case Focus2Operation.UpdateUpgrade:
op = "UpdateUpgrade";
break;
case Focus2Operation.SentTrainingAmplifier:
op = "SentTrainingAmplifier";
break;
case Focus2Operation.UnbindUpgrade:
op = "UnbindUpgrade";
break;
case Focus2Operation.ConvertShard:
op = "ConvertShard";
break;
}
} else {
// Focus 3.0
switch (req.query.op) {
case Focus3Operation.InstallLens:
op = "InstallLens";
break;
case Focus3Operation.UnlockWay:
op = "UnlockWay";
break;
case Focus3Operation.UnlockUpgrade:
op = "UnlockUpgrade";
break;
case Focus3Operation.LevelUpUpgrade:
op = "LevelUpUpgrade";
break;
case Focus3Operation.ActivateWay:
op = "ActivateWay";
break;
case Focus3Operation.SentTrainingAmplifier:
op = "SentTrainingAmplifier";
break;
case Focus3Operation.UnbindUpgrade:
op = "UnbindUpgrade";
break;
case Focus3Operation.ConvertShard:
op = "ConvertShard";
break;
}
}
switch (op) {
default:
logger.error("Unhandled focus op type: " + String(req.query.op));
logger.debug(String(req.body));
res.end();
break;
case FocusOperation.InstallLens: {
case "InstallLens": {
const request = JSON.parse(String(req.body)) as ILensInstallRequest;
const inventory = await getInventory(accountId);
const inventory = await getInventory(account._id.toString());
const item = inventory[request.Category].id(request.WeaponId);
if (item) {
item.FocusLens = request.LensType;
@ -34,15 +103,15 @@ export const focusController: RequestHandler = async (req, res) => {
});
break;
}
case FocusOperation.UnlockWay: {
case "UnlockWay": {
const focusType = (JSON.parse(String(req.body)) as IWayRequest).FocusType;
const focusPolarity = focusTypeToPolarity(focusType);
const inventory = await getInventory(accountId);
const inventory = await getInventory(account._id.toString(), "FocusAbility FocusUpgrades FocusXP");
const cost = inventory.FocusAbility ? 50_000 : 0;
inventory.FocusAbility ??= focusType;
inventory.FocusUpgrades.push({ ItemType: focusType });
if (inventory.FocusXP) {
inventory.FocusXP[focusPolarity]! -= cost;
if (cost) {
inventory.FocusXP![focusPolarity]! -= cost;
}
await inventory.save();
res.json({
@ -51,12 +120,29 @@ export const focusController: RequestHandler = async (req, res) => {
});
break;
}
case FocusOperation.ActivateWay: {
case "IncreasePool": {
const request = JSON.parse(String(req.body)) as IIncreasePoolRequest;
const focusPolarity = focusTypeToPolarity(request.FocusType);
const inventory = await getInventory(account._id.toString(), "FocusXP FocusCapacity");
let cost = 0;
for (let capacity = request.CurrentTotalCapacity; capacity != request.NewTotalCapacity; ++capacity) {
cost += increasePoolCost[capacity - 5];
}
inventory.FocusXP![focusPolarity]! -= cost;
inventory.FocusCapacity = request.NewTotalCapacity;
await inventory.save();
res.json({
TotalCapacity: request.NewTotalCapacity,
FocusPointCosts: { [focusPolarity]: cost }
});
break;
}
case "ActivateWay": {
const focusType = (JSON.parse(String(req.body)) as IWayRequest).FocusType;
await Inventory.updateOne(
{
accountOwnerId: accountId
accountOwnerId: account._id.toString()
},
{
FocusAbility: focusType
@ -68,13 +154,20 @@ export const focusController: RequestHandler = async (req, res) => {
});
break;
}
case FocusOperation.UnlockUpgrade: {
case "UnlockUpgrade": {
const request = JSON.parse(String(req.body)) as IUnlockUpgradeRequest;
const focusPolarity = focusTypeToPolarity(request.FocusTypes[0]);
const inventory = await getInventory(accountId);
const inventory = await getInventory(account._id.toString());
let cost = 0;
for (const focusType of request.FocusTypes) {
cost += ExportFocusUpgrades[focusType].baseFocusPointCost;
if (focusType in ExportFocusUpgrades) {
cost += ExportFocusUpgrades[focusType].baseFocusPointCost;
} else if (focusType == "/Lotus/Upgrades/Focus/Power/Residual/ChannelEfficiencyFocusUpgrade") {
// Zenurik's Inner Might (Focus 2.0)
cost += 50_000;
} else {
logger.warn(`unknown focus upgrade ${focusType}, will unlock it for free`);
}
inventory.FocusUpgrades.push({ ItemType: focusType, Level: 0 });
}
inventory.FocusXP![focusPolarity]! -= cost;
@ -85,15 +178,20 @@ export const focusController: RequestHandler = async (req, res) => {
});
break;
}
case FocusOperation.LevelUpUpgrade: {
case "LevelUpUpgrade":
case "UpdateUpgrade": {
const request = JSON.parse(String(req.body)) as ILevelUpUpgradeRequest;
const focusPolarity = focusTypeToPolarity(request.FocusInfos[0].ItemType);
const inventory = await getInventory(accountId);
const inventory = await getInventory(account._id.toString());
let cost = 0;
for (const focusUpgrade of request.FocusInfos) {
cost += focusUpgrade.FocusXpCost;
const focusUpgradeDb = inventory.FocusUpgrades.find(entry => entry.ItemType == focusUpgrade.ItemType)!;
focusUpgradeDb.Level = focusUpgrade.Level;
if (op == "UpdateUpgrade") {
focusUpgradeDb.IsActive = focusUpgrade.IsActive;
} else {
focusUpgradeDb.Level = focusUpgrade.Level;
}
}
inventory.FocusXP![focusPolarity]! -= cost;
await inventory.save();
@ -103,9 +201,9 @@ export const focusController: RequestHandler = async (req, res) => {
});
break;
}
case FocusOperation.SentTrainingAmplifier: {
case "SentTrainingAmplifier": {
const request = JSON.parse(String(req.body)) as ISentTrainingAmplifierRequest;
const inventory = await getInventory(accountId);
const inventory = await getInventory(account._id.toString());
const inventoryChanges = addEquipment(inventory, "OperatorAmps", request.StartingWeaponType, {
ModularParts: [
"/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/SentAmpTrainingGrip",
@ -118,10 +216,10 @@ export const focusController: RequestHandler = async (req, res) => {
res.json(inventoryChanges.OperatorAmps![0]);
break;
}
case FocusOperation.UnbindUpgrade: {
case "UnbindUpgrade": {
const request = JSON.parse(String(req.body)) as IUnbindUpgradeRequest;
const focusPolarity = focusTypeToPolarity(request.FocusTypes[0]);
const inventory = await getInventory(accountId);
const inventory = await getInventory(account._id.toString());
inventory.FocusXP![focusPolarity]! -= 750_000 * request.FocusTypes.length;
addMiscItems(inventory, [
{
@ -148,7 +246,7 @@ export const focusController: RequestHandler = async (req, res) => {
});
break;
}
case FocusOperation.ConvertShard: {
case "ConvertShard": {
const request = JSON.parse(String(req.body)) as IConvertShardRequest;
// Tally XP
let xp = 0;
@ -166,7 +264,7 @@ export const focusController: RequestHandler = async (req, res) => {
for (const shard of request.Shards) {
shard.ItemCount *= -1;
}
const inventory = await getInventory(accountId);
const inventory = await getInventory(account._id.toString());
const polarity = request.Polarity;
inventory.FocusXP ??= {};
inventory.FocusXP[polarity] ??= 0;
@ -178,7 +276,8 @@ export const focusController: RequestHandler = async (req, res) => {
}
};
enum FocusOperation {
// Focus 3.0
enum Focus3Operation {
InstallLens = "1",
UnlockWay = "2",
UnlockUpgrade = "3",
@ -189,6 +288,20 @@ enum FocusOperation {
ConvertShard = "9"
}
// Focus 2.0
enum Focus2Operation {
InstallLens = "1",
UnlockWay = "2",
UnlockUpgrade = "3",
IncreasePool = "4",
LevelUpUpgrade = "5",
ActivateWay = "6",
UpdateUpgrade = "7", // used to change the IsActive state, same format as ILevelUpUpgradeRequest
SentTrainingAmplifier = "9",
UnbindUpgrade = "10",
ConvertShard = "11"
}
// For UnlockWay & ActivateWay
interface IWayRequest {
FocusType: string;
@ -198,6 +311,13 @@ interface IUnlockUpgradeRequest {
FocusTypes: string[];
}
// Focus 2.0
interface IIncreasePoolRequest {
FocusType: string;
CurrentTotalCapacity: number;
NewTotalCapacity: number;
}
interface ILevelUpUpgradeRequest {
FocusInfos: {
ItemType: string;
@ -205,6 +325,7 @@ interface ILevelUpUpgradeRequest {
IsUniversal: boolean;
Level: number;
IsActiveAbility: boolean;
IsActive?: number; // Focus 2.0
}[];
}
@ -230,7 +351,7 @@ interface ILensInstallRequest {
// Works for ways & upgrades
const focusTypeToPolarity = (type: string): TFocusPolarity => {
return ("AP_" + type.substr(1).split("/")[3].toUpperCase()) as TFocusPolarity;
return ("AP_" + type.substring(1).split("/")[3].toUpperCase()) as TFocusPolarity;
};
const shardValues = {
@ -239,3 +360,19 @@ const shardValues = {
"/Lotus/Types/Gameplay/Eidolon/Resources/SentientShards/SentientShardBrilliantItem": 25_000,
"/Lotus/Types/Gameplay/Eidolon/Resources/SentientShards/SentientShardBrilliantTierTwoItem": 40_000
};
// Starting at a capacity of 5 (Source: https://wiki.warframe.com/w/Focus_2.0)
const increasePoolCost = [
2576, 3099, 3638, 4190, 4755, 5331, 5918, 6514, 7120, 7734, 8357, 8988, 9626, 10271, 10923, 11582, 12247, 12918,
13595, 14277, 14965, 15659, 16357, 17061, 17769, 18482, 19200, 19922, 20649, 21380, 22115, 22854, 23597, 24344,
25095, 25850, 26609, 27371, 28136, 28905, 29678, 30454, 31233, 32015, 32801, 33590, 34382, 35176, 35974, 36775,
37579, 38386, 39195, 40008, 40823, 41641, 42461, 43284, 44110, 44938, 45769, 46603, 47439, 48277, 49118, 49961,
50807, 51655, 52505, 53357, 54212, 55069, 55929, 56790, 57654, 58520, 59388, 60258, 61130, 62005, 62881, 63759,
64640, 65522, 66407, 67293, 68182, 69072, 69964, 70858, 71754, 72652, 73552, 74453, 75357, 76262, 77169, 78078,
78988, 79900, 80814, 81730, 82648, 83567, 84488, 85410, 86334, 87260, 88188, 89117, 90047, 90980, 91914, 92849,
93786, 94725, 95665, 96607, 97550, 98495, 99441, 100389, 101338, 102289, 103241, 104195, 105150, 106107, 107065,
108024, 108985, 109948, 110911, 111877, 112843, 113811, 114780, 115751, 116723, 117696, 118671, 119647, 120624,
121603, 122583, 123564, 124547, 125531, 126516, 127503, 128490, 129479, 130470, 131461, 132454, 133448, 134443,
135440, 136438, 137437, 138437, 139438, 140441, 141444, 142449, 143455, 144463, 145471, 146481, 147492, 148503,
149517
];

View File

@ -0,0 +1,27 @@
import type { RequestHandler } from "express";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import { getInventory } from "../../services/inventoryService.ts";
import type { IInventoryChanges } from "../../types/purchaseTypes.ts";
export const forceRemoveItemController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const inventory = await getInventory(accountId, "MiscItems");
const body = getJSONfromString<IForceRemoveItemRequest>(String(req.body));
const inventoryChanges: IInventoryChanges = {};
for (const item of body.items) {
const index = inventory.MiscItems.findIndex(x => x.ItemType == item);
if (index != -1) {
inventoryChanges.MiscItems ??= [];
inventoryChanges.MiscItems.push({ ItemType: item, ItemCount: inventory.MiscItems[index].ItemCount * -1 });
inventory.MiscItems.splice(index, 1);
}
}
await inventory.save();
res.json({ InventoryChanges: inventoryChanges });
};
interface IForceRemoveItemRequest {
items: string[];
}

View File

@ -1,9 +1,9 @@
import { RequestHandler } from "express";
import type { RequestHandler } from "express";
import { ExportResources } from "warframe-public-export-plus";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { addFusionTreasures, addMiscItems, getInventory } from "@/src/services/inventoryService";
import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
import { parseFusionTreasure } from "@/src/helpers/inventoryHelpers";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { addFusionTreasures, addMiscItems, getInventory } from "../../services/inventoryService.ts";
import type { IMiscItem } from "../../types/inventoryTypes/inventoryTypes.ts";
import { parseFusionTreasure } from "../../helpers/inventoryHelpers.ts";
interface IFusionTreasureRequest {
oldTreasureName: string;

View File

@ -1,14 +1,14 @@
import { toMongoDate } from "@/src/helpers/inventoryHelpers";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { addMiscItem, getInventory } from "@/src/services/inventoryService";
import { toStoreItem } from "@/src/services/itemDataService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { createGarden, getPersonalRooms } from "@/src/services/personalRoomsService";
import { IMongoDate } from "@/src/types/commonTypes";
import { IMissionReward } from "@/src/types/missionTypes";
import { IGardeningClient, IPersonalRoomsClient } from "@/src/types/personalRoomsTypes";
import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { RequestHandler } from "express";
import { toMongoDate } from "../../helpers/inventoryHelpers.ts";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import { addMiscItem, getInventory } from "../../services/inventoryService.ts";
import { toStoreItem } from "../../services/itemDataService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { createGarden, getPersonalRooms } from "../../services/personalRoomsService.ts";
import type { IMongoDate } from "../../types/commonTypes.ts";
import type { IMissionReward } from "../../types/missionTypes.ts";
import type { IGardeningClient, IPersonalRoomsClient } from "../../types/personalRoomsTypes.ts";
import type { IInventoryChanges } from "../../types/purchaseTypes.ts";
import type { RequestHandler } from "express";
import { dict_en, ExportResources } from "warframe-public-export-plus";
export const gardeningController: RequestHandler = async (req, res) => {

View File

@ -1,8 +1,8 @@
import { getAccountIdForRequest } from "@/src/services/loginService";
import { updateGeneric } from "@/src/services/inventoryService";
import { RequestHandler } from "express";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { IGenericUpdate } from "@/src/types/genericUpdate";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { updateGeneric } from "../../services/inventoryService.ts";
import type { RequestHandler } from "express";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import type { IGenericUpdate } from "../../types/genericUpdate.ts";
// This endpoint used to be /api/genericUpdate.php, but sometime around the Jade Shadows update, it was changed to /api/updateNodeIntros.php.
// SpaceNinjaServer supports both endpoints right now.

View File

@ -1,8 +1,8 @@
import { Alliance, Guild } from "@/src/models/guildModel";
import { getAllianceClient } from "@/src/services/guildService";
import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { RequestHandler } from "express";
import { Alliance, Guild } from "../../models/guildModel.ts";
import { getAllianceClient } from "../../services/guildService.ts";
import { getInventory } from "../../services/inventoryService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import type { RequestHandler } from "express";
export const getAllianceController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);

View File

@ -1,5 +1,5 @@
import { DailyDeal } from "@/src/models/worldStateModel";
import { RequestHandler } from "express";
import { DailyDeal } from "../../models/worldStateModel.ts";
import type { RequestHandler } from "express";
export const getDailyDealStockLevelsController: RequestHandler = async (req, res) => {
const dailyDeal = (await DailyDeal.findOne({ StoreItem: req.query.productName }, "AmountSold"))!;

View File

@ -1,9 +1,9 @@
import { toOid } from "@/src/helpers/inventoryHelpers";
import { Friendship } from "@/src/models/friendModel";
import { addAccountDataToFriendInfo, addInventoryDataToFriendInfo } from "@/src/services/friendService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { IFriendInfo } from "@/src/types/friendTypes";
import { Request, RequestHandler, Response } from "express";
import { toOid } from "../../helpers/inventoryHelpers.ts";
import { Friendship } from "../../models/friendModel.ts";
import { addAccountDataToFriendInfo, addInventoryDataToFriendInfo } from "../../services/friendService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import type { IFriendInfo } from "../../types/friendTypes.ts";
import type { Request, RequestHandler, Response } from "express";
// POST with {} instead of GET as of 38.5.0
export const getFriendsController: RequestHandler = async (req: Request, res: Response) => {

View File

@ -1,8 +1,8 @@
import { GuildMember } from "@/src/models/guildModel";
import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { IGuildMemberClient } from "@/src/types/guildTypes";
import { RequestHandler } from "express";
import { GuildMember } from "../../models/guildModel.ts";
import { getInventory } from "../../services/inventoryService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import type { IGuildMemberClient } from "../../types/guildTypes.ts";
import type { RequestHandler } from "express";
export const getGuildContributionsController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);

View File

@ -1,9 +1,9 @@
import { RequestHandler } from "express";
import { Guild } from "@/src/models/guildModel";
import { getAccountForRequest } from "@/src/services/loginService";
import { logger } from "@/src/utils/logger";
import { getInventory } from "@/src/services/inventoryService";
import { createUniqueClanName, getGuildClient } from "@/src/services/guildService";
import type { RequestHandler } from "express";
import { Guild } from "../../models/guildModel.ts";
import { getAccountForRequest } from "../../services/loginService.ts";
import { logger } from "../../utils/logger.ts";
import { getInventory } from "../../services/inventoryService.ts";
import { createUniqueClanName, getGuildClient } from "../../services/guildService.ts";
export const getGuildController: RequestHandler = async (req, res) => {
const account = await getAccountForRequest(req);

View File

@ -1,8 +1,8 @@
import { RequestHandler } from "express";
import type { RequestHandler } from "express";
import { Types } from "mongoose";
import { Guild } from "@/src/models/guildModel";
import { getDojoClient } from "@/src/services/guildService";
import { Account } from "@/src/models/loginModel";
import { Guild } from "../../models/guildModel.ts";
import { getDojoClient } from "../../services/guildService.ts";
import { Account } from "../../models/loginModel.ts";
export const getGuildDojoController: RequestHandler = async (req, res) => {
const guildId = req.query.guildId as string;
@ -19,7 +19,7 @@ export const getGuildDojoController: RequestHandler = async (req, res) => {
_id: new Types.ObjectId(),
pf: "/Lotus/Levels/ClanDojo/DojoHall.level",
ppf: "",
CompletionTime: new Date(Date.now()),
CompletionTime: new Date(Date.now() - 1000),
DecoCapacity: 600
});
await guild.save();

View File

@ -1,7 +1,7 @@
import { RequestHandler } from "express";
import { getAccountForRequest } from "@/src/services/loginService";
import { getInventory } from "@/src/services/inventoryService";
import { Guild } from "@/src/models/guildModel";
import type { RequestHandler } from "express";
import { getAccountForRequest } from "../../services/loginService.ts";
import { getInventory } from "../../services/inventoryService.ts";
import { Guild } from "../../models/guildModel.ts";
export const getGuildEventScoreController: RequestHandler = async (req, res) => {
const account = await getAccountForRequest(req);

View File

@ -1,9 +1,9 @@
import { toMongoDate } from "@/src/helpers/inventoryHelpers";
import { Guild } from "@/src/models/guildModel";
import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { IMongoDate } from "@/src/types/commonTypes";
import { RequestHandler } from "express";
import { toMongoDate } from "../../helpers/inventoryHelpers.ts";
import { Guild } from "../../models/guildModel.ts";
import { getInventory } from "../../services/inventoryService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import type { IMongoDate } from "../../types/commonTypes.ts";
import type { RequestHandler } from "express";
export const getGuildLogController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);

View File

@ -1,9 +1,9 @@
import { toOid } from "@/src/helpers/inventoryHelpers";
import { Account, Ignore } from "@/src/models/loginModel";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { IFriendInfo } from "@/src/types/friendTypes";
import { parallelForeach } from "@/src/utils/async-utils";
import { RequestHandler } from "express";
import { toOid } from "../../helpers/inventoryHelpers.ts";
import { Account, Ignore } from "../../models/loginModel.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import type { IFriendInfo } from "../../types/friendTypes.ts";
import { parallelForeach } from "../../utils/async-utils.ts";
import type { RequestHandler } from "express";
export const getIgnoredUsersController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);

View File

@ -1,7 +1,7 @@
import { Inventory } from "@/src/models/inventoryModels/inventoryModel";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { generateRewardSeed } from "@/src/services/rngService";
import { RequestHandler } from "express";
import { Inventory } from "../../models/inventoryModels/inventoryModel.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { generateRewardSeed } from "../../services/rngService.ts";
import type { RequestHandler } from "express";
export const getNewRewardSeedController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);

View File

@ -1,9 +1,9 @@
import { RequestHandler } from "express";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { getInventory } from "@/src/services/inventoryService";
import { EPOCH, getSeasonChallengePools, getWorldState, pushWeeklyActs } from "@/src/services/worldStateService";
import { unixTimesInMs } from "@/src/constants/timeConstants";
import { ISeasonChallenge } from "@/src/types/worldStateTypes";
import type { RequestHandler } from "express";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { getInventory } from "../../services/inventoryService.ts";
import { getSeasonChallengePools, getWorldState, pushWeeklyActs } from "../../services/worldStateService.ts";
import { EPOCH, unixTimesInMs } from "../../constants/timeConstants.ts";
import type { ISeasonChallenge } from "../../types/worldStateTypes.ts";
import { ExportChallenges } from "warframe-public-export-plus";
export const getPastWeeklyChallengesController: RequestHandler = async (req, res) => {

View File

@ -1,11 +1,9 @@
import { RequestHandler } from "express";
import { config } from "@/src/services/configService";
import allShipFeatures from "@/static/fixed_responses/allShipFeatures.json";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { createGarden, getPersonalRooms } from "@/src/services/personalRoomsService";
import { IGetShipResponse, IPersonalRoomsClient } from "@/src/types/personalRoomsTypes";
import { getLoadout } from "@/src/services/loadoutService";
import { toOid } from "@/src/helpers/inventoryHelpers";
import type { RequestHandler } from "express";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { createGarden, getPersonalRooms } from "../../services/personalRoomsService.ts";
import type { IGetShipResponse, IPersonalRoomsClient } from "../../types/personalRoomsTypes.ts";
import { getLoadout } from "../../services/loadoutService.ts";
import { toOid } from "../../helpers/inventoryHelpers.ts";
export const getShipController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
@ -31,9 +29,5 @@ export const getShipController: RequestHandler = async (req, res) => {
TailorShop: personalRooms.TailorShop
};
if (config.unlockAllShipFeatures) {
getShipResponse.Ship.Features = allShipFeatures;
}
res.json(getShipResponse);
};

View File

@ -1,8 +1,8 @@
import { RequestHandler } from "express";
import { applyStandingToVendorManifest, getVendorManifestByTypeName } from "@/src/services/serversideVendorsService";
import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { config } from "@/src/services/configService";
import type { RequestHandler } from "express";
import { applyStandingToVendorManifest, getVendorManifestByTypeName } from "../../services/serversideVendorsService.ts";
import { getInventory } from "../../services/inventoryService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { config } from "../../services/configService.ts";
export const getVendorInfoController: RequestHandler = async (req, res) => {
let manifest = getVendorManifestByTypeName(req.query.vendor as string);

View File

@ -1,9 +1,9 @@
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { crackRelic } from "@/src/helpers/relicHelper";
import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { IVoidTearParticipantInfo } from "@/src/types/requestTypes";
import { RequestHandler } from "express";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import { crackRelic } from "../../helpers/relicHelper.ts";
import { getInventory } from "../../services/inventoryService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import type { IVoidTearParticipantInfo } from "../../types/requestTypes.ts";
import type { RequestHandler } from "express";
export const getVoidProjectionRewardsController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
@ -11,7 +11,11 @@ export const getVoidProjectionRewardsController: RequestHandler = async (req, re
if (data.ParticipantInfo.QualifiesForReward && !data.ParticipantInfo.HaveRewardResponse) {
const inventory = await getInventory(accountId);
await crackRelic(inventory, data.ParticipantInfo);
const reward = await crackRelic(inventory, data.ParticipantInfo);
if (!inventory.MissionRelicRewards || inventory.MissionRelicRewards.length >= data.CurrentWave) {
inventory.MissionRelicRewards = [];
}
inventory.MissionRelicRewards.push({ ItemType: reward.type, ItemCount: reward.itemCount });
await inventory.save();
}

View File

@ -1,18 +1,19 @@
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { Account } from "@/src/models/loginModel";
import { areFriends } from "@/src/services/friendService";
import { createMessage } from "@/src/services/inboxService";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import { Account } from "../../models/loginModel.ts";
import { areFriends } from "../../services/friendService.ts";
import { createMessage } from "../../services/inboxService.ts";
import {
combineInventoryChanges,
getEffectiveAvatarImageType,
getInventory,
updateCurrency
} from "@/src/services/inventoryService";
import { getAccountForRequest, getSuffixedName } from "@/src/services/loginService";
import { handleDailyDealPurchase, handleStoreItemAcquisition } from "@/src/services/purchaseService";
import { IOid } from "@/src/types/commonTypes";
import { IPurchaseParams, IPurchaseResponse, PurchaseSource } from "@/src/types/purchaseTypes";
import { RequestHandler } from "express";
} from "../../services/inventoryService.ts";
import { getAccountForRequest, getSuffixedName } from "../../services/loginService.ts";
import { handleDailyDealPurchase, handleStoreItemAcquisition } from "../../services/purchaseService.ts";
import type { IOid } from "../../types/commonTypes.ts";
import type { IPurchaseParams, IPurchaseResponse } from "../../types/purchaseTypes.ts";
import { PurchaseSource } from "../../types/purchaseTypes.ts";
import type { RequestHandler } from "express";
import { ExportBundles, ExportFlavour } from "warframe-public-export-plus";
const checkPurchaseParams = (params: IPurchaseParams): boolean => {

View File

@ -1,13 +1,14 @@
import { RequestHandler } from "express";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { sendWsBroadcastTo } from "@/src/services/wsService";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { addMiscItems, getInventory } from "@/src/services/inventoryService";
import { TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
import { ArtifactPolarity } from "@/src/types/inventoryTypes/commonInventoryTypes";
import type { RequestHandler } from "express";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { broadcastInventoryUpdate } from "../../services/wsService.ts";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import { addMiscItems, getInventory } from "../../services/inventoryService.ts";
import type { TEquipmentKey } from "../../types/inventoryTypes/inventoryTypes.ts";
import type { ArtifactPolarity } from "../../types/inventoryTypes/commonInventoryTypes.ts";
import { ExportRecipes } from "warframe-public-export-plus";
import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { EquipmentFeatures, IEquipmentClient } from "@/src/types/equipmentTypes";
import type { IInventoryChanges } from "../../types/purchaseTypes.ts";
import type { IEquipmentClient } from "../../types/equipmentTypes.ts";
import { EquipmentFeatures } from "../../types/equipmentTypes.ts";
interface IGildWeaponRequest {
ItemName: string;
@ -74,5 +75,5 @@ export const gildWeaponController: RequestHandler = async (req, res) => {
InventoryChanges: inventoryChanges,
AffiliationMods: affiliationMods
});
sendWsBroadcastTo(accountId, { update_inventory: true });
broadcastInventoryUpdate(req);
};

View File

@ -1,16 +1,17 @@
import { RequestHandler } from "express";
import { parseString } from "@/src/helpers/general";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { getInventory } from "@/src/services/inventoryService";
import { giveKeyChainItem } from "@/src/services/questService";
import { IKeyChainRequest } from "@/src/types/requestTypes";
import type { RequestHandler } from "express";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import { getInventory } from "../../services/inventoryService.ts";
import { giveKeyChainItem } from "../../services/questService.ts";
import type { IKeyChainRequest } from "../../types/requestTypes.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
export const giveKeyChainTriggeredItemsController: RequestHandler = async (req, res) => {
const accountId = parseString(req.query.accountId);
const accountId = await getAccountIdForRequest(req);
const keyChainInfo = getJSONfromString<IKeyChainRequest>((req.body as string).toString());
const inventory = await getInventory(accountId);
const inventoryChanges = await giveKeyChainItem(inventory, keyChainInfo);
const questKey = inventory.QuestKeys.find(qk => qk.ItemType === keyChainInfo.KeyChain)!;
const inventoryChanges = await giveKeyChainItem(inventory, keyChainInfo, questKey);
await inventory.save();
res.send(inventoryChanges);

View File

@ -1,15 +1,16 @@
import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { giveKeyChainMessage } from "@/src/services/questService";
import { IKeyChainRequest } from "@/src/types/requestTypes";
import { RequestHandler } from "express";
import { getInventory } from "../../services/inventoryService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { giveKeyChainMessage } from "../../services/questService.ts";
import type { IKeyChainRequest } from "../../types/requestTypes.ts";
import type { RequestHandler } from "express";
export const giveKeyChainTriggeredMessageController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const keyChainInfo = JSON.parse((req.body as Buffer).toString()) as IKeyChainRequest;
const inventory = await getInventory(accountId, "QuestKeys");
await giveKeyChainMessage(inventory, accountId, keyChainInfo);
const inventory = await getInventory(accountId, "QuestKeys accountOwnerId");
const questKey = inventory.QuestKeys.find(qk => qk.ItemType === keyChainInfo.KeyChain)!;
await giveKeyChainMessage(inventory, keyChainInfo, questKey);
await inventory.save();
res.send(1);

View File

@ -1,8 +1,8 @@
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { addItem, getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { IOid } from "@/src/types/commonTypes";
import { RequestHandler } from "express";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import { addItem, getInventory } from "../../services/inventoryService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import type { IOid } from "../../types/commonTypes.ts";
import type { RequestHandler } from "express";
export const giveQuestKeyRewardController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);

Some files were not shown because too many files have changed in this diff Show More