LZT Contests Info

Информация о розыгрышах в профиле LZT с кэшем на 10 минут

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         LZT Contests Info
// @namespace    LZTContestsInfo
// @version      1.5
// @description  Информация о розыгрышах в профиле LZT с кэшем на 10 минут
// @author       llimonix
// @match        https://zelenka.guru/*
// @match        https://lzt.market/*
// @match        https://lolz.guru/*
// @match        https://lolz.live/*
// @match        https://zelenka.guru
// @match        https://lzt.market
// @match        https://lolz.guru
// @match        https://lolz.live
// @icon         https://cdn-icons-png.flaticon.com/512/5899/5899678.png
// @license      MIT
// ==/UserScript==

(function() {
    $('<style>').text(`
        .contestsInfoContainer {
            margin-top: -20px;
            padding: 20px;
            border-top: 1px solid #363636;
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            gap: 10px;
        }
        .contestsInfo {
            padding: 0 10px;
            background: #303030;
            border-radius: 8px;
            line-height: 40px;
            box-sizing: border-box;
            font-weight: 600;
            transition: 0.3s;
            margin: 0;
            width: 100%;
            height: 40px;
            display: flex;
            align-items: center;
            min-width: 0;
        }
        .contestsInfoContainer .contestsInfo.totalWinnings {
            background-color: #00ba7820;
            border: 1px solid #00ba7832;
        }
        .contestsInfoContainer .contestsInfo.totalGiveaway {
            background-color: #d7515720;
            border: 1px solid #d7515732;
        }
        .contestsInfo .data {
            margin: 0 0 0 10px;
            white-space: nowrap;
            overflow: hidden;
            max-width: 78%;
            display: inline-block;
            text-overflow: ellipsis;
            user-select: all;
        }
        .contestsInfo .contactIcon {
            font-size: 20px;
            line-height: 42px;
            float: left;
        }
        .constestsInfoDetails.button {
            background-color: #1c1c1c;
        }
        .LZTContestsInfo {
            display: flex;
            flex-wrap: wrap;
            gap: 12px;
        }
        .LZTContestsInfo .LZTContestsItem {
            height: 63px;
            background: #2D2D2D;
            border-radius: 8px;
            padding: 12px;
            box-sizing: border-box;
            display: flex;
            align-items: center;
            white-space: normal;
            flex: 1 1 100%;
            max-width: 100%;
            font-weight: 700;
        }
        @media screen and (min-width: 700px) {
            .LZTContestsInfo .LZTContestsItem {
                flex: 1 1 calc((100% / 2) - 24px);
            }
        }
        .LZTContestsIcon {
            margin-right: 12px;
        }
        .LZTContestsInfo .LZTContestsItem i {
            width: 24px;
            height: 24px;
            font-size: 24px;
        }
        .LZTContestsInfo--status {
            display: flex;
            color: #949494;
            background: #303030;
            padding: 15px 20px;
            border-radius: 12px;
            flex-direction: column;
        }
        .LZTContestsInfo--status .statusTitle {
            font-size: 16px;
            font-weight: 700;
            margin: 0 0 4px;
            color: #FF6A70;
        }
        .LZTContestsInfoWrapper {
            display: flex;
            flex-wrap: wrap;
        }
        .xenOverlay .userStatCounters.hasContacts,
        .Responsive .xenOverlay .userStatCounters.hasContacts {
            margin-bottom: 0px;
        }
        .xenOverlay .userStatCounters {
            margin-bottom: 20px!important;
        }
        .memberCardInner {
            & .contestsInfoContainer {
                @media (max-width: 520px) {
                    grid-template-columns: repeat(1, 1fr);
                }
            }
        }
     `).appendTo('head');

    function getUserContestData(userId) {
        return new Promise(function(resolve, reject) {
            const cacheKey = 'LZTUserData_' + userId;
            const cachedRaw = sessionStorage.getItem(cacheKey);
            const TEN_MIN = 10 * 60 * 1000;

            if (cachedRaw) {
                try {
                    const cached = JSON.parse(cachedRaw);
                    if (Date.now() - cached.timestamp < TEN_MIN) {
                        console.log('[LZT Contests Info] Использую кэш для ' + userId);
                        return resolve(cached.data);
                    } else {
                        console.log('[LZT Contests Info] Кэш устарел, обновляю...');
                    }
                } catch(e) {
                    console.warn('[LZT Contests Info] Ошибка чтения кэша:', e);
                }
            }

            $.ajax({
                url: 'https://lzt-winners-contests.vercel.app/user/' + userId + '?include_ranks=true',
                dataType: 'json',
                success: function(data) {
                    sessionStorage.setItem(cacheKey, JSON.stringify({
                        timestamp: Date.now(),
                        data: data
                    }));
                    resolve(data);
                },
                error: function(xhr, status, err) {
                    reject(err || status);
                }
            });
        });
    }

    XenForo.register(".profilePage .insuranceDeposit", function() {
        var $el = $(this);

        var userLink = $('.userContentLinks a').first();
        if (!userLink.length) return;

        var userIdMatch = userLink.attr('href').match(/\/(\d+)\/?$/);
        if (!userIdMatch) return;
        var userId = userIdMatch[1];

        var contestsBlock = $('.section.contestsInfoWin, .section.contestsInfoCreate').length > 0;
        if (contestsBlock) return;

        $el.after(`
            <div class="section contestsInfoWin">
                <div class="secondaryContent">
                    <h3 style="margin-bottom:6px;">Победы в розыгрышах</h3>
                    <p class="amount mainc" id="totalWinnings" style="font-size:18px;font-weight:600;">Загрузка...</p>
                </div>
            </div>
            <div class="section contestsInfoCreate">
                <div class="secondaryContent">
                    <h3 style="margin-bottom:6px;">Проведённые розыгрыши</h3>
                    <p class="amount redc" id="totalGiveaway" style="font-size:18px;font-weight:600;">Загрузка...</p>
                </div>
            </div>
            <div class="section button block constestsInfoDetails">Подробнее</div>
        `);

        $('.constestsInfoDetails.button').on('click', () => renderContestsInfo(userId));

        getUserContestData(userId).then(function(data){
            $('#totalWinnings').text(
                (data.total_winnings || 0).toLocaleString('ru-RU') + ' ₽'
            );
            $('#totalGiveaway').text(
                (data.total_giveaway || 0).toLocaleString('ru-RU') + ' ₽'
            );
        }).catch(function(err){
            console.error('[LZT Contests Info] Ошибка загрузки данных:', err);
            $('#totalWinnings').text('Ошибка');
            $('#totalGiveaway').text('Ошибка');
        });
    });

    $(document).on('XFOverlay', function(e){
        var $overlay = e.overlay.getOverlay();
        if (!$overlay.is('.memberCard')) return;

        var $stat = $overlay.find('.bottomContainer');
        if (!$stat.length || $overlay.find('.contestsInfoContainer').length) return;

        var userLink = $overlay.find('.controlsBlock a').first();
        if (!userLink.length) return;
        var userIdMatch = userLink.attr('href').match(/\/(\d+)\/?$/);
        if (!userIdMatch) return;
        var userId = userIdMatch[1];

        var $block = $(`
            <div class="contestsInfoContainer">
                <div class="contestsInfo totalWinnings">
                    <span class="contactIcon fas fa-plus-circle mainc"></span>
                    <span class="data" id="totalWinnings">Выиграно: загрузка...</span>
                </div>
                <div class="contestsInfo totalGiveaway">
                    <span class="contactIcon fas fa-minus-circle redc"></span>
                    <span class="data" id="totalGiveaway">Разыграно: загрузка...</span>
                </div>
            </div>
        `);
        $stat.after($block);

        var $menuElement = $overlay.find('.memberCardInner a[rel="Menu"]');

        if ($menuElement.length) {
            var $buttonOverlay = $('.Menu .blockLinksList').last();
            var $buttonInfo = $('<li><a id="constestsInfoDetails" class="OverlayTrigger">Статистика розыгрышей</a></li>');
            $buttonInfo.on('click', () => renderContestsInfo(userId));
            $buttonOverlay.append($buttonInfo);
        }
        getUserContestData(userId).then(function(data){
            $block.find('#totalWinnings').text(
                'Выиграно: ' + (data.total_winnings || 0).toLocaleString('ru-RU') + ' ₽'
            );
            $block.find('#totalGiveaway').text(
                'Разыграно: ' + (data.total_giveaway || 0).toLocaleString('ru-RU') + ' ₽'
            );
        }).catch(function(err){
            console.error('[LZT Contests Info] Ошибка загрузки данных:', err);
            $block.find('#totalWinnings, #totalGiveaway').text('Ошибка');
        });
    });

    async function renderContestsInfo(userId) {
        const cacheKey = 'LZTUserData_' + userId;
        const cachedRaw = sessionStorage.getItem(cacheKey);

        let cached = null;
        try {
            cached = cachedRaw ? JSON.parse(cachedRaw) : null;
        } catch (e) {
            console.warn('Ошибка парсинга кэша:', e);
        }

        const TOTAL_WINNINGS = (cached?.data?.total_winnings || 0).toLocaleString('ru-RU');
        const TOTAL_GIVEAWAY = (cached?.data?.total_giveaway || 0).toLocaleString('ru-RU');
        const WINS_COUNT = (cached?.data?.wins_count || 0).toLocaleString('ru-RU');
        const CONTESTS_CREATED = (cached?.data?.contests_created || 0).toLocaleString('ru-RU');
        const MAX_SINGLE_WIN = (cached?.data?.max_single_win || 0).toLocaleString('ru-RU');
        const MAX_SINGLE_GIVEAWAY = (cached?.data?.max_single_giveaway || 0).toLocaleString('ru-RU');
        const RANK_BY_WINNINGS = (cached?.data?.rank_by_winnings || 0).toLocaleString('ru-RU');
        const RANK_BY_WINS_COUNT = (cached?.data?.rank_by_wins_count || 0).toLocaleString('ru-RU');
        const RANK_BY_GIVEAWAY = (cached?.data?.rank_by_giveaway || 0).toLocaleString('ru-RU');
        const RANK_BY_CONTESTS_COUNT = (cached?.data?.rank_by_contests_count || 0).toLocaleString('ru-RU');

        const statsData = [
            { label: 'Выиграно', icon: '', value: TOTAL_WINNINGS, symbol: '₽' },
            { label: 'Разыграно', icon: '', value: TOTAL_GIVEAWAY, symbol: '₽' },
            { label: 'Количество побед', icon: '', value: WINS_COUNT },
            { label: 'Количество розыгрышей', icon: '', value: CONTESTS_CREATED },
            { label: 'Максимальная сумма победы', icon: '', value: MAX_SINGLE_WIN, symbol: '₽' },
            { label: 'Максимальная сумма розыгрыша', icon: '', value: MAX_SINGLE_GIVEAWAY, symbol: '₽' },
            { label: 'Топ по сумме побед', icon: '', value: RANK_BY_WINNINGS, prefix: '#' },
            { label: 'Топ по сумме розыгрышей', icon: '', value: RANK_BY_GIVEAWAY, prefix: '#' },
            { label: 'Топ по количеству побед', icon: '', value: RANK_BY_WINS_COUNT, prefix: '#' },
            { label: 'Топ по количеству розыгрышей', icon: '', value: RANK_BY_CONTESTS_COUNT, prefix: '#' },
        ];

        const $items = statsData.map(({ label, icon, value, prefix = '', symbol = '' }) => `
            <div class="LZTContestsItem">
                <div class="LZTContestsIcon">
                    <i class="${icon}"></i>
                </div>
                <div><p>${label}</p>${prefix}${value} ${symbol}</div>
            </div>
        `).join('');

        const $warningContestsInfo = `
            <div class="LZTContestsInfo--statusContainer">
                <div class="LZTContestsInfo--status">
                  <div class="statusTitle">Важная информация</div>
                  <div>Топ и суммы могут отличаться от официального значения форума, так как средствами парсера не возможно получать удалённые темы, а форум может.</div>
                  <div>Данная статистика учитывает только денежные розыгрыши с неудалённых тем.</div>
                </div>
            </div>
        `;

        const $contestsInfo = $(`
            <div class="LZTContestsInfoWrapper">
              <div class="LZTContestsInfo">
                ${$items}
              </div>
              ${$warningContestsInfo}
            </div>
        `);

        XenForo.alert($contestsInfo.prop('outerHTML'), 'LZT Contests Info');
    }
})();