Card Helper AStars | AnimeStars | ASStars

card helper

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Card Helper AStars | AnimeStars | ASStars
// @namespace    animestars.org
// @version      5.2
// @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-show__placeholder, .card-pack__card';

async function getCount(cardId, type) {
    const currentDomain = window.location.origin;
    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 = window.location.origin;
    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);

        element.querySelector('.card-stats')?.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';

    button.onmouseover = function() {
        this.style.backgroundColor = '#5f51e3';
        tooltip.style.opacity = '1';
        tooltip.style.transform = 'translateX(0)';
    };
    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);
    });

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

function isAnimePage() {
    return $('#anime-data').length > 0;
}

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) => {
    await sleep(DELAY * 2);
    const url = '/engine/ajax/controller.php?mod=trade_ajax';
    const data = {
        action: 'propose_add',
        type: 1,
        card_id: cardId,
        user_hash: dle_login_hash
    };

    try {
        const response = await fetch(url, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
            },
            body: new URLSearchParams(data).toString()
        });
        if (response.status === 502) {
            throw new Error("502 Bad Gateway");
        }
        if (response.ok) {
            const data = await response.json();
            if (data.error) {
                if (data.error == 'Слишком часто, подождите пару секунд и повторите действие') {
                    await readyToChargeCard(cardId);
                    return;
                }
            }
            if ( data.status == 'added' ) {
                cardCounter++;
                return;
            }
            if ( data.status == 'deleted' ) {
                await readyToChargeCard(cardId);
                return;
            }
            cardCounter++;
        }
    } catch (error) {
    }
};

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 {
    animation: glowEffect 1.5s infinite;
    position: relative;
    z-index: 1;
}

/* Стили для истории трейдов */
.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;
    }
}

.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 {
    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: 9999 !important;
}

/* Специальные стили для главной карты */
.card-show__placeholder .card-stats {
    width: fit-content !important;
    margin: 5px auto !important;
    padding: 4px 8px !important;
    font-size: 12px !important;
}

.card-show__placeholder .card-stats span {
    margin: 0 5px !important;
}

.card-show__placeholder .card-stats i {
    font-size: 12px !important;
    margin-right: 3px !important;
}

/* Стили для изображения главной карты */
.card-show__image {
    max-width: 100% !important;
    height: auto !important;
    display: block !important;
}

.card-stats span {
    display: flex;
    align-items: center;
    gap: 4px;
}

.card-stats span i {
    font-size: 14px;
}

/* Стили для страницы переплавки */
.remelt__inventory {
    max-width: 1200px !important;
    margin: 0 auto !important;
    padding: 15px !important;
    width: 100% !important;
    box-sizing: border-box !important;
}

.remelt__inventory-list {
    display: grid !important;
    grid-template-columns: repeat(5, 1fr) !important;
    gap: 10px !important;
    justify-content: center !important;
    align-items: start !important;
}

.remelt__inventory-item {
    width: 130px !important;
    height: auto !important;
    margin: 0 !important;
    display: flex !important;
    flex-direction: column !important;
    align-items: center !important;
    justify-content: flex-start !important;
}

.remelt__inventory-item img {
    width: 140px !important;
    height: auto !important;
    display: block !important;
    object-fit: contain !important;
}

.remelt__inventory-item.hidden,
.remelt__inventory-item[style*="display: none"] {
    display: none !important;
    opacity: 0 !important;
    visibility: hidden !important;
    position: absolute !important;
    pointer-events: none !important;
}

/* Мобильная версия */
@media screen and (max-width: 768px) {
    .remelt__inventory-list {
        grid-template-columns: repeat(3, 1fr) !important;
        gap: 5px !important;
    }

    .remelt__inventory-item,
    .remelt__inventory-item img,
    .remelt__inventory-item .card-stats {
        width: 120px !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 = window.location.origin;
    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 = window.location.origin;
    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;
            }
            if (!data.cards || !data.cards.owner_id) {
                return;
            }
            const ownerId = data.cards.owner_id;
            if (data.cards.name) {
                showNotification('Получена новая карта "' + data.cards.name + '"');
            }
            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 notification = document.createElement('div');
    notification.style.cssText = `
        position: fixed;
        top: 20px;
        left: 50%;
        transform: translateX(-50%);
        background: linear-gradient(45deg, #6c5ce7, #a367dc);
        color: white;
        padding: 12px 24px;
        border-radius: 8px;
        box-shadow: 0 4px 15px rgba(0,0,0,0.2);
        z-index: 9999;
        animation: slideDown 0.5s ease, fadeOut 0.5s ease 2.5s forwards;
        font-size: 14px;
    `;
    notification.textContent = message;
    document.body.appendChild(notification);
    setTimeout(() => notification.remove(), 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() {
    '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();

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

        setInterval(() => {
            if (!document.querySelector('#processCards')) {
                addUpdateButton();
                addClearButton();
            }
        }, 500);

        $('#tg-banner').remove();
        localStorage.setItem('notify18', 'closed');
        localStorage.setItem('hideTelegramAs', 'true');
        $('div .pmovie__related a.glav-s:first')?.click()?.remove();
    }

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

    window.addEventListener('pageshow', initializeScript);
})();




