Card Helper AStars | AnimeStars | ASStars

card helper

  1. // ==UserScript==
  2. // @name Card Helper AStars | AnimeStars | ASStars
  3. // @namespace animestars.org
  4. // @version 6.7
  5. // @description card helper
  6. // @author bmr
  7. // @match https://astars.club/*
  8. // @match https://asstars1.astars.club/*
  9. // @match https://animestars.org/*
  10. // @match https://as1.astars.club/*
  11. // @match https://asstars.tv/*
  12. // @license MIT
  13. // @grant none
  14. // ==/UserScript==
  15.  
  16. const DELAY = 40;
  17.  
  18. const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
  19.  
  20. let cardCounter = 0;
  21.  
  22. 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';
  23.  
  24. function getCurrentDomain() {
  25. const hostname = window.location.hostname;
  26. const protocol = window.location.protocol;
  27. return `${protocol}//${hostname}`;
  28. }
  29.  
  30. async function getCount(cardId, type) {
  31. const currentDomain = getCurrentDomain();
  32. let count = 0;
  33. let needResponse = await fetch(`${currentDomain}/cards/${cardId}/users/${type}/`);
  34. if (needResponse.status === 502) {
  35. throw new Error("502 Bad Gateway");
  36. }
  37. let needHtml = '';
  38. let needDoc = '';
  39. if (needResponse.ok) {
  40. needHtml = await needResponse.text();
  41. needDoc = new DOMParser().parseFromString(needHtml, 'text/html');
  42. count = needDoc.querySelectorAll('.profile__friends-item').length;
  43. } else {
  44. return count;
  45. }
  46.  
  47. const pagination = needDoc.querySelector('.pagination__pages');
  48. if (pagination && count >= 50) {
  49. const lastPageNum = pagination.querySelector('a:last-of-type');
  50. const totalPages = lastPageNum ? parseInt(lastPageNum.innerText, 10) : 1;
  51. if (totalPages > 1) {
  52. count = (totalPages - 1) * 50;
  53. }
  54. needResponse = await fetch(`${currentDomain}/cards/${cardId}/users/${type}/page/${totalPages}`);
  55. if (needResponse.status === 502) {
  56. throw new Error("502 Bad Gateway");
  57. }
  58. if (needResponse.ok) {
  59. needHtml = await needResponse.text();
  60. needDoc = new DOMParser().parseFromString(needHtml, 'text/html');
  61. count += needDoc.querySelectorAll('.profile__friends-item').length;
  62. }
  63. }
  64.  
  65. return count;
  66. }
  67.  
  68. async function loadCard(cardId) {
  69. const cacheKey = 'cardId: ' + cardId;
  70. let card = await getCard(cacheKey) ?? {};
  71.  
  72. if (Object.keys(card).length) {
  73. return card;
  74. }
  75.  
  76. const currentDomain = getCurrentDomain();
  77. await sleep(DELAY);
  78. let needCount = await getCount(cardId, 'need');
  79. await sleep(DELAY);
  80. let tradeCount = await getCount(cardId, 'trade');
  81. await sleep(DELAY);
  82.  
  83. let popularityCount = 0;
  84. const popularityResponse = await fetch(`${currentDomain}/cards/${cardId}/users/`);
  85.  
  86. if (popularityResponse.ok) {
  87. const popularityHtml = await popularityResponse.text();
  88. const popularityDoc = new DOMParser().parseFromString(popularityHtml, 'text/html');
  89.  
  90. const pagination = popularityDoc.querySelector('.pagination__pages');
  91. if (pagination) {
  92. const lastPageNum = pagination.querySelector('a:last-of-type');
  93. const totalPages = lastPageNum ? parseInt(lastPageNum.innerText, 10) : 1;
  94.  
  95. if (totalPages > 1) {
  96. popularityCount = (totalPages - 1) * 40;
  97. await sleep(DELAY);
  98. const lastPageResponse = await fetch(`${currentDomain}/cards/${cardId}/users/page/${totalPages}`);
  99. if (lastPageResponse.ok) {
  100. const lastPageHtml = await lastPageResponse.text();
  101. const lastPageDoc = new DOMParser().parseFromString(lastPageHtml, 'text/html');
  102. const lastPageCount = lastPageDoc.querySelectorAll('.card-show__owner').length;
  103. popularityCount += lastPageCount;
  104. }
  105. } else {
  106. popularityCount = popularityDoc.querySelectorAll('.card-show__owner').length;
  107. }
  108. } else {
  109. popularityCount = popularityDoc.querySelectorAll('.card-show__owner').length;
  110. }
  111. }
  112.  
  113. card = {popularityCount, needCount, tradeCount};
  114. await cacheCard(cacheKey, card);
  115. return card;
  116. }
  117.  
  118. async function updateCardInfo(cardId, element) {
  119. if (!cardId || !element) {
  120. return;
  121. }
  122. try {
  123. const card = await loadCard(cardId);
  124.  
  125. let statsContainer = document.querySelector('.card-stats-container');
  126. if (!statsContainer) {
  127. statsContainer = document.createElement('div');
  128. statsContainer.className = 'card-stats-container';
  129. document.body.appendChild(statsContainer);
  130. }
  131.  
  132. const oldStats = element.querySelector('.card-stats');
  133. if (oldStats) {
  134. oldStats.remove();
  135. }
  136.  
  137. const stats = document.createElement('div');
  138. stats.className = 'card-stats';
  139.  
  140. stats.innerHTML = `
  141. <span title="Владельцев">
  142. <i class="fas fa-users"></i>
  143. ${card.popularityCount}
  144. </span>
  145. <span title="Хотят получить">
  146. <i class="fas fa-heart"></i>
  147. ${card.needCount}
  148. </span>
  149. <span title="Готовы обменять">
  150. <i class="fas fa-sync-alt"></i>
  151. ${card.tradeCount}
  152. </span>
  153. `;
  154.  
  155. element.appendChild(stats);
  156.  
  157. } catch (error) {
  158. throw error;
  159. }
  160. }
  161.  
  162. function clearMarkFromCards() {
  163. cleanByClass('div-marked');
  164. }
  165.  
  166. function removeAllLinkIcons() {
  167. cleanByClass('link-icon');
  168. }
  169.  
  170. function cleanByClass(className) {
  171. const list = document.querySelectorAll('.' + className);
  172. list.forEach(item => item.remove());
  173. }
  174.  
  175. function getCardsOnPage() {
  176. return Array.from(
  177. document.querySelectorAll(cardClasses)
  178. ).filter(card => card.offsetParent !== null);
  179. }
  180.  
  181. async function processCards() {
  182. if (isCardRemeltPage()) {
  183. const storedData = JSON.parse(localStorage.getItem('animeCardsData')) || {};
  184. if (Object.keys(storedData).length < 1) {
  185. await readyRemeltCards();
  186. return;
  187. }
  188. }
  189.  
  190. removeMatchingWatchlistItems();
  191. removeAllLinkIcons();
  192. clearMarkFromCards();
  193.  
  194. const cards = getCardsOnPage();
  195. let counter = cards.length;
  196.  
  197. if (!counter) {
  198. return;
  199. }
  200.  
  201. let buttonId = 'processCards';
  202. startAnimation(buttonId);
  203. updateButtonCounter(buttonId, counter);
  204.  
  205. showNotification('Проверяю спрос на ' + counter + ' карточек');
  206.  
  207. for (const card of cards) {
  208. if (card.classList.contains('trade__inventory-item--lock') || card.classList.contains('remelt__inventory-item--lock')) {
  209. continue;
  210. }
  211. card.classList.add('processing-card');
  212. let cardId = await getCardId(card);
  213. if (cardId) {
  214. await updateCardInfo(cardId, card).catch(error => {
  215. return;
  216. });
  217. counter--;
  218. updateButtonCounter(buttonId, counter);
  219. }
  220. card.classList.remove('processing-card');
  221.  
  222. if (card.classList.contains('lootbox__card')) {
  223. card.addEventListener('click', removeAllLinkIcons);
  224. }
  225. }
  226.  
  227. showNotification('Проверка спроса завершена');
  228. stopAnimation(buttonId);
  229. }
  230.  
  231. function removeMatchingWatchlistItems() {
  232. const watchlistItems = document.querySelectorAll('.watchlist__item');
  233. if (watchlistItems.length == 0) {
  234. return;
  235. }
  236. watchlistItems.forEach(item => {
  237. const episodesText = item.querySelector('.watchlist__episodes')?.textContent.trim();
  238. if (episodesText) {
  239. const matches = episodesText.match(/[\d]+/g);
  240. if (matches) {
  241. const currentEpisode = parseInt(matches[0], 10);
  242. const totalEpisodes = parseInt(matches.length === 4 ? matches[3] : matches[1], 10);
  243. if (currentEpisode === totalEpisodes) {
  244. item.remove();
  245. }
  246. }
  247. }
  248. });
  249.  
  250. if (watchlistItems.length) {
  251. showNotification('Из списка удалены просмотренные аниме. В списке осталось ' + document.querySelectorAll('.watchlist__item').length + ' записей.');
  252. }
  253. }
  254.  
  255. function startAnimation(id) {
  256. $('#' + id + ' span:first').css('animation', 'pulseIcon 1s ease-in-out infinite');
  257. }
  258.  
  259. function stopAnimation(id) {
  260. $('#' + id + ' span:first').css('animation', '');
  261. }
  262.  
  263. function getButton(id, className, percent, text, clickFunction) {
  264. const button = document.createElement('button');
  265. button.id = id;
  266. button.style.position = 'fixed';
  267. button.style.top = percent + '%';
  268. button.style.right = '1%';
  269. button.style.zIndex = '1000';
  270. button.style.backgroundColor = '#6c5ce7';
  271. button.style.color = '#fff';
  272. button.style.border = 'none';
  273. button.style.borderRadius = '50%';
  274. button.style.width = '45px';
  275. button.style.height = '45px';
  276. button.style.padding = '0';
  277. button.style.cursor = 'pointer';
  278. button.style.boxShadow = '0 2px 5px rgba(0, 0, 0, 0.2)';
  279. button.style.transition = 'all 0.3s ease';
  280.  
  281. let tooltipTimeout;
  282.  
  283. button.onmouseover = function() {
  284. this.style.backgroundColor = '#5f51e3';
  285. tooltip.style.opacity = '1';
  286. tooltip.style.transform = 'translateX(0)';
  287. if (tooltipTimeout) {
  288. clearTimeout(tooltipTimeout);
  289. }
  290. };
  291. button.onmouseout = function() {
  292. this.style.backgroundColor = '#6c5ce7';
  293. tooltip.style.opacity = '0';
  294. tooltip.style.transform = 'translateX(10px)';
  295. };
  296.  
  297. const icon = document.createElement('span');
  298. icon.className = 'fal fa-' + className;
  299. icon.style.display = 'inline-block';
  300. icon.style.fontSize = '20px';
  301. button.appendChild(icon);
  302.  
  303. const tooltip = document.createElement('div');
  304. tooltip.style.cssText = `
  305. position: fixed;
  306. right: calc(1% + 55px);
  307. background-color: #2d3436;
  308. color: #fff;
  309. padding: 8px 12px;
  310. border-radius: 4px;
  311. font-size: 14px;
  312. opacity: 0;
  313. transition: all 0.3s ease;
  314. white-space: nowrap;
  315. top: ${percent}%;
  316. transform: translateX(10px);
  317. z-index: 999;
  318. pointer-events: none;
  319. `;
  320.  
  321. switch(id) {
  322. case 'processCards':
  323. tooltip.textContent = 'Узнать спрос';
  324. break;
  325. case 'readyToCharge':
  326. tooltip.textContent = 'Отметить всё как "Готов обменять"';
  327. break;
  328. case 'readyRemeltCards':
  329. tooltip.textContent = 'Кешировать карточки';
  330. break;
  331. default:
  332. tooltip.textContent = text;
  333. }
  334.  
  335. button.addEventListener('click', function(e) {
  336. e.stopPropagation();
  337. clickFunction(e);
  338.  
  339. if (window.innerWidth <= 768) {
  340. tooltip.style.opacity = '1';
  341. tooltip.style.transform = 'translateX(0)';
  342.  
  343. if (tooltipTimeout) {
  344. clearTimeout(tooltipTimeout);
  345. }
  346. tooltipTimeout = setTimeout(() => {
  347. tooltip.style.opacity = '0';
  348. tooltip.style.transform = 'translateX(10px)';
  349. }, 1000);
  350. }
  351. });
  352.  
  353. const container = document.createElement('div');
  354. container.appendChild(tooltip);
  355. container.appendChild(button);
  356.  
  357. button.classList.add('action-button');
  358.  
  359. return container;
  360. }
  361.  
  362. function updateButtonCounter(id, counter) {
  363. return;
  364. }
  365.  
  366. function addUpdateButton() {
  367. if (window.location.pathname.includes('/pm/') ||
  368. window.location.pathname.includes('emotions.php') ||
  369. window.frameElement) {
  370. return;
  371. }
  372.  
  373. if (!document.querySelector('#fetchLinksButton')) {
  374. let cards = getCardsOnPage();
  375.  
  376. document.body.appendChild(getButton('processCards', 'star', 37, 'Сравнить карточки', processCards));
  377.  
  378. if (!cards.length) {
  379. return
  380. }
  381.  
  382. if (isMyCardPage()) {
  383. document.body.appendChild(getButton('readyToCharge', 'handshake', 50, '"Готов поменять" на все карточки', readyToCharge));
  384. }
  385.  
  386. if (isCardRemeltPage()) {
  387. document.body.appendChild(getButton('readyRemeltCards', 'yin-yang', 50, 'закешировать карточки', readyRemeltCards));
  388. const storedData = JSON.parse(localStorage.getItem('animeCardsData')) || {};
  389. updateButtonCounter('readyRemeltCards', Object.keys(storedData).length);
  390. }
  391. }
  392. }
  393.  
  394. function isMyCardPage() {
  395. return (/^\/user\/(.*)\/cards(\/page\/\d+\/)?/).test(window.location.pathname)
  396. }
  397.  
  398. function isCardRemeltPage() {
  399. return (/^\/cards_remelt\//).test(window.location.pathname)
  400. }
  401.  
  402. async function readyRemeltCards() {
  403. showNotification('Кеширую все карты так как иначе на этой странице не получится их определить рейтинги');
  404. const linkElement = document.querySelector('a.button.button--left-icon.mr-3');
  405. const href = linkElement ? linkElement.href : null;
  406. if (!href) {
  407. return;
  408. }
  409. removeMatchingWatchlistItems();
  410. removeAllLinkIcons();
  411. clearMarkFromCards();
  412. const cards = getCardsOnPage();
  413. let counter = cards.length;
  414. if (!counter) {
  415. return;
  416. }
  417. let buttonId = 'readyRemeltCards';
  418. startAnimation(buttonId);
  419. await scrapeAllPages(href, buttonId);
  420. stopAnimation(buttonId);
  421. }
  422.  
  423. async function scrapeAllPages(firstPageHref, buttonId) {
  424. const response = await fetch(firstPageHref);
  425. if (!response.ok) {
  426. throw new Error(`Ошибка HTTP: ${response.status}`);
  427. }
  428. const firstPageDoc = new DOMParser().parseFromString(await response.text(), 'text/html');
  429. const pagination = firstPageDoc.querySelector('#pagination');
  430. if (!pagination) {
  431. return;
  432. }
  433.  
  434. const progressBar = createProgressBar();
  435. let totalCards = 0;
  436. let processedCards = 0;
  437.  
  438. let storedData = JSON.parse(localStorage.getItem('animeCardsData')) || {};
  439. const titleElement = firstPageDoc.querySelector('h1.secondary-title.text-center');
  440. if (titleElement) {
  441. const match = titleElement.textContent.match(/\((\d+)\s*шт\.\)/);
  442. totalCards = match ? parseInt(match[1], 10) : -1;
  443. if (totalCards == Object.keys(storedData).length) {
  444. showNotification('На данный момент в кеше карточек ровно столько же сколько в профиле пользователя');
  445. return;
  446. }
  447. }
  448.  
  449. async function processCardsToLocalstorage(doc) {
  450. const cards = doc.querySelectorAll('.anime-cards__item');
  451. for (let i = 0; i < cards.length; i += 5) {
  452. const cardGroup = Array.from(cards).slice(i, i + 5);
  453. for (const card of cardGroup) {
  454. const cardId = card.getAttribute('data-id');
  455. const ownerId = card.getAttribute('data-owner-id');
  456. const name = card.getAttribute('data-name');
  457. const rank = card.getAttribute('data-rank');
  458. const animeLink = card.getAttribute('data-anime-link');
  459. const image = card.getAttribute('data-image');
  460. const ownerKey = 'o_' + ownerId;
  461. if (!ownerId || !cardId) continue;
  462. if (!storedData[ownerKey]) {
  463. storedData[ownerKey] = [];
  464. }
  465. storedData[ownerKey].push({ cardId, name, rank, animeLink, image, ownerId });
  466. processedCards++;
  467. if (totalCards > 0) {
  468. progressBar.update(processedCards, totalCards);
  469. }
  470. }
  471. await sleep(10);
  472. }
  473. }
  474.  
  475. async function fetchPage(url) {
  476. try {
  477. const response = await fetch(url);
  478. if (!response.ok) throw new Error(`Ошибка загрузки страницы ${url}`);
  479. return await response.text();
  480. } catch (error) {
  481. return null;
  482. }
  483. }
  484.  
  485. await processCardsToLocalstorage(firstPageDoc);
  486.  
  487. const lastPageLink = pagination.querySelector('a:last-of-type');
  488. if (lastPageLink) {
  489. const lastPageNumber = parseInt(lastPageLink.textContent.trim(), 10);
  490. if (!isNaN(lastPageNumber) && lastPageNumber > 1) {
  491. const parser = new DOMParser();
  492. for (let i = 2; i <= lastPageNumber; i++) {
  493. const pageUrl = lastPageLink.href.replace(/page\/\d+/, `page/${i}`);
  494. const pageHTML = await fetchPage(pageUrl);
  495. if (pageHTML) {
  496. await processCardsToLocalstorage(parser.parseFromString(pageHTML, 'text/html'));
  497. }
  498. await sleep(1000);
  499.  
  500. if (i % 3 === 0) {
  501. localStorage.setItem('animeCardsData', JSON.stringify(storedData));
  502. }
  503. }
  504. }
  505. }
  506.  
  507. localStorage.setItem('animeCardsData', JSON.stringify(storedData));
  508.  
  509. setTimeout(() => {
  510. progressBar.remove();
  511. }, 1000);
  512.  
  513. document.body.appendChild(getButton('processCards', 'star', 37, 'Сравнить карточки', processCards));
  514. await processCards();
  515. }
  516.  
  517. async function getCardId(card) {
  518. let cardId = card.getAttribute('card-id') || card.getAttribute('data-card-id') || card.getAttribute('data-id');
  519. const href = card.getAttribute('href');
  520. if (href) {
  521. let cardIdMatch = href.match(/\/cards\/(\d+)\/users\//);
  522. if (cardIdMatch) {
  523. cardId = cardIdMatch[1];
  524. }
  525. }
  526. if (cardId) {
  527. const cardByOwner = await getFirstCardByOwner(cardId);
  528. if (cardByOwner) {
  529. cardId = cardByOwner.cardId;
  530. }
  531. }
  532. return cardId;
  533. }
  534.  
  535. async function getFirstCardByOwner(ownerId) {
  536. const storedData = JSON.parse(localStorage.getItem('animeCardsData')) || {};
  537. const key = 'o_' + ownerId;
  538. return storedData[key] && storedData[key].length > 0 ? storedData[key][0] : null;
  539. }
  540.  
  541. async function readyToCharge() {
  542. showNotification('Отмечаем все карты на странице как: "Готов обменять" кроме тех что на обмене и заблокированных');
  543. let cards = getCardsOnPage();
  544.  
  545. let counter = cards.length;
  546. let buttonId = 'readyToCharge';
  547. startAnimation(buttonId);
  548. updateButtonCounter(buttonId, counter);
  549. clearMarkFromCards();
  550.  
  551. const progressBar = createProgressBar();
  552. cardCounter = 0;
  553.  
  554. const totalCards = cards.filter(card => !card.classList.contains('trade__inventory-item--lock')).length;
  555. let processedCards = 0;
  556.  
  557. for (const card of cards) {
  558. if (card.classList.contains('trade__inventory-item--lock')) {
  559. continue;
  560. }
  561.  
  562. let cardId = await getCardId(card);
  563. if (cardId) {
  564. await readyToChargeCard(cardId);
  565. processedCards++;
  566. progressBar.update(processedCards, totalCards);
  567. counter--;
  568. updateButtonCounter(buttonId, counter);
  569. }
  570. }
  571.  
  572. setTimeout(() => {
  573. progressBar.remove();
  574. }, 1000);
  575.  
  576. showNotification('Отправили на обмен ' + cardCounter + ' карточек на странице');
  577. stopAnimation(buttonId);
  578. }
  579.  
  580. const readyToChargeCard = async (cardId) => {
  581. await sleep(DELAY * 2);
  582. const url = '/engine/ajax/controller.php?mod=trade_ajax';
  583. const data = {
  584. action: 'propose_add',
  585. type: 1,
  586. card_id: cardId,
  587. user_hash: dle_login_hash
  588. };
  589.  
  590. try {
  591. const response = await fetch(url, {
  592. method: 'POST',
  593. headers: {
  594. 'Content-Type': 'application/x-www-form-urlencoded',
  595. },
  596. body: new URLSearchParams(data).toString()
  597. });
  598. if (response.status === 502) {
  599. throw new Error("502 Bad Gateway");
  600. }
  601. if (response.ok) {
  602. const data = await response.json();
  603. if (data.error) {
  604. if (data.error == 'Слишком часто, подождите пару секунд и повторите действие') {
  605. await readyToChargeCard(cardId);
  606. return;
  607. }
  608. }
  609. if ( data.status == 'added' ) {
  610. cardCounter++;
  611. return;
  612. }
  613. if ( data.status == 'deleted' ) {
  614. await readyToChargeCard(cardId);
  615. return;
  616. }
  617. cardCounter++;
  618. }
  619. } catch (error) {
  620. }
  621. };
  622.  
  623. const style = document.createElement('style');
  624. style.textContent = `
  625. @keyframes glowEffect {
  626. 0% { box-shadow: 0 0 5px #6c5ce7; }
  627. 50% { box-shadow: 0 0 20px #6c5ce7; }
  628. 100% { box-shadow: 0 0 5px #6c5ce7; }
  629. }
  630.  
  631. @keyframes fadeInUp {
  632. from {
  633. opacity: 0;
  634. transform: translateY(10px);
  635. }
  636. to {
  637. opacity: 1;
  638. transform: translateY(0);
  639. }
  640. }
  641.  
  642. .processing-card {
  643. position: relative;
  644. }
  645.  
  646. .processing-card img {
  647. position: relative;
  648. z-index: 2;
  649. }
  650.  
  651. .processing-card::after {
  652. content: '';
  653. position: absolute;
  654. top: 0;
  655. left: 0;
  656. width: 100%;
  657. height: 100%;
  658. max-height: calc(100% - 30px);
  659. border-radius: 8px;
  660. z-index: 1;
  661. animation: glowEffect 1.5s infinite;
  662. pointer-events: none;
  663. }
  664.  
  665. /* Общие стили для карт */
  666. .card-stats {
  667. position: relative;
  668. background: linear-gradient(45deg, #6c5ce7, #a367dc);
  669. padding: 8px;
  670. color: white;
  671. font-size: 12px;
  672. margin-top: 5px;
  673. border-radius: 5px;
  674. display: flex;
  675. justify-content: space-between;
  676. align-items: center;
  677. text-shadow: 1px 1px 2px rgba(0,0,0,0.3);
  678. animation: fadeInUp 0.3s ease;
  679. z-index: 0 !important;
  680. }
  681.  
  682. /* Стили для истории трейдов */
  683. .history__inner {
  684. max-width: 1200px !important;
  685. margin: 0 auto !important;
  686. padding: 15px !important;
  687. }
  688.  
  689. .history__item {
  690. background: rgba(108, 92, 231, 0.05) !important;
  691. border-radius: 10px !important;
  692. padding: 20px !important;
  693. margin-bottom: 20px !important;
  694. }
  695.  
  696. .history__body {
  697. display: flex !important;
  698. flex-wrap: wrap !important;
  699. gap: 15px !important;
  700. padding: 15px !important;
  701. border-radius: 8px !important;
  702. }
  703.  
  704. .history__body--gained {
  705. background: rgba(46, 213, 115, 0.1) !important;
  706. margin-bottom: 10px !important;
  707. }
  708.  
  709. .history__body--lost {
  710. background: rgba(255, 71, 87, 0.1) !important;
  711. }
  712.  
  713. /* Увеличенные размеры для карточек в истории трейдов на ПК */
  714. @media screen and (min-width: 769px) {
  715. .history__body-item {
  716. width: 150px !important;
  717. height: auto !important;
  718. transition: transform 0.2s !important;
  719. }
  720.  
  721. .history__body-item img {
  722. width: 150px !important;
  723. height: auto !important;
  724. border-radius: 8px !important;
  725. box-shadow: 0 2px 8px rgba(0,0,0,0.1) !important;
  726. }
  727. }
  728.  
  729. .history__body-item:hover {
  730. transform: scale(1.05) !important;
  731. z-index: 2 !important;
  732. }
  733.  
  734. /* Мобильная версия истории трейдов */
  735. @media screen and (max-width: 768px) {
  736. .history__body-item,
  737. .history__body-item img {
  738. width: 120px !important;
  739. }
  740.  
  741. .processing-card::before {
  742. top: -1px !important;
  743. left: -1px !important;
  744. right: -1px !important;
  745. bottom: -1px !important;
  746. opacity: 0.5 !important;
  747. }
  748. }
  749.  
  750. .progress-bar {
  751. position: fixed;
  752. top: 0;
  753. left: 0;
  754. width: 100%;
  755. height: 4px;
  756. background: #ddd;
  757. z-index: 10000;
  758. }
  759.  
  760. .progress-bar__fill {
  761. width: 0%;
  762. height: 100%;
  763. background: linear-gradient(to right, #6c5ce7, #a367dc);
  764. transition: width 0.3s ease;
  765. position: relative;
  766. }
  767.  
  768. .progress-bar__text {
  769. position: absolute;
  770. left: 50%;
  771. top: 5px;
  772. transform: translateX(-50%);
  773. color: #ffffff;
  774. font-size: 14px;
  775. font-weight: bold;
  776. background: #6c5ce7;
  777. padding: 2px 8px;
  778. border-radius: 10px;
  779. box-shadow: 0 2px 4px rgba(0,0,0,0.2);
  780. text-shadow: 1px 1px 2px rgba(0,0,0,0.3);
  781. }
  782.  
  783. .card-stats span {
  784. display: flex;
  785. align-items: center;
  786. gap: 4px;
  787. }
  788.  
  789. .card-stats span i {
  790. font-size: 14px;
  791. }
  792.  
  793.  
  794. /* Мобильная версия */
  795. @media screen and (max-width: 768px) {
  796. .action-button {
  797. transform: scale(0.8) !important;
  798. }
  799.  
  800. .action-button button {
  801. width: 35px !important;
  802. height: 35px !important;
  803. }
  804.  
  805. .action-button span {
  806. font-size: 16px !important;
  807. }
  808.  
  809. .action-button div {
  810. font-size: 12px !important;
  811. padding: 6px 10px !important;
  812. }
  813.  
  814. .card-stats {
  815. font-size: 10px !important;
  816. padding: 4px !important;
  817. }
  818.  
  819. .card-stats span i {
  820. font-size: 12px !important;
  821. }
  822.  
  823. /* Исправление отображения карт на странице переплавки */
  824. .remelt__inventory-list {
  825. grid-template-columns: repeat(2, 1fr) !important;
  826. gap: 10px !important;
  827. }
  828.  
  829. .remelt__inventory-item {
  830. width: 100% !important;
  831. margin: 0 !important;
  832. }
  833.  
  834. .remelt__inventory-item img {
  835. width: 100% !important;
  836. height: auto !important;
  837. }
  838.  
  839. .remelt__inventory-item .card-stats {
  840. width: 100% !important;
  841. margin-top: 4px !important;
  842. }
  843.  
  844. /* Уменьшение размера карт в паках */
  845. .lootbox__card {
  846. transform: scale(0.75) !important;
  847. margin-top: -10px !important;
  848. margin-bottom: 25px !important;
  849. }
  850.  
  851. .lootbox__card .card-stats {
  852. font-size: 14px !important;
  853. padding: 6px !important;
  854. }
  855.  
  856. .lootbox__card .card-stats i {
  857. font-size: 14px !important;
  858. }
  859.  
  860. .lootbox__list {
  861. gap: 15px !important;
  862. padding-bottom: 15px !important;
  863. }
  864.  
  865. /* Уменьшение размера карт в истории */
  866. .history__body-item {
  867. width: 100px !important;
  868. }
  869.  
  870. .history__body-item img {
  871. width: 100px !important;
  872. }
  873.  
  874. /* Уменьшение размера прогресс-бара */
  875. .progress-bar {
  876. height: 3px !important;
  877. }
  878.  
  879. .progress-bar__text {
  880. font-size: 12px !important;
  881. padding: 1px 6px !important;
  882. }
  883. }
  884.  
  885. /* Стили для карт */
  886. .card-stats {
  887. position: relative;
  888. background: linear-gradient(45deg, #6c5ce7, #a367dc);
  889. padding: 8px;
  890. color: white;
  891. font-size: 12px;
  892. margin-top: 5px;
  893. border-radius: 5px;
  894. display: flex;
  895. justify-content: space-between;
  896. align-items: center;
  897. text-shadow: 1px 1px 2px rgba(0,0,0,0.3);
  898. animation: fadeInUp 0.3s ease;
  899. }
  900.  
  901. /* Специальные стили для карт в паках */
  902. .lootbox__card {
  903. position: relative !important;
  904. transform: scale(0.85) !important;
  905. margin-top: -15px !important;
  906. margin-bottom: 35px !important;
  907. }
  908.  
  909. .lootbox__card .card-stats {
  910. position: absolute !important;
  911. bottom: -35px !important;
  912. left: 0 !important;
  913. right: 0 !important;
  914. margin: 0;
  915. padding: 8px !important;
  916. border-radius: 5px;
  917. z-index: 9999 !important;
  918. background: linear-gradient(45deg, #6c5ce7, #a367dc) !important;
  919. font-size: 16px !important;
  920. width: 100% !important;
  921. transform: none !important;
  922. text-rendering: optimizeLegibility !important;
  923. -webkit-font-smoothing: antialiased !important;
  924. }
  925.  
  926. .lootbox__card .card-stats span {
  927. color: white !important;
  928. text-shadow: 1px 1px 2px rgba(0,0,0,0.3) !important;
  929. padding: 0 8px !important;
  930. flex: 1;
  931. text-align: center;
  932. font-weight: 500 !important;
  933. }
  934.  
  935. .lootbox__card .card-stats i {
  936. color: white !important;
  937. font-size: 16px !important;
  938. margin-right: 4px;
  939. }
  940.  
  941. .lootbox__list {
  942. gap: 25px !important;
  943. padding-bottom: 20px !important;
  944. }
  945.  
  946. /* Мобильная версия */
  947. @media screen and (max-width: 768px) {
  948. .lootbox__card {
  949. transform: scale(0.8) !important;
  950. margin-top: -20px !important;
  951. margin-bottom: 30px !important;
  952. }
  953. }
  954. `;
  955. document.head.appendChild(style);
  956.  
  957. function clearIcons() {
  958. $('.card-notification:first')?.click();
  959. }
  960.  
  961. function autoRepeatCheck() {
  962. clearIcons();
  963. checkGiftCard(document);
  964.  
  965. Audio.prototype.play = function() {
  966. return new Promise(() => {});
  967. };
  968. }
  969.  
  970. async function checkGiftCard(doc) {
  971. const button = doc.querySelector('#gift-icon');
  972. if (!button) return;
  973.  
  974. const giftCode = button.getAttribute('data-code');
  975. if (!giftCode) return false;
  976.  
  977. try {
  978. const response = await fetch('/engine/ajax/controller.php?mod=gift_code_game', {
  979. method: 'POST',
  980. headers: {
  981. 'Content-Type': 'application/x-www-form-urlencoded'
  982. },
  983. body: new URLSearchParams({
  984. code: giftCode,
  985. user_hash: dle_login_hash
  986. })
  987. });
  988. const data = await response.json();
  989. if (data.status === 'ok') {
  990. showNotification(data.text);
  991. button.remove();
  992. }
  993. } catch (error) {
  994. }
  995. }
  996.  
  997. function startPing() {
  998. const userHash = window.dle_login_hash;
  999. if (!userHash) {
  1000. return;
  1001. }
  1002. const currentDomain = getCurrentDomain();
  1003. const url = `${currentDomain}/engine/ajax/controller.php?mod=user_count_timer&user_hash=${userHash}`;
  1004. fetch(url)
  1005. .then(response => {
  1006. if (!response.ok) {
  1007. throw new Error(`HTTP error! Status: ${response.status}`);
  1008. }
  1009. return response.json();
  1010. })
  1011. .then(data => {
  1012. })
  1013. .catch(error => {
  1014. });
  1015. }
  1016.  
  1017. function checkNewCard() {
  1018. let userHash = window.dle_login_hash;
  1019.  
  1020. if (!userHash) {
  1021. setTimeout(() => {
  1022. userHash = window.dle_login_hash;
  1023. if (userHash) {
  1024. checkNewCard();
  1025. }
  1026. }, 2000);
  1027. return;
  1028. }
  1029.  
  1030. const currentDateTime = new Date();
  1031. const localStorageKey = 'checkCardStopped' + userHash;
  1032. if (localStorage.getItem(localStorageKey) === currentDateTime.toISOString().slice(0, 13)) {
  1033. return;
  1034. }
  1035.  
  1036. const currentDomain = getCurrentDomain();
  1037. const url = `${currentDomain}/engine/ajax/controller.php?mod=reward_card&action=check_reward&user_hash=${userHash}`;
  1038.  
  1039. fetch(url)
  1040. .then(response => {
  1041. if (!response.ok) {
  1042. throw new Error(`HTTP error! Status: ${response.status}`);
  1043. }
  1044. return response.json();
  1045. })
  1046. .then(data => {
  1047. if (data.stop_reward === "yes") {
  1048. localStorage.setItem(localStorageKey, currentDateTime.toISOString().slice(0, 13));
  1049. return;
  1050. }
  1051. if (!data.cards || !data.cards.owner_id) {
  1052. return;
  1053. }
  1054. const ownerId = data.cards.owner_id;
  1055. if (data.cards.name) {
  1056. showNotification('Получена новая карта "' + data.cards.name + '"');
  1057. }
  1058. const url = `${currentDomain}/engine/ajax/controller.php?mod=cards_ajax`;
  1059. const postData = new URLSearchParams({
  1060. action: "take_card",
  1061. owner_id: ownerId
  1062. });
  1063. fetch(url, {
  1064. method: "POST",
  1065. headers: {
  1066. "Content-Type": "application/x-www-form-urlencoded"
  1067. },
  1068. body: postData.toString()
  1069. })
  1070. .then(response => {
  1071. if (!response.ok) {
  1072. throw new Error(`HTTP error! Status: ${response.status}`);
  1073. }
  1074. return response.json();
  1075. })
  1076. .then(data => {
  1077. })
  1078. .catch(error => {
  1079. });
  1080. })
  1081. .catch(error => {
  1082. });
  1083. }
  1084.  
  1085. async function setCache(key, data, ttlInSeconds) {
  1086. const expires = Date.now() + ttlInSeconds * 1000;
  1087. const cacheData = { data, expires };
  1088. localStorage.setItem(key, JSON.stringify(cacheData));
  1089. }
  1090.  
  1091. async function getCache(key) {
  1092. const cacheData = JSON.parse(localStorage.getItem(key));
  1093. if (!cacheData) return null;
  1094. if (Date.now() > cacheData.expires) {
  1095. localStorage.removeItem(key);
  1096. return null;
  1097. }
  1098. return cacheData.data;
  1099. }
  1100.  
  1101. async function cacheCard(key, data) {
  1102. await setCache(key, data, 3600);
  1103. }
  1104.  
  1105. async function getCard(key) {
  1106. return await getCache(key);
  1107. }
  1108.  
  1109. function addClearButton() {
  1110. const filterControls = document.querySelector('.card-filter-form__controls');
  1111. if (!filterControls) {
  1112. return;
  1113. }
  1114. const inputField = filterControls.querySelector('.card-filter-form__search');
  1115. if (!inputField) {
  1116. return;
  1117. }
  1118. const searchButton = filterControls.querySelector('.tabs__search-btn');
  1119. if (!searchButton) {
  1120. return;
  1121. }
  1122. inputField.addEventListener('keydown', function (event) {
  1123. if (event.key === 'Enter') {
  1124. event.preventDefault();
  1125. searchButton.click();
  1126. }
  1127. });
  1128. const clearButton = document.createElement('button');
  1129. clearButton.innerHTML = '<i class="fas fa-times"></i>';
  1130. clearButton.classList.add('clear-search-btn');
  1131. clearButton.style.margin = '5px';
  1132. clearButton.style.position = 'absolute';
  1133. clearButton.style.padding = '10px';
  1134. clearButton.style.background = 'red';
  1135. clearButton.style.color = 'white';
  1136. clearButton.style.border = 'none';
  1137. clearButton.style.cursor = 'pointer';
  1138. clearButton.style.boxShadow = '0 2px 5px rgba(0, 0, 0, 0.2)';
  1139. clearButton.style.fontSize = '14px';
  1140. clearButton.style.borderRadius = '5px';
  1141. clearButton.addEventListener('click', function () {
  1142. inputField.value = '';
  1143. searchButton.click();
  1144. });
  1145. inputField.style.marginLeft = '30px';
  1146. inputField.parentNode.insertBefore(clearButton, inputField);
  1147. }
  1148.  
  1149. function showNotification(message) {
  1150. const notification = document.createElement('div');
  1151. notification.style.cssText = `
  1152. position: fixed;
  1153. top: 20px;
  1154. left: 50%;
  1155. transform: translateX(-50%);
  1156. background: linear-gradient(45deg, #6c5ce7, #a367dc);
  1157. color: white;
  1158. padding: 12px 24px;
  1159. border-radius: 8px;
  1160. box-shadow: 0 4px 15px rgba(0,0,0,0.2);
  1161. z-index: 9999;
  1162. animation: slideDown 0.5s ease, fadeOut 0.5s ease 2.5s forwards;
  1163. font-size: 14px;
  1164. `;
  1165. notification.textContent = message;
  1166. document.body.appendChild(notification);
  1167. setTimeout(() => notification.remove(), 3000);
  1168. }
  1169.  
  1170. function createProgressBar() {
  1171. const progressBar = document.createElement('div');
  1172. progressBar.className = 'progress-bar';
  1173. progressBar.innerHTML = `
  1174. <div class="progress-bar__fill"></div>
  1175. <div class="progress-bar__text">0%</div>
  1176. `;
  1177.  
  1178. const style = document.createElement('style');
  1179. style.textContent = `
  1180. .progress-bar {
  1181. position: fixed;
  1182. top: 0;
  1183. left: 0;
  1184. width: 100%;
  1185. height: 4px;
  1186. background: #ddd;
  1187. z-index: 10000;
  1188. }
  1189. .progress-bar__fill {
  1190. width: 0%;
  1191. height: 100%;
  1192. background: linear-gradient(to right, #6c5ce7, #a367dc);
  1193. transition: width 0.3s ease;
  1194. position: relative;
  1195. }
  1196. .progress-bar__text {
  1197. position: absolute;
  1198. left: 50%;
  1199. top: 5px;
  1200. transform: translateX(-50%);
  1201. color: #ffffff;
  1202. font-size: 14px;
  1203. font-weight: bold;
  1204. background: #6c5ce7;
  1205. padding: 2px 8px;
  1206. border-radius: 10px;
  1207. box-shadow: 0 2px 4px rgba(0,0,0,0.2);
  1208. text-shadow: 1px 1px 2px rgba(0,0,0,0.3);
  1209. }
  1210. `;
  1211. document.head.appendChild(style);
  1212. document.body.appendChild(progressBar);
  1213.  
  1214. return {
  1215. update: (current, total) => {
  1216. const percentage = Math.round((current / total) * 100);
  1217. progressBar.querySelector('.progress-bar__fill').style.width = percentage + '%';
  1218. progressBar.querySelector('.progress-bar__text').textContent = percentage + '%';
  1219. },
  1220. remove: () => progressBar.remove()
  1221. };
  1222. }
  1223.  
  1224. function clearCardCache() {
  1225. const keys = Object.keys(localStorage);
  1226. let clearedCount = 0;
  1227.  
  1228. keys.forEach(key => {
  1229. if (key.startsWith('cardId: ')) {
  1230. localStorage.removeItem(key);
  1231. clearedCount++;
  1232. }
  1233. });
  1234.  
  1235. showNotification(`Очищено ${clearedCount} карточек из кеша`);
  1236. }
  1237.  
  1238. function addClearCacheButton() {
  1239. if (window.location.pathname.includes('/pm/') ||
  1240. window.location.pathname.includes('emotions.php') ||
  1241. window.frameElement) {
  1242. return;
  1243. }
  1244.  
  1245. if (!document.querySelector('#clearCacheButton')) {
  1246. const button = getButton('clearCacheButton', 'trash', 63, 'Очистить кеш карт', clearCardCache);
  1247. document.body.appendChild(button);
  1248. }
  1249. }
  1250.  
  1251. (function() {
  1252. 'use strict';
  1253.  
  1254. function checkTimeAndReset() {
  1255. const now = new Date();
  1256. const mskTime = new Date(now.getTime() + (now.getTimezoneOffset() + 180) * 60000);
  1257. const hours = mskTime.getHours();
  1258. const today = mskTime.toISOString().split('T')[0];
  1259.  
  1260. const userHash = window.dle_login_hash;
  1261. if (!userHash) return;
  1262.  
  1263. const stopKey = 'checkCardStopped' + userHash;
  1264. const stopValue = localStorage.getItem(stopKey);
  1265.  
  1266. if (hours >= 0 && stopValue) {
  1267. localStorage.removeItem(stopKey);
  1268. console.log('Сброс ограничения сбора карт выполнен');
  1269.  
  1270. checkNewCard();
  1271. setTimeout(checkNewCard, 2000);
  1272. setTimeout(checkNewCard, 5000);
  1273. }
  1274. }
  1275.  
  1276. function initializeScript() {
  1277. checkTimeAndReset();
  1278.  
  1279. setInterval(autoRepeatCheck, 2000);
  1280. setInterval(startPing, 31000);
  1281. setInterval(checkNewCard, 10000);
  1282. setInterval(checkTimeAndReset, 60000);
  1283.  
  1284. setInterval(() => {
  1285. if (!document.querySelector('#processCards')) {
  1286. addUpdateButton();
  1287. addClearButton();
  1288. addClearCacheButton();
  1289. }
  1290. }, 500);
  1291.  
  1292. $('#tg-banner').remove();
  1293. localStorage.setItem('notify18', 'closed');
  1294. localStorage.setItem('hideTelegramAs', 'true');
  1295. }
  1296.  
  1297. if (document.readyState === 'loading') {
  1298. document.addEventListener('DOMContentLoaded', initializeScript);
  1299. } else {
  1300. initializeScript();
  1301. }
  1302.  
  1303. window.addEventListener('pageshow', initializeScript);
  1304. })();
  1305.  
  1306. (function() {
  1307. 'use strict';
  1308.  
  1309. let lastActiveTime = localStorage.getItem('lastCrystalTime') || "00:00";
  1310. let processedCrystals = new Set(JSON.parse(localStorage.getItem('processedCrystals') || '[]'));
  1311. let workerBlob = null;
  1312. let worker = null;
  1313.  
  1314. function getCurrentTime() {
  1315. const now = new Date();
  1316. return now.getHours().toString().padStart(2, '0') + ":" + now.getMinutes().toString().padStart(2, '0');
  1317. }
  1318.  
  1319. function getCrystalId(msg) {
  1320. const timeElement = msg.querySelector(".lc_chat_li_date");
  1321. const author = msg.querySelector(".lc_chat_li_autor");
  1322. if (timeElement && author) {
  1323. return `${author.textContent.trim()}_${timeElement.textContent.trim()}`;
  1324. }
  1325. return null;
  1326. }
  1327.  
  1328. function initializeWorker() {
  1329. if (worker) return;
  1330.  
  1331. if (typeof Worker === 'undefined') {
  1332. console.log('Web Workers не поддерживаются в этом браузере');
  1333. setInterval(clickOnCrystal, 1000);
  1334. setInterval(preventTimeout, 2000);
  1335. setInterval(detectInactiveCrystal, 4000);
  1336. setInterval(() => {
  1337. const now = new Date();
  1338. if (now.getHours() === 0 && now.getMinutes() === 0) {
  1339. resetTimerAtMidnight();
  1340. }
  1341. }, 60000);
  1342. return;
  1343. }
  1344.  
  1345. try {
  1346. const workerCode = `
  1347. let intervalIds = [];
  1348.  
  1349. self.onmessage = function(e) {
  1350. if (e.data.action === 'start') {
  1351. clearAllIntervals();
  1352. startIntervals();
  1353. } else if (e.data.action === 'stop') {
  1354. clearAllIntervals();
  1355. }
  1356. };
  1357.  
  1358. function clearAllIntervals() {
  1359. intervalIds.forEach(id => clearInterval(id));
  1360. intervalIds = [];
  1361. }
  1362.  
  1363. function startIntervals() {
  1364. intervalIds.push(setInterval(() => {
  1365. self.postMessage({ type: 'checkCrystal' });
  1366. }, 1000));
  1367.  
  1368. intervalIds.push(setInterval(() => {
  1369. self.postMessage({ type: 'checkTimeout' });
  1370. }, 2000));
  1371.  
  1372. intervalIds.push(setInterval(() => {
  1373. self.postMessage({ type: 'checkInactive' });
  1374. }, 4000));
  1375.  
  1376. intervalIds.push(setInterval(() => {
  1377. const now = new Date();
  1378. if (now.getHours() === 0 && now.getMinutes() === 0) {
  1379. self.postMessage({ type: 'midnight' });
  1380. }
  1381. }, 60000));
  1382. }
  1383. `;
  1384.  
  1385. workerBlob = new Blob([workerCode], { type: 'application/javascript' });
  1386. worker = new Worker(URL.createObjectURL(workerBlob));
  1387.  
  1388. worker.onmessage = function(e) {
  1389. switch(e.data.type) {
  1390. case 'checkCrystal':
  1391. clickOnCrystal();
  1392. break;
  1393. case 'checkTimeout':
  1394. preventTimeout();
  1395. break;
  1396. case 'checkInactive':
  1397. detectInactiveCrystal();
  1398. break;
  1399. case 'midnight':
  1400. resetTimerAtMidnight();
  1401. break;
  1402. }
  1403. };
  1404.  
  1405. worker.onerror = function(error) {
  1406. console.log('Ошибка воркера:', error);
  1407. setInterval(clickOnCrystal, 1000);
  1408. setInterval(preventTimeout, 2000);
  1409. setInterval(detectInactiveCrystal, 4000);
  1410. setInterval(() => {
  1411. const now = new Date();
  1412. if (now.getHours() === 0 && now.getMinutes() === 0) {
  1413. resetTimerAtMidnight();
  1414. }
  1415. }, 60000);
  1416. };
  1417. } catch (error) {
  1418. console.log('Ошибка при создании воркера:', error);
  1419. setInterval(clickOnCrystal, 1000);
  1420. setInterval(preventTimeout, 2000);
  1421. setInterval(detectInactiveCrystal, 4000);
  1422. setInterval(() => {
  1423. const now = new Date();
  1424. if (now.getHours() === 0 && now.getMinutes() === 0) {
  1425. resetTimerAtMidnight();
  1426. }
  1427. }, 60000);
  1428. }
  1429. }
  1430.  
  1431. function clickOnCrystal() {
  1432. try {
  1433. const chatMessages = document.querySelectorAll(".lc_chat_li");
  1434. chatMessages.forEach(msg => {
  1435. const diamond = msg.querySelector("#diamonds-chat");
  1436. const timeElement = msg.querySelector(".lc_chat_li_date");
  1437.  
  1438. if (diamond && timeElement) {
  1439. const crystalId = getCrystalId(msg);
  1440. let messageTime = timeElement.textContent.trim();
  1441.  
  1442. if (messageTime >= lastActiveTime && !processedCrystals.has(crystalId)) {
  1443. diamond.click();
  1444. lastActiveTime = messageTime;
  1445. processedCrystals.add(crystalId);
  1446. try {
  1447. localStorage.setItem('lastCrystalTime', lastActiveTime);
  1448. localStorage.setItem('processedCrystals', JSON.stringify([...processedCrystals]));
  1449. } catch (e) {
  1450. console.log('Ошибка сохранения в localStorage:', e);
  1451. }
  1452. }
  1453. }
  1454. });
  1455. } catch (error) {
  1456. console.log('Ошибка в clickOnCrystal:', error);
  1457. }
  1458. }
  1459.  
  1460. function preventTimeout() {
  1461. const chatContainer = document.querySelector('.lc_chat');
  1462. if (chatContainer) {
  1463. try {
  1464. const moveEvent = new MouseEvent('mousemove', {
  1465. bubbles: true,
  1466. cancelable: true,
  1467. view: window,
  1468. clientX: 1,
  1469. clientY: 1
  1470. });
  1471. chatContainer.dispatchEvent(moveEvent);
  1472. } catch (e) {}
  1473. }
  1474.  
  1475. const timeoutButton = document.querySelector(".lc_chat_timeout_imback");
  1476. if (timeoutButton) {
  1477. timeoutButton.click();
  1478. }
  1479. }
  1480.  
  1481. function detectInactiveCrystal() {
  1482. const warning = document.querySelector(".DLEPush-notification.push-warning");
  1483. if (warning) {
  1484. const closeButton = warning.querySelector(".DLEPush-close");
  1485. if (closeButton) {
  1486. closeButton.click();
  1487. }
  1488. lastActiveTime = getCurrentTime();
  1489. localStorage.setItem('lastCrystalTime', lastActiveTime);
  1490. }
  1491. }
  1492.  
  1493. function resetTimerAtMidnight() {
  1494. const now = new Date();
  1495. if (now.getHours() === 0 && now.getMinutes() === 0) {
  1496. lastActiveTime = "00:00";
  1497. processedCrystals.clear();
  1498. localStorage.setItem('lastCrystalTime', lastActiveTime);
  1499. localStorage.setItem('processedCrystals', JSON.stringify([]));
  1500. }
  1501. }
  1502.  
  1503. initializeWorker();
  1504. worker.postMessage({ action: 'start' });
  1505.  
  1506. window.addEventListener('beforeunload', function() {
  1507. if (worker) {
  1508. worker.terminate();
  1509. URL.revokeObjectURL(workerBlob);
  1510. }
  1511. });
  1512.  
  1513. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址