A rewrite of Poly+, my quality-of-life browser extension for Polytoria. Built entirely fresh using the WXT extension framework, Typescript, and with added better overall code quality.
extension
1import { cache } from "./storage"; 2import { cacheInterface } from "./types"; 3import * as currencyPackages from "./currencyPackages.json" 4 5export async function pullCache(key: string, replenish: Function, expiry: number, forceReplenish: boolean) { 6 const cacheStorage: cacheInterface = await cache.getValue(); 7 const metadata = await cache.getMeta() as { [key: string]: number; }; 8 9 const overlap = Date.now() - metadata[key]; 10 if (!cacheStorage[key] || forceReplenish || (overlap >= expiry)) { 11 console.info('[Poly+] "' + key + '" cache is stale replenishing...', timeAgo(overlap)); 12 13 const replenishedCache = await replenish(); 14 15 // Don't cache the response when the config file has APIs disabled 16 if (replenishedCache != "disabled") { 17 cacheStorage[key] = replenishedCache; 18 metadata[key] = Date.now(); 19 20 cache.setValue(cacheStorage); 21 cache.setMeta(metadata); 22 } else { 23 return "disabled"; 24 }; 25 }; 26 27 return cacheStorage[key]; 28}; 29 30export async function expireCache(key: string) { 31 console.info('[Poly+] Forcefully expiring "' + key + '" cache...'); 32 33 const metadata = await cache.getMeta() as { [key: string]: number }; 34 metadata[key] = 0; 35 cache.setMeta(metadata); 36}; 37 38function timeAgo(overlap: number) { 39 const units = [ 40 { label: 'day', value: 24 * 60 * 60 * 1000 }, 41 { label: 'hour', value: 60 * 60 * 1000 }, 42 { label: 'min', value: 60 * 1000 }, 43 { label: 'sec', value: 1000 }, 44 ]; 45 46 for (const { label, value } of units) { 47 const count = Math.floor(overlap / value); 48 if (count > 0) { 49 return `${count} ${label}${count > 1 ? 's' : ''} ago`; 50 } 51 overlap %= value; 52 } 53 54 return 'just now'; 55}; 56 57export async function getUserDetails() { 58 const profileLink: HTMLLinkElement = document.querySelector('.navbar a.text-reset[href^="/users/"]')!; 59 const brickBalance = document.getElementsByClassName('brickBalanceCount')[0]; 60 61 if (!profileLink || !brickBalance) return null; 62 return { 63 username: profileLink.innerText.trim(), 64 userId: parseInt(profileLink.href.split('/')[4]), 65 bricks: parseInt(brickBalance.textContent!.replace(/,/g, "")) 66 } 67}; 68 69export function bricksToCurrency(bricks: number, currency: string): string | null { 70 if (isNaN(bricks) || bricks == 0) return null; 71 72 const _currencyPackages = currencyPackages as Record<string, Array<Array<number>>>; 73 const packages = _currencyPackages[currency].toSorted((a, b) => b[1] - a[1]); 74 75 if (!packages) { 76 console.warn('[Poly+] Missing currency package data for selected currency!'); 77 return null; 78 } 79 80 let totalValue = 0; 81 for (const [currencyValue, bricksValue] of packages) { 82 while (bricks >= bricksValue) { 83 bricks -= bricksValue; 84 totalValue += currencyValue; 85 } 86 } 87 88 if (bricks > 0) { 89 const cheapestPackage = packages[packages.length - 1]; 90 const [currencyValue, bricksValue] = cheapestPackage; 91 const unitPrice = currencyValue / bricksValue; 92 totalValue += bricks * unitPrice; 93 } 94 95 return `~${totalValue.toFixed(2)} ${currency}`; 96};