Compare commits

..

1 Commits

Author SHA1 Message Date
9a20c1259e fix: only roll unique rewards for peely pix booster packs 2025-03-23 16:59:59 +01:00
12 changed files with 83 additions and 68188 deletions

8
package-lock.json generated
View File

@ -18,7 +18,7 @@
"mongoose": "^8.11.0", "mongoose": "^8.11.0",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"typescript": ">=5.5 <5.6.0", "typescript": ">=5.5 <5.6.0",
"warframe-public-export-plus": "^0.5.47", "warframe-public-export-plus": "^0.5.48",
"warframe-riven-info": "^0.1.2", "warframe-riven-info": "^0.1.2",
"winston": "^3.17.0", "winston": "^3.17.0",
"winston-daily-rotate-file": "^5.0.0" "winston-daily-rotate-file": "^5.0.0"
@ -4013,9 +4013,9 @@
} }
}, },
"node_modules/warframe-public-export-plus": { "node_modules/warframe-public-export-plus": {
"version": "0.5.47", "version": "0.5.48",
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.47.tgz", "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.48.tgz",
"integrity": "sha512-ZJK3VT1PdSPwZlhIzUVBlydwK4DM0sOmeCiixVMgOM8XuOPJ8OHfQUoLKydtw5rxCsowzFPbx5b3KBke5C4akQ==" "integrity": "sha512-vJitVYnaViQo43xAkL/h3MJ/6wS7YknKEYhYs+N/GrsspYLMPGf9KSuR19tprB2g9KVGS5o67t0v5K8p0RTQCQ=="
}, },
"node_modules/warframe-riven-info": { "node_modules/warframe-riven-info": {
"version": "0.1.2", "version": "0.1.2",

View File

@ -23,7 +23,7 @@
"mongoose": "^8.11.0", "mongoose": "^8.11.0",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"typescript": ">=5.5 <5.6.0", "typescript": ">=5.5 <5.6.0",
"warframe-public-export-plus": "^0.5.47", "warframe-public-export-plus": "^0.5.48",
"warframe-riven-info": "^0.1.2", "warframe-riven-info": "^0.1.2",
"winston": "^3.17.0", "winston": "^3.17.0",
"winston-daily-rotate-file": "^5.0.0" "winston-daily-rotate-file": "^5.0.0"

View File

@ -1,23 +1,25 @@
import { getAccountIdForRequest } from "@/src/services/loginService"; import { getAccountIdForRequest } from "@/src/services/loginService";
import { getJSONfromString } from "@/src/helpers/stringHelpers"; import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { updateTheme } from "@/src/services/inventoryService";
import { IThemeUpdateRequest } from "@/src/types/requestTypes";
import { RequestHandler } from "express"; import { RequestHandler } from "express";
import { getInventory } from "@/src/services/inventoryService";
export const updateThemeController: RequestHandler = async (request, response) => { const updateThemeController: RequestHandler = async (request, response) => {
const accountId = await getAccountIdForRequest(request); const accountId = await getAccountIdForRequest(request);
const data = getJSONfromString<IThemeUpdateRequest>(String(request.body)); const body = String(request.body);
const inventory = await getInventory(accountId, "ThemeStyle ThemeBackground ThemeSounds"); try {
if (data.Style) inventory.ThemeStyle = data.Style; const json = getJSONfromString<IThemeUpdateRequest>(body);
if (data.Background) inventory.ThemeBackground = data.Background; if (typeof json !== "object") {
if (data.Sounds) inventory.ThemeSounds = data.Sounds; throw new Error("Invalid data format");
await inventory.save(); }
await updateTheme(json, accountId);
} catch (err) {
console.error("Error parsing JSON data:", err);
}
response.json({}); response.json({});
}; };
interface IThemeUpdateRequest { export { updateThemeController };
Style?: string;
Background?: string;
Sounds?: string;
}

View File

@ -1,24 +1,9 @@
import { Account } from "@/src/models/loginModel"; import { getAccountForRequest } from "@/src/services/loginService";
import { RequestHandler } from "express"; import { RequestHandler } from "express";
export const ircDroppedController: RequestHandler = async (req, res) => { export const ircDroppedController: RequestHandler = async (req, res) => {
if (!req.query.accountId) { const account = await getAccountForRequest(req);
throw new Error("Request is missing accountId parameter"); account.Dropped = true;
} await account.save();
const nonce: number = parseInt(req.query.nonce as string);
if (!nonce) {
throw new Error("Request is missing nonce parameter");
}
await Account.updateOne(
{
_id: req.query.accountId,
Nonce: nonce
},
{
Dropped: true
}
);
res.end(); res.end();
}; };

View File

@ -1,6 +1,5 @@
import { RequestHandler } from "express"; import { RequestHandler } from "express";
import { getAccountForRequest, isAdministrator, isNameTaken } from "@/src/services/loginService"; import { getAccountForRequest, isNameTaken } from "@/src/services/loginService";
import { config, saveConfig } from "@/src/services/configService";
export const renameAccountController: RequestHandler = async (req, res) => { export const renameAccountController: RequestHandler = async (req, res) => {
const account = await getAccountForRequest(req); const account = await getAccountForRequest(req);
@ -8,18 +7,8 @@ export const renameAccountController: RequestHandler = async (req, res) => {
if (await isNameTaken(req.query.newname)) { if (await isNameTaken(req.query.newname)) {
res.status(409).json("Name already in use"); res.status(409).json("Name already in use");
} else { } else {
if (isAdministrator(account)) {
for (let i = 0; i != config.administratorNames!.length; ++i) {
if (config.administratorNames![i] == account.DisplayName) {
config.administratorNames![i] = req.query.newname;
}
}
await saveConfig();
}
account.DisplayName = req.query.newname; account.DisplayName = req.query.newname;
await account.save(); await account.save();
res.end(); res.end();
} }
} else { } else {

View File

@ -19,13 +19,9 @@ import mongoose from "mongoose";
return "<BIGINT>" + this.toString() + "</BIGINT>"; return "<BIGINT>" + this.toString() + "</BIGINT>";
}; };
const og_stringify = JSON.stringify; const og_stringify = JSON.stringify;
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
JSON.stringify = (obj: any, replacer?: any, space?: string | number): string => { (JSON as any).stringify = (obj: any): string => {
return og_stringify(obj, replacer as string[], space) return og_stringify(obj).split(`"<BIGINT>`).join(``).split(`</BIGINT>"`).join(``);
.split(`"<BIGINT>`)
.join(``)
.split(`</BIGINT>"`)
.join(``);
}; };
} }

View File

@ -34,7 +34,7 @@ interface IConfig {
httpsPort?: number; httpsPort?: number;
myIrcAddresses?: string[]; myIrcAddresses?: string[];
NRS?: string[]; NRS?: string[];
administratorNames?: string[]; administratorNames?: string[] | string;
autoCreateAccount?: boolean; autoCreateAccount?: boolean;
skipTutorial?: boolean; skipTutorial?: boolean;
skipAllDialogue?: boolean; skipAllDialogue?: boolean;
@ -83,15 +83,10 @@ export const updateConfig = async (data: string): Promise<void> => {
Object.assign(config, JSON.parse(data)); Object.assign(config, JSON.parse(data));
}; };
export const saveConfig = async (): Promise<void> => {
amnesia = true;
await fsPromises.writeFile(configPath, JSON.stringify(config, null, 2));
};
export const validateConfig = (): void => { export const validateConfig = (): void => {
if (typeof config.administratorNames == "string") { if (typeof config.administratorNames == "string") {
logger.info(`Updating config.json to make administratorNames an array.`); logger.warn(
config.administratorNames = [config.administratorNames]; `"administratorNames" should be an array; please add square brackets: ["${config.administratorNames}"]`
void saveConfig(); );
} }
}; };

View File

@ -29,7 +29,11 @@ import {
ICrewShipWeaponClient ICrewShipWeaponClient
} from "@/src/types/inventoryTypes/inventoryTypes"; } from "@/src/types/inventoryTypes/inventoryTypes";
import { IGenericUpdate, IUpdateNodeIntrosResponse } from "../types/genericUpdate"; import { IGenericUpdate, IUpdateNodeIntrosResponse } from "../types/genericUpdate";
import { IMissionInventoryUpdateRequest, IUpdateChallengeProgressRequest } from "../types/requestTypes"; import {
IMissionInventoryUpdateRequest,
IThemeUpdateRequest,
IUpdateChallengeProgressRequest
} from "../types/requestTypes";
import { logger } from "@/src/utils/logger"; import { logger } from "@/src/utils/logger";
import { convertInboxMessage, fromStoreItem, getExalted, getKeyChainItems } from "@/src/services/itemDataService"; import { convertInboxMessage, fromStoreItem, getExalted, getKeyChainItems } from "@/src/services/itemDataService";
import { import {
@ -887,6 +891,15 @@ export const updateGeneric = async (data: IGenericUpdate, accountId: string): Pr
}; };
}; };
export const updateTheme = async (data: IThemeUpdateRequest, accountId: string): Promise<void> => {
const inventory = await getInventory(accountId);
if (data.Style) inventory.ThemeStyle = data.Style;
if (data.Background) inventory.ThemeBackground = data.Background;
if (data.Sounds) inventory.ThemeSounds = data.Sounds;
await inventory.save();
};
export const addEquipment = ( export const addEquipment = (
inventory: TInventoryDatabaseDocument, inventory: TInventoryDatabaseDocument,
category: TEquipmentKey, category: TEquipmentKey,

View File

@ -69,31 +69,36 @@ export const getAccountForRequest = async (req: Request): Promise<TAccountDocume
if (!req.query.accountId) { if (!req.query.accountId) {
throw new Error("Request is missing accountId parameter"); throw new Error("Request is missing accountId parameter");
} }
const nonce: number = parseInt(req.query.nonce as string); if (!req.query.nonce || parseInt(req.query.nonce as string) === 0) {
if (!nonce) {
throw new Error("Request is missing nonce parameter"); throw new Error("Request is missing nonce parameter");
} }
const account = await Account.findOne({ const account = await Account.findOne({
_id: req.query.accountId, _id: req.query.accountId,
Nonce: nonce Nonce: req.query.nonce
}); });
if (!account) { if (!account) {
throw new Error("Invalid accountId-nonce pair"); throw new Error("Invalid accountId-nonce pair");
} }
if (account.Dropped && req.query.ct) {
account.Dropped = undefined;
await account.save();
}
return account; return account;
}; };
export const getAccountIdForRequest = async (req: Request): Promise<string> => { export const getAccountIdForRequest = async (req: Request): Promise<string> => {
return (await getAccountForRequest(req))._id.toString(); const account = await getAccountForRequest(req);
if (account.Dropped && req.query.ct) {
account.Dropped = undefined;
await account.save();
}
return account._id.toString();
}; };
export const isAdministrator = (account: TAccountDocument): boolean => { export const isAdministrator = (account: TAccountDocument): boolean => {
return !!config.administratorNames?.find(x => x == account.DisplayName); if (!config.administratorNames) {
return false;
}
if (typeof config.administratorNames == "string") {
return config.administratorNames == account.DisplayName;
}
return !!config.administratorNames.find(x => x == account.DisplayName);
}; };
const platform_magics = [753, 639, 247, 37, 60]; const platform_magics = [753, 639, 247, 37, 60];

