您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Hide posts by keywords with the dashboard and hide posts from verified accounts
// ==UserScript== // @name Panel Control XFilter 2.4.60 (c) tapeavion // @namespace http://tampermonkey.net/ // @version 2.4.60 // @description Hide posts by keywords with the dashboard and hide posts from verified accounts // @author gullampis810 // @match https://x.com/* // @match https://x.com/i/grok* // @match https://blank.org/* // @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 // @downloadURL // @updateURL // ==/UserScript== (function () { "use strict"; // ===== Настройки и инициализация ===== // const STORAGE_KEY = "hiddenKeywords"; const FAVORITE_USERS_KEY = "favoriteUsers"; let hiddenKeywords = JSON.parse(localStorage.getItem(STORAGE_KEY)) || []; let favoriteUsers = JSON.parse(localStorage.getItem(FAVORITE_USERS_KEY)) || []; let unblockedKeywords = JSON.parse(localStorage.getItem("unblockedKeywords")) || []; let hideVerifiedAccounts = false; let hideNonVerifiedAccounts = JSON.parse(localStorage.getItem("hideNonVerifiedAccounts")) || false; let isShowingFavorites = false; 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帿ŨÆ]/, turkish: /[a-zA-ZıİçÇğĞöÖşŞüÜ]/, portuguese: /[a-zA-Zàáâãçéêíóôõúü]/, }; let activeLanguageFilters = {}; // Получение имени пользователя из профиля function getUsernameFromProfile() { const profileUrl = window.location.href; const match = profileUrl.match(/x\.com\/([^\?\/]+)/); if (match && match[1]) { return match[1].toLowerCase(); } const usernameElement = document.querySelector('a[href*="/"][role="link"] div[dir="ltr"] span'); return usernameElement ? usernameElement.textContent.replace('@', '').toLowerCase() : ''; } // ===== Сохранение в localStorage ===== // function saveKeywords() { localStorage.setItem(STORAGE_KEY, JSON.stringify(hiddenKeywords)); } function saveFavoriteUsers() { localStorage.setItem(FAVORITE_USERS_KEY, JSON.stringify(favoriteUsers)); } function saveUnblockedKeywords() { localStorage.setItem("unblockedKeywords", JSON.stringify(unblockedKeywords)); } // ===== Функция для обновления фильтров по языкам ===== // 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; } } return false; } // ===== Функция скрытия постов ===== // function hidePosts() { document.querySelectorAll("article").forEach((article) => { const textContent = article.innerText.toLowerCase(); const isVerifiedAccount = article.querySelector('[data-testid="icon-verified"]'); const usernameElement = article.querySelector('a[href*="/"]'); const usernameFromSpan = article.querySelector('a[href*="/"] div[dir="ltr"] span')?.textContent.replace('@', '').toLowerCase() || ''; const username = usernameElement ? usernameElement.getAttribute("href").slice(1).toLowerCase() : usernameFromSpan; // Отладка console.log("Checking username:", username, "in favoriteUsers:", favoriteUsers); // Проверяем, является ли пользователь избранным const isFavoriteUser = favoriteUsers.includes(username); // Если пользователь в избранных, не скрываем его посты if (isFavoriteUser) { article.style.display = ""; // Показываем пост return; // Прекращаем выполнение остальных проверок } // Проверка на ключевые слова const matchesKeyword = hiddenKeywords.some((keyword) => { try { return new RegExp(keyword, "i").test(textContent); } catch (e) { return textContent.includes(keyword.toLowerCase()); } }); // Логика скрытия постов const shouldHideVerified = hideVerifiedAccounts && isVerifiedAccount; const shouldHideNonVerified = hideNonVerifiedAccounts && !isVerifiedAccount; const shouldHideByLanguage = isTextInLanguage(textContent); const shouldHideByKeyword = matchesKeyword; // Скрываем пост только если он соответствует правилам скрытия if (shouldHideVerified || shouldHideNonVerified || shouldHideByLanguage || shouldHideByKeyword) { article.style.display = "none"; // Скрываем пост } else { article.style.display = ""; // Показываем пост } }); } // ===== Debounce для оптимизации ===== // function debounce(func, wait) { let timeout; return function (...args) { clearTimeout(timeout); timeout = setTimeout(() => func.apply(this, args), wait); }; } const debouncedHidePosts = debounce(hidePosts, 200); // ===== Создание панели управления ===== // const panel = document.createElement("div"); panel.style.position = "fixed"; panel.style.bottom = "62px"; panel.style.right = "180px"; panel.style.width = "335px"; panel.style.height = "510px"; 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> <div style="display: flex; align-items: center; gap: 10px;"> <span id="counter-keywords" title="Keywords">🔤 0</span> <span id="counter-users" title="Favorite Users">👤 0</span> </div> <h4 class="version-text">v2.4.60</h4> <div style="display: flex; align-items: center; gap: 5px; margin: 10px 0;"> <input id="keywordInput" type="text" placeholder="Enter the word or @username" style="width: calc(100% - 95px); height: 40px; padding: 5px; border-radius: 5px; border: none; background: #15202b; color: #fff;"> <button id="addKeyword" style="min-width: 79px; max-width: 80px; padding: 8px; background: #203142; color: white; border: none; border-radius: 5px; height: 40px;">Add it</button> </div> <div style="display: flex; flex-wrap: wrap; gap: 5px; position: relative;"> <div style="display: flex; align-items: center; gap: 5px;"> <div id="searchWrapper" style="position: relative;"> <input id="searchInput" type="text" placeholder="Search keywords or users" style="width: 240px; height: 40px; padding: 5px; border-radius: 5px; border: none; background-color: #15202b; color: #fff;"> <span id="clearSearch" style="display: none; position: absolute; right: 10px; top: 50%; transform: translateY(-50%); color: #fff; cursor: pointer;">✖</span> </div> <button id="openLanguagePopup" style="width: 80px; padding: 8px; background: #203142; color: white; border: none; border-radius: 5px; height: 40px; font-size: 13px; font-weight: bold;">Language Filtering</button> </div> <button id="exportKeywords" style="flex: 1; min-width: 60px; max-width: 70px; padding: 8px; background: #203142; color: white; border: none; border-radius: 5px; height: 40px;">Export</button> <button id="importKeywords" style="flex: 1; min-width: 60px; max-width: 70px; padding: 8px; background: #203142; color: white; border: none; border-radius: 5px; height: 40px;">Import</button> <button id="toggleBlockKeywords" style="flex: 1; min-width: 80px; max-width: 90px; padding: 8px; background: #203142; color: white; border: none; border-radius: 5px; height: 40px; font-size: 13px; font-weight: bold;">Unblock All</button> <button id="clearKeywords" style="flex: 1; min-width: 60px; max-width: 80px; padding: 8px; background: #203142; color: white; border: none; border-radius: 5px; height: 40px;">Clear all</button> <button id="toggleVerifiedPosts" style="width: 242px; padding: 8px; background: #203142; color: white; border: none; border-radius: 5px; height: 40px; font-size: 13px; font-weight: bold;">Hide verified accounts: Click to Enable</button> <button id="toggleNonVerifiedPosts" style="width: 242px; padding: 8px; background: #203142; color: white; border: none; border-radius: 5px; height: 40px; font-size: 13px; font-weight: bold;">Hide non-verified accounts: Turn ON</button> <button id="toggleFavoriteUsers" style="width: 80px; padding: 8px; background: #203142; color: white; border: none; border-radius: 5px; height: 40px; font-size: 13px; font-weight: bold;">Favorite Users</button> </div> <div id="listLabel" style="margin-top: 10px; font-size: 14px; color: #fff;">List of Keywords</div> <ul id="keywordList" style="list-style: position: relative; inside; padding: 0; margin-top: 5px; font-size: 14px; color: #fff; max-height: 135px; overflow-y: auto; border: 1px solid #ccc; border-radius: 5px; background-color: #15202b; box-shadow: 0 2px 5px rgba(0,0,0,0.3);"></ul> `; document.body.appendChild(panel); // ===== Функция обновления счетчиков ===== // function updateCounters() { document.getElementById("counter-keywords").textContent = `🔤 ${hiddenKeywords.length}`; document.getElementById("counter-users").textContent = `👤 ${favoriteUsers.length}`; } const searchInput = document.getElementById("searchInput"); const clearSearch = document.getElementById("clearSearch"); searchInput.addEventListener("input", () => { clearSearch.style.display = searchInput.value.trim() ? "block" : "none"; updateKeywordList(); updateCounters(); }); clearSearch.addEventListener("click", () => { searchInput.value = ""; clearSearch.style.display = "none"; updateKeywordList(); updateCounters(); }); const lengthFilterInput = document.createElement("input"); lengthFilterInput.type = "number"; lengthFilterInput.placeholder = "Min length"; lengthFilterInput.style.width = "80px"; lengthFilterInput.style.marginTop = "10px"; panel.appendChild(lengthFilterInput); lengthFilterInput.addEventListener("change", () => { debouncedHidePosts(); }); // ===== Предотвращение перетаскивания для поля ввода ===== // const keywordInput = document.getElementById("keywordInput"); keywordInput.addEventListener("mousedown", (event) => { event.stopPropagation(); }); // ===== Перетаскивание панели ===== // let isDragging = false; let offsetX = 0; let offsetY = 0; panel.addEventListener("mousedown", (event) => { isDragging = true; offsetX = event.clientX - panel.offsetLeft; offsetY = event.clientY - panel.offsetTop; panel.style.cursor = "grabbing"; }); document.addEventListener("mousemove", (event) => { if (isDragging) { panel.style.left = event.clientX - offsetX + "px"; panel.style.top = event.clientY - offsetY + "px"; } }); document.addEventListener("mouseup", () => { isDragging = false; panel.style.cursor = "grab"; }); // ===== Создание попапа для языков ===== // const languagePopup = document.createElement("div"); languagePopup.style.display = "none"; languagePopup.style.position = "fixed"; languagePopup.style.top = "460px"; languagePopup.style.right = "65px"; languagePopup.style.transform = "translate(-52%, 7%)"; languagePopup.style.backgroundColor = "#34506c"; languagePopup.style.padding = "20px"; languagePopup.style.borderRadius = "8px"; languagePopup.style.zIndex = "10000"; languagePopup.style.width = "288px"; languagePopup.style.boxShadow = "0 0 10px rgba(0,0,0,0.5)"; languagePopup.style.fontFamily = "Arial, sans-serif"; for (const language in languageFilters) { const checkbox = document.createElement("input"); checkbox.type = "checkbox"; checkbox.id = `lang-${language}`; checkbox.name = language; const label = document.createElement("label"); label.htmlFor = `lang-${language}`; label.textContent = language.charAt(0).toUpperCase() + language.slice(1); const wrapper = document.createElement("div"); wrapper.appendChild(checkbox); wrapper.appendChild(label); languagePopup.appendChild(wrapper); } const closeButton = document.createElement("button"); closeButton.textContent = "X"; closeButton.style.position = "relative"; closeButton.style.width = "40px"; closeButton.style.height = "40px"; closeButton.style.borderRadius = "50%"; closeButton.style.backgroundColor = "#203142"; closeButton.style.color = "#fff"; closeButton.style.border = "none"; closeButton.style.display = "flex"; closeButton.style.alignItems = "center"; closeButton.style.justifyContent = "center"; closeButton.style.cursor = "pointer"; closeButton.style.marginTop = "10px"; closeButton.style.left = "82%"; closeButton.style.top = "56px"; closeButton.addEventListener("click", () => { languagePopup.style.display = "none"; }); languagePopup.appendChild(closeButton); document.body.appendChild(languagePopup); document.getElementById("openLanguagePopup").addEventListener("click", () => { languagePopup.style.display = "block"; }); const warningText = document.createElement("div"); warningText.textContent = "⚠️it may stops working"; warningText.style.color = "#ffcc00"; warningText.style.fontSize = "14px"; warningText.style.marginBottom = "10px"; warningText.style.textAlign = "end"; warningText.style.right = "38px"; warningText.style.position = "relative"; warningText.style.top = "20px"; languagePopup.appendChild(warningText); for (const language in languageFilters) { document.getElementById(`lang-${language}`).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); } #keywordList::-webkit-scrollbar { width: 25px; } #keywordList::-webkit-scrollbar-thumb { background-color: #C1A5EF; border-radius: 8px; border: 3px solid #4F3E6A; height: 80px; } #keywordList::-webkit-scrollbar-thumb:hover { background-color: #C6AEFF; } #keywordList::-webkit-scrollbar-thumb:active { background-color: #B097C9; } #keywordList::-webkit-scrollbar-track { background: #455565; border-radius: 0px 0px 8px 0px; } .version-text { left: 275px; position: relative; bottom: 18px; color: #15202b; margin: 0; font-size: 14px; width: 47px; } #keywordInput { cursor: #ffffff59; background: #15202b; } #favoriteUserButton:hover img { filter: brightness(1.2); } #favoriteUserButton:active img { transform: scale(0.9); } `; document.head.appendChild(style); // ===== Кнопка переключения панели FilterX ===== // let isSwitchOn = localStorage.getItem("isSwitchOn") === "true"; const toggleButton = document.createElement("div"); toggleButton.style.display = "flex"; toggleButton.style.alignItems = "center"; toggleButton.style.gap = "10px"; toggleButton.style.background = " #15202b"; toggleButton.style.border = "4px solid #6c7e8e"; toggleButton.style.borderRadius = "45px"; toggleButton.style.padding = "8px 12px"; toggleButton.style.marginTop = "10px"; toggleButton.style.cursor = "pointer"; toggleButton.style.width = "auto"; toggleButton.className = "css-175oi2r r-16y2uox"; function isGrokPage() { return window.location.href.startsWith("https://x.com/i/grok"); } 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 = isSwitchOn ? " #425364" : " #0d1319"; toggleLabel.style.position = "relative"; toggleLabel.style.cursor = "pointer"; toggleLabel.style.transition = "background-color 0.3s"; toggleLabel.style.top = "0px"; toggleLabel.style.left = "75px"; 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"; function toggleSwitchState(event) { event.stopPropagation(); isSwitchOn = !isSwitchOn; localStorage.setItem("isSwitchOn", isSwitchOn.toString()); toggleSwitch.style.left = isSwitchOn ? "calc(100% - 23px)" : "2px"; toggleLabel.style.backgroundColor = isSwitchOn ? " #425364" : " #0d1319"; if (isSwitchOn) { panel.style.display = "block"; setTimeout(() => { panel.style.height = "485px"; }, 10); } else { panel.style.height = "0px"; setTimeout(() => { panel.style.display = "none"; }, 300); } isPanelVisible = isSwitchOn; localStorage.setItem("panelVisible", isPanelVisible.toString()); } const debouncedToggleSwitchState = debounce(toggleSwitchState, 300); toggleButton.addEventListener("click", debouncedToggleSwitchState); toggleLabel.appendChild(toggleSwitch); toggleButton.appendChild(toggleLabel); const toggleText = document.createElement("span"); toggleText.textContent = "FilterX"; toggleText.style.color = " #6c7e8e"; toggleText.style.fontFamily = "Arial, sans-serif"; toggleText.style.fontSize = "16px"; toggleText.style.fontWeight = "bold"; toggleText.style.position = "absolute"; toggleText.style.top = "12px"; toggleText.style.left = "25px"; toggleButton.appendChild(toggleText); function waitForPostButton(callback) { const interval = setInterval(() => { const postButton = document.querySelector("[data-testid='SideNav_NewTweet_Button']"); const postButtonContainer = postButton?.parentElement; if (postButtonContainer) { clearInterval(interval); callback(postButtonContainer); } else { console.warn("Контейнер кнопки 'Опубликовать пост' не найден"); } }, 500); } waitForPostButton((postButtonContainer) => { toggleButton.style.display = isGrokPage() ? "none" : "flex"; postButtonContainer.appendChild(toggleButton); }); // ===== Управление высотой панели ===== // let isPanelVisible = localStorage.getItem("panelVisible") === "true"; function togglePanel() { if (isPanelVisible) { panel.style.height = "0px"; setTimeout(() => { panel.style.display = "none"; }, 300); } else { panel.style.display = "block"; setTimeout(() => { panel.style.height = "510px"; }, 10); } isPanelVisible = !isPanelVisible; localStorage.setItem("panelVisible", isPanelVisible.toString()); } toggleButton.addEventListener("click", togglePanel); if (isPanelVisible) { panel.style.height = "510px"; panel.style.display = "block"; } else { panel.style.height = "0px"; panel.style.display = "none"; } // ===== Элементы управления ===== // const addKeywordBtn = document.getElementById("addKeyword"); const clearKeywordsBtn = document.getElementById("clearKeywords"); const exportKeywordsBtn = document.getElementById("exportKeywords"); const importKeywordsBtn = document.getElementById("importKeywords"); const toggleVerifiedBtn = document.getElementById("toggleVerifiedPosts"); const toggleNonVerifiedBtn = document.getElementById("toggleNonVerifiedPosts"); const toggleBlockBtn = document.getElementById("toggleBlockKeywords"); const openLanguagePopupBtn = document.getElementById("openLanguagePopup"); const toggleFavoriteUsersBtn = document.getElementById("toggleFavoriteUsers"); const keywordList = document.getElementById("keywordList"); // ===== Обработчики событий ===== // addKeywordBtn.addEventListener("click", () => { const inputValue = keywordInput.value.trim(); if (inputValue) { if (isShowingFavorites && inputValue.startsWith("@")) { const username = inputValue.slice(1).toLowerCase(); // Убираем @ перед добавлением if (!favoriteUsers.includes(username)) { favoriteUsers.push(username); saveFavoriteUsers(); updateKeywordList(); updateCounters(); debouncedHidePosts(); } } else if (!isShowingFavorites && !hiddenKeywords.includes(inputValue)) { hiddenKeywords.push(inputValue); saveKeywords(); updateKeywordList(); updateCounters(); debouncedHidePosts(); } keywordInput.value = ""; } }); toggleNonVerifiedBtn.textContent = `Hide non-verified accounts: ${hideNonVerifiedAccounts ? "Turn OFF" : "Turn ON"}`; toggleNonVerifiedBtn.addEventListener("click", () => { hideNonVerifiedAccounts = !hideNonVerifiedAccounts; localStorage.setItem("hideNonVerifiedAccounts", JSON.stringify(hideNonVerifiedAccounts)); toggleNonVerifiedBtn.textContent = `Hide non-verified accounts: ${hideNonVerifiedAccounts ? "Turn OFF" : "Turn ON"}`; hidePosts(); }); clearKeywordsBtn.addEventListener("click", () => { if (confirm("Are you sure you want to clear the list?")) { if (isShowingFavorites) { favoriteUsers = []; saveFavoriteUsers(); } else { hiddenKeywords = []; unblockedKeywords = []; saveKeywords(); saveUnblockedKeywords(); } updateKeywordList(); updateCounters(); hidePosts(); } }); exportKeywordsBtn.addEventListener("click", () => { const data = isShowingFavorites ? favoriteUsers : hiddenKeywords; const dataStr = `data:text/json;charset=utf-8,${encodeURIComponent(JSON.stringify(data))}`; const count = data.length; const baseName = isShowingFavorites ? "favorite_users" : "hidden_keywords"; const nameWord = isShowingFavorites ? 'users' : 'keywords'; const fileName = `${baseName}_${count}_${nameWord}.json`; const downloadAnchor = document.createElement("a"); downloadAnchor.setAttribute("href", dataStr); downloadAnchor.setAttribute("download", fileName); document.body.appendChild(downloadAnchor); downloadAnchor.click(); document.body.removeChild(downloadAnchor); }); importKeywordsBtn.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 importedData = JSON.parse(reader.result); if (Array.isArray(importedData)) { if (isShowingFavorites) { favoriteUsers = [...new Set([...favoriteUsers, ...importedData.map(u => u.startsWith("@") ? u.slice(1).toLowerCase() : u.toLowerCase())])]; saveFavoriteUsers(); } else { hiddenKeywords = [...new Set([...hiddenKeywords, ...importedData])]; saveKeywords(); } updateKeywordList(); updateCounters(); hidePosts(); } else { alert("Incorrect file format."); } } catch (e) { alert("Error reading the file."); } }; reader.readAsText(file); }); input.click(); }); toggleBlockBtn.addEventListener("click", () => { if (!isShowingFavorites) { if (hiddenKeywords.length > 0) { unblockedKeywords = [...hiddenKeywords]; hiddenKeywords = []; toggleBlockBtn.textContent = "Block All"; } else { hiddenKeywords = [...unblockedKeywords]; unblockedKeywords = []; toggleBlockBtn.textContent = "Unblock All"; } saveKeywords(); saveUnblockedKeywords(); updateKeywordList(); updateCounters(); hidePosts(); } }); toggleBlockBtn.textContent = hiddenKeywords.length > 0 ? "Unblock All" : "Block All"; toggleVerifiedBtn.addEventListener("click", () => { hideVerifiedAccounts = !hideVerifiedAccounts; toggleVerifiedBtn.textContent = `Hide verified accounts: ${hideVerifiedAccounts ? "Turn OFF" : "Turn ON"}`; hidePosts(); }); toggleFavoriteUsersBtn.addEventListener("click", () => { isShowingFavorites = !isShowingFavorites; toggleFavoriteUsersBtn.textContent = isShowingFavorites ? "Keywords" : "Favorite Users"; keywordInput.placeholder = isShowingFavorites ? "Enter @username" : "Enter the word"; updateKeywordList(); updateCounters(); }); openLanguagePopupBtn.addEventListener("click", () => { const panelRect = panel.getBoundingClientRect(); languagePopup.style.top = `${panelRect.top - 320}px`; languagePopup.style.left = `${panelRect.right - 10}px`; languagePopup.style.display = "block"; }); // ===== Обновление списка ключевых слов или избранных пользователей ===== // function updateKeywordList() { const list = document.getElementById("keywordList"); const label = document.getElementById("listLabel"); const searchInput = document.getElementById("searchInput"); const searchQuery = searchInput ? searchInput.value.trim().toLowerCase() : ""; list.innerHTML = ""; if (isShowingFavorites) { label.textContent = "Favorite Users"; const filteredUsers = favoriteUsers.filter(user => user.toLowerCase().includes(searchQuery)); if (filteredUsers.length === 0) { list.textContent = searchQuery ? "No matches found" : "Нет"; } else { filteredUsers.forEach((user, index) => { const listItem = document.createElement("li"); listItem.textContent = `@${user}`; 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", () => { favoriteUsers.splice(favoriteUsers.indexOf(user), 1); saveFavoriteUsers(); updateKeywordList(); updateCounters(); hidePosts(); }); listItem.appendChild(deleteButton); list.appendChild(listItem); }); } } else { label.textContent = "List of Keywords"; const filteredKeywords = hiddenKeywords.filter(keyword => keyword.toLowerCase().includes(searchQuery)); if (filteredKeywords.length === 0) { list.textContent = searchQuery ? "No matches found" : "Нет"; } else { filteredKeywords.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(hiddenKeywords.indexOf(keyword), 1); saveKeywords(); updateKeywordList(); updateCounters(); hidePosts(); }); listItem.appendChild(deleteButton); list.appendChild(listItem); }); } } } // ===== Создание кнопки избранного в профиле ===== // function addFavoriteButtonToProfile() { const checkProfileLoaded = setInterval(() => { const buttonContainer = document.querySelector('.css-175oi2r.r-obd0qt.r-18u37iz.r-1w6e6rj.r-1h0z5md.r-dnmrzs') || document.querySelector('[data-testid="userActions"]'); const username = getUsernameFromProfile(); if (buttonContainer && username && !buttonContainer.querySelector('#favoriteUserButton')) { clearInterval(checkProfileLoaded); const isFavorite = favoriteUsers.includes(username); const favoriteButton = document.createElement('button'); favoriteButton.id = 'favoriteUserButton'; favoriteButton.setAttribute('aria-label', isFavorite ? 'Удалить из избранного' : 'Добавить в избранное'); favoriteButton.setAttribute('role', 'button'); favoriteButton.type = 'button'; favoriteButton.className = 'css-175oi2r r-sdzlij r-1phboty r-rs99b7 r-lrvibr r-6gpygo r-1wron08 r-2yi16 r-1qi8awa r-1loqt21 r-o7ynqc r-6416eg r-1ny4l3l css-175oi2r r-18u37iz r-1wtj0ep'; favoriteButton.style.borderColor = 'rgb(83, 100, 113)'; favoriteButton.style.backgroundColor = 'rgba(0, 0, 0, 0)'; const buttonContent = document.createElement('div'); buttonContent.dir = 'ltr'; buttonContent.className = 'css-146c3p1 r-bcqeeo r-qvutc0 r-1qd0xha r-q4m81j r-a023e6 r-rjixqe r-b88u0q r-1awozwy r-6koalj r-18u37iz r-16y2uox r-1777fci'; buttonContent.style.color = 'rgb(239, 243, 244)'; const icon = document.createElement('img'); icon.src = 'data:image/png;base64,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'; icon.style.width = '25px'; icon.style.height = '25px'; icon.style.filter = isFavorite ? 'hue-rotate(300deg) saturate(2)' : 'none'; icon.setAttribute('aria-hidden', 'true'); icon.className = 'r-4qtqp9 r-yyyyoo r-dnmrzs r-bnwqim r-lrvibr r-m6rgpd r-z80fyv r-19wmn03'; buttonContent.appendChild(icon); favoriteButton.appendChild(buttonContent); favoriteButton.addEventListener('click', () => { // Recalculate isFavorite on each click const currentlyFavorite = favoriteUsers.includes(username); if (currentlyFavorite) { favoriteUsers = favoriteUsers.filter(user => user !== username); favoriteButton.setAttribute('aria-label', 'Добавить в избранное'); icon.style.filter = 'none'; } else { favoriteUsers.push(username); favoriteButton.setAttribute('aria-label', 'Удалить из избранного'); icon.style.filter = 'hue-rotate(300deg) saturate(2)'; } saveFavoriteUsers(); updateKeywordList(); updateCounters(); debouncedHidePosts(); }); buttonContainer.insertBefore(favoriteButton, buttonContainer.firstChild); console.log('Favorite button added for:', username); } }, 500); setTimeout(() => clearInterval(checkProfileLoaded), 5000); // Остановить проверку через 5 секунд } // ===== Автоматическое скрытие панели при открытии фото ===== // function isPhotoViewerOpen() { const currentUrl = window.location.href; const isPhotoOpen = /\/photo\/\d+$/.test(currentUrl); const photoModal = document.querySelector('div[aria-label="Image"]') || document.querySelector('div[data-testid="imageViewer"]'); return isPhotoOpen || !!photoModal; } function updateToggleButtonVisibility() { toggleButton.style.display = isGrokPage() ? "none" : "flex"; } function toggleElementsVisibility() { const isPhotoOpen = isPhotoViewerOpen(); if (isPhotoOpen || isGrokPage()) { panel.style.display = "none"; toggleButton.style.display = "none"; } else { if (isPanelVisible) { panel.style.display = "block"; panel.style.height = "510px"; } toggleButton.style.display = "flex"; } } // ===== Наблюдение за изменениями DOM ===== // const observer = new MutationObserver(() => { debouncedHidePosts(); }); observer.observe(document.body, { childList: true, subtree: true }); const profileObserver = new MutationObserver(() => { if (window.location.href.includes('x.com/') && !window.location.href.includes('/status/')) { addFavoriteButtonToProfile(); } }); profileObserver.observe(document.body, { childList: true, subtree: true }); toggleElementsVisibility(); window.addEventListener("popstate", () => { updateToggleButtonVisibility(); toggleElementsVisibility(); }); const urlObserver = new MutationObserver(() => { updateToggleButtonVisibility(); toggleElementsVisibility(); }); urlObserver.observe(document.body, { childList: true, subtree: true }); document.addEventListener("click", (event) => { if (event.target.closest('div[aria-label="Close"]') || event.target.closest('div[data-testid="imageViewer-close"]')) { setTimeout(toggleElementsVisibility, 100); } }); // ===== Инициализация ===== // updateKeywordList(); updateCounters(); hidePosts(); })(); (function() { 'use strict'; // Функция для поиска и клика по кнопке "Не сейчас" function closeModal() { const notNowButton = document.querySelector('button[role="button"][type="button"] div span span'); if (notNowButton && notNowButton.textContent === 'Не сейчас') { notNowButton.closest('button').click(); console.log('Modal closed'); } } // Запускаем проверку каждые 500 мс const interval = setInterval(() => { closeModal(); }, 500); // Останавливаем интервал через 10 секунд, чтобы не нагружать страницу setTimeout(() => { clearInterval(interval); console.log('Stopped checking for modal'); }, 10000); })();