animestars Auto Helper

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

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
  1. // ==UserScript==
  2. // @name animestars Auto Helper
  3. // @namespace animestars.org
  4. // @version 3.23
  5. // @description хелпер который помогает определить популярность карты на сайте astars.club
  6. // @author astars lover
  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.  
  15. // ==/UserScript==
  16.  
  17. const DELAY = 40; // Задержка между запросами в миллисекундах (по умолчанию 0,5 секунды) не менять чтоб не делать избыточную нагрузку на сайт
  18.  
  19. const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
  20.  
  21. let cardCounter = 0;
  22.  
  23. const cardClasses = '.remelt__inventory-item, .lootbox__card, .anime-cards__item, .trade__inventory-item, .trade__main-item, .card-filter-list__card, .deck__item, .history__body-item, .history__body-item, .card-show__placeholder';
  24.  
  25. async function getCount(cardId, type) {
  26.  
  27. // Определяем текущий домен
  28. const currentDomain = window.location.origin;
  29.  
  30. let count = 0;
  31. let needResponse = await fetch(`${currentDomain}/cards/${cardId}/users/${type}/`);
  32. if (needResponse.status === 502) {
  33. console.error("Ошибка 502: Остановка выполнения скриптов.");
  34. throw new Error("502 Bad Gateway");
  35. }
  36. let needHtml = '';
  37. let needDoc = '';
  38. if (needResponse.ok) {
  39. needHtml = await needResponse.text();
  40. needDoc = new DOMParser().parseFromString(needHtml, 'text/html');
  41. count = needDoc.querySelectorAll('.profile__friends-item').length;
  42. } else {
  43. return count;
  44. }
  45.  
  46. const pagination = needDoc.querySelector('.pagination__pages');
  47. if (pagination && count >= 50) {
  48. const lastPageNum = pagination.querySelector('a:last-of-type');
  49. const totalPages = lastPageNum ? parseInt(lastPageNum.innerText, 10) : 1;
  50. if (totalPages > 1) {
  51. count = (totalPages - 1) * 50;
  52. }
  53. needResponse = await fetch(`${currentDomain}/cards/${cardId}/users/${type}/page/${totalPages}`);
  54. if (needResponse.status === 502) {
  55. console.error("Ошибка 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. function addCheckMark(element) {
  69. if (!element) return;
  70. const checkMark = document.createElement('i');
  71. checkMark.classList.add('fas', 'fa-check', 'div-marked');
  72. checkMark.style.position = 'absolute';
  73. checkMark.style.bottom = '5px';
  74. checkMark.style.left = '5px';
  75. checkMark.style.background = 'green';
  76. checkMark.style.color = 'white';
  77. checkMark.style.borderRadius = '50%';
  78. checkMark.style.padding = '5px';
  79. checkMark.style.fontSize = '16px';
  80. checkMark.style.width = '24px';
  81. checkMark.style.height = '24px';
  82. checkMark.style.display = 'flex';
  83. checkMark.style.alignItems = 'center';
  84. checkMark.style.justifyContent = 'center';
  85. element.classList.add('div-checked');
  86. if (window.getComputedStyle(element).position === 'static') {
  87. element.style.position = 'relative';
  88. }
  89. element.appendChild(checkMark);
  90. }
  91.  
  92. function addInCardMark(element, count) {
  93. if (!element) return;
  94. const checkMark = document.createElement('i');
  95. checkMark.classList.add('fal', 'fa-suitcase');
  96. checkMark.style.position = 'absolute';
  97. checkMark.style.bottom = '5px';
  98. checkMark.style.right = '5px';
  99. checkMark.style.background = 'green';
  100. checkMark.style.color = 'white';
  101. checkMark.style.borderRadius = '50%';
  102. checkMark.style.padding = '5px';
  103. checkMark.style.fontSize = '16px';
  104. checkMark.style.width = '34px';
  105. checkMark.style.height = '24px';
  106. checkMark.style.display = 'flex';
  107. checkMark.style.alignItems = 'center';
  108. checkMark.style.justifyContent = 'center';
  109. element.classList.add('div-checked');
  110. checkMark.title = 'Карт в корзине';
  111. if (window.getComputedStyle(element).position === 'static') {
  112. element.style.position = 'relative';
  113. }
  114. checkMark.innerText = ' ' + count;
  115. element.appendChild(checkMark);
  116. }
  117.  
  118. async function iNeedCard(cardId) {
  119. await sleep(DELAY * 2);
  120. const url = '/engine/ajax/controller.php?mod=trade_ajax';
  121. const data = {
  122. action: 'propose_add',
  123. type: 0,
  124. card_id: cardId,
  125. user_hash: dle_login_hash
  126. };
  127.  
  128. try {
  129. const response = await fetch(url, {
  130. method: 'POST',
  131. headers: {
  132. 'Content-Type': 'application/x-www-form-urlencoded',
  133. },
  134. credentials: 'same-origin',
  135. body: new URLSearchParams(data).toString()
  136. });
  137. if (response.status === 502) {
  138. console.error("Ошибка 502: Остановка выполнения скриптов.");
  139. throw new Error("502 Bad Gateway");
  140. }
  141. if (response.ok) {
  142. const data = await response.json();
  143. if (data.error) {
  144. if (data.error == 'Слишком часто, подождите пару секунд и повторите действие') {
  145. await readyToChargeCard(cardId);
  146. return;
  147. } else {
  148. DLEPush?.info(data.error);
  149. }
  150. }
  151. if ( data.status == 'added' ) {
  152. cardCounter++;
  153. return;
  154. }
  155. if ( data.status == 'deleted' ) {
  156. await sleep(DELAY * 2);
  157. await iNeedCard(cardId);
  158. return;
  159. }
  160. cardCounter++;
  161. } else {
  162. console.error('Ошибка запроса:', response.status);
  163. }
  164. } catch (error) {
  165. console.error('Ошибка выполнения POST-запроса:', error);
  166. }
  167. }
  168.  
  169. async function loadCard(cardId) {
  170. const cacheKey = 'cardId: ' + cardId;
  171. let card = await getCard(cacheKey) ?? {};
  172. if (Object.keys(card).length) {
  173. return card;
  174. }
  175.  
  176. // console.log(`Обработка карточки с ID: ${cardId}`);
  177. const currentDomain = window.location.origin;
  178. await sleep(DELAY);
  179. let needCount = await getCount(cardId, 'need');
  180. await sleep(DELAY);
  181. let tradeCount = await getCount(cardId, 'trade');
  182. await sleep(DELAY);
  183. const popularityResponse = await fetch(`${currentDomain}/cards/${cardId}/users/`);
  184. if (popularityResponse.status === 502) {
  185. console.error("Ошибка 502: Остановка выполнения скриптов.");
  186. throw new Error("502 Bad Gateway");
  187. }
  188. let likes = 0;
  189. let dislikes = 0;
  190. let popularityCount = 0;
  191. let rankText = '';
  192. if (popularityResponse.ok) {
  193. const popularityHtml = await popularityResponse.text();
  194. const popularityDoc = new DOMParser().parseFromString(popularityHtml, 'text/html');
  195. const rankElement = popularityDoc.querySelector('.anime-cards__rank');
  196. if (rankElement) {
  197. rankText = rankElement.textContent.trim();
  198. }
  199. await checkGiftCard(popularityDoc); // ищем небесный камень заодно
  200. const animeUrl = popularityDoc.querySelector('.card-show__placeholder')?.href;
  201. if (animeUrl) {
  202. try {
  203. const response = await fetch(animeUrl);
  204. if (!response.ok) {
  205. throw new Error(`Ошибка HTTP: ${response.status}`);
  206. }
  207. const htmlText = await response.text();
  208. const parser = new DOMParser();
  209. const doc = parser.parseFromString(htmlText, 'text/html');
  210. likes = parseInt(doc.querySelector('[data-likes-id]')?.textContent?.trim(), 10);
  211. dislikes = parseInt(doc.querySelector('[data-dislikes-id]')?.textContent?.trim(), 10);
  212. checkGiftCard(doc); // ищем небесный камень заодно
  213. } catch (error) {
  214. console.error('Ошибка при загрузке страницы:', error);
  215. }
  216. }
  217. popularityCount = popularityDoc.querySelectorAll('.card-show__owner').length;
  218. const pagination = popularityDoc.querySelector('.pagination__pages');
  219. if (pagination) {
  220. const lastPageNum = pagination.querySelector('a:last-of-type');
  221. const totalPages = lastPageNum ? parseInt(lastPageNum.innerText, 10) : 1;
  222. if (totalPages > 1 && popularityCount >= 35) {
  223. popularityCount = (totalPages - 1) * 35;
  224. const needResponse = await fetch(`${currentDomain}/cards/${cardId}/users/page/${totalPages}`);
  225. if (needResponse.status === 502) {
  226. console.error("Ошибка 502: Остановка выполнения скриптов.");
  227. throw new Error("502 Bad Gateway");
  228. }
  229. if (needResponse.ok) {
  230. const lastPageDoc = new DOMParser().parseFromString(await needResponse.text(), 'text/html');
  231. await checkGiftCard(lastPageDoc); // ищем небесный камень заодно
  232. popularityCount += lastPageDoc.querySelectorAll('.card-show__owner').length;
  233. }
  234. }
  235. }
  236. }
  237.  
  238. card = {likes: likes, dislikes: dislikes, rankText: rankText, popularityCount: popularityCount, needCount: needCount, tradeCount: tradeCount};
  239.  
  240. if (card.likes || card.dislikes) {
  241. await cacheCard(cacheKey, card)
  242. }
  243.  
  244. return card;
  245. }
  246.  
  247. async function updateCardInfo(cardId, element) {
  248. if (!cardId || !element) {
  249. console.log(cardId, 'updateCardInfo error');
  250. return;
  251. }
  252. try {
  253. const card = await loadCard(cardId);
  254. console.log(card);
  255.  
  256. element.querySelector('.link-icon')?.remove();
  257. const icon = document.createElement('div');
  258. icon.className = 'link-icon';
  259. icon.style.position = 'absolute';
  260. icon.style.top = '10px';
  261. icon.style.right = '10px';
  262. icon.style.backgroundColor = 'rgba(0, 0, 0, 0.6)';
  263. icon.style.color = '#05ed5b';
  264. icon.style.padding = '5px';
  265. icon.style.borderRadius = '5px';
  266. icon.style.fontSize = '8px';
  267. const anime = card.likes && card.dislikes ? `<br>аниме: +${card.likes} / -${card.dislikes}` : '';
  268. icon.innerHTML = `Ранг: ${card.rankText}<br>имеют: ${card.popularityCount}<br>хотят: ${card.needCount}<br>отдают: ${card.tradeCount}` + anime;
  269. element.style.position = 'relative';
  270. element.appendChild(icon);
  271. } catch (error) {
  272. console.error(`Ошибка обработки карты ${cardId}:`, error);
  273. throw error;
  274. }
  275. }
  276.  
  277. function clearMarkFromCards() {
  278. cleanByClass('div-marked');
  279. }
  280.  
  281. function removeAllLinkIcons() {
  282. cleanByClass('link-icon');
  283. }
  284.  
  285. function cleanByClass(className) {
  286. const list = document.querySelectorAll('.' + className);
  287. list.forEach(item => item.remove());
  288. }
  289.  
  290. function getCardsOnPage() {
  291. return Array.from(
  292. document.querySelectorAll(cardClasses)
  293. ).filter(card => card.offsetParent !== null);
  294. }
  295.  
  296. async function processCards() {
  297.  
  298. if (isCardRemeltPage()) {
  299. const storedData = JSON.parse(localStorage.getItem('animeCardsData')) || {};
  300. if (Object.keys(storedData).length < 1) {
  301. await readyRemeltCards();
  302. return;
  303. }
  304. }
  305.  
  306. removeMatchingWatchlistItems();
  307. removeAllLinkIcons();
  308. clearMarkFromCards();
  309.  
  310. const cards = getCardsOnPage();
  311. let counter = cards.length;
  312.  
  313. if (!counter) {
  314. return;
  315. }
  316.  
  317. let buttonId = 'processCards';
  318. startAnimation(buttonId);
  319. updateButtonCounter(buttonId, counter);
  320. for (const card of cards) {
  321.  
  322. if (card.classList.contains('trade__inventory-item--lock') || card.classList.contains('remelt__inventory-item--lock')) {
  323. continue;
  324. }
  325. let cardId = await getCardId(card);
  326. if (cardId) {
  327. await updateCardInfo(cardId, card).catch(error => {
  328. console.error("Остановка из-за критической ошибки:", error.message);
  329. return;
  330. });
  331. addCheckMark(card);
  332. counter--;
  333. updateButtonCounter(buttonId, counter);
  334. } else {
  335. console.log(cardId, 'cardId not found');
  336. }
  337.  
  338. if (card.classList.contains('lootbox__card')) {
  339. card.addEventListener('click', removeAllLinkIcons);
  340. }
  341. }
  342. stopAnimation(buttonId);
  343. }
  344.  
  345. function removeMatchingWatchlistItems() {
  346. const watchlistItems = document.querySelectorAll('.watchlist__item');
  347. if (watchlistItems.length == 0) {
  348. return;
  349. }
  350. watchlistItems.forEach(item => {
  351. const episodesText = item.querySelector('.watchlist__episodes')?.textContent.trim();
  352. if (episodesText) {
  353. const matches = episodesText.match(/[\d]+/g);
  354. if (matches) {
  355. const currentEpisode = parseInt(matches[0], 10);
  356. const totalEpisodes = parseInt(matches.length === 4 ? matches[3] : matches[1], 10);
  357. if (currentEpisode === totalEpisodes) {
  358. item.remove();
  359. //console.log(`Удалён блок: ${item}`);
  360. }
  361. }
  362. }
  363. });
  364.  
  365. if (watchlistItems.length) {
  366. DLEPush?.info('Из списка удалены просмотренные аниме. В списке осталось ' + document.querySelectorAll('.watchlist__item').length + ' записей.');
  367. }
  368. }
  369.  
  370. function startAnimation(id) {
  371. $('#' + id + ' span:first').css('animation', 'rotateIcon 2s linear infinite');
  372. }
  373.  
  374. function stopAnimation(id) {
  375. $('#' + id + ' span:first').css('animation', '');
  376. }
  377.  
  378. function getButton(id, className, percent, text, clickFunction) {
  379. const button = document.createElement('button');
  380. button.id = id;
  381. button.title = text;
  382. button.style.position = 'fixed';
  383. button.style.top = percent + '%';
  384. button.style.right = '1%';
  385. button.style.zIndex = '1000';
  386. button.style.backgroundColor = '#007bff';
  387. button.style.color = '#fff';
  388. button.style.border = 'none';
  389. button.style.borderRadius = '5px';
  390. button.style.padding = '10px 15px';
  391. button.style.cursor = 'pointer';
  392. button.style.boxShadow = '0 2px 5px rgba(0, 0, 0, 0.2)';
  393. const icon = document.createElement('span');
  394. icon.className = 'fal fa-' + className;
  395. icon.style.display = 'inline-block';
  396. button.appendChild(icon);
  397. const info = document.createElement('span');
  398. info.id = id + '_counter';
  399. info.className = 'guest__notification';
  400. info.style.display = 'none';
  401. button.appendChild(info);
  402. button.addEventListener('click', clickFunction);
  403. return button;
  404. }
  405.  
  406. function updateButtonCounter(id, counter) {
  407. let c = $('#' + id + '_counter');
  408. c.css('display', counter > 0 ? 'flex' : 'none');
  409. c.text(counter);
  410. }
  411.  
  412. function addUpdateButton() {
  413. if (!document.querySelector('#fetchLinksButton')) {
  414. let cards = getCardsOnPage();
  415.  
  416. document.body.appendChild(getButton('processCards', 'rocket', 37, 'Сравнить карточки', processCards));
  417.  
  418. if (!cards.length) {
  419. return
  420. }
  421.  
  422. let myCardPage = isMyCardPage();
  423. if (myCardPage) {
  424. document.body.appendChild(getButton('readyToCharge', 'circle-check', 50, '"Готов поменять" на все карточки', readyToCharge));
  425. }
  426.  
  427. let animePage = isAnimePage();
  428. if (animePage) {
  429. document.body.appendChild(getButton('iNeedAllThisCards', 'search', 50, '"Хочу карту" на все карточки', iNeedAllThisCards));
  430. }
  431.  
  432. let cardRemeltPage = isCardRemeltPage();
  433. if (cardRemeltPage) {
  434. document.body.appendChild(getButton('readyRemeltCards', 'yin-yang', 50, 'закешировать карточки', readyRemeltCards));
  435. const storedData = JSON.parse(localStorage.getItem('animeCardsData')) || {};
  436. updateButtonCounter('readyRemeltCards', Object.keys(storedData).length);
  437. }
  438.  
  439. if (document.querySelectorAll(`[data-id][data-name]`).length) {
  440. let percent = myCardPage || cardRemeltPage || cardRemeltPage ? 63 : 50;
  441. document.body.appendChild(getButton('checkCardsCountInCart', 'suitcase', percent, 'проверить наличие в корзине', checkCardsCountInCart));
  442. }
  443. }
  444. }
  445.  
  446. async function checkCardsCountInCart() {
  447. let cardsCountInCart = document.querySelectorAll(`[data-id][data-name]`);
  448. if (cardsCountInCart.length < 1) {
  449. return;
  450. }
  451.  
  452. let buttonId = 'checkCardsCountInCart';
  453. startAnimation(buttonId);
  454. let counter = cardsCountInCart.length;
  455. updateButtonCounter(buttonId, counter);
  456.  
  457. for (const card of cardsCountInCart) {
  458. let name = card?.getAttribute("data-name");
  459. let id = card?.getAttribute("data-id");
  460. if (!name || !id) {
  461. continue;
  462. }
  463. let num = await getCardsCountInCart(name, id);
  464. addInCardMark(card, num);
  465. counter--;
  466. updateButtonCounter(buttonId, counter);
  467. }
  468. stopAnimation(buttonId);
  469. }
  470.  
  471. function isMyCardPage() {
  472. return (/^\/user\/(.*)\/cards(\/page\/\d+\/)?/).test(window.location.pathname)
  473. }
  474.  
  475. function isCardRemeltPage() {
  476. return (/^\/cards_remelt\//).test(window.location.pathname)
  477. }
  478.  
  479. function isAnimePage() {
  480. return document.getElementById('anime-data') !== null;
  481. }
  482.  
  483. async function readyRemeltCards() {
  484. DLEPush.info('Кеширую все карты так как иначе на этой странице не получится их определить рейтинги');
  485. // получить все карты пользователя и запомнить ассоциации номеров карт в локальный кеш
  486. const linkElement = document.querySelector('a.button.button--left-icon.mr-3');
  487. const href = linkElement ? linkElement.href : null;
  488. if (!href) {
  489. return;
  490. }
  491. removeMatchingWatchlistItems();
  492. removeAllLinkIcons();
  493. clearMarkFromCards();
  494. const cards = getCardsOnPage();
  495. let counter = cards.length;
  496. if (!counter) {
  497. return;
  498. }
  499. let buttonId = 'readyRemeltCards';
  500. startAnimation(buttonId);
  501. updateButtonCounter(buttonId, 0);
  502. await scrapeAllPages(href, buttonId);
  503. stopAnimation(buttonId);
  504. }
  505.  
  506. async function scrapeAllPages(firstPageHref, buttonId) {
  507. const response = await fetch(firstPageHref);
  508. if (!response.ok) {
  509. throw new Error(`Ошибка HTTP: ${response.status}`);
  510. }
  511. const firstPageDoc = new DOMParser().parseFromString(await response.text(), 'text/html');
  512. const pagination = firstPageDoc.querySelector('#pagination');
  513. if (!pagination) {
  514. console.log('Пагинация не найдена');
  515. return;
  516. }
  517. let storedData = JSON.parse(localStorage.getItem('animeCardsData')) || {};
  518. const titleElement = firstPageDoc.querySelector('h1.secondary-title.text-center');
  519. if (titleElement) {
  520. const match = titleElement.textContent.match(/\((\d+)\s*шт\.\)/);
  521. const cardsCount = match ? parseInt(match[1], 10) : -1;
  522. if (cardsCount == Object.keys(storedData).length) {
  523. DLEPush.info('На данный момент в кеше карточек ровно столько же сколько в профиле пользователя');
  524. return;
  525. }
  526. }
  527.  
  528. // Получаем ссылку на последнюю страницу
  529. const lastPageLink = pagination.querySelector('a:last-of-type');
  530. if (!lastPageLink) {
  531. console.log('Последняя страница не найдена');
  532. return;
  533. }
  534. const lastPageNumber = parseInt(lastPageLink.textContent.trim(), 10);
  535. if (isNaN(lastPageNumber)) {
  536. console.log('Не удалось определить номер последней страницы');
  537. return;
  538. }
  539. updateButtonCounter(buttonId, lastPageNumber);
  540. // console.log(`Обнаружено страниц: ${lastPageNumber}`);
  541. // clear data
  542. localStorage.removeItem('animeCardsData');
  543. storedData = JSON.parse(localStorage.getItem('animeCardsData')) || {};
  544. // Функция для обработки карточек на странице
  545. async function processCardsToLocalstorage(doc, pageNum) {
  546. const cards = doc.querySelectorAll('.anime-cards__item');
  547. cards.forEach(card => {
  548. const cardId = card.getAttribute('data-id');
  549. const ownerId = card.getAttribute('data-owner-id');
  550. const name = card.getAttribute('data-name');
  551. const rank = card.getAttribute('data-rank');
  552. const animeLink = card.getAttribute('data-anime-link');
  553. const image = card.getAttribute('data-image');
  554. const ownerKey = 'o_' + ownerId;
  555. if (!ownerId || !cardId) return;
  556. if (!storedData[ownerKey]) {
  557. storedData[ownerKey] = []; // Если ключа нет, создаем пустой массив
  558. }
  559. storedData[ownerKey].push({ cardId, name, rank, animeLink, image, ownerId });
  560. });
  561. // console.log(`Обработано ${cards.length} карточек на странице: ` + pageNum + ', всего к сохранению: ' + Object.keys(storedData).length);
  562. }
  563.  
  564. async function fetchPage(url) {
  565. try {
  566. const response = await fetch(url);
  567. if (!response.ok) throw new Error(`Ошибка загрузки страницы ${url}`);
  568. return await response.text();
  569. } catch (error) {
  570. console.error(error);
  571. return null;
  572. }
  573. }
  574.  
  575. processCardsToLocalstorage(firstPageDoc, 1);
  576. updateButtonCounter(buttonId, lastPageNumber);
  577.  
  578. if (lastPageNumber > 1) {
  579. const parser = new DOMParser();
  580. for (let i = 2; i <= lastPageNumber; i++) {
  581. const pageUrl = lastPageLink.href.replace(/page\/\d+/, `page/${i}`);
  582. // console.log(`Загружаем страницу ${i}: ${pageUrl}`);
  583. const pageHTML = await fetchPage(pageUrl);
  584. if (pageHTML) {
  585. processCardsToLocalstorage(parser.parseFromString(pageHTML, 'text/html'), i);
  586. }
  587. await new Promise(resolve => setTimeout(resolve, 3000)); // Ждем 3 секунды между запросами
  588. updateButtonCounter(buttonId, lastPageNumber - i);
  589. }
  590. }
  591.  
  592. // console.log('Данные сохранены в localStorage');
  593. localStorage.setItem('animeCardsData', JSON.stringify(storedData));
  594. updateButtonCounter(buttonId, Object.keys(storedData).length);
  595.  
  596. document.body.appendChild(getButton('processCards', 'rocket', 37, 'Сравнить карточки', processCards));
  597. await processCards();
  598. }
  599.  
  600. async function iNeedAllThisCards() {
  601. let cards = getCardsOnPage();
  602. DLEPush.info('Отметить "Хочу карточку" на все ' + cards.length + ' карточек на странице');
  603.  
  604. let counter = cards.length;
  605. let buttonId = 'iNeedAllThisCards';
  606. startAnimation(buttonId);
  607. updateButtonCounter(buttonId, counter);
  608. clearMarkFromCards();
  609.  
  610. cardCounter = 0;
  611. for (const card of cards) {
  612. if (card.classList.contains('anime-cards__owned-by-user')) {
  613. counter--;
  614. updateButtonCounter(buttonId, counter);
  615. continue;
  616. }
  617. let cardId = await getCardId(card);
  618. if (cardId) {
  619. await iNeedCard(cardId).catch(error => {
  620. console.error("Остановка из-за критической ошибки:", error.message);
  621. return;
  622. });
  623. addCheckMark(card);
  624. counter--;
  625. updateButtonCounter(buttonId, counter);
  626. } else {
  627. console.log(cardId, 'cardId not found');
  628. }
  629. }
  630. stopAnimation(buttonId);
  631. }
  632.  
  633. async function getCardId(card) {
  634. let cardId = card.getAttribute('card-id') || card.getAttribute('data-card-id') || card.getAttribute('data-id');
  635. const href = card.getAttribute('href');
  636. if (href) {
  637. let cardIdMatch = href.match(/\/cards\/(\d+)\/users\//);
  638. if (cardIdMatch) {
  639. cardId = cardIdMatch[1];
  640. }
  641. }
  642. if (cardId) {
  643. // проверяем что в локально нет такого номера
  644. console.log('проверка в хранилище номера карты ' + cardId);
  645. const cardByOwner = await getFirstCardByOwner(cardId);
  646. // console.log('localStorage', cardByOwner);
  647. if (cardByOwner) {
  648. cardId = cardByOwner.cardId;
  649. }
  650. }
  651. return cardId;
  652. }
  653.  
  654. async function getCardType(card) {
  655. return card.getAttribute('data-type') || null;
  656. }
  657.  
  658. async function getFirstCardByOwner(ownerId) {
  659. const storedData = JSON.parse(localStorage.getItem('animeCardsData')) || {};
  660. const key = 'o_' + ownerId;
  661.  
  662. return storedData[key] && storedData[key].length > 0 ? storedData[key][0] : null;
  663. }
  664.  
  665. async function readyToCharge() {
  666. DLEPush.info('Отмечаем все карты на странице как: "Готов обменять" кроме тех что на обмене и заблокированных');
  667. let cards = getCardsOnPage();
  668. // DLEPush.info('Карт на странице: ' + cards.length);
  669.  
  670. let counter = cards.length;
  671. let buttonId = 'readyToCharge';
  672. startAnimation(buttonId);
  673. updateButtonCounter(buttonId, counter);
  674. clearMarkFromCards();
  675.  
  676. cardCounter = 0;
  677. for (const card of cards) {
  678. if (card.classList.contains('trade__inventory-item--lock')) {
  679. continue;
  680. }
  681. let cardId = await getCardId(card);
  682. if (cardId) {
  683. await sleep(1000);
  684. //await readyToChargeCard(cardId);
  685. await cardProposeAdd(cardId)
  686. counter--;
  687. addCheckMark(card);
  688. updateButtonCounter(buttonId, counter);
  689. }
  690. }
  691. DLEPush.info('Отправили на обмен ' + cardCounter + ' карточек на странице');
  692. stopAnimation(buttonId);
  693. }
  694.  
  695. async function cardProposeAdd(card_id) {
  696. try {
  697. await sleep(DELAY * 3);
  698.  
  699. const data = await new Promise((resolve, reject) => {
  700. $.ajax({
  701. url: "/engine/ajax/controller.php?mod=trade_ajax",
  702. type: "post",
  703. data: {
  704. action: "propose_add",
  705. type: 1,
  706. card_id: card_id,
  707. user_hash: dle_login_hash
  708. },
  709. dataType: "json",
  710. cache: false,
  711. success: resolve,
  712. error: reject
  713. });
  714. });
  715.  
  716. if (data?.error) {
  717. if (data.error === 'Слишком часто, подождите пару секунд и повторите действие') {
  718. return await cardProposeAdd(card_id);
  719. }
  720. console.log('cardProposeAdd ' + card_id, data.error);
  721.  
  722. return false;
  723. }
  724.  
  725. if (data?.status == "added") {
  726. return true;
  727. }
  728.  
  729. if (data?.status == "deleted") {
  730. return await cardProposeAdd(card_id);
  731. }
  732.  
  733. return false;
  734.  
  735. } catch (e) {
  736. console.error("Ошибка запроса:", e);
  737. return false;
  738. }
  739. }
  740.  
  741. const readyToChargeCard = async (cardId) => {
  742. await sleep(DELAY * 2);
  743. const url = '/engine/ajax/controller.php?mod=trade_ajax';
  744. const data = {
  745. action: 'propose_add',
  746. type: 1,
  747. card_id: cardId,
  748. user_hash: dle_login_hash
  749. };
  750.  
  751. try {
  752. const response = await fetch(url, {
  753. method: 'POST',
  754. headers: {
  755. 'Content-Type': 'application/x-www-form-urlencoded',
  756. },
  757. credentials: 'same-origin',
  758. body: new URLSearchParams(data).toString()
  759. });
  760. if (response.status === 502) {
  761. console.error("Ошибка 502: Остановка выполнения скриптов.");
  762. throw new Error("502 Bad Gateway");
  763. }
  764. if (response.ok) {
  765. const data = await response.json();
  766. if (data.error) {
  767. if (data.error == 'Слишком часто, подождите пару секунд и повторите действие') {
  768. await readyToChargeCard(cardId);
  769. return;
  770. }
  771. }
  772. if ( data.status == 'added' ) {
  773. cardCounter++;
  774. return;
  775. }
  776. if ( data.status == 'deleted' ) {
  777. await readyToChargeCard(cardId);
  778. return;
  779. }
  780. cardCounter++;
  781. //console.log('Ответ сервера:', data);
  782. } else {
  783. console.error('Ошибка запроса:', response.status);
  784. }
  785. } catch (error) {
  786. console.error('Ошибка выполнения POST-запроса:', error);
  787. }
  788. };
  789.  
  790. // Анимация вращения в CSS
  791. const style = document.createElement('style');
  792. style.textContent = `
  793. @keyframes rotateIcon {
  794. 0% {
  795. transform: rotate(0deg);
  796. }
  797. 100% {
  798. transform: rotate(360deg);
  799. }
  800. }
  801. `;
  802. document.head.appendChild(style);
  803.  
  804. function clearIcons() {
  805. document.querySelector('.card-notification')?.click();
  806. }
  807.  
  808. function autoRepeatCheck() {
  809. clearIcons();
  810. checkGiftCard(document);
  811. document.querySelector('.adv_volume.volume_on')?.click();
  812.  
  813. Audio.prototype.play = function() {
  814. return new Promise(() => {});
  815. };
  816. }
  817.  
  818. async function checkGiftCard(doc) {
  819. const button = doc.querySelector('#gift-icon');
  820. if (!button) return;
  821.  
  822. const giftCode = button.getAttribute('data-code');
  823. if (!giftCode) return false;
  824.  
  825. try {
  826. const response = await fetch('/engine/ajax/controller.php?mod=gift_code_game', {
  827. method: 'POST',
  828. headers: {
  829. 'Content-Type': 'application/x-www-form-urlencoded'
  830. },
  831. credentials: 'same-origin',
  832. body: new URLSearchParams({
  833. code: giftCode,
  834. user_hash: dle_login_hash
  835. })
  836. });
  837. const data = await response.json();
  838. if (data.status === 'ok') {
  839. DLEPush.info(data.text);
  840. button.remove();
  841. }
  842. } catch (error) {
  843. console.error('Ошибка при проверке подарочной карты:', error);
  844. }
  845. }
  846.  
  847. async function startPing() {
  848. if (!dle_login_hash) {
  849. console.error("Переменная dle_login_hash не определена.");
  850. return;
  851. }
  852.  
  853. // Определяем текущий домен
  854. const currentDomain = window.location.origin;
  855.  
  856. try {
  857. await sleep(DELAY * 3);
  858. const user_count_timer_data = await new Promise((resolve, reject) => {
  859. $.ajax({
  860. url: "/engine/ajax/controller.php?mod=user_count_timer",
  861. type: "post",
  862. data: {
  863. user_hash: dle_login_hash
  864. },
  865. dataType: "json",
  866. cache: false,
  867. success: resolve,
  868. error: reject
  869. });
  870. });
  871. } catch (e) {
  872. console.error("Ошибка запроса:", e);
  873. return false;
  874. }
  875. }
  876.  
  877. async function checkNewCard() {
  878. const currentDateTime = new Date();
  879. // Получаем значение из глобальной переменной
  880. // const userHash = window.dle_login_hash;
  881.  
  882. if (!dle_login_hash) {
  883. console.error("Переменная dle_login_hash не определена.");
  884. return;
  885. }
  886.  
  887. const localStorageKey = 'checkCardStopped' + window.dle_login_hash; // Формат YYYY-MM-DDTHH
  888.  
  889. if (localStorage.getItem(localStorageKey) === currentDateTime.toISOString().slice(0, 13)) {
  890. console.log("Проверка карты уже остановлена на текущий час.");
  891. return;
  892. }
  893.  
  894. // Определяем текущий домен
  895. const currentDomain = window.location.origin;
  896.  
  897. try {
  898. await sleep(DELAY * 3);
  899.  
  900. const data = await new Promise((resolve, reject) => {
  901. $.ajax({
  902. url: "/engine/ajax/controller.php?mod=reward_card",
  903. type: "post",
  904. data: {
  905. action: "check_reward",
  906. user_hash: dle_login_hash
  907. },
  908. dataType: "json",
  909. cache: false,
  910. success: resolve,
  911. error: reject
  912. });
  913. });
  914.  
  915. if (data?.stop_reward === "yes") {
  916. console.log("Проверка карт остановлена на текущий час:", data.reason);
  917. localStorage.setItem(localStorageKey, currentDateTime.toISOString().slice(0, 13));
  918. return;
  919. }
  920.  
  921. if (!data.cards || !data.cards.owner_id) {
  922. return;
  923. }
  924.  
  925. if ( data.cards.name ) {
  926. DLEPush?.info('Получена новая карта "' + data.cards.name + '"');
  927. }
  928.  
  929. const ownerId = data.cards.owner_id;
  930. console.log("owner_id получен:", ownerId); // Выводим owner_id
  931.  
  932. $.ajax({
  933. url: "/engine/ajax/controller.php?mod=cards_ajax",
  934. type: "post",
  935. data: {
  936. action: "take_card",
  937. owner_id: ownerId
  938. },
  939. dataType: "json",
  940. cache: false,
  941. success: function () {},
  942. error: function () {}
  943. });
  944.  
  945. } catch (e) {
  946. console.error("Ошибка запроса:", e);
  947. return false;
  948. }
  949.  
  950.  
  951. }
  952.  
  953. async function setCache(key, data, ttlInSeconds) {
  954. const expires = Date.now() + ttlInSeconds * 1000; // Устанавливаем срок хранения
  955. const cacheData = { data, expires };
  956. localStorage.setItem(key, JSON.stringify(cacheData));
  957. }
  958.  
  959. async function getCardsCountInCart(name, id) {
  960. if (!name || !id) return;
  961.  
  962. try {
  963. await sleep(DELAY * 4);
  964. const searchUrl = document.querySelector('.lgn__btn-profile').getAttribute('href') + `cards/?search=${encodeURIComponent(name)}`;
  965. const response = await fetch(searchUrl);
  966. const html = new DOMParser().parseFromString(await response.text(), 'text/html');
  967. await checkGiftCard(html); // ищем небесный камень заодно
  968. const foundCards = html.querySelectorAll(`[data-id="${id}"]`);
  969.  
  970. return foundCards.length;
  971. } catch (err) {
  972. console.error("Ошибка при запросе:", err);
  973. return "❌";
  974. }
  975. }
  976.  
  977. async function getCache(key) {
  978. const cacheData = JSON.parse(localStorage.getItem(key));
  979. if (!cacheData) return null; // Если данных нет
  980. if (Date.now() > cacheData.expires) {
  981. localStorage.removeItem(key); // Если срок истёк, удаляем
  982. return null;
  983. }
  984. return cacheData.data;
  985. }
  986.  
  987. async function cacheCard(key, data) {
  988. await setCache(key, data, 86400); // Записываем данные на 24 часа (86400 секунд)
  989. }
  990.  
  991. async function getCard(key) {
  992. return await getCache(key); // Записываем данные на 24 часа (86400 секунд)
  993. }
  994.  
  995. function addClearButton() {
  996. const filterControls = document.querySelector('.card-filter-form__controls');
  997. if (!filterControls) {
  998. return;
  999. }
  1000. const inputField = filterControls.querySelector('.card-filter-form__search');
  1001. if (!inputField) {
  1002. return;
  1003. }
  1004. const searchButton = filterControls.querySelector('.tabs__search-btn');
  1005. if (!searchButton) {
  1006. return;
  1007. }
  1008. inputField.addEventListener('keydown', function (event) {
  1009. if (event.key === 'Enter') {
  1010. event.preventDefault();
  1011. searchButton.click();
  1012. }
  1013. });
  1014. const clearButton = document.createElement('button');
  1015. clearButton.innerHTML = '<i class="fas fa-times"></i>'; // Иконка Font Awesome
  1016. clearButton.classList.add('clear-search-btn'); // Добавляем класс для стилизации
  1017. clearButton.style.margin = '5px';
  1018. clearButton.style.position = 'absolute';
  1019. clearButton.style.padding = '10px';
  1020. clearButton.style.background = 'red';
  1021. clearButton.style.color = 'white';
  1022. clearButton.style.border = 'none';
  1023. clearButton.style.cursor = 'pointer';
  1024. clearButton.style.fontSize = '14px';
  1025. clearButton.style.borderRadius = '5px';
  1026. clearButton.addEventListener('click', function () {
  1027. inputField.value = '';
  1028. searchButton.click();
  1029. });
  1030. inputField.style.marginLeft = '30px';
  1031. inputField.parentNode.insertBefore(clearButton, inputField);
  1032. }
  1033.  
  1034. function addPromocodeBtn() {
  1035. const button = document.createElement('button');
  1036. button.innerText = 'Промокоды';
  1037. button.style.position = 'fixed';
  1038. button.style.bottom = '80px';
  1039. button.style.right = '0px';
  1040. button.style.zIndex = '1000';
  1041. button.style.background = 'rgb(0, 123, 255)';
  1042. button.style.fontSize = '8px';
  1043. button.style.cursor = 'pointer';
  1044. button.style.transform = 'rotateY(47deg);';
  1045. button.addEventListener('click', () => {
  1046. window.location.href = '/promo_codes';
  1047. });
  1048. document.body.appendChild(button);
  1049. }
  1050.  
  1051. (function() {
  1052. 'use strict';
  1053.  
  1054. setInterval(autoRepeatCheck, 2000);
  1055. setInterval(startPing, 31000);
  1056. setInterval(checkNewCard, 10000);
  1057.  
  1058. addUpdateButton();
  1059. addClearButton();
  1060. addPromocodeBtn();
  1061.  
  1062. document.getElementById('tg-banner')?.remove();
  1063. localStorage.setItem('notify18', 'closed');
  1064. localStorage.setItem('hideTelegramAs', 'true');
  1065.  
  1066. // авто открытие карт под аниме
  1067. // $('div .pmovie__related a.glav-s:first')?.click()?.remove();
  1068.  
  1069. // немного увеличиваем карты на списках чтоб читались надписи
  1070. document.querySelectorAll('.anime-cards__item-wrapper').forEach(el => {
  1071. el.style.setProperty('min-width', '20.28%');
  1072. });
  1073.  
  1074. })();
  1075.  
  1076. document.addEventListener('click', function (e) {
  1077. const target = e.target;
  1078.  
  1079. if (target.classList.contains('anime-cards__name')) {
  1080. const name = target.textContent.trim();
  1081. const searchUrl = document.querySelector('.lgn__btn-profile').getAttribute('href') + `cards/?search=${encodeURIComponent(name)}`;
  1082. window.open(searchUrl, '_blank');
  1083. }
  1084. });
  1085.  
  1086. const styleGlobal = document.createElement('style');
  1087. style.textContent = `
  1088. .anime-cards__name {
  1089. cursor: pointer;
  1090. text-decoration: underline;
  1091. }
  1092. `;
  1093. document.head.appendChild(styleGlobal);