animestars Auto Helper

хелпер который помогает определить популярность карты на сайте astars.club

Ajankohdalta 6.2.2025. Katso uusin versio.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         animestars Auto Helper
// @namespace    animestars.org
// @version      3.04
// @description  хелпер который помогает определить популярность карты на сайте astars.club
// @author       astars lover
// @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 = 500; // Задержка между запросами в миллисекундах (по умолчанию 0,5 секунды) не менять чтоб не делать избыточную нагрузку на сайт

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

let cardCounter = 0;

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) {
        console.error("Ошибка 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) {
            console.error("Ошибка 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 updateCardInfo(cardId, element) {

    // Определяем текущий домен
    const currentDomain = window.location.origin;

    if (!cardId || !element) {
        console.log(cardId, 'updateCardInfo error');
        return;
    }

    try {
        console.log(`Обработка карточки с ID: ${cardId}`);

        await sleep(DELAY);

        // Получение количества "Желающих"
        let needCount = await getCount(cardId, 'need');

        await sleep(DELAY);

        // Получение количества "Готовых поменять"
        let tradeCount = await getCount(cardId, 'trade');

        await sleep(DELAY);

        // Получение популярности и ранга
        const popularityResponse = await fetch(`${currentDomain}/cards/${cardId}/users/`);
        if (popularityResponse.status === 502) {
            console.error("Ошибка 502: Остановка выполнения скриптов.");
            throw new Error("502 Bad Gateway");
        }

        let likes = 0;
        let dislikes = 0;
        let popularityCount = 0;
        let rankText = '';
        if (popularityResponse.ok) {
            const popularityHtml = await popularityResponse.text();
            const popularityDoc = new DOMParser().parseFromString(popularityHtml, 'text/html');

            const rankElement = popularityDoc.querySelector('.anime-cards__rank');
            if (rankElement) {
                rankText = rankElement.textContent.trim();
            }
            checkGiftCard(popularityDoc); // ищем небесный камень заодно

            // anime url
            const animeUrl = popularityDoc.querySelector('.card-show__placeholder')?.href;
            if (animeUrl) {
                try {
                    const response = await fetch(animeUrl);
                    if (!response.ok) {
                        throw new Error(`Ошибка HTTP: ${response.status}`);
                    }
                    const htmlText = await response.text();
                    const parser = new DOMParser();
                    const doc = parser.parseFromString(htmlText, 'text/html');
                    likes = parseInt(doc.querySelector('[data-likes-id]')?.textContent?.trim(), 10);
                    dislikes = parseInt(doc.querySelector('[data-dislikes-id]')?.textContent?.trim(), 10);
                    checkGiftCard(doc); // ищем небесный камень заодно
                } catch (error) {
                    console.error('Ошибка при загрузке страницы:', error);
                }
            }

            popularityCount = popularityDoc.querySelectorAll('.card-show__owner').length;

            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 >= 35) {
                    popularityCount = (totalPages - 1) * 35;
                    const needResponse = await fetch(`${currentDomain}/cards/${cardId}/users/page/${totalPages}`);
                    if (needResponse.status === 502) {
                        console.error("Ошибка 502: Остановка выполнения скриптов.");
                        throw new Error("502 Bad Gateway");
                    }
                    if (needResponse.ok) {
                        const lastPageDoc = new DOMParser().parseFromString(await needResponse.text(), 'text/html');
                        checkGiftCard(lastPageDoc); // ищем небесный камень заодно
                        popularityCount += lastPageDoc.querySelectorAll('.card-show__owner').length;
                    }
                }
            }
        }

        // Очистка старой информации
        element.querySelector('.link-icon')?.remove();

        // Добавление новой информации
        const icon = document.createElement('div');
        icon.className = 'link-icon';
        icon.style.position = 'absolute';
        icon.style.top = '10px';
        icon.style.right = '10px';
        icon.style.backgroundColor = 'rgba(0, 0, 0, 0.6)';
        icon.style.color = '#05ed5b';
        icon.style.padding = '5px';
        icon.style.borderRadius = '5px';
        icon.style.fontSize = '8px';
        const anime = likes && dislikes ? `<br>аниме: +${likes} / -${dislikes}` : '';
        icon.innerHTML = `Ранг: ${rankText}<br>имеют: ${popularityCount}<br>хотят: ${needCount}<br>не хотят: ${tradeCount}` + anime;

        element.style.position = 'relative';
        element.appendChild(icon);

    } catch (error) {
        console.error(`Ошибка обработки карты ${cardId}:`, error);
        // Остановка выполнения скриптов
        throw error;
    }
}

