::Steam Search: Hide Games Under Minimum Price::

Hides games by minimum price set by the user, also can hide no-reviews or mixed/negative reviewed games on Steam search.

  1. // ==UserScript==
  2. // @name ::Steam Search: Hide Games Under Minimum Price::
  3. // @namespace masterofobzene-Hide Games Under Minimum Price
  4. // @version 1.5
  5. // @description Hides games by minimum price set by the user, also can hide no-reviews or mixed/negative reviewed games on Steam search.
  6. // @author masterofobzene
  7. // @homepage https://github.com/masterofobzene/UserScriptRepo
  8. // @icon https://store.steampowered.com/favicon.ico
  9. // @match https://store.steampowered.com/search*
  10. // @license GNU GPLv3
  11. // @grant none
  12. // ==/UserScript==
  13.  
  14. (function () {
  15. 'use strict';
  16.  
  17. // Load settings from localStorage or use defaults
  18. let minPrice = parseFloat(localStorage.getItem('minPrice')) || 5.00;
  19. let enablePriceFilter = localStorage.getItem('enablePriceFilter') === 'true' || true;
  20. let hideMixedNegative = localStorage.getItem('hideMixedNegative') === 'true' || false;
  21. let hideNoRating = localStorage.getItem('hideNoRating') === 'true' || false;
  22.  
  23. function createToggleUI() {
  24. const insertAfter = document.querySelector('#narrow_category1');
  25. if (!insertAfter) {
  26. console.warn('Could not find #narrow_category1 to insert filters.');
  27. return;
  28. }
  29.  
  30. const container = document.createElement('div');
  31. container.className = 'additional_filters_ctn';
  32. container.style.padding = '10px';
  33. container.style.marginTop = '10px';
  34. container.style.backgroundColor = '#2a475e';
  35. container.style.border = '1px solid #66c0f4';
  36. container.style.color = '#c6d4df';
  37. container.style.fontSize = '13px';
  38.  
  39. container.innerHTML = `
  40. <div style="margin-bottom: 6px;"><strong>🧹 Custom Filter</strong></div>
  41.  
  42. <label style="display: block; margin-bottom: 6px;">
  43. <input type="checkbox" id="enablePriceFilter" ${enablePriceFilter ? 'checked' : ''}> Enable Price Filter
  44. </label>
  45. <label style="display: block; margin-bottom: 4px;">
  46. Min Price: $<span id="priceValue">${minPrice.toFixed(2)}</span><br>
  47. <input type="range" id="priceSlider" min="0" max="60" step="0.5" value="${minPrice}" style="width: 100%;">
  48. </label>
  49.  
  50. <hr style="margin: 10px 0; border-color: #66c0f4;">
  51.  
  52. <label style="display: block; margin-bottom: 4px;">
  53. <input type="checkbox" id="hideMixedNegative" ${hideMixedNegative ? 'checked' : ''}> Hide Mixed/Negative
  54. </label>
  55. <label style="display: block;">
  56. <input type="checkbox" id="hideNoRating" ${hideNoRating ? 'checked' : ''}> Hide No Rating
  57. </label>
  58. `;
  59.  
  60. insertAfter.parentNode.insertBefore(container, insertAfter.nextSibling);
  61.  
  62. document.getElementById('enablePriceFilter').addEventListener('change', e => {
  63. enablePriceFilter = e.target.checked;
  64. localStorage.setItem('enablePriceFilter', enablePriceFilter); // Save setting
  65. console.log('Enable Price Filter:', enablePriceFilter);
  66. hideLowPriceGames();
  67. });
  68.  
  69. document.getElementById('priceSlider').addEventListener('input', e => {
  70. minPrice = parseFloat(e.target.value);
  71. localStorage.setItem('minPrice', minPrice); // Save setting
  72. document.getElementById('priceValue').textContent = minPrice.toFixed(2);
  73. hideLowPriceGames();
  74. });
  75.  
  76. document.getElementById('hideMixedNegative').addEventListener('change', e => {
  77. hideMixedNegative = e.target.checked;
  78. localStorage.setItem('hideMixedNegative', hideMixedNegative); // Save setting
  79. console.log('Hide Mixed/Negative:', hideMixedNegative);
  80. hideLowPriceGames();
  81. });
  82.  
  83. document.getElementById('hideNoRating').addEventListener('change', e => {
  84. hideNoRating = e.target.checked;
  85. localStorage.setItem('hideNoRating', hideNoRating); // Save setting
  86. console.log('Hide No Rating:', hideNoRating);
  87. hideLowPriceGames();
  88. });
  89. }
  90.  
  91. function hideLowPriceGames() {
  92. const rows = document.querySelectorAll('.search_result_row');
  93. rows.forEach(row => {
  94. let shouldHide = false;
  95.  
  96. const priceElement = row.querySelector('.discount_final_price');
  97. if (!priceElement) return;
  98.  
  99. const priceText = priceElement.textContent.trim();
  100. if (/free/i.test(priceText)) {
  101. shouldHide = enablePriceFilter;
  102. } else {
  103. const priceMatch = priceText.match(/\$(\d+\.\d{2})/);
  104. if (priceMatch && enablePriceFilter) {
  105. const price = parseFloat(priceMatch[1]);
  106. if (price < minPrice) shouldHide = true;
  107. }
  108. }
  109.  
  110. const reviewElement = row.querySelector('.search_review_summary');
  111. const hasRating = !!reviewElement;
  112. const ratingClass = reviewElement?.classList?.[1];
  113.  
  114. if (hideNoRating && !hasRating) {
  115. shouldHide = true;
  116. }
  117.  
  118. if (hideMixedNegative && (ratingClass === 'mixed' || ratingClass === 'negative')) {
  119. shouldHide = true;
  120. }
  121.  
  122. row.style.display = shouldHide ? 'none' : '';
  123. });
  124. }
  125.  
  126. function setupObserver() {
  127. const resultsContainer = document.getElementById('search_resultsRows');
  128. if (!resultsContainer) return;
  129.  
  130. const observer = new MutationObserver(() => {
  131. hideLowPriceGames();
  132. });
  133.  
  134. observer.observe(resultsContainer, {
  135. childList: true,
  136. subtree: true
  137. });
  138. }
  139.  
  140. function waitForSidebarAndInit() {
  141. const checkInterval = setInterval(() => {
  142. const anchor = document.querySelector('#narrow_category1');
  143. if (anchor) {
  144. clearInterval(checkInterval);
  145. createToggleUI();
  146. hideLowPriceGames();
  147. setupObserver();
  148. }
  149. }, 300);
  150. }
  151.  
  152. waitForSidebarAndInit();
  153. })();