Nedo Auto Contest Participation Script

Автоматом участвует в розыгрышах после ручного входа в первый розыгрыш.

  1. // ==UserScript==
  2. // @name Nedo Auto Contest Participation Script
  3. // @namespace http://tampermonkey.net/
  4. // @version 2.3
  5. // @description Автоматом участвует в розыгрышах после ручного входа в первый розыгрыш.
  6. // @author eretly
  7. // @match https://lolz.live/*
  8. // @match https://lolz.guru/*
  9. // @match https://zelenka.guru/*
  10. // @grant none
  11. // @license MIT
  12. // ==/UserScript==
  13.  
  14. (function() {
  15. 'use strict';
  16.  
  17. // Конфиг
  18. const CHECK_INTERVAL = 100; // 100 ms
  19. const PARTICIPATION_WAIT = 3000; // ms, ждем 3 секунды, прежде чем проверять скрытое состояние кнопки
  20. const SCROLL_ATTEMPTS = 2; // Количество попыток прокрутки
  21. const SCROLL_INTERVAL = 1000; // ms, интервал между попытками прокрутки
  22. const SCROLL_OFFSET = 150; // px, Выше (0+) / Ниже (-0) прокручивать страницу
  23. const DOUBLE_CHECK_DELAY = 500; // 400 ms, задержка перед повторной проверкой скролла
  24. const BEFORE_CLICK_DELAY = 100; // 100 ms, задержка перед нажатием на кнопку
  25. const AFTER_CLICK_DELAY = 300; // 300 ms, задержка после нажатия на кнопку
  26.  
  27. const STORAGE_KEY = 'nedoAutoContestListURL';
  28. const AUTO_MODE_KEY = 'nedoAutoContestMode';
  29. const FIRST_ENTRY_KEY = 'nedoFirstContestEntry';
  30. const SUCCESSFUL_PARTICIPATION_KEY = 'nedoSuccessfulParticipation';
  31.  
  32. function saveContestListURL(url) {
  33. sessionStorage.setItem(STORAGE_KEY, url);
  34. console.log('Сохранен URL списка розыгрышей с фильтрами:', url);
  35. }
  36.  
  37. function getSavedContestListURL() {
  38. return sessionStorage.getItem(STORAGE_KEY);
  39. }
  40.  
  41. function getContestListURL() {
  42. const hostname = window.location.hostname;
  43. switch (hostname) {
  44. case 'lolz.live':
  45. return 'https://lolz.live/forums/contests/';
  46. case 'lolz.guru':
  47. return 'https://lolz.guru/forums/contests/';
  48. case 'zelenka.guru':
  49. return 'https://zelenka.guru/forums/contests/';
  50. default:
  51. console.warn('Неизвестный домен:', hostname);
  52. return null;
  53. }
  54. }
  55.  
  56. function setAutoMode(value) {
  57. sessionStorage.setItem(AUTO_MODE_KEY, value.toString());
  58. console.log(value ? 'Авто-режим активирован' : 'Авто-режим деактивирован');
  59. }
  60.  
  61. function getAutoMode() {
  62. return sessionStorage.getItem(AUTO_MODE_KEY) === 'true';
  63. }
  64.  
  65. function setFirstEntry(value) {
  66. sessionStorage.setItem(FIRST_ENTRY_KEY, value.toString());
  67. }
  68.  
  69. function getFirstEntry() {
  70. return sessionStorage.getItem(FIRST_ENTRY_KEY) === 'true';
  71. }
  72.  
  73. function setSuccessfulParticipation(value) {
  74. sessionStorage.setItem(SUCCESSFUL_PARTICIPATION_KEY, value.toString());
  75. }
  76.  
  77. function getSuccessfulParticipation() {
  78. return sessionStorage.getItem(SUCCESSFUL_PARTICIPATION_KEY) === 'true';
  79. }
  80.  
  81. function updateContestListURL() {
  82. const contestListURL = getContestListURL();
  83. if (window.location.href.includes(contestListURL)) {
  84. saveContestListURL(window.location.href);
  85. if (isDirectAccess()) {
  86. resetAllStates();
  87. console.log('Обнаружен прямой вход на страницу розыгрышей. Все состояния сброшены.');
  88. }
  89. }
  90. }
  91.  
  92. function isDirectAccess() {
  93. return document.referrer === '' || !document.referrer.includes(window.location.hostname);
  94. }
  95.  
  96. function resetAllStates() {
  97. setFirstEntry(false);
  98. setAutoMode(false);
  99. setSuccessfulParticipation(false);
  100. }
  101.  
  102. function cleanUpContestPage() {
  103. let contestBlock = document.querySelector(".contestThreadBlock");
  104.  
  105. if (contestBlock) {
  106. let contentBlock = document.querySelector(".messageText.SelectQuoteContainer.baseHtml.ugc");
  107. let pollBlock = document.querySelector(".PollContainer");
  108.  
  109. if (contentBlock) contentBlock.remove();
  110. if (pollBlock) pollBlock.remove();
  111. }
  112. }
  113.  
  114. async function scrollToElement() {
  115. const selector = "a.LztContest--Participate";
  116. const element = await waitForElement(selector);
  117.  
  118. if (element) {
  119. const elementRect = element.getBoundingClientRect();
  120. const absoluteElementTop = elementRect.top + window.pageYOffset;
  121. const middleOfElement = absoluteElementTop + elementRect.height / 2;
  122. const scrollTo = middleOfElement - (window.innerHeight / 2) - SCROLL_OFFSET;
  123.  
  124. window.scrollTo({
  125. top: scrollTo,
  126. behavior: 'smooth'
  127. });
  128. console.log('Прокручено к элементу:', element);
  129.  
  130. setTimeout(() => {
  131. const newElementRect = element.getBoundingClientRect();
  132. if (newElementRect.top > SCROLL_OFFSET) {
  133. console.log('Элемент не на нужной позиции, корректировка...');
  134. window.scrollTo({
  135. top: window.pageYOffset + newElementRect.top - SCROLL_OFFSET,
  136. behavior: 'smooth'
  137. });
  138. }
  139. }, DOUBLE_CHECK_DELAY);
  140.  
  141. return true;
  142. } else {
  143. console.log('Элемент не найден после ожидания');
  144. return false;
  145. }
  146. }
  147.  
  148. function waitForElement(selector) {
  149. return new Promise((resolve) => {
  150. const interval = setInterval(() => {
  151. const element = document.querySelector(selector);
  152. if (element) {
  153. clearInterval(interval);
  154. resolve(element);
  155. }
  156. }, CHECK_INTERVAL);
  157. });
  158. }
  159. function clickNextContestLink() {
  160. const selector = ".discussionListItem a.listBlock.main.PreviewTooltip";
  161. const links = document.querySelectorAll(selector);
  162. for (let link of links) {
  163. if (!link.closest('.discussionListItem').classList.contains('participated')) {
  164. console.log('Кликаем по следующему розыгрышу:', link.href);
  165. link.click();
  166. return true;
  167. }
  168. }
  169. console.log('Не найдено больше розыгрышей для участия.');
  170. return false;
  171. }
  172.  
  173. function waitForParticipateButton() {
  174. return new Promise((resolve) => {
  175. const interval = setInterval(() => {
  176. const participateButton = document.querySelector('a.LztContest--Participate.button:not([disabled])');
  177. if (participateButton && participateButton.getAttribute('href').includes('token')) {
  178. clearInterval(interval);
  179. resolve(participateButton);
  180. }
  181. }, CHECK_INTERVAL);
  182. });
  183. }
  184.  
  185. async function participateInContest() {
  186. console.log('Ожидание доступности кнопки участия...');
  187. const participateButton = await waitForParticipateButton();
  188. console.log('Кнопка участия доступна, ожидаем перед кликом...');
  189.  
  190. await new Promise(resolve => setTimeout(resolve, BEFORE_CLICK_DELAY));
  191.  
  192. participateButton.click();
  193. console.log('Кликнули по кнопке участия');
  194.  
  195. await new Promise(resolve => setTimeout(resolve, AFTER_CLICK_DELAY));
  196.  
  197. console.log('Участие завершено, возвращаемся к списку розыгрышей');
  198. setSuccessfulParticipation(true);
  199.  
  200. const contestListURL = getSavedContestListURL();
  201. if (contestListURL) {
  202. window.location.href = contestListURL;
  203. } else {
  204. console.warn('Не удалось определить URL списка розыгрышей для возврата.');
  205. }
  206. }
  207.  
  208. async function init() {
  209. const currentURL = window.location.href;
  210. const contestListURL = getContestListURL();
  211.  
  212. if (currentURL.includes('/threads/')) {
  213. console.log('На странице розыгрыша, начинаем процесс участия...');
  214.  
  215. const elementFound = await scrollToElement();
  216.  
  217. if (elementFound) {
  218. if (!getFirstEntry()) {
  219. setFirstEntry(true);
  220. setAutoMode(true);
  221. console.log('Первый вход в розыгрыш, активируем авто-режим');
  222. }
  223.  
  224. if (getAutoMode()) {
  225. setTimeout(participateInContest, PARTICIPATION_WAIT);
  226. }
  227. } else {
  228. console.log('Элемент для прокрутки не найден, участие не будет выполнено.');
  229. }
  230. } else if (currentURL.includes(contestListURL)) {
  231. console.log('На странице списка розыгрышей, обновляем URL...');
  232. updateContestListURL();
  233.  
  234. if (getAutoMode() && getSuccessfulParticipation()) {
  235. setSuccessfulParticipation(false);
  236. setTimeout(() => {
  237. if (!clickNextContestLink()) {
  238. setAutoMode(false);
  239. setFirstEntry(false);
  240. console.log('Авто-режим выключен, так как не осталось розыгрышей для участия.');
  241. }
  242. }, 1000);
  243. }
  244. } else {
  245. console.log('На другой странице, скрипт не активирован.');
  246. }
  247. }
  248.  
  249. let lastUrl = location.href;
  250. new MutationObserver(() => {
  251. const url = location.href;
  252. if (url !== lastUrl) {
  253. lastUrl = url;
  254. init();
  255. }
  256. }).observe(document, { subtree: true, childList: true });
  257.  
  258. init();
  259. })();

QingJ © 2025

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