// ==UserScript== // @name ProtonDB Integration for Steam // @description Adds game ratings from ProtonDB to the Steam Store // @version 1.0.0 // @author guihkx // @match https://store.steampowered.com/app/* // @connect www.protondb.com // @run-at document-end // @noframes // @license MIT; https://opensource.org/licenses/MIT // @namespace https://github.com/guihkx // @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 // @downloadURL https://raw.githubusercontent.com/guihkx/user-scripts/master/scripts/protondb-integration-for-steam.user.js // @updateURL https://raw.githubusercontent.com/guihkx/user-scripts/master/scripts/protondb-integration-for-steam.user.js // @homepageURL https://github.com/guihkx/user-scripts // @supportURL https://github.com/guihkx/user-scripts/issues // ==/UserScript== /** * Changelog: * * v1.0.0 (2020-04-28): * - First release */ ;(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 tempPrefs = {} const userPrefs = { skip_native_games: GM_getValue('skip_native_games', true), open_in_new_tab: GM_getValue('open_in_new_tab', false), 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 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 = 'unavailable' } else { log('Got unexpected HTTP code from ProtonDB:', response.status) tier = 'error' } const container = Object.assign(document.createElement('div'), { className: 'protondb_rating_row', title: 'View on www.protondb.com' }) const subtitle = Object.assign(document.createElement('div'), { className: 'subtitle column', textContent: 'ProtonDB Score:' }) const link = Object.assign(document.createElement('a'), { className: `protondb_rating_link protondb_rating_${tier}`, href: `${PROTONDB_HOMEPAGE}/app/${appId}`, target: userPrefs.open_in_new_tab ? '_blank' : '_self' }) if ( 'confidence' in reports && userPrefs.show_confidence_level && PROTONDB_CONFIDENCE_LEVELS.includes(reports.confidence) ) { tier += ` (${reports.confidence} confidence)` } link.textContent = tier container.appendChild(subtitle) container.appendChild(link) const element = document.querySelector('.user_reviews') element.prepend(container) buildPreferencesDialog() } function buildPreferencesDialog () { const container = Object.assign(document.createElement('div'), { className: 'protondb_prefs_icon', title: 'Preferences for ProtonDB for Steam', textContent: '⚙' }) container.addEventListener('click', () => { // Clear any temporary preferences tempPrefs = {} const html = `