diff --git a/package-lock.json b/package-lock.json index 84daa390..b7ad3ae0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "license": "GNU", "dependencies": { + "body-parser": "^2.2.0", "chokidar": "^4.0.3", "crc-32": "^1.2.2", "express": "^5", @@ -37,6 +38,7 @@ "node": ">=20.18.1" }, "optionalDependencies": { + "@types/body-parser": "^1.19.6", "@types/express": "^5", "@types/morgan": "^1.9.9", "@types/websocket": "^1.0.10", diff --git a/package.json b/package.json index fadc699f..3b8c2f1f 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "license": "GNU", "type": "module", "dependencies": { + "body-parser": "^2.2.0", "chokidar": "^4.0.3", "crc-32": "^1.2.2", "express": "^5", @@ -42,6 +43,7 @@ "ws": "^8.18.2" }, "optionalDependencies": { + "@types/body-parser": "^1.19.6", "@types/express": "^5", "@types/morgan": "^1.9.9", "@types/websocket": "^1.0.10", diff --git a/src/controllers/api/nemesisController.ts b/src/controllers/api/nemesisController.ts index 2388d0a7..9f4af582 100644 --- a/src/controllers/api/nemesisController.ts +++ b/src/controllers/api/nemesisController.ts @@ -1,4 +1,4 @@ -import { fromDbOid, version_compare } from "../../helpers/inventoryHelpers.ts"; +import { fromDbOid, toMongoDate, version_compare } from "../../helpers/inventoryHelpers.ts"; import type { IKnifeResponse } from "../../helpers/nemesisHelpers.ts"; import { antivirusMods, @@ -310,6 +310,15 @@ export const nemesisController: RequestHandler = async (req, res) => { res.json({ target: inventory.toJSON().Nemesis }); + } else if ((req.query.mode as string) == "t") { + const inventory = await getInventory(account._id.toString(), "LastNemesisAllySpawnTime"); + //const body = getJSONfromString(String(req.body)); + const now = new Date(Math.trunc(Date.now() / 1000) * 1000); + inventory.LastNemesisAllySpawnTime = now; + await inventory.save(); + res.json({ + NewTime: toMongoDate(now) + } satisfies IUpdateAllySpawnTimeResponse); } else if ((req.query.mode as string) == "d") { const inventory = await getInventory(account._id.toString(), "NemesisHistory"); const body = getJSONfromString(String(req.body)); @@ -462,3 +471,11 @@ const consumeModCharge = ( interface IRelinquishAdversariesRequest { nemesisFingerprints: (bigint | number)[]; } + +// interface IUpdateAllySpawnTimeRequest { +// LastSpawnTime: IMongoDate; +// } + +interface IUpdateAllySpawnTimeResponse { + NewTime: IMongoDate; +} diff --git a/src/models/inventoryModels/inventoryModel.ts b/src/models/inventoryModels/inventoryModel.ts index 830338be..bcdd8cb5 100644 --- a/src/models/inventoryModels/inventoryModel.ts +++ b/src/models/inventoryModels/inventoryModel.ts @@ -1753,7 +1753,7 @@ const inventorySchema = new Schema( NemesisAbandonedRewards: { type: [String], default: [] }, Nemesis: nemesisSchema, NemesisHistory: { type: [nemesisSchema], default: undefined }, - //LastNemesisAllySpawnTime: Schema.Types.Mixed, + LastNemesisAllySpawnTime: { type: Date, default: undefined }, //TradingRulesConfirmed,ShowFriendInvNotifications(Option->Social) Settings: settingsSchema, @@ -1874,6 +1874,9 @@ inventorySchema.set("toJSON", { if (inventoryDatabase.BlessingCooldown) { inventoryResponse.BlessingCooldown = toMongoDate(inventoryDatabase.BlessingCooldown); } + if (inventoryDatabase.LastNemesisAllySpawnTime) { + inventoryResponse.LastNemesisAllySpawnTime = toMongoDate(inventoryDatabase.LastNemesisAllySpawnTime); + } if (inventoryDatabase.NextRefill) { inventoryResponse.NextRefill = toMongoDate(inventoryDatabase.NextRefill); } diff --git a/src/services/importService.ts b/src/services/importService.ts index e137396b..c949a953 100644 --- a/src/services/importService.ts +++ b/src/services/importService.ts @@ -73,6 +73,7 @@ import type { ITailorShop, ITailorShopDatabase } from "../types/personalRoomsTypes.ts"; +import { fromMongoDate } from "../helpers/inventoryHelpers.ts"; const convertDate = (value: IMongoDate): Date => { return new Date(parseInt(value.$date.$numberLong)); @@ -326,7 +327,15 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial< "GiftsRemaining", "ChallengesFixVersion", "Founder", - "Guide" + "Guide", + "BountyScore", + "EntratiVaultCountLastPeriod", + "EntratiLabConquestUnlocked", + "EntratiLabConquestHardModeStatus", + "EntratiLabConquestCacheScoreMission", + "EchoesHexConquestUnlocked", + "EchoesHexConquestHardModeStatus", + "EchoesHexConquestCacheScoreMission" ] as const) { if (client[key] !== undefined) { db[key] = client[key]; @@ -354,12 +363,28 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial< "NodeIntrosCompleted", "DeathMarks", "Wishlist", - "NemesisAbandonedRewards" + "NemesisAbandonedRewards", + "EntratiLabConquestActiveFrameVariants", + "EchoesHexConquestActiveFrameVariants", + "EchoesHexConquestActiveStickers" ] as const) { if (client[key] !== undefined) { db[key] = client[key]; } } + // IMongoDate + for (const key of [ + "Created", + "TrainingDate", + "BlessingCooldown", + "LastNemesisAllySpawnTime", + "NextRefill", + "EntratiVaultCountResetDate" + ] as const) { + if (client[key] !== undefined) { + db[key] = fromMongoDate(client[key]); + } + } // IRewardAtten[] for (const key of ["SortieRewardAttenuation", "SpecialItemRewardAttenuation"] as const) { if (client[key] !== undefined) { diff --git a/src/services/inventoryService.ts b/src/services/inventoryService.ts index 4279fb97..6e3a2533 100644 --- a/src/services/inventoryService.ts +++ b/src/services/inventoryService.ts @@ -1489,7 +1489,13 @@ export const addSkin = ( inventoryChanges: IInventoryChanges = {} ): IInventoryChanges => { if (inventory.WeaponSkins.some(x => x.ItemType == typeName)) { - logger.debug(`refusing to add WeaponSkin ${typeName} because account already owns it`); + if (typeName == "/Lotus/Upgrades/Skins/Clan/BountyHunterBadgeItem") { + logger.debug(`account already owns stratos emblem, increasing bounty score instead`); + inventory.BountyScore ??= 0; + inventory.BountyScore += 1; + } else { + logger.debug(`refusing to add WeaponSkin ${typeName} because account already owns it`); + } } else { const index = inventory.WeaponSkins.push({ diff --git a/src/types/inventoryTypes/inventoryTypes.ts b/src/types/inventoryTypes/inventoryTypes.ts index 39307e54..097d2dca 100644 --- a/src/types/inventoryTypes/inventoryTypes.ts +++ b/src/types/inventoryTypes/inventoryTypes.ts @@ -102,6 +102,7 @@ export interface IInventoryDatabase | "NextRefill" | "Nemesis" | "NemesisHistory" + | "LastNemesisAllySpawnTime" | "EntratiVaultCountResetDate" | "BrandedSuits" | "LockedWeaponGroup" @@ -146,6 +147,7 @@ export interface IInventoryDatabase NextRefill?: Date; Nemesis?: INemesisDatabase; NemesisHistory?: INemesisBaseDatabase[]; + LastNemesisAllySpawnTime?: Date; EntratiVaultCountResetDate?: Date; BrandedSuits?: Types.ObjectId[]; LockedWeaponGroup?: ILockedWeaponGroupDatabase; @@ -370,7 +372,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu ThemeStyle: string; ThemeBackground: string; ThemeSounds: string; - BountyScore: number; + BountyScore?: number; //ChallengeInstanceStates: IChallengeInstanceState[]; LoginMilestoneRewards: string[]; RecentVendorPurchases?: IRecentVendorPurchaseClient[]; @@ -382,7 +384,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu //InvasionChainProgress: IInvasionChainProgress[]; Nemesis?: INemesisClient; NemesisHistory?: INemesisBaseClient[]; - //LastNemesisAllySpawnTime?: IMongoDate; + LastNemesisAllySpawnTime?: IMongoDate; Settings?: ISettings; PersonalTechProjects: IPersonalTechProjectClient[]; PlayerSkills: IPlayerSkills; diff --git a/static/webui/script.js b/static/webui/script.js index a2615eef..dc0d7f32 100644 --- a/static/webui/script.js +++ b/static/webui/script.js @@ -2844,8 +2844,8 @@ function removeCountItems(uniqueName, count) { function addItemByItemType() { const ItemType = document.getElementById("typeName-type").value; - // Must start with "/Lotus/", contain only A–Z letters, no "//", and not end with "/" - if (!ItemType || !/^\/Lotus\/(?:[A-Za-z]+(?:\/[A-Za-z]+)*)$/.test(ItemType)) { + // Must start with "/Lotus/", contain only letters A–Z, digits 0–9, no "//", and not end with "/" + if (!ItemType || !/^\/Lotus\/(?:[A-Za-z0-9]+(?:\/[A-Za-z0-9]+)*)$/.test(ItemType)) { $("#typeName-type").addClass("is-invalid").focus(); return; }