declare function GM_fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise import '../GM_fetch' import { logDebug } from '../utils/log' async function getPricesToken(): Promise { return new Promise((resolve, reject) => { GM_fetch('https://api2.prices.tf/auth/access', { method: 'post', headers: new Headers({ 'Accept': 'application/json' }) }) .then((response) => { if (response.status != 200) { reject(response.status) } return response.json() }) .then((responseData) => resolve(responseData['accessToken'])) }) } class PricesResponse { keys: number metal: number } /** * Fetches the current price data for Team Fortress 2 items from prices.tf. * * This function authenticates with the prices.tf API using the provided token, * and uses it to fetch the latest pricing data for the given item in keys and metal. * * @example * const price = await priceUsingPricesTF(token, '105;11'); * console.log("Strange Brigade Helm price: ${price.keys} keys ${price.metal} metal") * * @returns {Promise} Object containing 'keys' and 'metal' prices * @throws When authentication fails or API returns non-200 status code */ async function priceUsingPricesTF(token: string, sku: string, retries: number = 3): Promise { // prices.tf // https://api2.prices.tf/prices/${sku} // Authorization: Bearer ${token} return new Promise(async (resolve, reject) => { if (!token) { reject(401) } var response = await GM_fetch(`https://api2.prices.tf/prices/${encodeURIComponent(sku)}`, { method: 'get', headers: new Headers({ 'Accept': 'application/json', 'Authorization': `Bearer ${token}` }) }) if (response.status === 404 && sku.includes(';')) { const quality: number = parseInt(sku.split(';')[1], 10); if(quality === 6) { // Try uncraftable variant if unique weapon response = await GM_fetch(`https://api2.prices.tf/prices/${encodeURIComponent(sku + ';uncraftable')}`, { method: 'get', headers: new Headers({ 'Accept': 'application/json', 'Authorization': `Bearer ${token}` }) }) } } switch (response.status) { case 200: const json = await response.json() resolve({ keys: json['sellKeys'], metal: json['sellHalfScrap'] / 18.0 }) break; case 404: reject("Not found in prices.tf") break; case 429: case 503: // Happens if we send too many requests in a short period of time // Retry after a few seconds if(retries > 0) { logDebug(`Cloudflare rate limit exceeded, trying again after 2 seconds, ${retries} retries left`) await new Promise(resolve => setTimeout(resolve, 2000)); try { const retryResult = await priceUsingPricesTF(token, sku, retries - 1); resolve(retryResult); } catch (err) { reject(err); } } else { reject("Cloudflare rate limit exceeded, stopping") } break; default: // Something went wrong logDebug(`Received ${response.status} error while pricing ${sku}`) logDebug(`${JSON.stringify(response.headers)}`) reject("Unknown error") break; } }) } export { getPricesToken, priceUsingPricesTF, PricesResponse }