rename file
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 DrakiaXYZ
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"nextUpdate": 1717917796,
|
||||
"maxIncreaseMult": 10
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "LiveFleaPrices",
|
||||
"version": "1.1.1",
|
||||
"main": "src/mod.js",
|
||||
"license": "MIT",
|
||||
"author": "DrakiaXYZ",
|
||||
"akiVersion": "~3.8",
|
||||
"scripts": {
|
||||
"setup": "npm i",
|
||||
"build": "node ./build.mjs",
|
||||
"buildinfo": "node ./build.mjs --verbose"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "20.11",
|
||||
"@typescript-eslint/eslint-plugin": "7.2",
|
||||
"@typescript-eslint/parser": "7.2",
|
||||
"archiver": "^6.0",
|
||||
"eslint": "8.57",
|
||||
"fs-extra": "11.2",
|
||||
"ignore": "^5.2",
|
||||
"os": "^0.1",
|
||||
"tsyringe": "4.8.0",
|
||||
"typescript": "5.4",
|
||||
"winston": "3.12"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const fs = __importStar(require("node:fs"));
|
||||
const path = __importStar(require("node:path"));
|
||||
class Mod {
|
||||
static container;
|
||||
static updateTimer;
|
||||
static config;
|
||||
static configPath = path.resolve(__dirname, "../config/config.json");
|
||||
static pricesPath = path.resolve(__dirname, "../config/prices.json");
|
||||
static originalPrices;
|
||||
async postDBLoadAsync(container) {
|
||||
Mod.container = container;
|
||||
Mod.config = JSON.parse(fs.readFileSync(Mod.configPath, "utf-8"));
|
||||
// Store a clone of the original prices table, so we can make sure things don't go too crazy
|
||||
const databaseServer = Mod.container.resolve("DatabaseServer");
|
||||
const priceTable = databaseServer.getTables().templates.prices;
|
||||
Mod.originalPrices = structuredClone(priceTable);
|
||||
// Update prices on startup
|
||||
const currentTime = Math.floor(Date.now() / 1000);
|
||||
let fetchPrices = false;
|
||||
if (currentTime > Mod.config.nextUpdate) {
|
||||
fetchPrices = true;
|
||||
}
|
||||
if (!await Mod.updatePrices(fetchPrices)) {
|
||||
return;
|
||||
}
|
||||
// Setup a refresh interval to update once every hour
|
||||
Mod.updateTimer = setInterval(Mod.updatePrices, (60 * 60 * 1000));
|
||||
}
|
||||
static async updatePrices(fetchPrices = true) {
|
||||
const logger = Mod.container.resolve("WinstonLogger");
|
||||
const databaseServer = Mod.container.resolve("DatabaseServer");
|
||||
const ragfairPriceService = Mod.container.resolve("RagfairPriceService");
|
||||
const priceTable = databaseServer.getTables().templates.prices;
|
||||
const itemTable = databaseServer.getTables().templates.items;
|
||||
const handbookTable = databaseServer.getTables().templates.handbook;
|
||||
let prices;
|
||||
// Fetch the latest prices.json if we're triggered with fetch enabled, or the prices file doesn't exist
|
||||
if (fetchPrices || !fs.existsSync(Mod.pricesPath)) {
|
||||
logger.info("Fetching Flea Prices...");
|
||||
const response = await fetch("https://raw.githubusercontent.com/DrakiaXYZ/SPT-LiveFleaPriceDB/main/prices.json");
|
||||
// If the request failed, disable future updating
|
||||
if (!response?.ok) {
|
||||
logger.error(`Error fetching flea prices: ${response.status} (${response.statusText})`);
|
||||
clearInterval(Mod.updateTimer);
|
||||
return false;
|
||||
}
|
||||
prices = await response.json();
|
||||
// Store the prices to disk for next time
|
||||
fs.writeFileSync(Mod.pricesPath, JSON.stringify(prices));
|
||||
// Update config file with the next update time
|
||||
Mod.config.nextUpdate = Math.floor(Date.now() / 1000) + 3600;
|
||||
fs.writeFileSync(Mod.configPath, JSON.stringify(Mod.config, null, 4));
|
||||
}
|
||||
// Otherwise, read the file from disk
|
||||
else {
|
||||
prices = JSON.parse(fs.readFileSync(Mod.pricesPath, "utf-8"));
|
||||
}
|
||||
// Loop through the new prices file, updating all prices present
|
||||
for (const itemId in prices) {
|
||||
if (!itemTable[itemId]) {
|
||||
continue;
|
||||
}
|
||||
let basePrice = Mod.originalPrices[itemId];
|
||||
if (!basePrice) {
|
||||
basePrice = handbookTable.Items.find(x => x.Id === itemId)?.Price ?? 0;
|
||||
}
|
||||
const maxPrice = basePrice * Mod.config.maxIncreaseMult;
|
||||
if (maxPrice !== 0 && prices[itemId] <= maxPrice) {
|
||||
priceTable[itemId] = prices[itemId];
|
||||
}
|
||||
else {
|
||||
logger.debug(`Setting ${itemId} to ${maxPrice} instead of ${prices[itemId]} due to over inflation`);
|
||||
priceTable[itemId] = maxPrice;
|
||||
}
|
||||
}
|
||||
// Update dynamic price cache.
|
||||
// Note: We currently cast to `any` to bypass the protected state of the generateDynamicPrices method
|
||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||
ragfairPriceService.generateDynamicPrices();
|
||||
logger.info("Flea Prices Updated!");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
module.exports = { mod: new Mod() };
|
||||
//# sourceMappingURL=mod.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"mod.js","sourceRoot":"","sources":["mod.ts"],"names":[],"mappings":"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"}
|
||||
@@ -0,0 +1,128 @@
|
||||
import type { DependencyContainer } from "tsyringe";
|
||||
|
||||
import type { ILogger } from "@spt-aki/models/spt/utils/ILogger";
|
||||
import type { IPostDBLoadModAsync } from "@spt-aki/models/external/IPostDBLoadModAsync";
|
||||
import type { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
|
||||
import type { RagfairPriceService } from "@spt-aki/services/RagfairPriceService";
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
|
||||
class Mod implements IPostDBLoadModAsync
|
||||
{
|
||||
private static container: DependencyContainer;
|
||||
private static updateTimer: NodeJS.Timeout;
|
||||
private static config: Config;
|
||||
private static configPath = path.resolve(__dirname, "../config/config.json");
|
||||
private static pricesPath = path.resolve(__dirname, "../config/prices.json");
|
||||
private static originalPrices;
|
||||
|
||||
public async postDBLoadAsync(container: DependencyContainer): Promise<void>
|
||||
{
|
||||
Mod.container = container;
|
||||
Mod.config = JSON.parse(fs.readFileSync(Mod.configPath, "utf-8"));
|
||||
|
||||
// Store a clone of the original prices table, so we can make sure things don't go too crazy
|
||||
const databaseServer = Mod.container.resolve<DatabaseServer>("DatabaseServer");
|
||||
const priceTable = databaseServer.getTables().templates.prices;
|
||||
Mod.originalPrices = structuredClone(priceTable);
|
||||
|
||||
// Update prices on startup
|
||||
const currentTime = Math.floor(Date.now() / 1000);
|
||||
let fetchPrices = false;
|
||||
if (currentTime > Mod.config.nextUpdate)
|
||||
{
|
||||
fetchPrices = true;
|
||||
}
|
||||
|
||||
if (!await Mod.updatePrices(fetchPrices))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup a refresh interval to update once every hour
|
||||
Mod.updateTimer = setInterval(Mod.updatePrices, (60 * 60 * 1000));
|
||||
}
|
||||
|
||||
static async updatePrices(fetchPrices = true): Promise<boolean>
|
||||
{
|
||||
const logger = Mod.container.resolve<ILogger>("WinstonLogger");
|
||||
const databaseServer = Mod.container.resolve<DatabaseServer>("DatabaseServer");
|
||||
const ragfairPriceService = Mod.container.resolve<RagfairPriceService>("RagfairPriceService");
|
||||
const priceTable = databaseServer.getTables().templates.prices;
|
||||
const itemTable = databaseServer.getTables().templates.items;
|
||||
const handbookTable = databaseServer.getTables().templates.handbook;
|
||||
let prices: Record<string, number>;
|
||||
|
||||
// Fetch the latest prices.json if we're triggered with fetch enabled, or the prices file doesn't exist
|
||||
if (fetchPrices || !fs.existsSync(Mod.pricesPath))
|
||||
{
|
||||
logger.info("Fetching Flea Prices...");
|
||||
const response = await fetch("https://raw.githubusercontent.com/DrakiaXYZ/SPT-LiveFleaPriceDB/main/prices.json");
|
||||
|
||||
// If the request failed, disable future updating
|
||||
if (!response?.ok)
|
||||
{
|
||||
logger.error(`Error fetching flea prices: ${response.status} (${response.statusText})`);
|
||||
clearInterval(Mod.updateTimer);
|
||||
return false;
|
||||
}
|
||||
|
||||
prices = await response.json();
|
||||
|
||||
// Store the prices to disk for next time
|
||||
fs.writeFileSync(Mod.pricesPath, JSON.stringify(prices));
|
||||
|
||||
// Update config file with the next update time
|
||||
Mod.config.nextUpdate = Math.floor(Date.now() / 1000) + 3600;
|
||||
fs.writeFileSync(Mod.configPath, JSON.stringify(Mod.config, null, 4));
|
||||
}
|
||||
// Otherwise, read the file from disk
|
||||
else
|
||||
{
|
||||
prices = JSON.parse(fs.readFileSync(Mod.pricesPath, "utf-8"));
|
||||
}
|
||||
|
||||
// Loop through the new prices file, updating all prices present
|
||||
for (const itemId in prices)
|
||||
{
|
||||
if (!itemTable[itemId])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let basePrice = Mod.originalPrices[itemId];
|
||||
if (!basePrice)
|
||||
{
|
||||
basePrice = handbookTable.Items.find(x => x.Id === itemId)?.Price ?? 0;
|
||||
}
|
||||
|
||||
const maxPrice = basePrice * Mod.config.maxIncreaseMult;
|
||||
if (maxPrice !== 0 && prices[itemId] <= maxPrice)
|
||||
{
|
||||
priceTable[itemId] = prices[itemId];
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.debug(`Setting ${itemId} to ${maxPrice} instead of ${prices[itemId]} due to over inflation`);
|
||||
priceTable[itemId] = maxPrice;
|
||||
}
|
||||
}
|
||||
|
||||
// Update dynamic price cache.
|
||||
// Note: We currently cast to `any` to bypass the protected state of the generateDynamicPrices method
|
||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||
(ragfairPriceService as any).generateDynamicPrices();
|
||||
|
||||
logger.info("Flea Prices Updated!");
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
interface Config
|
||||
{
|
||||
nextUpdate: number,
|
||||
maxIncreaseMult: number,
|
||||
}
|
||||
|
||||
module.exports = { mod: new Mod() }
|
||||
Reference in New Issue
Block a user