Yandex CleanSearch

Блокировка страниц по домену и заголовкам, рекламы и прочего дерьма в яндекс.

Asenna tämä skripti?
Author's suggested script

Saatat myös pitää

Asenna tämä skripti
// ==UserScript==
// @name         Yandex CleanSearch
// @namespace    http://tampermonkey.net/
// @version      3.3.1
// @description  Блокировка страниц по домену и заголовкам, рекламы и прочего дерьма в яндекс.
// @author       Zzakhar
// @match        https://yandex.ru/search/*
// @match        ya.ru/*
// @grant        GM_xmlhttpRequest
// @grant        GM_info
// @license      CC BY-NC-ND
// ==/UserScript==

(function() {
    'use strict';




    // auto redirect ya.ru/search to legit page
    function autoredirecttolegit() {
        if (window.location.hostname === "ya.ru" && window.location.pathname === "/search/") {
            const urlParams = new URLSearchParams(window.location.search);
            const text = urlParams.get('text');
            if (text) {
                window.location.href = `https://yandex.ru/search/?text=${text}`;
            }
        }
    }


    // Дб локал
    let blockedSites = JSON.parse(localStorage.getItem('blockedSites')) || [];
     function saveBlockedSites() {
        localStorage.setItem('blockedSites', JSON.stringify(blockedSites));
    }

    let blockedPropagandaCount = 0;
    let blockedAdsCount = 0;
    let blockedPropaganda = [];
    let isHidden = true;
    const currentVersion = GM_info.script.version;


    // Update notification
    function checkForUpdates() {
        GM_xmlhttpRequest({
            method: 'GET',
            url: versionCheckUrl,
            onload: function(response) {
                if (response.status === 200) {
                    const newVersionMatch = response.responseText.match(/@version\s+([0-9]+\.[0-9]+\.[0-9]+)/);
                    if (newVersionMatch) {
                        const newVersion = newVersionMatch[1];
                        if (newVersion !== currentVersion) {
                            alert(`Доступно обновление: версия ${newVersion}.`);
                        } else {
                            console.log('У вас самая последняя версия скрипта.');
                        }
                    }
                } else {
                    console.error('Ошибка при проверке обновлений:', response.statusText);
                }
            }
        });
    }

    //перенес все в 1 функцию для блока дерьма и рекламы
    function blockContainers() {
        if (window.location.hostname === 'ya.ru') {
            const marketFeed = document.querySelector("body > main > div:nth-child(3) > div > div > noindex > div.market-feed");
            if (marketFeed) {
                marketFeed.style.display = 'none';
            }
        }
        else if (window.location.hostname.includes('yandex.ru') && window.location.pathname.includes('search')) {
            const containerToHide = document.querySelector("body > main > div > div.main__container > div > div > div.content__left > div.VanillaReact.RelatedBottom");
            if (containerToHide) {
                containerToHide.style.display = 'none';
                console.log("Рекомендации скрыты.");
            }

            const searchResultAside = document.querySelector('#search-result-aside');
            const rsyaGuarantee = searchResultAside ? searchResultAside.querySelector('#rsya-guarantee') : null;

            //rsya-guarantee
            if (rsyaGuarantee) {
                const serpList = searchResultAside.querySelector('div.serp-list');
                if (serpList) {
                    serpList.style.display = 'none';
                    console.log("Контейнер serp-list удален.");
                }
            }
        }
    }


    // remove shit from search bar

    //deb for no lags
    function debounce(func, delay) {
        let debounceTimer;
        return function () {
            const context = this;
            const args = arguments;
            clearTimeout(debounceTimer);
            debounceTimer = setTimeout(() => func.apply(context, args), delay);
        };
    }

    // delete shit
    function removeBlockedSuggestions() {
        const suggestionList = document.querySelector('div.Root.Root_inited > div.HeaderDesktop > header > form > div.mini-suggest__popup.mini-suggest__popup_visible > ul.mini-suggest__popup-content');

        if (suggestionList) {
            const suggestionItems = suggestionList.querySelectorAll('li.mini-suggest__item');

            suggestionItems.forEach((item) => {
                const dataText = item.getAttribute('data-text');
                const anchor = item.querySelector('a.mini-suggest__item-link');
                let containsBlockedWord = false;
                let containsBlockedLink = false;
                if (dataText) {
                    containsBlockedWord = blockedSites.some(site => dataText.includes(site));
                }
                if (anchor) {
                    const href = anchor.href;
                    containsBlockedLink = blockedSites.some(site => href.includes(site));
                }
                const subtitleDiv = anchor?.querySelector('div.mini-suggest__item-content > div.mini-suggest__item-subtitle > span.mini-suggest__item-label'); // чтобы удалялась реклама из поиска
                if (subtitleDiv || containsBlockedWord || containsBlockedLink) {
                    //console.log("Удалено из поиска:", item.getAttribute('data-text'));
                    item.remove();
                }
            });
        }
    }


    // счетчик
    function updateBlockCounter() {
        const resultsContainer = document.querySelector('.main__center');
        let counterCard = resultsContainer.querySelector('.blocked-counter-card');

        if (!counterCard) {
            counterCard = document.createElement('div');
            counterCard.className = 'blocked-counter-card';
            counterCard.style.backgroundColor = '#300';
            counterCard.style.color = '#ffffff';
            counterCard.style.padding = '10px';
            counterCard.style.marginBottom = '10px';
            counterCard.style.border = '1px solid #600';
            counterCard.style.borderRadius = '5px';
            counterCard.style.maxWidth = '34%';
            counterCard.style.overflow = 'auto';
            resultsContainer.prepend(counterCard);
        }

        const counterText = `Заблокировано: ${blockedPropagandaCount} ненужного мусора и ${blockedAdsCount} рекламы`;
        let textElement = counterCard.querySelector('.counter-text');
        if (!textElement) {
            textElement = document.createElement('div');
            textElement.className = 'counter-text';
            counterCard.appendChild(textElement);
        }
        textElement.textContent = counterText;

        updateButtons(counterCard);
    }

    // смена кнопок
    function updateButtons(counterCard) {
        let showButton = counterCard.querySelector('.show-button');
        let removeButton = counterCard.querySelector('.remove-button');

        if (blockedPropagandaCount > 0 || blockedAdsCount > 0) {
            counterCard.style.display = 'flex';
            counterCard.style.justifyContent = 'space-between';
            counterCard.style.alignItems = 'center';

            if (isHidden) {
                if (!showButton) {
                    showButton = document.createElement('button');
                    showButton.className = 'show-button';
                    showButton.textContent = 'Показать';
                    showButton.style.backgroundColor = '#007bff';
                    showButton.style.color = '#ffffff';
                    showButton.style.border = 'none';
                    showButton.style.borderRadius = '5px';
                    showButton.style.padding = '5px 10px';
                    showButton.style.cursor = 'pointer';
                    showButton.style.marginLeft = '10px';
                    showButton.onclick = showBlockedPropaganda;

                    counterCard.appendChild(showButton);
                }

                if (removeButton) {
                    removeButton.remove();
                }
            } else {
                if (!removeButton) {
                    removeButton = document.createElement('button');
                    removeButton.className = 'remove-button';
                    removeButton.textContent = 'Убрать';
                    removeButton.style.backgroundColor = '#dc3545';
                    removeButton.style.color = '#ffffff';
                    removeButton.style.border = 'none';
                    removeButton.style.borderRadius = '5px';
                    removeButton.style.padding = '5px 10px';
                    removeButton.style.cursor = 'pointer';
                    removeButton.style.marginLeft = '10px';
                    removeButton.onclick = hideBlockedPropaganda;

                    counterCard.appendChild(removeButton);
                }

                if (showButton) {
                    showButton.remove();
                }
            }
        } else {
            if (showButton) {
                showButton.remove();
            }
            if (removeButton) {
                removeButton.remove();
            }
        }
    }

    // Мейн функция поиска
    function blockLinksAndAds() {
        const results = document.querySelectorAll('.serp-item');

        blockedPropagandaCount = 0;
        blockedAdsCount = 0;
        blockedPropaganda = [];

        results.forEach(result => {
            const isAd = checkIfAd(result);
            const link = result.querySelector('a.Link');
            const title = result.querySelector('.OrganicTitle-LinkText');

            if (link && title) {
                const href = link.href.toLowerCase();
                const titleText = title.textContent.toLowerCase();

                const isBlocked = blockedSites.some(site => href.includes(site) || titleText.includes(site));

                if (isAd) {
                    result.style.display = 'none';
                    blockedAdsCount++;
                } else if (isBlocked) {
                    result.style.display = 'none';
                    blockedPropagandaCount++;
                    blockedPropaganda.push(result);
                    console.log("Заблокирована дичь: ", result);
                }
            }
        });

        updateBlockCounter();
    }

    //проверка на рекламу, пофикшено 3.3
    function checkIfAd(result) {
        const adTextIndicators = ['реклама', 'баннер', 'advertise'];

        // Проверяем первый селектор для текущей версии
        const currentVersionTarget = result.querySelector(':scope > div > span');
        if (currentVersionTarget) {
            if (adTextIndicators.some(text => currentVersionTarget.textContent.toLowerCase().includes(text))) {
                return true; // Реклама найдена в текущей версии
            }
        }

        // Проверяем второй селектор для старой версии
        const oldVersionTarget = result.querySelector('div > div.Organic-ContentWrapper.organiccontent-wrapper > div.TextContainer.OrganicText.organictext.text-container.Typo.Typo_text_m.Typo_line_m > span > span');
        if (oldVersionTarget) {
            if (adTextIndicators.some(text => oldVersionTarget.textContent.toLowerCase().includes(text))) {
                return true; // Реклама найдена в старой версии
            }
        }

        return false; // Реклама не найдена
    }

    // Показать бтн
    function showBlockedPropaganda() {
        console.log("Показать");
        if (isHidden) {
            blockedPropaganda.forEach(prop => {
                prop.style.display = 'block';
            });
            isHidden = false;
            updateButtons(document.querySelector('.blocked-counter-card'));
        }
    }

    // Скрыть бтн
    function hideBlockedPropaganda() {
        console.log("Убрать");
        if (!isHidden) {
            blockedPropaganda.forEach(prop => {
                prop.style.display = 'none';
            });
            isHidden = true;
            updateButtons(document.querySelector('.blocked-counter-card'));
        }
    }





    // настройки


    function updateBlockedSitesList() {
        const list = document.getElementById('blockedSitesList');
        if (list){
            list.innerHTML = '';
            blockedSites.forEach(site => {
                const li = document.createElement('li');
                li.textContent = site;
                list.appendChild(li);
            });
        }
    }

    function resetBlockedSites() {
        blockedSites.length = 0;
        saveBlockedSites();
        updateBlockedSitesList();
        setTimeout(() => {
            location.reload(); // релоад
        }, 2000);
        showNotification('Все заблокированные сайты были очищены.');
    }

    function createPopup() {
        const popup = document.createElement('div');
        popup.className = 'custom-popup';

        // тёмная тема или нет
        const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
        const isDarkMode = darkModeMediaQuery.matches;

        // Применяем стили для темной или светлой темы
        popup.style.cssText = `
        position: fixed;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        border-radius: 10px;
        padding: 20px;
        z-index: 9999;
        display: none;
        width: 300px;
        max-width: 90%;
        transition: transform 0.3s ease, opacity 0.3s ease;
        opacity: 0;
        ${isDarkMode ? `
            background-color: #0e1011;
            border: 2px solid #970e05; /* Красная рамка */
            box-shadow: 0 0 15px #4c0803, 0 0 30px #8b0903; /* Красное свечение */
            color: white;
        ` : `
            background-color: #f9f9f9;
            border: 2px solid #007BFF; /* Синяя рамка */
            box-shadow: 0 0 15px #007BFF, 0 0 30px #00A3FF; /* Синее свечение */
            color: black;
        `}
    `;

        // Определяем содержимое попапа в зависимости от текущего URL
        const currentUrl = window.location.href;
        if (currentUrl.includes('yandex.ru/search')) {
            popup.innerHTML = `
            <h3>Yandex CleanSearch</h3>
            <input type="text" id="siteInput" placeholder="Домен или заголовок (Например: rutube.ru)"
                   style="
                       width: 90%;
                       padding: 10px;
                       margin-bottom: 10px;
                       border: 2px solid ${isDarkMode ? '#970e05' : '#007BFF'};
                       background-color: ${isDarkMode ? '#0e1011' : 'white'};
                       color: ${isDarkMode ? 'white' : 'black'};
                       border-radius: 10px;
                       outline: none;
                       transition: border-color 0.3s ease, box-shadow 0.3s ease;
                   ">
            <div style="display: flex; justify-content: center; gap: 10px; margin-bottom: 10px;">
                <button id="blockSiteBtn" style="
                    background-color: red;
                    color: white;
                    border: none;
                    border-radius: 20px;
                    padding: 10px 20px;
                    cursor: pointer;
                    font-size: 14px;
                    transition: background-color 0.3s ease;">Заблокировать</button>
                <button id="unblockSiteBtn" style="
                    background-color: green;
                    color: white;
                    border: none;
                    border-radius: 20px;
                    padding: 10px 20px;
                    cursor: pointer;
                    font-size: 14px;
                    transition: background-color 0.3s ease;">Разблокировать</button>
            </div>
            <div style="display: flex; align-items: center; justify-content: space-between;">
                <h4 style="margin: 0;">Заблокированные сайты:</h4>
                <button id="resetBtn" style="
                    background-color: orange;
                    color: white;
                    border: none;
                    border-radius: 10px;
                    padding: 5px 10px;
                    cursor: pointer;
                    font-size: 12px;
                    transition: background-color 0.3s ease;">Сбросить</button>
            </div>
            <ul id="blockedSitesList" style="max-height: 80px; overflow-y: auto;"></ul>
            <span id="closePopupBtn" style="
                position: absolute;
                top: 10px;
                right: 10px;
                cursor: pointer;
                font-size: 20px;
                font-weight: bold;">&times;</span>
        `;
        } else if (currentUrl.includes('ya.ru')) {
            popup.innerHTML = `
            <div class="popup-container">
    <h3>Yandex CleanSearch</h3>
    <p>
        Скрипт был создан для TamperMonkey и написан для Javascript пользователем zzakhar. <br>
        Для использования скрипта начните поиск и введите запрос.
    </p>
    <p>
        Через несколько мгновений на странице поиска в левом верхнем углу появится иконка расширения. Нажмите на неё
        и начните конфигурацию. Вы можете блокировать как домены, так и ключевые слова.
    </p>
    <p>Спасибо за использование!</p>
    <span id="closePopupBtn" style="
        position: absolute;
        top: 10px;
        right: 10px;
        cursor: pointer;
        font-size: 20px;
        font-weight: bold;">
        &times;
    </span>
</div>
        `;
        }
        document.body.appendChild(popup);
        if (currentUrl.includes('yandex.ru/search')) {
            document.getElementById('blockSiteBtn').addEventListener('click', blockSite);
            document.getElementById('unblockSiteBtn').addEventListener('click', unblockSite);
            document.getElementById('resetBtn').addEventListener('click', resetBlockedSites);
        }
        document.getElementById('closePopupBtn').addEventListener('click', () => {
            popup.style.display = 'none';
        });
    }


    function showPopup() {
        const popup = document.querySelector('.custom-popup');
        popup.style.display = 'block';
        popup.style.opacity = '1';
        popup.style.transform = 'translate(-50%, -50%) scale(1.05)'; // анимация
        updateBlockedSitesList();
    }

   function showNotification(message) {
       const notification = document.createElement('div');
       notification.className = 'notification';
       notification.innerText = message;
       notification.style.cssText = `
        position: fixed;
        top: 20px;
        right: 20px;
        background-color: #4caf50; /* Зеленый цвет для успешного уведомления */
        color: white;
        padding: 15px;
        border-radius: 5px;
        box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
        z-index: 10000;
        opacity: 0;
        transform: translateY(-20px);
        transition: opacity 0.5s, transform 0.5s;
    `;

       document.body.appendChild(notification);
       setTimeout(() => {
           notification.style.opacity = '1';
           notification.style.transform = 'translateY(0)';
       }, 100);
       setTimeout(() => {
           notification.style.opacity = '0';
           notification.style.transform = 'translateY(-20px)';
           setTimeout(() => {
               document.body.removeChild(notification);
           }, 500);
       }, 3000);
   }

    function blockSite() {
        const site = document.getElementById('siteInput').value.toLowerCase();
        if (site && !blockedSites.includes(site)) {
            blockedSites.push(site);
            saveBlockedSites();
            updateBlockedSitesList();
            showNotification(`${site} has been blocked.`);
        } else {
            showNotification('Site is already blocked or input is empty.');
        }
    }

    function unblockSite() {
        const site = document.getElementById('siteInput').value.toLowerCase();
        const index = blockedSites.indexOf(site);
        if (index !== -1) {
            blockedSites.splice(index, 1);
            saveBlockedSites();
            showNotification(`${site} has been unblocked.`);
            updateBlockedSitesList()
            setTimeout(() => {
            location.reload();
            }, 2000);
        } else {
            showNotification('Site not found in blocked list.');
        }
    }

    function getHeaderLogo() {
        let headerLogo = document.querySelector('header.HeaderDesktop-Main .HeaderLogo');
        if (!headerLogo) {
            headerLogo = document.querySelector('main.body__wrapper .headline');//for ya.ru
        }
        return headerLogo;
    }

    // иконка для настроек
    function createIcon() {
        const headerLogo = getHeaderLogo();

        if (!headerLogo) {
            console.error('Логотип не найден');
            return;
        }

        headerLogo.removeAttribute('href'); // rem href
        headerLogo.style.cursor = 'default';

        const icon = document.createElement('img');
        icon.className = 'custom-icon';
        icon.src = 'https://avatars.mds.yandex.net/i?id=6a46c4318776cd395ef17ab922147471976ebe7d-3569718-images-thumbs&n=13';
        icon.id = 'YandexCleanSearch'; // ID
        icon.alt = 'FREEINTERNET';

        if (window.location.hostname === 'ya.ru') { // для ya.ru т.к. там надо чуть больше короче и чуть правее
            icon.style.cssText = `
            width: 2.2rem;
            height: 2.2rem;
            border-radius: 50%;
            cursor: pointer;
            position: relative;
            left: 30px; /* Смещение для ya.ru */
            vertical-align: middle;
            opacity: 0;
            transform: scale(0.9);
            transition: opacity 0.5s ease, transform 0.5s ease;
        `;
        } else {
            icon.style.cssText = `
            width: 36px;
            height: 36px;
            border-radius: 50%;
            cursor: pointer;
            position: relative;
            left: 25px;
            vertical-align: middle;
            opacity: 0;
            transform: scale(0.9);
            transition: opacity 0.5s ease, transform 0.5s ease;
        `;
        }
        headerLogo.insertBefore(icon,headerLogo.children[1])
        //headerLogo.appendChild(icon); - prev vers
        setTimeout(() => {
            icon.style.opacity = '1';
            icon.style.transform = 'scale(1)';
        }, 50);

        icon.addEventListener('click', showPopup);
    }
    function removeDuplicateIcon() {
        const elements = document.querySelectorAll('#YandexCleanSearch');
        if (elements.length > 1) {
            for (let i = 1; i < elements.length; i++) {
                elements[i].remove();
            }
            console.log("Лишние элементы #YandexCleanSearch были удалены.");
        }
    }


    window.addEventListener('load', function() {
        createPopup();
        createIcon();
        autoredirecttolegit()
        console.log("Создана иконка и заблокирована реклама(в контейнерах)");
    });


        //основной цикл
    const observer = new MutationObserver(() => {
        if (window.location.hostname !== 'ya.ru') { // NO YA.RU SHIT
            if (isHidden) {
                blockLinksAndAds();
            }
            // icon 2-ule check
            const suggestionPopup = document.querySelector('div.Root.Root_inited > div.HeaderDesktop > header > form > div.mini-suggest__popup');
            if (suggestionPopup && suggestionPopup.classList.contains('mini-suggest__popup_visible')) {
                removeBlockedSuggestions();
            }
            setTimeout(() => {
                const secondCheckIcon = document.querySelector('#YandexCleanSearch');
                if (!secondCheckIcon) {
                    createIcon();
                }
            }, 500);
            removeDuplicateIcon()
        } else {
                const marketFeed = document.querySelector("body > main > div:nth-child(3) > div > div > noindex > div");
                if (marketFeed) {
                    blockContainers()
                    console.log("market-feed удален.");
                }
            }

        });
    observer.observe(document.body, { childList: true, subtree: true });
    console.log('Yandex ClearSearch v',currentVersion,' launched');
})();