Загрузка...

Utility
Sorting participants of the draw by the date of participation

Thread in Extentions created by TiHostLiza Mar 26, 2025. (bumped Sep 24, 2025) 477 views

  1. TiHostLiza
    по просьбе avatarklopybrittan сделал

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

    Code

    // ==UserScript==
    // @name Lolz Contest Sorter (v6.0 Final Fixed)
    // @namespace [URL]http://tampermonkey.net/[/URL]
    // @version 6.0
    // @description Финальная версия с исправленной логикой загрузки страниц. Самый быстрый и надежный метод.
    // @author Your Name
    // @match *://lolz.live/threads/*/contest-users*
    // @match *://zelenka.guru/threads/*/contest-users*
    // @match *://lolz.guru/threads/*/contest-users*
    // @connect lolz.live
    // @connect zelenka.guru
    // @connect lolz.guru
    // @grant GM_xmlhttpRequest
    // @grant GM_addStyle
    // ==/UserScript==

    (function() {
    'use strict';

    // Стили интерфейса
    GM_addStyle(`
    #lzt-final-sorter-btn {
    position: fixed; top: 15px; right: 15px; z-index: 10000;
    background-color: #c0392b; color: white; padding: 10px 18px;
    border: none; border-radius: 5px; font-size: 14px;
    font-weight: bold; cursor: pointer; box-shadow: 0 2px 5px rgba(0,0,0,0.2);
    transition: all 0.2s ease-in-out;
    }
    #lzt-final-sorter-btn:hover { background-color: #e74c3c; }
    #lzt-final-sorter-btn:disabled { background-color: #e6b0aa; cursor: not-allowed; }
    #lzt-sorter-status {
    position: fixed; top: 70px; right: 15px; z-index: 9998;
    background: rgba(0, 0, 0, 0.85); color: white; padding: 12px;
    border-radius: 5px; font-size: 13px; max-width: 280px;
    box-shadow: 0 2px 10px rgba(0,0,0,0.3); white-space: pre-wrap;
    }
    #lzt-sorter-results-panel {
    position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
    width: 600px; max-width: 90vw; height: 70vh; z-index: 10001;
    background: #f7f7f7; color: #333; border-radius: 8px;
    box-shadow: 0 5px 25px rgba(0,0,0,0.2); display: flex; flex-direction: column;
    }
    #lzt-sorter-results-panel .header { padding: 15px 20px; background: #333; color: white; border-radius: 8px 8px 0 0; font-size: 16px; font-weight: bold; display: flex; justify-content: space-between; align-items: center; }
    #lzt-sorter-results-panel .content { flex-grow: 1; overflow-y: auto; padding: 0; margin: 0; }
    #lzt-sorter-results-panel .footer { padding: 10px; background: #eee; text-align: right; border-top: 1px solid #ddd; border-radius: 0 0 8px 8px; }
    #lzt-sorter-results-panel button { padding: 8px 15px; border: none; border-radius: 4px; cursor: pointer; font-weight: bold; margin-left: 10px; }
    #lzt-sorter-copy-btn { background-color: #3498db; color: white; }
    #lzt-sorter-close-btn { background-color: #e74c3c; color: white; }
    #lzt-sorter-results-list { list-style-type: none; padding: 0; margin: 0; }
    #lzt-sorter-results-list li { padding: 12px 20px; border-bottom: 1px solid #eee; display: flex; justify-content: space-between; align-items: center; font-size: 14px; }
    #lzt-sorter-results-list li:nth-child(even) { background-color: #fff; }
    #lzt-sorter-results-list .username { font-weight: bold; color: #2c3e50; }
    #lzt-sorter-results-list .timestamp { font-size: 12px; color: #7f8c8d; }
    `);

    window.addEventListener('load', () => {
    if (document.querySelector('li.primaryContent.memberListItem')) {
    createInterface();
    }
    });

    function createInterface() {
    const btn = document.createElement('button');
    btn.id = 'lzt-final-sorter-btn';
    btn.textContent = 'Собрать и отсортировать ВСЕХ';
    document.body.appendChild(btn);
    btn.addEventListener('click', startProcessing);
    }

    async function startProcessing() {
    const btn = document.getElementById('lzt-final-sorter-btn');
    btn.disabled = true;
    btn.textContent = 'В процессе...';
    const status = showStatus('Подготовка...');

    try {
    const usersFromCurrentPage = getUsersFromPage(document);
    let allUsers = [...usersFromCurrentPage];
    status.textContent = `Собрано на этой странице: ${allUsers.length}`;

    const pageNav = document.querySelector('.PageNav');
    if (pageNav) {
    const totalPages = parseInt(pageNav.dataset.last, 10);
    const currentPage = parseInt(pageNav.dataset.page, 10);
    status.textContent += `\nВсего страниц: ${totalPages}`;

    const pageUrlsToFetch = [];
    for (let i = 1; i <= totalPages; i++) {
    if (i !== currentPage) {
    pageUrlsToFetch.push({ pageNum: i, url: buildAbsoluteUrl(i) });
    }
    }

    if (pageUrlsToFetch.length > 0) {
    const fetchedUsers = await processUrlsWithGM(pageUrlsToFetch, status, allUsers.length);
    allUsers.push(...fetchedUsers);
    }
    }

    status.textContent = 'Обработка данных...';
    processAndShowResults(allUsers);

    } catch (error) {
    console.error('Критическая ошибка:', error);
    status.textContent = ' Ошибка: ' + error.message;
    } finally {
    btn.disabled = false;
    btn.textContent = 'Собрать и отсортировать ВСЕХ';
    setTimeout(() => removeElement('#lzt-sorter-status'), 5000);
    }
    }

    // ИСПРАВЛЕННЫЙ МЕТОД ЗАГРУЗКИ
    async function processUrlsWithGM(pages, status, initialCount) {
    const allFetchedUsers = [];
    let totalCollected = initialCount;

    for (const page of pages) {
    status.textContent = `Загрузка: стр. ${page.pageNum}...\nСобрано: ${totalCollected}`;
    try {
    const html = await fetchPage(page.url);
    const pageDoc = new DOMParser().parseFromString(html, 'text/html');
    const usersFromPage = getUsersFromPage(pageDoc);

    if (usersFromPage.length > 0) {
    allFetchedUsers.push(...usersFromPage);
    totalCollected += usersFromPage.length;
    } else {
    status.textContent = `Стр. ${page.pageNum} пуста. Завершаем сбор.`;
    await new Promise(resolve => setTimeout(resolve, 1500));
    break; // Если страница пустая, значит, дошли до конца
    }
    } catch (error) {
    console.warn(`Ошибка загрузки стр. ${page.pageNum}:`, error.message, "Пропускаем.");
    status.textContent = `Ошибка на стр. ${page.pageNum}. Пропускаем...`;
    await new Promise(resolve => setTimeout(resolve, 1000));
    }
    }
    return allFetchedUsers;
    }

    function buildAbsoluteUrl(pageNum) {
    const pageNav = document.querySelector('.PageNav');
    const relativeUrl = pageNav.dataset.baseurl.replace('%7B%7Bsentinel%7D%7D', '');
    // Исправляем формирование URL, используя `location.origin` (https://lolz.live)
    return new URL(relativeUrl + pageNum, window.location.origin).href;
    }

    function fetchPage(url) {
    return new Promise((resolve, reject) => {
    GM_xmlhttpRequest({
    method: 'GET', url: url, timeout: 15000,
    onload: r => (r.status >= 200 && r.status < 400) ? resolve(r.responseText) : reject(new Error(`Статус ${r.status}`)),
    onerror: () => reject(new Error('Сетевая ошибка')),
    ontimeout: () => reject(new Error('Таймаут')),
    });
    });
    }

    function processAndShowResults(users) {
    const totalFound = users.length;
    const uniqueUsers = Array.from(new Map(users.map(u => [u.userId, u])).values());
    const totalUnique = uniqueUsers.length;
    uniqueUsers.sort((a, b) => a.timestamp - b.timestamp);
    showResultsPanel(uniqueUsers, totalFound);
    }

    function getUsersFromPage(doc) {
    const users = [];
    const items = doc.querySelectorAll('li.primaryContent.memberListItem');
    items.forEach(item => {
    const usernameLink = item.querySelector('h3.username a.username');
    const timeElement = item.querySelector('.DateTime');
    if (usernameLink && timeElement) {
    const userHref = usernameLink.getAttribute('href');
    const userIdMatch = userHref.match(/(?:members\/|)(\d+|[a-zA-Z0-9_-]+)\//);
    const userId = userIdMatch ? userIdMatch[1] : 'unknown';
    users.push({
    username: usernameLink.textContent.trim(),
    timestamp: parseInt(timeElement.dataset.time, 10) || 0,
    userId: userId
    });
    }
    });
    return users;
    }

    function showResultsPanel(users, totalFound) {
    removeElement('#lzt-sorter-results-panel');
    const panel = document.createElement('div');
    panel.id = 'lzt-sorter-results-panel';
    let listItems = users.map((user, index) => `
    <li>
    <div><span class="username">${index + 1}. ${user.username}</span></div>
    <span class="timestamp">${formatDate(user.timestamp)}</span>
    </li>`).join('');
    const headerText = `Результаты (Найдено: ${totalFound}, Уникальных: ${users.length})`;
    panel.innerHTML = `<div class="header"><span>${headerText}</span></div><ul class="content" id="lzt-sorter-results-list">${listItems}</ul><div class="footer"><button id="lzt-sorter-copy-btn">Копировать ники</button><button id="lzt-sorter-close-btn">Закрыть</button></div>`;
    document.body.appendChild(panel);
    document.getElementById('lzt-sorter-close-btn').onclick = () => panel.remove();
    document.getElementById('lzt-sorter-copy-btn').onclick = e => {
    navigator.clipboard.writeText(users.map((u, i) => `${i + 1}. ${u.username}`).join('\n'));
    e.target.textContent = ' Скопировано!';
    setTimeout(() => e.target.textContent = 'Копировать ники', 2000);
    };
    }

    function showStatus(message) {
    let status = document.getElementById('lzt-sorter-status');
    if (!status) {
    status = document.createElement('div');
    status.id = 'lzt-sorter-status';
    document.body.appendChild(status);
    }
    status.textContent = message;
    return status;
    }

    function formatDate(timestamp) {
    if (!timestamp) return 'нет данных';
    return new Date(timestamp * 1000).toLocaleString('ru-RU');
    }

    function removeElement(selector) {
    const el = document.querySelector(selector);
    if (el) el.remove();
    }
    })();

    2. Заходишь на страницу с участниками и жмешь кнопку.

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

    [IMG]

    3. Ждешь

    [IMG]

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

    [IMG]


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

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

    [IMG]
     
    This article was useful for you?
    You can thank the author of the topic by transferring funds to your balance
    Thank the author
  2. TiHostLiza
    Апнул в связи розыгрышами на 2 мульта, ищите фермы и мульты
     
    1. a911
      avatarTiHostLiza , лень, пусть этим кф занимаеца
  3. RoflanHmm
    RoflanHmm Aug 17, 2025 :thinking: 27,089 Nov 18, 2016
    у кого то самое быстрое ау на диком западе :roflanZdarova:
     
  4. clownery
    clownery Aug 17, 2025 bunnyhop 64 Nov 9, 2019
    пользоваться не буду, но в качестве поддержки поставлю симпатию
     
Loading...