// ==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();
}
})();