Card Helper AStars | AnimeStars | ASStars

card helper

当前为 2025-05-17 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Card Helper AStars | AnimeStars | ASStars
// @namespace    animestars.org
// @version      7.8
// @description  card helper
// @author       bmr
// @match        https://astars.club/*
// @match        https://asstars.club/*
// @match        https://asstars1.astars.club/*
// @match        https://animestars.org/*
// @match        https://as1.astars.club/*
// @match        https://asstars.tv/*
// @license      MIT
// @grant        none
// ==/UserScript==

const DELAY = 50;

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

let cardCounter = 0;
const cardClasses = '.remelt__inventory-item, .lootbox__card, .anime-cards__item, .trade__inventory-item, .trade__main-item, .card-filter-list__card, .deck__item, .history__body-item, .history__body-item, .card-pack__card';

function getCurrentDomain() {
    const hostname = window.location.hostname;
    const protocol = window.location.protocol;
    return `${protocol}//${hostname}`;
}

async function loadCard(cardId) {
    const cacheKey = 'cardId: ' + cardId;
    let card = await getCard(cacheKey) ?? {};
    if (Object.keys(card).length) {
        return card;
    }
    const currentDomain = getCurrentDomain();

    let popularityCount = 0;
    let needCount = 0;
    let tradeCount = 0;
    let rankText = '';

    try {
        await sleep(DELAY);
        const mainCardPageResponse = await fetch(`${currentDomain}/cards/${cardId}/users/`);

        if (!mainCardPageResponse.ok) {
            console.error(`Failed to fetch card page ${currentDomain}/cards/${cardId}/users/. Status: ${mainCardPageResponse.status}`);
        } else {
            const mainCardPageHtml = await mainCardPageResponse.text();
            const mainCardPageDoc = new DOMParser().parseFromString(mainCardPageHtml, 'text/html');

            rankText = mainCardPageDoc.querySelector('.ncard__rank')?.innerText.replace('Редкость\n','').trim() || '';

            const ownersCountElement = mainCardPageDoc.querySelector('#owners-count');
            popularityCount = ownersCountElement ? parseInt(ownersCountElement.textContent.trim(), 10) || 0 : 0;

            const ownersNeedElement = mainCardPageDoc.querySelector('#owners-need');
            needCount = ownersNeedElement ? parseInt(ownersNeedElement.textContent.trim(), 10) || 0 : 0;

            const ownersTradeElement = mainCardPageDoc.querySelector('#owners-trade');
            tradeCount = ownersTradeElement ? parseInt(ownersTradeElement.textContent.trim(), 10) || 0 : 0;
        }
    } catch (error) {
        console.error(`Error in loadCard for cardId ${cardId}:`, error);
        popularityCount = 0;
        needCount = 0;
        tradeCount = 0;
        rankText = 'N/A';
    }

    card = { popularityCount, needCount, tradeCount, rankText };
    await cacheCard(cacheKey, card);
    return card;
}

async function updateCardInfo(cardId, element) {
    if (!cardId || !element) {
        return;
    }
    try {
        const cardData = await loadCard(cardId);
        let statsContainer = document.querySelector('.card-stats-container');
        if (!statsContainer) {
            statsContainer = document.createElement('div');
            statsContainer.className = 'card-stats-container';
            document.body.appendChild(statsContainer);
        }
        const oldStats = element.querySelector('.card-stats');
        if (oldStats) {
            oldStats.remove();
        }
        const stats = document.createElement('div');
        stats.className = 'card-stats';
        stats.innerHTML = `
            <span title="Редкость: ${cardData.rankText || 'N/A'} | Владельцев">
                <i class="fas fa-users"></i>
                ${cardData.popularityCount}
            </span>
            <span title="Хотят получить">
                <i class="fas fa-heart"></i>
                 ${cardData.needCount}
            </span>
            <span title="Готовы обменять">
                <i class="fas fa-sync-alt"></i>
                ${cardData.tradeCount}
            </span>
        `;
        element.appendChild(stats);
    } catch (error) {
        throw error;
    }
}

function clearMarkFromCards() {
    cleanByClass('div-marked');
}

function removeAllLinkIcons() {
    cleanByClass('link-icon');
}

function cleanByClass(className) {
    const list = document.querySelectorAll('.' + className);
    list.forEach(item => item.remove());
}

function getCardsOnPage() {
    return Array.from(
        document.querySelectorAll(cardClasses)
    ).filter(cardEl => cardEl.offsetParent !== null);
}

async function processCards() {
    if (isCardRemeltPage()) {
        const storedData = JSON.parse(localStorage.getItem('animeCardsData')) || {};
        if (Object.keys(storedData).length < 1) {
            await readyRemeltCards();
            return;
        }
    }
    removeMatchingWatchlistItems();
    removeAllLinkIcons();
    clearMarkFromCards();
    const cardsOnPage = getCardsOnPage();
    let counter = cardsOnPage.length;
    if (!counter) {
        return;
    }
    let buttonId = 'processCards';
    startAnimation(buttonId);
    updateButtonCounter(buttonId, counter);
    showNotification('Проверяю спрос на ' + counter + ' карточек');
    for (const cardElement of cardsOnPage) {
        if (cardElement.classList.contains('trade__inventory-item--lock') || cardElement.classList.contains('remelt__inventory-item--lock')) {
            continue;
        }
        cardElement.classList.add('processing-card');
        let cardId = await getCardId(cardElement);
        if (cardId) {
            await updateCardInfo(cardId, cardElement).catch(error => {
                return;
            });
            counter--;
            updateButtonCounter(buttonId, counter);
        }
        cardElement.classList.remove('processing-card');
        if (cardElement.classList.contains('lootbox__card')) {
            cardElement.addEventListener('click', removeAllLinkIcons);
        }
    }
    showNotification('Проверка спроса завершена');
    stopAnimation(buttonId);
}

function removeMatchingWatchlistItems() {
    const watchlistItems = document.querySelectorAll('.watchlist__item');
    if (watchlistItems.length == 0) {
        return;
    }
    watchlistItems.forEach(item => {
        const episodesText = item.querySelector('.watchlist__episodes')?.textContent.trim();
        if (episodesText) {
            const matches = episodesText.match(/[\d]+/g);
            if (matches) {
                const currentEpisode = parseInt(matches[0], 10);
                const totalEpisodes = parseInt(matches.length === 4 ? matches[3] : matches[1], 10);
                if (currentEpisode === totalEpisodes) {
                    item.remove();
                }
            }
        }
    });
    if (watchlistItems.length && document.querySelectorAll('.watchlist__item').length < watchlistItems.length) {
        showNotification('Из списка удалены просмотренные аниме. В списке осталось ' + document.querySelectorAll('.watchlist__item').length + ' записей.');
    }
}

function startAnimation(id) {
    $('#' + id + ' span:first').css('animation', 'pulseIcon 1s ease-in-out infinite');
}

function stopAnimation(id) {
    $('#' + id + ' span:first').css('animation', '');
}

function getButton(id, className, percent, text, clickFunction) {
    const button = document.createElement('button');
    button.id = id;
    button.style.position = 'fixed';
    button.style.top = percent + '%';
    button.style.right = '1%';
    button.style.zIndex = '1000';
    button.style.backgroundColor = '#6c5ce7';
    button.style.color = '#fff';
    button.style.border = 'none';
    button.style.borderRadius = '50%';
    button.style.width = '45px';
    button.style.height = '45px';
    button.style.padding = '0';
    button.style.cursor = 'pointer';
    button.style.boxShadow = '0 2px 5px rgba(0, 0, 0, 0.2)';
    button.style.transition = 'all 0.3s ease';
    let tooltipTimeout;
    const tooltip = document.createElement('div');
    tooltip.style.cssText = `
        position: fixed;
        right: calc(1% + 55px);
        background-color: #2d3436;
        color: #fff;
        padding: 8px 12px;
        border-radius: 4px;
        font-size: 14px;
        opacity: 0;
        transition: all 0.3s ease;
        white-space: nowrap;
        top: ${percent}%;
        transform: translateX(10px);
        z-index: 999;
        pointer-events: none;
    `;
    button.onmouseover = function() {
        this.style.backgroundColor = '#5f51e3';
        tooltip.style.opacity = '1';
        tooltip.style.transform = 'translateX(0)';
        if (tooltipTimeout) {
            clearTimeout(tooltipTimeout);
        }
    };
    button.onmouseout = function() {
        this.style.backgroundColor = '#6c5ce7';
        tooltip.style.opacity = '0';
        tooltip.style.transform = 'translateX(10px)';
    };
    const icon = document.createElement('span');
    icon.className = 'fal fa-' + className;
    icon.style.display = 'inline-block';
    icon.style.fontSize = '20px';
    button.appendChild(icon);

    switch(id) {
        case 'processCards':
            tooltip.textContent = 'Узнать спрос';
            break;
        case 'readyToCharge':
            tooltip.textContent = 'Отметить всё как "Готов обменять"';
            break;
        case 'readyRemeltCards':
            tooltip.textContent = 'Кешировать карточки';
            break;
        default:
            tooltip.textContent = text;
    }
    button.addEventListener('click', function(e) {
        e.stopPropagation();
        clickFunction(e);
        if (window.innerWidth <= 768) {
            tooltip.style.opacity = '1';
            tooltip.style.transform = 'translateX(0)';
            if (tooltipTimeout) {
                clearTimeout(tooltipTimeout);
            }
            tooltipTimeout = setTimeout(() => {
                tooltip.style.opacity = '0';
                tooltip.style.transform = 'translateX(10px)';
            }, 1000);
        }
    });
    const container = document.createElement('div');
    container.appendChild(tooltip);
    container.appendChild(button);
    button.classList.add('action-button');
    return container;
}

