You've already forked tf2wikipricing
feat: fetch currency exchange rate
exchange rates are fetched at script startup, and at most once daily, then cached. attribution is added as per ExchangeRate-API's requirements
This commit is contained in:
@@ -3,6 +3,9 @@ export const storage_lastUpdateTime = 'tf2wikipricing_lastUpdate';
|
|||||||
export const storage_schema = 'tf2wikipricing_schema';
|
export const storage_schema = 'tf2wikipricing_schema';
|
||||||
export const storage_version = 'tf2wikipricing_version';
|
export const storage_version = 'tf2wikipricing_version';
|
||||||
export const storage_priceprefix = 'tf2wikipricing_sku_';
|
export const storage_priceprefix = 'tf2wikipricing_sku_';
|
||||||
|
export const storage_exchangerates = 'tf2wikipricing_exchangerates';
|
||||||
|
export const storage_exchangerates_update = 'tf2wikipricing_exchangerates_update';
|
||||||
|
export const storage_exchangerates_next = 'tf2wikipricing_exchangerates_next';
|
||||||
export const conversion_ref_usd = 0.0265;
|
export const conversion_ref_usd = 0.0265;
|
||||||
export const defindex_key = 5021;
|
export const defindex_key = 5021;
|
||||||
export const defindex_metal_refined = 5002;
|
export const defindex_metal_refined = 5002;
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ import { fetchPrice, fetchKeyPrice, ItemPriceData } from './priceService'
|
|||||||
import { createPriceRow, createStoreButton } from './uiRenderer'
|
import { createPriceRow, createStoreButton } from './uiRenderer'
|
||||||
import { findFirstElement, findFirstChildElement } from './utils/dom'
|
import { findFirstElement, findFirstChildElement } from './utils/dom'
|
||||||
import { extractPageTitleFromURL } from './utils/url';
|
import { extractPageTitleFromURL } from './utils/url';
|
||||||
|
import { ExchangeRates, prepareExchangeRates } from './exchangeRateService';
|
||||||
var itemSchema: ItemSchema | null;
|
var itemSchema: ItemSchema | null;
|
||||||
|
var exchangeRates: ExchangeRates | null;
|
||||||
|
|
||||||
var locale: string = 'en'
|
var locale: string = 'en'
|
||||||
|
|
||||||
@@ -425,7 +427,8 @@ async function inject() {
|
|||||||
label.style.fontSize = "85%";
|
label.style.fontSize = "85%";
|
||||||
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 attributionText = $T("Trade prices sourced from %@. Currency conversions are approximate.", locale).replace('%@', '<a rel="nofollow" class="external text" href="https://prices.tf">prices.tf</a>');
|
const attributionText = $T("Trade prices sourced from %@. Currency conversions are approximate.", locale).replace('%@', '<a rel="nofollow" class="external text" href="https://prices.tf">prices.tf</a>');
|
||||||
label.innerHTML = `${updateText}<br>${attributionText}`;
|
const exchangeRateAttribution = `<a rel="nofollow" class="external text" href="https://www.exchangerate-api.com">Rates By Exchange Rate API</a>.`;
|
||||||
|
label.innerHTML = `${updateText}<br>${attributionText}<br>${exchangeRateAttribution}`;
|
||||||
row.appendChild(label);
|
row.appendChild(label);
|
||||||
|
|
||||||
priceProgressRow.insertAdjacentElement('afterend', row);
|
priceProgressRow.insertAdjacentElement('afterend', row);
|
||||||
@@ -440,15 +443,22 @@ function addStyles() {
|
|||||||
style.innerHTML = styleCss;
|
style.innerHTML = styleCss;
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareSchema().then(function (schema) {
|
prepareSchema()
|
||||||
|
.then(schema => {
|
||||||
itemSchema = schema;
|
itemSchema = schema;
|
||||||
if (!itemSchema) {
|
if (!itemSchema) {
|
||||||
logError("No item schema ready, exiting.");
|
|
||||||
wipeSchema(); // FIXME: ugly hack. requires additional page reload. if prepareSchema returns null, we should handle it properly
|
wipeSchema(); // FIXME: ugly hack. requires additional page reload. if prepareSchema returns null, we should handle it properly
|
||||||
return;
|
throw new Error("No item schema ready");
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.then(prepareExchangeRates)
|
||||||
|
.then(rates => exchangeRates = rates)
|
||||||
|
.then(() => {
|
||||||
locale = extractLocaleFromURL(document.URL)
|
locale = extractLocaleFromURL(document.URL)
|
||||||
addStyles();
|
addStyles();
|
||||||
inject();
|
inject();
|
||||||
// TODO: Purge expired price data
|
// TODO: Purge expired price data
|
||||||
});
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
logError(error);
|
||||||
|
})
|
||||||
55
src/content/exchangeRateService.ts
Normal file
55
src/content/exchangeRateService.ts
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import { getStorageValue, setStorageValue } from './storage'
|
||||||
|
import { logDebug, log, logError } from './utils/log'
|
||||||
|
import { storage_exchangerates, storage_exchangerates_next, storage_exchangerates_update } from './config'
|
||||||
|
declare function GM_fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response>
|
||||||
|
import './GM_fetch'
|
||||||
|
|
||||||
|
export interface ExchangeRates {
|
||||||
|
[key: string]: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function wipeExchangeRates(): Promise<void> {
|
||||||
|
await setStorageValue(storage_exchangerates, null)
|
||||||
|
await setStorageValue(storage_exchangerates_update, new Date().toISOString())
|
||||||
|
await setStorageValue(storage_exchangerates_next, new Date().toISOString())
|
||||||
|
logDebug(`Exchange rates wiped`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function prepareExchangeRates(): Promise<ExchangeRates> {
|
||||||
|
var needsUpdate: Boolean = false
|
||||||
|
var rates: ExchangeRates | null = null
|
||||||
|
|
||||||
|
rates = await getStorageValue(storage_exchangerates, null);
|
||||||
|
const update = await getStorageValue(storage_exchangerates_update, null)
|
||||||
|
const nextUpdate = await getStorageValue(storage_exchangerates_next, null)
|
||||||
|
if (update && nextUpdate) {
|
||||||
|
const lastUpdateTime = new Date(update);
|
||||||
|
const nextUpdateTime = new Date(nextUpdate);
|
||||||
|
log(`Exchange rates updated at ${lastUpdateTime}`);
|
||||||
|
if (rates == null || Object.keys(rates).length === 0 || lastUpdateTime.getTime() > nextUpdateTime.getTime()) {
|
||||||
|
needsUpdate = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
needsUpdate = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if(needsUpdate) {
|
||||||
|
log("Exchange rates out of Date. Rebuilding...");
|
||||||
|
const url = "https://open.er-api.com/v6/latest/USD"
|
||||||
|
const response = await GM_fetch(url);
|
||||||
|
if (response.ok) {
|
||||||
|
await setStorageValue(storage_exchangerates_update, new Date().toISOString())
|
||||||
|
var json = await response.json()
|
||||||
|
if(json != null){
|
||||||
|
rates = json['rates']
|
||||||
|
await setStorageValue(storage_exchangerates, rates)
|
||||||
|
await setStorageValue(storage_exchangerates_next, json['time_next_update_utc'])
|
||||||
|
}
|
||||||
|
logDebug(`Exchange rates updated at ${new Date()}`)
|
||||||
|
} else {
|
||||||
|
logError(`Failed to fetch exchange rates. Status code: ${response.status}`, response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rates
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user