Загрузка...

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

Тема в разделе Дополнения создана пользователем Победилдо 26 мар 2025. 125 просмотров

Загрузка...
  1. Победилдо
    Победилдо Автор темы 26 мар 2025 Лучший курс на TRX,Energy - https://lolz.live/threads/8610296/ 15 704 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]
     
    26 мар 2025 Изменено
Top