// ==UserScript==
// @name HeroesWM Clan Masters Info
// @namespace http://tampermonkey.net/
// @version 1.37
// @description Собирает информацию о мастерах и кузницах клана и выводит в виде таблиц во всплывающем окне
// @author o3-mini-ChatGPT
// @match https://www.heroeswm.ru/clan_info.php?id=*
// @grant none
// @license MIT
// ==/UserScript==
(function() {
'use strict';
/**
* @typedef {Object} PlayerResult
* @property {string} id - Идентификатор игрока
* @property {string} name - Имя игрока
* @property {string} link - HTML-ссылка на страницу игрока
* @property {number} guildKuznetsov - Значение "Гильдия Кузнецов"
* @property {number} guildOrujejnikov - Значение "Гильдия Оружейников"
* @property {number} masterOrujie - Значение "Мастер оружия"
* @property {number} masterBronya - Значение "Мастер доспехов"
* @property {number} jeweler - Значение "Ювелир"
*/
// Создаём кнопку в левом верхнем углу для открытия анализа
const button = document.createElement('button');
button.innerText = 'Анализ мастеров';
button.style.position = 'fixed';
button.style.top = '10px';
button.style.left = '10px';
button.style.zIndex = '1000';
button.style.padding = '5px';
button.style.cursor = 'pointer';
document.body.appendChild(button);
// Функция для создания модального окна с содержимым
function createModal(content) {
const modal = document.createElement('div');
modal.style.position = 'fixed';
modal.style.top = '50%';
modal.style.left = '50%';
modal.style.transform = 'translate(-50%, -50%)';
modal.style.backgroundColor = '#fff';
modal.style.padding = '20px';
modal.style.border = '1px solid #000';
modal.style.boxShadow = '0 0 10px rgba(0,0,0,0.5)';
modal.style.zIndex = '2000';
modal.style.maxHeight = '80vh';
modal.style.overflowY = 'auto';
modal.innerHTML = content;
const closeButton = document.createElement('button');
closeButton.innerText = 'Закрыть';
closeButton.style.marginTop = '10px';
closeButton.onclick = () => {
document.body.removeChild(modal);
};
modal.appendChild(closeButton);
return modal;
}
// Обработчик нажатия на кнопку анализа
button.onclick = async function() {
// Создаем модальное окно с индикатором прогресса
const modalDiv = document.createElement('div');
modalDiv.style.position = 'fixed';
modalDiv.style.top = '50%';
modalDiv.style.left = '50%';
modalDiv.style.transform = 'translate(-50%, -50%)';
modalDiv.style.backgroundColor = '#fff';
modalDiv.style.padding = '20px';
modalDiv.style.border = '1px solid #000';
modalDiv.style.boxShadow = '0 0 10px rgba(0,0,0,0.5)';
modalDiv.style.zIndex = '2000';
modalDiv.style.minWidth = '600px';
modalDiv.innerHTML = '<h2>Сбор информации...</h2><div id="progress">0%</div>';
document.body.appendChild(modalDiv);
// Поиск игроков только в таблице с id "table-content"
const tableContent = document.getElementById('table-content');
if (!tableContent) {
modalDiv.innerHTML = '<h2>Ошибка</h2><p>Не найден блок с игроками (table-content).</p>';
return;
}
const playerLinkElements = tableContent.querySelectorAll('a.pi[href^="pl_info.php?id="]');
if (playerLinkElements.length === 0) {
modalDiv.innerHTML = '<h2>Ошибка</h2><p>Игроки не найдены в блоке table-content.</p>';
return;
}
/** @type {PlayerResult[]} */
const results = [];
const playerElements = Array.from(playerLinkElements);
console.log(`Найдено ${playerElements.length} игроков`);
// Обрабатываем каждого игрока по очереди
for (let i = 0; i < playerElements.length; i++) {
const linkElem = playerElements[i];
const href = linkElem.getAttribute('href');
const id = href.split('=')[1];
const name = linkElem.textContent.trim();
const playerLink = linkElem.outerHTML;
// Обновляем индикатор прогресса
const progressElem = document.getElementById('progress');
if (progressElem) {
progressElem.textContent = `${Math.round(((i + 1) / playerElements.length) * 100)}% (${i + 1}/${playerElements.length})`;
}
try {
// Загружаем страницу игрока через скрытый iframe для эмуляции поведения реального пользователя
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
document.body.appendChild(iframe);
iframe.src = `https://www.heroeswm.ru/pl_info.php?id=${id}`;
await new Promise(resolve => iframe.onload = resolve);
const doc = iframe.contentDocument || iframe.contentWindow.document;
document.body.removeChild(iframe);
// Используем innerText для видимого текста и нормализуем его, заменяя неразрывные пробелы
const normalizedBody = doc.body.innerText.replace(/\u00A0/g, ' ');
// Извлекаем значение Гильдия Кузнецов из нормализованного текста страницы
const guildKuznetsovMatch = normalizedBody.match(/Гильдия Кузнецов\s*:\s*(\d+)/i);
const guildKuznetsov = guildKuznetsovMatch ? parseInt(guildKuznetsovMatch[1], 10) : 0;
// Для Гильдии оружейников сначала пробуем получить данные из элемента с id "home_2"
let guildOrujejnikov = 0;
const home2 = doc.getElementById("home_2");
if (home2) {
const normalizedHome2 = home2.innerText.replace(/\u00A0/g, ' ');
const match = normalizedHome2.match(/Гильдия Оружейников\s*:\s*(\d+)/i);
guildOrujejnikov = match ? parseInt(match[1], 10) : 0;
}
// Если не найдено через home2, пробуем по всему тексту
if (guildOrujejnikov === 0) {
const extraMatch = normalizedBody.match(/Гильдия Оружейников\s*:\s*(\d+)/i);
if (extraMatch) {
guildOrujejnikov = parseInt(extraMatch[1], 10);
}
}
// Извлекаем значения отделения гильдии оружейников (навыки) из нормализованного текста
const masterOrujieMatch = normalizedBody.match(/Мастер оружия\s*:\s*(\d+)/i);
const masterOrujie = masterOrujieMatch ? parseInt(masterOrujieMatch[1], 10) : 0;
const masterBronyaMatch = normalizedBody.match(/Мастер доспехов\s*:\s*(\d+)/i);
const masterBronya = masterBronyaMatch ? parseInt(masterBronyaMatch[1], 10) : 0;
const jewelerMatch = normalizedBody.match(/Ювелир\s*:\s*(\d+)/i);
const jeweler = jewelerMatch ? parseInt(jewelerMatch[1], 10) : 0;
console.log(`Игрок ${name}: Кузница=${guildKuznetsov}, Оружейники=${guildOrujejnikov}, Мастер оружия=${masterOrujie}, Мастер доспехов=${masterBronya}, Ювелир=${jeweler}`);
// Если у игрока не прокачан ни один стата в оружейных, пропускаем его
if (masterOrujie === 0 && masterBronya === 0 && jeweler === 0 && guildKuznetsov === 0) {
console.log(`Игрок ${name} пропущен, т.к. отсутствует прокачка статов в оружейных и кузницы.`);
continue;
}
results.push({
id,
name,
link: playerLink,
guildKuznetsov,
guildOrujejnikov,
masterOrujie,
masterBronya,
jeweler
});
// Пауза между запросами
await new Promise(resolve => setTimeout(resolve, 300));
} catch (error) {
console.error(`Ошибка при обработке игрока ${name}:`, error);
}
}
// Если результатов нет, выводим сообщение
if (results.length === 0) {
document.body.removeChild(modalDiv);
const noDataModal = createModal('<h2>Результаты</h2><p>Нет игроков с прокачанными статами в оружейных.</p>');
document.body.appendChild(noDataModal);
return;
}
// Формирование текстовой таблицы мастеров для удобного копирования
let masterTableText = '|| Оружие || Броня || Ювелир || Персонаж\n\n';
results.forEach(player => {
if (!player.guildOrujejnikov) return; // пропускаем игроков с 0 гильдий
const weaponValue = (player.guildOrujejnikov && player.masterOrujie) ? (Math.min(player.guildOrujejnikov + 1, 5) + '*' + String(Math.min(player.masterOrujie + 1, 12)).padStart(2, '0') + '%') : '- - - - -';
const armorValue = (player.guildOrujejnikov && player.masterBronya) ? (Math.min(player.guildOrujejnikov + 1, 5) + '*' + String(Math.min(player.masterBronya + 1, 12)).padStart(2, '0') + '%') : '- - - - -';
const jewelerValue = (player.guildOrujejnikov && player.jeweler) ? (Math.min(player.guildOrujejnikov + 1, 5) + '*' + String(Math.min(player.jeweler + 1, 12)).padStart(2, '0') + '%') : '- - - - -';
masterTableText += `|| ${weaponValue} || ${armorValue} || ${jewelerValue} || ${player.name}\n`;
});
let smithTableText = '|| Кузница || Персонаж\n\n';
results.forEach(player => {
if (!(player.guildKuznetsov > 0)) return;
const smithValue = (Math.min((player.guildKuznetsov + 1) * 10, 90)) + '%';
smithTableText += `|| ${smithValue} || ${player.name}\n`;
});
const finalHTML = '<h2>Информация о мастерах клана</h2><textarea style="width:100%;height:200px;" readonly>' + masterTableText + '</textarea>' +
'<h2>Информация о кузницах клана</h2><textarea style="width:100%;height:200px;" readonly>' + smithTableText + '</textarea>';
document.body.removeChild(modalDiv);
const resultModal = createModal(finalHTML);
document.body.appendChild(resultModal);
};
})();