Files
tf2wikipricing/src/content/pricing/pricestf.ts
2025-04-07 13:30:39 -04:00

104 lines
3.4 KiB
TypeScript

declare function GM_fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response>
import '../GM_fetch'
import { logDebug } from '../utils/log'
async function getPricesToken(): Promise<string> {
return new Promise<any>((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<PricesResponse>} 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<PricesResponse> {
// 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 }