您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Автоматически покидает выбранные переписки.
// ==UserScript== // @name Auto Leave Conversations // @namespace http://tampermonkey.net/ // @version 1.3 // @description Автоматически покидает выбранные переписки. // @author eretly // @match https://zelenka.guru/conversations/* // @match https://lolz.guru/conversations/* // @match https://lolz.live/conversations/* // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; const scriptNavigation = sessionStorage.getItem('scriptNavigation') === 'true'; if (!scriptNavigation) { localStorage.removeItem('selectedConversations'); localStorage.removeItem('leavingInProgress'); localStorage.removeItem('selectedRadioOption'); } else { sessionStorage.removeItem('scriptNavigation'); } const selectedConversations = JSON.parse(localStorage.getItem('selectedConversations')) || []; const leavingInProgress = localStorage.getItem('leavingInProgress') === 'true'; let isLeaving = false; const selectedRadioOption = localStorage.getItem('selectedRadioOption') || 'delete'; const leaveFormHTML = ` <div id="leave-conversation-form" style="border: 1px solid #ccc; border-radius: 6px; padding: 20px; width: 300px; background: #272727; position: fixed; top: 50px; right: 20px; z-index: 9999; display: none;"> <h2 class="heading h1">Покинуть переписку:</h2> <p>Если покинуть переписку, она исчезнет из Вашего списка.</p> <dl class="ctrlUnit"> <dt><label for="delete_type_delete">Обработка новых ответов:</label></dt> <dd> <ul> <li> <label for="delete_type_delete"> <input type="radio" name="delete_type" value="delete" id="delete_type_delete" ${selectedRadioOption === 'delete' ? 'checked' : ''}> Принимать последующие сообщения </label> <p class="hint">Если появятся новые ответы, то переписка будет восстановлена у Вас во входящих.</p> </li> <li> <label for="delete_type_delete_ignore"> <input type="radio" name="delete_type" value="delete_ignore" id="delete_type_delete_ignore" ${selectedRadioOption === 'delete_ignore' ? 'checked' : ''}> Игнорировать последующие сообщения </label> <p class="hint">Вы не будете получать уведомления о новых ответах, а переписка будет оставаться удалённой.</p> </li> </ul> </dd> </dl> <dl class="ctrlUnit submitUnit"> <dt></dt> <dd> <button id="submit-leave" class="button primary">Покинуть переписку</button> <button id="cancel-leave" class="button OverlayCloser">Отмена</button> </dd> </dl> </div> `; document.body.insertAdjacentHTML('beforeend', leaveFormHTML); // Утилиты для надежной работы function waitForElement(selector, timeout = 5000, parent = document) { return new Promise((resolve, reject) => { const element = parent.querySelector(selector); if (element) { resolve(element); return; } const observer = new MutationObserver((mutations, obs) => { const element = parent.querySelector(selector); if (element) { obs.disconnect(); resolve(element); } }); observer.observe(parent, { childList: true, subtree: true }); setTimeout(() => { observer.disconnect(); reject(new Error(`Элемент ${selector} не найден за ${timeout}мс`)); }, timeout); }); } async function safeClick(element, maxRetries = 3) { for (let i = 0; i < maxRetries; i++) { try { if (element && typeof element.click === 'function') { element.click(); return true; } else if (element) { const event = new MouseEvent('click', { view: window, bubbles: true, cancelable: true }); element.dispatchEvent(event); return true; } } catch (error) { console.log(`Попытка клика ${i + 1} неудачна:`, error); if (i < maxRetries - 1) { await delay(200); } } } return false; } function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } function getCurrentConversationId() { const urlMatch = window.location.pathname.match(/\/conversations\/(\d+)/); return urlMatch ? urlMatch[1] : null; } function getBaseUrl() { return window.location.origin; } document.addEventListener('click', (e) => { if (e.ctrlKey && e.target.closest('.conversationItem')) { e.preventDefault(); toggleConversationSelection(e.target.closest('.conversationItem')); } }); // Обработчик для выбора/снятия всех переписок document.addEventListener('mousedown', (e) => { if (e.ctrlKey && e.button === 1) { // Средняя кнопка мыши + Ctrl e.preventDefault(); const conversationList = document.querySelector('#ConversationListItems'); if (conversationList && e.target.closest('#ConversationListItems')) { toggleAllConversations(); } } }); document.addEventListener('keydown', (e) => { if (e.ctrlKey && e.key === 'Enter') { e.preventDefault(); if (selectedConversations.length > 0) { showLeaveForm(); } else { console.log("Нет выбранных переписок для покидания."); } } }); document.getElementById('submit-leave').addEventListener('click', async () => { const selectedRadio = document.querySelector('input[name="delete_type"]:checked'); if (selectedRadio) { localStorage.setItem('selectedRadioOption', selectedRadio.value); await leaveSelectedConversations(); hideLeaveForm(); } }); document.getElementById('cancel-leave').addEventListener('click', hideLeaveForm); if (leavingInProgress) { setTimeout(() => { leaveSelectedConversations(); }, 1000); } function toggleConversationSelection(conversationItem) { const conversationId = conversationItem.dataset.cid; if (selectedConversations.includes(conversationId)) { selectedConversations.splice(selectedConversations.indexOf(conversationId), 1); conversationItem.style.backgroundColor = ''; } else { selectedConversations.push(conversationId); conversationItem.style.backgroundColor = '#48b04cb2'; } localStorage.setItem('selectedConversations', JSON.stringify(selectedConversations)); } function toggleAllConversations() { const conversationItems = document.querySelectorAll('.conversationItem'); if (conversationItems.length === 0) { console.log("Переписки не найдены"); return; } const allConversationIds = Array.from(conversationItems).map(item => item.dataset.cid); const allSelected = allConversationIds.every(id => selectedConversations.includes(id)); if (allSelected) { console.log("Снимаем выделение со всех переписок"); conversationItems.forEach(item => { const cid = item.dataset.cid; const index = selectedConversations.indexOf(cid); if (index > -1) { selectedConversations.splice(index, 1); } item.style.backgroundColor = ''; }); } else { console.log("Выбираем все переписки"); conversationItems.forEach(item => { const cid = item.dataset.cid; if (!selectedConversations.includes(cid)) { selectedConversations.push(cid); } item.style.backgroundColor = '#48b04cb2'; }); } localStorage.setItem('selectedConversations', JSON.stringify(selectedConversations)); console.log(`Выбрано переписок: ${selectedConversations.length}`); } async function leaveSelectedConversations() { if (isLeaving) return; if (selectedConversations.length === 0) { console.log("Нет выбранных переписок."); return; } isLeaving = true; localStorage.setItem('leavingInProgress', 'true'); const remainingConversations = [...selectedConversations]; const baseUrl = getBaseUrl(); for (const cid of remainingConversations) { try { console.log(`Начинаем обработку переписки с ID: ${cid}`); const currentCid = getCurrentConversationId(); if (currentCid !== cid) { console.log(`Переходим к переписке ${cid}`); sessionStorage.setItem('scriptNavigation', 'true'); window.location.href = `${baseUrl}/conversations/${cid}/`; await new Promise(resolve => { const checkLoad = () => { if (document.readyState === 'complete' && getCurrentConversationId() === cid) { resolve(); } else { setTimeout(checkLoad, 100); } }; checkLoad(); }); await delay(300); } console.log('Ищем кнопку меню...'); const menuButton = await waitForElement('.membersAndActions .PopupControl', 3000); console.log(`Найдена кнопка меню для переписки с ID: ${cid}`); const menuClicked = await safeClick(menuButton); if (!menuClicked) { console.error(`Не удалось кликнуть по кнопке меню для переписки ${cid}`); continue; } await delay(200); console.log('Ищем ссылку выхода...'); const leaveLink = await waitForElement(`a[href*="conversations/${cid}/leave"]`, 2000); console.log(`Найдена ссылка выхода для переписки с ID: ${cid}`); const leaveLinkClicked = await safeClick(leaveLink); if (!leaveLinkClicked) { console.error(`Не удалось кликнуть по ссылке выхода для переписки ${cid}`); continue; } await delay(200); console.log('Ищем форму...'); const form = await waitForElement('form.xenForm', 3000); console.log(`Найдена форма для переписки с ID: ${cid}`); const selectedOption = localStorage.getItem('selectedRadioOption'); if (selectedOption) { try { const radioOption = await waitForElement( `input[name="delete_type"][value="${selectedOption}"]`, 1000, form ); console.log(`Выбираем радиокнопку: ${selectedOption}`); await safeClick(radioOption); await delay(200); } catch (error) { console.log(`Радиокнопка не найдена или не обязательна: ${error.message}`); } } console.log('Ищем кнопку отправки...'); const submitButton = await waitForElement('input[type="submit"]', 2000, form); console.log(`Найдена кнопка отправки для переписки с ID: ${cid}`); const submitClicked = await safeClick(submitButton); if (!submitClicked) { console.error(`Не удалось кликнуть по кнопке отправки для переписки ${cid}`); continue; } console.log(`Покинута переписка с ID: ${cid}`); const index = selectedConversations.indexOf(cid); if (index > -1) { selectedConversations.splice(index, 1); localStorage.setItem('selectedConversations', JSON.stringify(selectedConversations)); } await delay(500); } catch (error) { console.error(`Ошибка при обработке переписки ${cid}:`, error); continue; } } localStorage.removeItem('leavingInProgress'); isLeaving = false; console.log('Завершена обработка всех переписок'); window.location.href = `${baseUrl}/conversations/`; } function showLeaveForm() { document.getElementById('leave-conversation-form').style.display = 'block'; } function hideLeaveForm() { document.getElementById('leave-conversation-form').style.display = 'none'; } function simulateClick(element) { const mouseEvent = new MouseEvent('click', { view: window, bubbles: true, cancelable: true }); element.dispatchEvent(mouseEvent); } })();