- // ==UserScript==
- // @name Card Helper AStars | AnimeStars | ASStars
- // @namespace animestars.org
- // @version 6.7
- // @description card helper
- // @author bmr
- // @match https://astars.club/*
- // @match https://asstars1.astars.club/*
- // @match https://animestars.org/*
- // @match https://as1.astars.club/*
- // @match https://asstars.tv/*
- // @license MIT
- // @grant none
- // ==/UserScript==
-
- const DELAY = 40;
-
- const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
-
- let cardCounter = 0;
-
- const cardClasses = '.remelt__inventory-item, .lootbox__card, .anime-cards__item, .trade__inventory-item, .trade__main-item, .card-filter-list__card, .deck__item, .history__body-item, .history__body-item, .card-pack__card';
-
- function getCurrentDomain() {
- const hostname = window.location.hostname;
- const protocol = window.location.protocol;
- return `${protocol}//${hostname}`;
- }
-
- async function getCount(cardId, type) {
- const currentDomain = getCurrentDomain();
- let count = 0;
- let needResponse = await fetch(`${currentDomain}/cards/${cardId}/users/${type}/`);
- if (needResponse.status === 502) {
- throw new Error("502 Bad Gateway");
- }
- let needHtml = '';
- let needDoc = '';
- if (needResponse.ok) {
- needHtml = await needResponse.text();
- needDoc = new DOMParser().parseFromString(needHtml, 'text/html');
- count = needDoc.querySelectorAll('.profile__friends-item').length;
- } else {
- return count;
- }
-
- const pagination = needDoc.querySelector('.pagination__pages');
- if (pagination && count >= 50) {
- const lastPageNum = pagination.querySelector('a:last-of-type');
- const totalPages = lastPageNum ? parseInt(lastPageNum.innerText, 10) : 1;
- if (totalPages > 1) {
- count = (totalPages - 1) * 50;
- }
- needResponse = await fetch(`${currentDomain}/cards/${cardId}/users/${type}/page/${totalPages}`);
- if (needResponse.status === 502) {
- throw new Error("502 Bad Gateway");
- }
- if (needResponse.ok) {
- needHtml = await needResponse.text();
- needDoc = new DOMParser().parseFromString(needHtml, 'text/html');
- count += needDoc.querySelectorAll('.profile__friends-item').length;
- }
- }
-
- return count;
- }
-
- async function loadCard(cardId) {
- const cacheKey = 'cardId: ' + cardId;
- let card = await getCard(cacheKey) ?? {};
-
- if (Object.keys(card).length) {
- return card;
- }
-
- const currentDomain = getCurrentDomain();
- await sleep(DELAY);
- let needCount = await getCount(cardId, 'need');
- await sleep(DELAY);
- let tradeCount = await getCount(cardId, 'trade');
- await sleep(DELAY);
-
- let popularityCount = 0;
- const popularityResponse = await fetch(`${currentDomain}/cards/${cardId}/users/`);
-
- if (popularityResponse.ok) {
- const popularityHtml = await popularityResponse.text();
- const popularityDoc = new DOMParser().parseFromString(popularityHtml, 'text/html');
-
- const pagination = popularityDoc.querySelector('.pagination__pages');
- if (pagination) {
- const lastPageNum = pagination.querySelector('a:last-of-type');
- const totalPages = lastPageNum ? parseInt(lastPageNum.innerText, 10) : 1;
-
- if (totalPages > 1) {
- popularityCount = (totalPages - 1) * 40;
- await sleep(DELAY);
- const lastPageResponse = await fetch(`${currentDomain}/cards/${cardId}/users/page/${totalPages}`);
- if (lastPageResponse.ok) {
- const lastPageHtml = await lastPageResponse.text();
- const lastPageDoc = new DOMParser().parseFromString(lastPageHtml, 'text/html');
- const lastPageCount = lastPageDoc.querySelectorAll('.card-show__owner').length;
- popularityCount += lastPageCount;
- }
- } else {
- popularityCount = popularityDoc.querySelectorAll('.card-show__owner').length;
- }
- } else {
- popularityCount = popularityDoc.querySelectorAll('.card-show__owner').length;
- }
- }
-
- card = {popularityCount, needCount, tradeCount};
- await cacheCard(cacheKey, card);
- return card;
- }
-
- async function updateCardInfo(cardId, element) {
- if (!cardId || !element) {
- return;
- }
- try {
- const card = await loadCard(cardId);
-
- let statsContainer = document.querySelector('.card-stats-container');
- if (!statsContainer) {
- statsContainer = document.createElement('div');
- statsContainer.className = 'card-stats-container';
- document.body.appendChild(statsContainer);
- }
-
- const oldStats = element.querySelector('.card-stats');
- if (oldStats) {
- oldStats.remove();
- }
-
- const stats = document.createElement('div');
- stats.className = 'card-stats';
-
- stats.innerHTML = `
- <span title="Владельцев">
- <i class="fas fa-users"></i>
- ${card.popularityCount}
- </span>
- <span title="Хотят получить">
- <i class="fas fa-heart"></i>
- ${card.needCount}
- </span>
- <span title="Готовы обменять">
- <i class="fas fa-sync-alt"></i>
- ${card.tradeCount}
- </span>
- `;
-
- element.appendChild(stats);
-
- } catch (error) {
- throw error;
- }
- }
-
- function clearMarkFromCards() {
- cleanByClass('div-marked');
- }
-
- function removeAllLinkIcons() {
- cleanByClass('link-icon');
- }
-
- function cleanByClass(className) {
- const list = document.querySelectorAll('.' + className);
- list.forEach(item => item.remove());
- }
-
- function getCardsOnPage() {
- return Array.from(
- document.querySelectorAll(cardClasses)
- ).filter(card => card.offsetParent !== null);
- }
-
- async function processCards() {
- if (isCardRemeltPage()) {
- const storedData = JSON.parse(localStorage.getItem('animeCardsData')) || {};
- if (Object.keys(storedData).length < 1) {
- await readyRemeltCards();
- return;
- }
- }
-
- removeMatchingWatchlistItems();
- removeAllLinkIcons();
- clearMarkFromCards();
-
- const cards = getCardsOnPage();
- let counter = cards.length;
-
- if (!counter) {
- return;
- }
-
- let buttonId = 'processCards';
- startAnimation(buttonId);
- updateButtonCounter(buttonId, counter);
-
- showNotification('Проверяю спрос на ' + counter + ' карточек');
-
- for (const card of cards) {
- if (card.classList.contains('trade__inventory-item--lock') || card.classList.contains('remelt__inventory-item--lock')) {
- continue;
- }
- card.classList.add('processing-card');
- let cardId = await getCardId(card);
- if (cardId) {
- await updateCardInfo(cardId, card).catch(error => {
- return;
- });
- counter--;
- updateButtonCounter(buttonId, counter);
- }
- card.classList.remove('processing-card');
-
- if (card.classList.contains('lootbox__card')) {
- card.addEventListener('click', removeAllLinkIcons);
- }
- }
-
- showNotification('Проверка спроса завершена');
- stopAnimation(buttonId);
- }
-
- function removeMatchingWatchlistItems() {
- const watchlistItems = document.querySelectorAll('.watchlist__item');
- if (watchlistItems.length == 0) {
- return;
- }
- watchlistItems.forEach(item => {
- const episodesText = item.querySelector('.watchlist__episodes')?.textContent.trim();
- if (episodesText) {
- const matches = episodesText.match(/[\d]+/g);
- if (matches) {
- const currentEpisode = parseInt(matches[0], 10);
- const totalEpisodes = parseInt(matches.length === 4 ? matches[3] : matches[1], 10);
- if (currentEpisode === totalEpisodes) {
- item.remove();
- }
- }
- }
- });
-
- if (watchlistItems.length) {
- showNotification('Из списка удалены просмотренные аниме. В списке осталось ' + document.querySelectorAll('.watchlist__item').length + ' записей.');
- }
- }
-
- function startAnimation(id) {
- $('#' + id + ' span:first').css('animation', 'pulseIcon 1s ease-in-out infinite');
- }
-
- function stopAnimation(id) {
- $('#' + id + ' span:first').css('animation', '');
- }
-
- function getButton(id, className, percent, text, clickFunction) {
- const button = document.createElement('button');
- button.id = id;
- button.style.position = 'fixed';
- button.style.top = percent + '%';
- button.style.right = '1%';
- button.style.zIndex = '1000';
- button.style.backgroundColor = '#6c5ce7';
- button.style.color = '#fff';
- button.style.border = 'none';
- button.style.borderRadius = '50%';
- button.style.width = '45px';
- button.style.height = '45px';
- button.style.padding = '0';
- button.style.cursor = 'pointer';
- button.style.boxShadow = '0 2px 5px rgba(0, 0, 0, 0.2)';
- button.style.transition = 'all 0.3s ease';
-
- let tooltipTimeout;
-
- button.onmouseover = function() {
- this.style.backgroundColor = '#5f51e3';
- tooltip.style.opacity = '1';
- tooltip.style.transform = 'translateX(0)';
- if (tooltipTimeout) {
- clearTimeout(tooltipTimeout);
- }
- };
- button.onmouseout = function() {
- this.style.backgroundColor = '#6c5ce7';
- tooltip.style.opacity = '0';
- tooltip.style.transform = 'translateX(10px)';
- };
-
- const icon = document.createElement('span');
- icon.className = 'fal fa-' + className;
- icon.style.display = 'inline-block';
- icon.style.fontSize = '20px';
- button.appendChild(icon);
-
- const tooltip = document.createElement('div');
- tooltip.style.cssText = `
- position: fixed;
- right: calc(1% + 55px);
- background-color: #2d3436;
- color: #fff;
- padding: 8px 12px;
- border-radius: 4px;
- font-size: 14px;
- opacity: 0;
- transition: all 0.3s ease;
- white-space: nowrap;
- top: ${percent}%;
- transform: translateX(10px);
- z-index: 999;
- pointer-events: none;
- `;
-
- switch(id) {
- case 'processCards':
- tooltip.textContent = 'Узнать спрос';
- break;
- case 'readyToCharge':
- tooltip.textContent = 'Отметить всё как "Готов обменять"';
- break;
- case 'readyRemeltCards':
- tooltip.textContent = 'Кешировать карточки';
- break;
- default:
- tooltip.textContent = text;
- }
-
- button.addEventListener('click', function(e) {
- e.stopPropagation();
- clickFunction(e);
-
- if (window.innerWidth <= 768) {
- tooltip.style.opacity = '1';
- tooltip.style.transform = 'translateX(0)';
-
- if (tooltipTimeout) {
- clearTimeout(tooltipTimeout);
- }
- tooltipTimeout = setTimeout(() => {
- tooltip.style.opacity = '0';
- tooltip.style.transform = 'translateX(10px)';
- }, 1000);
- }
- });
-
- const container = document.createElement('div');
- container.appendChild(tooltip);
- container.appendChild(button);
-
- button.classList.add('action-button');
-
- return container;
- }
-
- function updateButtonCounter(id, counter) {
- return;
- }
-
- function addUpdateButton() {
- if (window.location.pathname.includes('/pm/') ||
- window.location.pathname.includes('emotions.php') ||
- window.frameElement) {
- return;
- }
-
- if (!document.querySelector('#fetchLinksButton')) {
- let cards = getCardsOnPage();
-
- document.body.appendChild(getButton('processCards', 'star', 37, 'Сравнить карточки', processCards));
-
- if (!cards.length) {
- return
- }
-
- if (isMyCardPage()) {
- document.body.appendChild(getButton('readyToCharge', 'handshake', 50, '"Готов поменять" на все карточки', readyToCharge));
- }
-
- if (isCardRemeltPage()) {
- document.body.appendChild(getButton('readyRemeltCards', 'yin-yang', 50, 'закешировать карточки', readyRemeltCards));
- const storedData = JSON.parse(localStorage.getItem('animeCardsData')) || {};
- updateButtonCounter('readyRemeltCards', Object.keys(storedData).length);
- }
- }
- }
-
- function isMyCardPage() {
- return (/^\/user\/(.*)\/cards(\/page\/\d+\/)?/).test(window.location.pathname)
- }
-
- function isCardRemeltPage() {
- return (/^\/cards_remelt\//).test(window.location.pathname)
- }
-
- async function readyRemeltCards() {
- showNotification('Кеширую все карты так как иначе на этой странице не получится их определить рейтинги');
- const linkElement = document.querySelector('a.button.button--left-icon.mr-3');
- const href = linkElement ? linkElement.href : null;
- if (!href) {
- return;
- }
- removeMatchingWatchlistItems();
- removeAllLinkIcons();
- clearMarkFromCards();
- const cards = getCardsOnPage();
- let counter = cards.length;
- if (!counter) {
- return;
- }
- let buttonId = 'readyRemeltCards';
- startAnimation(buttonId);
- await scrapeAllPages(href, buttonId);
- stopAnimation(buttonId);
- }
-
- async function scrapeAllPages(firstPageHref, buttonId) {
- const response = await fetch(firstPageHref);
- if (!response.ok) {
- throw new Error(`Ошибка HTTP: ${response.status}`);
- }
- const firstPageDoc = new DOMParser().parseFromString(await response.text(), 'text/html');
- const pagination = firstPageDoc.querySelector('#pagination');
- if (!pagination) {
- return;
- }
-
- const progressBar = createProgressBar();
- let totalCards = 0;
- let processedCards = 0;
-
- let storedData = JSON.parse(localStorage.getItem('animeCardsData')) || {};
- const titleElement = firstPageDoc.querySelector('h1.secondary-title.text-center');
- if (titleElement) {
- const match = titleElement.textContent.match(/\((\d+)\s*шт\.\)/);
- totalCards = match ? parseInt(match[1], 10) : -1;
- if (totalCards == Object.keys(storedData).length) {
- showNotification('На данный момент в кеше карточек ровно столько же сколько в профиле пользователя');
- return;
- }
- }
-
- async function processCardsToLocalstorage(doc) {
- const cards = doc.querySelectorAll('.anime-cards__item');
- for (let i = 0; i < cards.length; i += 5) {
- const cardGroup = Array.from(cards).slice(i, i + 5);
- for (const card of cardGroup) {
- const cardId = card.getAttribute('data-id');
- const ownerId = card.getAttribute('data-owner-id');
- const name = card.getAttribute('data-name');
- const rank = card.getAttribute('data-rank');
- const animeLink = card.getAttribute('data-anime-link');
- const image = card.getAttribute('data-image');
- const ownerKey = 'o_' + ownerId;
- if (!ownerId || !cardId) continue;
- if (!storedData[ownerKey]) {
- storedData[ownerKey] = [];
- }
- storedData[ownerKey].push({ cardId, name, rank, animeLink, image, ownerId });
- processedCards++;
- if (totalCards > 0) {
- progressBar.update(processedCards, totalCards);
- }
- }
- await sleep(10);
- }
- }
-
- async function fetchPage(url) {
- try {
- const response = await fetch(url);
- if (!response.ok) throw new Error(`Ошибка загрузки страницы ${url}`);
- return await response.text();
- } catch (error) {
- return null;
- }
- }
-
- await processCardsToLocalstorage(firstPageDoc);
-
- const lastPageLink = pagination.querySelector('a:last-of-type');
- if (lastPageLink) {
- const lastPageNumber = parseInt(lastPageLink.textContent.trim(), 10);
- if (!isNaN(lastPageNumber) && lastPageNumber > 1) {
- const parser = new DOMParser();
- for (let i = 2; i <= lastPageNumber; i++) {
- const pageUrl = lastPageLink.href.replace(/page\/\d+/, `page/${i}`);
- const pageHTML = await fetchPage(pageUrl);
- if (pageHTML) {
- await processCardsToLocalstorage(parser.parseFromString(pageHTML, 'text/html'));
- }
- await sleep(1000);
-
- if (i % 3 === 0) {
- localStorage.setItem('animeCardsData', JSON.stringify(storedData));
- }
- }
- }
- }
-
- localStorage.setItem('animeCardsData', JSON.stringify(storedData));
-
- setTimeout(() => {
- progressBar.remove();
- }, 1000);
-
- document.body.appendChild(getButton('processCards', 'star', 37, 'Сравнить карточки', processCards));
- await processCards();
- }
-
- async function getCardId(card) {
- let cardId = card.getAttribute('card-id') || card.getAttribute('data-card-id') || card.getAttribute('data-id');
- const href = card.getAttribute('href');
- if (href) {
- let cardIdMatch = href.match(/\/cards\/(\d+)\/users\//);
- if (cardIdMatch) {
- cardId = cardIdMatch[1];
- }
- }
- if (cardId) {
- const cardByOwner = await getFirstCardByOwner(cardId);
- if (cardByOwner) {
- cardId = cardByOwner.cardId;
- }
- }
- return cardId;
- }
-
- async function getFirstCardByOwner(ownerId) {
- const storedData = JSON.parse(localStorage.getItem('animeCardsData')) || {};
- const key = 'o_' + ownerId;
- return storedData[key] && storedData[key].length > 0 ? storedData[key][0] : null;
- }
-
- async function readyToCharge() {
- showNotification('Отмечаем все карты на странице как: "Готов обменять" кроме тех что на обмене и заблокированных');
- let cards = getCardsOnPage();
-
- let counter = cards.length;
- let buttonId = 'readyToCharge';
- startAnimation(buttonId);
- updateButtonCounter(buttonId, counter);
- clearMarkFromCards();
-
- const progressBar = createProgressBar();
- cardCounter = 0;
-
- const totalCards = cards.filter(card => !card.classList.contains('trade__inventory-item--lock')).length;
- let processedCards = 0;
-
- for (const card of cards) {
- if (card.classList.contains('trade__inventory-item--lock')) {
- continue;
- }
-
- let cardId = await getCardId(card);
- if (cardId) {
- await readyToChargeCard(cardId);
- processedCards++;
- progressBar.update(processedCards, totalCards);
- counter--;
- updateButtonCounter(buttonId, counter);
- }
- }
-
- setTimeout(() => {
- progressBar.remove();
- }, 1000);
-
- showNotification('Отправили на обмен ' + cardCounter + ' карточек на странице');
- stopAnimation(buttonId);
- }
-
- const readyToChargeCard = async (cardId) => {
- 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 {
- position: relative;
- }
-
- .processing-card img {
- position: relative;
- z-index: 2;
- }
-
- .processing-card::after {
- content: '';
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- max-height: calc(100% - 30px);
- border-radius: 8px;
- z-index: 1;
- animation: glowEffect 1.5s infinite;
- pointer-events: none;
- }
-
- /* Общие стили для карт */
- .card-stats {
- position: relative;
- background: linear-gradient(45deg, #6c5ce7, #a367dc);
- padding: 8px;
- color: white;
- font-size: 12px;
- margin-top: 5px;
- border-radius: 5px;
- display: flex;
- justify-content: space-between;
- align-items: center;
- text-shadow: 1px 1px 2px rgba(0,0,0,0.3);
- animation: fadeInUp 0.3s ease;
- z-index: 0 !important;
- }
-
- /* Стили для истории трейдов */
- .history__inner {
- max-width: 1200px !important;
- margin: 0 auto !important;
- padding: 15px !important;
- }
-
- .history__item {
- background: rgba(108, 92, 231, 0.05) !important;
- border-radius: 10px !important;
- padding: 20px !important;
- margin-bottom: 20px !important;
- }
-
- .history__body {
- display: flex !important;
- flex-wrap: wrap !important;
- gap: 15px !important;
- padding: 15px !important;
- border-radius: 8px !important;
- }
-
- .history__body--gained {
- background: rgba(46, 213, 115, 0.1) !important;
- margin-bottom: 10px !important;
- }
-
- .history__body--lost {
- background: rgba(255, 71, 87, 0.1) !important;
- }
-
- /* Увеличенные размеры для карточек в истории трейдов на ПК */
- @media screen and (min-width: 769px) {
- .history__body-item {
- width: 150px !important;
- height: auto !important;
- transition: transform 0.2s !important;
- }
-
- .history__body-item img {
- width: 150px !important;
- height: auto !important;
- border-radius: 8px !important;
- box-shadow: 0 2px 8px rgba(0,0,0,0.1) !important;
- }
- }
-
- .history__body-item:hover {
- transform: scale(1.05) !important;
- z-index: 2 !important;
- }
-
- /* Мобильная версия истории трейдов */
- @media screen and (max-width: 768px) {
- .history__body-item,
- .history__body-item img {
- width: 120px !important;
- }
-
- .processing-card::before {
- top: -1px !important;
- left: -1px !important;
- right: -1px !important;
- bottom: -1px !important;
- opacity: 0.5 !important;
- }
- }
-
- .progress-bar {
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- height: 4px;
- background: #ddd;
- z-index: 10000;
- }
-
- .progress-bar__fill {
- width: 0%;
- height: 100%;
- background: linear-gradient(to right, #6c5ce7, #a367dc);
- transition: width 0.3s ease;
- position: relative;
- }
-
- .progress-bar__text {
- position: absolute;
- left: 50%;
- top: 5px;
- transform: translateX(-50%);
- color: #ffffff;
- font-size: 14px;
- font-weight: bold;
- background: #6c5ce7;
- padding: 2px 8px;
- border-radius: 10px;
- box-shadow: 0 2px 4px rgba(0,0,0,0.2);
- text-shadow: 1px 1px 2px rgba(0,0,0,0.3);
- }
-
- .card-stats span {
- display: flex;
- align-items: center;
- gap: 4px;
- }
-
- .card-stats span i {
- font-size: 14px;
- }
-
-
- /* Мобильная версия */
- @media screen and (max-width: 768px) {
- .action-button {
- transform: scale(0.8) !important;
- }
-
- .action-button button {
- width: 35px !important;
- height: 35px !important;
- }
-
- .action-button span {
- font-size: 16px !important;
- }
-
- .action-button div {
- font-size: 12px !important;
- padding: 6px 10px !important;
- }
-
- .card-stats {
- font-size: 10px !important;
- padding: 4px !important;
- }
-
- .card-stats span i {
- font-size: 12px !important;
- }
-
- /* Исправление отображения карт на странице переплавки */
- .remelt__inventory-list {
- grid-template-columns: repeat(2, 1fr) !important;
- gap: 10px !important;
- }
-
- .remelt__inventory-item {
- width: 100% !important;
- margin: 0 !important;
- }
-
- .remelt__inventory-item img {
- width: 100% !important;
- height: auto !important;
- }
-
- .remelt__inventory-item .card-stats {
- width: 100% !important;
- margin-top: 4px !important;
- }
-
- /* Уменьшение размера карт в паках */
- .lootbox__card {
- transform: scale(0.75) !important;
- margin-top: -10px !important;
- margin-bottom: 25px !important;
- }
-
- .lootbox__card .card-stats {
- font-size: 14px !important;
- padding: 6px !important;
- }
-
- .lootbox__card .card-stats i {
- font-size: 14px !important;
- }
-
- .lootbox__list {
- gap: 15px !important;
- padding-bottom: 15px !important;
- }
-
- /* Уменьшение размера карт в истории */
- .history__body-item {
- width: 100px !important;
- }
-
- .history__body-item img {
- width: 100px !important;
- }
-
- /* Уменьшение размера прогресс-бара */
- .progress-bar {
- height: 3px !important;
- }
-
- .progress-bar__text {
- font-size: 12px !important;
- padding: 1px 6px !important;
- }
- }
-
- /* Стили для карт */
- .card-stats {
- position: relative;
- background: linear-gradient(45deg, #6c5ce7, #a367dc);
- padding: 8px;
- color: white;
- font-size: 12px;
- margin-top: 5px;
- border-radius: 5px;
- display: flex;
- justify-content: space-between;
- align-items: center;
- text-shadow: 1px 1px 2px rgba(0,0,0,0.3);
- animation: fadeInUp 0.3s ease;
- }
-
- /* Специальные стили для карт в паках */
- .lootbox__card {
- position: relative !important;
- transform: scale(0.85) !important;
- margin-top: -15px !important;
- margin-bottom: 35px !important;
- }
-
- .lootbox__card .card-stats {
- position: absolute !important;
- bottom: -35px !important;
- left: 0 !important;
- right: 0 !important;
- margin: 0;
- padding: 8px !important;
- border-radius: 5px;
- z-index: 9999 !important;
- background: linear-gradient(45deg, #6c5ce7, #a367dc) !important;
- font-size: 16px !important;
- width: 100% !important;
- transform: none !important;
- text-rendering: optimizeLegibility !important;
- -webkit-font-smoothing: antialiased !important;
- }
-
- .lootbox__card .card-stats span {
- color: white !important;
- text-shadow: 1px 1px 2px rgba(0,0,0,0.3) !important;
- padding: 0 8px !important;
- flex: 1;
- text-align: center;
- font-weight: 500 !important;
- }
-
- .lootbox__card .card-stats i {
- color: white !important;
- font-size: 16px !important;
- margin-right: 4px;
- }
-
- .lootbox__list {
- gap: 25px !important;
- padding-bottom: 20px !important;
- }
-
- /* Мобильная версия */
- @media screen and (max-width: 768px) {
- .lootbox__card {
- transform: scale(0.8) !important;
- margin-top: -20px !important;
- margin-bottom: 30px !important;
- }
- }
- `;
- document.head.appendChild(style);
-
- function clearIcons() {
- $('.card-notification:first')?.click();
- }
-
- function autoRepeatCheck() {
- clearIcons();
- checkGiftCard(document);
-
- Audio.prototype.play = function() {
- return new Promise(() => {});
- };
- }
-
- async function checkGiftCard(doc) {
- const button = doc.querySelector('#gift-icon');
- if (!button) return;
-
- const giftCode = button.getAttribute('data-code');
- if (!giftCode) return false;
-
- try {
- const response = await fetch('/engine/ajax/controller.php?mod=gift_code_game', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/x-www-form-urlencoded'
- },
- body: new URLSearchParams({
- code: giftCode,
- user_hash: dle_login_hash
- })
- });
- const data = await response.json();
- if (data.status === 'ok') {
- showNotification(data.text);
- button.remove();
- }
- } catch (error) {
- }
- }
-
- function startPing() {
- const userHash = window.dle_login_hash;
- if (!userHash) {
- return;
- }
- const currentDomain = getCurrentDomain();
- const url = `${currentDomain}/engine/ajax/controller.php?mod=user_count_timer&user_hash=${userHash}`;
- fetch(url)
- .then(response => {
- if (!response.ok) {
- throw new Error(`HTTP error! Status: ${response.status}`);
- }
- return response.json();
- })
- .then(data => {
- })
- .catch(error => {
- });
- }
-
- function checkNewCard() {
- let userHash = window.dle_login_hash;
-
- if (!userHash) {
- setTimeout(() => {
- userHash = window.dle_login_hash;
- if (userHash) {
- checkNewCard();
- }
- }, 2000);
- return;
- }
-
- const currentDateTime = new Date();
- const localStorageKey = 'checkCardStopped' + userHash;
- if (localStorage.getItem(localStorageKey) === currentDateTime.toISOString().slice(0, 13)) {
- return;
- }
-
- const currentDomain = getCurrentDomain();
- const url = `${currentDomain}/engine/ajax/controller.php?mod=reward_card&action=check_reward&user_hash=${userHash}`;
-
- fetch(url)
- .then(response => {
- if (!response.ok) {
- throw new Error(`HTTP error! Status: ${response.status}`);
- }
- return response.json();
- })
- .then(data => {
- if (data.stop_reward === "yes") {
- localStorage.setItem(localStorageKey, currentDateTime.toISOString().slice(0, 13));
- return;
- }
- 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 clearCardCache() {
- const keys = Object.keys(localStorage);
- let clearedCount = 0;
-
- keys.forEach(key => {
- if (key.startsWith('cardId: ')) {
- localStorage.removeItem(key);
- clearedCount++;
- }
- });
-
- showNotification(`Очищено ${clearedCount} карточек из кеша`);
- }
-
- function addClearCacheButton() {
- if (window.location.pathname.includes('/pm/') ||
- window.location.pathname.includes('emotions.php') ||
- window.frameElement) {
- return;
- }
-
- if (!document.querySelector('#clearCacheButton')) {
- const button = getButton('clearCacheButton', 'trash', 63, 'Очистить кеш карт', clearCardCache);
- document.body.appendChild(button);
- }
- }
-
- (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();
- addClearCacheButton();
- }
- }, 500);
-
- $('#tg-banner').remove();
- localStorage.setItem('notify18', 'closed');
- localStorage.setItem('hideTelegramAs', 'true');
- }
-
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', initializeScript);
- } else {
- initializeScript();
- }
-
- window.addEventListener('pageshow', initializeScript);
- })();
-
- (function() {
- 'use strict';
-
- let lastActiveTime = localStorage.getItem('lastCrystalTime') || "00:00";
- let processedCrystals = new Set(JSON.parse(localStorage.getItem('processedCrystals') || '[]'));
- 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;
-
- if (typeof Worker === 'undefined') {
- console.log('Web Workers не поддерживаются в этом браузере');
- setInterval(clickOnCrystal, 1000);
- setInterval(preventTimeout, 2000);
- setInterval(detectInactiveCrystal, 4000);
- setInterval(() => {
- const now = new Date();
- if (now.getHours() === 0 && now.getMinutes() === 0) {
- resetTimerAtMidnight();
- }
- }, 60000);
- return;
- }
-
- try {
- 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;
- }
- };
-
- worker.onerror = function(error) {
- console.log('Ошибка воркера:', error);
- setInterval(clickOnCrystal, 1000);
- setInterval(preventTimeout, 2000);
- setInterval(detectInactiveCrystal, 4000);
- setInterval(() => {
- const now = new Date();
- if (now.getHours() === 0 && now.getMinutes() === 0) {
- resetTimerAtMidnight();
- }
- }, 60000);
- };
- } catch (error) {
- console.log('Ошибка при создании воркера:', error);
- setInterval(clickOnCrystal, 1000);
- setInterval(preventTimeout, 2000);
- setInterval(detectInactiveCrystal, 4000);
- setInterval(() => {
- const now = new Date();
- if (now.getHours() === 0 && now.getMinutes() === 0) {
- resetTimerAtMidnight();
- }
- }, 60000);
- }
- }
-
- function clickOnCrystal() {
- try {
- const chatMessages = document.querySelectorAll(".lc_chat_li");
- chatMessages.forEach(msg => {
- const diamond = msg.querySelector("#diamonds-chat");
- const timeElement = msg.querySelector(".lc_chat_li_date");
-
- if (diamond && timeElement) {
- const crystalId = getCrystalId(msg);
- let messageTime = timeElement.textContent.trim();
-
- if (messageTime >= lastActiveTime && !processedCrystals.has(crystalId)) {
- diamond.click();
- lastActiveTime = messageTime;
- processedCrystals.add(crystalId);
- try {
- localStorage.setItem('lastCrystalTime', lastActiveTime);
- localStorage.setItem('processedCrystals', JSON.stringify([...processedCrystals]));
- } catch (e) {
- console.log('Ошибка сохранения в localStorage:', e);
- }
- }
- }
- });
- } catch (error) {
- console.log('Ошибка в clickOnCrystal:', error);
- }
- }
-
- function preventTimeout() {
- 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() {
- 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([]));
- }
- }
-
- initializeWorker();
- worker.postMessage({ action: 'start' });
-
- window.addEventListener('beforeunload', function() {
- if (worker) {
- worker.terminate();
- URL.revokeObjectURL(workerBlob);
- }
- });
-
- })();