您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Hide posts by keywords with the dashboard and hide posts from verified accounts
当前为
// ==UserScript== // @name PanelControl - 1.8 Filtration Posts by language by Keywords - XFilter Twitter X-com (c) tapeavion // @namespace http://tampermonkey.net/ // @version 1.8 // @description Hide posts by keywords with the dashboard and hide posts from verified accounts // @author gullampis810 // @match https://x.com/* // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // @grant GM_setValue // @grant GM_getValue // @license MIT // @icon https://www.pinclipart.com/picdir/big/450-4507608_twitter-circle-clipart.png // ==/UserScript== (function () { 'use strict'; // ===== Настройки и инициализация ===== // const STORAGE_KEY = 'hiddenKeywords'; let hiddenKeywords = JSON.parse(localStorage.getItem(STORAGE_KEY)) || []; let hideVerifiedAccounts = true; // Скрывать подтвержденные аккаунты const languageFilters = { english: /[a-zA-Z]/, russian: /[А-Яа-яЁё]/, japanese: /[\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Han}]/u, ukrainian: /[А-Яа-яІіЄєЇїҐґ]/, belarusian: /[А-Яа-яЎўЁёІі]/, tatar: /[А-Яа-яӘәӨөҮүҖҗ]/, mongolian: /[\p{Script=Mongolian}]/u, chinese: /[\p{Script=Han}]/u, german: /[a-zA-ZßÄäÖöÜü]/, polish: /[a-zA-ZąćęłńóśźżĄĆĘŁŃÓŚŹŻ]/, french: /[a-zA-Zàâçéèêëîïôûùüÿ]/, swedish: /[a-zA-ZåäöÅÄÖ]/, estonian: /[a-zA-ZäõöüÄÕÖÜ]/, danish: /[a-zA-Z帿ŨÆ]/, }; let activeLanguageFilters = {}; // Словарь активных языков // ===== Сохранение в localStorage ===== // function saveKeywords() { localStorage.setItem(STORAGE_KEY, JSON.stringify(hiddenKeywords)); } // ===== Функция для обновления отображения по языкам ===== // function updateLanguageFilter(language) { if (activeLanguageFilters[language]) { delete activeLanguageFilters[language]; } else { activeLanguageFilters[language] = languageFilters[language]; } hidePosts(); // Пересчитываем скрытие постов } // ===== Проверка языка текста ===== // function isTextInLanguage(text) { for (const [language, regex] of Object.entries(activeLanguageFilters)) { if (regex.test(text)) { return true; // Возвращаем true, если текст на этом языке } } return false; } // ===== Функция Скрытия постов ===== // function hidePosts() { document.querySelectorAll('article').forEach((article) => { const textContent = article.innerText.toLowerCase(); const isVerifiedAccount = hideVerifiedAccounts && article.querySelector('[data-testid="icon-verified"]'); if (hiddenKeywords.some(keyword => textContent.includes(keyword.toLowerCase())) || isTextInLanguage(textContent) || isVerifiedAccount) { article.style.display = 'none'; } }); } // ===== Создание панели управления ===== // function createControlPanel() { const panel = document.createElement('div'); panel.style.position = 'fixed'; panel.style.bottom = '0px'; panel.style.right = '440px'; panel.style.width = '450px'; panel.style.height = '545px'; // Увеличена высота панели panel.style.padding = '8px'; panel.style.fontFamily = 'Arial, sans-serif'; panel.style.backgroundColor = '#34506c'; panel.style.color = '#fff'; panel.style.borderRadius = '8px'; panel.style.boxShadow = '0 0 10px rgba(0,0,0,0.5)'; panel.style.zIndex = '9999'; panel.style.overflow = 'auto'; // Делаем прокручиваемым содержимое панели panel.style.transition = 'height 0.3s ease'; // Анимация высоты panel.innerHTML = ` <h3 style="margin: 0; font-size: 16px;">Hiding Control</h3> <input id="keywordInput" type="text" placeholder="Enter the word" style="width: calc(100% - 95px); height: 30px; padding: 5px; margin: 10px 0; border-radius: 5px; border: none;"> <button id="addKeyword" style="position: relative; width: 80px; padding: 8px; margin-bottom: 5px; background: #203142; color: white; border: none; border-radius: 5px; height: 45px; left: 5px;"> Add it </button> <button id="clearKeywords" style="width: 75px; padding: 8px; margin-bottom: 5px; background: #203142; color: white; border: none; border-radius: 5px; height: 40px;">Clear all</button> <button id="exportKeywords" style="width: 60px; padding: 8px; margin-bottom: 5px; background: #203142; color: white; border: none; border-radius: 5px; height: 40px;">Export</button> <button id="importKeywords" style="width: 60px; padding: 8px; background: #203142; color: white; border: none; border-radius: 5px; margin-bottom: 10px; height: 40px;">Import</button> <button id="toggleVerifiedPosts" style="width: 242px; padding: 8px; margin-bottom: 5px; background: #203142; color: white; border: none; border-radius: 5px; height: 40px; font-size: 13px; font-weight: bold;"> Hide verified accounts: Click to Disable </button> <label style="display: block; margin: 10px 0;"></label> <ul id="keywordList" style="list-style: inside; padding: 0; margin-top: 10px; font-size: 14px; color: #fff;"></ul> <div style="margin-top: 20px; font-size: 14px; color: #fff;"> <p>Select Language to Hide Posts:</p> <div id="languageButtons" style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px;"> ${Object.keys(languageFilters).map(language => ` <label style="display: flex; align-items: center; color: #fff;"> <input type="checkbox" id="lang-${language}" style="margin-right: 8px;"> ${language.charAt(0).toUpperCase() + language.slice(1)} </label> `).join('')} </div> </div> `; document.body.appendChild(panel); // Обработчик для кнопок языков Object.keys(languageFilters).forEach(language => { const checkbox = document.getElementById(`lang-${language}`); checkbox.addEventListener('change', () => { updateLanguageFilter(language); // Включение/выключение фильтра для языка }); }); // Стили для подсветки const style = document.createElement('style'); style.textContent = ` button { transition: box-shadow 0.3s, transform 0.3s; } button:hover { box-shadow: 0 0 10px rgba(255, 255, 255, 0.7); } button:active { transform: scale(0.95); box-shadow: 0 0 5px rgba(255, 255, 255, 0.7); } `; document.head.appendChild(style); // ======== Кнопка iOS-переключатель Panel FilterX ========= // // Проверяем сохраненное состояние переключателя в localStorage let isSwitchOn = localStorage.getItem('isSwitchOff') === 'true'; // Начальное состояние переключателя из localStorage // Создание элементов панели const toggleButton = document.createElement('div'); toggleButton.style.position = 'fixed'; toggleButton.style.top = '94%'; toggleButton.style.right = '90px'; toggleButton.style.width = '192px'; toggleButton.style.display = 'flex'; toggleButton.style.alignItems = 'center'; toggleButton.style.gap = '10px'; toggleButton.style.zIndex = '1'; toggleButton.style.background = '#15202b'; toggleButton.style.border = '4px solid #6c7e8e'; toggleButton.style.borderRadius = '18px'; toggleButton.style.boxSizing = 'border-box'; // Создаем label для переключателя const toggleLabel = document.createElement('label'); toggleLabel.style.display = 'inline-block'; toggleLabel.style.width = '50px'; toggleLabel.style.height = '25px'; toggleLabel.style.borderRadius = '25px'; toggleLabel.style.backgroundColor = '#0d1319'; toggleLabel.style.position = 'relative'; toggleLabel.style.cursor = 'pointer'; toggleLabel.style.transition = 'background-color 0.3s'; // Создаем кружок переключателя const toggleSwitch = document.createElement('div'); toggleSwitch.style.position = 'absolute'; toggleSwitch.style.width = '21px'; toggleSwitch.style.height = '21px'; toggleSwitch.style.borderRadius = '50%'; toggleSwitch.style.backgroundColor = '#6c7e8e'; toggleSwitch.style.top = '2px'; // Устанавливаем начальное положение кружка в зависимости от состояния toggleSwitch.style.left = isSwitchOn ? 'calc(100% - 23px)' : '2px'; // Если выключено, то слева, если включено — справа // Плавная анимация toggleSwitch.style.transition = 'left 0.3s ease'; toggleSwitch.style.boxShadow = 'rgb(21, 32, 43) -1px 1px 4px 1px'; // Устанавливаем начальное состояние фона toggleLabel.style.backgroundColor = isSwitchOn ? '#425364' : '#0d1319'; // Функция для изменения состояния переключателя function toggleSwitchState() { isSwitchOn = !isSwitchOn; localStorage.setItem('isSwitchOn', isSwitchOn.toString()); // Сохраняем состояние в localStorage (как строку) // Обновляем стиль переключателя toggleSwitch.style.left = isSwitchOn ? 'calc(100% - 23px)' : '2px'; toggleLabel.style.backgroundColor = isSwitchOn ? '#425364' : '#0d1319'; } // Добавляем обработчик на клик по переключателю toggleLabel.addEventListener('click', toggleSwitchState); // Добавляем элементы на страницу toggleLabel.appendChild(toggleSwitch); toggleButton.appendChild(toggleLabel); document.body.appendChild(toggleButton); // Добавление текста к переключателю const toggleText = document.createElement('span'); toggleText.style.position = 'relative'; toggleText.style.right = '5px'; toggleText.textContent = 'Panel FilterX'; toggleText.style.color = '#6c7e8e'; toggleText.style.fontFamily = 'Arial, sans-serif'; toggleText.style.fontSize = '16px'; toggleText.style.marginLeft = '10px'; toggleText.style.fontWeight = 'bold'; toggleButton.appendChild(toggleText); //====================== Управление высотой панели =======================// let isPanelVisible = localStorage.getItem('panelVisible') === 'false'; // По умолчанию скрыта, если в localStorage не сохранено другое значение function togglePanel() { if (isPanelVisible) { panel.style.height = '0px'; // Сворачиваем панель setTimeout(() => { panel.style.display = 'none'; // Скрываем панель после анимации }, 300); } else { panel.style.display = 'block'; // Показываем панель setTimeout(() => { panel.style.height = '545px'; // Разворачиваем панель }, 10); } isPanelVisible = !isPanelVisible; // Переключаем состояние localStorage.setItem('panelVisible', isPanelVisible.toString()); // Сохраняем состояние } toggleButton.addEventListener('click', togglePanel); // При загрузке восстанавливаем состояние панели if (isPanelVisible) { panel.style.height = '545px'; // Разворачиваем панель panel.style.display = 'block'; } else { panel.style.height = '0px'; // Сворачиваем панель panel.style.display = 'none'; } // ===== Обработчики событий ===== // document.getElementById('addKeyword').addEventListener('click', () => { const input = document.getElementById('keywordInput'); const keyword = input.value.trim(); if (keyword && !hiddenKeywords.includes(keyword)) { hiddenKeywords.push(keyword); saveKeywords(); updateKeywordList(); hidePosts(); input.value = ''; } }); document.getElementById('clearKeywords').addEventListener('click', () => { if (confirm('Are you sure you want to clear the list?')) { hiddenKeywords = []; saveKeywords(); updateKeywordList(); hidePosts(); } }); document.getElementById('exportKeywords').addEventListener('click', () => { const dataStr = `data:text/json;charset=utf-8,${encodeURIComponent(JSON.stringify(hiddenKeywords))}`; const downloadAnchor = document.createElement('a'); downloadAnchor.setAttribute('href', dataStr); downloadAnchor.setAttribute('download', 'hidden_keywords.json'); document.body.appendChild(downloadAnchor); downloadAnchor.click(); document.body.removeChild(downloadAnchor); }); document.getElementById('importKeywords').addEventListener('click', () => { const input = document.createElement('input'); input.type = 'file'; input.accept = 'application/json'; input.addEventListener('change', (event) => { const file = event.target.files[0]; const reader = new FileReader(); reader.onload = () => { try { const importedKeywords = JSON.parse(reader.result); if (Array.isArray(importedKeywords)) { hiddenKeywords = [...new Set([...hiddenKeywords, ...importedKeywords])]; saveKeywords(); updateKeywordList(); hidePosts(); } else { alert('Incorrect file format.'); } } catch (e) { alert('Error reading the file.'); } }; reader.readAsText(file); }); input.click(); }); // ===== Кнопка для включения/выключения скрытия подтвержденных аккаунтов ===== // document.getElementById('toggleVerifiedPosts').addEventListener('click', () => { hideVerifiedAccounts = !hideVerifiedAccounts; document.getElementById('toggleVerifiedPosts').textContent = `Hide verified accounts: ${hideVerifiedAccounts ? 'Turn OFF' : 'Turn ON'}`; hidePosts(); // Перепроверка всех постов с новыми настройками }); } // ===== Обновление списка ключевых слов ===== // function updateKeywordList() { const list = document.getElementById('keywordList'); list.style.maxHeight = '150px'; list.style.overflowY = 'auto'; list.style.paddingRight = '10px'; list.style.border = '1px solid #ccc'; list.style.borderRadius = '5px'; list.style.backgroundColor = '#15202b'; list.style.boxShadow = '0 2px 5px rgba(0, 0, 0, 0.3)'; list.innerHTML = ''; hiddenKeywords.forEach((keyword, index) => { const listItem = document.createElement('li'); listItem.textContent = keyword; listItem.style.marginBottom = '5px'; const deleteButton = document.createElement('button'); deleteButton.textContent = '❌'; deleteButton.style.marginLeft = '10px'; deleteButton.style.backgroundColor = '#f44336'; deleteButton.style.color = '#fff'; deleteButton.style.border = 'none'; deleteButton.style.borderRadius = '3px'; deleteButton.style.cursor = 'pointer'; deleteButton.addEventListener('click', () => { hiddenKeywords.splice(index, 1); saveKeywords(); updateKeywordList(); hidePosts(); }); listItem.appendChild(deleteButton); list.appendChild(listItem); }); if (hiddenKeywords.length === 0) { list.textContent = 'Нет'; } } // ===== Инициализация ===== // createControlPanel(); updateKeywordList(); // Обновление списка при загрузке страницы hidePosts(); // ===== Наблюдатель для динамического контента ===== // const observer = new MutationObserver(hidePosts); observer.observe(document.body, { childList: true, subtree: true }); })();