// ==UserScript==

// @name Reddit Auto-Expand All Comments (Claude & ChatGPT - 2025)

// @namespace http://tampermonkey.net/

// @version 4.2

// @description Automatically expands all Reddit comments, including collapsed deleted/removed comments that require clicking the circle icon

// @author Marking The Way LLC

// @match https://www.reddit.com/r/*/comments/*

// @grant none

// @run-at document-end

// ==/UserScript==

(function() {

'use strict';

console.log('[Reddit Auto-Expand] Starting v4.2...');

let clickedButtons = new Set();

let expansionCount = 0;

let observer = null;

/**

* Helper: climb ancestors up to `levels` and return the first element

* whose textContent indicates the comment was deleted/removed.

*/

function findDeletedAncestor(el, levels = 6) {

let cur = el;

for (let i = 0; i < levels && cur; i++) {

// Normalize text a bit to be safe

const txt = (cur.textContent || '').replace(/\s+/g, ' ').toLowerCase();

if (txt.includes('[deleted]') || txt.includes('deleted') || txt.includes('[removed]') || txt.includes('removed')) {

return cur;

}

cur = cur.parentElement;

}

return null;

}

/**

* Detects the special collapsed "[deleted]" comment expand button.

* This version checks for empty-text buttons with an expand icon OR

* buttons that contain an add-circle icon adjacent to a deleted/removed label.

*/

function isDeletedCollapsedButton(button) {

try {

// Ignore hidden/invisible

if (button.getAttribute('aria-hidden') === 'true') return false;

if (button.classList.contains('invisible')) return false;

const text = button.textContent.trim();

// If there's visible text, it's probably not the tiny circle used for deleted collapsing.

const noText = text === '';

// Icons that can represent collapse/expand on Reddit

const hasExpandIcon = !!button.querySelector('svg[icon-name="chevron-right"], svg[icon-name="chevron-down"], svg[icon-name="expand"]');

// Some deleted/placeholder comments use the "add-circle" icon (plus in a circle).

const hasAddCircleIcon = !!button.querySelector('svg[icon-name="add-circle"], svg path[d*="M10 1a9 9 0 100 18"]');

// If it's an empty button with an expand icon -> likely the correct button

if (noText && (hasExpandIcon || hasAddCircleIcon)) {

// Verify it's near a deleted/removed label

const deletedAncestor = findDeletedAncestor(button, 8);

if (deletedAncestor) return true;

// Alternatively check for comment container that looks collapsed (best-effort)

const container = button.closest('div, article');

if (container && (container.classList.contains('collapsed') || container.getAttribute('collapsed') === 'true')) {

return true;

}

}

// Another case: button has add-circle icon AND somewhere nearby (within a few ancestor levels)

// there is explicit "[deleted]" or "deleted".

if (hasAddCircleIcon) {

const deletedAncestor = findDeletedAncestor(button, 8);

if (deletedAncestor) return true;

}

} catch (e) {

console.error('[Reddit Auto-Expand] isDeletedCollapsedButton error', e);

}

return false;

}

/**

* Main clicker logic

*/

function clickAllMoreButtons() {

let clicked = 0;

const allButtons = document.querySelectorAll('button');

allButtons.forEach(button => {

// build a key using approximate position + element id/text to avoid re-clicking same element

const rect = button.getBoundingClientRect();

const text = (button.textContent || '').trim();

const buttonKey = (text || '[no-text]') + '|' + Math.round(rect.top) + '|' + Math.round(rect.left) + '|' + (button.getAttribute('data-test-id') || '');

if (clickedButtons.has(buttonKey)) return;

// --- Standard "More Replies" detection

if (text.toLowerCase().includes('more repl')) {

const hasAddIcon = !!button.querySelector('svg[icon-name="add-circle"]');

const hasFaceplate = !!button.querySelector('faceplate-number');

if (button.getAttribute('aria-hidden') === 'true') return;

if (button.classList.contains('invisible')) return;

if (hasAddIcon || hasFaceplate) {

try {

console.log(`[Reddit Auto-Expand] Clicking: "${text}"`);

button.click();

clickedButtons.add(buttonKey);

clicked++;

expansionCount++;

} catch (err) {

console.error('[Reddit Auto-Expand] Click error:', err);

}

}

return;

}

// --- NEW: explicit handling for deleted/removed collapsed comments (circle icon)

if (isDeletedCollapsedButton(button)) {

try {

console.log('[Reddit Auto-Expand] Expanding collapsed deleted/removed comment (circle icon) ...');

button.click();

clickedButtons.add(buttonKey);

clicked++;

expansionCount++;

} catch (err) {

console.error('[Reddit Auto-Expand] Deleted-comment click error:', err);

}

return;

}

});

// Faceplate-partial fallback (keeps previous behavior)

const faceplatePartials = document.querySelectorAll('faceplate-partial[loading="action"]');

faceplatePartials.forEach(partial => {

const button = partial.querySelector('button:not([aria-hidden="true"]):not(.invisible)');

if (!button) return;

const text = (button.textContent || '').trim().toLowerCase();

const buttonKey = text + '|' + Math.round(button.getBoundingClientRect().top);

if (!clickedButtons.has(buttonKey) && text.includes('more repl')) {

try {

console.log(`[Reddit Auto-Expand] Clicking faceplate button: "${text}"`);

button.click();

clickedButtons.add(buttonKey);

clicked++;

expansionCount++;

} catch (err) {

console.error('[Reddit Auto-Expand] Faceplate click error:', err);

}

}

});

if (clicked > 0) {

console.log(`[Reddit Auto-Expand] ✓ Clicked ${clicked} buttons (Total: ${expansionCount})`);

}

return clicked;

}

/**

* Mutation observer watches new comments loading.

*/

function setupObserver() {

if (observer) observer.disconnect();

let debounceTimer = null;

observer = new MutationObserver(() => {

if (debounceTimer) clearTimeout(debounceTimer);

debounceTimer = setTimeout(clickAllMoreButtons, 500);

});

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

console.log('[Reddit Auto-Expand] Observer active');

}

/** Periodic aggressive clicker */

function startPeriodicCheck() {

setInterval(clickAllMoreButtons, 2000);

}

/** Scroll-based triggering */

function setupScrollListener() {

let scrollTimer = null;

window.addEventListener('scroll', () => {

if (scrollTimer) clearTimeout(scrollTimer);

scrollTimer = setTimeout(clickAllMoreButtons, 300);

}, { passive: true });

}

/** PageDown trigger */

function setupKeyboardListener() {

document.addEventListener('keydown', (e) => {

if (e.key === 'PageDown' || e.keyCode === 34) {

setTimeout(clickAllMoreButtons, 300);

}

});

}

/**

* Initialization

*/

function init() {

console.log('[Reddit Auto-Expand] Initializing...');

setTimeout(clickAllMoreButtons, 1500);

setTimeout(clickAllMoreButtons, 3500);

setTimeout(clickAllMoreButtons, 6500);

setupObserver();

setupScrollListener();

setupKeyboardListener();

startPeriodicCheck();

console.log('[Reddit Auto-Expand] ✓ All systems active (v4.2)');

}

if (document.readyState === 'loading') {

document.addEventListener('DOMContentLoaded', init);

} else {

init();

}

})();