Skip to content

Instantly share code, notes, and snippets.

@rzfury
Last active January 23, 2024 13:34
Show Gist options
  • Save rzfury/1b5d16e04152f6e3ff13987a94fe950d to your computer and use it in GitHub Desktop.
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
// ==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