function removeAllLinkIcons() {
    const linkIcons = document.querySelectorAll('.link-icon');
    linkIcons.forEach(icon => icon.remove());
}

function getCardsOnPage() {
    return Array.from(
        document.querySelectorAll('.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')
    ).filter(card => card.offsetParent !== null);
}

async function processCards() {
    removeMatchingWatchlistItems();
    removeAllLinkIcons();

    const cards = getCardsOnPage();
    let counter = cards.length;

    if (!counter) {
        return;
    }

    let buttonId = 'processCards';
    startAnimation(buttonId);
    updateButtonCounter(buttonId, counter);
    for (const card of cards) {

        if (card.classList.contains('trade__inventory-item--lock')) {
            continue; // Пропускаем эту карту
        }

        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) {
            await updateCardInfo(cardId, card).catch(error => {
                console.error("Остановка из-за критической ошибки:", error.message);
                return;
            });
            card.style.border = '2px solid ' + (card.classList.contains('anime-cards__owned-by-user') ? 'rgb(255 0 0)' : '#217412');
            counter--;
            updateButtonCounter(buttonId, counter);
        } else {
            console.log(cardId, 'cardId not found');
        }

        if (card.classList.contains('lootbox__card')) {
            card.addEventListener('click', removeAllLinkIcons);
        }
    }
    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();
                    console.log(`Удалён блок: ${item}`);
                }
            }
        }
    });

    if (watchlistItems.length) {
        DLEPush?.info('Из списка удалены просмотренные аниме. В списке осталось ' + document.querySelectorAll('.watchlist__item').length + ' записей.');
    }
}

function startAnimation(id) {
    $('#' + id + ' span:first').css('animation', 'rotateIcon 2s linear 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.title = text;
    button.style.position = 'fixed';
    button.style.top = percent + '%';
    button.style.right = '1%';
    button.style.zIndex = '1000';
    button.style.backgroundColor = '#007bff';
    button.style.color = '#fff';
    button.style.border = 'none';
    button.style.borderRadius = '5px';
    button.style.padding = '10px 15px';
    button.style.cursor = 'pointer';
    button.style.boxShadow = '0 2px 5px rgba(0, 0, 0, 0.2)';
    const icon = document.createElement('span');
    icon.className = 'fal fa-' + className;
    icon.style.display = 'inline-block';
    button.appendChild(icon);
    const info = document.createElement('span');
    info.id = id + '_counter';
    info.className = 'guest__notification';
    info.style.display = 'none';
    button.appendChild(info);
    button.addEventListener('click', clickFunction);
    return button;
}

function updateButtonCounter(id, counter) {
    let c = $('#' + id + '_counter');
    c.css('display', counter > 0 ? 'flex' : 'none');
    c.text(counter);
}

function addUpdateButton() {
    if (!document.querySelector('#fetchLinksButton')) {
        document.body.appendChild(getButton('processCards', 'rocket', 37, 'Сравнить карточки', processCards));

        let cards = getCardsOnPage();
        if (cards.length && isMyCardPage()) {
            document.body.appendChild(getButton('readyToCharge', 'circle-check', 50, 'Готов поменять карточки', readyToCharge));
        }
    }
}

function isMyCardPage() {
    const regex = /^\/user\/[^/\s]+\/cards(\/page\/\d+\/)?$/;
    return regex.test(window.location.pathname);
}

async function readyToCharge() {
    DLEPush.info('Отмечаем все карты на странице как: "Готов обменять" кроме тех что на обмене и заблокированных');
    let cards = getCardsOnPage();
    DLEPush.info('Карт на странице: ' + cards.length);

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

    cardCounter = 0;
    for (const card of cards) {
        if (card.classList.contains('trade__inventory-item--lock')) {
            continue;
        }
        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) {
            await readyToChargeCard(cardId);
            counter--;
            card.style.border = '2px solid ' + (card.classList.contains('anime-cards__owned-by-user') ? 'rgb(255 0 0)' : '#217412');
            updateButtonCounter(buttonId, counter);
        }
    }
    DLEPush.info('Отправили на обмен ' + 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) {
            console.error("Ошибка 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++;
            //console.log('Ответ сервера:', data);
        } else {
            console.error('Ошибка запроса:', response.status);
        }
    } catch (error) {
        console.error('Ошибка выполнения POST-запроса:', error);
    }
};

