animestars Auto Helper

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

当前为 2025-03-01 提交的版本,查看 最新版本

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