// ==UserScript==
// @name ProtonDB SteamPlay Integration
// @description Adds game ratings from ProtonDB to the Steam Store
// @version 0.3.0
// @author Phlebiac
// @match https://store.steampowered.com/app/*
// @connect www.protondb.com
// @run-at document-end
// @noframes
// @license MIT; https://opensource.org/licenses/MIT
// @namespace Phlebiac/ProtonDB
// @icon https://www.protondb.com/sites/protondb/images/apple-touch-icon.png
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_xmlhttpRequest
// @grant unsafeWindow
// ==/UserScript==
/* Inspired by / some ideas and code from:
* https://openuserjs.org/install/DanMan/Steam_Play_Community_Rating_Notice.user.js
* https://raw.githubusercontent.com/guihkx/user-scripts/master/scripts/protondb-integration-for-steam.user.js
*/
;(async () => {
'use strict'
const PROTONDB_TIERS = [
'pending',
'borked',
'bronze',
'silver',
'gold',
'platinum'
]
const PROTONDB_CONFIDENCE_LEVELS = ['low', 'moderate', 'good', 'strong']
const PROTONDB_HOMEPAGE = 'https://www.protondb.com'
let userPrefs = {
open_in_new_tab: GM_getValue('open_in_new_tab', false),
skip_native_games: GM_getValue('skip_native_games', true),
show_confidence_level: GM_getValue('show_confidence_level', true)
}
const appId = getCurrentAppId();
if (!appId) {
return;
}
if (userPrefs.skip_native_games) {
if (document.querySelector('span.platform_img.linux') !== null) {
log('Ignoring native Linux game:', appId);
return;
}
}
injectCSS();
GM_xmlhttpRequest({
method: 'GET',
url: `${PROTONDB_HOMEPAGE}/api/v1/reports/summaries/${appId}.json`,
onload: addRatingToStorePage
})
function getCurrentAppId() {
const urlPath = window.location.pathname;
const appId = urlPath.match(/\/app\/(\d+)/);
if (appId === null) {
log('Unable to get AppId from URL path:', urlPath);
return false;
}
return appId[1];
}
function addRatingToStorePage(response) {
let reports = {};
let tier = 'N/A';
if (response.status === 200) {
try {
reports = JSON.parse(response.responseText);
tier = reports.tier;
} catch (err) {
log('Unable to parse ProtonDB response as JSON:', response);
log('Javascript error:', err);
tier = 'error';
}
if (!PROTONDB_TIERS.includes(tier)) {
log('Unknown tier:', tier);
tier = 'unknown';
}
} else if (response.status === 404) {
log(`App ${appId} doesn't have a page on ProtonDB yet`);
tier = 'N/A';
} else {
log('Got unexpected HTTP code from ProtonDB:', response.status);
tier = 'error';
}
let confidence, tooltip = '';
if ('confidence' in reports && PROTONDB_CONFIDENCE_LEVELS.includes(reports.confidence)) {
confidence = reports.confidence;
tooltip = `Confidence: ${confidence}`;
if ('total' in reports) {
tooltip += ` with ${reports.total} reports`;
}
tooltip += ' | ';
}
let target = document.querySelector('.game_area_purchase_platform');
if (target) {
let node = Object.assign(document.createElement('span'), {
className: 'protondb_rating_row'
});
node.appendChild(preferencesDialog());
node.appendChild(createBadge(tier, confidence, tooltip, appId));
target.insertBefore(node, target.firstChild);
}
}
function createBadge(tier, confidence, tooltip, appId) {
let confidence_style = confidence && userPrefs.show_confidence_level ?` protondb_confidence_${confidence}` : '';
return Object.assign(document.createElement('a'), {
textContent: tier,
className: `protondb_rating_link protondb_rating_${tier}${confidence_style}`,
title: tooltip + 'View on www.protondb.com',
href: `${PROTONDB_HOMEPAGE}/app/${appId}`,
target: userPrefs.open_in_new_tab ? '_blank' : '_self'
});
}
function preferencesDialog() {
const container = Object.assign(document.createElement('span'), {
className: 'protondb_prefs_icon',
title: 'Preferences for ProtonDB SteamPlay Integration',
textContent: '⚙'
})
container.addEventListener('click', () => {
const html = `
<div class="protondb_prefs">
<div class="newmodal_prompt_description">
New preferences will only take effect after you refresh the page.
</div>
<blockquote>
<div>
<input type="checkbox" id="protondb_open_in_new_tab" ${ userPrefs.open_in_new_tab ? 'checked' : '' } />
<label for="protondb_open_in_new_tab">Open ProtonDB links in new tab</label>
</div>
<div>
<input type="checkbox" id="protondb_skip_native_games" ${ userPrefs.skip_native_games ? 'checked' : '' } />
<label for="protondb_skip_native_games">Don't check native Linux games</label>
</div>
<div>
<input type="checkbox" id="protondb_show_confidence_level" ${ userPrefs.show_confidence_level ? 'checked' : '' } />
<label for="protondb_show_confidence_level">Style based on confidence level of ratings</label>
</div>
</blockquote>
</div>`
unsafeWindow.ShowDialog('ProtonDB SteamPlay Prefs', html);
// Handle preferences changes
const inputs = document.querySelectorAll('.protondb_prefs input');
for (const input of inputs) {
input.addEventListener('change', event => {
const target = event.target;
const prefName = target.id.replace('protondb_', '');
switch (target.type) {
case 'text':
userPrefs[prefName] = target.value;
GM_setValue(prefName, target.value);
break;
case 'checkbox':
userPrefs[prefName] = target.checked;
GM_setValue(prefName, target.checked);
break;
default:
break;
}
})
}
})
return container;
}
function injectCSS() {
GM_addStyle(`
.protondb_rating_row {
text-transform: capitalize;
vertical-align: top;
}
.protondb_rating_link {
background: #4d4b49 url('https://support.steampowered.com/images/custom/platform_steamplay.png') no-repeat -51px center;
display: inline-block;
line-height: 19px;
font-size: 14px;
padding: 1px 10px 2px 79px;
margin-right: 1ex;
color: #b0aeac;
}
.protondb_rating_borked {
color: #FF1919 !important;
}
.protondb_rating_bronze {
color: #CD7F32 !important;
}
.protondb_rating_silver {
color: #C0C0C0 !important;
}
.protondb_rating_gold {
color: #FFD799 !important;
}
.protondb_rating_platinum {
color: #B4C7DC !important;
}
.protondb_confidence_low {
font-style: italic;
}
.protondb_confidence_moderate {
font-weight: normal;
}
.protondb_confidence_good {
font-weight: bold;
}
.protondb_confidence_strong {
font-variant: small-caps;
font-weight: bold;
}
.protondb_prefs_icon {
font-size: 16px;
padding: 0 4px;
cursor: pointer;
}
.protondb_prefs input[type="checkbox"], .protondb_prefs label {
line-height: 20px;
vertical-align: middle;
display: inline-block;
color: #66c0f4;
cursor: pointer;
}
.protondb_prefs blockquote {
margin: 15px 0 5px 10px;
}`)
}
function log() {
console.log('[ProtonDB SteamPlay Integration]', ...arguments)
}
})()