Last active
January 23, 2024 13:34
-
-
Save rzfury/1b5d16e04152f6e3ff13987a94fe950d to your computer and use it in GitHub Desktop.
A userscript to add an earning estimation section for steamdb inside chart section
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Rough earning estimation for steamdb | |
// @namespace http://tampermonkey.net/ | |
// @version 2024-01-23 | |
// @description Userscript to add an earning section for steamdb in chart section | |
// @author RZFury | |
// @match https://steamdb.info/app/* | |
// @icon https://www.google.com/s2/favicons?sz=64&domain=steamdb.info | |
// @updateURL https://gist.githubusercontent.com/rzfury/1b5d16e04152f6e3ff13987a94fe950d/raw/9a02911546ebcc087b4cc49ab3bdec8eb03ab065/userscript-steamdb-game-earning.js | |
// @downloadURL https://gist.githubusercontent.com/rzfury/1b5d16e04152f6e3ff13987a94fe950d/raw/9a02911546ebcc087b4cc49ab3bdec8eb03ab065/userscript-steamdb-game-earning.js | |
// @grant none | |
// ==/UserScript== | |
(function () { | |
'use strict'; | |
let interval = setInterval(() => { | |
const row = document.querySelector('.row.row-app-charts'); | |
if (row) { | |
const prices = getPrices(); | |
if (isNaN(prices[0]) || isNaN(prices[1])) { | |
clearInterval(interval); | |
return; | |
} | |
const newRow = document.createElement('div'); | |
const span1 = document.createElement('div'); | |
span1.className = 'span4'; | |
earningSteamReviewCount().forEach(el => span1.appendChild(el)); | |
const span2 = document.createElement('div'); | |
span2.className = 'span4'; | |
earningBoxleiter().forEach(el => span2.appendChild(el)); | |
const span3 = document.createElement('div'); | |
span3.className = 'span4'; | |
earningOwnerEstimationSum().forEach(el => span3.appendChild(el)); | |
row.after(newRow); | |
newRow.className = 'row row-app-charts'; | |
newRow.appendChild(span1); | |
newRow.appendChild(span2); | |
newRow.appendChild(span3); | |
clearInterval(interval); | |
} | |
}, 100); | |
// Your code here... | |
})(); | |
function earningSteamReviewCount() { | |
const ulEl = document.createElement('ul'); | |
ulEl.classList.add('app-chart-numbers'); | |
const titleEl = document.createElement('h3'); | |
titleEl.textContent = 'Earning by Steam review count'; | |
const [idrGamePrice, usdGamePrice] = getPrices(); | |
const rv = getReviewCount(); | |
const grossIdr = rv * idrGamePrice; | |
const grossUsd = rv * usdGamePrice; | |
const infoIdr = additionalInfo(grossIdr); | |
const infoUsd = additionalInfo(grossUsd); | |
const liEl1 = document.createElement('li'); | |
liEl1.innerHTML = `<div style="color:var(--contrast-color);font-weight:700">Gross Revenue:</div><div style="color:var(--contrast-color);font-size:20px;font-weight:700"><span class="muted">~</span>${num2USD(grossUsd)}</div><div style="margin-bottom:4px"></div><div style="display:flex;justify-content:space-between;"><span>Adj. Regional Pricing</span><span>${num2USD(infoUsd.adjustedRegionalPricing)}</span></div><div style="display:flex;justify-content:space-between;"><span>Discounts</span><span>${num2USD(infoUsd.discounts)}</span></div><div style="display:flex;justify-content:space-between;"><span>Refunds</span><span>${num2USD(infoUsd.refunds)}</span></div><div style="display:flex;justify-content:space-between;"><span>Steam cut</span><span>${num2USD(infoUsd.steamFee)}</span></div><div style="display:flex;justify-content:space-between;"><span>VAT / SALES TAX</span><span>${num2USD(infoUsd.vat)}</span></div><div style="margin-bottom:4px"></div><div style="color:var(--contrast-color);font-weight:700">NET Revenue:</div><div style="color:var(--contrast-color);font-size:20px;font-weight:700"><span class="muted">~</span>${num2USD(infoUsd.netRevenue)}</div>`; | |
const liEl2 = document.createElement('li'); | |
liEl2.innerHTML = `<div style="margin-block:6px;width:100%;height:0.5px;background-color:var(--muted-color)"></div><div style="color:var(--contrast-color);font-weight:700">Gross Revenue:</div><div style="color:var(--contrast-color);font-size:20px;font-weight:700"><span class="muted">~</span>${num2IDR(grossIdr)}</div><div style="margin-bottom:4px"></div><div style="display:flex;justify-content:space-between;"><span>Adj. Regional Pricing</span><span>${num2IDR(infoIdr.adjustedRegionalPricing)}</span></div><div style="display:flex;justify-content:space-between;"><span>Discounts</span><span>${num2IDR(infoIdr.discounts)}</span></div><div style="display:flex;justify-content:space-between;"><span>Refunds</span><span>${num2IDR(infoIdr.refunds)}</span></div><div style="display:flex;justify-content:space-between;"><span>Steam cut</span><span>${num2IDR(infoIdr.steamFee)}</span></div><div style="display:flex;justify-content:space-between;"><span>VAT / SALES TAX</span><span>${num2IDR(infoIdr.vat)}</span></div><div style="margin-bottom:4px"></div><div style="color:var(--contrast-color);font-weight:700">NET Revenue:</div><div style="color:var(--contrast-color);font-size:20px;font-weight:700"><span class="muted">~</span>${num2IDR(infoIdr.netRevenue)}</div>`; | |
ulEl.appendChild(liEl1); | |
ulEl.appendChild(liEl2); | |
return [titleEl, ulEl]; | |
} | |
function earningBoxleiter() { | |
const ulEl = document.createElement('ul'); | |
ulEl.classList.add('app-chart-numbers'); | |
const titleEl = document.createElement('h3'); | |
titleEl.textContent = 'Earning by Boxleiter Method'; | |
const [idrGamePrice, usdGamePrice] = getPrices(); | |
const rv = getReviewCount(); | |
const grossIdr = boxleiterGrossRev(rv, idrGamePrice); | |
const grossUsd = boxleiterGrossRev(rv, usdGamePrice); | |
const infoIdr = additionalInfo(grossIdr); | |
const infoUsd = additionalInfo(grossUsd); | |
const liEl1 = document.createElement('li'); | |
liEl1.innerHTML = `<div style="color:var(--contrast-color);font-weight:700">Gross Revenue:</div><div style="color:var(--contrast-color);font-size:20px;font-weight:700"><span class="muted">~</span>${num2USD(grossUsd)}</div><div style="margin-bottom:4px"></div><div style="display:flex;justify-content:space-between;"><span>Adj. Regional Pricing</span><span>${num2USD(infoUsd.adjustedRegionalPricing)}</span></div><div style="display:flex;justify-content:space-between;"><span>Discounts</span><span>${num2USD(infoUsd.discounts)}</span></div><div style="display:flex;justify-content:space-between;"><span>Refunds</span><span>${num2USD(infoUsd.refunds)}</span></div><div style="display:flex;justify-content:space-between;"><span>Steam cut</span><span>${num2USD(infoUsd.steamFee)}</span></div><div style="display:flex;justify-content:space-between;"><span>VAT / SALES TAX</span><span>${num2USD(infoUsd.vat)}</span></div><div style="margin-bottom:4px"></div><div style="color:var(--contrast-color);font-weight:700">NET Revenue:</div><div style="color:var(--contrast-color);font-size:20px;font-weight:700"><span class="muted">~</span>${num2USD(infoUsd.netRevenue)}</div>`; | |
const liEl2 = document.createElement('li'); | |
liEl2.innerHTML = `<div style="margin-block:6px;width:100%;height:0.5px;background-color:var(--muted-color)"></div><div style="color:var(--contrast-color);font-weight:700">Gross Revenue:</div><div style="color:var(--contrast-color);font-size:20px;font-weight:700"><span class="muted">~</span>${num2IDR(grossIdr)}</div><div style="margin-bottom:4px"></div><div style="display:flex;justify-content:space-between;"><span>Adj. Regional Pricing</span><span>${num2IDR(infoIdr.adjustedRegionalPricing)}</span></div><div style="display:flex;justify-content:space-between;"><span>Discounts</span><span>${num2IDR(infoIdr.discounts)}</span></div><div style="display:flex;justify-content:space-between;"><span>Refunds</span><span>${num2IDR(infoIdr.refunds)}</span></div><div style="display:flex;justify-content:space-between;"><span>Steam cut</span><span>${num2IDR(infoIdr.steamFee)}</span></div><div style="display:flex;justify-content:space-between;"><span>VAT / SALES TAX</span><span>${num2IDR(infoIdr.vat)}</span></div><div style="margin-bottom:4px"></div><div style="color:var(--contrast-color);font-weight:700">NET Revenue:</div><div style="color:var(--contrast-color);font-size:20px;font-weight:700"><span class="muted">~</span>${num2IDR(infoIdr.netRevenue)}</div>`; | |
ulEl.appendChild(liEl1); | |
ulEl.appendChild(liEl2); | |
return [titleEl, ulEl]; | |
} | |
function earningOwnerEstimationSum() { | |
const ulEl = document.createElement('ul'); | |
ulEl.classList.add('app-chart-numbers'); | |
const titleEl = document.createElement('h3'); | |
titleEl.textContent = 'Earning by Owner estimation sum'; | |
const [idrGamePrice, usdGamePrice] = getPrices(); | |
let nthChild = 3; | |
if (!document.querySelector('.row.row-app-charts > :nth-child(3)')) nthChild = 2; | |
const ownerEstimations = Array.from(document.querySelectorAll(`.row.row-app-charts > :nth-child(${nthChild}) li`)) | |
.map(li => { | |
const str = li.querySelector('strong').textContent; | |
const unitMul = unit2Number(str); | |
return parseFloat(str.replace('~', '').slice(0, -2)) * unitMul; | |
}); | |
const totalOwner = Math.floor(ownerEstimations.reduce((p, c) => p + c, 0) / ownerEstimations.length) || 0; | |
const grossIdr = totalOwner * idrGamePrice; | |
const grossUsd = totalOwner * usdGamePrice; | |
const infoIdr = additionalInfo(grossIdr); | |
const infoUsd = additionalInfo(grossUsd); | |
const liEl1 = document.createElement('li'); | |
liEl1.innerHTML = `<div style="color:var(--contrast-color);">(SUM of Estimations) / ${ownerEstimations.length} * Price</div>`; | |
const liEl2 = document.createElement('li'); | |
liEl2.innerHTML = `<div style="margin-block:6px;width:100%;height:0.5px;background-color:var(--muted-color)"></div><div style="color:var(--contrast-color);font-weight:700">Gross Revenue:</div><div style="color:var(--contrast-color);font-size:20px;font-weight:700"><span class="muted">~</span>${num2USD(grossUsd)}</div><div style="margin-bottom:4px"></div><div style="display:flex;justify-content:space-between;"><span>Adj. Regional Pricing</span><span>${num2USD(infoUsd.adjustedRegionalPricing)}</span></div><div style="display:flex;justify-content:space-between;"><span>Discounts</span><span>${num2USD(infoUsd.discounts)}</span></div><div style="display:flex;justify-content:space-between;"><span>Refunds</span><span>${num2USD(infoUsd.refunds)}</span></div><div style="display:flex;justify-content:space-between;"><span>Steam cut</span><span>${num2USD(infoUsd.steamFee)}</span></div><div style="display:flex;justify-content:space-between;"><span>VAT / SALES TAX</span><span>${num2USD(infoUsd.vat)}</span></div><div style="margin-bottom:4px"></div><div style="color:var(--contrast-color);font-weight:700">NET Revenue:</div><div style="color:var(--contrast-color);font-size:20px;font-weight:700"><span class="muted">~</span>${num2USD(infoUsd.netRevenue)}</div>`; | |
const liEl3 = document.createElement('li'); | |
liEl3.innerHTML = `<div style="margin-block:6px;width:100%;height:0.5px;background-color:var(--muted-color)"></div><div style="color:var(--contrast-color);font-weight:700">Gross Revenue:</div><div style="color:var(--contrast-color);font-size:20px;font-weight:700"><span class="muted">~</span>${num2IDR(grossIdr)}</div><div style="margin-bottom:4px"></div><div style="display:flex;justify-content:space-between;"><span>Adj. Regional Pricing</span><span>${num2IDR(infoIdr.adjustedRegionalPricing)}</span></div><div style="display:flex;justify-content:space-between;"><span>Discounts</span><span>${num2IDR(infoIdr.discounts)}</span></div><div style="display:flex;justify-content:space-between;"><span>Refunds</span><span>${num2IDR(infoIdr.refunds)}</span></div><div style="display:flex;justify-content:space-between;"><span>Steam cut</span><span>${num2IDR(infoIdr.steamFee)}</span></div><div style="display:flex;justify-content:space-between;"><span>VAT / SALES TAX</span><span>${num2IDR(infoIdr.vat)}</span></div><div style="margin-bottom:4px"></div><div style="color:var(--contrast-color);font-weight:700">NET Revenue:</div><div style="color:var(--contrast-color);font-size:20px;font-weight:700"><span class="muted">~</span>${num2IDR(infoIdr.netRevenue)}</div>`; | |
ulEl.appendChild(liEl1); | |
ulEl.appendChild(liEl2); | |
ulEl.appendChild(liEl3); | |
return [titleEl, ulEl]; | |
} | |
function getReviewCount() { | |
const metaRvCount = document.querySelector('meta[itemprop="reviewCount"]'); | |
if (metaRvCount) { | |
return parseFloat(metaRvCount.getAttribute('content')); | |
} | |
const ulEl = document.querySelector('#charts .row.row-app-charts > :nth-child(1) .app-chart-numbers'); | |
let pRv = parseFloat(ulEl.querySelector(':nth-child(2) > strong').textContent.replace(',', '')); | |
let nRv = parseFloat(ulEl.querySelector(':nth-child(3) > strong').textContent.replace(',', '')); | |
let rv = pRv + nRv; | |
if (!isNaN(rv)) return rv; | |
pRv = parseFloat(ulEl.querySelector(':nth-child(3) > strong').textContent.replace(',', '')); | |
nRv = parseFloat(ulEl.querySelector(':nth-child(4) > strong').textContent.replace(',', '')); | |
rv = pRv + nRv; | |
return rv; | |
} | |
function boxleiterGrossRev(review, price) { | |
return review * (() => review < 999 ? 20 : review < 9999 ? 36 : review < 49999 ? 49 : review < 99999 ? 59 : 48)() * price | |
} | |
function additionalInfo(grossRevenue) { | |
let adjustedRegionalPricing = grossRevenue * 0.09; | |
let discounts = grossRevenue * 0.2; | |
let refunds = grossRevenue * 0.12; | |
let r = grossRevenue - adjustedRegionalPricing - discounts - refunds; | |
let steamFee = r * 0.3; | |
let vat = r * 0.2; | |
let netRevenue = r - steamFee - vat; | |
return { | |
adjustedRegionalPricing, | |
discounts, | |
grossRevenue, | |
netRevenue, | |
refunds, | |
steamFee, | |
vat | |
} | |
} | |
function getPrices() { | |
return Array.from(document.querySelectorAll('#prices > div.table-responsive > table > tbody > tr')).filter(tr => tr.innerHTML.includes('id.svg') || tr.innerHTML.includes('us.svg')).map(tr => parseFloat(tr.querySelector('td:nth-child(2)').textContent.replace(/[^0-9.]/g, ''))); | |
} | |
function unit2Number(str) { | |
switch (str.toLowerCase().slice(-1)) { | |
case 'k': return 1_000; | |
case 'm': return 1_000_000; | |
default: return 1; | |
} | |
} | |
function num2USD(num) { | |
return num.toLocaleString('en-US', { currency: 'USD', style: 'currency', maximumFractionDigits: 0 }); | |
} | |
function num2IDR(num) { | |
return num.toLocaleString('id-ID', { currency: 'IDR', style: 'currency', maximumFractionDigits: 0 }); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment