Card Helper AStars | AnimeStars | ASStars

card helper

当前为 2025-04-19 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Card Helper AStars | AnimeStars | ASStars
// @namespace    animestars.org
// @version      6.9
// @description  card helper
// @author       bmr
// @match        https://astars.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 = 40;

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 getCount(cardId, type) {
    const currentDomain = getCurrentDomain();
    let count = 0;
    let needResponse = await fetch(`${currentDomain}/cards/${cardId}/users/${type}/`);
    if (needResponse.status === 502) {
        throw new Error("502 Bad Gateway");
    }
    let needHtml = '';
    let needDoc = '';
    if (needResponse.ok) {
        needHtml = await needResponse.text();
        needDoc = new DOMParser().parseFromString(needHtml, 'text/html');
        count = needDoc.querySelectorAll('.profile__friends-item').length;
    } else {
        return count;
    }

    const pagination = needDoc.querySelector('.pagination__pages');
    if (pagination && count >= 50) {
        const lastPageNum = pagination.querySelector('a:last-of-type');
        const totalPages = lastPageNum ? parseInt(lastPageNum.innerText, 10) : 1;
        if (totalPages > 1) {
            count = (totalPages - 1) * 50;
        }
        needResponse = await fetch(`${currentDomain}/cards/${cardId}/users/${type}/page/${totalPages}`);
        if (needResponse.status === 502) {
            throw new Error("502 Bad Gateway");
        }
        if (needResponse.ok) {
            needHtml = await needResponse.text();
            needDoc = new DOMParser().parseFromString(needHtml, 'text/html');
            count += needDoc.querySelectorAll('.profile__friends-item').length;
        }
    }

    return count;
}

