работает на стренице подписок на сашины. проврка на бывшие авто и взаимные подписки, автоматически отписывается от бывших авто и от машин чьи владельцы не во взаимной подписке.
目前為
// ==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();