您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Отображения спроса карт и Авто-Лут карточек с просмотра
当前为
// ==UserScript== // @name Card Helper AStars | AnimeStars | ASStars // @namespace animestars.org // @version 7.14 // @description Отображения спроса карт и Авто-Лут карточек с просмотра // @author bmr // @match https://astars.club/* // @match https://asstars.club/* // @match https://asstars1.astars.club/* // @match https://animestars.org/* // @match https://as1.astars.club/* // @match https://asstars.tv/* // @license MIT // @grant none // ==/UserScript== const DELAY = 250; 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'; let currentNotification = { element: null, id: null, type: null, timeoutId: null }; function displayNotification(id, message, type = 'temporary', options = {}) { if (window.location.pathname.includes('/pm/') || window.location.pathname.includes('emotions.php') || window.location.pathname.includes('/messages/')) { return; } const { total, current, isSuccess = true, duration = 3500, sticky = false } = options; if (currentNotification.element && currentNotification.id !== id) { if (currentNotification.timeoutId) clearTimeout(currentNotification.timeoutId); if (currentNotification.element.parentNode) { currentNotification.element.remove(); } currentNotification.element = null; currentNotification.id = null; } let notificationElement = currentNotification.element; if (!notificationElement || currentNotification.id !== id || (currentNotification.type === 'progress' && type !== 'progress')) { if (notificationElement && notificationElement.parentNode) { notificationElement.remove(); } notificationElement = document.createElement('div'); notificationElement.className = 'card-helper-status-notification'; document.body.appendChild(notificationElement); currentNotification.element = notificationElement; currentNotification.id = id; } currentNotification.type = type; let iconHtml = ''; if (type === 'progress') { iconHtml = '<div class="card-helper-spinner"></div>'; if (total !== undefined && current !== undefined) { let countText = total === 'неизвестно' ? `${current}` : `${current}/${total}`; let progressMessageSuffix = `Обработано ${countText}`; message = `${message} ${progressMessageSuffix}`; } } else if (type === 'completion' || type === 'temporary') { const iconClass = isSuccess ? 'card-helper-checkmark' : 'card-helper-crossmark'; const iconChar = isSuccess ? '✔' : '✖'; iconHtml = `<span class="${iconClass}">${iconChar}</span>`; } notificationElement.innerHTML = ` <div class="ch-status-icon-container">${iconHtml}</div> <span class="card-helper-status-text">${message}</span> `; requestAnimationFrame(() => { notificationElement.classList.add('show'); }); if (currentNotification.timeoutId) { clearTimeout(currentNotification.timeoutId); currentNotification.timeoutId = null; } if (!sticky && (type === 'completion' || type === 'temporary')) { currentNotification.timeoutId = setTimeout(() => { hideCurrentNotification(id); }, duration); } } function updateNotificationProgress(id, messagePrefix, current, total) { if (currentNotification.id === id && currentNotification.type === 'progress') { const textElement = currentNotification.element.querySelector('.card-helper-status-text'); let countText = total === 'неизвестно' ? `${current}` : `${current}/${total}`; let progressMessageSuffix = `Обработано ${countText}`; const fullMessage = `${messagePrefix} ${progressMessageSuffix}`; if (textElement && textElement.textContent !== fullMessage) { textElement.textContent = fullMessage; } } else { displayNotification(id, messagePrefix, 'progress', { current, total, sticky: true }); } } function completeProgressNotification(id, message, isSuccess = true, duration = 3500) { displayNotification(id, message, 'completion', { isSuccess, duration }); } function showTemporaryMessage(id, message, isSuccess = true, duration = 3500) { displayNotification(id, message, 'temporary', { isSuccess, duration }); } function hideCurrentNotification(idToHide) { if (currentNotification.element && currentNotification.id === idToHide) { const element = currentNotification.element; element.classList.remove('show'); if (currentNotification.timeoutId) { clearTimeout(currentNotification.timeoutId); currentNotification.timeoutId = null; } setTimeout(() => { if (element.parentNode) { element.remove(); } if (currentNotification.element === element) { currentNotification.element = null; currentNotification.id = null; currentNotification.type = null; } }, 400); } } function getCurrentDomain() { const hostname = window.location.hostname; const protocol = window.location.protocol; return `${protocol}//${hostname}`; } async function loadCard(cardId, maxRetries = 2, initialRetryDelay = 2500) { const cacheKey = 'cardId: ' + cardId; const cachedCard = await getCard(cacheKey); if (cachedCard) { return cachedCard; } const currentDomain = getCurrentDomain(); let popularityCount = 0, needCount = 0, tradeCount = 0; for (let attempt = 1; attempt <= maxRetries + 1; attempt++) { if (attempt > 1) { const retryDelay = initialRetryDelay * Math.pow(1.5, attempt - 2); console.warn(`Карта ${cardId}: Попытка ${attempt}/${maxRetries + 1}. Ждем ${retryDelay / 1000}с перед повтором...`); await sleep(retryDelay); } else { await sleep(DELAY); } try { const mainCardPageResponse = await fetch(`${currentDomain}/cards/${cardId}/users/`); if (mainCardPageResponse.status === 403) { console.error(`Карта ${cardId}: Попытка ${attempt} - Ошибка 403 Forbidden.`); continue; } if (!mainCardPageResponse.ok) { console.error(`Карта ${cardId}: Попытка ${attempt} - Ошибка HTTP ${mainCardPageResponse.status}.`); continue; } const mainCardPageHtml = await mainCardPageResponse.text(); const mainCardPageDoc = new DOMParser().parseFromString(mainCardPageHtml, 'text/html'); const newPopularityCount = parseInt(mainCardPageDoc.querySelector('#owners-count')?.textContent.trim(), 10) || 0; const newNeedCount = parseInt(mainCardPageDoc.querySelector('#owners-need')?.textContent.trim(), 10) || 0; const newTradeCount = parseInt(mainCardPageDoc.querySelector('#owners-trade')?.textContent.trim(), 10) || 0; popularityCount = newPopularityCount; needCount = newNeedCount; tradeCount = newTradeCount; const finalCardData = { popularityCount, needCount, tradeCount }; await cacheCard(cacheKey, finalCardData); return finalCardData; } catch (error) { } } return { popularityCount, needCount, tradeCount }; } async function updateCardInfo(cardId, element) { if (!cardId || !element) return; try { const cardData = await loadCard(cardId); 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> ${cardData.popularityCount}</span> <span title="Хотят получить"><i class="fas fa-heart"></i> ${cardData.needCount}</span> <span title="Готовы обменять"><i class="fas fa-sync-alt"></i> ${cardData.tradeCount}</span> `; element.appendChild(stats); } catch (error) { } } function clearMarkFromCards() { cleanByClass('div-marked'); } function removeAllLinkIcons() { cleanByClass('link-icon'); } function cleanByClass(className) { document.querySelectorAll('.' + className).forEach(item => item.remove()); } function getCardsOnPage() { return Array.from(document.querySelectorAll(cardClasses)).filter(cardEl => cardEl.offsetParent !== null); } async function processCards() { if (isCardRemeltPage()) { const storedData = JSON.parse(localStorage.getItem('animeCardsData')) || {}; if (Object.keys(storedData).length < 1) { await readyRemeltCards(); return; } } removeMatchingWatchlistItems(); removeAllLinkIcons(); clearMarkFromCards(); const cardsOnPage = getCardsOnPage(); let totalCardsToProcess = cardsOnPage.length; if (!totalCardsToProcess) return; const buttonId = 'processCards'; startAnimation(buttonId); displayNotification(buttonId, 'Проверка спроса:', 'progress', { current: 0, total: totalCardsToProcess, sticky: true }); let processedCardCount = 0; for (const cardElement of cardsOnPage) { if (cardElement.classList.contains('trade__inventory-item--lock') || cardElement.classList.contains('remelt__inventory-item--lock')) continue; cardElement.classList.add('processing-card'); let cardId = await getCardId(cardElement); if (cardId) { await updateCardInfo(cardId, cardElement); } processedCardCount++; updateNotificationProgress(buttonId, 'Проверено карт:', processedCardCount, totalCardsToProcess); cardElement.classList.remove('processing-card'); if (cardElement.classList.contains('lootbox__card')) cardElement.addEventListener('click', removeAllLinkIcons); } completeProgressNotification(buttonId, 'Проверка спроса завершена', true); stopAnimation(buttonId); } function removeMatchingWatchlistItems() { const watchlistItems = document.querySelectorAll('.watchlist__item'); if (watchlistItems.length == 0) return; let initialCount = watchlistItems.length; 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(); } } }); let currentCount = document.querySelectorAll('.watchlist__item').length; if (initialCount > currentCount) { showTemporaryMessage('watchlistUpdate', `Из списка удалены просмотренные аниме. Осталось: ${currentCount}`, true); } } function startAnimation(id) { const buttonElement = document.getElementById(id); if (buttonElement) { buttonElement.classList.add('is-working'); buttonElement.style.animationPlayState = 'paused'; const iconElement = buttonElement.querySelector('span[class*="fa-"]'); if (iconElement) { iconElement.style.animation = 'pulseIcon 1s ease-in-out infinite'; } } } function stopAnimation(id) { const buttonElement = document.getElementById(id); if (buttonElement) { buttonElement.classList.remove('is-working'); if (!buttonElement.matches(':hover')) { buttonElement.style.animationPlayState = 'running'; } const iconElement = buttonElement.querySelector('span[class*="fa-"]'); if (iconElement) { iconElement.style.animation = ''; } } } function getButton(id, iconClassFASuffix, percent, tooltipText, clickFunction) { const wrapper = document.createElement('div'); wrapper.style.position = 'fixed'; wrapper.style.top = percent + '%'; wrapper.style.right = '1%'; wrapper.style.zIndex = '1000'; const buttonElement = document.createElement('button'); buttonElement.id = id; buttonElement.classList.add('anim-interactive-button'); const icon = document.createElement('span'); icon.className = 'fal fa-' + iconClassFASuffix; buttonElement.appendChild(icon); let tooltipTimeout; const tooltip = document.createElement('div'); tooltip.className = 'anim-button-tooltip'; tooltip.textContent = tooltipText; buttonElement.addEventListener('mouseenter', () => { tooltip.style.opacity = '1'; tooltip.style.transform = 'translateY(-50%) translateX(0px)'; buttonElement.style.animationPlayState = 'paused'; if (tooltipTimeout) clearTimeout(tooltipTimeout); }); buttonElement.addEventListener('mouseleave', () => { tooltip.style.opacity = '0'; tooltip.style.transform = 'translateY(-50%) translateX(10px)'; if (!buttonElement.classList.contains('is-working')) { buttonElement.style.animationPlayState = 'running'; } }); buttonElement.addEventListener('click', (e) => { e.stopPropagation(); clickFunction(e); if (window.innerWidth <= 768) { tooltip.style.opacity = '1'; tooltip.style.transform = 'translateY(-50%) translateX(0px)'; if (tooltipTimeout) clearTimeout(tooltipTimeout); tooltipTimeout = setTimeout(() => { tooltip.style.opacity = '0'; tooltip.style.transform = 'translateY(-50%) translateX(10px)'; }, 1500); } }); wrapper.appendChild(buttonElement); wrapper.appendChild(tooltip); return wrapper; } function addUpdateButton() { if (window.location.pathname.includes('/pm/') || window.location.pathname.includes('emotions.php') || window.frameElement) return; if (!document.getElementById('processCards')) { document.body.appendChild(getButton('processCards', 'star', 37, 'Узнать спрос', processCards)); } if (isMyCardPage() && !document.getElementById('readyToCharge')) { document.body.appendChild(getButton('readyToCharge', 'handshake', 50, 'Отметить всё как "Готов обменять"', readyToCharge)); } if (isCardRemeltPage() && !document.getElementById('readyRemeltCards')) { document.body.appendChild(getButton('readyRemeltCards', 'yin-yang', 50, 'Кешировать карточки', readyRemeltCards)); } if (!document.getElementById('clearCacheButton')) { document.body.appendChild(getButton('clearCacheButton', 'trash', 63, 'Очистить кеш карт', clearCardCache)); } } function isMyCardPage() { return (/^\/user\/(.*)\/cards(\/page\/\d+\/)?/).test(window.location.pathname); } function isCardRemeltPage() { return (/^\/cards_remelt\//).test(window.location.pathname); } async function readyRemeltCards() { const buttonId = 'readyRemeltCards'; const notificationId = 'remeltCache'; showTemporaryMessage(notificationId, 'Запрос на кеширование всех карт..', true, 2000); const userCardsLinkElement = document.querySelector('a.ncard__tabs-btn[href*="/user/"][href*="/cards/"]'); const relativeHref = userCardsLinkElement ? userCardsLinkElement.href : null; if (!relativeHref) { showTemporaryMessage(notificationId, 'Не найдена ссылка на страницу "Мои карты" для начала кеширования.', false, 5000); return; } const absoluteHref = new URL(relativeHref, window.location.origin).href; removeMatchingWatchlistItems(); removeAllLinkIcons(); clearMarkFromCards(); startAnimation(buttonId); try { await scrapeAllPages(absoluteHref); } catch (e) { showTemporaryMessage(notificationId, 'Произошла ошибка при кешировании.', false, 5000); } finally { stopAnimation(buttonId); } } async function scrapeAllPages(firstPageHref) { const notificationId = 'scrapeAllPages'; try { const response = await fetch(firstPageHref); if (!response.ok) throw new Error(`Ошибка HTTP: ${response.status}`); const firstPageHtml = await response.text(); const firstPageDoc = new DOMParser().parseFromString(firstPageHtml, 'text/html'); const pagination = firstPageDoc.querySelector('#pagination'); let storedData = JSON.parse(localStorage.getItem('animeCardsData')) || {}; let totalCardsInProfile = -1; const countCurrentlyCachedCards = () => Object.values(storedData).reduce((sum, userCards) => sum + (Array.isArray(userCards) ? userCards.length : 0), 0); let processedCardsCount = 0; const titleElement = firstPageDoc.querySelector('h1.ncard__main-title.ncard__main-title-2.as-center.bolder'); if (titleElement) { const match = titleElement.textContent.match(/\((\d+)\s*шт\.\)/); if (match) totalCardsInProfile = parseInt(match[1], 10); } if (totalCardsInProfile !== -1) { let currentTotalCached = countCurrentlyCachedCards(); if (totalCardsInProfile === currentTotalCached && currentTotalCached > 0) { showTemporaryMessage(notificationId, 'Кеш карточек уже актуален.', true); await processCards(); return; } } displayNotification(notificationId, 'Кеширование страниц:', 'progress', { current: countCurrentlyCachedCards(), total: totalCardsInProfile > 0 ? totalCardsInProfile : 'неизвестно', sticky: true }); async function processBatchInStorage(doc) { const cardsElements = doc.querySelectorAll('.anime-cards__item'); let newCardsThisBatch = 0; for (let i = 0; i < cardsElements.length; i += 10) { const cardGroup = Array.from(cardsElements).slice(i, i + 10); for (const cardEl of cardGroup) { const cardId = cardEl.getAttribute('data-id'), ownerId = cardEl.getAttribute('data-owner-id'); if (!ownerId || !cardId) continue; const ownerKey = 'o_' + ownerId; if (!storedData[ownerKey]) storedData[ownerKey] = []; if (!storedData[ownerKey].find(c => c.cardId === cardId && c.ownerId === ownerId)) { storedData[ownerKey].push({ cardId, name: cardEl.getAttribute('data-name'), rank: cardEl.getAttribute('data-rank'), animeLink: cardEl.getAttribute('data-anime-link'), image: cardEl.getAttribute('data-image'), ownerId }); newCardsThisBatch++; } } await sleep(10); } if (newCardsThisBatch > 0) processedCardsCount += newCardsThisBatch; updateNotificationProgress(notificationId, 'Кешировано карт:', countCurrentlyCachedCards(), totalCardsInProfile > 0 ? totalCardsInProfile : 'неизвестно'); } await processBatchInStorage(firstPageDoc); if (pagination) { const pageLinks = Array.from(pagination.querySelectorAll('a[href*="page/"]')); let lastPageNumber = 1; if (pageLinks.length > 0) { const numbers = pageLinks.map(a => (a.getAttribute('href')?.match(/page\/(\d+)/) || [])[1]).map(n => parseInt(n, 10)).filter(n => n > 0); if (numbers.length > 0) lastPageNumber = Math.max(...numbers); else { const lastLinkText = pagination.querySelector('a:last-of-type:not(.pagination__item--next)'); if(lastLinkText) lastPageNumber = parseInt(lastLinkText.textContent.trim(), 10) || 1; } } if (lastPageNumber > 1) { const parser = new DOMParser(); let basePageUrl = firstPageHref.replace(/\/page\/\d+(\/)?$/, '').replace(/\/$/, ''); for (let i = 2; i <= lastPageNumber; i++) { const pageUrl = `${basePageUrl}/page/${i}/`; const pageHTML = await (async (url) => { try { const r = await fetch(url); return r.ok ? await r.text() : null; } catch (e) { console.error(`Workspace error ${url}:`, e); return null; } })(pageUrl); if (pageHTML) await processBatchInStorage(parser.parseFromString(pageHTML, 'text/html')); await sleep(1000 + Math.random() * 1500); if (i % 5 === 0) localStorage.setItem('animeCardsData', JSON.stringify(storedData)); } } } localStorage.setItem('animeCardsData', JSON.stringify(storedData)); completeProgressNotification(notificationId, 'Кеширование завершено. Всего в кеше: ' + countCurrentlyCachedCards(), true); await processCards(); } catch (error) { completeProgressNotification(notificationId, 'Ошибка кеширования страниц.', false); } } async function getCardId(cardElement) { let cardId = cardElement.getAttribute('card-id') || cardElement.getAttribute('data-card-id') || cardElement.getAttribute('data-id'); const href = cardElement.getAttribute('href'); if (href) { const cardIdMatch = href.match(/\/cards\/(\d+)\/users\//); if (cardIdMatch) cardId = cardIdMatch[1]; } if (cardId) { const cardByOwner = await getFirstCardByOwner(cardId); if (cardByOwner?.cardId) cardId = cardByOwner.cardId; } return cardId; } async function getFirstCardByOwner(ownerId) { const storedData = JSON.parse(localStorage.getItem('animeCardsData')) || {}; const key = 'o_' + ownerId; return storedData[key]?.[0] || null; } async function readyToCharge() { const buttonId = 'readyToCharge'; displayNotification(buttonId, 'Подготовка к отметке карт...', 'progress', {sticky: true}); let cardsOnPage = getCardsOnPage(); if (!cardsOnPage || cardsOnPage.length === 0) { completeProgressNotification(buttonId, 'Карты на странице не найдены.', false); return; } const cardsToProcess = cardsOnPage.filter(cardEl => !cardEl.classList.contains('trade__inventory-item--lock')); const totalCardsToProcess = cardsToProcess.length; if (totalCardsToProcess === 0) { completeProgressNotification(buttonId, 'Нет карт для отметки.', false); return; } updateNotificationProgress(buttonId, 'Отмечаем карт:', 0, totalCardsToProcess); startAnimation(buttonId); clearMarkFromCards(); cardCounter = 0; let successfullyProcessedCount = 0, attemptedToProcessCount = 0; for (const cardElement of cardsToProcess) { cardElement.classList.add('charging-card'); let idToSend = cardElement.getAttribute('data-owner-id') || await getCardId(cardElement); attemptedToProcessCount++; if (idToSend) { await sleep(1000 + Math.random() * 500); try { if (await readyToChargeCard(idToSend)) successfullyProcessedCount++; } catch (error) { console.error("Ошибка при отметке карты " + idToSend + ":", error); } } updateNotificationProgress(buttonId, 'Обработано карт:', attemptedToProcessCount, totalCardsToProcess); cardElement.classList.remove('charging-card'); } completeProgressNotification(buttonId, `Отправлено на обмен ${cardCounter} из ${successfullyProcessedCount} (${attemptedToProcessCount} попыток).`, true, 5000); stopAnimation(buttonId); } async function readyToChargeCard(card_id_to_send) { try { await sleep(DELAY * 2 + Math.random() * DELAY); const data = await $.ajax({ url: "/engine/ajax/controller.php?mod=trade_ajax", type: "post", data: { action: "propose_add", type: 1, card_id: card_id_to_send, user_hash: dle_login_hash }, dataType: "json", cache: false }); if (data?.error) { if (data.error === 'Слишком часто, подождите пару секунд и повторите действие') { await sleep(2500 + Math.random() * 1000); return await readyToChargeCard(card_id_to_send); } console.warn(`Ошибка от сервера (карта ${card_id_to_send}): ${data.error}`); return false; } if (data?.status == "added") { cardCounter++; return true; } if (data?.status == "deleted") { await sleep(1000); return await readyToChargeCard(card_id_to_send); } console.warn(`Неожиданный ответ от сервера для карты ${card_id_to_send}:`, data); return false; } catch (e) { console.error(`readyToChargeCard AJAX/исключение (ID ${card_id_to_send}):`, e.statusText || e.message || 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 glowChargeEffect {0% { box-shadow: 0 0 7px #4CAF50; } 50% { box-shadow: 0 0 25px #4CAF50; } 100% { box-shadow: 0 0 7px #4CAF50; }} @keyframes fadeInUp {from {opacity: 0; transform: translateY(10px);} to {opacity: 1; transform: translateY(0);}} @keyframes breatheShadowInteractive { 0% { box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15); transform: scale(1); } 50% { box-shadow: 0 5px 15px rgba(108, 92, 231, 0.4); transform: scale(1.02); } 100% { box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15); transform: scale(1); } } @keyframes pulseWorkingBorderInteractive { 0% { box-shadow: 0 0 0 0px rgba(86, 200, 239, 0.7), 0 3px 8px rgba(0,0,0,0.25); } 70% { box-shadow: 0 0 0 10px rgba(86, 200, 239, 0), 0 5px 12px rgba(0,0,0,0.3); } 100% { box-shadow: 0 0 0 0px rgba(86, 200, 239, 0), 0 3px 8px rgba(0,0,0,0.25); } } @keyframes pulseIcon { 0% { transform: scale(1) rotate(0deg); opacity: 1; } 50% { transform: scale(1.2) rotate(0deg); opacity: 0.7; } 100% { transform: scale(1) rotate(0deg); opacity: 1; } } @keyframes cardHelperSpin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .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; } .charging-card {position: relative;} .charging-card img {position: relative; z-index: 2;} .charging-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: glowChargeEffect 1.5s infinite; pointer-events: none; } .card-stats {position: relative; background: linear-gradient(45deg, #6c5ce7, #a367dc); padding: 8px; color: white; font-size: 12px; margin-top: 5px; border-radius: 5px; display: flex; justify-content: space-between; align-items: center; text-shadow: 1px 1px 2px rgba(0,0,0,0.3); animation: fadeInUp 0.3s ease; z-index: 0 !important;} .history__inner {max-width: 1200px !important; margin: 0 auto !important; padding: 15px !important;} .history__item {background: rgba(108, 92, 231, 0.05) !important; border-radius: 10px !important; padding: 20px !important; margin-bottom: 20px !important;} .history__body {display: flex !important; flex-wrap: wrap !important; gap: 15px !important; padding: 15px !important; border-radius: 8px !important;} .history__body--gained {background: rgba(46, 213, 115, 0.1) !important; margin-bottom: 10px !important;} .history__body--lost {background: rgba(255, 71, 87, 0.1) !important;} @media screen and (min-width: 769px) {.history__body-item {width: 120px !important; height: auto !important; transition: transform 0.2s !important;} .history__body-item img {width: 120px !important; height: auto !important; border-radius: 8px !important; box-shadow: 0 2px 8px rgba(0,0,0,0.1) !important;}} .history__body-item:hover {transform: scale(1.05) !important; z-index: 2 !important;} .card-stats span {display: flex; align-items: center; gap: 4px;} .card-stats span i {font-size: 14px;} .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) { .history__body-item, .history__body-item img {width: 100px !important;} .processing-card::before, .charging-card::before {top: -1px !important; left: -1px !important; right: -1px !important; bottom: -1px !important; opacity: 0.5 !important;} div[style*="position: fixed"][style*="right: 1%"] { transform: scale(0.9); transform-origin: bottom right; } .anim-interactive-button { width: 40px !important; height: 40px !important; } .anim-interactive-button span[class*="fa-"] { font-size: 18px !important; } .anim-button-tooltip { font-size: 11px !important; padding: 5px 8px !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.8) !important; margin-top: -20px !important; margin-bottom: 30px !important;} } .anim-interactive-button { background-color: #6c5ce7; color: #fff; border: none; border-radius: 50%; width: 45px; height: 45px; padding: 0; cursor: pointer; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); transition: all 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275); display: flex; justify-content: center; align-items: center; animation: breatheShadowInteractive 2.5s infinite ease-in-out; outline: none; position: relative; } .anim-interactive-button span[class*="fa-"] { display: inline-block; font-size: 20px; transition: transform 0.25s ease-out; } .anim-interactive-button:hover { background-color: #5f51e3; transform: scale(1.12) translateY(-3px); box-shadow: 0 7px 18px rgba(0, 0, 0, 0.25); } .anim-interactive-button:hover span[class*="fa-"] { transform: rotate(18deg); } .anim-interactive-button:active { background-color: #5245c9; transform: scale(0.93) translateY(0px); box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); transition-duration: 0.08s; } .anim-interactive-button:active span[class*="fa-"] { transform: rotate(-8deg) scale(0.88); } .anim-interactive-button.is-working { animation: pulseWorkingBorderInteractive 1s infinite ease-in-out, breatheShadowInteractive 2.5s infinite ease-in-out paused !important; } .anim-interactive-button.is-working:hover { transform: scale(1.05) translateY(-1px); } .anim-button-tooltip { position: absolute; right: calc(100% + 10px); top: 50%; transform: translateY(-50%) translateX(10px); background-color: #2d3436; color: #fff; padding: 8px 12px; border-radius: 4px; font-size: 14px; opacity: 0; transition: opacity 0.25s ease, transform 0.25s ease; white-space: nowrap; z-index: 1001; pointer-events: none; } .card-helper-status-notification { position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); background-color: #3e444c; color: #f0f0f0; padding: 10px 18px; border-radius: 6px; font-size: 14px; font-family: Arial, sans-serif; z-index: 2147483647; display: flex; align-items: center; box-shadow: 0 2px 6px rgba(0,0,0,0.25); opacity: 0; transition: opacity 0.4s ease, bottom 0.4s ease; max-width: 380px; min-width: 280px; box-sizing: border-box; } .card-helper-status-notification.show { opacity: 1; bottom: 30px; } .ch-status-icon-container { margin-right: 10px; display: flex; align-items: center; height: 18px; } .card-helper-spinner { width: 16px; height: 16px; border: 2px solid #666; border-top: 2px solid #ddd; border-radius: 50%; animation: cardHelperSpin 0.8s linear infinite; } .card-helper-checkmark, .card-helper-crossmark { font-size: 18px; line-height: 1; } .card-helper-checkmark { color: #76c779; } .card-helper-crossmark { color: #e57373; } .card-helper-status-text { white-space: normal; text-align: left; line-height: 1.3; } `; document.head.appendChild(style); function clearIcons() { $('.card-notification:first')?.click(); } function autoRepeatCheck() { clearIcons(); checkGiftCard(document); const volumeButton = document.querySelector('.adv_volume.volume_on'); if (volumeButton) { volumeButton.click(); } } async function checkGiftCard(doc) { const button = doc.querySelector('#gift-icon'); if (!button) return; const giftCode = button.getAttribute('data-code'); if (!giftCode) return; 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') { showTemporaryMessage('giftStatus', data.text, true); button.remove(); } else if (data.text) { showTemporaryMessage('giftStatus', data.text, false); } } catch (error) { console.error("Error checking gift card:", error); showTemporaryMessage('giftError', "Ошибка проверки гифт карты.", false); } } async function checkNewCard() { const userHash = window.dle_login_hash; if (!userHash) { setTimeout(() => { if (window.dle_login_hash) { checkNewCard(); } }, 2000); return; } const currentDateTime = new Date(); const localStorageKey = 'checkCardStopped' + userHash; const currentHourMarker = currentDateTime.toISOString().slice(0, 13); if (localStorage.getItem(localStorageKey) === currentHourMarker) { return; } try { await sleep(DELAY * 2); const cardForWatchPayload = { user_hash: userHash }; const responseText = await $.ajax({ url: "/ajax/card_for_watch/", type: "post", data: cardForWatchPayload, cache: false }); if (typeof responseText === 'string') { let jsonData; if (responseText.startsWith("cards{") && responseText.endsWith("}")) { try { const jsonString = responseText.substring(5); jsonData = JSON.parse(jsonString); } catch (e) { } } else { } if (jsonData && jsonData.if_reward && jsonData.if_reward.toLowerCase() === "yes") { if (jsonData.reward_limit !== undefined && parseInt(jsonData.reward_limit, 10) === 0) { localStorage.setItem(localStorageKey, currentHourMarker); } } } } catch (e) { let errorMsg = "Ошибка автосбора: "; if (e.status !== undefined) errorMsg += `HTTP ${e.status} `; if (e.statusText) errorMsg += `${e.statusText} `; } } async function setCache(key, data, baseTtlInSeconds = 86400) { const jitterPercent = 0.10; const jitter = Math.round(baseTtlInSeconds * jitterPercent * (Math.random() * 2 - 1)); const finalTtlInSeconds = baseTtlInSeconds + jitter; const expires = Date.now() + finalTtlInSeconds * 1000; const cacheData = { data, expires }; try { localStorage.setItem(key, JSON.stringify(cacheData)); } catch (e) { console.error("Ошибка при записи в localStorage (возможно, переполнен):", e); showTemporaryMessage('localStorageError', 'Ошибка записи в localStorage.', false); } } async function getCache(key) { const cacheDataJSON = localStorage.getItem(key); if (!cacheDataJSON) return null; try { const cacheData = JSON.parse(cacheDataJSON); if (!cacheData || typeof cacheData !== 'object' || !cacheData.expires || !('data' in cacheData) || Date.now() > cacheData.expires) { localStorage.removeItem(key); return null; } return cacheData.data; } catch (e) { localStorage.removeItem(key); return null; } } async function cacheCard(key, data) { await setCache(key, data); } async function getCard(key) { return await getCache(key); } function clearCardCache() { let clearedCount = 0, animeCardsDataCleared = false; Object.keys(localStorage).forEach(key => { if (key.startsWith('cardId: ')) { try { const parsed = JSON.parse(localStorage.getItem(key)); if (parsed?.data && parsed.expires) { localStorage.removeItem(key); clearedCount++; } } catch (e) {} } else if (key === 'animeCardsData') { localStorage.removeItem(key); animeCardsDataCleared = true; } }); showTemporaryMessage('cacheCleared', `Очищено ${clearedCount} карт. ${animeCardsDataCleared ? "Общий кеш очищен." : ""}`, true); } (function() { 'use strict'; function initializeScript() { if (typeof $ === 'undefined') { console.error("jQuery не найден."); showTemporaryMessage('jQueryError', 'jQuery не найден!', false, 10000); } if (typeof dle_login_hash === 'undefined') console.warn("dle_login_hash не определена."); addUpdateButton(); setInterval(autoRepeatCheck, 2000); setInterval(checkNewCard, 15000); $('#tg-banner')?.remove(); try { localStorage.setItem('notify18', 'closed'); localStorage.setItem('hideTelegramAs', 'true'); } catch (e) {} } if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', initializeScript); else initializeScript(); })();