function updateButtonCounter(id, counter) {
    return;
}

function addUpdateButton() {
    if (window.location.pathname.includes('/pm/') ||
        window.location.pathname.includes('emotions.php') ||
        window.frameElement) {
        return;
    }
    if (!document.querySelector('#processCards')) {
        let cardsOnPage = getCardsOnPage();
        document.body.appendChild(getButton('processCards', 'star', 37, 'Сравнить карточки', processCards));
        if (!cardsOnPage.length) {
            return
        }
        if (isMyCardPage()) {
            document.body.appendChild(getButton('readyToCharge', 'handshake', 50, '"Готов поменять" на все карточки', readyToCharge));
        }
        if (isCardRemeltPage()) {
            document.body.appendChild(getButton('readyRemeltCards', 'yin-yang', 50, 'закешировать карточки', readyRemeltCards));
            const storedData = JSON.parse(localStorage.getItem('animeCardsData')) || {};
            updateButtonCounter('readyRemeltCards', Object.keys(storedData).length);
        }
    }
}

function isMyCardPage() {
    return (/^\/user\/(.*)\/cards(\/page\/\d+\/)?/).test(window.location.pathname)
}

function isCardRemeltPage() {
    return (/^\/cards_remelt\//).test(window.location.pathname)
}

async function readyRemeltCards() {
    showNotification('Кеширую все карты так как иначе на этой странице не получится их определить рейтинги');
    const linkElement = document.querySelector('a.button.button--left-icon.mr-3');
    const href = linkElement ? linkElement.href : null;
    if (!href) {
        return;
    }
    removeMatchingWatchlistItems();
    removeAllLinkIcons();
    clearMarkFromCards();
    const cardsOnPage = getCardsOnPage();
    let counter = cardsOnPage.length;
    if (!counter) {
        return;
    }
    let buttonId = 'readyRemeltCards';
    startAnimation(buttonId);
    await scrapeAllPages(href, buttonId);
    stopAnimation(buttonId);
}

async function scrapeAllPages(firstPageHref, buttonId) {
    const response = await fetch(firstPageHref);
    if (!response.ok) {
        throw new Error(`Ошибка HTTP: ${response.status}`);
    }
    const firstPageDoc = new DOMParser().parseFromString(await response.text(), 'text/html');
    const pagination = firstPageDoc.querySelector('#pagination');
    if (!pagination) {
        return;
    }
    const progressBar = createProgressBar();
    let totalCards = 0;
    let processedCards = 0;
    let storedData = JSON.parse(localStorage.getItem('animeCardsData')) || {};
    const titleElement = firstPageDoc.querySelector('h1.secondary-title.text-center');
    if (titleElement) {
        const match = titleElement.textContent.match(/\((\d+)\s*шт\.\)/);
        totalCards = match ? parseInt(match[1], 10) : -1;
        if (totalCards !== -1 && totalCards === Object.keys(storedData).length) {
            showNotification('На данный момент в кеше карточек ровно столько же сколько в профиле пользователя');
            progressBar.remove();
            return;
        }
    }
    async function processCardsToLocalstorage(doc) {
        const cardsElements = doc.querySelectorAll('.anime-cards__item');
        for (let i = 0; i < cardsElements.length; i += 5) {
            const cardGroup = Array.from(cardsElements).slice(i, i + 5);
            for (const cardEl of cardGroup) {
                const cardId = cardEl.getAttribute('data-id');
                const ownerId = cardEl.getAttribute('data-owner-id');
                const name = cardEl.getAttribute('data-name');
                const rank = cardEl.getAttribute('data-rank');
                const animeLink = cardEl.getAttribute('data-anime-link');
                const image = cardEl.getAttribute('data-image');
                const ownerKey = 'o_' + ownerId;
                if (!ownerId || !cardId) continue;
                if (!storedData[ownerKey]) {
                    storedData[ownerKey] = [];
                }
                storedData[ownerKey].push({ cardId, name, rank, animeLink, image, ownerId });
                processedCards++;
                if (totalCards > 0) {
                    progressBar.update(processedCards, totalCards);
                }
            }
            await sleep(10);
        }
    }
    async function fetchPage(url) {
        try {
            const responsePage = await fetch(url);
            if (!responsePage.ok) throw new Error(`Ошибка загрузки страницы ${url}`);
            return await responsePage.text();
        } catch (error) {
            return null;
        }
    }
    await processCardsToLocalstorage(firstPageDoc);
    const lastPageLink = pagination.querySelector('a:last-of-type');
    if (lastPageLink) {
        const lastPageNumber = parseInt(lastPageLink.textContent.trim(), 10);
        if (!isNaN(lastPageNumber) && lastPageNumber > 1) {
            const parser = new DOMParser();
            for (let i = 2; i <= lastPageNumber; i++) {
                const pageUrl = lastPageLink.href.replace(/page\/\d+/, `page/${i}`);
                const pageHTML = await fetchPage(pageUrl);
                if (pageHTML) {
                    await processCardsToLocalstorage(parser.parseFromString(pageHTML, 'text/html'));
                }
                await sleep(3000);
                if (i % 3 === 0) {
                    localStorage.setItem('animeCardsData', JSON.stringify(storedData));
                }
            }
        }
    }
    localStorage.setItem('animeCardsData', JSON.stringify(storedData));
    setTimeout(() => {
        progressBar.remove();
    }, 1000);
    await processCards();
}

async function getCardId(cardElement) {
    let cardId = cardElement.getAttribute('card-id') ||
    cardElement.getAttribute('data-card-id') || cardElement.getAttribute('data-id');
    const href = cardElement.getAttribute('href');
    if (href) {
        let cardIdMatch = href.match(/\/cards\/(\d+)\/users\//);
        if (cardIdMatch) {
            cardId = cardIdMatch[1];
        }
    }
    if (cardId) {
        const cardByOwner = await getFirstCardByOwner(cardId);
        if (cardByOwner) {
            cardId = cardByOwner.cardId;
        }
    }
    return cardId;
}

async function getFirstCardByOwner(ownerId) {
    const storedData = JSON.parse(localStorage.getItem('animeCardsData')) || {};
    const key = 'o_' + ownerId;
    return storedData[key] && storedData[key].length > 0 ? storedData[key][0] : null;
}

async function readyToCharge() {
    showNotification('Отмечаем все карты на странице как: "Готов обменять" кроме тех что на обмене и заблокированных');
    let cardsOnPage = getCardsOnPage();

    if (!cardsOnPage || cardsOnPage.length === 0) {
        showNotification('Карты на странице не найдены.');
        return;
    }

    const cardsToProcess = cardsOnPage.filter(cardEl => !cardEl.classList.contains('trade__inventory-item--lock'));
    const totalCardsToProcess = cardsToProcess.length;

    if (totalCardsToProcess === 0) {
        showNotification('Нет карт для отметки (все заблокированы или отсутствуют).');
        return;
    }

    let uiCounter = totalCardsToProcess;
    let buttonId = 'readyToCharge';

    startAnimation(buttonId);
    updateButtonCounter(buttonId, uiCounter);
    clearMarkFromCards();

    const progressBar = createProgressBar();
    cardCounter = 0;
    let successfullyProcessedForProgressBar = 0;

    for (const cardElement of cardsToProcess) {
        let idToSend = cardElement.getAttribute('data-owner-id');
        if (!idToSend) {
            idToSend = await getCardId(cardElement);
        }

        if (idToSend) {
            await sleep(1000);
            const success = await readyToChargeCard(idToSend);
            if (success) {
                successfullyProcessedForProgressBar++;
            }
            progressBar.update(successfullyProcessedForProgressBar, totalCardsToProcess);
        }
        uiCounter--;
        updateButtonCounter(buttonId, uiCounter);
    }

    setTimeout(() => {
        progressBar.remove();
    }, 1000);
    showNotification('Отправлено на обмен ' + cardCounter + ' карточек на странице.');
    stopAnimation(buttonId);
}

async function readyToChargeCard(card_id_to_send) {
    try {
        await sleep(DELAY * 3);
        const data = await new Promise((resolve, reject) => {
            $.ajax({
                url: "/engine/ajax/controller.php?mod=trade_ajax",
                type: "post",
                data: {
                    action: "propose_add",
                    type: 1,
                    card_id: card_id_to_send,
                    user_hash: dle_login_hash
                },
                dataType: "json",
                cache: false,
                success: (response) => {
                    resolve(response);
                },
                error: (jqXHR, textStatus, errorThrown) => {
                    console.error(`readyToChargeCard: AJAX error для ${card_id_to_send}: ${textStatus}, ${errorThrown}`, jqXHR);
                    reject(errorThrown);
                }
            });
        });

        if (data?.error) {
            if (data.error === 'Слишком часто, подождите пару секунд и повторите действие') {
                return await readyToChargeCard(card_id_to_send);
            }
            return false;
        }

        if (data?.status == "added") {
            cardCounter++;
            return true;
        }

        if (data?.status == "deleted") {
            return await readyToChargeCard(card_id_to_send);
        }
        return false;
    } catch (e) {
        console.error(`readyToChargeCard: Исключение при обработке ID ${card_id_to_send}:`, e);
        return false;
    }
}

const style = document.createElement('style');
style.textContent = `
@keyframes glowEffect {0% { box-shadow: 0 0 5px #6c5ce7; } 50% { box-shadow: 0 0 20px #6c5ce7; } 100% { box-shadow: 0 0 5px #6c5ce7; }}
@keyframes fadeInUp {from {opacity: 0; transform: translateY(10px);} to {opacity: 1; transform: translateY(0);}}
.processing-card {position: relative;}
.processing-card img {position: relative; z-index: 2;}
.processing-card::after {content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 100%; max-height: calc(100% - 30px); border-radius: 8px; z-index: 1; animation: glowEffect 1.5s infinite; pointer-events: none;}
.card-stats {position: relative; background: linear-gradient(45deg, #6c5ce7, #a367dc); padding: 8px; color: white; font-size: 12px; margin-top: 5px; border-radius: 5px; display: flex; justify-content: space-between; align-items: center; text-shadow: 1px 1px 2px rgba(0,0,0,0.3); animation: fadeInUp 0.3s ease; z-index: 0 !important;}
.history__inner {max-width: 1200px !important; margin: 0 auto !important; padding: 15px !important;}
.history__item {background: rgba(108, 92, 231, 0.05) !important; border-radius: 10px !important; padding: 20px !important; margin-bottom: 20px !important;}
.history__body {display: flex !important; flex-wrap: wrap !important; gap: 15px !important; padding: 15px !important; border-radius: 8px !important;}
.history__body--gained {background: rgba(46, 213, 115, 0.1) !important; margin-bottom: 10px !important;}
.history__body--lost {background: rgba(255, 71, 87, 0.1) !important;}
@media screen and (min-width: 769px) {.history__body-item {width: 120px !important; height: auto !important; transition: transform 0.2s !important;} .history__body-item img {width: 120px !important; height: auto !important; border-radius: 8px !important; box-shadow: 0 2px 8px rgba(0,0,0,0.1) !important;}}
.history__body-item:hover {transform: scale(1.05) !important; z-index: 2 !important;}
@media screen and (max-width: 768px) {.history__body-item, .history__body-item img {width: 120px !important;} .processing-card::before {top: -1px !important; left: -1px !important; right: -1px !important; bottom: -1px !important; opacity: 0.5 !important;}}
.progress-bar {position: fixed; top: 0; left: 0; width: 100%; height: 4px; background: #ddd; z-index: 10000;}
.progress-bar__fill {width: 0%; height: 100%; background: linear-gradient(to right, #6c5ce7, #a367dc); transition: width 0.3s ease; position: relative;}
.progress-bar__text {position: absolute; left: 50%; top: 5px; transform: translateX(-50%); color: #ffffff; font-size: 14px; font-weight: bold; background: #6c5ce7; padding: 2px 8px; border-radius: 10px; box-shadow: 0 2px 4px rgba(0,0,0,0.2); text-shadow: 1px 1px 2px rgba(0,0,0,0.3);}
.card-stats span {display: flex; align-items: center; gap: 4px;}
.card-stats span i {font-size: 14px;}
@media screen and (max-width: 768px) {.action-button {transform: scale(0.8) !important;} .action-button button {width: 35px !important; height: 35px !important;} .action-button span {font-size: 16px !important;} .action-button div {font-size: 12px !important; padding: 6px 10px !important;} .card-stats {font-size: 10px !important; padding: 4px !important;} .card-stats span i {font-size: 12px !important;} .remelt__inventory-list {grid-template-columns: repeat(2, 1fr) !important; gap: 10px !important;} .remelt__inventory-item {width: 100% !important; margin: 0 !important;} .remelt__inventory-item img {width: 100% !important; height: auto !important;} .remelt__inventory-item .card-stats {width: 100% !important; margin-top: 4px !important;} .lootbox__card {transform: scale(0.75) !important; margin-top: -10px !important; margin-bottom: 25px !important;} .lootbox__card .card-stats {font-size: 14px !important; padding: 6px !important;} .lootbox__card .card-stats i {font-size: 14px !important;} .lootbox__list {gap: 15px !important; padding-bottom: 15px !important;} .history__body-item {width: 100px !important;} .history__body-item img {width: 100px !important;} .progress-bar {height: 3px !important;} .progress-bar__text {font-size: 12px !important; padding: 1px 6px !important;}}
.card-stats {position: relative; background: linear-gradient(45deg, #6c5ce7, #a367dc); padding: 8px; color: white; font-size: 12px; margin-top: 5px; border-radius: 5px; display: flex; justify-content: space-between; align-items: center; text-shadow: 1px 1px 2px rgba(0,0,0,0.3); animation: fadeInUp 0.3s ease;}
.lootbox__card {position: relative !important; transform: scale(0.85) !important; margin-top: -15px !important; margin-bottom: 35px !important;}
.lootbox__card .card-stats {position: absolute !important; bottom: -35px !important; left: 0 !important; right: 0 !important; margin: 0; padding: 8px !important; border-radius: 5px; z-index: 9999 !important; background: linear-gradient(45deg, #6c5ce7, #a367dc) !important; font-size: 16px !important; width: 100% !important; transform: none !important; text-rendering: optimizeLegibility !important; -webkit-font-smoothing: antialiased !important;}
.lootbox__card .card-stats span {color: white !important; text-shadow: 1px 1px 2px rgba(0,0,0,0.3) !important; padding: 0 8px !important; flex: 1; text-align: center; font-weight: 500 !important;}
.lootbox__card .card-stats i {color: white !important; font-size: 16px !important; margin-right: 4px;}
.lootbox__list {gap: 25px !important; padding-bottom: 20px !important;}
@media screen and (max-width: 768px) {.lootbox__card {transform: scale(0.8) !important; margin-top: -20px !important; margin-bottom: 30px !important;}}
`;
document.head.appendChild(style);

function clearIcons() {
    $('.card-notification:first')?.click();
}

function autoRepeatCheck() {
    clearIcons();
    checkGiftCard(document);
    document.querySelector('.adv_volume.volume_on')?.click();
    Audio.prototype.play = function() {
       return new Promise(() => {});
    };
}

async function checkGiftCard(doc) {
    const button = doc.querySelector('#gift-icon');
    if (!button) return;

    const giftCode = button.getAttribute('data-code');
    if (!giftCode) return false;

    try {
        const response = await fetch('/engine/ajax/controller.php?mod=gift_code_game', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded'
            },
            body: new URLSearchParams({
                code: giftCode,
                user_hash: dle_login_hash
            })
        });
        const data = await response.json();
        if (data.status === 'ok') {
            showNotification(data.text);
            button.remove();
        }
    } catch (error) {
        console.error("Error checking gift card:", error);
    }
}

