From 874b3a643e78215d272d6f7b7db13ceaf480a797 Mon Sep 17 00:00:00 2001 From: rapture-party Date: Sat, 2 May 2026 15:41:15 -0400 Subject: [PATCH] feat: 0.9.0 --- .gitea/workflows/build.yaml | 5 +- .gitea/workflows/release.yaml | 7 +- bun.lockb | Bin 317623 -> 323863 bytes package.json | 2 +- src/background/background.ts | 68 ++++++------------- src/content/content.ts | 105 ++++++++++++++++-------------- src/content/priceService.ts | 22 +++---- src/content/pricing/pricedb.ts | 67 +++++++++++++++++++ src/content/pricing/pricestf.ts | 91 -------------------------- src/content/uiRenderer.ts | 2 +- src/content/utils/formatting.ts | 2 + src/content/utils/localization.ts | 24 +++---- src/manifest.json | 78 +++++++++++----------- src/strings/ar.js | 4 +- src/strings/cs.js | 4 +- src/strings/da.js | 4 +- src/strings/de.js | 4 +- src/strings/en.js | 2 +- src/strings/es.js | 2 +- src/strings/fi.js | 4 +- src/strings/fr.js | 4 +- src/strings/hu.js | 4 +- src/strings/it.js | 4 +- src/strings/ja.js | 2 +- src/strings/ko.js | 4 +- src/strings/nl.js | 4 +- src/strings/no.js | 4 +- src/strings/pl.js | 4 +- src/strings/pt-BR.js | 4 +- src/strings/pt.js | 4 +- src/strings/ro.js | 4 +- src/strings/ru.js | 2 +- src/strings/sv.js | 4 +- src/strings/tr.js | 4 +- src/strings/zh-Hans.js | 4 +- src/strings/zh-Hant.js | 4 +- src/userscript_header.js | 6 +- webpack.config.js | 3 +- 38 files changed, 263 insertions(+), 303 deletions(-) create mode 100644 src/content/pricing/pricedb.ts delete mode 100644 src/content/pricing/pricestf.ts diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index efa0685..a351a5d 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -13,11 +13,14 @@ on: jobs: build: - runs-on: debian-latest + runs-on: ubuntu-latest steps: - name: Check out repository uses: actions/checkout@v4.1.2 + - uses: oven-sh/setup-bun@v2 + with: + bun-version: latest - name: Read package version uses: tyankatsu0105/read-package-version-actions@v1 id: version diff --git a/.gitea/workflows/release.yaml b/.gitea/workflows/release.yaml index c15a765..f7ef4f2 100644 --- a/.gitea/workflows/release.yaml +++ b/.gitea/workflows/release.yaml @@ -7,13 +7,16 @@ on: jobs: build: - runs-on: debian-latest + runs-on: ubuntu-latest outputs: version: ${{ steps.version.outputs.version }} steps: - name: Check out repository uses: actions/checkout@v4.1.2 + - uses: oven-sh/setup-bun@v2 + with: + bun-version: latest - name: Read package version uses: tyankatsu0105/read-package-version-actions@v1 id: version @@ -31,7 +34,7 @@ jobs: name: tf2wikipricing path: dist/ deploy: - runs-on: debian-latest + runs-on: ubuntu-latest needs: build steps: - name: Download release artifacts diff --git a/bun.lockb b/bun.lockb index c86c55f3e65e6d46e3637a36bd080eabca5b1978..9f97e780148d3634482cfd4b27159df1c3178b86 100755 GIT binary patch delta 10145 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 delta 10090 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 diff --git a/package.json b/package.json index d042b39..a9c1fdc 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "tf2wikipricing", "displayName": "TF2 Wiki Pricing", - "version": "0.8.1", + "version": "0.9.0", "description": "Adds item pricing to the Team Fortress 2 wiki", "author": "rapture.party", "devDependencies": { diff --git a/src/background/background.ts b/src/background/background.ts index 1798bd1..047e58b 100644 --- a/src/background/background.ts +++ b/src/background/background.ts @@ -27,61 +27,44 @@ chrome.runtime.onMessage.addListener( } ); -chrome.runtime.onMessage.addListener( - function(request, sender, sendResponse) { - if (request.contentScriptQuery == "getPricesTFToken") { - fetch('https://api2.prices.tf/auth/access', { - method: 'post', - headers: new Headers({ - 'Accept': 'application/json' - }) - }) - .then(response => response.json()) - .then(json => sendResponse(json['accessToken'])) - .catch(error => { - console.error("Failed to get access token", error); - }) - return true; - } - } -) - class PricesResponse { keys: number metal: number } -async function priceUsingPricesTF(token: string, sku: string, retries: number = 3): Promise { - const url = `https://api2.prices.tf/prices/${encodeURIComponent(sku)}`; +async function priceUsingPricedb(sku: string, retries: number = 3): Promise { + const url = `https://pricedb.io/api/item/${encodeURIComponent(sku)}`; const response = await fetch(url, { method: 'get', headers: { 'Accept': 'application/json', - 'Authorization': `Bearer ${token}`, } }) if (response.status === 404 && sku.includes(';') && !sku.includes(';uncraftable')) { const quality: number = parseInt(sku.split(';')[1], 10); if(quality === 6) { // Try uncraftable variant if unique weapon - return priceUsingPricesTF(token, sku + ';uncraftable'); + return priceUsingPricedb(sku + ';uncraftable'); } } - if(response.status === 503) { - // Happens if we send too many requests in a short period of time + if(response.status === 429) { + // Happens if we send too many requests (rate limit: 180 req/min) // Retry after a few seconds if(retries >= 0) { - console.log(`Cloudflare rate limit exceeded, trying again after 1 second, ${retries} retries left`) + console.log(`Rate limit exceeded, trying again after 1 second, ${retries} retries left`) await new Promise(resolve => setTimeout(resolve, 1000)); - return priceUsingPricesTF(token, sku, retries - 1); + return priceUsingPricedb(sku, retries - 1); } else { - throw new Error(`Cloudflare rate limit exceeded, stopping`) + throw new Error(`Rate limit exceeded, stopping`) } } + if (!response.ok) { + throw new Error(`Pricing request for ${sku} failed with status code: ${response.status}`); + } const data = await response.json(); const prices = new PricesResponse(); - prices.keys = data['sellKeys'] - prices.metal = data['sellHalfScrap'] / 18.0; + prices.keys = data.sell.keys + prices.metal = data.sell.metal return prices; } @@ -89,23 +72,14 @@ chrome.runtime.onMessage.addListener( function(request, sender, sendResponse) { if (request.contentScriptQuery == "priceSKU") { const sku: string = request.sku - const service: string = request.service - const token: string = request.token - if(token === "" || !token) { - sendResponse(new Error("No token provided")) + priceUsingPricedb(sku) + .then((response) => sendResponse(response)) + .catch(error => { + console.error(`Received "${error}" error while pricing ${sku} using pricedb.io`) + sendResponse(null); return false; - } - switch (service) { - case "prices.tf": { - priceUsingPricesTF(token, sku) - .then((response) => sendResponse(response)) - .catch(error => { - sendResponse(error); - return false; - }) - } - } - return true; + }) } + return true; } -); \ No newline at end of file +); diff --git a/src/content/content.ts b/src/content/content.ts index 9d0afae..0c1f6ad 100644 --- a/src/content/content.ts +++ b/src/content/content.ts @@ -2,7 +2,6 @@ declare const __ENV_WEBEXTENSION: boolean; declare const __ENV_USERSCRIPT: boolean; import { logDebug, log, logError } from './utils/log' -import { getPricesToken } from './pricing/pricestf' import itemQualities from 'tf2-static-schema/static/qualities.json'; import { getItemIndexByName, getTradableStatusByDefindex, ItemSchema, ItemSlot, prepareSchema, wipeSchema } from './schemaService' import { $T, extractLocaleFromURL } from './utils/localization' @@ -16,7 +15,7 @@ let exchangeRates: ExchangeRates | null; let locale: string = 'en' -/** Exclude these from the pricelist. */ +/** Exclude these from pricelist. */ const excludedQualities = new Set([ 15, // Decorated 5, // Unusual @@ -118,7 +117,7 @@ async function inject() { /// Create buttons // Item infobox is a table with the following layout: - // + // // th.infobox-header (Item Name) // tr (3D item viewer/2D preview image) // tr (buy buttons if applicable) @@ -175,22 +174,11 @@ async function inject() { priceProgressRow.appendChild(priceProgressData); priceInfoboxHeadingRow.insertAdjacentElement('afterend', priceProgressRow); - let token: string | null; - // Steam Community Market // TODO: Change this to lazy-load, so that it doesn't make network requests when we have cached data. // var steamMarketResults = await getSteamResults(itemName) // logDebug(JSON.stringify(steamMarketResults)) - // Fetch prices.tf access token - // https://api2.prices.tf/auth/access - try { - // throw new Error('dont wanna') - token = await getPricesToken(); - } catch (err) { - log('Failed to get an access token for prices.tf: ' + err); - } - let updateTime: Date | null = null; enum PriceRowCategory { @@ -208,7 +196,24 @@ async function inject() { const priceRows: PriceRow[]= []; // Get current key price - const keyPrice = await fetchKeyPrice(token); + let keyPrice: ItemPriceData + try { + keyPrice = await fetchKeyPrice(); + } catch (error) { + logError('Failed to get a key price from pricedb.io: ' + error); + // Footer row + const row = document.createElement("tr"); + + const label = document.createElement("td"); + label.colSpan = 2; + label.style.fontSize = "85%"; + label.style.textAlign = "center"; + label.innerHTML = `Failed to get prices from pricedb.io. Service may be down.`; + row.appendChild(label); + priceProgressRow.insertAdjacentElement('afterend', row); + priceProgressRow.remove(); + return + } const currentTime = new Date() @@ -219,7 +224,7 @@ async function inject() { let data: ItemPriceData | null try { - data = await fetchPrice(token, itemIndex + ";" + quality, currentTime); + data = await fetchPrice(itemIndex + ";" + quality, currentTime); updateTime = new Date(data.update) } catch { log(`${qualifiedName} is unpriced or unavailable, skipping...`) @@ -233,7 +238,7 @@ async function inject() { // Check item schema for Australium variant of current defindex if(itemSchema[itemIndex].hasAustraliumVariant) { - promises.push(fetchPrice(token, `${itemIndex};11;australium`, currentTime).then(data => { + promises.push(fetchPrice(`${itemIndex};11;australium`, currentTime).then(data => { updateTime = new Date(data.update) logDebug(`Saving price for Australium ${itemName}`) @@ -261,7 +266,7 @@ async function inject() { festiveHeadingRow.style.display = 'none'; festiveHeadingRow.appendChild(festiveHeading); - promises.push(fetchPrice(token, `${itemSchema[itemIndex].festiveVariant};6`, currentTime).then(data => { + promises.push(fetchPrice(`${itemSchema[itemIndex].festiveVariant};6`, currentTime).then(data => { updateTime = new Date(data.update) logDebug(`Saving price for Festive ${itemName}`) @@ -273,7 +278,7 @@ async function inject() { logError(error) log(`Festive ${itemName} is unpriced or unavailable, skipping...`) })) - promises.push(fetchPrice(token, `${itemSchema[itemIndex].festiveVariant};11`, currentTime).then(data => { + promises.push(fetchPrice(`${itemSchema[itemIndex].festiveVariant};11`, currentTime).then(data => { updateTime = new Date(data.update) logDebug(`Saving price for Strange Festive ${itemName}`) @@ -293,36 +298,36 @@ async function inject() { itemSchema[itemIndex].slot == ItemSlot.Secondary || itemSchema[itemIndex].slot == ItemSlot.Melee) { - /// Create subheading - killstreakKitHeadingRow = document.createElement("tr") - const heading = document.createElement("th") - heading.className = "infobox-subheader" - heading.colSpan = 2 - heading.innerText = $T("Killstreak Kit") - heading.style.fontSize = '1em'; - heading.style.backgroundColor = '#F5C087'; - killstreakKitHeadingRow.style.display = 'none'; - killstreakKitHeadingRow.appendChild(heading); - [1,2,3].map((tier) => { - let kitIndex: number - switch (tier) { - default: - case 1: kitIndex = 6527; break; - case 2: kitIndex = 6523; break; - case 3: kitIndex = 6526; break; - } - promises.push(fetchPrice(token, `${kitIndex};6;uncraftable;kt-${tier};td-${itemIndex}`, currentTime).then(data => { - updateTime = new Date(data.update) - logDebug(`Saving price for ${itemName} Killstreak Kit Tier ${tier}`) + /// Create subheading + killstreakKitHeadingRow = document.createElement("tr") + const heading = document.createElement("th") + heading.className = "infobox-subheader" + heading.colSpan = 2 + heading.innerText = $T("Killstreak Kit") + heading.style.fontSize = '1em'; + heading.style.backgroundColor = '#F5C087'; + killstreakKitHeadingRow.style.display = 'none'; + killstreakKitHeadingRow.appendChild(heading); + [1,2,3].map((tier) => { + let kitIndex: number + switch (tier) { + default: + case 1: kitIndex = 6527; break; + case 2: kitIndex = 6523; break; + case 3: kitIndex = 6526; break; + } + promises.push(fetchPrice(`${kitIndex};6;uncraftable;kt-${tier};td-${itemIndex}`, currentTime).then(data => { + updateTime = new Date(data.update) + logDebug(`Saving price for ${itemName} Killstreak Kit Tier ${tier}`) - const priceRow = createPriceRow($T(`kt-${tier}`), data, keyPrice, exchangeRates, locale, "https://wiki.teamfortress.com/wiki/Killstreak_Kit") + const priceRow = createPriceRow($T(`kt-${tier}`), data, keyPrice, exchangeRates, locale, "https://wiki.teamfortress.com/wiki/Killstreak_Kit") - priceRows.push({order: tier, row: priceRow, category: PriceRowCategory.KillstreakKit}) + priceRows.push({order: tier, row: priceRow, category: PriceRowCategory.KillstreakKit}) + }) + .catch((error) => { + logError(`Failed to fetch price for ${itemName} Killstreak Kit Tier ${tier}`, error) + })) }) - .catch((error) => { - logError(`Failed to fetch price for ${itemName} Killstreak Kit Tier ${tier}`, error) - })) - }) } // Silver Mk.I, Gold Mk.II, Rust, Blood, Carbonado, Diamond, Silver Mk.II, Gold Mk.II @@ -352,7 +357,7 @@ async function inject() { itemSchema[itemIndex].botkillerVariants.map((variantIndex) => { const itemName = itemSchema[variantIndex].name const variantName = itemName.includes('Mk.II') ? itemName.split(' ')[0] + ' Mk.II' : itemName.split(' ')[0] - promises.push(fetchPrice(token, `${variantIndex};11`, currentTime).then(data => { + promises.push(fetchPrice(`${variantIndex};11`, currentTime).then(data => { logDebug(`Saving price for ${itemName}`) updateTime = new Date(data.update) @@ -404,9 +409,9 @@ async function inject() { label.colSpan = 2; label.style.fontSize = "85%"; label.style.textAlign = "center"; - const updateText = $T("Updated %@.", locale).replace('%@', updateTime.toLocaleString(locale, { year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit', timeZoneName: 'short' })) + const updateText = $T("Updated %@", locale).replace('%@', updateTime.toLocaleString(locale, { year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit', timeZoneName: 'short' })) const attributionHeader = $T("Acknowledgements"); - const pricesAttribution = `prices.tf`; + const pricesAttribution = `pricedb.io`; const exchangeRateAttribution = `Rates By Exchange Rate API`; label.innerHTML = `${updateText}
${attributionHeader}
${pricesAttribution}
${exchangeRateAttribution}`; row.appendChild(label); @@ -443,4 +448,4 @@ prepareSchema() }) .catch((error) => { logError(error); -}) \ No newline at end of file +}) diff --git a/src/content/priceService.ts b/src/content/priceService.ts index fac3b1a..8b6901f 100644 --- a/src/content/priceService.ts +++ b/src/content/priceService.ts @@ -1,5 +1,5 @@ import { defindex_key, storage_priceprefix } from "./config" -import { priceUsingPricesTF } from "./pricing/pricestf" +import { priceUsingPricedb } from "./pricing/pricedb" import { getStorageValue, setStorageValue } from "./storage" import { logDebug } from "./utils/log" declare const __ENV_WEBEXTENSION: boolean; @@ -21,7 +21,7 @@ export class ItemPriceData { scmPrice: number toString(): string { - return `Price for ${this.sku}, fetched ${new Date(this.update)} (expires ${new Date(this.update + this.ttl)})\n` + + return `Price for ${this.sku}, fetched ${new Date(this.update)} (expires ${new Date(this.update + this.ttl)})\n` + JSON.stringify({ keys: this.keys, metal: this.metal, @@ -31,17 +31,16 @@ export class ItemPriceData { } -export async function fetchKeyPrice(token: string) { - return fetchPrice(token, `${defindex_key};6`, new Date(), 86400000) +export async function fetchKeyPrice() { + return fetchPrice(`${defindex_key};6`, new Date(), 86400000) } /** * Fetch a price for a given SKU, using cached values if available. - * @param token prices.tf access token. * @param update Date retrieved. * @param ttl Time to cache results in milliseconds. 30 minutes by default. */ -export async function fetchPrice(token: string, sku: string, update: Date = new Date(), ttl: number = 30 * 60 * 1000): Promise { +export async function fetchPrice(sku: string, update: Date = new Date(), ttl: number = 30 * 60 * 1000): Promise { let data: ItemPriceData | null const cached: ItemPriceData = await getStorageValue(storage_priceprefix + sku, null) @@ -51,9 +50,6 @@ export async function fetchPrice(token: string, sku: string, update: Date = new if (!data || data.sku != sku || 'update' in data && 'ttl' in data && Date.now() > (new Date(data.update).getTime() + data.ttl)) { logDebug(`Fetching price data for ${sku}`) - if(!token || token === '') { - throw new Error('No token provided') - } data = new ItemPriceData() data.sku = sku data.update = update.getTime() @@ -62,9 +58,9 @@ export async function fetchPrice(token: string, sku: string, update: Date = new try { let response: PricesResponse if(__ENV_USERSCRIPT) { - response = await priceUsingPricesTF(token, sku) + response = await priceUsingPricedb(sku) } else { - response = await chrome.runtime.sendMessage({contentScriptQuery: "priceSKU", service: "prices.tf", sku: sku, token: token}); + response = await chrome.runtime.sendMessage({contentScriptQuery: "priceSKU", sku: sku}); } if (!response || response instanceof Error) { throw new Error(`Bad response: ${response}`) @@ -72,7 +68,7 @@ export async function fetchPrice(token: string, sku: string, update: Date = new data.keys = response.keys data.metal = response.metal } catch (error) { - throw new Error(`Received "${error}" error while pricing ${sku} using prices.tf`) + throw new Error(`Received "${error}" error while pricing ${sku} using pricedb.io`) } if ('metal' in data && 'keys' in data) { @@ -82,4 +78,4 @@ export async function fetchPrice(token: string, sku: string, update: Date = new logDebug(`Using cached price data for ${sku}`) } return data -} + } diff --git a/src/content/pricing/pricedb.ts b/src/content/pricing/pricedb.ts new file mode 100644 index 0000000..d7419f0 --- /dev/null +++ b/src/content/pricing/pricedb.ts @@ -0,0 +1,67 @@ +import { fetchWrap } from '../fetchWrap' +import { logDebug, logError } from '../utils/log' +declare const __ENV_WEBEXTENSION: boolean; +declare const __ENV_USERSCRIPT: boolean; + +class PricesResponse { + keys: number + metal: number +} + +/** + * Fetches current price data for Team Fortress 2 items from pricedb.io. + * + * This function uses the pricedb.io API to fetch latest pricing data for a given item in keys and metal. + * No authentication is required. + * + * @example + * const price = await priceUsingPricedb('105;11'); + * console.log("Strange Brigade Helm price: ${price.keys} keys ${price.metal} metal") + * + * @returns {Promise} Object containing 'keys' and 'metal' prices + * @throws When API returns non-200 status code + */ +async function priceUsingPricedb(sku: string, retries: number = 3): Promise { + // pricedb.io + // https://pricedb.io/api/item/${sku} + try { + const response = await fetchWrap(`https://pricedb.io/api/item/${encodeURIComponent(sku)}`, { + method: 'get', + headers: { + 'Accept': 'application/json', + } + }) + if (response.status === 404 && sku.includes(';') && !sku.includes(';uncraftable')) { + const quality: number = parseInt(sku.split(';')[1], 10); + if(quality === 6) { + // Try uncraftable variant if unique weapon + return priceUsingPricedb(sku + ';uncraftable'); + } + } + if(response.status === 429) { + // Happens if we send too many requests (rate limit: 180 req/min) + // Retry after a few seconds + if(retries >= 0) { + logDebug(`Rate limit exceeded, trying again after 1 second, ${retries} retries left`) + await new Promise(resolve => setTimeout(resolve, 1000)); + return priceUsingPricedb(sku, retries - 1); + } else { + throw new Error(`Rate limit exceeded, stopping`) + } + } + if (!response.ok) { + throw new Error(`Pricing request for ${sku} failed with status code: ${response.status}`); + } + const data = await response.json(); + const prices = new PricesResponse(); + prices.keys = data.sell.keys + prices.metal = data.sell.metal + return prices; + } + catch(error) { + logError(`Failed to fetch prices from pricedb.io for item ${sku}`) + throw error; + } +} + +export { priceUsingPricedb, PricesResponse } diff --git a/src/content/pricing/pricestf.ts b/src/content/pricing/pricestf.ts deleted file mode 100644 index abeff72..0000000 --- a/src/content/pricing/pricestf.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { fetchWrap } from '../fetchWrap' -import { logDebug, logError } from '../utils/log' -declare const __ENV_WEBEXTENSION: boolean; -declare const __ENV_USERSCRIPT: boolean; - -async function getPricesToken(): Promise { - if(__ENV_USERSCRIPT) { - return new Promise((resolve, reject) => { - fetchWrap('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'])) - }) - } else { - return chrome.runtime.sendMessage({contentScriptQuery: 'getPricesTFToken'}) - } -} - -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} - try { - const response = await fetchWrap(`https://api2.prices.tf/prices/${encodeURIComponent(sku)}`, { - method: 'get', - headers: { - 'Accept': 'application/json', - 'Authorization': `Bearer ${token}`, - } - }) - if (response.status === 404 && sku.includes(';') && !sku.includes(';uncraftable')) { - const quality: number = parseInt(sku.split(';')[1], 10); - if(quality === 6) { - // Try uncraftable variant if unique weapon - return priceUsingPricesTF(token, sku + ';uncraftable'); - } - } - if(response.status === 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 1 second, ${retries} retries left`) - await new Promise(resolve => setTimeout(resolve, 1000)); - return priceUsingPricesTF(token, sku, retries - 1); - } else { - throw new Error(`Cloudflare rate limit exceeded, stopping`) - } - } - if (!response.ok) { - throw new Error(`Pricing request for ${sku} failed with status code: ${response.status}`); - } - const data = await response.json(); - const prices = new PricesResponse(); - prices.keys = data['sellKeys'] - prices.metal = data['sellHalfScrap'] / 18.0; - return prices; - } - catch(error) { - logError(`Failed to fetch prices from prices.tf for item ${sku}`) - throw error; - } -} - -export { getPricesToken, priceUsingPricesTF, PricesResponse } \ No newline at end of file diff --git a/src/content/uiRenderer.ts b/src/content/uiRenderer.ts index 3f60e3d..829d6da 100644 --- a/src/content/uiRenderer.ts +++ b/src/content/uiRenderer.ts @@ -24,7 +24,7 @@ export function createPriceRow(qualityName: string, data: ItemPriceData, keyPric const priceLink = document.createElement("span"); let priceString: string = '' - if(data) { + if(data && data.keys != null && data.metal != null) { const gamePrice = formatPrice(data.keys, data.metal, keyPrice.metal, locale).trim() const realPriceUSD = convertTF2PriceToUSD(data.keys, data.metal, keyPrice.metal) diff --git a/src/content/utils/formatting.ts b/src/content/utils/formatting.ts index 0418a18..d1ba4a3 100644 --- a/src/content/utils/formatting.ts +++ b/src/content/utils/formatting.ts @@ -6,6 +6,8 @@ function toFixed(num: number, fixed: number) { } export function formatPrice(keys: number, metal: number, keyPrice: number, locale: string = 'en') { + if(keys == null) keys = 0 + if(metal == null) metal = 0 const formattedKeys = +(keys + (metal / keyPrice)).toFixed(2) let output: string = '' diff --git a/src/content/utils/localization.ts b/src/content/utils/localization.ts index 4706b8b..d3701b3 100644 --- a/src/content/utils/localization.ts +++ b/src/content/utils/localization.ts @@ -1,40 +1,40 @@ -import { logDebug } from "./log"; +import { logDebug, logError } from "./log"; const localizations: Record = { - 'en': require('../../strings/en'), // English - 'es': require('../../strings/es'), // Spanish - 'ja': require('../../strings/ja'), // Japanese - 'it': require('../../strings/it'), // Italian 'ar': require('../../strings/ar'), // Arabic 'cs': require('../../strings/cs'), // Czech 'da': require('../../strings/da'), // Danish - 'de': require('../../strings/de'), // German + 'nl': require('../../strings/nl'), // Dutch 'fi': require('../../strings/fi'), // Finnish 'fr': require('../../strings/fr'), // French + 'en': require('../../strings/en'), // English 'hu': require('../../strings/hu'), // Hungarian + 'it': require('../../strings/it'), // Italian + 'ja': require('../../strings/ja'), // Japanese + 'de': require('../../strings/de'), // German 'ko': require('../../strings/ko'), // Korean - 'nl': require('../../strings/nl'), // Dutch 'no': require('../../strings/no'), // Norwegian Bokmål 'pl': require('../../strings/pl'), // Polish 'pt': require('../../strings/pt'), // Portuguese - 'pt-BR': require('../../strings/pt-BR'), // Brazilian Portuguese + 'pt-br': require('../../strings/pt-BR'), // Brazilian Portuguese 'ro': require('../../strings/ro'), // Romanian + 'zh-hans': require('../../strings/zh-Hans'), // Simplified Chinese 'ru': require('../../strings/ru'), // Russian + 'es': require('../../strings/es'), // Spanish 'sv': require('../../strings/sv'), // Swedish + 'zh-hant': require('../../strings/zh-Hant'), // Traditional Chinese 'tr': require('../../strings/tr'), // Turkish - 'zh-Hans': require('../../strings/zh-Hans'), // Simplified Chinese - 'zh-Hant': require('../../strings/zh-Hant'), // Traditional Chinese } export function $T(s: string, locale?: Intl.LocalesArgument): string { const code = locale ? locale.toString() : extractLocaleFromURL(document.URL) if (code in localizations) { - const translation = localizations[code] as Record; + const translation = localizations[code.toLowerCase()] as Record; const result = translation[s] ?? s; logDebug(`Translating "${s}" to locale "${code}": ${result}`); return result; } - logDebug(`Untranslated string "${s}" in locale "${code}`); + logError(`Untranslated string "${s}" in locale "${code}"`); return s; } diff --git a/src/manifest.json b/src/manifest.json index 0d5fd6e..62ddfaf 100755 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,39 +1,39 @@ -{ - "name": EXTENSION_NAME, - "description": EXTENSION_DESCRIPTION, - "author": EXTENSION_AUTHOR, - "manifest_version": 3, - "version": EXTENSION_VERSION, - "permissions": [ - "storage", - "scripting" - ], - "host_permissions": [ - "https://wiki.teamfortress.com/wiki/*", - "https://*.prices.tf/*", - "https://open.er-api.com/*" - ], - "web_accessible_resources": [ - { - "resources": ["lib/style.css", "resources/*"], - "matches": ["https://wiki.teamfortress.com/*"] - } - ], - "content_scripts": [ - { - "matches": ["*://wiki.teamfortress.com/wiki/*"], - "run_at": "document_start", - "all_frames": true, - "css": ["lib/style.css"], - "js": ["content/content.js"] - } - ], - "background": { - "service_worker": "background/background.js", - "type": "module" - }, - "icons": { - "48": "icons/icon-48.png", - "96": "icons/icon-96.png" - } -} +{ + "name": EXTENSION_NAME, + "description": EXTENSION_DESCRIPTION, + "author": EXTENSION_AUTHOR, + "manifest_version": 3, + "version": EXTENSION_VERSION, + "permissions": [ + "storage", + "scripting" + ], + "host_permissions": [ + "https://wiki.teamfortress.com/wiki/*", + "https://*.pricedb.io/*", + "https://open.er-api.com/*" + ], + "web_accessible_resources": [ + { + "resources": ["lib/style.css", "resources/*"], + "matches": ["https://wiki.teamfortress.com/*"] + } + ], + "content_scripts": [ + { + "matches": ["*://wiki.teamfortress.com/wiki/*"], + "run_at": "document_start", + "all_frames": true, + "css": ["lib/style.css"], + "js": ["content/content.js"] + } + ], + "background": { + "service_worker": "background/background.js", + "type": "module" + }, + "icons": { + "48": "icons/icon-48.png", + "96": "icons/icon-96.png" + } +} diff --git a/src/strings/ar.js b/src/strings/ar.js index dc30ea5..ef17217 100644 --- a/src/strings/ar.js +++ b/src/strings/ar.js @@ -5,11 +5,11 @@ module.exports = { // Itembox header "Community Pricing": "Community Pricing", // Itembox footer - "Updated %@.": "Updated %@.", // %@ is a date string, sourced from AppleGlot + "Updated %@": "تم التحديث يوم %@", // %@ is a date string, sourced from AppleGlot "Acknowledgements": "Acknowledgements", // sourced from AppleGlot // Price strings - "Data unavailable": "Data unavailable", // sourced from AppleGlot + "Data unavailable": "البيانات غير متوفرة", // sourced from AppleGlot "%@ ref": "%@ ref", "%@ key": "%@ key", "%@ keys": "%@ keys", diff --git a/src/strings/cs.js b/src/strings/cs.js index dc30ea5..e63b053 100644 --- a/src/strings/cs.js +++ b/src/strings/cs.js @@ -5,11 +5,11 @@ module.exports = { // Itembox header "Community Pricing": "Community Pricing", // Itembox footer - "Updated %@.": "Updated %@.", // %@ is a date string, sourced from AppleGlot + "Updated %@": "Aktualizováno %@", // %@ is a date string, sourced from AppleGlot "Acknowledgements": "Acknowledgements", // sourced from AppleGlot // Price strings - "Data unavailable": "Data unavailable", // sourced from AppleGlot + "Data unavailable": "Data nejsou k dispozici", // sourced from AppleGlot "%@ ref": "%@ ref", "%@ key": "%@ key", "%@ keys": "%@ keys", diff --git a/src/strings/da.js b/src/strings/da.js index dc30ea5..581f524 100644 --- a/src/strings/da.js +++ b/src/strings/da.js @@ -5,11 +5,11 @@ module.exports = { // Itembox header "Community Pricing": "Community Pricing", // Itembox footer - "Updated %@.": "Updated %@.", // %@ is a date string, sourced from AppleGlot + "Updated %@": "Opdateret %@", // %@ is a date string, sourced from AppleGlot "Acknowledgements": "Acknowledgements", // sourced from AppleGlot // Price strings - "Data unavailable": "Data unavailable", // sourced from AppleGlot + "Data unavailable": "Data utilgængelige", // sourced from AppleGlot "%@ ref": "%@ ref", "%@ key": "%@ key", "%@ keys": "%@ keys", diff --git a/src/strings/de.js b/src/strings/de.js index dc30ea5..6126e08 100644 --- a/src/strings/de.js +++ b/src/strings/de.js @@ -5,11 +5,11 @@ module.exports = { // Itembox header "Community Pricing": "Community Pricing", // Itembox footer - "Updated %@.": "Updated %@.", // %@ is a date string, sourced from AppleGlot + "Updated %@": "Updated %@", // %@ is a date string, sourced from AppleGlot "Acknowledgements": "Acknowledgements", // sourced from AppleGlot // Price strings - "Data unavailable": "Data unavailable", // sourced from AppleGlot + "Data unavailable": "Daten nicht verfügbar", // sourced from AppleGlot "%@ ref": "%@ ref", "%@ key": "%@ key", "%@ keys": "%@ keys", diff --git a/src/strings/en.js b/src/strings/en.js index dc30ea5..f4caea0 100644 --- a/src/strings/en.js +++ b/src/strings/en.js @@ -5,7 +5,7 @@ module.exports = { // Itembox header "Community Pricing": "Community Pricing", // Itembox footer - "Updated %@.": "Updated %@.", // %@ is a date string, sourced from AppleGlot + "Updated %@": "Updated %@", // %@ is a date string, sourced from AppleGlot "Acknowledgements": "Acknowledgements", // sourced from AppleGlot // Price strings diff --git a/src/strings/es.js b/src/strings/es.js index d8ed300..da4cf09 100644 --- a/src/strings/es.js +++ b/src/strings/es.js @@ -5,7 +5,7 @@ module.exports = { // Itembox header "Community Pricing": "Precios de la comunidad", // Itembox footer - "Updated %@.": "Actualizado %@.", // %@ is a date string, sourced from AppleGlot + "Updated %@": "Actualizado %@", // %@ is a date string, sourced from AppleGlot "Acknowledgements": "Agradecimientos", // sourced from AppleGlot // Price strings diff --git a/src/strings/fi.js b/src/strings/fi.js index dc30ea5..d61a5f1 100644 --- a/src/strings/fi.js +++ b/src/strings/fi.js @@ -5,11 +5,11 @@ module.exports = { // Itembox header "Community Pricing": "Community Pricing", // Itembox footer - "Updated %@.": "Updated %@.", // %@ is a date string, sourced from AppleGlot + "Updated %@": "Updated %@", // %@ is a date string, sourced from AppleGlot "Acknowledgements": "Acknowledgements", // sourced from AppleGlot // Price strings - "Data unavailable": "Data unavailable", // sourced from AppleGlot + "Data unavailable": "Dataa ei saatavilla", // sourced from AppleGlot "%@ ref": "%@ ref", "%@ key": "%@ key", "%@ keys": "%@ keys", diff --git a/src/strings/fr.js b/src/strings/fr.js index dc30ea5..f79bde6 100644 --- a/src/strings/fr.js +++ b/src/strings/fr.js @@ -5,11 +5,11 @@ module.exports = { // Itembox header "Community Pricing": "Community Pricing", // Itembox footer - "Updated %@.": "Updated %@.", // %@ is a date string, sourced from AppleGlot + "Updated %@": "Updated %@", // %@ is a date string, sourced from AppleGlot "Acknowledgements": "Acknowledgements", // sourced from AppleGlot // Price strings - "Data unavailable": "Data unavailable", // sourced from AppleGlot + "Data unavailable": "Données non disponibles", // sourced from AppleGlot "%@ ref": "%@ ref", "%@ key": "%@ key", "%@ keys": "%@ keys", diff --git a/src/strings/hu.js b/src/strings/hu.js index dc30ea5..b4193ed 100644 --- a/src/strings/hu.js +++ b/src/strings/hu.js @@ -5,11 +5,11 @@ module.exports = { // Itembox header "Community Pricing": "Community Pricing", // Itembox footer - "Updated %@.": "Updated %@.", // %@ is a date string, sourced from AppleGlot + "Updated %@": "Updated %@", // %@ is a date string, sourced from AppleGlot "Acknowledgements": "Acknowledgements", // sourced from AppleGlot // Price strings - "Data unavailable": "Data unavailable", // sourced from AppleGlot + "Data unavailable": "Adat nem érhető el", // sourced from AppleGlot "%@ ref": "%@ ref", "%@ key": "%@ key", "%@ keys": "%@ keys", diff --git a/src/strings/it.js b/src/strings/it.js index 734302e..1ff28cc 100644 --- a/src/strings/it.js +++ b/src/strings/it.js @@ -5,11 +5,11 @@ module.exports = { // Itembox header "Community Pricing": "Prezzo Comunitario", // Itembox footer - "Updated %@.": "Aggiornato il %@.", + "Updated %@": "Aggiornato il %@", "Acknowledgements": "Note legali", // sourced from AppleGlot // Price strings - "Data unavailable": "Data unavailable", // sourced from AppleGlot + "Data unavailable": "Dati non disponibili", // sourced from AppleGlot "%@ ref": "%@ raf", "%@ key": "%@ chiave", "%@ keys": "%@ chiavi", diff --git a/src/strings/ja.js b/src/strings/ja.js index ffcc18d..87314ed 100644 --- a/src/strings/ja.js +++ b/src/strings/ja.js @@ -5,7 +5,7 @@ module.exports = { // Itembox header "Community Pricing": "共同体価格", // Itembox footer - "Updated %@.": "アップデート: %@。", // %@ is a date string, sourced from AppleGlot + "Updated %@": "アップデート: %@", // %@ is a date string, sourced from AppleGlot "Acknowledgements": "謝辞", // sourced from AppleGlot // Price strings diff --git a/src/strings/ko.js b/src/strings/ko.js index dc30ea5..c9f4851 100644 --- a/src/strings/ko.js +++ b/src/strings/ko.js @@ -5,11 +5,11 @@ module.exports = { // Itembox header "Community Pricing": "Community Pricing", // Itembox footer - "Updated %@.": "Updated %@.", // %@ is a date string, sourced from AppleGlot + "Updated %@": "Updated %@", // %@ is a date string, sourced from AppleGlot "Acknowledgements": "Acknowledgements", // sourced from AppleGlot // Price strings - "Data unavailable": "Data unavailable", // sourced from AppleGlot + "Data unavailable": "데이터를 사용할 수 없음", // sourced from AppleGlot "%@ ref": "%@ ref", "%@ key": "%@ key", "%@ keys": "%@ keys", diff --git a/src/strings/nl.js b/src/strings/nl.js index dc30ea5..12f9f1a 100644 --- a/src/strings/nl.js +++ b/src/strings/nl.js @@ -5,11 +5,11 @@ module.exports = { // Itembox header "Community Pricing": "Community Pricing", // Itembox footer - "Updated %@.": "Updated %@.", // %@ is a date string, sourced from AppleGlot + "Updated %@": "Bijgewerkt %@", // %@ is a date string, sourced from AppleGlot "Acknowledgements": "Acknowledgements", // sourced from AppleGlot // Price strings - "Data unavailable": "Data unavailable", // sourced from AppleGlot + "Data unavailable": "Gegevens niet beschikbaar", // sourced from AppleGlot "%@ ref": "%@ ref", "%@ key": "%@ key", "%@ keys": "%@ keys", diff --git a/src/strings/no.js b/src/strings/no.js index dc30ea5..0c72348 100644 --- a/src/strings/no.js +++ b/src/strings/no.js @@ -5,11 +5,11 @@ module.exports = { // Itembox header "Community Pricing": "Community Pricing", // Itembox footer - "Updated %@.": "Updated %@.", // %@ is a date string, sourced from AppleGlot + "Updated %@": "Updated %@", // %@ is a date string, sourced from AppleGlot "Acknowledgements": "Acknowledgements", // sourced from AppleGlot // Price strings - "Data unavailable": "Data unavailable", // sourced from AppleGlot + "Data unavailable": "Data ikke tilgjengelig", // sourced from AppleGlot "%@ ref": "%@ ref", "%@ key": "%@ key", "%@ keys": "%@ keys", diff --git a/src/strings/pl.js b/src/strings/pl.js index dc30ea5..7f9f0f9 100644 --- a/src/strings/pl.js +++ b/src/strings/pl.js @@ -5,11 +5,11 @@ module.exports = { // Itembox header "Community Pricing": "Community Pricing", // Itembox footer - "Updated %@.": "Updated %@.", // %@ is a date string, sourced from AppleGlot + "Updated %@": "Updated %@", // %@ is a date string, sourced from AppleGlot "Acknowledgements": "Acknowledgements", // sourced from AppleGlot // Price strings - "Data unavailable": "Data unavailable", // sourced from AppleGlot + "Data unavailable": "Dane niedostępne", // sourced from AppleGlot "%@ ref": "%@ ref", "%@ key": "%@ key", "%@ keys": "%@ keys", diff --git a/src/strings/pt-BR.js b/src/strings/pt-BR.js index dc30ea5..cc1483f 100644 --- a/src/strings/pt-BR.js +++ b/src/strings/pt-BR.js @@ -5,11 +5,11 @@ module.exports = { // Itembox header "Community Pricing": "Community Pricing", // Itembox footer - "Updated %@.": "Updated %@.", // %@ is a date string, sourced from AppleGlot + "Updated %@": "Atualizado: %@", // %@ is a date string, sourced from AppleGlot "Acknowledgements": "Acknowledgements", // sourced from AppleGlot // Price strings - "Data unavailable": "Data unavailable", // sourced from AppleGlot + "Data unavailable": "Dados indisponíveis", // sourced from AppleGlot "%@ ref": "%@ ref", "%@ key": "%@ key", "%@ keys": "%@ keys", diff --git a/src/strings/pt.js b/src/strings/pt.js index dc30ea5..afdc497 100644 --- a/src/strings/pt.js +++ b/src/strings/pt.js @@ -5,11 +5,11 @@ module.exports = { // Itembox header "Community Pricing": "Community Pricing", // Itembox footer - "Updated %@.": "Updated %@.", // %@ is a date string, sourced from AppleGlot + "Updated %@": "Updated %@", // %@ is a date string, sourced from AppleGlot "Acknowledgements": "Acknowledgements", // sourced from AppleGlot // Price strings - "Data unavailable": "Data unavailable", // sourced from AppleGlot + "Data unavailable": "Dados indisponíveis", // sourced from AppleGlot "%@ ref": "%@ ref", "%@ key": "%@ key", "%@ keys": "%@ keys", diff --git a/src/strings/ro.js b/src/strings/ro.js index dc30ea5..bc0de7d 100644 --- a/src/strings/ro.js +++ b/src/strings/ro.js @@ -5,11 +5,11 @@ module.exports = { // Itembox header "Community Pricing": "Community Pricing", // Itembox footer - "Updated %@.": "Updated %@.", // %@ is a date string, sourced from AppleGlot + "Updated %@": "Updated %@", // %@ is a date string, sourced from AppleGlot "Acknowledgements": "Acknowledgements", // sourced from AppleGlot // Price strings - "Data unavailable": "Data unavailable", // sourced from AppleGlot + "Data unavailable": "Date indisponibile", // sourced from AppleGlot "%@ ref": "%@ ref", "%@ key": "%@ key", "%@ keys": "%@ keys", diff --git a/src/strings/ru.js b/src/strings/ru.js index 245f637..dd7f631 100644 --- a/src/strings/ru.js +++ b/src/strings/ru.js @@ -5,7 +5,7 @@ module.exports = { // Itembox header "Community Pricing": "Цены сообщества", // Itembox footer - "Updated %@.": "Обновлено %@.", // %@ is a date string, sourced from AppleGlot + "Updated %@": "Обновлено %@", // %@ is a date string, sourced from AppleGlot "Acknowledgements": "Уведомления", // sourced from AppleGlot // Price strings diff --git a/src/strings/sv.js b/src/strings/sv.js index dc30ea5..6d1054c 100644 --- a/src/strings/sv.js +++ b/src/strings/sv.js @@ -5,11 +5,11 @@ module.exports = { // Itembox header "Community Pricing": "Community Pricing", // Itembox footer - "Updated %@.": "Updated %@.", // %@ is a date string, sourced from AppleGlot + "Updated %@": "Updated %@", // %@ is a date string, sourced from AppleGlot "Acknowledgements": "Acknowledgements", // sourced from AppleGlot // Price strings - "Data unavailable": "Data unavailable", // sourced from AppleGlot + "Data unavailable": "Data ej tillgängliga", // sourced from AppleGlot "%@ ref": "%@ ref", "%@ key": "%@ key", "%@ keys": "%@ keys", diff --git a/src/strings/tr.js b/src/strings/tr.js index dc30ea5..e67aab3 100644 --- a/src/strings/tr.js +++ b/src/strings/tr.js @@ -5,11 +5,11 @@ module.exports = { // Itembox header "Community Pricing": "Community Pricing", // Itembox footer - "Updated %@.": "Updated %@.", // %@ is a date string, sourced from AppleGlot + "Updated %@": "Updated %@", // %@ is a date string, sourced from AppleGlot "Acknowledgements": "Acknowledgements", // sourced from AppleGlot // Price strings - "Data unavailable": "Data unavailable", // sourced from AppleGlot + "Data unavailable": "Veri yok", // sourced from AppleGlot "%@ ref": "%@ ref", "%@ key": "%@ key", "%@ keys": "%@ keys", diff --git a/src/strings/zh-Hans.js b/src/strings/zh-Hans.js index dc30ea5..fc33cd7 100644 --- a/src/strings/zh-Hans.js +++ b/src/strings/zh-Hans.js @@ -5,11 +5,11 @@ module.exports = { // Itembox header "Community Pricing": "Community Pricing", // Itembox footer - "Updated %@.": "Updated %@.", // %@ is a date string, sourced from AppleGlot + "Updated %@": "Updated %@", // %@ is a date string, sourced from AppleGlot "Acknowledgements": "Acknowledgements", // sourced from AppleGlot // Price strings - "Data unavailable": "Data unavailable", // sourced from AppleGlot + "Data unavailable": "数据不可用", // sourced from AppleGlot "%@ ref": "%@ ref", "%@ key": "%@ key", "%@ keys": "%@ keys", diff --git a/src/strings/zh-Hant.js b/src/strings/zh-Hant.js index dc30ea5..10cbe33 100644 --- a/src/strings/zh-Hant.js +++ b/src/strings/zh-Hant.js @@ -5,11 +5,11 @@ module.exports = { // Itembox header "Community Pricing": "Community Pricing", // Itembox footer - "Updated %@.": "Updated %@.", // %@ is a date string, sourced from AppleGlot + "Updated %@": "Updated %@", // %@ is a date string, sourced from AppleGlot "Acknowledgements": "Acknowledgements", // sourced from AppleGlot // Price strings - "Data unavailable": "Data unavailable", // sourced from AppleGlot + "Data unavailable": "無法使用資料", // sourced from AppleGlot "%@ ref": "%@ ref", "%@ key": "%@ key", "%@ keys": "%@ keys", diff --git a/src/userscript_header.js b/src/userscript_header.js index 11d0f31..2702446 100644 --- a/src/userscript_header.js +++ b/src/userscript_header.js @@ -8,8 +8,8 @@ // @inject-into content // @connect steamcommunity.com // @domain steamcommunity.com -// @connect prices.tf -// @domain prices.tf +// @connect pricedb.io +// @domain pricedb.io // @connect open.er-api.com // @domain open.er-api.com // @grant GM.setValue @@ -18,4 +18,4 @@ // @grant GM_getValue // @grant GM.xmlhttpRequest // @grant GM_xmlhttpRequest -// ==/UserScript== \ No newline at end of file +// ==/UserScript== diff --git a/webpack.config.js b/webpack.config.js index 151bb28..8117b8d 100755 --- a/webpack.config.js +++ b/webpack.config.js @@ -22,9 +22,10 @@ const defines = { __VERSION__: JSON.stringify(package.version), } -module.exports = [ +module.exports = (env, argv) => [ // WebExtension { + devtool: argv.mode === 'production' ? false : 'source-map', entry: { content: './src/content/content.ts', background: './src/background/background.ts',