// Анимация вращения в CSS
const style = document.createElement('style');
style.textContent = `
@keyframes rotateIcon {
    0% {
        transform: rotate(0deg);
    }
    100% {
        transform: rotate(360deg);
    }
}
`;
document.head.appendChild(style);

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

function autoRepeatCheck() {
    clearIcons();
    checkGiftCard(document);

    // блочим воспроизведение звуков при получении карты
    Audio.prototype.play = function() {
       return new Promise(() => {}); // Возвращаем 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') {
            DLEPush.info(data.text);
            button.remove();
        }
    } catch (error) {
        console.error('Ошибка при проверке подарочной карты:', error);
    }
}

function startPing() {
    // Получаем значение из глобальной переменной
    const userHash = window.dle_login_hash;

    if (!userHash) {
        console.error("Переменная dle_login_hash не определена.");
        return;
    }

    // Определяем текущий домен
    const currentDomain = window.location.origin;

    // Формируем URL с учетом userHash
    const url = `${currentDomain}/engine/ajax/controller.php?mod=user_count_timer&user_hash=${userHash}`;

    // Выполняем GET-запрос
    fetch(url)
        .then(response => {
        if (!response.ok) {
            throw new Error(`HTTP error! Status: ${response.status}`);
        }
        return response.json(); // Если ответ в формате JSON
    })
        .then(data => {
        // console.log("Данные получены:", data); // Обрабатываем полученные данные
    })
        .catch(error => {
        console.error("Ошибка при выполнении запроса:", error);
    });
}

function checkNewCard() {
    const currentDateTime = new Date();
    // Получаем значение из глобальной переменной
    const userHash = window.dle_login_hash;

    if (!userHash) {
        console.error("Переменная dle_login_hash не определена.");
        return;
    }

    const localStorageKey = 'checkCardStopped' + window.dle_login_hash; // Формат YYYY-MM-DDTHH

    if (localStorage.getItem(localStorageKey) === currentDateTime.toISOString().slice(0, 13)) {
        console.log("Проверка карты уже остановлена на текущий час.");
        return;
    }

    // Определяем текущий домен
    const currentDomain = window.location.origin;

    // Формируем URL с учетом userHash
    const url = `${currentDomain}/engine/ajax/controller.php?mod=reward_card&action=check_reward&user_hash=${userHash}`;

    // Выполняем GET-запрос
    fetch(url)
        .then(response => {
        if (!response.ok) {
            throw new Error(`HTTP error! Status: ${response.status}`);
        }
        return response.json(); // Если ответ в формате JSON
    })
        .then(data => {
        if (data.stop_reward === "yes") {
            console.log("Проверка карт остановлена на текущий час:", data.reason);
            localStorage.setItem(localStorageKey, currentDateTime.toISOString().slice(0, 13));
            return;
        }
        if (!data.cards || !data.cards.owner_id) {
            return;
        }
        const ownerId = data.cards.owner_id;
        console.log("owner_id получен:", ownerId); // Выводим owner_id

        if ( data.cards.name ) {
            DLEPush?.info('Получена новая карта "' + data.cards.name + '"');
        }

        const url = `${currentDomain}/engine/ajax/controller.php?mod=cards_ajax`;

        // Подготавливаем параметры запроса
        const postData = new URLSearchParams({
            action: "take_card",
            owner_id: ownerId
        });

        // Выполняем POST-запрос
        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(); // Если ответ в формате JSON
        })
            .then(data => {
            console.log("Данные получены:", data); // Обрабатываем полученные данные
        })
            .catch(error => {
            console.error("Ошибка при выполнении запроса:", error);
        });
    })
        .catch(error => {
        console.error("Ошибка при выполнении запроса:", error);
    });
}

(function() {
    'use strict';

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

    addUpdateButton();

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