diff --git a/Grepolis_Data_Sender.js b/Grepolis_Data_Sender.js index 71731fd..b304748 100644 --- a/Grepolis_Data_Sender.js +++ b/Grepolis_Data_Sender.js @@ -1,97 +1,179 @@ // ==UserScript== -// @name Grepolis Data Sender (Deep Scan Edition) +// @name Grepolis Data Sender (Deep Scan v4.8 — Fixed Town ID) // @namespace http://tampermonkey.net/ -// @version 3.1 -// @description Full diagnostics + HTTPS endpoint for secure data transmission +// @version 4.8 +// @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://your-gitea-domain.com/dimitrios/grepolis-scripts/raw/branch-name/Grepolis-Data-Sender.user.js +// @downloadURL https://your-gitea-domain.com/dimitrios/grepolis-scripts/raw/branch-name/Grepolis-Data-Sender.user.js // ==/UserScript== (function () { 'use strict'; - console.log("🔍 [DeepScan] Initializing Grepolis data sender..."); - const uw = typeof unsafeWindow !== "undefined" - ? (console.log("✅ [DeepScan] unsafeWindow available"), unsafeWindow) - : typeof wrappedJSObject !== "undefined" - ? (console.log("✅ [DeepScan] wrappedJSObject fallback"), wrappedJSObject) - : (console.log("⚠️ [DeepScan] Using default window"), window); + console.log("🧠 DeepScan v4.8 initialized..."); - function collectAndSendData() { + const uw = typeof unsafeWindow !== "undefined" ? unsafeWindow : window; + let latestBasicPayload = null; + + // 🔄 Send town snapshot and cache + function sendBasicStats() { try { - console.log("🚀 [DeepScan] Collecting town data..."); - + const towns = uw.ITowns?.towns || {}; const player = uw.Game?.player_name || "unknown"; const player_id = uw.Game?.player_id || "unknown"; - const towns = uw.ITowns?.towns || {}; - const townEntries = Object.values(towns); - console.log(`📊 [DeepScan] Found ${townEntries.length} towns in ITowns.towns`); - if (townEntries.length === 0) { - console.warn("⚠️ [DeepScan] Town data structure is present but empty"); - return; - } + 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; - const stats = townEntries.map(town => { - try { - const resources = town.resources(); - const buildings = town.buildings()?.attributes ?? {}; - return { - town_id: town.id, - town_name: town.name, - wood: resources.wood, - stone: resources.stone, - iron: resources.iron, - population: resources.population, - points: town.getPoints(), - buildings: buildings - }; - } catch (innerErr) { - console.error("💥 [DeepScan] Error in town parsing:", innerErr); - return null; - } - }).filter(Boolean); + 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 } + }; + }); - const payload = { + latestBasicPayload = { + type: "basic", + timestamp: new Date().toISOString(), player, player_id, - timestamp: new Date().toISOString(), towns: stats }; - console.log("📤 [DeepScan] Dispatching payload to grepo.haunter-pets.top:", payload); - fetch("https://grepo.haunter-pets.top/api/grepolis-data", { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify(payload) + body: JSON.stringify(latestBasicPayload) }) - .then(r => r.text()) - .then(txt => console.log("✅ [DeepScan] Server response:", txt)) - .catch(err => console.error("❌ [DeepScan] Data send failed:", err)); - + .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("💥 [DeepScan] Fatal error:", e); + console.error("💥 Error collecting basic stats:", e); } } - function waitUntilDataReady() { - console.log("🕰️ [DeepScan] Checking if town data is ready..."); - const townsObj = uw.ITowns?.towns; + // 📄 Parse marketplace trade offers + function parseMarketOffers() { + const rows = document.querySelectorAll(".js-tutorial-market-offers-table tbody tr"); + const offers = []; - if (townsObj && Object.keys(townsObj).length > 0) { - console.log("🎯 [DeepScan] Towns are ready — beginning dispatch"); - collectAndSendData(); - setInterval(collectAndSendData, 30000); + 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("⏳ [DeepScan] Towns not ready yet — retrying in 1s"); - setTimeout(waitUntilDataReady, 1000); + console.log("⏳ Waiting for town data..."); + setTimeout(waitForTowns, 1000); } } window.addEventListener("load", () => { - console.log("🌐 [DeepScan] Window loaded — monitoring town readiness"); - waitUntilDataReady(); + console.log("🚀 Game page loaded — DeepScan v4.8 booting..."); + waitForTowns(); }); })();