// 👋 Hola, usa 🐒Tampermonkey 👇 // https://www.tampermonkey.net/ // ==UserScript== // @name Xbox Store Price & Deals Filter // @name:es Filtros de Precio y Ofertas para Xbox Store // @name:it Filtri Prezzi e Offerte per Xbox Store // @name:fr Filtres de Prix et Offres pour Xbox Store // @name:de Preis- und Angebotsfilter für Xbox Store // @namespace https://jlcareglio.github.io/ // @version 0.8.14 // @description Add price range filters and deal filters to Xbox store. Filter by Game Pass discounts, specific discount percentages, and price ranges. // @description:es Agrega filtros de rango de precios y ofertas a la tienda Xbox. Filtra por descuentos de Game Pass, porcentajes específicos de descuento y rangos de precios. // @description:it Aggiunge filtri per fascia di prezzo e offerte allo store Xbox. Filtra per sconti Game Pass, percentuali di sconto specifiche e fasce di prezzo. // @description:fr Ajoute des filtres de gamme de prix et d'offres au magasin Xbox. Filtre par réductions Game Pass, pourcentages de réduction spécifiques et gammes de prix. // @description:de Fügt Preisbereich- und Angebotsfilter zum Xbox-Store hinzu. Filtert nach Game Pass-Rabatten, spezifischen Rabattprozenten und Preisbereichen. // @icon https://www.google.com/s2/favicons?sz=64&domain=xbox.com // @grant none // @author Jesús Lautaro Careglio Albornoz // @source https://gist.githubusercontent.com/JLCareglio/9cbddea558658f695983a64b9cece6a6/raw/ // @match https://www.xbox.com/*/games/all-games* // @match https://www.xbox.com/*/games/browse* // @match https://www.xbox.com/*/Search/Result* // @supportURL https://gist.githubusercontent.com/JLCareglio/9cbddea558658f695983a64b9cece6a6/ // @downloadURL none // ==/UserScript== // MARK: CONSTANTS const SELECTORS = { GAME_PASS_DISCOUNT: "Price-module__afterPriceTextContainer___r7fdq", DISCOUNT_TAG: "ProductCard-module__discountTag___OjGFy", FINAL_PRICE: "ProductCard-module__price___cs1xr", FILTER_LIST: "SortAndFilters-module__filterList___T81LH", BUTTON_APPLY_FILTERS: "ApplyFiltersButton-module__applyButton___faTvE", BUTTON_SHOW_FILTERS: "SortAndFilters-module__button___OeFeU", LOAD_MORE_ROW: "BrowsePage-module__loadMoreRow___sx0qx", XBOX_SPINNER_FILTER: "XboxSpinner-module__xenonFilter___lbfRN", }; // MARK: DOM HELPER const DOMHelper = { waitForElement(selector) { return new Promise((resolve) => { if (document.querySelector(selector)) { return resolve(document.querySelector(selector)); } const observer = new MutationObserver(() => { if (document.querySelector(selector)) { observer.disconnect(); resolve(document.querySelector(selector)); } }); observer.observe(document.documentElement, { childList: true, subtree: true, }); }); }, }; // MARK: FILTER CREATOR const FilterCreator = { createFilter(id, title, options, additionalContent = "") { const li = document.createElement("li"); li.className = "SortAndFilters-module__li___aV+Oo"; const svgExpanded = ` `; const svgCollapsed = ` `; li.innerHTML = `
`; return li; }, addFilterHandlers(filterElement, filterFn, stateManager) { const button = filterElement.querySelector( ".SelectionDropdown-module__titleContainer___YyoD0" ); const optionsDiv = filterElement.querySelector("div[style]"); const options = filterElement.querySelectorAll( ".Selections-module__selectionContainer___m2xzM" ); button.addEventListener("click", () => { const isExpanded = button.getAttribute("aria-expanded") === "true"; button.setAttribute("aria-expanded", !isExpanded); optionsDiv.classList.toggle("hidden"); }); options.forEach((option) => { const input = option.querySelector("input"); if (!input) return; const toggleFilter = () => { const isSelected = option.getAttribute("aria-selected") === "true"; const newState = !isSelected; if (input.type === "radio") { options.forEach((opt) => { const otherInput = opt.querySelector('input[type="radio"]'); if (otherInput && otherInput.name === input.name) { opt.setAttribute("aria-selected", "false"); opt.setAttribute("aria-checked", "false"); otherInput.checked = false; } }); } option.setAttribute("aria-selected", newState); option.setAttribute("aria-checked", newState); input.checked = newState; // Aplicar filtro filterFn(); stateManager.save(); }; option.addEventListener("click", (e) => { if (e.target !== input) { e.preventDefault(); toggleFilter(); } }); input.addEventListener("change", () => { if (input.type === "radio") { options.forEach((opt) => { const otherInput = opt.querySelector('input[type="radio"]'); if (otherInput && otherInput.name === input.name) { opt.setAttribute("aria-selected", otherInput.checked); opt.setAttribute("aria-checked", otherInput.checked); } }); } else { option.setAttribute("aria-selected", input.checked); option.setAttribute("aria-checked", input.checked); } // Aplicar filtro filterFn(); stateManager.save(); }); }); }, }; // MARK: FILTER LOGIC const FilterLogic = { applyAllFilters() { const productCards = document.querySelectorAll( ".ProductCard-module__cardWrapper___6Ls86" ); const selectedDiscountFilter = document.querySelector('input[name="discountGroup"]:checked')?.value || "none"; const onlyGamePass = document.querySelector("#gamePassOnly_checkbox")?.checked || false; const customDiscountPercent = parseInt( document.querySelector("#customDiscountPercent")?.value || "0" ); productCards.forEach((card) => { const minPrice = parseFloat(document.querySelector("#priceMin").value) || 0; const maxPrice = parseFloat(document.querySelector("#priceMax").value) || Infinity; const showFree = document.querySelector("#free_checkbox").checked; const showPaid = document.querySelector("#paid_checkbox").checked; const showUnpurchasable = document.querySelector( "#unpurchasable_checkbox" ).checked; const priceElement = card.querySelector(`.${SELECTORS.FINAL_PRICE}`); let shouldShow = true; if (!priceElement) { shouldShow = showUnpurchasable; } else { const priceText = priceElement.innerText; const hasNumbers = /\d/.test(priceText); const isFree = !hasNumbers; if (isFree) { shouldShow = showFree; } else { shouldShow = showPaid; if (shouldShow) { const priceString = priceText.trim().replace(/[^\d.,]/g, ""); let price; const match = priceString.match(/^(.*?)[\.,](\d{2})$/); if (match) { const integerPart = match[1].replace(/[.,]/g, ""); const decimalPart = match[2]; price = parseFloat(`${integerPart}.${decimalPart}`); } else { price = parseFloat(priceString.replace(/[.,]/g, "")); } shouldShow = !isNaN(price) && price >= minPrice && (maxPrice === 0 || price <= maxPrice); } } } // Verificar descuentos if (shouldShow) { const discountTag = card.querySelector(`.${SELECTORS.DISCOUNT_TAG}`); const hasGamePassDiscount = card.querySelector( `.${SELECTORS.GAME_PASS_DISCOUNT}` ); const discountPercentage = discountTag ? parseInt(discountTag.innerText.replace(/[^0-9]/g, "")) : 0; if (onlyGamePass && selectedDiscountFilter !== "none") { shouldShow = hasGamePassDiscount ? true : false; // Si tiene Game Pass, ahora verificamos el porcentaje de descuento if (shouldShow) { switch (selectedDiscountFilter) { case "anyDiscount": shouldShow = discountTag !== null; break; case "discount50plus": shouldShow = discountPercentage >= 50; break; case "discount75plus": shouldShow = discountPercentage >= 75; break; case "discountCustom": shouldShow = discountPercentage >= customDiscountPercent; break; } } } else if (onlyGamePass) { shouldShow = hasGamePassDiscount ? true : false; } else if (selectedDiscountFilter !== "none") { switch (selectedDiscountFilter) { case "anyDiscount": shouldShow = discountTag !== null; break; case "discount50plus": shouldShow = discountPercentage >= 50; break; case "discount75plus": shouldShow = discountPercentage >= 75; break; case "discountCustom": shouldShow = discountPercentage >= customDiscountPercent; break; } } } card.parentElement.style.display = shouldShow ? "" : "none"; }); }, }; // MARK: STATE MANAGER class FilterStateManager { constructor() { this.state = { priceMin: "", priceMax: "", free: true, paid: true, unpurchasable: true, discountGroup: "none", customDiscountPercent: "", gamePassOnly: false, }; } save() { this.state = { priceMin: document.querySelector("#priceMin")?.value || "", priceMax: document.querySelector("#priceMax")?.value || "", free: document.querySelector("#free_checkbox")?.checked || false, paid: document.querySelector("#paid_checkbox")?.checked || false, unpurchasable: document.querySelector("#unpurchasable_checkbox")?.checked || false, discountGroup: document.querySelector('input[name="discountGroup"]:checked')?.value || "none", customDiscountPercent: document.querySelector("#customDiscountPercent")?.value || "", gamePassOnly: document.querySelector("#gamePassOnly_checkbox")?.checked || false, }; } load() { if (!this.state) return; if (document.querySelector("#priceMin")) document.querySelector("#priceMin").value = this.state.priceMin; if (document.querySelector("#priceMax")) document.querySelector("#priceMax").value = this.state.priceMax; if (document.querySelector("#free_checkbox")) document.querySelector("#free_checkbox").checked = this.state.free; if (document.querySelector("#paid_checkbox")) document.querySelector("#paid_checkbox").checked = this.state.paid; if (document.querySelector("#unpurchasable_checkbox")) document.querySelector("#unpurchasable_checkbox").checked = this.state.unpurchasable; if ( this.state.discountGroup !== "none" && document.querySelector(`#${this.state.discountGroup}_radio`) ) { document.querySelector( `#${this.state.discountGroup}_radio` ).checked = true; } if (document.querySelector("#customDiscountPercent")) document.querySelector("#customDiscountPercent").value = this.state.customDiscountPercent; if (document.querySelector("#gamePassOnly_checkbox")) document.querySelector("#gamePassOnly_checkbox").checked = this.state.gamePassOnly; } } // MARK: MAIN class XboxStoreFilter { constructor() { this.isInitialized = false; this.stateManager = new FilterStateManager(); this.lastFilterList = null; } async initialize() { const filterList = await DOMHelper.waitForElement( `.${SELECTORS.FILTER_LIST}` ); if (filterList) { this.setupFilters(filterList); this.setupObservers(); this.setupEventListeners(); } } setupFilters(filterList) { const existingPriceFilter = filterList.querySelector("#PriceRange"); const existingOffersFilter = filterList.querySelector("#Offers"); if (existingPriceFilter) existingPriceFilter.closest("li")?.remove(); if (existingOffersFilter) existingOffersFilter.closest("li")?.remove(); const priceFilterFn = () => { FilterLogic.applyAllFilters(); this.stateManager.save(); return true; }; const offersFilterFn = () => { FilterLogic.applyAllFilters(); this.stateManager.save(); return true; }; const priceFilter = FilterCreator.createFilter( "PriceRange", "Precio", [ { id: "free", label: "Mostrar Gratis", defaultSelected: this.stateManager.state.free, }, { id: "paid", label: "Mostrar de Pago", defaultSelected: this.stateManager.state.paid, }, { id: "unpurchasable", label: "Mostrar Incomprable", defaultSelected: this.stateManager.state.unpurchasable, }, ], `
Más de
Menos de
` ); const offersFilter = FilterCreator.createFilter("Offers", "Oferta", [ { id: "anyDiscount", label: "Con descuento", type: "radio", group: "discountGroup", }, { id: "discount50plus", label: "50% o más", type: "radio", group: "discountGroup", }, { id: "discount75plus", label: "75% o más", type: "radio", group: "discountGroup", }, { id: "discountCustom", label: `% o más`, type: "radio", group: "discountGroup", }, { id: "gamePassOnly", label: "Solo con Game Pass", type: "checkbox" }, ]); filterList.appendChild(priceFilter); filterList.appendChild(offersFilter); FilterCreator.addFilterHandlers( priceFilter, priceFilterFn, this.stateManager ); FilterCreator.addFilterHandlers( offersFilter, offersFilterFn, this.stateManager ); const priceInputs = document.querySelectorAll("#priceMin, #priceMax"); priceInputs.forEach((input) => { input.addEventListener("change", () => { FilterLogic.applyAllFilters(); this.stateManager.save(); }); }); document .querySelector("#customDiscountPercent") ?.addEventListener("change", (e) => { const radio = document.querySelector("#discountCustom_radio"); if (radio) { radio.checked = true; radio.dispatchEvent(new Event("change")); this.stateManager.save(); } }); this.stateManager.load(); FilterLogic.applyAllFilters(); } setupObservers() { const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.type === "childList") { const currentFilterList = document.querySelector( `.${SELECTORS.FILTER_LIST}` ); // Si aparece una nueva lista de filtros y es diferente a la última observada if (currentFilterList && currentFilterList !== this.lastFilterList) { this.lastFilterList = currentFilterList; this.setupFilters(currentFilterList); this.isInitialized = true; } // Si desaparece la lista de filtros, reiniciamos para la próxima if (!document.querySelector(`.${SELECTORS.FILTER_LIST}`)) { this.isInitialized = false; this.lastFilterList = null; } } }); }); observer.observe(document.body, { childList: true, subtree: true, }); } setupEventListeners() { const applyButton = document.querySelector( `.${SELECTORS.BUTTON_APPLY_FILTERS}` ); if (applyButton) { applyButton.addEventListener("click", () => { FilterLogic.applyAllFilters(); this.stateManager.save(); }); } } } // MARK: INITIALIZE APP (async () => { const app = new XboxStoreFilter(); await app.initialize(); })();