PanelControl - 1.8 Filtration Posts by language by Keywords - XFilter Twitter X-com (c) tapeavion

Hide posts by keywords with the dashboard and hide posts from verified accounts

目前为 2025-01-29 提交的版本。查看 最新版本

// ==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 });
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址