Files
TurboTrades/services/marketPrice.js
2026-01-10 04:57:43 +00:00

320 lines
9.2 KiB
JavaScript

import MarketPrice from "../models/MarketPrice.js";
/**
* Market Price Service
* Helper functions to look up prices from the market reference database
* Used when loading inventory or updating item prices
*/
class MarketPriceService {
/**
* Get price for an item by market hash name (exact match)
* @param {string} marketHashName - Steam market hash name
* @param {string} game - Game identifier ('cs2' or 'rust')
* @returns {Promise<number|null>} - Price in USD or null if not found
*/
async getPrice(marketHashName, game = null) {
try {
const query = { marketHashName };
if (game) query.game = game;
const marketItem = await MarketPrice.findOne(query);
return marketItem ? marketItem.price : null;
} catch (error) {
console.error("Error getting price:", error.message);
return null;
}
}
/**
* Get full item data by market hash name
* @param {string} marketHashName - Steam market hash name
* @param {string} game - Game identifier ('cs2' or 'rust')
* @returns {Promise<Object|null>} - Full market item data or null
*/
async getItem(marketHashName, game = null) {
try {
const query = { marketHashName };
if (game) query.game = game;
return await MarketPrice.findOne(query);
} catch (error) {
console.error("Error getting item:", error.message);
return null;
}
}
/**
* Get prices for multiple items (batch lookup)
* @param {Array<string>} marketHashNames - Array of market hash names
* @param {string} game - Game identifier ('cs2' or 'rust')
* @returns {Promise<Object>} - Map of marketHashName to price
*/
async getPrices(marketHashNames, game = null) {
try {
const query = { marketHashName: { $in: marketHashNames } };
if (game) query.game = game;
const items = await MarketPrice.find(query);
// Create a map for quick lookups
const priceMap = {};
items.forEach((item) => {
priceMap[item.marketHashName] = item.price;
});
return priceMap;
} catch (error) {
console.error("Error getting batch prices:", error.message);
return {};
}
}
/**
* Get full items data for multiple items (batch lookup)
* @param {Array<string>} marketHashNames - Array of market hash names
* @param {string} game - Game identifier ('cs2' or 'rust')
* @returns {Promise<Array>} - Array of market items
*/
async getItems(marketHashNames, game = null) {
try {
const query = { marketHashName: { $in: marketHashNames } };
if (game) query.game = game;
return await MarketPrice.find(query);
} catch (error) {
console.error("Error getting batch items:", error.message);
return [];
}
}
/**
* Search for items by name (partial match)
* @param {string} searchTerm - Search term
* @param {string} game - Game identifier ('cs2' or 'rust')
* @param {number} limit - Maximum results to return
* @returns {Promise<Array>} - Array of matching items
*/
async search(searchTerm, game = null, limit = 20) {
try {
const query = {
$or: [
{ name: { $regex: searchTerm, $options: "i" } },
{ marketHashName: { $regex: searchTerm, $options: "i" } },
],
};
if (game) query.game = game;
return await MarketPrice.find(query)
.limit(limit)
.sort({ price: -1 });
} catch (error) {
console.error("Error searching items:", error.message);
return [];
}
}
/**
* Get price statistics for a game
* @param {string} game - Game identifier ('cs2' or 'rust')
* @returns {Promise<Object>} - Statistics object
*/
async getStats(game = null) {
try {
return await MarketPrice.getStats(game);
} catch (error) {
console.error("Error getting stats:", error.message);
return {
count: 0,
avgPrice: 0,
minPrice: 0,
maxPrice: 0,
totalValue: 0,
};
}
}
/**
* Get top priced items
* @param {string} game - Game identifier ('cs2' or 'rust')
* @param {number} limit - Number of items to return
* @returns {Promise<Array>} - Array of top priced items
*/
async getTopPriced(game = null, limit = 50) {
try {
const query = game ? { game } : {};
return await MarketPrice.find(query)
.sort({ price: -1 })
.limit(limit);
} catch (error) {
console.error("Error getting top priced items:", error.message);
return [];
}
}
/**
* Get items by price range
* @param {number} minPrice - Minimum price
* @param {number} maxPrice - Maximum price
* @param {string} game - Game identifier ('cs2' or 'rust')
* @param {number} limit - Maximum results to return
* @returns {Promise<Array>} - Array of items in price range
*/
async getByPriceRange(minPrice, maxPrice, game = null, limit = 100) {
try {
const query = {
price: { $gte: minPrice, $lte: maxPrice },
};
if (game) query.game = game;
return await MarketPrice.find(query)
.sort({ price: -1 })
.limit(limit);
} catch (error) {
console.error("Error getting items by price range:", error.message);
return [];
}
}
/**
* Check if price data exists for a game
* @param {string} game - Game identifier ('cs2' or 'rust')
* @returns {Promise<boolean>} - True if data exists
*/
async hasData(game) {
try {
const count = await MarketPrice.countDocuments({ game });
return count > 0;
} catch (error) {
console.error("Error checking data:", error.message);
return false;
}
}
/**
* Get count of items in database
* @param {string} game - Game identifier ('cs2' or 'rust'), or null for all
* @returns {Promise<number>} - Count of items
*/
async getCount(game = null) {
try {
const query = game ? { game } : {};
return await MarketPrice.countDocuments(query);
} catch (error) {
console.error("Error getting count:", error.message);
return 0;
}
}
/**
* Get last update timestamp for a game
* @param {string} game - Game identifier ('cs2' or 'rust')
* @returns {Promise<Date|null>} - Last update date or null
*/
async getLastUpdate(game) {
try {
const item = await MarketPrice.findOne({ game })
.sort({ lastUpdated: -1 })
.select("lastUpdated");
return item ? item.lastUpdated : null;
} catch (error) {
console.error("Error getting last update:", error.message);
return null;
}
}
/**
* Check if price data is outdated
* @param {string} game - Game identifier ('cs2' or 'rust')
* @param {number} hoursThreshold - Hours before considering outdated
* @returns {Promise<boolean>} - True if outdated
*/
async isOutdated(game, hoursThreshold = 24) {
try {
const lastUpdate = await this.getLastUpdate(game);
if (!lastUpdate) return true;
const now = new Date();
const diff = now - lastUpdate;
const hoursDiff = diff / (1000 * 60 * 60);
return hoursDiff > hoursThreshold;
} catch (error) {
console.error("Error checking if outdated:", error.message);
return true;
}
}
/**
* Enrich inventory items with market prices
* Used when loading Steam inventory to add price data
* @param {Array} inventoryItems - Array of inventory items with market_hash_name
* @param {string} game - Game identifier ('cs2' or 'rust')
* @returns {Promise<Array>} - Inventory items enriched with price data
*/
async enrichInventory(inventoryItems, game) {
try {
// Extract all market hash names
const marketHashNames = inventoryItems.map(
(item) => item.market_hash_name
);
// Get prices for all items
const priceMap = await this.getPrices(marketHashNames, game);
// Enrich each item with price
return inventoryItems.map((item) => ({
...item,
marketPrice: priceMap[item.market_hash_name] || null,
hasPriceData: !!priceMap[item.market_hash_name],
}));
} catch (error) {
console.error("Error enriching inventory:", error.message);
return inventoryItems;
}
}
/**
* Get suggested price for an item (with optional markup)
* @param {string} marketHashName - Steam market hash name
* @param {string} game - Game identifier ('cs2' or 'rust')
* @param {number} markup - Markup percentage (e.g., 1.1 for 10% markup)
* @returns {Promise<number|null>} - Suggested price or null
*/
async getSuggestedPrice(marketHashName, game, markup = 1.0) {
try {
const price = await this.getPrice(marketHashName, game);
if (!price) return null;
// Apply markup
return parseFloat((price * markup).toFixed(2));
} catch (error) {
console.error("Error getting suggested price:", error.message);
return null;
}
}
/**
* Format price for display
* @param {number} price - Price in USD
* @returns {string} - Formatted price string
*/
formatPrice(price) {
if (price === null || price === undefined) return "N/A";
return new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
}).format(price);
}
}
// Export singleton instance
const marketPriceService = new MarketPriceService();
export default marketPriceService;