auto click read more stuff

clicks read more buttons automatically cuz im lazy lol

// ==UserScript==
// @name         auto click read more stuff
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  clicks read more buttons automatically cuz im lazy lol
// @author       me
// @license      MIT
// @match        *://*/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // stuff to look for
    var keywords = [
        'read more', 'show more', 'see more', 'view more', 'load more',
        'more', 'expand', 'show all', 'see all', 'continue reading',
        'read full', 'full story', 'show thread',
        'show replies', 'view replies', 'see replies'
    ];
    
    var clickDelay = 500; // wait between clicks
    var maxClicks = 50; // dont go crazy
    
    // where to look for buttons
    var containers = [
        '[data-testid*="tweet"]',
        '.post', '.comment', '.entry', '.content',
        '[role="article"]', '.article',
        '.ytd-comment-thread-renderer',
        '.Comment', '.Thing',
        '.feed-shared-update-v2',
    ];
    
    // dont click these
    var dontClick = [
        '.advertisement', '.ad', '.sponsored',
        'header', 'nav', 'footer', '.menu'
    ];

    var clickCount = 0;
    var processing = false;
    var clicked = new WeakSet();
    var lastTime = 0;

    function normalize(text) {
        return text.toLowerCase().trim().replace(/\s+/g, ' ');
    }

    function hasKeyword(text) {
        var norm = normalize(text);
        for(var i = 0; i < keywords.length; i++) {
            if(norm.indexOf(keywords[i]) !== -1) return true;
        }
        return false;
    }

    function canClick(el) {
        if (!el || el.offsetParent === null) return false;
        var rect = el.getBoundingClientRect();
        if (rect.width === 0 || rect.height === 0) return false;
        var style = window.getComputedStyle(el);
        if (style.display === 'none' || style.visibility === 'hidden') return false;
        if (style.pointerEvents === 'none') return false;
        return true;
    }

    function shouldSkip(el) {
        for(var i = 0; i < dontClick.length; i++) {
            try {
                if(el.closest(dontClick[i])) return true;
            } catch(e) {}
        }
        return false;
    }

    // site specific stuff
    function getYouTubeButtons() {
        if(!window.location.hostname.includes('youtube.com')) return [];
        return document.querySelectorAll('#more, .more-button, .expand-button, [aria-label*="more"], [aria-label*="Show"]');
    }

    function getTwitterButtons() {
        var isTwitter = window.location.hostname.includes('twitter.com') || window.location.hostname.includes('x.com');
        if(!isTwitter) return [];
        return document.querySelectorAll('[data-testid="tweet-text-show-more-link"], span[role="button"]:not([data-clicked])');
    }

    function getRedditButtons() {
        if(!window.location.hostname.includes('reddit.com')) return [];
        return document.querySelectorAll('.expando-button, .morecomments a, button[aria-expanded="false"]');
    }

    function clickStuff() {
        if (processing || clickCount >= maxClicks) return;
        
        processing = true;
        var totalClicked = 0;

        try {
            // try site specific first
            var ytButtons = getYouTubeButtons();
            if(ytButtons.length > 0) {
                console.log('found youtube buttons');
                totalClicked += clickButtons(ytButtons);
            }
            
            var twitterButtons = getTwitterButtons();
            if(twitterButtons.length > 0) {
                console.log('found twitter buttons');
                totalClicked += clickButtons(twitterButtons);
            }
            
            var redditButtons = getRedditButtons();
            if(redditButtons.length > 0) {
                console.log('found reddit buttons');
                totalClicked += clickButtons(redditButtons);
            }

            // fallback - look everywhere
            if (totalClicked === 0) {
                var allButtons = findButtons();
                totalClicked += clickButtons(allButtons);
            }

            if (totalClicked > 0) {
                console.log('clicked ' + totalClicked + ' buttons');
            }

        } catch (error) {
            console.log('error:', error);
        }
        
        processing = false;
    }

    function findButtons() {
        var found = [];
        var selectors = ['button', 'a[href]', '[role="button"]', 'span[onclick]', 'div[onclick]', '.btn', '.button', '.link'];

        for(var i = 0; i < selectors.length; i++) {
            try {
                var elements = document.querySelectorAll(selectors[i]);
                for(var j = 0; j < elements.length; j++) {
                    if (shouldProcess(elements[j])) {
                        found.push(elements[j]);
                    }
                }
            } catch (e) {
                // whatever
            }
        }

        return found;
    }

    function shouldProcess(el) {
        if (clicked.has(el)) return false;
        if (!canClick(el)) return false;
        if (shouldSkip(el)) return false;

        var text = el.textContent || el.getAttribute('aria-label') || '';
        return hasKeyword(text);
    }

    function clickButtons(buttons) {
        var clickedNow = 0;
        
        for(var i = 0; i < buttons.length; i++) {
            if (clickCount >= maxClicks) break;
            if (clicked.has(buttons[i])) continue;
            
            (function(btn, delay) {
                setTimeout(function() {
                    if (canClick(btn) && !clicked.has(btn)) {
                        try {
                            clicked.add(btn);
                            btn.click();
                            clickCount++;
                            btn.setAttribute('data-clicked', 'true');
                            console.log('clicked: ' + (btn.textContent || 'button').trim().substring(0, 30));
                        } catch (error) {
                            console.log('click failed:', error);
                        }
                    }
                }, delay);
            })(buttons[i], i * clickDelay);
            
            clickedNow++;
        }

        return clickedNow;
    }

    // watch for new stuff
    var observer = new MutationObserver(function(mutations) {
        var now = Date.now();
        if (now - lastTime < 250) return;
        lastTime = now;
        
        var hasNew = false;
        for(var i = 0; i < mutations.length; i++) {
            if (mutations[i].type === 'childList' && mutations[i].addedNodes.length > 0) {
                for(var j = 0; j < mutations[i].addedNodes.length; j++) {
                    if (mutations[i].addedNodes[j].nodeType === 1) {
                        hasNew = true;
                        break;
                    }
                }
            }
            if(hasNew) break;
        }

        if (hasNew) {
            setTimeout(clickStuff, 100);
        }
    });

    // scroll stuff
    var lastScrollTime = 0;
    function onScroll() {
        var now = Date.now();
        if (now - lastScrollTime < 500) return;
        lastScrollTime = now;
        
        var scrollPos = window.scrollY + window.innerHeight;
        var docHeight = document.documentElement.scrollHeight;
        
        if (scrollPos > docHeight - 1000) {
            setTimeout(clickStuff, 200);
        }
    }

    function reset() {
        clickCount = 0;
        clicked = new WeakSet();
        console.log('reset for new page');
    }

    // ctrl+shift+e to trigger
    document.addEventListener('keydown', function(e) {
        if (e.ctrlKey && e.shiftKey && e.key === 'E') {
            e.preventDefault();
            console.log('manual trigger');
            clickStuff();
        }
    });

    function start() {
        console.log('starting auto clicker...');
        
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', function() {
                setTimeout(clickStuff, 1000);
            });
        } else {
            setTimeout(clickStuff, 1000);
        }

        if(document.body) {
            observer.observe(document.body, {
                childList: true,
                subtree: true
            });
        }

        window.addEventListener('scroll', onScroll);

        // check for spa navigation
        var currentUrl = window.location.href;
        setInterval(function() {
            if (window.location.href !== currentUrl) {
                currentUrl = window.location.href;
                reset();
                setTimeout(clickStuff, 1000);
            }
        }, 1000);

        // backup scan
        setInterval(clickStuff, 5000);
    }

    if (document.body) {
        start();
    } else {
        var bodyWatcher = new MutationObserver(function() {
            if (document.body) {
                bodyWatcher.disconnect();
                start();
            }
        });
        bodyWatcher.observe(document.documentElement, { childList: true });
    }

})();