Drive2 Old Auto UnSubscriber

работает на стренице подписок на сашины. проврка на бывшие авто и взаимные подписки, автоматически отписывается от бывших авто и от машин чьи владельцы не во взаимной подписке.

目前為 2025-01-29 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Drive2 Old Auto UnSubscriber
// @namespace    drive2.ru
// @version      0.78
// @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;

// Создаем блок статистики
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';
document.body.appendChild(statsDiv);

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');

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');

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';

unsubscribeCheckboxDiv.appendChild(unsubscribeCheckbox);
unsubscribeCheckboxDiv.appendChild(unsubscribeLabel);
statsDiv.appendChild(unsubscribeCheckboxDiv);
hideOldCarsCheckboxDiv.appendChild(hideOldCarsCheckbox);
hideOldCarsCheckboxDiv.appendChild(hideOldCarsLabel);
statsDiv.appendChild(hideOldCarsCheckboxDiv);
statsDiv.appendChild(checkButton);

let stats = {
    pages: 0,
    totalBlocks: 0,
    processedBlocks: 0,
    removedBlocks: 0,
    unsubscribedBlocks: 0,
    oldSubscribedBlocks: 0
};

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);
};

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, 3000));
        processBlocks();

    } else {
        console.log('Кнопка не найдена, продолжаем проверку...');
    }
};

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('.c-car-card-sa');
    updateStats();

    for (const block of blocks) {
        const titleElement = block.querySelector('.c-car-title');
        stats.totalBlocks = document.querySelectorAll('.c-car-card-sa').length;

        // Если чекбокс скрытия старых авто включен и блок старый – пропускаем или удаляем
        if (titleElement.classList.contains('x-secondary-color')) {
            stats.oldSubscribedBlocks++;
            stats.processedBlocks++;
            if (hideOldCarsCheckbox.checked) {
                block.remove();
                stats.removedBlocks++;
            }
            updateStats();
            continue;
        }

        const subscribeButton = block.querySelector('subscribe-button');
        const userId = block.querySelector('a.c-username')?.getAttribute('data-ihc-token');

        if (!userId) continue;

        // Делаем 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);
        const myCarIds = myCars.map(car => car.id);
        const hasMyCar = followedCarIds ? followedCarIds.some(carId => myCarIds.includes(carId)) : false;

        if (hasMyCar) {
            console.log('Блок содержит одну из моих машин, пропускаем.');
        } else {
            console.log('Блок не содержит моих машин.');
            if (unsubscribeCheckbox.checked) {
                unsubscribeCar(subscribeButton.getAttribute('uid'));
                stats.unsubscribedBlocks++;
            }
        }

        // Удаляем блок, если он не содержит мою машину
        block.remove();
        stats.removedBlocks++;
        stats.processedBlocks++;
        updateStats();

        // Ждём 1 секунду перед обработкой следующего блока
        await new Promise(resolve => setTimeout(resolve, 2000));
    }
    clickMoreButton();
};

checkButton.addEventListener('click', processBlocks);

function init_me() {
    // находим секретные ключи для автоматических запросов
    if (window.d2Env) {
        tail = window.d2Env.userId;
        fctx = window.d2Env.formContext['.FCTX'];
    } else {
        alert('обновите версию скрипта!');
        return;
    }
}

init_me();