animestars Auto Helper

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

当前为 2025-06-02 提交的版本,查看 最新版本

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