Compare commits

..

3 Commits

Author SHA1 Message Date
e361d7ee5c make the array optional 2025-04-09 20:55:17 +02:00
2187b1cae5 some better noting 2025-04-09 20:47:28 +02:00
8e4f507780 feat: track EudicoHeists in CompletedJobChains 2025-04-09 20:42:19 +02:00
9 changed files with 45 additions and 43 deletions

View File

@ -13,13 +13,4 @@ jobs:
- run: npm ci - run: npm ci
- run: cp config.json.example config.json - run: cp config.json.example config.json
- run: npm run verify - run: npm run verify
- run: npm run lint:ci - run: npm run lint
- run: npm run prettier
- name: Fail if there are uncommitted changes
run: |
if [[ -n "$(git status --porcelain)" ]]; then
echo "Uncommitted changes detected:"
git status
git diff
exit 1
fi

View File

@ -9,7 +9,6 @@
"build": "tsc --incremental --sourceMap && ncp static/webui build/static/webui", "build": "tsc --incremental --sourceMap && ncp static/webui build/static/webui",
"verify": "tsgo --noEmit", "verify": "tsgo --noEmit",
"lint": "eslint --ext .ts .", "lint": "eslint --ext .ts .",
"lint:ci": "eslint --ext .ts --rule \"prettier/prettier: off\" .",
"lint:fix": "eslint --fix --ext .ts .", "lint:fix": "eslint --fix --ext .ts .",
"prettier": "prettier --write .", "prettier": "prettier --write .",
"update-translations": "cd scripts && node update-translations.js" "update-translations": "cd scripts && node update-translations.js"

View File

