From eef87947ce44ae791f075d9eeacc268c8b131044 Mon Sep 17 00:00:00 2001 From: Sainan Date: Tue, 11 Mar 2025 15:57:17 +0100 Subject: [PATCH] feat: track vendor purchases --- src/models/inventoryModels/inventoryModel.ts | 32 +++++++++++++++- src/services/purchaseService.ts | 40 ++++++++++++++++++++ src/types/inventoryTypes/inventoryTypes.ts | 26 ++++++++++++- 3 files changed, 95 insertions(+), 3 deletions(-) diff --git a/src/models/inventoryModels/inventoryModel.ts b/src/models/inventoryModels/inventoryModel.ts index 880dd09e..c7446f41 100644 --- a/src/models/inventoryModels/inventoryModel.ts +++ b/src/models/inventoryModels/inventoryModel.ts @@ -76,7 +76,10 @@ import { IIncentiveState, ISongChallenge, ILibraryPersonalProgress, - ICrewShipWeaponDatabase + ICrewShipWeaponDatabase, + IRecentVendorPurchaseDatabase, + IVendorPurchaseHistoryEntryDatabase, + IVendorPurchaseHistoryEntryClient } from "../../types/inventoryTypes/inventoryTypes"; import { IOid } from "../../types/commonTypes"; import { @@ -974,6 +977,31 @@ const incentiveStateSchema = new Schema( { _id: false } ); +const vendorPurchaseHistoryEntrySchema = new Schema( + { + Expiry: Date, + NumPurchased: Number, + ItemId: String + }, + { _id: false } +); + +vendorPurchaseHistoryEntrySchema.set("toJSON", { + transform(_doc, obj) { + const db = obj as IVendorPurchaseHistoryEntryDatabase; + const client = obj as IVendorPurchaseHistoryEntryClient; + client.Expiry = toMongoDate(db.Expiry); + } +}); + +const recentVendorPurchaseSchema = new Schema( + { + VendorType: String, + PurchaseHistory: [vendorPurchaseHistoryEntrySchema] + }, + { _id: false } +); + const collectibleEntrySchema = new Schema( { CollectibleType: String, @@ -1361,7 +1389,7 @@ const inventorySchema = new Schema( RandomUpgradesIdentified: Number, BountyScore: Number, ChallengeInstanceStates: [Schema.Types.Mixed], - RecentVendorPurchases: [Schema.Types.Mixed], + RecentVendorPurchases: { type: [recentVendorPurchaseSchema], default: undefined }, Robotics: [Schema.Types.Mixed], UsedDailyDeals: [Schema.Types.Mixed], CollectibleSeries: { type: [collectibleEntrySchema], default: undefined }, diff --git a/src/services/purchaseService.ts b/src/services/purchaseService.ts index b153c86c..e300e2c5 100644 --- a/src/services/purchaseService.ts +++ b/src/services/purchaseService.ts @@ -28,6 +28,7 @@ import { import { config } from "./configService"; import { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel"; import { fromStoreItem, toStoreItem } from "./itemDataService"; +import { toMongoDate } from "../helpers/inventoryHelpers"; export const getStoreItemCategory = (storeItem: string): string => { const storeItemString = getSubstringFromKeyword(storeItem, "StoreItems/"); @@ -69,6 +70,45 @@ export const handlePurchase = async ( ); } purchaseRequest.PurchaseParams.Quantity *= offer.QuantityMultiplier; + + inventory.RecentVendorPurchases ??= []; + let vendorPurchases = inventory.RecentVendorPurchases.find( + x => x.VendorType == manifest.VendorInfo.TypeName + ); + if (!vendorPurchases) { + vendorPurchases = + inventory.RecentVendorPurchases[ + inventory.RecentVendorPurchases.push({ + VendorType: manifest.VendorInfo.TypeName, + PurchaseHistory: [] + }) - 1 + ]; + } + const expiry = new Date(offer.RotatedWeekly ? Date.now() + 7 * 24 * 3600 * 1000 : 2051240400000); + const historyEntry = vendorPurchases.PurchaseHistory.find(x => x.ItemId == ItemId); + let numPurchased = purchaseRequest.PurchaseParams.Quantity; + if (historyEntry) { + numPurchased += historyEntry.NumPurchased; + historyEntry.NumPurchased += purchaseRequest.PurchaseParams.Quantity; + } else { + vendorPurchases.PurchaseHistory.push({ + ItemId: ItemId, + NumPurchased: purchaseRequest.PurchaseParams.Quantity, + Expiry: expiry + }); + } + inventoryChanges.RecentVendorPurchases = [ + { + VendorType: manifest.VendorInfo.TypeName, + PurchaseHistory: [ + { + ItemId: ItemId, + NumPurchased: numPurchased, + Expiry: toMongoDate(expiry) + } + ] + } + ]; } else if (!ExportVendors[purchaseRequest.PurchaseParams.SourceId!]) { throw new Error(`unknown vendor: ${purchaseRequest.PurchaseParams.SourceId!}`); } diff --git a/src/types/inventoryTypes/inventoryTypes.ts b/src/types/inventoryTypes/inventoryTypes.ts index af029a34..1e46376c 100644 --- a/src/types/inventoryTypes/inventoryTypes.ts +++ b/src/types/inventoryTypes/inventoryTypes.ts @@ -41,6 +41,7 @@ export interface IInventoryDatabase | "KubrowPetEggs" | "PendingCoupon" | "Drones" + | "RecentVendorPurchases" | TEquipmentKey >, InventoryDatabaseEquipment { @@ -67,6 +68,7 @@ export interface IInventoryDatabase KubrowPetEggs?: IKubrowPetEggDatabase[]; PendingCoupon?: IPendingCouponDatabase; Drones: IDroneDatabase[]; + RecentVendorPurchases?: IRecentVendorPurchaseDatabase[]; } export interface IQuestKeyDatabase { @@ -277,7 +279,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu BountyScore: number; ChallengeInstanceStates: IChallengeInstanceState[]; LoginMilestoneRewards: string[]; - RecentVendorPurchases: Array; + RecentVendorPurchases?: IRecentVendorPurchaseClient[]; NodeIntrosCompleted: string[]; GuildId?: IOid; CompletedJobChains: ICompletedJobChain[]; @@ -361,6 +363,28 @@ export interface IParam { v: string; } +export interface IRecentVendorPurchaseClient { + VendorType: string; + PurchaseHistory: IVendorPurchaseHistoryEntryClient[]; +} + +export interface IVendorPurchaseHistoryEntryClient { + Expiry: IMongoDate; + NumPurchased: number; + ItemId: string; +} + +export interface IRecentVendorPurchaseDatabase { + VendorType: string; + PurchaseHistory: IVendorPurchaseHistoryEntryDatabase[]; +} + +export interface IVendorPurchaseHistoryEntryDatabase { + Expiry: Date; + NumPurchased: number; + ItemId: string; +} + export interface IChallengeProgress { Progress: number; Name: string;