async function loadCard(cardId) {
    const cacheKey = 'cardId: ' + cardId;
    let card = await getCard(cacheKey) ?? {};

    if (Object.keys(card).length) {
        return card;
    }

    const currentDomain = getCurrentDomain();
    await sleep(DELAY);
    let needCount = await getCount(cardId, 'need');
    await sleep(DELAY);
    let tradeCount = await getCount(cardId, 'trade');
    await sleep(DELAY);

    let popularityCount = 0;
    const popularityResponse = await fetch(`${currentDomain}/cards/${cardId}/users/`);

    if (popularityResponse.ok) {
        const popularityHtml = await popularityResponse.text();
        const popularityDoc = new DOMParser().parseFromString(popularityHtml, 'text/html');

        const pagination = popularityDoc.querySelector('.pagination__pages');
        if (pagination) {
            const lastPageNum = pagination.querySelector('a:last-of-type');
            const totalPages = lastPageNum ? parseInt(lastPageNum.innerText, 10) : 1;

            if (totalPages > 1) {
                popularityCount = (totalPages - 1) * 40;
                await sleep(DELAY);
                const lastPageResponse = await fetch(`${currentDomain}/cards/${cardId}/users/page/${totalPages}`);
                if (lastPageResponse.ok) {
                    const lastPageHtml = await lastPageResponse.text();
                    const lastPageDoc = new DOMParser().parseFromString(lastPageHtml, 'text/html');
                    const lastPageCount = lastPageDoc.querySelectorAll('.card-show__owner').length;
                    popularityCount += lastPageCount;
                }
            } else {
                popularityCount = popularityDoc.querySelectorAll('.card-show__owner').length;
            }
        } else {
            popularityCount = popularityDoc.querySelectorAll('.card-show__owner').length;
        }
    }

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

async function updateCardInfo(cardId, element) {
    if (!cardId || !element) {
        return;
    }
    try {
        const card = 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="Владельцев">
                <i class="fas fa-users"></i>
                ${card.popularityCount}
            </span>
            <span title="Хотят получить">
                <i class="fas fa-heart"></i>
                ${card.needCount}
            </span>
            <span title="Готовы обменять">
                <i class="fas fa-sync-alt"></i>
                ${card.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(card => card.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 cards = getCardsOnPage();
    let counter = cards.length;

    if (!counter) {
        return;
    }

    let buttonId = 'processCards';
    startAnimation(buttonId);
    updateButtonCounter(buttonId, counter);

    showNotification('Проверяю спрос на ' + counter + ' карточек');

    for (const card of cards) {
        if (card.classList.contains('trade__inventory-item--lock') || card.classList.contains('remelt__inventory-item--lock')) {
            continue;
        }
        card.classList.add('processing-card');
        let cardId = await getCardId(card);
        if (cardId) {
            await updateCardInfo(cardId, card).catch(error => {
                return;
            });
            counter--;
            updateButtonCounter(buttonId, counter);
        }
        card.classList.remove('processing-card');

        if (card.classList.contains('lootbox__card')) {
            card.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) {
        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;

    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);

    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;
    `;

    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('#fetchLinksButton')) {
        let cards = getCardsOnPage();

        document.body.appendChild(getButton('processCards', 'star', 37, 'Сравнить карточки', processCards));

        if (!cards.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 cards = getCardsOnPage();
    let counter = cards.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 == Object.keys(storedData).length) {
            showNotification('На данный момент в кеше карточек ровно столько же сколько в профиле пользователя');
            return;
        }
    }

    async function processCardsToLocalstorage(doc) {
        const cards = doc.querySelectorAll('.anime-cards__item');
        for (let i = 0; i < cards.length; i += 5) {
            const cardGroup = Array.from(cards).slice(i, i + 5);
            for (const card of cardGroup) {
                const cardId = card.getAttribute('data-id');
                const ownerId = card.getAttribute('data-owner-id');
                const name = card.getAttribute('data-name');
                const rank = card.getAttribute('data-rank');
                const animeLink = card.getAttribute('data-anime-link');
                const image = card.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 response = await fetch(url);
            if (!response.ok) throw new Error(`Ошибка загрузки страницы ${url}`);
            return await response.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(1000);

                if (i % 3 === 0) {
                    localStorage.setItem('animeCardsData', JSON.stringify(storedData));
                }
            }
        }
    }

    localStorage.setItem('animeCardsData', JSON.stringify(storedData));

    setTimeout(() => {
        progressBar.remove();
    }, 1000);

    document.body.appendChild(getButton('processCards', 'star', 37, 'Сравнить карточки', processCards));
    await processCards();
}

async function getCardId(card) {
    let cardId = card.getAttribute('card-id') || card.getAttribute('data-card-id') || card.getAttribute('data-id');
    const href = card.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 cards = getCardsOnPage();

    let counter = cards.length;
    let buttonId = 'readyToCharge';
    startAnimation(buttonId);
    updateButtonCounter(buttonId, counter);
    clearMarkFromCards();

    const progressBar = createProgressBar();
    cardCounter = 0;

    const totalCards = cards.filter(card => !card.classList.contains('trade__inventory-item--lock')).length;
    let processedCards = 0;

    for (const card of cards) {
        if (card.classList.contains('trade__inventory-item--lock')) {
            continue;
        }

        let cardId = await getCardId(card);
        if (cardId) {
            await readyToChargeCard(cardId);
            processedCards++;
            progressBar.update(processedCards, totalCards);
            counter--;
            updateButtonCounter(buttonId, counter);
        }
    }

    setTimeout(() => {
        progressBar.remove();
    }, 1000);

    showNotification('Отправили на обмен ' + cardCounter + ' карточек на странице');
    stopAnimation(buttonId);
}

const readyToChargeCard = async (cardId) => {
    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: cardId,
                    user_hash: dle_login_hash
                },
                dataType: "json",
                cache: false,
                success: resolve,
                error: reject
            });
        });

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

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

        if (data?.status == "deleted") {
            return await readyToChargeCard(cardId);
        }

        return false;

    } catch (e) {
        console.error("Ошибка запроса:", 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: 150px !important;
        height: auto !important;
        transition: transform 0.2s !important;
    }

    .history__body-item img {
        width: 150px !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);

    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) {
    }
}

function startPing() {
    const userHash = window.dle_login_hash;
    if (!userHash) {
        return;
    }
    const currentDomain = getCurrentDomain();
    const url = `${currentDomain}/engine/ajax/controller.php?mod=user_count_timer&user_hash=${userHash}`;
    fetch(url)
        .then(response => {
        if (!response.ok) {
            throw new Error(`HTTP error! Status: ${response.status}`);
        }
        return response.json();
    })
        .then(data => {
    })
        .catch(error => {
    });
}

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;
    if (localStorage.getItem(localStorageKey) === currentDateTime.toISOString().slice(0, 13)) {
        return;
    }

    const currentDomain = getCurrentDomain();
    const url = `${currentDomain}/engine/ajax/controller.php?mod=reward_card&action=check_reward&user_hash=${userHash}`;

    fetch(url)
        .then(response => {
            if (!response.ok) {
                throw new Error(`HTTP error! Status: ${response.status}`);
            }
            return response.json();
        })
        .then(data => {
            if (data.stop_reward === "yes") {
                localStorage.setItem(localStorageKey, currentDateTime.toISOString().slice(0, 13));
                return;
            }
            const url = `${currentDomain}/engine/ajax/controller.php?mod=cards_ajax`;
            const postData = new URLSearchParams({
                action: "take_card",
                owner_id: ownerId
            });
            fetch(url, {
                method: "POST",
                headers: {
                    "Content-Type": "application/x-www-form-urlencoded"
                },
                body: postData.toString()
            })
                .then(response => {
                    if (!response.ok) {
                        throw new Error(`HTTP error! Status: ${response.status}`);
                    }
                    return response.json();
                })
                .then(data => {
                })
                .catch(error => {
                });
        })
        .catch(error => {
        });
}

async function setCache(key, data, ttlInSeconds) {
    const expires = Date.now() + ttlInSeconds * 1000;
    const cacheData = { data, expires };
    localStorage.setItem(key, JSON.stringify(cacheData));
}

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, 3600);
}

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 = Math.round((current / total) * 100);
            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;

    keys.forEach(key => {
        if (key.startsWith('cardId: ')) {
            localStorage.removeItem(key);
            clearedCount++;
        }
    });

    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);
    }
}

let currentTime = 0;
const VIDEO_DURATION = 1440;

function simulateWatching() {
    const userHash = window.dle_login_hash;
    if (!userHash) return;

    const currentDomain = window.location.origin;
    currentTime += 30;
    if (currentTime > VIDEO_DURATION) currentTime = 30;

    fetch(`${currentDomain}/engine/ajax/controller.php`, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
            'X-Requested-With': 'XMLHttpRequest',
            'Accept': 'application/json, text/javascript, */*; q=0.01',
            'Sec-Fetch-Site': 'same-origin',
            'Sec-Fetch-Mode': 'cors'
        },
        body: new URLSearchParams({
            mod: 'user_count_timer',
            user_hash: userHash,
            time: currentTime.toString(),
            duration: VIDEO_DURATION.toString(),
            episode: '1',
            watch: '1'
        })
    });

    fetch(`${currentDomain}/engine/ajax/controller.php`, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
            'X-Requested-With': 'XMLHttpRequest',
            'Accept': 'application/json, text/javascript, */*; q=0.01'
        },
        body: new URLSearchParams({
            mod: 'reward_card',
            action: 'check_reward',
            user_hash: userHash,
            time: currentTime.toString(),
            duration: VIDEO_DURATION.toString()
        })
    });
}

(function() {
    'use strict';

    function checkTimeAndReset() {
        const now = new Date();
        const mskTime = new Date(now.getTime() + (now.getTimezoneOffset() + 180) * 60000);
        const hours = mskTime.getHours();
        const today = mskTime.toISOString().split('T')[0];

        const userHash = window.dle_login_hash;
        if (!userHash) return;

        const stopKey = 'checkCardStopped' + userHash;
        const stopValue = localStorage.getItem(stopKey);

        if (hours >= 0 && stopValue) {
            localStorage.removeItem(stopKey);
            console.log('Сброс ограничения сбора карт выполнен');

            checkNewCard();
            setTimeout(checkNewCard, 2000);
            setTimeout(checkNewCard, 5000);
        }
    }

    function initializeScript() {
        checkTimeAndReset();

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

        setInterval(autoRepeatCheck, 2000);
        setInterval(startPing, 31000);
        setInterval(checkNewCard, 10000);
        setInterval(simulateWatching, 30000);
        setInterval(checkTimeAndReset, 60000);
        setInterval(clickOnCrystal, 3000);
        setInterval(preventTimeout, 180000);
        setInterval(closeExpiredPopup, 5000);

        console.log("🚀 AutoCrystal функции успешно загружены!");

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

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

    window.addEventListener('pageshow', initializeScript);

    let lastActiveTime = "00:00";

const clickedDiamonds = new Set();

function clickOnCrystal() {
    const chatMessages = document.querySelectorAll(".lc_chat_li");

    chatMessages.forEach(msg => {
        const author = msg.querySelector(".lc_chat_li_autor");
        const text = msg.querySelector(".lc_chat_li_text");
        const diamond = msg.querySelector("#diamonds-chat");
        const timeElement = msg.querySelector(".lc_chat_li_date");

        if (author && text && diamond && timeElement) {
            const messageTime = timeElement.textContent.trim();
            const messageKey = messageTime + "_" + text.textContent.trim();

            if (
                author.textContent.toLowerCase().includes("ии космический посикунчик") &&
                messageTime >= lastActiveTime &&
                !clickedDiamonds.has(messageKey)
            ) {
                console.log("💎 Найден кристалл от бота.");
                diamond.click();
                clickedDiamonds.add(messageKey);
                lastActiveTime = messageTime;
            }
        }
    });
}

function preventTimeout() {
    let timeoutButton = document.querySelector(".lc_chat_timeout_imback") ||
                        document.querySelector(".timeout-button") ||
                        document.querySelector(".afk-return-button") ||
                        [...document.querySelectorAll("button")].find(b => b.textContent.includes("Я вернулся"));

    if (timeoutButton) {
        console.log("🔄 Найдена AFK-кнопка, нажимаем 'Я вернулся'!");
        timeoutButton.click();
    }
}

function closeExpiredPopup() {
    let popupCloseButton = document.querySelector(".modal-content .close") ||
                           document.querySelector(".modal-close") ||
                           document.querySelector(".notification-close");

    if (popupCloseButton) {
        console.log("❌ Найдено уведомление 'Камень не активный', закрываем.");
        popupCloseButton.click();
    }
}

})();