Super Duolingo Ad Blocker

Block ads and unwanted promotional content on Duolingo, including dynamically named ad classes, while preserving essential lesson content and handling fullscreen ads by pressing the exit button automatically or selecting "No Thanks" on specific ads.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Super Duolingo Ad Blocker
// @version      1.0
// @description  Block ads and unwanted promotional content on Duolingo, including dynamically named ad classes, while preserving essential lesson content and handling fullscreen ads by pressing the exit button automatically or selecting "No Thanks" on specific ads.
// @author       Zinovia
// @match        https://*.duolingo.com/*
// @grant        none
// @namespace https://greasyfork.org/users/1340999
// ==/UserScript==

(function() {
    'use strict';

    // Function to inject CSS into the document
    function addStyles(css) {
        const style = document.createElement('style');
        style.type = 'text/css';
        style.textContent = css;
        document.head.appendChild(style);
    }

    // CSS to hide specific promotional and ad content
    const styles = `
        /* Specific divs associated with ads and promotions, conditional on not being essential content */
        div[data-test="purchase-step-active"],
        div._3D_HB,
        div._16rRh,
        div._16rRh._2cnFr._1T_BQ,
        div._1tzFd,
        div._1Qh5D._36g4N._2YF0P.uapW2,
        div._3ZUrl._2BflZ,
        div.MGk8p,
        div._3ywWe,
        div._1145W,
        div.Vm8CO._2zxQ8,
        iframe[name="__privateStripeController7431"] {
            display: none !important;
        }
    `;

    // Inject styles into the document
    addStyles(styles);

    // Function to dynamically hide elements
    function hideElements() {
        const selectors = [
            'div[data-test="purchase-step-active"]',
            'div._3D_HB',
            'div._16rRh',
            'div._16rRh._2cnFr._1T_BQ',
            'div._1tzFd',
            'div._1Qh5D._36g4N._2YF0P.uapW2',
            'div._3ZUrl._2BflZ',
            'div.MGk8p',
            'div._3ywWe',
            'div._1145W',
            'div.Vm8CO._2zxQ8',
            'iframe[name="__privateStripeController7431"]'
        ];

        selectors.forEach(selector => {
            document.querySelectorAll(selector).forEach(element => {
                if (!element.closest('#session/PlayerFooter') && 
                    !element.closest('div._1QQhE') &&
                    !element.closest('div._3rB4d._1VTif._2HXQ9')) { // Ensure essential content is not hidden
                    element.style.display = 'none';
                }
            });
        });
    }

    // Function to press the exit button on fullscreen ads
    function pressExitButton() {
        const exitButton = document.querySelector('._3vGNs._2YF0P._1Udkq');
        if (exitButton) {
            exitButton.click();
            console.log('Fullscreen ad exit button clicked!');
        }
    }

    // Function to click the "No Thanks" button on the new type of full-screen ad
    function pressNoThanksButton() {
        const noThanksButton = document.querySelector('button._1Qh5D._36g4N._2YF0P._76ebC._3h0lA._1S2uf.cnGdv');
        if (noThanksButton) {
            noThanksButton.click();
            console.log('No Thanks button clicked!');
        }
    }

    // Function to click the button in the lesson
    function clickLessonButton() {
        const button = document.querySelector('._3vGNs._2YF0P._1Udkq');
        if (button) {
            button.click();
            console.log('Lesson button clicked!');
        }
    }

    // Function to handle the blank screen issue
    function handleBlankScreen() {
        const continueButton = document.querySelector('button[data-test="player-next"]');
        if (continueButton) {
            continueButton.click();
            console.log('Continue button clicked!');
        }
        const reviewLessonButton = document.querySelector('button span._1o-YO:contains("Review lesson")');
        if (reviewLessonButton) {
            reviewLessonButton.click();
            console.log('Review lesson button clicked!');
        }
    }

    // Run initial functions
    hideElements();
    pressExitButton();
    pressNoThanksButton();
    clickLessonButton();
    handleBlankScreen();

    // Observe DOM changes and apply modifications as necessary
    const observer = new MutationObserver((mutations) => {
        mutations.forEach((mutation) => {
            if (mutation.type === 'childList') {
                hideElements();
                pressExitButton();
                pressNoThanksButton();
                clickLessonButton();
                handleBlankScreen();
            }
        });
    });

    // Start observing the body for child list changes
    observer.observe(document.body, {
        childList: true,
        subtree: true
    });

    console.log('Super Duolingo Ad Blocker initialized.');
})();