// ==UserScript==
// @name (VIP) PEFL Features: Ненужные
// @namespace http://tampermonkey.net/
// @version 1.12
// @description Добавляет расширенный функционал для страницы ненужных
// @author Вы
// @match *://*/nenuj.php?t=scout*
// @grant GM_xmlhttpRequest
// @license MIT
// @downloadURL https://update.greasyfork.org/scripts/533465/%28VIP%29%20PEFL%20Features%3A%20%D0%9D%D0%B5%D0%BD%D1%83%D0%B6%D0%BD%D1%8B%D0%B5.user.js
// @updateURL https://update.greasyfork.org/scripts/533465/%28VIP%29%20PEFL%20Features%3A%20%D0%9D%D0%B5%D0%BD%D1%83%D0%B6%D0%BD%D1%8B%D0%B5.meta.js
// ==/UserScript==
(function() {
'use strict';
// Функция для логирования
function log(message, data) {
console.log(`[ХодТоргов] ${message}`, data || '');
}
log('Скрипт запущен');
// Функция для извлечения ID игрока из URL
function extractPlayerIdFromUrl(url) {
const match = url.match(/[?&]j=(\d+)/);
return match ? match[1] : null;
}
// Создаем контейнер для времени с флекс-разметкой
const timeContainer = document.createElement('div');
timeContainer.style.display = 'flex';
timeContainer.style.justifyContent = 'space-between';
timeContainer.style.margin = '10px 0';
timeContainer.style.padding = '5px';
timeContainer.style.backgroundColor = '#f0f0f0';
timeContainer.style.border = '1px solid #ccc';
timeContainer.style.borderRadius = '3px';
// Создаем элемент для отображения текущего времени
const currentTimeDisplay = document.createElement('div');
currentTimeDisplay.style.fontSize = '14px';
currentTimeDisplay.style.fontWeight = 'bold';
// Создаем элемент для заголовка
const titleDisplay = document.createElement('div');
titleDisplay.textContent = 'PF-Ненужные';
titleDisplay.style.fontSize = '16px';
titleDisplay.style.fontWeight = 'bold';
titleDisplay.style.color = '#666';
// Создаем элемент для отображения времени обновления
const updatedTimeDisplay = document.createElement('div');
updatedTimeDisplay.style.fontSize = '14px';
updatedTimeDisplay.style.fontWeight = 'bold';
// Функция форматирования времени
function formatTime(date) {
// Получаем текущее время в UTC
const now = new Date(date);
// Форматируем время в московском часовом поясе (UTC+3)
const options = {
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
timeZone: 'Europe/Moscow'
};
// Возвращаем отформатированное московское время
return new Intl.DateTimeFormat('ru-RU', options).format(now) + ' (МСК)';
}
// Функция обновления текущего времени
function updateCurrentTime() {
const now = new Date();
currentTimeDisplay.innerHTML = 'Текущее время: ' + formatTime(now);
}
// Устанавливаем время обновления один раз
const loadTime = new Date();
updatedTimeDisplay.innerHTML = 'Обновлено: ' + formatTime(loadTime);
// Обновляем текущее время сразу и затем каждую секунду
updateCurrentTime();
setInterval(updateCurrentTime, 1000);
// Добавляем элементы в контейнер
timeContainer.appendChild(currentTimeDisplay);
timeContainer.appendChild(titleDisplay);
timeContainer.appendChild(updatedTimeDisplay);
// Находим таблицу на странице
const tables = document.querySelectorAll('table[width="100%"][border="0"]');
log('Найдено таблиц:', tables.length);
if (tables.length > 0) {
// Вставляем элемент с временем перед таблицей
tables[0].parentNode.insertBefore(timeContainer, tables[0]);
}
// Задержка для запросов
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// Счетчик запросов (для отслеживания и добавления задержки)
let requestCounter = 0;
// Хранилище данных профилей игроков
let playerProfiles = JSON.parse(localStorage.getItem('playerProfiles') || '{}');
log('Загружено профилей из localStorage:', Object.keys(playerProfiles).length);
// Функция для получения дедлайна по позиции
function getDeadlineByPosition(position) {
// Приводим к нижнему регистру для упрощения проверки
position = position.toLowerCase();
// Проверяем наличие разных позиций по приоритету
if (position.includes('fw')) {
return '14:00';
} else if (position.includes('dm') || position.includes('mf') || position.includes('am')) {
return '14:30';
} else if (position.includes('sw') || position.includes('df')) {
return '15:00';
} else if (position.includes('gk')) {
return '15:30';
} else {
return 'Н/Д'; // Если позиция не определена
}
}
// Функция для обработки строки контракта
function processContractString(contractString) {
// Если контракт содержит пробел, берем только вторую часть (сумму)
const parts = contractString.split(' ');
if (parts.length > 1) {
return parts.slice(1).join(' ');
}
return contractString;
}
// Функция для извлечения ссылки "убрать"
function extractRemoveLink(element) {
// Ищем ссылку с текстом "убрать"
const links = element.querySelectorAll('a');
for (let i = 0; i < links.length; i++) {
if (links[i].textContent.trim() === 'убрать') {
return links[i].getAttribute('href');
}
}
return null; // Не найдено
}
// Функция для извлечения значения интереса
function extractInterest(element) {
// Ищем элемент с ID "td4", содержащий информацию об интересе
const interestCell = element.querySelector('td#td4');
if (interestCell) {
// Извлекаем число в скобках из текста
const match = interestCell.textContent.match(/\((\d+)\)/);
if (match && match[1]) {
return parseInt(match[1], 10);
}
}
return null; // Не найдено
}
// Функция для получения данных профиля игрока
async function getPlayerProfile(playerName, profileUrl) {
// Извлекаем ID игрока из URL
const playerId = extractPlayerIdFromUrl(profileUrl);
if (!playerId) {
log(`Не удалось извлечь ID для игрока ${playerName} из URL ${profileUrl}`);
return null;
}
// Проверяем, есть ли данные в кэше по ID
if (playerProfiles[playerId]) {
log(`Данные профиля для игрока ${playerName} (ID: ${playerId}) загружены из кэша`);
return playerProfiles[playerId];
}
log(`Получение данных профиля для ${playerName} (ID: ${playerId}) из ${profileUrl}`);
// Добавляем задержку перед каждым запросом
await delay(500 * requestCounter);
requestCounter++;
// Формируем полный URL, если это относительный путь
let fullUrl = profileUrl;
if (profileUrl.startsWith('/')) {
fullUrl = window.location.origin + profileUrl;
} else if (!profileUrl.startsWith('http')) {
fullUrl = window.location.origin + '/' + profileUrl;
}
return new Promise((resolve, reject) => {
try {
GM_xmlhttpRequest({
method: 'GET',
url: fullUrl,
onload: function(response) {
if (response.status === 200) {
// Создаем временный элемент для парсинга HTML
const tempDiv = document.createElement('div');
tempDiv.innerHTML = response.responseText;
// Извлекаем возраст (только число)
let age = extractTextAfterLabel(tempDiv, 'Возраст:');
// Оставляем только число, убираем буквы
if (age) {
const ageMatch = age.match(/\d+/);
age = ageMatch ? ageMatch[0] : 'Н/Д';
}
// Получаем позицию
const position = extractTextAfterLabel(tempDiv, 'Позиция:');
// Получаем контракт и обрабатываем его
const contractRaw = extractTextAfterLabel(tempDiv, 'Контракт:');
const contract = processContractString(contractRaw);
// Извлекаем данные профиля
const playerData = {
// Сохраняем имя игрока
name: playerName,
// Возраст (только число)
age: age,
// Позиция
position: position,
// Дедлайн
deadline: getDeadlineByPosition(position || ''),
// Номинал
value: extractValueFromProfile(tempDiv),
// Контракт (только сумма)
contract: contract,
// ID флага
flagId: extractFlagId(tempDiv),
// Ссылка "убрать"
removeLink: extractRemoveLink(tempDiv),
// Значение интереса
interest: extractInterest(tempDiv)
};
log(`Данные профиля для ${playerName}:`, playerData);
// Сохраняем данные в кэш
playerProfiles[playerId] = playerData;
localStorage.setItem('playerProfiles', JSON.stringify(playerProfiles));
resolve(playerData);
} else {
log(`Ошибка запроса профиля:`, response.status);
resolve(null);
}
},
onerror: function(error) {
log(`Ошибка запроса профиля:`, error);
resolve(null);
}
});
} catch (e) {
log('Исключение при выполнении запроса профиля:', e);
resolve(null);
}
});
}
// Функция для извлечения текста после метки
function extractTextAfterLabel(element, label) {
// Ищем все текстовые узлы
const textNodes = [];
const walkNodes = document.createTreeWalker(element, NodeFilter.SHOW_TEXT, null, false);
let node;
while (node = walkNodes.nextNode()) {
textNodes.push(node);
}
// Ищем текстовый узел с нашей меткой
for (let i = 0; i < textNodes.length; i++) {
if (textNodes[i].textContent.trim() === label) {
// Если нашли узел с меткой, берем следующий узел
if (i + 1 < textNodes.length) {
return textNodes[i + 1].textContent.trim();
}
}
}
// Альтернативный метод для поиска: через ячейки таблицы
const cells = element.querySelectorAll('td');
for (let i = 0; i < cells.length; i++) {
if (cells[i].textContent.trim() === label && i + 1 < cells.length) {
return cells[i + 1].textContent.trim();
}
}
return 'Н/Д'; // Не найдено
}
// Функция для извлечения номинала
function extractValueFromProfile(element) {
// Ищем ячейку с ID value0
const valueCell = element.querySelector('td#value0');
if (valueCell && valueCell.nextElementSibling) {
return valueCell.nextElementSibling.textContent.trim();
}
// Альтернативный поиск по тексту "Номинал:"
const cells = element.querySelectorAll('td');
for (let i = 0; i < cells.length; i++) {
if (cells[i].textContent.trim() === 'Номинал:' && i + 1 < cells.length) {
return cells[i + 1].textContent.trim();
}
}
return 'Н/Д'; // Не найдено
}
// Функция для извлечения ID флага
function extractFlagId(element) {
// Ищем ячейку с ID nation_img
const nationCell = element.querySelector('td#nation_img');
if (nationCell) {
const flagImg = nationCell.querySelector('img');
if (flagImg && flagImg.src) {
// Извлекаем ID из URL флага
const match = flagImg.src.match(/flags\/mod\/(\d+)\.gif/);
if (match && match[1]) {
return match[1];
}
}
}
return null; // Не найдено
}
// Добавляем функцию для фонового удаления игрока
function removePlayerBackground(removeLink, rowElement) {
log('Фоновое удаление игрока по ссылке:', removeLink);
// Формируем полный URL, если это относительный путь
let fullUrl = removeLink;
if (removeLink.startsWith('/')) {
fullUrl = window.location.origin + removeLink;
} else if (!removeLink.startsWith('http')) {
fullUrl = window.location.origin + '/' + removeLink;
}
// Выполняем запрос для удаления игрока
GM_xmlhttpRequest({
method: 'GET',
url: fullUrl,
onload: function(response) {
if (response.status === 200) {
log('Игрок успешно удален');
// Анимация удаления строки
rowElement.style.transition = 'opacity 0.5s ease-out';
rowElement.style.opacity = '0';
// Удаляем строку после завершения анимации
setTimeout(function() {
if (rowElement && rowElement.parentNode) {
rowElement.parentNode.removeChild(rowElement);
}
}, 500);
} else {
log('Ошибка при удалении игрока:', response.status);
alert('Ошибка при удалении игрока. Попробуйте перезагрузить страницу и повторить попытку.');
}
},
onerror: function(error) {
log('Ошибка при удалении игрока:', error);
alert('Ошибка при удалении игрока. Проверьте ваше интернет-соединение и повторите попытку.');
}
});
}
tables.forEach(table => {
log('Обработка таблицы');
// Добавляем бордер к таблице
table.style.border = '1px solid #000';
table.style.borderCollapse = 'collapse';
// Добавляем бордер к ячейкам таблицы
const cells = table.querySelectorAll('td');
cells.forEach(cell => {
cell.style.border = '1px solid #ccc';
cell.style.padding = '3px';
});
// Находим ссылки с именами игроков и меняем их поведение
const playerLinks = table.querySelectorAll('td:first-child a');
playerLinks.forEach(link => {
// Добавляем target="_blank" для открытия в новом окне
link.setAttribute('target', '_blank');
// Дополнительно, можно добавить подсветку при наведении
link.style.textDecoration = 'none';
link.addEventListener('mouseover', function() {
this.style.backgroundColor = '#f0f0f0';
});
link.addEventListener('mouseout', function() {
this.style.backgroundColor = '';
});
});
// Функция для преобразования текстовой ставки в числовое значение
function parseBidValue(bidText) {
bidText = bidText.trim();
// Удаляем все теги script, если они есть
bidText = bidText.replace(/