async function startPing() {
    const userHash = window.dle_login_hash;
    if (!userHash) {
        return;
    }
    try {
        await sleep(DELAY * 3);
        await new Promise((resolve, reject) => {
            $.ajax({
                url: "/engine/ajax/controller.php?mod=user_count_timer",
                type: "post",
                data: {
                    user_hash: userHash
                },
                dataType: "json",
                cache: false,
                success: (data) => {
                    resolve(data);
                },
                error: (jqXHR, textStatus, errorThrown) => {
                    console.error("Error in startPing (jQuery):", textStatus, errorThrown);
                    reject(errorThrown);
                }
            });
        });
    } catch (e) {
    }
}

async function checkNewCard() {
    let userHash = window.dle_login_hash;
    if (!userHash) {
        setTimeout(() => {
            userHash = window.dle_login_hash;
            if (userHash) {
                checkNewCard();
            }
        }, 2000);
        return;
    }

    const currentDateTime = new Date();
    const localStorageKey = 'checkCardStopped' + userHash;
    const currentHourMarker = currentDateTime.toISOString().slice(0, 13);
    if (localStorage.getItem(localStorageKey) === currentHourMarker) {
        return;
    }

    try {
        await sleep(DELAY * 3);
        const data = await new Promise((resolve, reject) => {
            $.ajax({
                url: "/engine/ajax/controller.php?mod=reward_card",
                type: "post",
                data: {
                    action: "check_reward",
                    user_hash: userHash
                },
                dataType: "json",
                cache: false,
                success: (responseData) => {
                    resolve(responseData);
                },
                error: (jqXHR, textStatus, errorThrown) => {
                    console.error("Error checking new card (jQuery check_reward):", textStatus, errorThrown);
                    reject(errorThrown);
                }
            });
        });

        if (data.stop_reward === "yes") {
            localStorage.setItem(localStorageKey, currentHourMarker);
            return;
        }
        if (data && data.cards && data.cards.owner_id) {
            const ownerId = data.cards.owner_id;
            if (data.cards.name) {
                showNotification('Получена новая карта "' + data.cards.name + '"');
            }
            await new Promise((resolve, reject) => {
                $.ajax({
                    url: "/engine/ajax/controller.php?mod=cards_ajax",
                    type: "post",
                    data: {
                        action: "take_card",
                        owner_id: ownerId,
                        user_hash: userHash
                    },
                    dataType: "json",
                    cache: false,
                    success: (data_take) => {
                        resolve(data_take);
                    },
                    error: (jqXHR, textStatus, errorThrown) => {
                        console.error("Error taking card (jQuery):", textStatus, errorThrown);
                        reject(errorThrown);
                    }
                });
            });
        }
    } catch (e) {
    }
}

