import config from "@/utils/config.json"; import { cache } from "./storage"; import { cacheInterface } from "./types"; import { avatarApiSchema } from "./api/types"; import * as currencyPackages from "./currencyPackages.json"; import { getAPI } from "./api"; export async function pullCache( key: string, replenish: Function, expiry: number, forceReplenish: boolean, ) { const cacheStorage: cacheInterface = await cache.getValue(); const metadata = (await cache.getMeta()) as { [key: string]: number }; const overlap = Date.now() - (metadata[key] || 0); const shouldReplenish = !cacheStorage[key] || cacheStorage[key] == "disabled" || !metadata[key] || forceReplenish || (expiry !== -1 && overlap >= expiry); if (shouldReplenish) { if (config.devBuild) { console.info( `[Poly+] "${key}" cache ${ expiry === -1 ? "doesn't exist" : "is stale" } replenishing...`, expiry !== -1 ? timeAgo(overlap) : "", ); } const replenishedCache = await replenish(); // Don't cache the response when the config file has APIs disabled if (replenishedCache !== "disabled") { cacheStorage[key] = replenishedCache; metadata[key] = Date.now(); await cache.setValue(cacheStorage); await cache.setMeta(metadata); } else { return "disabled"; } } return cacheStorage[key]; } export async function pullKVCache( store: string, key: string, replenish: Function, expiry: number, forceReplenish: boolean, ) { const cacheStorage: cacheInterface = await cache.getValue(); const metadata = (await cache.getMeta()) as { [key: string]: Record; }; if (!cacheStorage[store]) cacheStorage[store] = {}; if (!metadata[store]) metadata[store] = {}; const overlap = Date.now() - (metadata[store][key] || 0); const shouldReplenish = !cacheStorage[store][key] || cacheStorage[store][key] == "disabled" || forceReplenish || (expiry !== -1 && overlap >= expiry); if (shouldReplenish) { if (config.devBuild) { console.info( `[Poly+] "${key}" KV cache ${ expiry === -1 ? "doesn't exist" : "is stale" } replenishing...`, expiry !== -1 ? timeAgo(overlap) : "", ); } const replenishedCache = await replenish(); // Don't cache the response when the config file has APIs disabled if (replenishedCache !== "disabled") { cacheStorage[store][key] = replenishedCache; metadata[store][key] = Date.now(); await cache.setValue(cacheStorage); await cache.setMeta(metadata); } else { return "disabled"; } } return cacheStorage[store][key]; } export async function expireCache(key: string) { console.info('[Poly+] Forcefully expiring "' + key + '" cache...'); const metadata = await cache.getMeta() as { [key: string]: number }; metadata[key] = 0; cache.setMeta(metadata); } function timeAgo(overlap: number) { const units = [ { label: "day", value: 24 * 60 * 60 * 1000 }, { label: "hour", value: 60 * 60 * 1000 }, { label: "min", value: 60 * 1000 }, { label: "sec", value: 1000 }, ]; for (const { label, value } of units) { const count = Math.floor(overlap / value); if (count > 0) { return `${count} ${label}${count > 1 ? "s" : ""} ago`; } overlap %= value; } return "just now"; } export async function getUserDetails() { const publicAPI = getAPI("public"); const profileLink: HTMLLinkElement = document.querySelector( '.navbar a.text-reset[href^="/users/"]', )!; const brickBalance = document.getElementsByClassName("brickBalanceCount")[0]; if (!profileLink || !brickBalance) return null; const userId = parseInt(profileLink.href.split("/")[4]); return { username: profileLink.innerText.trim(), userId: userId, bricks: parseInt(brickBalance.textContent!.replace(/,/g, "")), getAvatar: async () => { if (publicAPI.enabled) { const avatar = await (await fetch( publicAPI.url + "users/" + userId + "/avatar", )).json(); return avatar as avatarApiSchema; } else { return "disabled"; } }, }; } export function bricksToCurrency( bricks: number, currency: string, ): string | null { if (isNaN(bricks) || bricks == 0) return null; const _currencyPackages = currencyPackages as { base: Array; fromUSD: Record; updatedAt: string; }; const packages = _currencyPackages.base.toSorted((a, b) => b[1] - a[1]); let totalValue = 0; for (const [currencyValue, bricksValue] of packages) { while (bricks >= bricksValue) { bricks -= bricksValue; totalValue += currencyValue; } } if (bricks > 0) { const cheapestPackage = packages[packages.length - 1]; const [currencyValue, bricksValue] = cheapestPackage; const unitPrice = currencyValue / bricksValue; totalValue += bricks * unitPrice; } if (currency != "USD") { if (!_currencyPackages.fromUSD[currency]) { console.warn( `[Poly+] Missing conversion from USD to ${currency}`, ); return null; } totalValue *= _currencyPackages.fromUSD[currency]; } return `~${totalValue.toFixed(2)} ${currency}`; }