ProtonDB SteamPlay Integration

Adds game ratings from ProtonDB to the Steam Store

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

You will need to install an extension such as Tampermonkey to install this script.

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

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