async function setCache(key, data, baseTtlInSeconds = 86400) {
    const jitterPercent = 0.10;
    const jitter = Math.round(baseTtlInSeconds * jitterPercent * (Math.random() * 2 - 1));
    const finalTtlInSeconds = baseTtlInSeconds + jitter;
    const expires = Date.now() + finalTtlInSeconds * 1000;
    const cacheData = { data, expires };
    try {
        localStorage.setItem(key, JSON.stringify(cacheData));
    } catch (e) {
        console.error("Ошибка при записи в localStorage (возможно, переполнен):", e);
    }
}

async function getCache(key) {
    const cacheData = JSON.parse(localStorage.getItem(key));
    if (!cacheData) return null;
    if (Date.now() > cacheData.expires) {
        localStorage.removeItem(key);
        return null;
    }
    return cacheData.data;
}

async function cacheCard(key, data) {
    await setCache(key, data);
}

async function getCard(key) {
    return await getCache(key);
}

function addClearButton() {
    const filterControls = document.querySelector('.card-filter-form__controls');
    if (!filterControls) {
        return;
    }
    const inputField = filterControls.querySelector('.card-filter-form__search');
    if (!inputField) {
        return;
    }
    const searchButton = filterControls.querySelector('.tabs__search-btn');
    if (!searchButton) {
        return;
    }
    inputField.addEventListener('keydown', function (event) {
        if (event.key === 'Enter') {
            event.preventDefault();
            searchButton.click();
        }
    });
    const clearButton = document.createElement('button');
    clearButton.innerHTML = '<i class="fas fa-times"></i>';
    clearButton.classList.add('clear-search-btn');
    clearButton.style.margin = '5px';
    clearButton.style.position = 'absolute';
    clearButton.style.padding = '10px';
    clearButton.style.background = 'red';
    clearButton.style.color = 'white';
    clearButton.style.border = 'none';
    clearButton.style.cursor = 'pointer';
    clearButton.style.boxShadow = '0 2px 5px rgba(0, 0, 0, 0.2)';
    clearButton.style.fontSize = '14px';
    clearButton.style.borderRadius = '5px';
    clearButton.addEventListener('click', function () {
        inputField.value = '';
        searchButton.click();
    });
    inputField.style.marginLeft = '30px';
    inputField.parentNode.insertBefore(clearButton, inputField);
}