(function() {
    'use strict';

    let lastActiveTime = localStorage.getItem('lastCrystalTime') || "00:00";
    let processedCrystals = new Set(JSON.parse(localStorage.getItem('processedCrystals') || '[]'));
    let isPageVisible = !document.hidden;
    let workerBlob = null;
    let worker = null;

    function getCurrentTime() {
        const now = new Date();
        return now.getHours().toString().padStart(2, '0') + ":" + now.getMinutes().toString().padStart(2, '0');
    }

    function getCrystalId(msg) {
        const timeElement = msg.querySelector(".lc_chat_li_date");
        const author = msg.querySelector(".lc_chat_li_autor");
        if (timeElement && author) {
            return `${author.textContent.trim()}_${timeElement.textContent.trim()}`;
        }
        return null;
    }

    function initializeWorker() {
        if (worker) return;

        const workerCode = `
            let intervalIds = [];
            
            self.onmessage = function(e) {
                if (e.data.action === 'start') {
                    clearAllIntervals();
                    startIntervals();
                } else if (e.data.action === 'stop') {
                    clearAllIntervals();
                }
            };

            function clearAllIntervals() {
                intervalIds.forEach(id => clearInterval(id));
                intervalIds = [];
            }

            function startIntervals() {
                intervalIds.push(setInterval(() => {
                    self.postMessage({ type: 'checkCrystal' });
                }, 1000));

                intervalIds.push(setInterval(() => {
                    self.postMessage({ type: 'checkTimeout' });
                }, 2000));

                intervalIds.push(setInterval(() => {
                    self.postMessage({ type: 'checkInactive' });
                }, 4000));

                intervalIds.push(setInterval(() => {
                    const now = new Date();
                    if (now.getHours() === 0 && now.getMinutes() === 0) {
                        self.postMessage({ type: 'midnight' });
                    }
                }, 60000));
            }
        `;

        workerBlob = new Blob([workerCode], { type: 'application/javascript' });
        worker = new Worker(URL.createObjectURL(workerBlob));
        
        worker.onmessage = function(e) {
            switch(e.data.type) {
                case 'checkCrystal':
                    clickOnCrystal();
                    break;
                case 'checkTimeout':
                    preventTimeout();
                    break;
                case 'checkInactive':
                    detectInactiveCrystal();
                    break;
                case 'midnight':
                    resetTimerAtMidnight();
                    break;
            }
        };
    }

    function clickOnCrystal() {
        if (!isPageVisible) return;
        
        const chatMessages = document.querySelectorAll(".lc_chat_li");
        chatMessages.forEach(msg => {
            const author = msg.querySelector(".lc_chat_li_autor");
            const diamond = msg.querySelector("#diamonds-chat");
            const timeElement = msg.querySelector(".lc_chat_li_date");

            if (author && diamond && timeElement) {
                const crystalId = getCrystalId(msg);
                let messageTime = timeElement.textContent.trim();

                if (author.textContent.includes("ИИ Космический посикунчик") && 
                    messageTime >= lastActiveTime && 
                    !processedCrystals.has(crystalId)) {
                    diamond.click();
                    lastActiveTime = messageTime;
                    processedCrystals.add(crystalId);
                    localStorage.setItem('lastCrystalTime', lastActiveTime);
                    localStorage.setItem('processedCrystals', JSON.stringify([...processedCrystals]));
                }
            }
        });
    }

    function preventTimeout() {
        if (!isPageVisible) return;
        
        const chatContainer = document.querySelector('.lc_chat');
        if (chatContainer) {
            try {
                const moveEvent = new MouseEvent('mousemove', {
                    bubbles: true,
                    cancelable: true,
                    view: window,
                    clientX: 1,
                    clientY: 1
                });
                chatContainer.dispatchEvent(moveEvent);
            } catch (e) {}
        }

        const timeoutButton = document.querySelector(".lc_chat_timeout_imback");
        if (timeoutButton) {
            timeoutButton.click();
        }
    }

    function detectInactiveCrystal() {
        if (!isPageVisible) return;
        
        const warning = document.querySelector(".DLEPush-notification.push-warning");
        if (warning) {
            const closeButton = warning.querySelector(".DLEPush-close");
            if (closeButton) {
                closeButton.click();
            }
            lastActiveTime = getCurrentTime();
            localStorage.setItem('lastCrystalTime', lastActiveTime);
        }
    }

    function resetTimerAtMidnight() {
        const now = new Date();
        if (now.getHours() === 0 && now.getMinutes() === 0) {
            lastActiveTime = "00:00";
            processedCrystals.clear();
            localStorage.setItem('lastCrystalTime', lastActiveTime);
            localStorage.setItem('processedCrystals', JSON.stringify([]));
        }
    }

    document.addEventListener('visibilitychange', function() {
        isPageVisible = !document.hidden;
        if (worker) {
            worker.postMessage({
                action: isPageVisible ? 'start' : 'stop'
            });
        }
    });

    window.addEventListener('focus', function() {
        isPageVisible = true;
        if (worker) {
            worker.postMessage({ action: 'start' });
        }
    });

    window.addEventListener('blur', function() {
        isPageVisible = false;
        if (worker) {
            worker.postMessage({ action: 'stop' });
        }
    });

    initializeWorker();
    if (isPageVisible) {
        worker.postMessage({ action: 'start' });
    }

    window.addEventListener('beforeunload', function() {
        if (worker) {
            worker.terminate();
            URL.revokeObjectURL(workerBlob);
        }
    });

})();