您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
работает на стренице подписок на сашины. проврка на бывшие авто и взаимные подписки, автоматически отписывается от бывших авто и от машин чьи владельцы не во взаимной подписке.
当前为
// ==UserScript== // @name Drive2 Old Auto UnSubscriber // @namespace drive2.ru // @version 0.74 // @description работает на стренице подписок на сашины. проврка на бывшие авто и взаимные подписки, автоматически отписывается от бывших авто и от машин чьи владельцы не во взаимной подписке. // @author drive2 lover // @match https://www.drive2.ru/*/carsfollowing // @match https://www.drive2.com/*/carsfollowing // @license MIT // @grant none // ==/UserScript== let tail = ''; let fctx = ''; const MY_CARS_KEY = 'my_cars'; const myCars = localStorage.getItem(MY_CARS_KEY) ? JSON.parse(localStorage.getItem(MY_CARS_KEY)) : false; console.log('myCars', myCars); 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 = '10px'; statsDiv.style.zIndex = '1000'; document.body.appendChild(statsDiv); let stats = { pages: 0, totalBlocks: 0, processedBlocks: 0, removedBlocks: 0, unsubscribedBlocks: 0, oldSubscribedBlocks: 0 }; 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'; const updateStats = () => { statsDiv.innerHTML = `<div> Всего страниц: ${stats.pages}<br> Всего блоков: ${stats.totalBlocks}<br> Обработано: ${stats.processedBlocks}<br> Удалено: ${stats.removedBlocks}<br> Отписались: ${stats.unsubscribedBlocks}<br> Подписан на старые авто: ${stats.oldSubscribedBlocks} </div>`; statsDiv.appendChild(checkButton); }; updateStats(); // Функция для поиска и клика по кнопке const clickMoreButton = async () => { const button = document.querySelector('button.x-box-more'); if (button) { stats.pages++; console.log('Загружаем страницу ' + stats.pages); button.click(); 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)); } updateStats(); await new Promise(resolve => setTimeout(resolve, 2000)); processBlocks(); } else { console.log('Кнопка не найдена, продолжаем проверку...'); } }; checkButton.addEventListener('click', clickMoreButton); const unsubscribeCar = async (id) => { const url = '/ajax/subscription'; const data = { _: 'unsubscribe', type: 'car', id: id, // Используем переданный параметр '.FCTX': '_wfqzlwoyisgAAUdQnJ1LldlYi4xOjg2NDY5MTEyODQ1NTEzODkzNTMBE9aV1TTAfio19mv_lNB3SPb24bE' }; 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(); // Если сервер вернул JSON console.log('Ответ сервера:', result); } else { console.error('Ошибка запроса:', response.status); } } catch (error) { console.error('Ошибка выполнения POST-запроса:', error); } }; const processBlocks = async () => { const blocks = document.querySelectorAll('.c-car-card-sa'); stats.totalBlocks = blocks.length; updateStats(); myCars for (const block of blocks) { const titleElement = block.querySelector('.c-car-title'); // Если блок уже подписан на старую машину, пропускаем его if (titleElement.classList.contains('x-secondary-color')) { stats.oldSubscribedBlocks++; stats.processedBlocks++; updateStats(); continue; } const subscribeButton = block.querySelector('subscribe-button'); const userId = block.querySelector('a.c-username').getAttribute('data-ihc-token'); // Делаем GET-запрос для проверки подписки const response = await fetch(`https://www.drive2.com/_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); // Убираем null const myCarIds = myCars.map(car => car.id); const hasMyCar = followedCarIds ? followedCarIds.some(carId => myCarIds.includes(carId)) : false; if (hasMyCar) { console.log('Блок содержит одну из моих машин, пропускаем.'); } else { console.log('Блок не содержит моих машин, отписываемся.'); unsubscribeCar(subscribeButton.getAttribute('uid')); stats.unsubscribedBlocks++; } // Удаляем блок и обновляем статистику block.remove(); stats.removedBlocks++; stats.processedBlocks++; updateStats(); // Ждём 1 секунду перед обработкой следующего блока await new Promise(resolve => setTimeout(resolve, 2000)); } clickMoreButton(); }; function init_me() { // находим секретные ключи для автоматических запросов if (window.d2Env) { tail = window.d2Env.userId; // Получаем userId fctx = window.d2Env.formContext['.FCTX']; // Получаем FCTX console.log('userId:', tail); console.log('FCTX:', fctx); } else { alert('обновите версию скрипта!'); return; } } init_me();