function showNotification(message) {
    const existing = document.querySelector('.custom-card-notification');
    if (existing) existing.remove();
    const notification = document.createElement('div');
    notification.className = 'custom-card-notification';
    notification.style.cssText = `
        position: fixed;
        top: -100px;
        left: 50%;
        transform: translateX(-50%);
        background: linear-gradient(45deg, #f21db2, #db0b81);
        color: white;
        padding: 10px 24px;
        border-radius: 10px;
        box-shadow: 0 8px 20px rgba(0,0,0,0.3);
        z-index: 2147483647;
        font-size: 16px;
        font-weight: bold;
        text-align: center;
        max-width: 90%;
        white-space: pre-wrap;
        transition: top 0.5s ease-in-out;
    `;
    notification.textContent = '⭐ ' + message;
    document.body.appendChild(notification);
    requestAnimationFrame(() => {
        notification.style.top = '20px';
    });
    setTimeout(() => {
        notification.style.top = '-100px';
        setTimeout(() => {
            notification.remove();
        }, 500);
    }, 3000);
}

function createProgressBar() {
    const progressBar = document.createElement('div');
    progressBar.className = 'progress-bar';
    progressBar.innerHTML = `
        <div class="progress-bar__fill"></div>
        <div class="progress-bar__text">0%</div>
    `;
    const style = document.createElement('style');
    style.textContent = `
        .progress-bar {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 4px;
            background: #ddd;
            z-index: 10000;
        }
        .progress-bar__fill {
            width: 0%;
            height: 100%;
            background: linear-gradient(to right, #6c5ce7, #a367dc);
            transition: width 0.3s ease;
            position: relative;
        }
        .progress-bar__text {
            position: absolute;
            left: 50%;
            top: 5px;
            transform: translateX(-50%);
            color: #ffffff;
            font-size: 14px;
            font-weight: bold;
            background: #6c5ce7;
            padding: 2px 8px;
            border-radius: 10px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.2);
            text-shadow: 1px 1px 2px rgba(0,0,0,0.3);
        }
    `;
    document.head.appendChild(style);
    document.body.appendChild(progressBar);
    return {
        update: (current, total) => {
            const percentage = total > 0 ? Math.round((current / total) * 100) : 0;
            progressBar.querySelector('.progress-bar__fill').style.width = percentage + '%';
            progressBar.querySelector('.progress-bar__text').textContent = percentage + '%';
        },
        remove: () => progressBar.remove()
    };
}

