Greasy Fork镜像 支持简体中文。

Lolzteam Multiaccount Finder

Your assistant in finding scammers on the forum

  1. // ==UserScript==
  2. // @name Lolzteam Multiaccount Finder
  3. // @version 2.7.2
  4. // @description Your assistant in finding scammers on the forum
  5. // @author vuchaev2015
  6. // @match https://zelenka.guru/*
  7. // @match https://lolz.live/*
  8. // @match https://lolz.guru/*
  9. // @match https://lzt.market/*
  10. // @icon https://www.google.com/s2/favicons?sz=64&domain=zelenka.guru
  11. // @grant GM_setValue
  12. // @grant GM_getValue
  13. // @grant GM.setValue
  14. // @grant GM_getValue
  15. // @grant GM_deleteValue
  16. // @grant GM.deleteValue
  17. // @namespace http://tampermonkey.net/
  18. // ==/UserScript==
  19.  
  20. // ВНИМАНИЕ ДАННЫЙ СКРИПТ СОДЕРЖИТ БОЛЬШОЕ КОЛИЧЕСТВО ГАВНОКОДА
  21. // ПОЖАЛУЙСТА, НЕ БЕЙТЕ. ГЛАВНОЕ, ЧТО ВСЁ РАБОТАЕТ!.
  22.  
  23. let domain = window.location.href.startsWith('https://lzt.market') ? 'lzt.market' :
  24. (window.location.href.startsWith('https://lolz.live') || window.location.href.startsWith('https://lolz.guru')) ? 'lolz.live' :
  25. 'zelenka.guru';
  26. logWithPrefix(domain)
  27. let items = [];
  28.  
  29. getCheckboxes(null, null, null, true).then(checkboxes => {
  30. items = checkboxes.map(checkbox => ({key: checkbox.id, value: checkbox.value}));
  31. });
  32.  
  33. async function deleteUncheckedItems(itemsParam = items) {
  34. for(let item of itemsParam) {
  35. if(item.key !== 'checked-list') {
  36. deleteItem(item.key);
  37. }
  38. }
  39. await addItems(items);
  40. }
  41.  
  42. addItems(items); // Создание значений при загрузке страницы, если их не существует
  43.  
  44. function createCases(bannedPercent, nonBannedUsersCount, bannedUsersCount, nonBannedPercent) {
  45. return [
  46. {
  47. condition: bannedPercent >= 80 && bannedUsersCount !== 0,
  48. imgSrc: "https://i.imgur.com/g5GxNHD.png",
  49. outputPrefix: "мошенник",
  50. tableSuffix: "scammers",
  51. sendMessage: true,
  52. isPossibleScammer: true
  53. },
  54. {
  55. condition: nonBannedUsersCount > 15 && bannedUsersCount < nonBannedUsersCount / 3,
  56. imgSrc: "https://i.imgur.com/o5qNA1o.png",
  57. outputPrefix: "использует VPN",
  58. tableSuffix: "vpn"
  59. },
  60. {
  61. condition: nonBannedUsersCount > 6 && nonBannedUsersCount <= 15 && bannedUsersCount < nonBannedUsersCount / 2,
  62. imgSrc: "https://i.imgur.com/o5qNA1o.png",
  63. outputPrefix: "возможно использует VPN",
  64. tableSuffix: "vpn"
  65. },
  66. {
  67. condition: bannedPercent >= nonBannedPercent / 2 && bannedPercent < 80,
  68. imgSrc: "https://i.imgur.com/g5GxNHD.png",
  69. outputPrefix: "возможно мошенник",
  70. tableSuffix: "scammers",
  71. sendMessage: true,
  72. isPossibleScammer: true,
  73. }
  74. ];
  75. }
  76.  
  77. async function addItem(item) {
  78. let currentValue = await checkKeyValue(item.key);
  79. if (currentValue === undefined) {
  80. changeItemValue(item.key, item.value);
  81. return item.key;
  82. }
  83. return null;
  84. }
  85.  
  86. function changeItemValue(key, value) {
  87. //console.log(key, value)
  88. GM_setValue(key, value);
  89. }
  90.  
  91. async function addItems(items) {
  92. let addedItems = [];
  93. for (let item of items) {
  94. let addedItem = await addItem(item);
  95. addedItem !== null ? addedItems.push(addedItem) : null;
  96.  
  97. }
  98. let message = addedItems.length > 1 ?
  99. `Были добавлены новые значения: ${addedItems.join(', ')}` :
  100. addedItems.length > 0 ? `Было добавлено новое значение: ${addedItems[0]}` :
  101. 'Новые значения не были добавлены.';
  102. logWithPrefix(message);
  103. }
  104.  
  105.  
  106. function deleteItem(key) {
  107. GM_deleteValue(key);
  108. }
  109.  
  110. function checkKeyValue(key) {
  111. return Promise.resolve(GM_getValue(key));
  112. }
  113.  
  114. /**
  115. * Функция для получения чекбоксов.
  116. *
  117. * @param {string} telegramBotTokenText - Токен бота Telegram. Если не указан, будет использоваться значение по умолчанию (null).
  118. * @param {string} telegramUserIDText - ID пользователя Telegram. Если не указан, будет использоваться значение по умолчанию (null).
  119. * @param {string} lastId - ID присваивающийся к элементу. Если не указан, будет использоваться значение по умолчанию (null).
  120. * @param {boolean} showParameters - Флаг, указывающий, следует ли отображать параметры. Если не указан, будет использоваться значение по умолчанию (false).
  121. *
  122. * @returns {Promise} Возвращает Promise, который при успешном выполнении вернет данные чекбоксов.
  123. */
  124. async function getCheckboxes(telegramBotTokenText = null, telegramUserIDText = null, lastId = null, showParameters = false) {
  125. let checkboxes = [
  126. { id: 'autocheck-profile', label: 'Автоматическая проверка в профиле', value: "false"},
  127. { id: 'autocheck-mini-profile', label: 'Автоматическая проверка в мини-профиле', value: "false" },
  128. { id: 'autocheck-banned-users-mporp', label: 'Автоматическая проверка для заблокированных', value: "false", dependent: ['autocheck-profile', 'autocheck-mini-profile'] },
  129. { id: 'autocheck-newmembers', label: 'Автоматическая проверка новых пользователей в разделе /members', value: "false" },
  130. { id: 'autocheck-online-registered', label: 'Автоматическая проверка в разделе /online/?type=registered', value: "false" },
  131. { id: 'autocheck-only-parameters', label: `Автоматическая проверка только по параметрам (<<span id="sympCount-${lastId}">${await checkKeyValue('autocheck-only-parameters-sympathies')}</span> симпатий и <<span id="msgCount-${lastId}">${await checkKeyValue('autocheck-only-parameters-messages')}</span> сообщений)`, value: "false" },
  132. { id: 'addbutton-threads', label: 'Кнопка на посты и комментарии в теме (три точки)', value: 'true' },
  133. { id: 'addbutton-chat', label: 'Кнопка на сообщения в чате (три точки)', value: "true" },
  134. { id: 'addbutton-profile', label: 'Кнопка на посты и комментарии в профиле (три точки)', value: "true" },
  135. { id: 'addbutton-alerts', label: 'Кнопка на уведомления (три точки)', value: "true" },
  136. { id: 'MarketAdButton', label: 'Кнопка на объявления маркета (три точки)', value: "true" },
  137. { id: 'addbutton-conversations', label: 'Кнопка на диалоги в личных сообщениях (три точки)', value: "true" },
  138. { id: 'show-blocked-percentage', label: 'Отображать в подробной информации % заблокированных', value: "true" },
  139. { id: 'show-unblocked-percentage', label: 'Отображать в подробной информации % не заблокированных', value: "true" },
  140. { id: 'show-total-users-ip', label: 'Отображать в подробной информации общее количество пользователей в общих IP', value: "true" },
  141. { id: 'show-blocked-this-month-percentage', label: 'Отображать в подробной информации % от заблокированных в этом месяце', value: "true" },
  142. { id: 'fast-switch-with-button', label: `Быстрое переключение на следующую страницу кнопкой <span id="buttonkey-${lastId}">${await checkKeyValue('fast-switch-with-button-key')}</span>`, value: "false" },
  143. { id: 'retry-after-error', label: 'Повторная проверка после ошибки Name not found (15000 ms)', value: "true" },
  144. { id: 'switch-page-automatically', label: 'Переключать на следующую страницу автоматически (пока не найдет мошенника)', value: "true" },
  145. { id: 'DelayBeforeSwitching', label: `Задержка перед переключением страницы, в секундах (от <span id="DelayBeforeSwitchingMin-${lastId}">${await checkKeyValue('DelayBeforeSwitchingMin')}</span> до <span id="DelayBeforeSwitchingMax-${lastId}">${await checkKeyValue('DelayBeforeSwitchingMax')}</span>)`, value: "true" },
  146. { id: 'not-to-check-previously-checked', label: `Не выполнять повторную проверку если пользователь ранее проверялся (<span id="clear-database-${lastId}">Нажмите здесь, чтобы очистить базу данных</span>)`, value: 'true'},
  147. { id: 'send-scammer-to-telegram', label: `Отправлять найденных мошенников в Telegram (токен: ${telegramBotTokenText}, ID пользователя: ${telegramUserIDText})`, value: "false" },
  148. { id: 'CircularNavigation', label: `При достижении последней страницы начинать с самого начала`, value: 'false' },
  149. { id: 'ContinueAfterScammerDetected', label: `Продолжать работу даже после обнаружения мошенника`, value: 'false'},
  150. { id: 'UnmarkBaseUnblocked', label: `Не помечать аккаунт в общих IP заблокированным, если основной аккаунт разблокирован`, value: 'true' },
  151. { id: 'HackBanExclude', label: `Не помечать аккаунт в общих IP заблокированным, если причина бана "Взломан"`, value: 'true' },
  152. { id: 'ForumComplaintHide', label: 'Устанавливать хайд для Команды Форума в жалобе', value: 'true'},
  153. { id: 'AutomaticFraudComplaint', label: `Автоматическое создание жалобы на найденного мошенника (От <span id="AutomaticFraudComplaintPercentage-${lastId}">${await checkKeyValue('AutomaticFraudComplaintPercentage')}</span>% заблокированных)`, value: 'false' },
  154. { id: 'CheckComplaintsPreviously', label: 'Не создавать жалобу автоматически если на пользователя жаловались за последний месяц', value: 'true' },
  155. { id: 'UnnotifyOnTopicCreation', label: 'Не оповещать подписчиков о создании темы', value: 'false' },
  156. { id: 'ResetToDefaults', label: 'Сбросить настройки до исходного состояния', value: 'false' },
  157. { id: 'UserCheckInterval', label: `Задержка перед проверкой пользователя в секундах (от <span id="UserCheckIntervalMin-${lastId}">${await checkKeyValue('UserCheckIntervalMin')}</span> до <span id="UserCheckIntervalMax-${lastId}">${await checkKeyValue('UserCheckIntervalMax')}</span>)`, value: 'false' }
  158. ]
  159.  
  160. let parameters = [
  161. {id: 'autocheck-only-parameters-sympathies', value: '20'},
  162. {id: 'autocheck-only-parameters-messages', value: '50'},
  163. {id: 'fast-switch-with-button-key', value: 'F2'},
  164. {id: 'checked-list', value: []},
  165. {id: 'telegram-bot-token', value: ""},
  166. {id: 'telegram-user-id', value: ""},
  167. {id: 'DelayBeforeSwitchingMin', value: '5'},
  168. {id: 'DelayBeforeSwitchingMax', value: '7'},
  169. {id: 'AutomaticFraudComplaintPercentage', value: '80'},
  170. {id: 'UserCheckIntervalMin', value: '1'},
  171. {id: 'UserCheckIntervalMax', value: '3'}
  172. ]
  173.  
  174. return showParameters ? [...checkboxes, ...parameters] : checkboxes;
  175. }
  176.  
  177. let accountMenu = $("#AccountMenu");
  178. let linksList = accountMenu.find(".blockLinksList");
  179. let buttonId = `lmfsettings`;
  180.  
  181. linksList.append(`<li><a href="javascript:void(0)" id="${buttonId}">Multiaccount Finder</a></li>`);
  182.  
  183. $("#" + buttonId).click(function (event) {
  184. event.preventDefault();
  185. openSettings();
  186. });
  187.  
  188. let hrefSearchUsers = $('a[href^="/search/search?users="]').attr('href').split('?users=')[1].split('&')[0];
  189. let profileLink = $("#AccountMenu > ul > li:nth-child(1) > a").attr("href").split(`${domain}`)[1];
  190.  
  191. const months = ['янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек'];
  192. const date = new Date();
  193. const moscowTime = date.toLocaleString("ru-RU", {timeZone: "Europe/Moscow"});
  194. const month = months[date.getMonth()];
  195.  
  196. async function isEmptyOrWhitespaces(value) {
  197. const stringValue = String(value);
  198. return stringValue.trim() !== '';
  199. }
  200.  
  201. async function sendTelegramMessage(botToken, chatId, text) {
  202. try {
  203. const url = `https://api.telegram.org/bot${botToken}/sendMessage?chat_id=${chatId}&text=${encodeURIComponent(text)}&parse_mode=HTML`;
  204. const response = await fetch(url, {method: 'GET'});
  205.  
  206. if (response.ok) {
  207. logWithPrefix('Сообщение в Telegram отправлено');
  208. } else {
  209. console.error(`Ошибка: ${response.status} ${response.statusText}`);
  210. }
  211. } catch (error) {
  212. console.error(`Ошибка: ${error.message}`);
  213. }
  214. }
  215.  
  216. async function waitWithRandomDelay() {
  217. const useDelay = await checkKeyValue('DelayBeforeSwitching');
  218. if (useDelay === "true") {
  219. const minDelaySec = parseInt(await checkKeyValue('DelayBeforeSwitchingMin'), 10);
  220. const maxDelaySec = parseInt(await checkKeyValue('DelayBeforeSwitchingMax'), 10);
  221. const delaySeconds = Math.floor(Math.random() * (maxDelaySec - minDelaySec + 1)) + minDelaySec;
  222. logWithPrefix(`Спим ${delaySeconds} секунд перед переключением страницы...`);
  223. await new Promise(resolve => setTimeout(resolve, delaySeconds * 1000));
  224. }
  225. }
  226. async function sleepBeforeUserCheck() {
  227. const useDelay = await checkKeyValue('UserCheckInterval');
  228. if (useDelay === "true") {
  229. const minDelaySec = parseInt(await checkKeyValue('UserCheckIntervalMin'), 10);
  230. const maxDelaySec = parseInt(await checkKeyValue('UserCheckIntervalMax'), 10);
  231. const delaySeconds = Math.floor(Math.random() * (maxDelaySec - minDelaySec + 1)) + minDelaySec;
  232. logWithPrefix(`Спим ${delaySeconds} секунд перед проверкой пользователя...`);
  233. await new Promise(resolve => setTimeout(resolve, delaySeconds * 1000));
  234. }
  235. }
  236. async function sendMessageIfConditionsMet(text) {
  237. const sendToTelegram = await checkKeyValue("send-scammer-to-telegram");
  238. const botToken = await checkKeyValue("telegram-bot-token");
  239. const userId = await checkKeyValue("telegram-user-id");
  240.  
  241. if (sendToTelegram && await isEmptyOrWhitespaces(botToken) && await isEmptyOrWhitespaces(userId)) {
  242. await sendTelegramMessage(botToken, userId, text);
  243. }
  244. }
  245.  
  246. async function renderCheckboxes(checkboxes, page, lastId) {
  247. const numCheckboxes = checkboxes.length;
  248. const checkboxesPerPage = 10;
  249. const numPages = Math.ceil(numCheckboxes / checkboxesPerPage);
  250. const startIndex = (page - 1) * checkboxesPerPage;
  251. const endIndex = page * checkboxesPerPage;
  252. filteredCheckboxes = checkboxes.slice(startIndex, endIndex);
  253. let nextPage = page + 1;
  254. let prevPage = page - 1;
  255. let content = "";
  256. const promises = filteredCheckboxes.map(async (checkbox) => {
  257. let isChecked = await checkKeyValue(checkbox.id) === "true";
  258. const disabled = checkbox.dependent && !checkbox.dependent.every(async function (dep) {
  259. return await checkKeyValue(dep) === "true";
  260. });
  261. return '<div> <input type="checkbox" id="' + checkbox.id + '-' + lastId + '" name="' + checkbox.id + '-' + lastId + '" ' + (isChecked ? 'checked="checked" ' : '') + (disabled ? 'disabled ' : '') + 'value="1"> ' + checkbox.label + '</div>';
  262. });
  263. const results = await Promise.all(promises);
  264. content = results.join('<br>');
  265.  
  266. function renderPage(content) {
  267. function addPreviousPageButton() {
  268. if (prevPage >= 1) {
  269. content += '<br>';
  270. content += (nextPage <= numPages) ? '<div style="margin-right: 10px; display: inline-block;"><button type="button" name="prev" value="Предыдущая страница" accesskey="s" class="button primary" id="prev-page-' + lastId + '">Предыдущая страница</button></div>' : '<button type="button" name="prev" value="Предыдущая страница" accesskey="s" class="button primary" id="prev-page-' + lastId + '">Предыдущая страница</button>';
  271. }
  272. }
  273.  
  274. function addNextPageButton() {
  275. if (nextPage <= numPages) {
  276. content += (prevPage >= 1) ? '<button type="button" name="next" value="Следующая страница" accesskey="s" class="button primary" id="next-page-' + lastId + '">Следующая страница</button>' : '<br><button type="button" name="next" value="Следующая страница" accesskey="s" class="button primary" id="next-page-' + lastId + '">Следующая страница</button>';
  277. }
  278. }
  279.  
  280. function addPageNumber() {
  281. content += '<span style="margin-left: 10px;">Страница: ' + page + ' из ' + numPages + '</span>';
  282. }
  283. addPreviousPageButton();
  284. addNextPageButton();
  285. addPageNumber()
  286. return content
  287. }
  288. content = await renderPage(content);
  289. return content
  290. }
  291.  
  292. let filteredCheckboxes
  293.  
  294. function replaceMiddleWithAsterisks(value) {
  295. if (value && typeof value === 'string') {
  296. const middleIndex = Math.floor(value.length / 2);
  297. const charsToReplace = Math.floor(value.length * 0.3);
  298. const start = middleIndex - Math.floor(charsToReplace / 2);
  299. const end = start + charsToReplace;
  300. return value.substring(0, start) + '*'.repeat(charsToReplace) + value.substring(end);
  301. }
  302. return value;
  303. }
  304.  
  305. async function openSettings(page = 1, lastId = generateRandomString(10)) {
  306. document.querySelectorAll('div.modal.fade').forEach(el => el.remove());
  307. const modalBackdrops = document.querySelectorAll('div.modal-backdrop');
  308. if (modalBackdrops.length > 0) {
  309. modalBackdrops[modalBackdrops.length - 1].remove();
  310. }
  311.  
  312. const [telegramBotToken, telegramUserID] = await Promise.all([checkKeyValue('telegram-bot-token'), checkKeyValue('telegram-user-id')]);
  313.  
  314. const isEmptyOrWhitespaces = str => {
  315. const inputStr = String(str);
  316. return inputStr === null || inputStr.match(/^ *$/) !== null;
  317. };
  318.  
  319. const generateSpanContent = (id, value, isToken) => {
  320. const spanClass = isToken ? "telegram-bot-token" : "telegram-user-id";
  321. value = replaceMiddleWithAsterisks(value);
  322. return !isEmptyOrWhitespaces(value)
  323. ? `<span id="set-${spanClass}-${id}">${value}</span>`
  324. : `<span class="prompt-${spanClass}-${id}">не указан</span>`;
  325. };
  326.  
  327.  
  328. const [telegramBotTokenText, telegramUserIDText] = [generateSpanContent(lastId, telegramBotToken, true), generateSpanContent(lastId, telegramUserID, false)];
  329. let checkboxes = await getCheckboxes(telegramBotTokenText, telegramUserIDText, lastId)
  330.  
  331.  
  332. let content = await renderCheckboxes(checkboxes, page, lastId);
  333. XenForo.alert(content, 'Lolzteam Multiaccount Finder');
  334.  
  335. filteredCheckboxes.forEach(checkbox => {
  336. const id = `${checkbox.id}-${lastId}`;
  337. const checkboxEl = document.getElementById(id);
  338. if (checkboxEl) {
  339. checkboxEl.onclick = async () => {
  340. if (checkbox.id === 'ResetToDefaults' && checkboxEl.checked) {
  341. if (confirm('Вы уверены, что хотите сбросить настройки до исходного состояния?')) {
  342. await deleteUncheckedItems(items, page);
  343. logWithPrefix('Настройки сброшены до исходного состояния');
  344. await openSettings(page);
  345. } else {
  346. checkboxEl.checked = false; // Снимаем галочку, если пользователь нажал "Отмена"
  347. }
  348. } else {
  349. changeItemValue(checkbox.id, `${checkboxEl.checked}`);
  350. logWithPrefix(`Значение ${checkbox.id} установлено как ${checkboxEl.checked}`);
  351. }
  352. }
  353. }
  354. });
  355.  
  356. const prevButton = document.getElementById(`prev-page-${lastId}`);
  357. const nextButton = document.getElementById(`next-page-${lastId}`);
  358.  
  359. if (prevButton) prevButton.addEventListener("click", () => openSettings(page - 1));
  360. if (nextButton) nextButton.addEventListener("click", () => openSettings(page + 1));
  361.  
  362. function updateElementWithNotSpecified(selector, value) {
  363. const el = document.querySelector(selector);
  364. if (el) {
  365. el.textContent = isEmptyOrWhitespaces(value) ? 'не указан' : value;
  366. }
  367. }
  368.  
  369. const addClickListener = (selector, handler, message, key, updateEl, allowEmpty, filter = true) => {
  370. document.querySelector(selector)?.addEventListener('click', async () => {
  371. let finalValue;
  372.  
  373. if (key === 'clear-database') {
  374. if (confirm("Вы уверены, что хотите очистить базу данных?")) {
  375. changeItemValue("checked-list", []);
  376. alert('База данных очищена');
  377. logWithPrefix('База данных с ранее проверенными пользователями была очищена')
  378.  
  379. }
  380. return
  381. } else {
  382. const input = prompt(message);
  383. if (input === null) return;
  384. if (input.trim() === '' && (key === 'telegram-bot-token' || key === 'telegram-user-id')) {
  385. changeItemValue(key, input.trim());
  386. updateElementWithNotSpecified(selector, input.trim());
  387. return
  388. }
  389.  
  390.  
  391. if (input === '' && !allowEmpty) return;
  392.  
  393. finalValue = input.trim();
  394.  
  395. if ((isNaN(finalValue) || finalValue === '') && !allowEmpty) return;
  396.  
  397.  
  398. if (filter && key === 'telegram-user-id') {
  399. finalValue = parseInt(finalValue.replace(/[^\d]/g, ''));
  400. if (isNaN(finalValue)) return logWithPrefix('ID пользователя Telegram не был сохранен, т.к. результат NaN')
  401. }
  402. }
  403. if (key.startsWith('DelayBeforeSwitching')) {
  404. const [val1, val2] = await Promise.all([
  405. checkKeyValue('DelayBeforeSwitchingMin'),
  406. checkKeyValue('DelayBeforeSwitchingMax')
  407. ]).then(([v1, v2]) => [parseInt(v1), parseInt(v2)]);
  408.  
  409. if (key.endsWith('Min')) {
  410. finalValue = Math.max(1, finalValue);
  411. if (finalValue >= val2) {
  412. changeItemValue('DelayBeforeSwitchingMax', finalValue + 1);
  413. (updateEl || (el => (el.textContent = finalValue + 1)))(document.querySelector(`#DelayBeforeSwitchingMax-${lastId}`));
  414. }
  415. } else if (key.endsWith('Max')) {
  416. finalValue = Math.max(2, finalValue);
  417. if (finalValue <= val1 + 1) {
  418. changeItemValue('DelayBeforeSwitchingMin', finalValue - 1);
  419. (updateEl || (el => (el.textContent = finalValue - 1)))(document.querySelector(`#DelayBeforeSwitchingMin-${lastId}`));
  420. }
  421. }
  422. }
  423.  
  424. if (key.startsWith('UserCheckInterval')) {
  425. const [val1, val2] = await Promise.all([
  426. checkKeyValue('UserCheckIntervalMin'),
  427. checkKeyValue('UserCheckIntervalMax')
  428. ]).then(([v1, v2]) => [parseInt(v1), parseInt(v2)]);
  429.  
  430. if (key.endsWith('Min')) {
  431. finalValue = Math.max(1, finalValue);
  432. if (finalValue >= val2) {
  433. changeItemValue('UserCheckIntervalMax', finalValue + 1);
  434. (updateEl || (el => (el.textContent = finalValue + 1)))(document.querySelector(`#UserCheckIntervalMax-${lastId}`));
  435. }
  436. } else if (key.endsWith('Max')) {
  437. finalValue = Math.max(2, finalValue);
  438. if (finalValue <= val1 + 1) {
  439. changeItemValue('UserCheckIntervalMin', finalValue - 1);
  440. (updateEl || (el => (el.textContent = finalValue - 1)))(document.querySelector(`#UserCheckIntervalMin-${lastId}`));
  441. }
  442. }
  443. }
  444.  
  445. if (key.startsWith('AutomaticFraudComplaintPercentage')) {
  446. if(finalValue > 100) {
  447. finalValue = 100;
  448. } else if(finalValue < 1) {
  449. finalValue = 1;
  450. }
  451.  
  452. changeItemValue('AutomaticFraudComplaintPercentage', finalValue);
  453. }
  454.  
  455. changeItemValue(key, finalValue);
  456. if (key === 'telegram-bot-token') {(updateEl || (el => (el.textContent = replaceMiddleWithAsterisks(finalValue))))(document.querySelector(selector))} else {
  457. (updateEl || (el => (el.textContent = replaceMiddleWithAsterisks(finalValue))))(document.querySelector(selector));
  458. }
  459. });
  460. };
  461. addClickListener('#clear-database-' + lastId, null, null, 'clear-database');
  462.  
  463. const elements = [
  464. {selector: telegramBotToken ? '#set-telegram-bot-token-' + lastId : '.prompt-telegram-bot-token-' + lastId, message: "Введите токен Telegram бота:", key: "telegram-bot-token", allowEmpty: true, filter: false},
  465. {selector: telegramUserID ? '#set-telegram-user-id-' + lastId : '.prompt-telegram-user-id-' + lastId, message: "Введите ID пользователя Telegram:", key: "telegram-user-id", allowEmpty: true, filter: false},
  466. {selector: '#DelayBeforeSwitchingMin-' + lastId, message: "Введите новое значение для задержки:", key: "DelayBeforeSwitchingMin"},
  467. {selector: '#DelayBeforeSwitchingMax-' + lastId, message: "Введите новое значение для задержки:", key: "DelayBeforeSwitchingMax"},
  468. {selector: '#sympCount-' + lastId, message: "Введите новое значение счетчика симпатий:", key: "autocheck-only-parameters-sympathies"},
  469. {selector: '#msgCount-' + lastId, message: "Введите новое значение счетчика сообщений:", key: "autocheck-only-parameters-messages"},
  470. {selector: '#AutomaticFraudComplaintPercentage-' + lastId, message: "Введите новое значение для процента заблокированных:", key: "AutomaticFraudComplaintPercentage"},
  471. {selector: '#UserCheckIntervalMin-' + lastId, message: "Введите новое значение для задержки:", key: "UserCheckIntervalMin"},
  472. {selector: '#UserCheckIntervalMax-' + lastId, message: "Введите новое значение для задержки:", key: "UserCheckIntervalMax"}
  473. ];
  474.  
  475. elements.forEach(el => {
  476. addClickListener(el.selector, null, el.message, el.key, el.func, el.allowEmpty || false);
  477. });
  478.  
  479. const buttonKey = document.querySelector('#buttonkey-' + lastId);
  480. if (buttonKey) {
  481. buttonKey.addEventListener('click', () => {
  482. buttonKey.style.color = 'red';
  483. let keydownListener;
  484. const toggleKeydownListener = () => {
  485. if (keydownListener) {
  486. document.removeEventListener('keydown', keydownListener);
  487. buttonKey.style.color = '';
  488. keydownListener = null;
  489. } else {
  490. const currentKey = checkKeyValue('fast-switch-with-button-key');
  491. keydownListener = (event) => {
  492. if (event.key !== currentKey) {
  493. changeItemValue('fast-switch-with-button-key', event.key);
  494. buttonKey.textContent = event.key;
  495. buttonKey.style.color = '';
  496. document.removeEventListener('keydown', keydownListener);
  497. keydownListener = null;
  498. }
  499. };
  500. document.addEventListener('keydown', keydownListener);
  501. }
  502. };
  503. toggleKeydownListener();
  504. });
  505. }
  506. }
  507.  
  508. async function checkMenuItems() {
  509. const sharedItems = document.querySelectorAll('.Menu a[href*="/shared-ips"]');
  510. for (let i = 0; i < sharedItems.length; i++) {
  511. const item = sharedItems[i];
  512. const menu = item.parentNode.parentNode;
  513. if (menu.hasAttribute("data-multiaccount-finder")) continue;
  514. menu.setAttribute("data-multiaccount-finder", "added");
  515. const buttonId = `multiaccountFinderButton-${generateRandomString(10)}`;
  516. const currentUrl = item.getAttribute('href');
  517. menu.appendChild(createButtonElement(buttonId, currentUrl));
  518. const makeClaimLink = menu.querySelector('a[href*="/make-claim"]');
  519. if (makeClaimLink) {
  520. if (await checkKeyValue("autocheck-mini-profile") !== 'true') continue;
  521. const bannedModule = document.querySelectorAll('.usernameAndStatus');
  522. for (let j = 0; j < bannedModule.length; j++) {
  523. const module = bannedModule[j];
  524. const bannedcheck = module.parentNode.parentNode;
  525. if (bannedcheck.hasAttribute("data-multiaccount-finder")) continue;
  526. bannedcheck.setAttribute("data-multiaccount-finder", "added");
  527. const gifId = `gif-profile-${generateRandomString(10)}`;
  528. const countsModule = document.querySelectorAll('.userStatCounters');
  529. let lastElement = countsModule[countsModule.length - 1]
  530. const miniprofileMenu = lastElement.parentNode.parentNode;
  531. if (miniprofileMenu.hasAttribute("data-multiaccount-finder")) continue;
  532. miniprofileMenu.setAttribute("data-multiaccount-finder", "added");
  533. let sympathies = lastElement.querySelector('a:nth-child(1) > span.count').textContent.replace(/ /g, "");
  534. let messages = lastElement.querySelector('a:nth-child(3) > span.count').textContent.replace(/ /g, "");
  535. if (sympathies || messages) {
  536. if (await checkKeyValue("autocheck-only-parameters") === 'true' && (sympathies >= parseInt(await checkKeyValue("autocheck-only-parameters-sympathies")) && messages >= parseInt(await checkKeyValue("autocheck-only-parameters-messages")))) continue;
  537. }
  538. lastElement.appendChild(createGifElement(gifId, 'https://i.imgur.com/I5VH0zp.gif', 24, 24));
  539. if (await checkKeyValue("autocheck-banned-users-mporp") === 'false' && bannedcheck.querySelector('.banInfo.muted.Tooltip') || bannedcheck.querySelector('div.errorPanel')) {
  540. const element = document.getElementById(gifId);
  541. if (element) element.remove();
  542. continue;
  543. }
  544. const element = document.getElementById(buttonId);
  545. if (element) element.remove();
  546. checkUser(currentUrl, undefined, gifId).then();
  547. }
  548. } else {
  549. if (await checkKeyValue("autocheck-profile") !== 'true') return;
  550. let sympathies = document.querySelector("#content > div > div > div.profilePage > div.mainProfileColumn > div > div.counts_module > a.page_counter.Tooltip > div.count").textContent.replace(/ /g, "");
  551. let messages = document.querySelector("#content > div > div > div.profilePage > div.mainProfileColumn > div > div.counts_module > a:nth-child(3) > div.count").textContent.replace(/ /g, "");
  552. if (await checkKeyValue("autocheck-only-parameters") === 'true' && document.querySelector("#content > div > div > div.profilePage > div.mainProfileColumn > div > div.counts_module > a.page_counter.Tooltip > div.count") && document.querySelector("#content > div > div > div.profilePage > div.mainProfileColumn > div > div.counts_module > a:nth-child(3) > div.count") && (sympathies >= parseInt(await checkKeyValue("autocheck-only-parameters-sympathies")) && messages >= parseInt(await checkKeyValue("autocheck-only-parameters-messages")))) return;
  553. const bannedModule = document.querySelectorAll('div.mainProfileColumn');
  554. for (let i = 0; i < bannedModule.length; i++) {
  555. const bannedcheck = bannedModule[i].parentNode.parentNode;
  556. if (bannedcheck.hasAttribute("data-multiaccount-finder")) continue;
  557. bannedcheck.setAttribute("data-multiaccount-finder", "added");
  558. const gifId = `gif-profile`;
  559. const countsModule = document.querySelectorAll('.counts_module');
  560. for (let j = 0; j < countsModule.length; j++) {
  561. const profilecounter = countsModule[j].parentNode.parentNode;
  562. if (profilecounter.hasAttribute("data-multiaccount-finder")) continue;
  563. profilecounter.setAttribute("data-multiaccount-finder", "added");
  564. if (sympathies || !messages) {
  565. if (await checkKeyValue("autocheck-only-parameters") === 'true' && (sympathies >= parseInt(await checkKeyValue("autocheck-only-parameters-sympathies")) && messages >= parseInt(await checkKeyValue("autocheck-only-parameters-messages")))) return;
  566. }
  567. countsModule[j].appendChild(createGifElement(gifId, 'https://i.imgur.com/I5VH0zp.gif', 32, 32));
  568. if (await checkKeyValue("autocheck-banned-users-mporp") === 'false' && bannedcheck.querySelector('div.errorPanel')) {
  569. const element = document.getElementById(gifId);
  570. if (element) element.remove();
  571. return;
  572. }
  573. const element = document.getElementById(buttonId);
  574. if (element) element.remove();
  575. checkUser(currentUrl, undefined, gifId).then();
  576. }}}}}
  577.  
  578. function createButtonElement(buttonId, currentUrl) {
  579. const multiaccountFinderItem = document.createElement("li");
  580. const button = document.createElement("a");
  581. button.setAttribute("href", "javascript:void(0)");
  582. button.setAttribute("id", buttonId);
  583. button.textContent = "Multiaccount Finder";
  584. button.addEventListener("click", function(event) {
  585. event.preventDefault();
  586. checkUser(`https://${domain}/${currentUrl}/shared-ips/`).then();
  587. });
  588. multiaccountFinderItem.appendChild(button);
  589. return multiaccountFinderItem;
  590. }
  591.  
  592. function createGifElement(gifId, src, width, height) {
  593. const gifElement = document.createElement('img');
  594. gifElement.id = gifId;
  595. gifElement.src = src;
  596. gifElement.width = width;
  597. gifElement.height = height;
  598. return gifElement;
  599. }
  600.  
  601. function generateRandomString(length) {
  602. let result = '';
  603. let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  604. let charactersLength = characters.length;
  605. for (let i = 0; i < length; i++) {
  606. result += characters.charAt(Math.floor(Math.random() * charactersLength));
  607. }
  608. return result;
  609. }
  610.  
  611. async function checkThreadItems() {
  612. if (await checkKeyValue("addbutton-threads") === 'true') {
  613. const linksLists = [...document.querySelectorAll('.secondaryContent.blockLinksList')]
  614. .filter(list => !list.querySelector('li[id^="multiaccountFinderButton-"]'));
  615.  
  616. linksLists.forEach((linksList) => {
  617. const links = linksList.querySelectorAll("a");
  618.  
  619. links.forEach((link) => {
  620. if (link.href.startsWith(`https://${domain}/posts/`)) {
  621. let postId;
  622. let newLink;
  623. let postElement;
  624.  
  625. if (link.href.includes('posts/comments/')) {
  626. postId = link.href.split('posts/comments/')[1].split('/')[0];
  627. newLink = `post-comment-${postId}`
  628. } else {
  629. postId = link.href.split('posts/')[1].split('/')[0];
  630. newLink = `post-${postId}`
  631. }
  632.  
  633. postElement = document.querySelector(`#${newLink}.${link.href.includes('posts/comments/') ? 'comment' : 'message'}`);
  634.  
  635. if (postElement && !postElement.hasAttribute("data-multiaccount-finder")) {
  636. postElement.setAttribute("data-multiaccount-finder", "added");
  637.  
  638. const menus = [...document.querySelectorAll('div.Menu')]
  639. .filter(menu => [...menu.querySelectorAll('a')].some(link => link.href.includes(`${postId}`)));
  640.  
  641. const author = postElement.querySelector('.username').textContent;
  642. if (author !== hrefSearchUsers) {
  643. const buttonId = `multiaccountFinderButton-${generateRandomString(10)}`;
  644.  
  645. const usernameLink = postElement.querySelector('a');
  646. const currentUrl = usernameLink.getAttribute('href');
  647.  
  648. menus[menus.length - 1].querySelector('.secondaryContent').appendChild(createButtonElement(buttonId, currentUrl));
  649. }
  650. }
  651. }
  652. });
  653. });
  654. }
  655. }
  656.  
  657. async function checkProfileItems() {
  658. const profilePostList = document.querySelector('ol#ProfilePostList');
  659. if (((await checkKeyValue("addbutton-profile")) === 'true') && profilePostList) {
  660. const linksLists = [...profilePostList.querySelectorAll(':not(li[id^="multiaccountFinderButton-"])')];
  661. linksLists.forEach((linksList) => {
  662. const links = linksList.querySelectorAll("a");
  663. links.forEach((link) => {
  664. if (link.href.startsWith(`https://${domain}/profile-posts/`)) {
  665. let postId;
  666. let newLink;
  667. let postElement;
  668. if (link.href.includes('profile-posts/comments')) {
  669. postId = link.href.split('posts/comments/')[1].split('/')[0];
  670. newLink = `profile-post-comment-${postId}`
  671. postElement = document.querySelector(`#${newLink}.comment`);
  672. } else if (!link.href.includes('profile-posts/comments/')) {
  673. postId = link.href.split('profile-posts/')[1].split('/')[0];
  674. newLink = `profile-post-${postId}`
  675. postElement = document.querySelector(`#${newLink}.messageSimple`);
  676. }
  677. if (postElement && !postElement.hasAttribute("data-multiaccount-finder")) {
  678. postElement.setAttribute("data-multiaccount-finder", "added");
  679. const menus = [...document.querySelectorAll('div.Menu')].filter(menu => [...menu.querySelectorAll('a')].some(link => link.href.includes(`${postId}`)));
  680. let author = postElement.querySelector('a.username.poster')
  681. if (author.textContent !== hrefSearchUsers) {
  682. document.createElement("li");
  683. let buttonId = `multiaccountFinderButton-${generateRandomString(10)}`;
  684. let currentUrl = author.getAttribute('href')
  685. if (menus.length > 0) {
  686. menus[menus.length - 1].querySelector('.secondaryContent').appendChild(createButtonElement(buttonId, currentUrl));
  687. }
  688. }
  689. }
  690. }
  691. })
  692. })
  693. }
  694. }
  695.  
  696. async function checkMarketItems() {
  697. if ((await checkKeyValue("MarketAdButton")) === 'true') {
  698. let marketElements = document.querySelectorAll('[id^="marketItem"]');
  699. marketElements.forEach((marketElement) => {
  700. let username = marketElement.querySelector('a.username');
  701. let usernameLink = username.getAttribute('href');
  702. let userId = usernameLink.match(/members\/(\d+)\//)[1];
  703.  
  704. const menuElements = Array.from(document.querySelectorAll('div.Menu')).filter(menuElement => !menuElement.hasAttribute("data-multiaccount-finder") && menuElement.querySelector(`.blockLinksList a[href^="user/${userId}/ignore"]`));
  705.  
  706. const targetElement = menuElements[0];
  707. if (targetElement) {
  708. document.createElement("li");
  709. let buttonId = `multiaccountFinderButton-${generateRandomString(10)}`;
  710. targetElement.querySelector('.blockLinksList').appendChild(createButtonElement(buttonId, username));
  711. targetElement.setAttribute("data-multiaccount-finder", "added");
  712. }
  713. });
  714. }
  715. }
  716.  
  717. async function checkAlertItems() {
  718. if ((await checkKeyValue("addbutton-alerts")) === 'true') {
  719. const alertElements = document.querySelectorAll('li.Alert');
  720. alertElements.forEach((alertElement) => {
  721. if (alertElement.querySelector('a[rel="menu"].PopupControl.dottesStyle.PopupContainerControl.PopupOpen')) {
  722. if (alertElement && !alertElement.hasAttribute("data-multiaccount-finder")) {
  723. const username = alertElement.querySelector('a.username')
  724. let menus = document.querySelectorAll('.Menu.MenuOpened');
  725. if (!username && !menus) return;
  726. const usernameLink = username.getAttribute('href');
  727. const lastMenu = menus[menus.length - 1];
  728. let buttonId = `multiaccountFinderButton-${generateRandomString(10)}`;
  729. lastMenu.querySelector('ul.secondaryContent.blockLinksList').appendChild(createButtonElement(buttonId, usernameLink));
  730. alertElement.setAttribute("data-multiaccount-finder", "added");
  731. }
  732. }
  733. })
  734. }
  735. }
  736.  
  737. async function checkConversationItems() {
  738. if ((await checkKeyValue("addbutton-conversations")) === 'true') {
  739. if (!window.location.href.startsWith(`https://${domain}/conversations/`)) return
  740. const username = document.querySelector('div.ImDialogHeader a.username');
  741. if (!username) return
  742. const usernameLink = username.getAttribute('href');
  743. const menuElements = Array.from(document.querySelectorAll('div.Menu')).filter(menuElement => menuElement.querySelector('.blockLinksList a[href^="conversations/"]'));
  744. const targetElement = menuElements[menuElements.length - 1];
  745. if (targetElement && !targetElement.hasAttribute("data-multiaccount-finder")) {
  746. document.createElement("li");
  747. let buttonId = `multiaccountFinderButton-${generateRandomString(10)}`;
  748. targetElement.querySelector('.blockLinksList').appendChild(createButtonElement(buttonId, usernameLink));
  749. targetElement.setAttribute("data-multiaccount-finder", "added");
  750. }
  751. }
  752. }
  753.  
  754. function update() {
  755. checkMenuItems();
  756. checkThreadItems();
  757. checkProfileItems();
  758. checkChatItems();
  759. checkAlertItems();
  760. checkConversationItems();
  761. domain == 'lzt.market' ? checkMarketItems() : null;
  762. requestAnimationFrame(update);
  763. }
  764.  
  765. requestAnimationFrame(update);
  766.  
  767. async function checkChatItems() {
  768. if (await checkKeyValue("addbutton-conversations") !== 'true') return;
  769. const elements = document.querySelectorAll('div[class^="chat2-message-block "]');
  770. const lztui = document.querySelectorAll('div[class^="lztui-Popup lztng-"]');
  771. const lastElement = lztui[lztui.length - 1];
  772.  
  773. elements.forEach((message, index) => {
  774. const popupElement = message.querySelector('div[class^="PopupControl PopupOpen"]');
  775. if (!popupElement) return;
  776.  
  777. let usernameLink = message.querySelector('.username[href]');
  778. if (usernameLink && usernameLink.href.includes(profileLink)) {
  779. usernameLink = null;
  780. }
  781.  
  782. if (!usernameLink) {
  783. let prevIndex = index - 1;
  784. while (prevIndex >= 0 && !usernameLink) {
  785. const prevMessage = elements[prevIndex];
  786. usernameLink = prevMessage.querySelector('.username[href]');
  787. prevIndex--;
  788. }
  789. }
  790.  
  791. if (usernameLink) {
  792. const ulElement = lastElement.querySelector('ul.secondaryContent.blockLinksList');
  793. if (ulElement && !ulElement.hasAttribute("data-multiaccount-finder")) {
  794. ulElement.setAttribute("data-multiaccount-finder", "added");
  795. const username = usernameLink.textContent;
  796. const buttonId = `multiaccountFinderButton-${generateRandomString(10)}`;
  797. if (username !== hrefSearchUsers) {
  798. ulElement.appendChild(createButtonElement(buttonId, usernameLink.href.replace(`https://${domain}`, '')));
  799. }
  800. }
  801. }
  802. });
  803. }
  804.  
  805.  
  806. async function OnlineChangeTable(classname, num) {
  807. const findelement = document.querySelector(`${classname}`);
  808. const remainedElement = document.querySelector('.remained dd');
  809. if (findelement) {
  810. const ddElement = findelement.querySelector('dd');
  811. if (ddElement) {
  812. const currentValue = parseInt(ddElement.textContent);
  813. ddElement.textContent = currentValue + num;
  814. if (findelement.classList.contains('scammers')) {
  815. ddElement.style.color = 'red';
  816. }
  817. }
  818. }
  819. const shouldDecrementRemainedElement = !(classname === 'dl.errors' && await checkKeyValue('retry-after-error') === 'true');
  820. if (remainedElement && shouldDecrementRemainedElement) {
  821. const currentValueRemained = parseInt(remainedElement.textContent);
  822. remainedElement.textContent = currentValueRemained - 1;
  823. }
  824. }
  825.  
  826. async function sleep(ms) {
  827. return new Promise(resolve => setTimeout(resolve, ms));
  828. }
  829.  
  830. async function AutoCheckOnlineRegistered() {
  831. if ((await checkKeyValue("autocheck-online-registered")) === 'true') {
  832. if (window.location.href.indexOf(`https://${domain}/online/?type=registered`) === 0) {
  833. const currentPageElement = document.querySelector('.currentPage');
  834. const maxPageElement = document.querySelector("#content > div > div > div > div > div > div > nav > a:nth-child(5)");
  835. const ulElement = document.querySelector('#content > div > div > div.mainContainer > div > ul');
  836.  
  837. if (!currentPageElement && !maxPageElement) {
  838. if (await checkKeyValue("CircularNavigation") === 'true') {
  839. await waitWithRandomDelay();
  840. window.location.href = `https://${domain}/online/?type=registered`;}} else {
  841. const currentPage = parseInt(currentPageElement.innerText.trim());
  842. const maxPage = parseInt(maxPageElement.innerText.trim());
  843.  
  844. if (await checkKeyValue("fast-switch-with-button") === 'true') {
  845. let fastswitchwithbuttonkey = await checkKeyValue("fast-switch-with-button-key");
  846. document.addEventListener('keydown', function(event) {
  847. if (event.key === fastswitchwithbuttonkey && currentPage < maxPage) {
  848. window.location.href = `https://${domain}/online/?type=registered&page=${currentPage + 1}`;
  849. }
  850. });
  851. }
  852.  
  853. const visitorCountDl = document.querySelector('dl.visitorCount');
  854. const members = document.querySelectorAll('.member');
  855. if (visitorCountDl && !visitorCountDl.querySelector('dl.clean')) {
  856. const newFootnoteDiv = document.createElement('div');
  857. newFootnoteDiv.className = 'footnote';
  858. newFootnoteDiv.innerHTML = `<h3>Lolzteam Multiaccount Finder</h3><dl class="clean"><dt>Не заподозрены:</dt><dd>0</dd></dl><dl class="vpn"><dt>VPN:</dt><dd>0</dd></dl><dl class="scammers"><dt>Мошенники:</dt><dd>0</dd></dl><dl class="errors"><dt>Ошибки:</dt><dd>0</dd>${await checkKeyValue("autocheck-only-parameters") === "true" ? `<dl class="skipped"><dt>Не подошли под указанные параметры:</dt><dd>0</dd></dl>` : ''}${await checkKeyValue("not-to-check-previously-checked") === "true" ? `<dl class="checked"><dt>Ранее проверялись:</dt><dd>0</dd></dl>` : ''}<dl class="remained"><dt>Осталось проверить:</dt><dd>${members.length}</dd></dl>`;
  859. visitorCountDl.appendChild(newFootnoteDiv);
  860. }
  861.  
  862. let index = 0;
  863. const checkNextMember = async () => {
  864. if (index >= members.length) {
  865. const scammersCount = parseInt(document.querySelector("dl.scammers dd").innerText);
  866. const continueAfterScammerDetected = await checkKeyValue("ContinueAfterScammerDetected") === 'true';
  867. const switchPageAutomatically = await checkKeyValue("switch-page-automatically") === 'true';
  868. const circularNavigation = await checkKeyValue("CircularNavigation") === 'true';
  869.  
  870. if (switchPageAutomatically && currentPage < maxPage && scammersCount < 1) {
  871. await waitWithRandomDelay();
  872. window.location.href = `https://${domain}/online/?type=registered&page=${currentPage + 1}`;
  873. } else if (scammersCount > 0 && continueAfterScammerDetected && switchPageAutomatically) {
  874. if (currentPage < maxPage) {
  875. await waitWithRandomDelay();
  876. window.location.href = `https://${domain}/online/?type=registered&page=${currentPage + 1}`;
  877. } else if (circularNavigation) {
  878. await waitWithRandomDelay();
  879. window.location.href = `https://${domain}/online/?type=registered`;
  880. }
  881. } else if (scammersCount > 0 && !continueAfterScammerDetected) {
  882. document.title = 'Обнаружен мошенник';
  883. } else if (circularNavigation && !(currentPage < maxPage) && !(scammersCount > 0)) {
  884. window.location.href = `https://${domain}/online/?type=registered`;
  885. }
  886. return;
  887. }
  888.  
  889. const member = members[index];
  890. const usernameLink = member.querySelector('a.username');
  891. const usernameStyle = member.querySelector('a.username > span')
  892. const usernameHref = usernameLink.getAttribute('href');
  893. const userStatCounters = member.querySelector('.userStatCounters');
  894.  
  895. const gifId = `gif-${index}`;
  896.  
  897. if (usernameStyle.classList.contains('banned') || usernameStyle.classList.contains('style18') || usernameStyle.classList.contains('style360')) {
  898. logWithPrefix('Заблокированный пользователь был пропущен')
  899. OnlineChangeTable('dl.clean', 0) // лень добавлять отдельный класс будем просто вычитать оставшихся
  900. index++;
  901. await checkNextMember();
  902. } else {
  903. if (usernameLink.textContent !== hrefSearchUsers) {
  904. const sympathies = member.querySelector("div.userStatCounters > div:nth-child(1) > span.count").textContent.replace(/ /g, "")
  905. const messages = member.querySelector("div.userStatCounters > div:nth-child(3) > span.count").textContent.replace(/ /g, "")
  906.  
  907. if (await checkKeyValue("autocheck-only-parameters") === "true") {
  908. if ((sympathies < parseInt(await checkKeyValue("autocheck-only-parameters-sympathies")) && messages < parseInt(await checkKeyValue("autocheck-only-parameters-messages")))
  909. || (sympathies >= parseInt(await checkKeyValue("autocheck-only-parameters-sympathies")) && messages < parseInt(await checkKeyValue("autocheck-only-parameters-messages")))
  910. || (sympathies < parseInt(await checkKeyValue("autocheck-only-parameters-sympathies")) && messages >= parseInt(await checkKeyValue("autocheck-only-parameters-messages")))) {
  911. userStatCounters.appendChild(createGifElement(gifId, 'https://i.imgur.com/I5VH0zp.gif', 24,24));
  912. await checkUser(`https://${domain}/${usernameHref}/shared-ips`, 'registered', gifId, usernameStyle);
  913. } else {
  914. OnlineChangeTable('dl.skipped', 1)
  915. }
  916. } else {
  917. userStatCounters.appendChild(createGifElement(gifId, 'https://i.imgur.com/I5VH0zp.gif', 24,24));
  918. await checkUser(`https://${domain}/${usernameHref}/shared-ips`, 'registered', gifId, usernameStyle);
  919.  
  920. }
  921. } else {
  922. OnlineChangeTable('dl.skipped', 0)
  923. }
  924. index++;
  925. await checkNextMember();
  926. }}
  927. checkNextMember().then();
  928. }
  929. }
  930. }
  931. }
  932.  
  933. AutoCheckOnlineRegistered()
  934.  
  935. async function AutoCheckNewMembers() {
  936. if ((await checkKeyValue("autocheck-newmembers")) === 'true') {
  937. if ( window.location.href.indexOf(`https://${domain}/members`) === 0) {
  938. const visitorCountDl = document.querySelector('dl.memberCount')
  939. const members = document.querySelectorAll('.secondaryContent.avatarHeap.avatarList li')
  940.  
  941. if (visitorCountDl && !visitorCountDl.querySelector('dl.clean')) {
  942. const newFootnoteDiv = document.createElement('div')
  943. newFootnoteDiv.className = 'footnote'
  944. newFootnoteDiv.innerHTML = `<h3>Lolzteam Multiaccount Finder</h3><dl class="clean"><dt>Не заподозрены:</dt><dd>0</dd></dl><dl class="vpn"><dt>VPN:</dt><dd>0</dd></dl><dl class="scammers"><dt>Мошенники:</dt><dd>0</dd></dl><dl class="errors"><dt>Ошибки:</dt><dd>0</dd>${await checkKeyValue("not-to-check-previously-checked") === "true" ? `<dl class="checked"><dt>Ранее проверялись:</dt><dd>0</dd></dl>` : ''}<dl class="remained"><dt>Осталось проверить:</dt><dd>${members.length}</dd></dl>`;
  945. visitorCountDl.appendChild(newFootnoteDiv)
  946. }
  947.  
  948. let index = 0
  949. const checkNextMember = async () => {
  950. if (index >= members.length) {
  951. return
  952. }
  953.  
  954. const member = members[index]
  955. const usernameLink = member.querySelector('a.username')
  956. const usernameStyle = member.querySelector('a.username > span')
  957. const usernameHref = usernameLink.getAttribute('href')
  958. const userStatCounters = member.querySelector('div.memberInfo')
  959. const gifId = `gif-${index}`
  960.  
  961. // проверка на забаненного или пользователя с обжалованием
  962. if (usernameStyle.classList.contains('banned') || usernameStyle.classList.contains('style18') || usernameStyle.classList.contains('style360')) {
  963. OnlineChangeTable('dl.clean', 0) // лень добавлять отдельный класс будем просто вычитать оставшихся
  964. index++;
  965. await checkNextMember();
  966. } else {
  967. userStatCounters.appendChild(createGifElement(gifId, 'https://i.imgur.com/I5VH0zp.gif', 24, 24))
  968. await checkUser(`https://${domain}/${usernameHref}/shared-ips`, 'members', gifId)
  969.  
  970. index++
  971. await checkNextMember()
  972. }
  973. }
  974. checkNextMember().then()
  975. }
  976. }}
  977.  
  978. AutoCheckNewMembers()
  979.  
  980. async function PreviouslyCreatedComplaints(name) {
  981. const nodeIds = [801, 803];
  982. for (let i = 0; i < nodeIds.length; i++) {
  983. let nodeId = nodeIds[i];
  984. let url = `https://zelenka.guru/forums/${nodeId}/?node_id=${nodeId}&order=post_date&direction=desc&period=month&title=${encodeURIComponent(`Жалоба на пользователя ${name}`)}+&prefix_id[]=92`;
  985. const response = await fetchWithRetry(url);
  986. const data = await response.text();
  987. const parser = new DOMParser();
  988. const htmlDocument = parser.parseFromString(data, "text/html");
  989. const elements = htmlDocument.querySelectorAll(".discussionListItem");
  990. if (elements.length >= 1) {
  991. logWithPrefix(`За последний месяц на пользователя ${name} уже была написана жалоба`)
  992. return true;
  993. }
  994. }
  995. logWithPrefix(`За последний месяц на пользователя ${name} не было жалоб`)
  996. return false;
  997. }
  998.  
  999. async function get_xfToken() {
  1000. return document.querySelector('input[name="_xfToken"]' || {}).value;
  1001. }
  1002.  
  1003. async function CreateComplaint(title, message_html, retries = 3, delay = 60000) {
  1004. try {
  1005. let xfTokenValue = await get_xfToken();
  1006. logWithPrefix(`Получен xfToken для создания автоматической жалобы ${xfTokenValue}`);
  1007.  
  1008. let params = new URLSearchParams([
  1009. ['hidden', ''],
  1010. ['prefix_id[]', '92'],
  1011. ['title', `Жалоба на пользователя ${title}`],
  1012. ['message_html', convertToHtmlText(message_html)],
  1013. ['_xfRelativeResolver', 'https://zelenka.guru/forums/801/create-thread'],
  1014. ['tags', ''],
  1015. ['watch_thread', '1'],
  1016. ['watch_thread_state', '1'],
  1017. ['poll[question]', ''],
  1018. ['poll[responses][]', ''],
  1019. ['poll[responses][]', ''],
  1020. ['poll[responses][]', ''],
  1021. ['poll[max_votes_type]', 'single'],
  1022. ['poll[change_vote]', '1'],
  1023. ['poll[view_results_unvoted]', '1'],
  1024. ['_xfToken', xfTokenValue],
  1025. ['_xfRequestUri', '/forums/801/create-thread'],
  1026. ['_xfNoRedirect', '1'],
  1027. ['_xfToken', xfTokenValue],
  1028. ['_xfResponseType', 'json']
  1029. ]);
  1030.  
  1031. if (checkKeyValue === 'UnnotifyOnTopicCreation') {
  1032. params.append('dont_alert_followers', '1');
  1033. }
  1034.  
  1035. let response = await fetch('https://zelenka.guru/forums/801/add-thread', {
  1036. method: 'POST',
  1037. body: params
  1038. });
  1039.  
  1040. logWithPrefix(`Жалоба на пользователя ${name} была успешно создана`)
  1041.  
  1042. let responseText = await response.text();
  1043. if (responseText.includes('error')) {
  1044. throw new Error('Error in response text');
  1045. }
  1046.  
  1047. return response;
  1048. } catch (error) {
  1049. if (retries > 1) {
  1050. console.error('Ошибка при получении данных:', error);
  1051. logWithPrefix(`Повторим через ${delay}ms...`);
  1052.  
  1053. return await new Promise((resolve, reject) => {
  1054. setTimeout(async () => {
  1055. try {
  1056. const resp = await CreateComplaint(title, message_html, retries - 1, delay);
  1057. resolve(resp);
  1058. } catch (err) {
  1059. reject(err);
  1060. }
  1061. }, delay);
  1062. });
  1063. } else {
  1064. logWithPrefix('Все попытки были исчерпаны:', error);
  1065. return
  1066. }
  1067. }
  1068. }
  1069.  
  1070. async function fetchWithRetry(url, retries = 3, delay = 5000) {
  1071. try {
  1072. const response = await fetch(url, {
  1073. "headers": {
  1074. "pragma": "no-cache",
  1075. "cache-control": "no-cache",
  1076. "accept": "application/json, text/javascript, */*; q=0.01",
  1077. "x-requested-with": "XMLHttpRequest",
  1078. "sec-fetch-site": "same-origin",
  1079. "sec-fetch-mode": "cors",
  1080. "sec-fetch-dest": "empty",
  1081. "accept-encoding": "gzip, deflate, br",
  1082. }});
  1083.  
  1084. return response;
  1085. } catch (error) {
  1086. if (retries > 0) {
  1087. console.error('Ошибка при получении данных:', error);
  1088. logWithPrefix(`Повторим через ${delay}ms...`);
  1089.  
  1090. return await new Promise((resolve, reject) => {
  1091. setTimeout(async () => {
  1092. try {
  1093. const resp = await fetchWithRetry(url, retries - 1, delay);
  1094. resolve(resp);
  1095. } catch (err) {
  1096. reject(err);
  1097. }
  1098. }, delay);
  1099. });
  1100. } else {
  1101. throw error;
  1102. }
  1103. }
  1104. }
  1105.  
  1106. function xenforoLogAndAlert(text, title) {
  1107. logWithPrefix(text)
  1108. XenForo.alert(`${text}`, `${title}`)
  1109. }
  1110.  
  1111. function encodeOutput(output) {
  1112. const text = output.toString();
  1113. return encodeURIComponent(text).replace("\n", "/%0A/g");
  1114. }
  1115.  
  1116. function cleanURL(domain, link) {
  1117. let domainCount = (link.match(/https:\/\//g) || []).length;
  1118.  
  1119. if (domainCount > 1) {
  1120. link = link.substring(link.lastIndexOf("https://"));
  1121. }
  1122.  
  1123. let sharedIpsPresent = link.includes('shared-ips');
  1124.  
  1125. if (!link.startsWith('https://')) {
  1126. link = 'https://' + domain + '/' + link;
  1127. }
  1128.  
  1129. let url;
  1130. try {
  1131. url = new URL(link);
  1132. } catch (error) {
  1133. console.error("Provided link is not a valid URL.");
  1134. return;
  1135. }
  1136.  
  1137. let pathnames = url.pathname.split('/').filter(p => p.trim() !== '' && p.trim() !== 'shared-ips');
  1138.  
  1139. if (sharedIpsPresent) {
  1140. pathnames.push('shared-ips');
  1141. }
  1142.  
  1143. url.pathname = pathnames.join('/');
  1144.  
  1145. if (!url.href.endsWith('/')) {
  1146. url.href += '/';
  1147. }
  1148.  
  1149. return url.href;
  1150. }
  1151.  
  1152.  
  1153. function logWithPrefix(text) {
  1154. console.log(`[Lolzteam Multiaccount Finder] ${text}`)
  1155. }
  1156.  
  1157. async function template(name, description, link, open = true, source = undefined, bannedPercent) {
  1158. let title = encodeOutput(`Жалоба на пользователя ${name}`)
  1159. let message;
  1160.  
  1161. if (await checkKeyValue("ForumComplaintHide") === "true") {
  1162. message = encodeOutput(`[CLUB]1. Никнейм нарушителя и ссылка на профиль: ${link.replace('/shared-ips', '')}\n2. Краткое описание жалобы: ${description}\n3. Доказательства: ${link}[/CLUB]\nЖалоба была сформирована с использованием Lolzteam Multiaccount Finder ([URL]https://${domain}/threads/6142521/[/URL])[CLUB]Создано по кнопке\nДата и время: ${moscowTime}[/CLUB]`)
  1163. }
  1164. else {
  1165. message = encodeOutput(`1. Никнейм нарушителя и ссылка на профиль: ${link.replace('/shared-ips', '')}\n2. Краткое описание жалобы: ${description}\n3. Доказательства: ${link}\n\nЖалоба была сформирована с использованием Lolzteam Multiaccount Finder ([URL]https://${domain}/threads/6142521/[/URL])[CLUB]Создано по кнопке\nДата и время: ${moscowTime}[/CLUB]`)
  1166. }
  1167.  
  1168. const template = `https://${domain}/forums/801/create-thread?prefix_id=92&title=${title}&message=${message}`
  1169.  
  1170. if (await checkKeyValue("AutomaticFraudComplaint") === "true") {
  1171. if (await checkKeyValue("ForumComplaintHide") === "true") {
  1172. message = `[CLUB]1. Никнейм нарушителя и ссылка на профиль: ${link.replace('/shared-ips', '')}\n2. Краткое описание жалобы: ${description}\n3. Доказательства: ${link}[/CLUB]\nЖалоба была сформирована с использованием Lolzteam Multiaccount Finder ([URL]https://${domain}/threads/6142521/[/URL])[CLUB]Создано автоматически\nДата и время: ${moscowTime}[/CLUB]`
  1173. }
  1174. else {
  1175. message = `1. Никнейм нарушителя и ссылка на профиль: ${link.replace('/shared-ips', '')}\n2. Краткое описание жалобы: ${description}\n3. Доказательства: ${link}\n\nЖалоба была сформирована с использованием Lolzteam Multiaccount Finder ([URL]https://${domain}/threads/6142521/[/URL])[CLUB]Создано автоматически\nДата и время: ${moscowTime}[/CLUB]`
  1176. }}
  1177.  
  1178. if (source === "members" || source === "registered") {
  1179. if (await checkKeyValue("AutomaticFraudComplaint") === "true") {
  1180. if (parseInt(await checkKeyValue("AutomaticFraudComplaintPercentage")) <= Math.round(bannedPercent)) {
  1181. if (await checkKeyValue("CheckComplaintsPreviously") === "true") {
  1182. if (await PreviouslyCreatedComplaints(name) === false) {
  1183. await CreateComplaint(name, message)}
  1184.  
  1185. } else {
  1186. await CreateComplaint(name, message)}
  1187. }
  1188. }
  1189. }
  1190. if (open) {
  1191. window.open(`${template}`, '_blank');
  1192. logWithPrefix(`Подготовленный шаблон с созданием жалобы был открыт в новом окне`)
  1193. }
  1194. else {
  1195. return template
  1196. }
  1197. }
  1198.  
  1199. function convertToHtmlText(text) {
  1200. // Заменяем все новые строки на <br>
  1201. let htmlText = text.replace(/\n/g, '<br>');
  1202. // Добавляем <p> в начале и </p> в конце
  1203. htmlText = '<p>' + htmlText + '</p>';
  1204. return htmlText;
  1205. }
  1206.  
  1207. async function checkUser(link, source, gifId, style = null) {
  1208. link = cleanURL(domain, link);
  1209. logWithPrefix(`${link}`);
  1210.  
  1211. const notToCheckPreviouslyChecked = await checkKeyValue('not-to-check-previously-checked') === "true";
  1212. const checkedList = (await checkKeyValue("checked-list")) || [];
  1213. const checkedItem = checkedList.find(item => item.link === link.replace('/shared-ips', ''));
  1214.  
  1215. if (notToCheckPreviouslyChecked && checkedItem) {
  1216. const { date, output: prevOutput } = checkedItem;
  1217. const output = prevOutput + `\nРезультат проверки был сохранен ${date}`;
  1218. const gifElement = document.getElementById(gifId);
  1219.  
  1220. if (gifElement) {
  1221. gifElement.src = prevOutput.includes("мошенник") ? 'https://i.imgur.com/g5GxNHD.png' :
  1222. prevOutput.includes("VPN") ? 'https://i.imgur.com/o5qNA1o.png' :
  1223. 'https://i.imgur.com/i4OlWJk.png';
  1224. if (source === 'members' || source === 'registered') OnlineChangeTable('dl.checked', 1);
  1225. gifElement.title = `${output}`;
  1226.  
  1227. gifElement.addEventListener('click', async () => {
  1228. gifElement.removeEventListener('click', onClick);
  1229. const index = checkedList.indexOf(checkedItem);
  1230. if (index > -1) {
  1231. checkedList.splice(index, 1);
  1232. await changeItemValue("checked-list", checkedList);
  1233. }
  1234. gifElement.src = 'https://i.imgur.com/I5VH0zp.gif';
  1235. checkUser(link, undefined, gifId);
  1236. });
  1237. } else {
  1238. xenforoLogAndAlert(`${output}`, `Lolzteam Multiaccount Finder`);
  1239. }
  1240. return;
  1241. }
  1242.  
  1243. if (await checkKeyValue("UserCheckInterval") == 'true') {
  1244. if (source === "members" || source === "registered") {
  1245. await sleepBeforeUserCheck()
  1246. }};
  1247.  
  1248. const response = await fetchWithRetry(link);
  1249. const data = await response.text();
  1250. const parser = new DOMParser();
  1251. const htmlDocument = parser.parseFromString(data, "text/html");
  1252. const userLogs = htmlDocument.getElementsByClassName("userLog");
  1253. const numUserLogs = userLogs.length;
  1254. let [bannedUsersCount, nonBannedUsersCount, bannedThisMonthCount, hackedUsersCount, unBannedUsersCount] = [0, 0, 0, 0, 0];
  1255. const nameEl = htmlDocument.querySelector(`a.crumb[href^="https://${domain}/"] span`);
  1256. const name = nameEl ? nameEl.textContent.trim() : "";
  1257. //console.log(name);
  1258. const gifElement = document.getElementById(gifId);
  1259.  
  1260. if (!name) {
  1261. if (gifElement) {
  1262. gifElement.src = 'https://i.imgur.com/wqXWudH.png';
  1263. }
  1264. OnlineChangeTable('dl.errors', 1);
  1265.  
  1266. if (data.includes("У Вас нет прав для просмотра этой страницы или для выполнения этого действия.")) {
  1267. return
  1268. }
  1269.  
  1270. if (await checkKeyValue('retry-after-error') === true) {
  1271. return new Promise((resolve) => {
  1272. setTimeout(() => {
  1273. logWithPrefix("Повторная проверка для пользователя:", link);
  1274. resolve(checkUser(link, source, gifId));
  1275. }, 15000);
  1276. });
  1277. } else {
  1278. throw new Error("Name not found");
  1279. }
  1280. }
  1281.  
  1282. const checkValue = await checkKeyValue("UnmarkBaseUnblocked");
  1283. checkValue === "true" ? logWithPrefix('UnmarkBaseUnblocked: True') : logWithPrefix('UnmarkBaseUnblocked: False');
  1284.  
  1285. for (const log of userLogs) {
  1286. const spans = log.getElementsByTagName("span");
  1287. const bannedSpan = Array.from(spans).find(span => span.classList.contains("banned"));
  1288. const isBanned = !!bannedSpan;
  1289.  
  1290. if (isBanned) {
  1291. const banReasonDiv = log.querySelector('div.banReason');
  1292. if (banReasonDiv && banReasonDiv.textContent.includes('Взломан')) {
  1293. const checkValue = await checkKeyValue("HackBanExclude");
  1294. if (checkValue === "true") {
  1295. hackedUsersCount++
  1296. nonBannedUsersCount++;
  1297. continue;
  1298. }
  1299. }
  1300. const boldElements = log.getElementsByTagName("b");
  1301. const anchorElements = Array.from(boldElements).flatMap(b => Array.from(b.getElementsByTagName("a")));
  1302. const hasBannedClass = anchorElements.some(a => !!a.querySelector("span.banned"));
  1303.  
  1304. if (checkValue === "true" && (!boldElements.length || hasBannedClass)) {
  1305. bannedUsersCount++;
  1306. } else if (checkValue !== "true") {
  1307. bannedUsersCount++;
  1308. } else {
  1309. bannedThisMonthCount--;
  1310. unBannedUsersCount++
  1311. nonBannedUsersCount++;
  1312. }
  1313.  
  1314. const li = log.querySelector('li.ipLog');
  1315. if (li) {
  1316. const abbr = li.querySelector('abbr.DateTime');
  1317. const span = li.querySelector('span.DateTime');
  1318. const title = abbr ? abbr.getAttribute('data-datestring') : span.getAttribute('title');
  1319. if (title.includes(month) || title.includes('Сегодня') || title.includes('Вчера')) {
  1320. bannedThisMonthCount++;
  1321. }
  1322. }
  1323. } else {
  1324. nonBannedUsersCount++;
  1325. }
  1326. }
  1327.  
  1328. const totalUsers = bannedUsersCount + nonBannedUsersCount;
  1329. const [bannedPercent, nonBannedPercent] = [bannedUsersCount, nonBannedUsersCount].map(x => totalUsers ? (x / totalUsers * 100).toFixed(2) : 0);
  1330. const bannedThisMonthPercent = bannedUsersCount ? (bannedThisMonthCount / bannedUsersCount * 100).toFixed(2) : 0;
  1331.  
  1332. const [showBlockedPercentage, showUnblockedPercentage, showTotalUsersIp, showBlockedThisMonthPercentage] = await Promise.all(
  1333. ["show-blocked-percentage", "show-unblocked-percentage", "show-total-users-ip", "show-blocked-this-month-percentage"].map(
  1334. key => checkKeyValue(key).then(value => value === "true")));
  1335.  
  1336. let output = `${showBlockedPercentage ? `\n% заблокированных: ${bannedPercent} (${bannedUsersCount})` : ''}${showUnblockedPercentage ? `\n% не заблокированных: ${nonBannedPercent} (${nonBannedUsersCount})` : ''}${showTotalUsersIp ? `\nОбщее количество пользователей в общих IP: ${numUserLogs}` : ''}${showBlockedThisMonthPercentage ? `\n% от заблокированных в этом месяце: ${bannedThisMonthPercent} (${bannedThisMonthCount})` : ''}`;
  1337.  
  1338. function updateGifElement(gifElement, src, title, cursor = null) {
  1339. if (gifElement) {
  1340. gifElement.src = src;
  1341. gifElement.title = title;
  1342. if (cursor) gifElement.style.cursor = cursor;
  1343. }
  1344. }
  1345.  
  1346. function xenforoLogAndAlertWrapper(output) {
  1347. if (!gifElement) {
  1348. xenforoLogAndAlert(output, "Lolzteam Multiaccount Finder");
  1349. }
  1350. }
  1351.  
  1352. async function updateOutputAndTable(condition, source, imgSrc, outputPrefix, tableSuffix, sendMessage = false) {
  1353. if (condition) {
  1354. if (outputPrefix.includes('пользователей по заданным параметрам не найдено')) {
  1355. output = `${name} - ${outputPrefix}`;
  1356. } else {
  1357. output = output ? `${name} - ${outputPrefix} ${output}` : `${name} - ${outputPrefix}`;
  1358. }
  1359.  
  1360. updateGifElement(gifElement, imgSrc, output);
  1361. xenforoLogAndAlertWrapper(output);
  1362. if (source === "members" || source === "registered") {
  1363. OnlineChangeTable(`dl.${tableSuffix}`, 1);
  1364. if (sendMessage) {
  1365. if (outputPrefix == 'мошенник') {
  1366. sendMessageIfConditionsMet(`❗️ Обнаружен мошенник - ${link.replace("/shared-ips", "")} (<a href="${await template(name, output, link, false)}">cоздать жалобу</a>) \n${output}`);
  1367. } else if (outputPrefix == 'возможно мошенник') {
  1368. sendMessageIfConditionsMet(`❗️ Возможно мошенник - ${link.replace("/shared-ips", "")} (<a href="${await template(name, output, link, false)}">cоздать жалобу</a>) \n${output}`);
  1369. }
  1370. }
  1371. }
  1372. }
  1373. }
  1374.  
  1375. if (htmlDocument.body.textContent.includes("Пользователей по заданным параметрам не найдено.") || htmlDocument.body.textContent.includes("No matching users were found.")) {
  1376. updateOutputAndTable(true, source, "https://i.imgur.com/i4OlWJk.png", "пользователей по заданным параметрам не найдено.", "clean");
  1377. } else {
  1378. const cases = createCases(bannedPercent, nonBannedUsersCount, bannedUsersCount);
  1379. let caseFound = false;
  1380.  
  1381. function createClickListener(templateFunc, ...args) {
  1382. return function () {
  1383. templateFunc(...args);
  1384. };
  1385. }
  1386.  
  1387. for (const currentCase of cases) {
  1388. updateOutputAndTable(currentCase.condition && !caseFound, source, currentCase.imgSrc, currentCase.outputPrefix, currentCase.tableSuffix, currentCase.sendMessage);
  1389. if (currentCase.condition) {
  1390. caseFound = true;
  1391.  
  1392. if (currentCase.isPossibleScammer && gifElement) {
  1393. const eventListener = createClickListener(template, name, output, link, true, source, bannedPercent);
  1394. const result = template(name, output, link, false, source, bannedPercent);
  1395. logWithPrefix(`Ссылка для создания жалобы была подготовлена ${await result}`);
  1396. logWithPrefix(source)
  1397. gifElement.style.cursor = 'pointer';
  1398. gifElement.addEventListener("click", eventListener);
  1399. }
  1400. }
  1401. }
  1402.  
  1403. if (!caseFound) {
  1404. updateOutputAndTable(true, source, "https://i.imgur.com/i4OlWJk.png", "мультиаккаунт", "clean");
  1405. }
  1406. }
  1407.  
  1408. if (notToCheckPreviouslyChecked) {
  1409. checkedList.push({ link: link.replace('/shared-ips', ''), output: output, date: date.toLocaleString() });
  1410. await changeItemValue("checked-list", checkedList);
  1411. }
  1412. }

QingJ © 2025

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