Compare game prices across Iceland, Ireland, Turkey and United States
Size
8.8 KB
Version
1.1.2
Created
Oct 26, 2025
Updated
about 1 month ago
1// ==UserScript==
2// @name Microsoft Store Regional Price Checker
3// @description Compare game prices across Iceland, Ireland, Turkey and United States
4// @version 1.1.2
5// @match https://*.apps.microsoft.com/*
6// @icon https://apps.microsoft.com/favicon.ico
7// @grant GM.xmlhttpRequest
8// ==/UserScript==
9(function() {
10 'use strict';
11
12 // Regional configurations
13 const REGIONS = [
14 { name: 'Iceland', code: 'is', language: 'is-is' },
15 { name: 'Ireland', code: 'IE', language: 'en-ie' },
16 { name: 'Turkey', code: 'tr', language: 'tr-tr' },
17 { name: 'United States', code: 'us', language: 'en-us' }
18 ];
19
20 // Extract product ID from URL
21 function getProductId() {
22 const match = window.location.pathname.match(/\/detail\/([^/?]+)/);
23 return match ? match[1] : null;
24 }
25
26 // Fetch price for a specific region
27 async function fetchRegionalPrice(productId, region) {
28 const url = `https://apps.microsoft.com/detail/${productId}?hl=${region.language}&gl=${region.code}`;
29
30 console.log(`Fetching price for ${region.name} from: ${url}`);
31
32 return new Promise((resolve) => {
33 GM.xmlhttpRequest({
34 method: 'GET',
35 url: url,
36 onload: function(response) {
37 try {
38 const parser = new DOMParser();
39 const doc = parser.parseFromString(response.responseText, 'text/html');
40
41 let price = 'Not found';
42
43 // Try to find price in span with aria-label containing "Purchase"
44 const purchaseSpan = doc.querySelector('span[aria-label*="Purchase"]');
45 if (purchaseSpan) {
46 price = purchaseSpan.textContent.trim();
47 console.log(`Found price in purchase span for ${region.name}: ${price}`);
48 resolve({ region: region.name, price: price });
49 return;
50 }
51
52 // Try to find sale price in span with class "current-price"
53 const salePriceSpan = doc.querySelector('span.current-price');
54 if (salePriceSpan) {
55 price = salePriceSpan.textContent.trim();
56 console.log(`Found sale price for ${region.name}: ${price}`);
57 resolve({ region: region.name, price: price });
58 return;
59 }
60
61 // Try to find price in meta tags
62 const priceMetaTag = doc.querySelector('meta[property="product:price:amount"]') ||
63 doc.querySelector('meta[name="price"]');
64 if (priceMetaTag) {
65 const amount = priceMetaTag.getAttribute('content');
66 const currencyTag = doc.querySelector('meta[property="product:price:currency"]');
67 const currency = currencyTag ? currencyTag.getAttribute('content') : '';
68 price = currency + amount;
69 console.log(`Found price in meta tag for ${region.name}: ${price}`);
70 resolve({ region: region.name, price: price });
71 return;
72 }
73
74 // Search for price patterns in the entire HTML text
75 const bodyText = doc.body.textContent;
76
77 // Try different currency patterns
78 const patterns = [
79 /€\s*[\d,]+\.?\d*/g, // Euro
80 /\$\s*[\d,]+\.?\d*/g, // Dollar
81 /£\s*[\d,]+\.?\d*/g, // Pound
82 /₺\s*[\d,]+\.?\d*/g, // Turkish Lira
83 /kr\s*[\d,]+\.?\d*/gi, // Krona
84 /[\d,]+\.?\d*\s*€/g, // Euro after number
85 /[\d,]+\.?\d*\s*\$/g, // Dollar after number
86 /[\d,]+\.?\d*\s*kr/gi // Krona after number
87 ];
88
89 for (const pattern of patterns) {
90 const matches = bodyText.match(pattern);
91 if (matches && matches.length > 0) {
92 // Get the first reasonable price (not 0 or very small)
93 for (const match of matches) {
94 const numValue = parseFloat(match.replace(/[^\d.]/g, ''));
95 if (numValue > 1) {
96 price = match.trim();
97 console.log(`Found price for ${region.name}: ${price}`);
98 break;
99 }
100 }
101 if (price !== 'Not found') break;
102 }
103 }
104
105 // If still not found, look for "Free" text
106 if (price === 'Not found' && /\bfree\b/i.test(bodyText)) {
107 price = 'Free';
108 }
109
110 console.log(`Final price for ${region.name}: ${price}`);
111 resolve({ region: region.name, price: price });
112 } catch (error) {
113 console.error(`Error parsing price for ${region.name}:`, error);
114 resolve({ region: region.name, price: 'Error' });
115 }
116 },
117 onerror: function(error) {
118 console.error(`Failed to fetch price for ${region.name}:`, error);
119 resolve({ region: region.name, price: 'Failed to load' });
120 }
121 });
122 });
123 }
124
125 // Create and style the price comparison box
126 function createPriceBox() {
127 const box = document.createElement('div');
128 box.id = 'regional-price-box';
129 box.innerHTML = `
130 <div style="font-weight: bold; margin-bottom: 10px; font-size: 16px; color: #0067b8;">
131 Regional Prices
132 </div>
133 <div id="price-list" style="font-size: 14px;">
134 <div style="color: #666;">Loading prices...</div>
135 </div>
136 `;
137
138 box.style.cssText = `
139 position: fixed;
140 top: 100px;
141 right: 20px;
142 background: white;
143 border: 2px solid #0067b8;
144 border-radius: 8px;
145 padding: 20px;
146 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
147 z-index: 10000;
148 min-width: 280px;
149 max-width: 350px;
150 font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
151 `;
152
153 document.body.appendChild(box);
154 return box;
155 }
156
157 // Update the price list in the box
158 function updatePriceList(prices) {
159 const priceList = document.getElementById('price-list');
160 if (!priceList) return;
161
162 priceList.innerHTML = prices.map(item => `
163 <div style="display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #e0e0e0;">
164 <span style="font-weight: 500; color: #333;">${item.region}:</span>
165 <span style="color: #0067b8; font-weight: bold;">${item.price}</span>
166 </div>
167 `).join('');
168 }
169
170 // Main initialization function
171 async function init() {
172 console.log('Microsoft Store Regional Price Checker initialized');
173
174 const productId = getProductId();
175 if (!productId) {
176 console.log('No product ID found in URL');
177 return;
178 }
179
180 console.log('Product ID:', productId);
181
182 // Wait for page to be fully loaded
183 if (document.readyState === 'loading') {
184 await new Promise(resolve => {
185 document.addEventListener('DOMContentLoaded', resolve);
186 });
187 }
188
189 // Create the price box
190 const priceBox = createPriceBox();
191
192 // Fetch prices for all regions
193 const pricePromises = REGIONS.map(region => fetchRegionalPrice(productId, region));
194 const prices = await Promise.all(pricePromises);
195
196 // Update the display
197 updatePriceList(prices);
198 }
199
200 // Start the extension
201 if (document.readyState === 'loading') {
202 document.addEventListener('DOMContentLoaded', init);
203 } else {
204 init();
205 }
206})();