function clearCardCache() {
    const keys = Object.keys(localStorage);
    let clearedCount = 0;
    for (const key of keys) {
        if (key.startsWith('cardId: ')) {
            const value = localStorage.getItem(key);
            try {
                const parsed = JSON.parse(value);
                if (parsed && typeof parsed === 'object' && 'data' in parsed && 'expires' in parsed) {
                    localStorage.removeItem(key);
                    clearedCount++;
                }
            } catch (e) {
            }
        }
    }
    showNotification(`Очищено ${clearedCount} карточек из кеша`);
}

function addClearCacheButton() {
    if (window.location.pathname.includes('/pm/') ||
        window.location.pathname.includes('emotions.php') ||
        window.frameElement) {
        return;
    }
    if (!document.querySelector('#clearCacheButton')) {
        const button = getButton('clearCacheButton', 'trash', 63, 'Очистить кеш карт', clearCardCache);
        document.body.appendChild(button);
    }
}

(function() {
    'use strict';

    function initializeScript() {
        if (typeof $ === 'undefined') {
            console.error("jQuery не найден. Функции, зависящие от jQuery, могут не работать.");
        }
        if (typeof dle_login_hash === 'undefined') {
            console.warn("Переменная dle_login_hash не определена. Некоторые функции могут не работать корректно.");
        }

        addUpdateButton();
        addClearButton();
        addClearCacheButton();

        setInterval(autoRepeatCheck, 2000);
        setInterval(startPing, 31000);
        setInterval(checkNewCard, 10000);

        $('#tg-banner')?.remove();
        localStorage.setItem('notify18', 'closed');
        localStorage.setItem('hideTelegramAs', 'true');
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initializeScript);
    } else {
        initializeScript();
    }
})();