ProtonDB SteamPlay Integration

Adds game ratings from ProtonDB to the Steam Store

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==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)
  }
})()