View File

@ -415,24 +415,24 @@ const handleBoosterPackPurchase = async (
"attempt to roll over 100 booster packs in a single go. possible but unlikely to be desirable for the user or the server." "attempt to roll over 100 booster packs in a single go. possible but unlikely to be desirable for the user or the server."
); );
} }
if (typeName == "/Lotus/Types/BoosterPacks/1999StickersPackEchoesArchimedeaFixed") {
for (const result of pack.components) {
purchaseResponse.BoosterPackItems += toStoreItem(result.Item) + ',{"lvl":0};';
combineInventoryChanges(purchaseResponse.InventoryChanges, await addItem(inventory, result.Item, 1));
}
} else {
for (let i = 0; i != quantity; ++i) { for (let i = 0; i != quantity; ++i) {
for (const weights of pack.rarityWeightsPerRoll) { const disallowedItems = new Set();
for (let roll = 0; roll != pack.rarityWeightsPerRoll.length; ) {
const weights = pack.rarityWeightsPerRoll[roll];
const result = getRandomWeightedRewardUc(pack.components, weights); const result = getRandomWeightedRewardUc(pack.components, weights);
if (result) { if (result) {
logger.debug(`booster pack rolled`, result); logger.debug(`booster pack rolled`, result);
if (disallowedItems.has(result.Item)) {
logger.debug(`oops, can't use that one; trying again`);
continue;
}
if (!pack.canGiveDuplicates) {
disallowedItems.add(result.Item);
}
purchaseResponse.BoosterPackItems += toStoreItem(result.Item) + ',{"lvl":0};'; purchaseResponse.BoosterPackItems += toStoreItem(result.Item) + ',{"lvl":0};';
combineInventoryChanges( combineInventoryChanges(purchaseResponse.InventoryChanges, await addItem(inventory, result.Item, 1));
purchaseResponse.InventoryChanges,
await addItem(inventory, result.Item, 1)
);
}
} }
++roll;
} }
} }
return purchaseResponse; return purchaseResponse;

View File

@ -19,6 +19,12 @@ import {
ICollectibleEntry ICollectibleEntry
} from "./inventoryTypes/inventoryTypes"; } from "./inventoryTypes/inventoryTypes";
export interface IThemeUpdateRequest {
Style?: string;
Background?: string;
Sounds?: string;
}
export interface IAffiliationChange { export interface IAffiliationChange {
Tag: string; Tag: string;
Standing: number; Standing: number;

File diff suppressed because it is too large Load Diff