Загрузка...

Утилита Сортировка участников розыгрыша по дате участия

Тема в разделе Дополнения создана пользователем Победил Среда в 16:28. 63 просмотра

Загрузка...
  1. Победил
    Победил Автор темы Среда в 16:28 Экономь TRX - lolz.live/threads/7681011 14 574 26 июл 2020
    по просьбе klopybrittan сделал

    1. B Tampermonkey суешь скрипт

    Код
    // ==UserScript== // @name Lolz.live Contest Sorter (Fixed UI) // @namespace [URL]http://tampermonkey.net/[/URL] // @version 1.5 // @description Надежный сбор и сортировка участников конкурса с исправленным интерфейсом // @author Your Name // @match [URL]https://lolz.live/threads/*/contest-users*[/URL] // @grant GM_xmlhttpRequest // @grant GM_addStyle // @connect lolz.live // ==/UserScript== (function() { 'use strict'; // Стили для наших элементов GM_addStyle(` #contestSorterBtn { background: #4CAF50 !important; color: white !important; border: none !important; padding: 10px 15px !important; border-radius: 4px !important; font-weight: bold !important; cursor: pointer !important; margin: 10px !important; position: fixed !important; top: 20px !important; right: 20px !important; z-index: 9999 !important; } #contestSorterStatus { position: fixed !important; top: 60px !important; right: 20px !important; background: white !important; padding: 10px !important; border-radius: 4px !important; box-shadow: 0 0 10px rgba(0,0,0,0.2) !important; z-index: 9998 !important; max-width: 300px !important; } #contestSorterResults { position: fixed !important; top: 100px !important; right: 20px !important; bottom: 20px !important; width: 550px !important; background: white !important; overflow-y: auto !important; padding: 20px !important; box-shadow: 0 0 20px rgba(0,0,0,0.3) !important; z-index: 9997 !important; border-radius: 5px !important; } .contestSorterControlBtn { position: fixed !important; top: 120px !important; right: 30px !important; background: #f44336 !important; color: white !important; border: none !important; padding: 5px 10px !important; border-radius: 4px !important; cursor: pointer !important; z-index: 9999 !important; } .contestSorterCopyBtn { right: 120px !important; background: #2196F3 !important; } `); // Проверяем и создаем интерфейс каждые 500мс пока не найдем нужные элементы const initInterval = setInterval(() => { if (document.querySelector('li.primaryContent.memberListItem')) { clearInterval(initInterval); createInterface(); } }, 500); // Также создаем интерфейс при изменениях DOM (на случай SPA-навигации) const observer = new MutationObserver(() => { if (!document.getElementById('contestSorterBtn') && document.querySelector('li.primaryContent.memberListItem')) { createInterface(); } }); observer.observe(document.body, { childList: true, subtree: true }); function createInterface() { // Если кнопка уже есть - выходим if (document.getElementById('contestSorterBtn')) return; // Создаем кнопку const btn = document.createElement('button'); btn.id = 'contestSorterBtn'; btn.textContent = 'Сортировать участников'; document.body.appendChild(btn); // Создаем статус const status = document.createElement('div'); status.id = 'contestSorterStatus'; document.body.appendChild(status); btn.addEventListener('click', startProcessing); } async function startProcessing() { const btn = document.getElementById('contestSorterBtn'); const status = document.getElementById('contestSorterStatus'); btn.disabled = true; status.innerHTML = 'Начинаем сбор участников...'; try { const allUsers = await getAllUsers(); if (allUsers.length === 0) { status.innerHTML = 'Участники не найдены. Проверьте структуру страницы.'; return; } // Удаление дубликатов по ID пользователя и времени const uniqueUsers = removeDuplicates(allUsers); if (uniqueUsers.length !== allUsers.length) { status.innerHTML += `<br>Обнаружено и удалено ${allUsers.length - uniqueUsers.length} дубликатов.`; } // Сортировка по времени с дополнительной проверкой uniqueUsers.sort((a, b) => { if (a.timestamp !== b.timestamp) return a.timestamp - b.timestamp; if (a.page !== b.page) return a.page - b.page; return a.pagePosition - b.pagePosition; }); showResults(uniqueUsers); } catch (error) { console.error('Ошибка:', error); status.innerHTML = 'Ошибка: ' + error.message; } finally { btn.disabled = false; } } function removeDuplicates(users) { const seen = new Set(); return users.filter(user => { const key = `${user.userId}_${user.timestamp}_${user.page}_${user.pagePosition}`; return seen.has(key) ? false : (seen.add(key), true); }); } async function getAllUsers() { const status = document.getElementById('contestSorterStatus'); const users = []; let page = 1; let hasMore = true; const baseUrl = window.location.href.split('?')[0].replace(/\/contest-users$/, ''); // Сначала собираем с текущей страницы const currentPageUsers = getUsersFromPage(document, 1); users.push(...currentPageUsers); status.innerHTML = `Собрано участников: ${users.length}. Проверяем другие страницы...`; // Затем проверяем другие страницы while (hasMore) { page++; const nextPageUrl = `${baseUrl}/contest-users?page=${page}`; status.innerHTML = `Проверяем страницу ${page}... (Собрано: ${users.length})`; try { const pageUsers = await getUsersFromUrl(nextPageUrl, page); if (pageUsers.length === 0) { hasMore = false; } else { users.push(...pageUsers); status.innerHTML = `Страница ${page} обработана. Участников: ${users.length}.`; await new Promise(resolve => setTimeout(resolve, 800)); } } catch (error) { console.error(`Ошибка при обработке страницы ${page}:`, error); hasMore = false; } } return users; } async function getUsersFromUrl(url, pageNumber) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: url, onload: function(response) { if (response.status === 200) { const doc = new DOMParser().parseFromString(response.responseText, 'text/html'); resolve(getUsersFromPage(doc, pageNumber)); } else if (response.status === 404) { resolve([]); } else { reject(new Error(`HTTP error! status: ${response.status}`)); } }, onerror: function(error) { reject(error); }, timeout: 10000 }); }); } function getUsersFromPage(doc, pageNumber) { const items = Array.from(doc.querySelectorAll('li.primaryContent.memberListItem')); return items.map((item, index) => { const timeElement = item.querySelector('.DateTime'); const usernameElement = item.querySelector('.username span'); const userId = item.querySelector('.username')?.getAttribute('data-user-id') || '0'; const timestamp = timeElement ? parseInt(timeElement.dataset.time) : 0; return { html: item.outerHTML, timestamp: isNaN(timestamp) ? 0 : timestamp, username: usernameElement ? usernameElement.textContent.trim() : 'Без имени', userId: userId, position: (pageNumber - 1) * 20 + index, page: pageNumber, pagePosition: index }; }); } function showResults(users) { const status = document.getElementById('contestSorterStatus'); // Удаляем старые результаты removeElement('contestSorterResults'); removeElement('contestSorterCloseBtn'); removeElement('contestSorterCopyBtn'); status.innerHTML = `Найдено уникальных участников: ${users.length}. Создаем список...`; // Создаем контейнер для результатов const resultsContainer = document.createElement('div'); resultsContainer.id = 'contestSorterResults'; document.body.appendChild(resultsContainer); // Добавляем заголовок const title = document.createElement('h3'); title.textContent = `Отсортированные участники (${users.length})`; title.style.marginTop = '0'; resultsContainer.appendChild(title); // Добавляем статистику const stats = document.createElement('div'); stats.style.marginBottom = '15px'; stats.style.padding = '10px'; stats.style.backgroundColor = '#f5f5f5'; stats.style.borderRadius = '4px'; stats.innerHTML = ` <div><strong>Первый участник:</strong> ${users[0].username} (${formatDate(users[0].timestamp)})</div> <div><strong>Последний участник:</strong> ${users[users.length-1].username} (${formatDate(users[users.length-1].timestamp)})</div> <div><strong>Всего страниц:</strong> ${users[users.length-1].page}</div> `; resultsContainer.appendChild(stats); // Добавляем список const list = document.createElement('div'); list.style.marginTop = '10px'; users.forEach((user, index) => { const userElement = document.createElement('div'); userElement.style.marginBottom = '15px'; userElement.style.padding = '10px'; userElement.style.border = '1px solid #eee'; userElement.style.borderRadius = '4px'; userElement.style.position = 'relative'; userElement.style.backgroundColor = index % 2 === 0 ? '#fafafa' : 'white'; const updatedHtml = user.html.replace(/Номер: \d+/, `Номер: ${index + 1}`); const numberSpan = document.createElement('span'); numberSpan.textContent = `${index + 1}. `; numberSpan.style.fontWeight = 'bold'; numberSpan.style.marginRight = '5px'; const timeSpan = document.createElement('span'); timeSpan.textContent = formatDate(user.timestamp); timeSpan.style.color = '#666'; timeSpan.style.fontSize = '0.9em'; timeSpan.style.marginLeft = '10px'; const pageSpan = document.createElement('span'); pageSpan.textContent = `(стр. ${user.page}, поз. ${user.pagePosition + 1})`; pageSpan.style.color = '#888'; pageSpan.style.fontSize = '0.8em'; pageSpan.style.marginLeft = '10px'; userElement.innerHTML = updatedHtml; userElement.prepend(numberSpan); userElement.append(timeSpan); userElement.append(pageSpan); if (index === 0) { userElement.style.borderLeft = '4px solid #4CAF50'; userElement.style.backgroundColor = '#f8fff8'; } else if (index === users.length - 1) { userElement.style.borderLeft = '4px solid #f44336'; userElement.style.backgroundColor = '#fff8f8'; } list.appendChild(userElement); }); resultsContainer.appendChild(list); // Добавляем кнопки управления addControlButton('Закрыть', 'contestSorterCloseBtn', () => { removeElement('contestSorterResults'); removeElement('contestSorterCloseBtn'); removeElement('contestSorterCopyBtn'); }); addControlButton('Копировать список', 'contestSorterCopyBtn', () => { const textToCopy = users.map((user, index) => `${index + 1}. ${user.username} (ID: ${user.userId}) - ${formatDate(user.timestamp)} (стр. ${user.page}, поз. ${user.pagePosition + 1})` ).join('\n'); navigator.clipboard.writeText(textToCopy).then(() => { const btn = document.getElementById('contestSorterCopyBtn'); const oldText = btn.textContent; btn.textContent = 'Скопировано!'; setTimeout(() => btn.textContent = oldText, 2000); }); }, true); status.innerHTML = `Сортировка завершена. Найдено ${users.length} участников.`; } function addControlButton(text, id, onClick, isCopyBtn = false) { const btn = document.createElement('button'); btn.id = id; btn.textContent = text; btn.className = isCopyBtn ? 'contestSorterControlBtn contestSorterCopyBtn' : 'contestSorterControlBtn'; btn.onclick = onClick; document.body.appendChild(btn); } function removeElement(id) { const element = document.getElementById(id); if (element) element.remove(); } function formatDate(timestamp) { if (!timestamp) return 'нет данных'; const date = new Date(timestamp * 1000); return date.toLocaleString('ru-RU', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' }); } })();
    2. Заходишь на страницу с участниками и жмешь кнопку.

    https://lolz.live/threads/8495486/contest-users?page=2

    [IMG]

    3. Ждешь

    [IMG]

    4. Получаешь список по Bремени: сверху самые старые участия - снизу самые ластовые

    [IMG]


    Скрипт иногда багует, но буду дорабатывать

    Пример "копироBания списка"

    [IMG]
     
    Среда в 16:28 Изменено
Top
JavaScript error: