howmuchdoesthesims4cost.lol/static/script.js

194 lines
7.1 KiB
JavaScript
Executable file

// This code is licensed under GNU AGPLv3.
const BASE_URL = "/api/appinfo";
const ORIGINAL_TS4_APP_ID = 1222670;
const APPID_REGEX = /#((http(s)?:\/\/store\.steampowered\.com\/app\/)?(?<appID>\d{4,10})(.*))/
function discover_app_id() {
let search = new URLSearchParams(window.location.search);
console.debug("Search params, parsed:", search);
let matches = APPID_REGEX.exec("#" + (search.get("app_id") || window.location.hash.slice(1)));
console.debug("Matches:", matches);
const decision = parseInt(matches?.groups?.appID || ORIGINAL_TS4_APP_ID);
console.debug("Decision:", decision);
return decision;
};
const APP_ID = discover_app_id();
const PRICE_ELEMENT = document.getElementById("price");
const NAME_ELEMENT = document.getElementById("name");
/*
* These prices are gathered from a range of sources, such as Tesco, Amazon, and CeX.
* Some prices may no longer be correct. They do not include discounts.
* Prices are often not updated after they're added. Prices are in GBP.
*
* Last updated: 2024-09-02
*/
const PRICES = {
"Freddo (18g)": 0.25,
"Skittles (136G)": 1.25,
"Cadbury Fingers (114G)": 1.70,
"howmuchdoesthesims4cost.lol": 1.78,
"Pepsi Max (2L)": 2.00,
"Pringles (Salt & Vinegar, 185g)": 2.25,
"BLÅHAJ Soft toy, shark (40 cm)": 22.00,
"Dell Optiplex 7040 USFF (6th generation Intel i5)": 25.00,
"Tenda AC10 V3.0 AC1200 Dual Band Gigabit Wireless Cable Router": 27.99,
"Seagate SkyHawk 4TB 3.5\" 7200RPM HDD": 30.00,
"Dell Optiplex 7040 SFF (6th generation Intel i5)": 36.00,
"Dell Optiplex 3020 USFF (4th generation Intel i5)": 39.99,
"TP-Link AX1800 WiFi 6 Router": 39.99,
"KIOXIA EXERIA NVMe M.2 SSD (1TB)": 43.20, // I *wish* they still cost this much - ~nex 2024-09-02
"Seagate BarraCuda,Internal Hard Drive 2.5 Inch (1TB)": 49.99,
"Kingston NV2 NVMe PCIe 4.0 Internal SSD (1TB)": 59.99,
"NVIDIA GeForce GTX 1650": 117.96,
"AMD Radeon RX 5700 8GB (Second hand from CeX)": 150.00,
"TP-Link AX5400 WiFi 6 Router": 159.99,
"Intel Core i5-13400F": 191.99,
"Apple Watch SE (1st generation)": 219.00,
"Windows 11 Pro license": 219.99, // https://www.microsoft.com/en-gb/d/windows-11-pro/dg7gmgf0d8h4/000P
"HP ProBook 455 G8": 289.79, // second hand from eBay, bought 2024-07-06
"iPhone SE (3rd generation/5G/2022)": 499.00,
"My custom PC (https://uk.pcpartpicker.com/user/nexy7574/saved/#view=cKYV4D)": 800.00,
"NVIDIA GeForce RTX 4090": 1519.00,
}
function compare(price, item) {
let n = Math.floor((price / PRICES[item]) * 100) / 100;
if(n>=1) {
n = Math.floor(n);
};
return n
}
function update(n, unit="$") {
PRICE_ELEMENT.textContent = unit + n;
console.debug("Updated price to: " + n);
}
function ids_to_body(ids) {
return JSON.stringify({"app_ids": ids})
}
function ratelimited(response) {
let retry_after = response.headers.get("retry-after");
console.warn("Rate-limited for " + retry_after + " seconds.");
setTimeout(() => {window.location.reload()}, retry_after * 1000);
setInterval(
() => {
let element = document.getElementById("retry-after");
let value = parseFloat(element.textContent);
value -= 0.1;
element.textContent = value.toFixed(1);
},
100
)
let element = document.getElementById("retrying");
element.hidden = false;
document.getElementById("retry-after").textContent = (retry_after * 1).toFixed(1);
document.querySelector("main").hidden = true;
document.querySelector("main").style.display = "none";
}
async function get_app_info(id) {
const response = await fetch(BASE_URL, {method: "POST", body: ids_to_body([id]), headers: {"Content-Type": "application/json"}});
if(response.status === 429) {
ratelimited(response);
return {id: {"name": "<rate limited>"}}
}
const data = await response.json();
console.debug(data);
return data;
}
async function calculate_price(ids) {
console.debug("Calculating price for: " + ids);
const response = await fetch(BASE_URL, {method: "POST", body: ids_to_body(ids), headers: {"Content-Type": "application/json"}});
if(response.status === 429) {
ratelimited(response);
let o = {}
for (let id of ids) {
o[id] = {"price_overview": {"final": 0.0}}
}
return o
}
const data = await response.json();
console.debug("mass price data:", data);
let _price = 0.0;
let unit = "£";
for (let value of Object.values(data)) {
console.debug("calculate_price(%s) value:", ids, value)
if (value.price_overview == null) {
continue;
}
let new_unit = value.price_overview.final_formatted.slice(0, 1);
if((/\d/).test(new_unit)) {
//new_unit = value.price_overview.final_formatted.slice(-2, -1);
new_unit = value.price_overview.currency || '£'
}
unit = new_unit
_price += parseFloat(value.price_overview.final / 100);
}
return [_price, unit];
}
function find_unit(formatted) {
let unit = "£";
if((/\d/).test(formatted.slice(0, 1))) {
unit = formatted.slice(-2, -1);
}
return unit;
}
async function main() {
console.debug("Fetching app info for base game");
let root;
try {
root = await get_app_info(APP_ID);
} catch (e) {
alert(`Error fetching information from Steam: ${e}`);
window.location.reload();
};
console.debug("Fetching app info for DLCs");
let key = Object.keys(root)[0];
NAME_ELEMENT.textContent = root[key].name;
console.debug("Checking key: " + key);
let dlc_price = (root[key].price_overview || {final: 0.0}).final / 100;
let unit = find_unit((root[key].price_overview || {final_formatted: "£0.00"}).final_formatted);
if(root[key].dlc && root[key].dlc.length >= 0) {
document.getElementById("dlcs").hidden = false;
document.getElementById("dlc-count").textContent = root[key].dlc.length + " ";
let _result= await calculate_price(root[key].dlc)
dlc_price += _result[0];
}
if(root[key].header_image && root[key].capsule_image) {
let element2 = document.createElement("img");
element2.src = root[key].header_image;
element2.alt = root[key].name;
element2.style.height = "2em"
NAME_ELEMENT.textContent = null;
NAME_ELEMENT.appendChild(element2);
}
update(dlc_price.toFixed(2), unit);
let element = document.getElementById("comparison");
element.hidden = false;
for(let item of Object.keys(PRICES)) {
let price = compare(dlc_price, item);
if(price >= 0.95) {
let li = document.createElement("li");
li.textContent = `${price}x ${item} (${unit}${PRICES[item]}/pc)`;
element.children[1].appendChild(li);
}
};
}
window.addEventListener("load", main);