Greasy Fork is available in English.
Show rating and supported platforms on steamgifts.com according information from Steam
// ==UserScript== // @name steamgifts rating and platforms 2023 // @namespace Gidden // @version 3.0.0 // @description Show rating and supported platforms on steamgifts.com according information from Steam // @require http://code.jquery.com/jquery.min.js // @author Gidden, update by Титан // @match http://www.steamgifts.com/ // @match http://www.steamgifts.com/giveaways/search?* // @match https://www.steamgifts.com/ // @match https://www.steamgifts.com/giveaways/search?* // @grant GM_xmlhttpRequest // @license MIT // ==/UserScript== // I replaced find() funcs with my responseQuerySelector() and responseQuerySelectorAll() funcs because I don't get how to get find() to work with responseText //TODO: redirect bundles and calculate rating as max of all games in bundle let log = false; // Storage existence test function supports_html5_storage() { try { return 'localStorage' in window && window.localStorage !== null; } catch (e) { return false; } } // Get color according percentage (0% = Red, 100% = Green) function getColor(value){ //value from 0 to 1 var hue=((value)*120).toString(10); return ["hsl(",hue,",100%,42%)"].join(""); } var platforms = ["win", "mac", "linux", "steamplay"]; // Get actual configuration from storage function getFilterCfg(myItem) { if (myItem == "percentage") { myValue = 0; } else { myValue = 1; } if (supports_html5_storage()) { if (localStorage.getItem(myItem) !== null) { myValue = localStorage.getItem(myItem); } } return myValue; } // Store information about game function saveGameData(url, data) { if (supports_html5_storage()) { localStorage.setItem(url, JSON.stringify(data)); } } // Get stored information about game function loadGameData(url) { var myValue = null; if (supports_html5_storage()) { myValue = localStorage.getItem(url); if (myValue) { myValue = JSON.parse(myValue); } } return myValue; } // Filtering functions var filterFunction = "" + "var platforms = [\"win\", \"mac\", \"linux\", \"steamplay\"];" + "function supports_html5_storage() {" + " try {" + " return 'localStorage' in window && window.localStorage !== null;" + " } catch (e) {" + " return false;" + " }" + "}" + "function saveFilterCfg()" + "{" + " if (supports_html5_storage()) {" + " for (var x in platforms){" + " platform = platforms[x];" + " if (document.getElementById(\"filter_\" + platform).checked) {" + " localStorage.setItem(platform, 1);" + " } else {" + " localStorage.setItem(platform, 0);" + " }" + " }" + " if (document.getElementById(\"filter_level\").checked) {" + " localStorage.setItem(\"level\", 1);" + " } else {" + " localStorage.setItem(\"level\", 0);" + " }" + " localStorage.setItem(\"percentage\", document.getElementById(\"filter_percentage\").value);" + " }" + "}" + "function changeFilter(){" + "" + " platformCheck = [];" + " for (var x in platforms){" + " platform = platforms[x];" + " if (document.getElementById(\"filter_\" + platform).checked) {" + " platformCheck.push(platform);" + " }" + " }" + " percCheck = document.getElementById(\"filter_percentage\").value;" + "" + " var headers = $(\"div.giveaway__summary h2.giveaway__heading\");" + "" + " headers.each(function() {" + " var header = $(this);" + " var showEl = true;" + "" + " if (document.getElementById(\"filter_level\").checked){" + " if (header.parent().find(\"div.giveaway__column--contributor-level--negative\").length) {" + " header.parent().parent().parent().hide();" + " return;" + " }" + " }" + "" + " percE = header.find(\"span.percentage\");" + "" + " if (percE.length)" + " {" + " var perc2 = percE[0].getAttribute(\"perc\");" + " if (perc2 < percCheck){" + " showEl = false;" + " } else {" + " showEl = false;" + " for (var x in platformCheck){" + " actPlatformCheck = platformCheck[x];" + " if (header.find(\"span.\"+actPlatformCheck).length) {" + " showEl = true;" + " }" + " }" + " }" + " }" + "" + " if (showEl) {" + " header.parent().parent().parent().show();" + " } else {" + " header.parent().parent().parent().hide();" + " }" + " });" + " saveFilterCfg();" + "}"; function renderPluginGame(header, gameData) { if ((header === null) || (gameData === null) /*|| ("error" in gameData)*/){ return; } // Remove old informations header.find("a.giveaway__icon span").remove(); header.find("h2.giveaway__heading span").remove(); var a_tag = header.find( "a.giveaway__icon" ); var i_tag = header.find( "i.giveaway__icon" ); if (! i_tag.length) { i_tag = a_tag; } a_tag.attr("style", "opacity:1"); a_tag.find("i.fa-steam").attr("style", "opacity:.35"); // Render perc + ppl if ("perc" in gameData) { var rating = gameData.perc + '% (' + gameData.ppl + ') '; header.append(" <span style=\"color:" + getColor(gameData.perc /100.0) + "\" class=\"percentage\" perc=\"" + gameData.perc + "\">" + rating + "</span>"); } // Render platforms if ("platforms" in gameData) { //a_tag.append(" <span>" + platform.html() + "</span>"); } var platformsHtml = "<span>"; for (var platf in gameData.platforms) { platformsHtml = platformsHtml + "<span class=\"platform_img " + platf + "\"></span>"; } a_tag.append(platformsHtml + "</span>"); // Render cards if (("cards" in gameData) && (gameData.cards)) { i_tag.after("<span class=\"platform_img right_allign\"><img src=\"http://store.akamai.steamstatic.com/public/images/v6/ico/ico_cards.png\"></span>"); } if ((gameData.perc < getFilterCfg("percentage"))) { header.parent().parent().parent().hide(); } hide = gameData.platforms?.length > 0; for (var x in platforms) { if (hide) break; platformCheck = platforms[x]; if ((gameData.platforms[platformCheck] || false) && (getFilterCfg(platformCheck) == 1)) { hide = false; } } if (hide) { header.parent().parent().parent().hide(); } } /** * Looks for a HTMLElement with {@link selector} in the responseText * @param responseText array of HTMLElements and shit * @param selector css selector * @returns {null|HTMLElement} first HTMLElement matching {@link selector} or null if not found */ function responseQuerySelector(responseText, selector) { for (var i = responseText.length-1; i >= 0 ; i--) { var currentResponse = responseText[i]; if (/*currentResponse.classList?.contains("responsive_page_frame") &&*/ currentResponse.querySelector) { // if it's a HTMLElement /*and is a responsive_page_frame (basically a whole page)*/ var node = currentResponse.querySelector(selector); if (node) { return node; } } } return null; } /** * Looks for a HTMLElements with {@link selector} in the responseText * @param responseText array of HTMLElements and shit * @param selector css selector * @returns {null|HTMLElement} array of HTMLElements matching {@link selector} or empty array if not found */ function responseQuerySelectorAll(responseText, selector) { var nodes = []; for (var i = responseText.length-1; i >= 0 ; i--) { var currentResponse = responseText[i]; if (currentResponse.classList?.contains("responsive_page_frame") && currentResponse.querySelectorAll) { // if it's a HTMLElement and is a responsive_page_frame (basically a whole page) var node = currentResponse.querySelectorAll(selector); if (node) { nodes.push(node); } } } return nodes; } function exportDataFromPage(steamURL, responseText, callFunction) { var gameData = {}; gameData.ppl = false; gameData.perc = false; gameData.platforms = {}; gameData.cards = false; gameData.fetchTime = new Date().getTime(); var ratingFull = responseQuerySelector(responseText,".responsive_reviewdesc_short"); if (!ratingFull === null) { gameData.error = true; saveGameData(steamURL, gameData); } else { let stage = "rating" let debugVar = ratingFull; try { // extract percentare review (perc) and peoples reviewed (ppl) variables. var ratingFullString = ratingFull.innerText.trim(); var perc = ratingFullString.match("[0-9]+ ?%"); if (perc === null) { perc = ratingFullString.match("% ?[0-9]+"); } gameData.perc = perc[0].replace("%", "").replace(" ", ""); ppl = ratingFullString.match(/[0-9,]+/g); if (ppl[0] == gameData.perc) { ppl = ppl[1]; } else { ppl = ppl[0]; } gameData.ppl = ppl.replace(",", "."); stage = "platforms" // extract platforms var platform = responseQuerySelector(responseText, "div.game_area_purchase_platform"); debugVar = platform; if (platform) { for (var x in platforms) { var actPlatformCheck = platforms[x]; if (platform.querySelector("span." + actPlatformCheck)) { gameData.platforms[actPlatformCheck] = true; } } } stage = "cards" // extract support of gaming cards var cards; cards = responseQuerySelector(responseText, "[class=\"game_area_details_specs_ctn\"][href=\"https://store.steampowered.com/search/?category2=29&snr=1_5_9__423\"]") debugVar = cards; gameData.cards = cards !== null; stage = "save" // Save saveGameData(steamURL, gameData); //renderPluginGame(header, gameData); if (callFunction !== null) { callFunction(steamURL, gameData); } } catch (e) { gameData.error = true; saveGameData(steamURL, gameData); if(log) { console.log(`[Steamgifts rating] Error on ${stage}: ` + e) console.log(debugVar) console.log(responseText) } } } return gameData; } function fetchSteamDate(steamURL, callFunction) { var gameData = {}; GM_xmlhttpRequest({ method: "GET", url: steamURL, context: steamURL, // synchronous: true, onload: function(response) { var responseText = $(response.responseText); var ageCheck = responseQuerySelector(responseText, "div#agegate_box") if (ageCheck) { GM_xmlhttpRequest({ method: "POST", data: "snr=1_agecheck_agecheck__age-gate&ageDay=1&ageMonth=January&ageYear=1980", headers: { "Content-Type": "application/x-www-form-urlencoded" }, url: steamURL.replace("/app/","/agecheck/app/"), context: steamURL, onload: function(response) { var responseText = $(response.responseText); exportDataFromPage(steamURL, responseText, callFunction); } }); } else { exportDataFromPage(steamURL, responseText, callFunction); } } }); return gameData; } //! MAIN function // Generate html string for checkboxes var checkboxes = ""; for (var x in platforms){ platform = platforms[x]; var checked = ""; if (getFilterCfg(platform) == 1) { checked = "checked"; } checkboxes += "<input class=\"filter_checkboxes\" type=\"checkbox\" name=\"filter_" + platform + "\" id=\"filter_" + platform + "\" onclick=\"changeFilter();\" " + checked + "><label class=\"checkboxes\" for=\"filter_" + platform + "\"><span class=\"platform_img " + platform + "\"></span></label>"; } // New headers. Mainly css styles $("head link:last") .before("<link rel=stylesheet type=text/css href=https://steamstore-a.akamaihd.net/public/css/v6/store.css>") .after("<style>span.platform_img {background-color: black; height:20px; display: inline-block; opacity: 0.35;} input.filter_checkboxes {height:15px; width:20px;} input.filter_percentage {height:18px; width:40px; margin:5px; padding:0px 10px;} label.checkboxes {margin-right:5px;}</style>") .after("<style>span.percentage {font-size: medium; font-weight: bold;} h2.giveaway__heading {position:relative;} span img {width: 20px; height: 16px;} span.right_allign {position:absolute; right: 0; top: 0;}</style>") .after("<script type=\"text/javascript\">" + filterFunction + "</script>"); var checkedLevel = ""; if (getFilterCfg("level") == 1) { checkedLevel = "checked"; } // Appending left filter panel filterPanel = "<h3 class=\"sidebar__heading\">Filter</h3><div style=\"margin-left:10px;\">" + " <span style=\"font-weight: bold;\">Game</span> >= <input type=\"text\" class=\"filter_percentage\" id=\"filter_percentage\" value=\"" + getFilterCfg("percentage") + "\" onchange=\"changeFilter()\" onkeyup=\"changeFilter()\">% <br>" + checkboxes + "</br>" + "<input type=\"checkbox\" class=\"filter_checkboxes\" id=\"filter_level\" onclick=\"changeFilter();\" " + checkedLevel + "><label class=\"checkboxes\" for=\"filter_level\"><span style=\"font-weight: bold;\">Hide higher level</span></label>" + "</div>"; $("div.sidebar__search-container").after(filterPanel); // Fetching steam for games information var needFetch = {}; var headers = $("div.giveaway__summary h2.giveaway__heading"); var actTime = new Date().getTime(); headers.each(function() { var header = $(this); var iconElement = header.find("a.giveaway__icon"); if (iconElement.length) { var steamURL = iconElement.attr("href").trim(); if ((getFilterCfg("level") == 1) && (header.parent().find("div.giveaway__column--contributor-level--negative").length)) { header.parent().parent().parent().hide(); } var gameData = loadGameData(steamURL); if (gameData === null || gameData.error === true) { needFetch[steamURL] = true; } else { renderPluginGame(header, gameData); // If game info are older than one day then fetch new info. if ((actTime - gameData.fetchTime) > 24*60*60*1000) { needFetch[steamURL] = true; } } } }); function afterFetch(steamURL, gameData) { var headers = $("a[href=\"" + steamURL + "\"]"); headers.each(function() { var header = $(this).parent(); renderPluginGame(header, gameData); }); } for(var steamURL in needFetch) { var gameData = fetchSteamDate(steamURL, afterFetch); }