@ -7,8 +7,6 @@ export const clearDialogueHistoryController: RequestHandler = async (req, res) =
const inventory = await getInventory(accountId); const inventory = await getInventory(accountId);
const request = JSON.parse(String(req.body)) as IClearDialogueRequest; const request = JSON.parse(String(req.body)) as IClearDialogueRequest;
if (inventory.DialogueHistory && inventory.DialogueHistory.Dialogues) { if (inventory.DialogueHistory && inventory.DialogueHistory.Dialogues) {
inventory.DialogueHistory.Resets ??= 0;
inventory.DialogueHistory.Resets += 1;
for (const dialogueName of request.Dialogues) { for (const dialogueName of request.Dialogues) {
const index = inventory.DialogueHistory.Dialogues.findIndex(x => x.DialogueName == dialogueName); const index = inventory.DialogueHistory.Dialogues.findIndex(x => x.DialogueName == dialogueName);
if (index != -1) { if (index != -1) {

View File

@ -1,7 +1,6 @@
import { addEmailItem, getInventory } from "@/src/services/inventoryService"; import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService"; import { getAccountIdForRequest } from "@/src/services/loginService";
import { ICompletedDialogue } from "@/src/types/inventoryTypes/inventoryTypes"; import { ICompletedDialogue } from "@/src/types/inventoryTypes/inventoryTypes";
import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { logger } from "@/src/utils/logger"; import { logger } from "@/src/utils/logger";
import { RequestHandler } from "express"; import { RequestHandler } from "express";
@ -22,10 +21,9 @@ export const saveDialogueController: RequestHandler = async (req, res) => {
if (!inventory.DialogueHistory) { if (!inventory.DialogueHistory) {
throw new Error("bad inventory state"); throw new Error("bad inventory state");
} }
if (request.OtherDialogueInfos.length != 0) { if (request.QueuedDialogues.length != 0 || request.OtherDialogueInfos.length != 0) {
logger.error(`saveDialogue request not fully handled: ${String(req.body)}`); logger.error(`saveDialogue request not fully handled: ${String(req.body)}`);
} }
const inventoryChanges: IInventoryChanges = {};
inventory.DialogueHistory.Dialogues ??= []; inventory.DialogueHistory.Dialogues ??= [];
let dialogue = inventory.DialogueHistory.Dialogues.find(x => x.DialogueName == request.DialogueName); let dialogue = inventory.DialogueHistory.Dialogues.find(x => x.DialogueName == request.DialogueName);
if (!dialogue) { if (!dialogue) {
@ -38,7 +36,6 @@ export const saveDialogueController: RequestHandler = async (req, res) => {
AvailableGiftDate: new Date(0), AvailableGiftDate: new Date(0),
RankUpExpiry: new Date(0), RankUpExpiry: new Date(0),
BountyChemExpiry: new Date(0), BountyChemExpiry: new Date(0),
QueuedDialogues: [],
Gifts: [], Gifts: [],
Booleans: [], Booleans: [],
Completed: [], Completed: [],
@ -48,16 +45,9 @@ export const saveDialogueController: RequestHandler = async (req, res) => {
} }
dialogue.Rank = request.Rank; dialogue.Rank = request.Rank;
dialogue.Chemistry = request.Chemistry; dialogue.Chemistry = request.Chemistry;
dialogue.QueuedDialogues = request.QueuedDialogues; //dialogue.QueuedDialogues = request.QueuedDialogues;
for (const bool of request.Booleans) { for (const bool of request.Booleans) {
dialogue.Booleans.push(bool); dialogue.Booleans.push(bool);
if (bool == "LizzieShawzin") {
await addEmailItem(
inventory,
"/Lotus/Types/Items/EmailItems/LizzieShawzinSkinEmailItem",
inventoryChanges
);
}
} }
for (const bool of request.ResetBooleans) { for (const bool of request.ResetBooleans) {
const index = dialogue.Booleans.findIndex(x => x == bool); const index = dialogue.Booleans.findIndex(x => x == bool);
@ -70,7 +60,7 @@ export const saveDialogueController: RequestHandler = async (req, res) => {
dialogue.AvailableDate = new Date(tomorrowAt0Utc); dialogue.AvailableDate = new Date(tomorrowAt0Utc);
await inventory.save(); await inventory.save();
res.json({ res.json({
InventoryChanges: inventoryChanges, InventoryChanges: [],
AvailableDate: { $date: { $numberLong: tomorrowAt0Utc.toString() } } AvailableDate: { $date: { $numberLong: tomorrowAt0Utc.toString() } }
}); });
} }
@ -87,7 +77,7 @@ interface SaveCompletedDialogueRequest {
Rank: number; Rank: number;
Chemistry: number; Chemistry: number;
CompletionType: number; CompletionType: number;
QueuedDialogues: string[]; QueuedDialogues: string[]; // unsure
Booleans: string[]; Booleans: string[];
ResetBooleans: string[]; ResetBooleans: string[];
Data: ICompletedDialogue; Data: ICompletedDialogue;

View File

@ -773,7 +773,7 @@ const dialogueSchema = new Schema<IDialogueDatabase>(
AvailableGiftDate: Date, AvailableGiftDate: Date,
RankUpExpiry: Date, RankUpExpiry: Date,
BountyChemExpiry: Date, BountyChemExpiry: Date,
QueuedDialogues: { type: [String], default: [] }, //QueuedDialogues: ???
Gifts: { type: [dialogueGiftSchema], default: [] }, Gifts: { type: [dialogueGiftSchema], default: [] },
Booleans: { type: [String], default: [] }, Booleans: { type: [String], default: [] },
Completed: { type: [completedDialogueSchema], default: [] }, Completed: { type: [completedDialogueSchema], default: [] },
@ -797,7 +797,6 @@ dialogueSchema.set("toJSON", {
const dialogueHistorySchema = new Schema<IDialogueHistoryDatabase>( const dialogueHistorySchema = new Schema<IDialogueHistoryDatabase>(
{ {
YearIteration: { type: Number, required: true }, YearIteration: { type: Number, required: true },
Resets: Number,
Dialogues: { type: [dialogueSchema], required: false } Dialogues: { type: [dialogueSchema], required: false }
}, },
{ _id: false } { _id: false }
@ -1415,7 +1414,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
//https://warframe.fandom.com/wiki/Heist //https://warframe.fandom.com/wiki/Heist
//ProfitTaker(1-4) Example:"LocationTag": "EudicoHeists", "Jobs":Mission name //ProfitTaker(1-4) Example:"LocationTag": "EudicoHeists", "Jobs":Mission name
CompletedJobChains: [completedJobChainsSchema], CompletedJobChains: { type: [completedJobChainsSchema], default: undefined },
//Night Wave Challenge //Night Wave Challenge
SeasonChallengeHistory: [seasonChallengeHistorySchema], SeasonChallengeHistory: [seasonChallengeHistorySchema],

View File

@ -1014,14 +1014,12 @@ export const addCustomization = (
customizationName: string, customizationName: string,
inventoryChanges: IInventoryChanges = {} inventoryChanges: IInventoryChanges = {}
): IInventoryChanges => { ): IInventoryChanges => {
if (!inventory.FlavourItems.find(x => x.ItemType == customizationName)) {
const flavourItemIndex = inventory.FlavourItems.push({ ItemType: customizationName }) - 1; const flavourItemIndex = inventory.FlavourItems.push({ ItemType: customizationName }) - 1;
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
inventoryChanges.FlavourItems ??= []; inventoryChanges.FlavourItems ??= [];
(inventoryChanges.FlavourItems as IFlavourItem[]).push( (inventoryChanges.FlavourItems as IFlavourItem[]).push(
inventory.FlavourItems[flavourItemIndex].toJSON<IFlavourItem>() inventory.FlavourItems[flavourItemIndex].toJSON<IFlavourItem>()
); );
}
return inventoryChanges; return inventoryChanges;
}; };

View File

@ -143,6 +143,28 @@ export const addMissionInventoryUpdates = async (
if (inventoryUpdates.RewardInfo.NemesisAbandonedRewards) { if (inventoryUpdates.RewardInfo.NemesisAbandonedRewards) {
inventory.NemesisAbandonedRewards = inventoryUpdates.RewardInfo.NemesisAbandonedRewards; inventory.NemesisAbandonedRewards = inventoryUpdates.RewardInfo.NemesisAbandonedRewards;
} }
if (inventoryUpdates.MissionStatus == "GS_SUCCESS" && inventoryUpdates.RewardInfo.jobId) {
// e.g. for Profit-Taker Phase 1:
// JobTier: -6,
// jobId: '/Lotus/Types/Gameplay/Venus/Jobs/Heists/HeistProfitTakerBountyOne_-6_SolarisUnitedHub1_663a71c80000000000000025_EudicoHeists',
// This is sent multiple times, with JobStage starting at 0 and incrementing each time, but only the final upload has GS_SUCCESS.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [bounty, tier, hub, id, tag] = inventoryUpdates.RewardInfo.jobId.split("_");
if (tag == "EudicoHeists") {
inventory.CompletedJobChains ??= [];
let chain = inventory.CompletedJobChains.find(x => x.LocationTag == tag);
if (!chain) {
chain =
inventory.CompletedJobChains[
inventory.CompletedJobChains.push({ LocationTag: tag, Jobs: [] }) - 1
];
}
if (!chain.Jobs.includes(bounty)) {
chain.Jobs.push(bounty);
}
}
}
} }
for (const [key, value] of getEntriesUnsafe(inventoryUpdates)) { for (const [key, value] of getEntriesUnsafe(inventoryUpdates)) {
if (value === undefined) { if (value === undefined) {

View File

@ -292,7 +292,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
RecentVendorPurchases?: IRecentVendorPurchaseClient[]; RecentVendorPurchases?: IRecentVendorPurchaseClient[];
NodeIntrosCompleted: string[]; NodeIntrosCompleted: string[];
GuildId?: IOid; GuildId?: IOid;
CompletedJobChains: ICompletedJobChain[]; CompletedJobChains?: ICompletedJobChain[];
SeasonChallengeHistory: ISeasonChallenge[]; SeasonChallengeHistory: ISeasonChallenge[];
EquippedInstrument?: string; EquippedInstrument?: string;
InvasionChainProgress: IInvasionChainProgress[]; InvasionChainProgress: IInvasionChainProgress[];
@ -1074,13 +1074,11 @@ export interface IEndlessXpProgress {
export interface IDialogueHistoryClient { export interface IDialogueHistoryClient {
YearIteration: number; YearIteration: number;
Resets?: number; // added in 38.5.0
Dialogues?: IDialogueClient[]; Dialogues?: IDialogueClient[];
} }
export interface IDialogueHistoryDatabase { export interface IDialogueHistoryDatabase {
YearIteration: number; YearIteration: number;
Resets?: number;
Dialogues?: IDialogueDatabase[]; Dialogues?: IDialogueDatabase[];
} }
@ -1091,7 +1089,7 @@ export interface IDialogueClient {
AvailableGiftDate: IMongoDate; AvailableGiftDate: IMongoDate;
RankUpExpiry: IMongoDate; RankUpExpiry: IMongoDate;
BountyChemExpiry: IMongoDate; BountyChemExpiry: IMongoDate;
QueuedDialogues: string[]; //QueuedDialogues: any[];
Gifts: IDialogueGift[]; Gifts: IDialogueGift[];
Booleans: string[]; Booleans: string[];
Completed: ICompletedDialogue[]; Completed: ICompletedDialogue[];

View File

@ -143,6 +143,13 @@ export interface IRewardInfo {
PurgatoryRewardQualifications?: string; PurgatoryRewardQualifications?: string;
rewardSeed?: number; rewardSeed?: number;
periodicMissionTag?: string; periodicMissionTag?: string;
// for bounties, only EOM_AFK and node are given from above, plus:
JobTier?: string;
jobId?: string;
JobStage?: string;
Q?: boolean; // always false?
CheckpointCounter?: number; // starts at 1, is incremented with each job stage upload, and does not reset when starting a new job
} }
export type IMissionStatus = "GS_SUCCESS" | "GS_FAILURE" | "GS_DUMPED" | "GS_QUIT" | "GS_INTERRUPTED"; export type IMissionStatus = "GS_SUCCESS" | "GS_FAILURE" | "GS_DUMPED" | "GS_QUIT" | "GS_INTERRUPTED";