// ==UserScript== // @name Grepolis Data Sender (Deep Scan v4.8 — Fixed Town ID) // @namespace http://tampermonkey.net/ // @version 4.9 // @description Sends town stats and merges marketplace offers into correct town (robust ID detection) // @author Dimitrios // @match https://*.grepolis.com/game/* // @grant unsafeWindow // @updateURL https://git.haunter-pets.top/haunter/grepodata/raw/branch/main/Grepolis_Data_Sender.user.js // @downloadURL https://git.haunter-pets.top/haunter/grepodata/raw/branch/main/Grepolis_Data_Sender.user.js // ==/UserScript== (function () { 'use strict'; console.log("🧠 DeepScan v4.9 initialized..."); const uw = typeof unsafeWindow !== "undefined" ? unsafeWindow : window; let latestBasicPayload = null; // 🔄 Send town snapshot and cache function sendBasicStats() { try { const towns = uw.ITowns?.towns || {}; const player = uw.Game?.player_name || "unknown"; const player_id = uw.Game?.player_id || "unknown"; const stats = Object.values(towns).map(town => { const res = town.resources(); const buildings = town.buildings()?.attributes ?? {}; const marketLevel = typeof town.getMarketplaceLevel === "function" ? town.getMarketplaceLevel() : buildings.market || null; return { town_id: town.id, town_name: town.name, wood: res.wood, stone: res.stone, iron: res.iron, population: res.population, points: town.getPoints(), buildings, market: { level: marketLevel } }; }); latestBasicPayload = { type: "basic", timestamp: new Date().toISOString(), player, player_id, towns: stats }; fetch("https://grepo.haunter-pets.top/api/grepolis-data", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(latestBasicPayload) }) .then(res => res.text()) .then(txt => console.log("✅ Basic stats sent:", txt)) .catch(err => console.error("❌ Failed to send basic stats:", err)); } catch (e) { console.error("💥 Error collecting basic stats:", e); } } // 📄 Parse marketplace trade offers function parseMarketOffers() { const rows = document.querySelectorAll(".js-tutorial-market-offers-table tbody tr"); const offers = []; rows.forEach(row => { const cells = row.querySelectorAll("td"); if (cells.length < 10) return; const extractType = (td) => { const icon = td.querySelector("span"); if (!icon) return ""; if (icon.classList.contains("market_wood_icon")) return "wood"; if (icon.classList.contains("market_stone_icon")) return "stone"; if (icon.classList.contains("market_iron_icon")) return "iron"; return ""; }; offers.push({ offer_id: row.dataset.offerId || null, give_type: extractType(cells[0]), give_amount: cells[1]?.textContent.trim() || "", get_amount: cells[4]?.textContent.trim() || "", get_type: extractType(cells[5]), duration: cells[7]?.textContent.trim() || "", player: cells[8]?.querySelector(".gp_player_link")?.textContent.trim() || "unknown" }); }); return offers; } // 🧬 Merge trades into correct town object function sendMergedMarketStats() { try { const offers = parseMarketOffers(); if (offers.length === 0) { console.warn("⚠️ No marketplace offers detected"); return; } const townId = uw.Game?.townId || uw.ITowns?.getCurrentTown()?.id || uw.current_town_id || "unknown"; console.log("🏙️ Active Town ID:", townId); console.log("📋 Available Town IDs:", latestBasicPayload?.towns?.map(t => t.town_id)); if (!latestBasicPayload || !latestBasicPayload.towns) { console.warn("🚫 Basic stats not yet initialized"); return; } const town = latestBasicPayload.towns.find(t => String(t.town_id) === String(townId)); if (!town) { console.warn(`🧭 No matching town found for ID ${townId}`); return; } town.market.offers = offers; latestBasicPayload.timestamp = new Date().toISOString(); fetch("https://grepo.haunter-pets.top/api/grepolis-data", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(latestBasicPayload) }) .then(res => res.text()) .then(txt => console.log("✅ Merged trades sent:", txt)) .catch(err => console.error("❌ Failed to send merged data:", err)); } catch (err) { console.error("💥 Error during marketplace merge:", err); } } // 👁️ Watch for marketplace panel opening function monitorMarketplace() { const observer = new MutationObserver(() => { const panel = document.querySelector('.marketplace.all_offers'); if (panel && panel.offsetParent !== null) { console.log("👁️ Marketplace opened — merging trades"); observer.disconnect(); sendMergedMarketStats(); setInterval(sendMergedMarketStats, 30000); } }); observer.observe(document.body, { childList: true, subtree: true }); } // 🕓 Start script after town data is ready function waitForTowns() { const towns = uw.ITowns?.towns; if (towns && Object.keys(towns).length > 0) { console.log("🧭 Towns ready — starting DeepScan"); sendBasicStats(); setInterval(sendBasicStats, 30000); monitorMarketplace(); } else { console.log("⏳ Waiting for town data..."); setTimeout(waitForTowns, 1000); } } window.addEventListener("load", () => { console.log("🚀 Game page loaded — DeepScan v4.8 booting..."); waitForTowns(); }); })();