您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
ЦЗБ текущего ремонта, данные с аукциона для сравнения
当前为
// ==UserScript== // @name HWM smith and cost per battle // @namespace http://tampermonkey.net/ // @version 4.8 // @description ЦЗБ текущего ремонта, данные с аукциона для сравнения // @author o3-mini-ChatGPT // @match https://www.heroeswm.ru/mod_workbench.php?art_id=*&type=repair // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; // Глобальные переменные let globalItemName = ""; let repairRatio = 0; // Соотношение стоимости ремонта (рассчитанное значение) let ratioSpan = null; // Элемент, в котором отображается ratio function runScript() { console.log("Запуск скрипта..."); //----------------------------- // Извлечение имени предмета //----------------------------- // Ищем элемент, содержащий информацию о предмете let itemTd = document.querySelector('td.wb[align="center"][colspan="2"]'); if (itemTd) { let itemText = itemTd.textContent.trim(); // Формат: "Выбрано: ИМЯ [0/..." let parts = itemText.split(": "); if (parts.length > 1) { let itemName = parts[1].split(" [0/")[0]; globalItemName = itemName; // сохраняем в глобальную переменную console.log("Имя предмета:", itemName); } else { console.error("Не удалось извлечь имя предмета из строки:", itemText); } } else { console.error("Элемент с информацией о предмете не найден!"); } //----------------------------- // Часть 1: Расчёт соотношения стоимости ремонта //----------------------------- let form = document.querySelector("form[name='fmain']"); if (!form) { console.error("Форма 'fmain' не найдена!"); return; } // Извлекаем прочность ремонта (формат "11/13") let bTag = form.querySelector("b"); if (!bTag) { console.error("Элемент с прочностью не найден!"); return; } let durabilityText = bTag.textContent.trim(); // например "11/13" let durabilityParts = durabilityText.split('/'); let currentDurability = parseFloat(durabilityParts[0]); if (isNaN(currentDurability)) { console.error("Не удалось извлечь текущее значение прочности из: '" + durabilityText + "'"); return; } console.log("Текущая прочность:", currentDurability); // Извлекаем стоимость ремонта let costImg = form.querySelector("img[src*='gold.png']"); if (!costImg) { console.error("Изображение стоимости ремонта не найдено!"); return; } // Ищем текстовый узел после изображения (например, "2400" или "10469") let costTextNode = costImg.nextSibling; while (costTextNode && costTextNode.nodeType === Node.TEXT_NODE && !costTextNode.nodeValue.trim()) { costTextNode = costTextNode.nextSibling; } if (!costTextNode) { costTextNode = costImg.nextElementSibling; } if (!costTextNode) { console.error("Текстовый узел стоимости ремонта не найден!"); return; } let costText = costTextNode.nodeValue ? costTextNode.nodeValue.trim() : costTextNode.textContent.trim(); // Убираем разделитель тысяч (если есть) let repairCost = parseFloat(costText.replace(/,/g, '')); if (isNaN(repairCost)) { console.error("Не удалось извлечь стоимость ремонта из текста: '" + costText + "'"); return; } console.log("Стоимость ремонта:", repairCost); // Вычисляем соотношение стоимости ремонта к прочности let ratio = repairCost / currentDurability; ratio = Math.round(ratio * 100) / 100; repairRatio = ratio; // сохраняем для дальнейшего сравнения console.log("Вычисленное соотношение:", ratio); // Выводим значение ratio рядом с прочностью. // Первоначально значение ЦЗБ выводится без выделения, // оно будет обновлено после вычисления аукционных данных. let resultSpan = document.createElement("span"); resultSpan.style.marginLeft = "10px"; resultSpan.style.fontWeight = "bold"; resultSpan.textContent = " ЦЗБ: " + ratio; bTag.parentNode.insertBefore(resultSpan, bTag.nextSibling); ratioSpan = resultSpan; // сохраняем ссылку для обновления цвета } // Функция для получения и вывода ссылки на аукцион через iframe с использованием MutationObserver function loadAuctionInfo() { // Поиск ссылки на страницу с информацией о предмете (например: art_info.php?id=sword18) let artInfoAnchor = document.querySelector('a[href^="art_info.php?id="]'); if (!artInfoAnchor) { console.error("Ссылка на информацию о предмете не найдена!"); return; } let artInfoUrl = artInfoAnchor.href; // Приводим относительный адрес к абсолютному, если нужно artInfoUrl = new URL(artInfoUrl, window.location.origin).href; console.log("Переход на страницу с информацией о предмете:", artInfoUrl); // Создаем скрытый iframe для загрузки страницы информации о предмете let iframe = document.createElement("iframe"); console.log("DEBUG: Создан iframe"); iframe.style.display = "none"; iframe.src = artInfoUrl; console.log("DEBUG: Установлен iframe.src =", artInfoUrl); iframe.onload = function() { console.log("DEBUG: iframe.onload сработал"); try { let iframeDoc = iframe.contentDocument || iframe.contentWindow.document; console.log("DEBUG: Получен документ iframe:", iframeDoc); // Используем MutationObserver для отслеживания появления ссылки на аукцион let observer = new MutationObserver(function(mutations, obs) { console.log("DEBUG: MutationObserver сработал, число мутаций =", mutations.length); let auctionLinkElem = iframeDoc.querySelector("a[href*='auction.php?']"); console.log("DEBUG: auctionLinkElem =", auctionLinkElem); if (auctionLinkElem) { // Извлекаем параметры из ссылки на аукцион let urlParams = new URLSearchParams(auctionLinkElem.href.split('?')[1]); let sortParam = urlParams.get('sort') || '4'; let catParam = urlParams.get('cat') || ''; let artTypeParam = urlParams.get('art_type') || ''; console.log("DEBUG: Извлечены параметры: sort =", sortParam, "| cat =", catParam, "| artType =", artTypeParam); // Если параметры не найдены, пытаемся другую стратегию if (!sortParam || !catParam || !artTypeParam) { console.log("DEBUG: Основные параметры отсутствуют, пробуем извлечь из других ссылок"); // Попытка извлечения из других элементов let links = iframeDoc.querySelectorAll("a"); links.forEach(link => { if (link.href.indexOf("auction.php?") !== -1) { let paramsTemp = new URLSearchParams(link.href.split('?')[1]); sortParam = paramsTemp.get('sort') || sortParam; catParam = paramsTemp.get('cat') || catParam; artTypeParam = paramsTemp.get('art_type') || artTypeParam; console.log("DEBUG: Обновленные параметры после перебора ссылки: sort =", sortParam, "| cat =", catParam, "| artType =", artTypeParam); } }); } if (!sortParam || !catParam || !artTypeParam) { console.error("DEBUG: Не удалось извлечь все параметры для формирования ссылки на аукцион."); obs.disconnect(); iframe.remove(); return; } // Формируем итоговую ссылку, добавляя параметры type=0 и snew=1 let finalAuctionLink = `https://www.heroeswm.ru/auction.php?sort=${sortParam}&cat=${catParam}&art_type=${artTypeParam}&sbn=1&sau=0&snew=1`; console.log("DEBUG: Сформированная finalAuctionLink =", finalAuctionLink); obs.disconnect(); iframe.remove(); // Загружаем страницу аукциона и ищем лоты, где в одном из элементов присутствует имя предмета processAuctionPage(finalAuctionLink); } }); observer.observe(iframeDoc.body, { childList: true, subtree: true }); console.log("DEBUG: MutationObserver установлен"); } catch(e) { console.error("DEBUG: Ошибка в iframe.onload:", e); iframe.remove(); } }; document.body.appendChild(iframe); console.log("DEBUG: iframe добавлен в document.body"); } // Функция для загрузки страницы аукциона, поиска лотов с нужным именем предмета, // извлечения значений прочности и цены, выбора минимальной цены для каждой прочности, // вычисления цены за единицу прочности и формирования гиперссылки с итоговой информацией. function processAuctionPage(finalAuctionLink) { console.log("Загрузка страницы аукциона:", finalAuctionLink); let auctionIframe = document.createElement("iframe"); auctionIframe.style.display = "none"; auctionIframe.src = finalAuctionLink; auctionIframe.onload = function() { try { let auctionDoc = auctionIframe.contentDocument || auctionIframe.contentWindow.document; console.log("Страница аукциона загружена. Ожидание динамического контента..."); // Задержка 2 секунды для полной загрузки динамического контента setTimeout(() => { // Находим все строки (элементы <tr> с классом "wb"), в которых встречается строка вида " - <itemName>" let allRows = auctionDoc.querySelectorAll("tr.wb"); let lots = []; allRows.forEach(row => { if (row.innerText.indexOf("- " + globalItemName) !== -1) { // Извлекаем прочность из ячейки с valign="top" (из строки "Прочность: NN/...") let strengthCell = row.querySelector("td[valign='top']"); if (strengthCell) { let strengthMatch = strengthCell.innerText.match(/Прочность:\s*(\d+)\//); if (strengthMatch) { let strength = Number(strengthMatch[1]); // Извлекаем цену из ячейки, содержащей изображение золота let goldImg = row.querySelector("img[src*='gold.png']"); if (goldImg && goldImg.parentElement) { let priceTd = goldImg.parentElement.nextElementSibling; if (priceTd) { // Извлекаем значение цены (например "13,000") let priceMatch = priceTd.textContent.match(/([\d,]+)/); if (priceMatch) { let priceStr = priceMatch[1].replace(/,/g, ''); let price = Number(priceStr); lots.push({ strength, price }); } } } } } } }); console.log("Извлеченные лоты (прочность и цена):", lots); // Если лотов нет, выходим if (lots.length === 0) { console.error("Лоты с предметом '" + globalItemName + "' не найдены."); auctionIframe.remove(); return; } // Группируем по прочности (минимальная цена для каждой прочности) let minPricesByStrength = {}; lots.forEach(lot => { if (!minPricesByStrength[lot.strength] || lot.price < minPricesByStrength[lot.strength]) { minPricesByStrength[lot.strength] = lot.price; } }); console.log("Минимальные цены для каждой прочности:", minPricesByStrength); // Вычисляем цену за единицу прочности для каждого лота и ищем: // - лот с минимальной прочностью // - лот с максимальной прочностью // - лот с наименьшей ценой за единицу прочности let candidateMin = lots[0], candidateMax = lots[0], candidateBest = lots[0]; for (let lot of lots) { if (lot.strength < candidateMin.strength) { candidateMin = lot; } if (lot.strength > candidateMax.strength) { candidateMax = lot; } if ((lot.price / lot.strength) < (candidateBest.price / candidateBest.strength)) { candidateBest = lot; } } let unitPriceMin = candidateMin.price / candidateMin.strength; let unitPriceMax = candidateMax.price / candidateMax.strength; let unitPriceBest = candidateBest.price / candidateBest.strength; // Формируем строку вида: // "Аук: <минимальная прочность> - <unitPrice_min>; // <максимальная прочность> - <unitPrice_max>; // <прочность лота с минимальной unitPrice> - <unitPrice_best (выделено жирным зелёным)>" let linkHTML = "Аук: " + candidateMin.strength + " - " + unitPriceMin.toFixed(2) + "; " + candidateMax.strength + " - " + unitPriceMax.toFixed(2) + "; " + candidateBest.strength + " - " + "<span style='color:green; font-weight:bold;'>" + unitPriceBest.toFixed(2) + "</span>"; console.log("Сформированная ссылка для аукциона:", linkHTML); // Сравнение соотношения ремонта (repairRatio) с unitPriceBest и выделение его цветом: // Если repairRatio меньше unitPriceBest - выделяем зелёным, иначе красным. let ratioColor = (repairRatio < unitPriceBest) ? "green" : "red"; if (ratioSpan) { ratioSpan.innerHTML = " ЦЗБ: <span style='color:" + ratioColor + "; font-weight:bold;'>" + repairRatio.toFixed(2) + "</span>"; } // Создаем гиперссылку на аукцион с полученной строкой let linkElement = document.createElement("a"); linkElement.href = finalAuctionLink; linkElement.innerHTML = linkHTML; console.log("Сформированная ссылка для аукциона:", linkElement.outerHTML); // Обновляем раздел стоимости ремонта на странице (в форме fmain) // Находим текстовый узел после изображения стоимости ремонта. let formElem = document.querySelector("form[name='fmain']"); if (formElem) { let goldImg = formElem.querySelector("img[src*='gold.png']"); if (goldImg) { let candidateNode = goldImg.nextSibling; while (candidateNode && candidateNode.nodeType === Node.TEXT_NODE && !candidateNode.nodeValue.trim()) { candidateNode = candidateNode.nextSibling; } if (candidateNode) { // Если сразу после текстового узла есть перенос строки, удаляем его. let nextElem = candidateNode.nextElementSibling; if (nextElem && nextElem.tagName.toLowerCase() === "br") { nextElem.remove(); } // Ищем существующий span после текста стоимости ремонта. let auctionSpan = candidateNode.nextElementSibling; if (auctionSpan && auctionSpan.tagName.toLowerCase() === "span") { // Обновляем содержимое этого span, убираем лишние <br>. auctionSpan.innerHTML = linkElement.outerHTML; // Убираем лишние переносы внутри span, если они есть. let brs = auctionSpan.querySelectorAll("br"); brs.forEach(br => br.remove()); // Если за span не следует перенос строки, добавляем его. let afterSpan = auctionSpan.nextSibling; if (!(afterSpan && afterSpan.nodeType === Node.ELEMENT_NODE && afterSpan.tagName.toLowerCase() === "br")) { let br = document.createElement("br"); auctionSpan.parentNode.insertBefore(br, auctionSpan.nextSibling); } } else { // Если span отсутствует, создаем его со структурой: // [перенос строки] <span>ссылка</span> [перенос строки] let brBefore = document.createElement("br"); let auctionContainer = document.createElement("span"); auctionContainer.innerHTML = linkElement.outerHTML; let brAfter = document.createElement("br"); candidateNode.parentNode.insertBefore(brBefore, candidateNode.nextSibling); candidateNode.parentNode.insertBefore(auctionContainer, brBefore.nextSibling); candidateNode.parentNode.insertBefore(brAfter, auctionContainer.nextSibling); } } } } auctionIframe.remove(); }, 2000); } catch (e) { console.error("Ошибка при обработке страницы аукциона:", e); auctionIframe.remove(); } }; document.body.appendChild(auctionIframe); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", function() { runScript(); loadAuctionInfo(); }); } else { runScript(); loadAuctionInfo(); } })();