// ==UserScript== // @name Backloggd - Steam Review Integration - Dark Mode // @namespace https://greasyfork.org/en/users/1410951-nzar-bayezid // @version 1.0 // @description Displays Steam reviews in unified format // @icon https://www.backloggd.com/favicon.ico // @require https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js // @match https://backloggd.com/* // @match https://www.backloggd.com/* // @grant GM_xmlhttpRequest // @connect store.steampowered.com // @license MIT // @downloadURL // @updateURL // @downloadURL https://update.greasyfork.icu/scripts/525561/Backloggd%20-%20Steam%20Review%20Integration%20-%20Dark%20Mode.user.js // @updateURL https://update.greasyfork.icu/scripts/525561/Backloggd%20-%20Steam%20Review%20Integration%20-%20Dark%20Mode.meta.js // ==/UserScript== /*========================= Version History ================================== v1.0 - - Dark Mode - Integrated Steam review data into Backloggd game pages - Unified UI styling for consistent appearance with existing integrations - Added support for displaying both recent and all-time Steam reviews - Implemented dynamic color coding based on review sentiment (e.g., Positive, Negative) - Optimized API request handling for Steam app ID and review data fetching - Ensured proper error handling and fallbacks for missing or unavailable data - Enhanced performance by minimizing redundant DOM manipulations - Added MutationObserver to dynamically handle page updates and new content loading - Improved localization of review counts using `toLocaleString` for better readability */ (function() { 'use strict'; const OBSERVER_CONFIG = { childList: true, subtree: true, attributes: false, characterData: false }; const REVIEW_COLORS = { 'Overwhelmingly Positive': '#16181c', 'Very Positive': '#16181c', 'Positive': '#16181c', 'Mostly Positive': '#16181c', 'Mixed': '#16181c', 'Mostly Negative': '#16181c', 'Negative': '#16181c', 'Very Negative': '#16181c', 'Overwhelmingly Negative': '#16181c' }; let processing = false; let currentPath = ''; async function mainExecutor() { if (processing) return; if (location.pathname === currentPath) return; if (!document.querySelector('#game-body')) return; currentPath = location.pathname; processing = true; cleanExistingElements(); await processGameData(); processing = false; } function cleanExistingElements() { $('.steam-integration').remove(); } async function processGameData() { try { const gameName = document.querySelector("#title h1").textContent; const appId = await fetchSteamAppId(gameName); if (!appId) return; const reviewData = await fetchSteamReviews(appId); renderSteamReview(reviewData, appId); } catch (error) { console.error('Steam Integration Error:', error); } } function fetchSteamAppId(gameName) { return new Promise((resolve) => { GM_xmlhttpRequest({ method: "GET", url: `https://store.steampowered.com/search/?term=${encodeURIComponent(gameName)}&category1=998`, headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36' }, onload: function(response) { const parser = new DOMParser(); const doc = parser.parseFromString(response.responseText, "text/html"); const firstResult = doc.querySelector('.search_result_row'); resolve(firstResult?.dataset.dsAppid || null); }, onerror: () => resolve(null), timeout: 5000 }); }); } function fetchSteamReviews(appId) { return new Promise((resolve) => { GM_xmlhttpRequest({ method: "GET", url: `https://store.steampowered.com/app/${appId}`, headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36', 'Accept-Language': 'en-US,en;q=0.9' }, onload: function(response) { try { const parser = new DOMParser(); const doc = parser.parseFromString(response.responseText, "text/html"); const userReviews = doc.querySelector('#userReviews'); if (!userReviews) return resolve(null); // Extract Recent Reviews const recent = userReviews.querySelector('.user_reviews_summary_row:nth-child(1)'); const recentRating = recent?.querySelector('.game_review_summary')?.textContent; const recentCount = extractNumber(recent?.querySelector('.responsive_hidden')?.textContent); // Extract All Reviews const all = userReviews.querySelector('.user_reviews_summary_row[itemprop="aggregateRating"]'); const allRating = all?.querySelector('.game_review_summary')?.textContent; const allCount = extractNumber(all?.querySelector('.responsive_hidden')?.textContent); resolve({ recent: { rating: recentRating, count: recentCount }, all: { rating: allRating, count: allCount } }); } catch { resolve(null); } }, onerror: () => resolve(null), timeout: 5000 }); }); } function extractNumber(text) { const match = text?.match(/\(([\d,]+)\)/); return match ? parseInt(match[1].replace(/,/g, '')) : null; } function formatReviewCount(num) { if (!num) return 'N/A'; return num.toLocaleString(); } function renderSteamReview(reviewData, appId) { if (!reviewData || !reviewData.all?.rating) return; const target = $("#game-body > div.col > div:nth-child(2) > div.col-12.col-lg-cus-32.mt-1.mt-lg-2"); if (!target.length) return; const color = REVIEW_COLORS[reviewData.all.rating] || '#607D8B'; const content = []; if (reviewData.recent?.rating) { content.push(`