您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
работает на стренице подписок на сашины. проврка на бывшие авто и взаимные подписки, автоматически отписывается от бывших авто и от машин чьи владельцы не во взаимной подписке.
当前为
// ==UserScript== // @name Drive2 Old Auto UnSubscriber // @namespace drive2.com // @version 0.85 // @description работает на стренице подписок на сашины. проврка на бывшие авто и взаимные подписки, автоматически отписывается от бывших авто и от машин чьи владельцы не во взаимной подписке. // @author drive2 lover // @match https://www.drive2.com/* // @match https://www.drive2.ru/* // @license MIT // @grant none // ==/UserScript== let tail = ''; let fctx = ''; const MY_CARS_KEY = 'my_cars__'; let myCars = localStorage.getItem(MY_CARS_KEY) ? JSON.parse(localStorage.getItem(MY_CARS_KEY)) : false; const localStorageKey = 'saveLastPage'; let stats = { pages: 0, totalBlocks: 0, processedBlocks: 0, removedBlocks: 0, unsubscribedBlocks: 0, oldSubscribedBlocks: 0 }; // Создаем блок статистики const statsDiv = document.createElement('div'); statsDiv.style.position = 'fixed'; statsDiv.style.top = '0'; statsDiv.style.left = '0'; statsDiv.style.backgroundColor = '#333'; statsDiv.style.color = '#fff'; statsDiv.style.padding = '15px'; statsDiv.style.margin = '10px'; statsDiv.style.zIndex = '1000'; // Блок "Отписываться от авто" const unsubscribeCheckboxDiv = document.createElement('div'); const unsubscribeCheckbox = document.createElement('input'); unsubscribeCheckbox.type = 'checkbox'; unsubscribeCheckbox.checked = true; unsubscribeCheckbox.id = 'unsubscribe-checkbox'; const unsubscribeLabel = document.createElement('label'); unsubscribeLabel.textContent = ' Отписываться от авто'; unsubscribeLabel.style.marginRight = '10px'; unsubscribeLabel.setAttribute('for', 'unsubscribe-checkbox'); unsubscribeCheckboxDiv.appendChild(unsubscribeCheckbox); unsubscribeCheckboxDiv.appendChild(unsubscribeLabel); // statsDiv.appendChild(unsubscribeCheckboxDiv); // Блок "Скрывать старые авто" const hideOldCarsCheckboxDiv = document.createElement('div'); const hideOldCarsCheckbox = document.createElement('input'); hideOldCarsCheckbox.type = 'checkbox'; hideOldCarsCheckbox.checked = true; hideOldCarsCheckbox.id = 'hide-old-cars-checkbox'; const hideOldCarsLabel = document.createElement('label'); hideOldCarsLabel.textContent = ' Скрывать старые авто'; hideOldCarsLabel.style.marginRight = '10px'; hideOldCarsLabel.setAttribute('for', 'hide-old-cars-checkbox'); hideOldCarsCheckboxDiv.appendChild(hideOldCarsCheckbox); hideOldCarsCheckboxDiv.appendChild(hideOldCarsLabel); // statsDiv.appendChild(hideOldCarsCheckboxDiv); // Кнопка "Проверить" const checkButton = document.createElement('button'); checkButton.innerText = 'Проверить'; checkButton.style.marginTop = '10px'; checkButton.style.padding = '5px 10px'; checkButton.style.backgroundColor = '#007bff'; checkButton.style.color = '#fff'; checkButton.style.border = 'none'; checkButton.style.borderRadius = '5px'; checkButton.style.cursor = 'pointer'; // statsDiv.appendChild(checkButton); // Блок для ввода страниц и кнопки "Пропустить страниц" const skipPagesDiv = document.createElement('div'); skipPagesDiv.style.marginTop = '10px'; // Поле ввода количества страниц const pagesInput = document.createElement('input'); pagesInput.type = 'number'; pagesInput.min = '1'; pagesInput.value = localStorage.getItem(localStorageKey) ?? 1; pagesInput.style.width = '50px'; pagesInput.style.marginRight = '10px'; // Кнопка "Пропустить страниц" const skipButton = document.createElement('button'); skipButton.innerText = 'Пропустить страниц'; skipButton.style.padding = '5px 10px'; skipButton.style.backgroundColor = '#dc3545'; skipButton.style.color = '#fff'; skipButton.style.border = 'none'; skipButton.style.borderRadius = '5px'; skipButton.style.cursor = 'pointer'; // Добавляем инпут и кнопку в блок skipPagesDiv.appendChild(pagesInput); skipPagesDiv.appendChild(skipButton); //statsDiv.appendChild(skipPagesDiv); const carBlock = document.querySelector('.l-container .u-link-area'); const carBlockClass = carBlock?.parentElement?.classList?.[0] ?? null; const carsBlockClass = carBlock?.parentElement?.parentElement?.className ?? null; const carsBlockClassFormatted = carsBlockClass?.split(' ').map(className => '.' + className).join('') ?? ''; console.log('carBlockClass ' + carBlockClass); console.log('carsBlockClassFormatted ' + carsBlockClassFormatted); async function init_me() { if (window.d2Env) { tail = window.d2Env.userId; fctx = window.d2Env.formContext['.FCTX']; } else { alert('обновите версию скрипта!'); return; } } // Функция для нажатия кнопки загрузки страниц и очистки элементов async function skipPages() { let pagesToSkip = parseInt(pagesInput.value, 10); if (isNaN(pagesToSkip) || pagesToSkip <= 0) { alert('Введите корректное число страниц'); return; } for (let i = 0; i < pagesToSkip; i++) { const loadMoreButton = document.querySelector('button.x-box-more'); if (loadMoreButton) { await clearGrid(); loadMoreButton.click(); console.log(`Нажатие ${i + 1} на кнопку "Показать ещё"`); await new Promise(resolve => setTimeout(resolve, 3000)); // Ждём 3 секунды stats.pages++; updateStats(); await clearEmptyBlocks(); } else { alert('Кнопка "Показать ещё" не найдена, остановка.'); break; } } } // Функция очистки блока .o-grid.o-grid--2.o-grid--equal async function clearGrid() { const grids = document.querySelectorAll(carsBlockClassFormatted); if (grids.length > 0) { let blocks; for (const grid of grids) { blocks = grid.querySelectorAll('.' + carBlockClass); if (blocks.length) { stats.processedBlocks += blocks.length; blocks.forEach(car => car.remove()); } } console.log('Удалены авто из пропущенных страниц.'); } else { console.log('Не найдено блоков для очистки.'); } } skipButton.addEventListener('click', skipPages); const updateStats = () => { statsDiv.innerHTML = `<div> Страниц пройдено: ${stats.pages}<br> Иконок авто в очереди: ${stats.totalBlocks}<br> Обработано: ${stats.processedBlocks}<br> Отписались автоматом: ${stats.unsubscribedBlocks}<br> Подписан на старые авто: ${stats.oldSubscribedBlocks} </div>`; statsDiv.appendChild(unsubscribeCheckboxDiv); statsDiv.appendChild(hideOldCarsCheckboxDiv); statsDiv.appendChild(checkButton); statsDiv.appendChild(skipPagesDiv); localStorage.setItem(localStorageKey, stats.pages); }; // Функция для поиска и клика по кнопке "Загрузить еще" const clickMoreButton = async () => { const button = document.querySelector('button.x-box-more'); if (button) { console.error('Нашли кнопку дальнейшей загрузки'); stats.pages++; console.log('Загружаем страницу ' + stats.pages); button.click(); updateStats(); await clearEmptyBlocks(); await new Promise(resolve => setTimeout(resolve, 3000)); console.log('Загрузили блоки с авто, приступаем к их обработке'); processBlocks(); } else { alert('Кнопка не найдена, остановка процесса'); } }; async function clearEmptyBlocks() { document.querySelectorAll(carsBlockClassFormatted).forEach(grid => {if (!grid.querySelector('div')) { grid.remove(); }}); } async function loadMyCars() { if (!localStorage.getItem(MY_CARS_KEY)) { const response = await fetch('/my/r/'); const html = await response.text(); const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); const cars = []; doc.querySelectorAll('.c-car-draglist__item .c-car-card__caption a').forEach(car => { if (!car.classList.contains('x-secondary-color')) { const id = car.href.match(/\/(\d+)\//)[1]; cars.push({ id, name: car.textContent.trim() }); } }); localStorage.setItem(MY_CARS_KEY, JSON.stringify(cars)); console.log('обновил кеш моих авто', cars); } else { console.log('кеш моих авто уже загружен', myCars); } } function addCloseButton(element) { if (!element) return; const closeButton = document.createElement('button'); closeButton.innerHTML = '×'; // Символ "×" (крестик) closeButton.style.position = 'absolute'; closeButton.style.top = '5px'; closeButton.style.right = '5px'; closeButton.style.background = 'red'; closeButton.style.color = 'white'; closeButton.style.border = 'none'; closeButton.style.padding = '5px 10px'; closeButton.style.cursor = 'pointer'; closeButton.style.fontSize = '16px'; closeButton.style.borderRadius = '50%'; closeButton.addEventListener('click', function () { this.parentNode.remove(); // Удаляет родительский элемент кнопки }); if (window.getComputedStyle(element).position === 'static') { element.style.position = 'relative'; } element.appendChild(closeButton); element.classList.remove(carBlockClass); element.querySelector('a.u-link-area').remove(); element.style.position = 'sticky'; } const unsubscribeCar = async (id) => { const url = '/ajax/subscription'; const data = { _: 'unsubscribe', type: 'car', id: id, '.FCTX': fctx }; try { const response = await fetch(url, { method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: new URLSearchParams(data).toString() }); if (response.ok) { //const result = await response.json(); //console.log('Ответ сервера:', result); } else { console.error('Ошибка запроса:', response.status); } } catch (error) { console.error('Ошибка выполнения POST-запроса:', error); } }; const processBlocks = async () => { const blocks = document.querySelectorAll('.' + carBlockClass); console.error('На странице найдено ' + blocks.length + ' авто'); updateStats(); let myCarNames = ''; for (const block of blocks) { const titleElement = block.querySelector('.c-car-title'); stats.totalBlocks = document.querySelectorAll('.' + carBlockClass).length; // Если чекбокс скрытия старых авто включен и блок старый – пропускаем или удаляем if (titleElement.classList.contains('x-secondary-color')) { stats.oldSubscribedBlocks++; stats.processedBlocks++; if (hideOldCarsCheckbox.checked) { block.remove(); stats.removedBlocks++; console.error('Старое авто, удаляем'); } else { console.error('Старое авто, оставляем на странице'); addCloseButton(block); } updateStats(); continue; } const subscribeButton = block.querySelector('subscribe-button'); const userId = block.querySelector('a.c-username')?.getAttribute('data-ihc-token'); if (!userId) { console.error('Не найден userId, пропускаем'); continue }; // Делаем GET-запрос для проверки подписки const response = await fetch(`/_api/hovercards/${userId}?tail=${tail}`); const data = await response.json(); const followedCarIds = data?.subscriptions?.followedCars?.map(car => { const match = car.url.match(/\/(\d+)\//); return match ? match[1] : null; }).filter(Boolean); const myCarIds = myCars ? myCars.map(car => car.id) : []; if (myCarIds.length == 0) { console.error('У текущего юзера не найдены авто'); return; } const hasMyCar = followedCarIds ? followedCarIds.some(carId => myCarIds.includes(carId)) : false; if (hasMyCar) { myCarNames = myCars .filter(car => followedCarIds.includes(car.id)) .map(car => car.name) .join(', '); console.log(`Блок ${userId} содержит одну из моих машин (${myCarNames}), пропускаем.`); } else { let uid = subscribeButton.getAttribute('uid'); console.log('Блок ' + userId + ' не содержит моих машин.'); if (unsubscribeCheckbox.checked) { unsubscribeCar(uid); stats.unsubscribedBlocks++; console.error('Отписываемся от ' + uid); } } // Удаляем блок, если он не содержит мою машину block.remove(); stats.removedBlocks++; stats.processedBlocks++; updateStats(); // Ждём 1 секунду перед обработкой следующего блока await new Promise(resolve => setTimeout(resolve, 2000)); } console.error('Обработали все авто'); clickMoreButton(); }; function isCarsFollowingPage() { return (/^\/users\/(.*)\/carsfollowing/).test(window.location.pathname); } checkButton.addEventListener('click', processBlocks); loadMyCars(); init_me(); if (isCarsFollowingPage()) { document.body.appendChild(statsDiv); updateStats(); }