Автоматически разворачивает ветки комментариев Dzen, нажимает «читать дальше» в комментариях и нажимает главную кнопку «показать больше комментариев».
// ==UserScript==
// @name Dzen Auto Expander (Comments & More) v1.2 (MutationObserver)
// @namespace http://tampermonkey.net/
// @version 1.2
// @description Автоматически разворачивает ветки комментариев Dzen, нажимает «читать дальше» в комментариях и нажимает главную кнопку «показать больше комментариев».
// @author Your Name Here (Updated)
// @match *://dzen.ru/*
// @grant none
// @run-at document-idle
// @license MIT
// ==/UserScript==
(function() {
'use strict';
const DEBOUNCE_DELAY_MS = 500; // Wait 500ms after the last DOM change before running checks
const INITIAL_DELAY_MS = 2000; // Wait 2 seconds after page idle before initial check and starting observer
let debounceTimer = null;
let observer = null;
let stopped = false;
let runCount = 0; // Counter for logging purposes
// --- Selectors ---
const SELECTORS = {
expandReplies: '.comments2--root-comment__btnWithSpinner-jK > button[aria-label^="Показать"]',
expandLongComment: '.comments2--rich-text-clamp__isInteractive-1s span.comments2--rich-text__expandWord-2_',
loadMoreRootComments: 'button[data-gvdytw8xp="show-more-comments"][aria-label="Показать ещё"]'
};
function clickElement(element, elementType) {
if (element && typeof element.click === 'function' && element.offsetParent !== null) {
// Check if the element is actually visible in the viewport (optional but good practice)
// This check might be too strict sometimes if elements are clickable just outside the viewport
// const rect = element.getBoundingClientRect();
// const isVisible = rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth);
// if (!isVisible) return false; // Skip if not strictly visible
console.log(`[Dzen Expander] Clicking ${elementType}:`, element);
try {
element.click();
return true; // Click attempt successful
} catch (e) {
console.error(`[Dzen Expander] Error clicking ${elementType}:`, e, element);
return false; // Click failed
}
}
return false; // Element not found, not clickable, or not visible
}
function runExpansionCycle() {
if (stopped) return;
runCount++;
console.log(`[Dzen Expander] Running check cycle #${runCount}...`);
let actionPerformed = false;
// 1. Expand long comment texts ("ещё")
// Query all each time as new ones might appear. Click all found ones.
document.querySelectorAll(SELECTORS.expandLongComment).forEach(element => {
// Check visibility again before clicking
if (element.offsetParent !== null) {
if (clickElement(element, "long comment 'ещё'")) {
actionPerformed = true;
}
}
});
// 2. Expand replies ("Показать еще X ответов")
// Click only *one* reply button per cycle to allow DOM to update smoothly.
const replyButton = document.querySelector(SELECTORS.expandReplies);
if (replyButton) {
if (clickElement(replyButton, "expand replies button")) {
actionPerformed = true;
}
}
// 3. Load more root comments ("Показать ещё")
// Click only if it exists and wasn't handled above (though selectors are distinct)
const loadMoreButton = document.querySelector(SELECTORS.loadMoreRootComments);
if (loadMoreButton && !actionPerformed) { // Prioritize expanding existing comments over loading new ones in the same cycle
if (clickElement(loadMoreButton, "load more comments button")) {
actionPerformed = true;
}
}
if (!actionPerformed) {
console.log(`[Dzen Expander] Cycle #${runCount}: No expandable elements found this time.`);
} else {
console.log(`[Dzen Expander] Cycle #${runCount}: Action performed. Observer will trigger next check if needed.`);
// We might want to immediately queue another check if an action was performed,
// as clicking one button might reveal another immediately. The debounce handles this.
}
}
// --- MutationObserver Callback ---
// This function is called when the DOM changes. It uses a debounce mechanism.
const mutationCallback = (mutationsList, obs) => {
if (stopped) return;
// We don't need to inspect mutationsList in detail. Any relevant change
// might add new comments or buttons anywhere. Just trigger a debounced check.
// console.log('[Dzen Expander] DOM Mutation detected.'); // Optional: Can be noisy
// Clear the previous timer if it exists
clearTimeout(debounceTimer);
// Set a new timer to run the check after a short delay
debounceTimer = setTimeout(() => {
runExpansionCycle();
}, DEBOUNCE_DELAY_MS);
};
function stopScript(reason) {
if (stopped) return; // Already stopped
stopped = true;
clearTimeout(debounceTimer); // Clear any pending debounced check
if (observer) {
observer.disconnect();
observer = null;
console.log("[Dzen Expander] MutationObserver disconnected.");
}
console.log(`[Dzen Expander] Script stopped. Reason: ${reason}`);
}
// --- Initialization ---
console.log("[Dzen Expander] Script loaded. Waiting for page idle...");
// Use setTimeout to delay the start slightly after document idle
setTimeout(() => {
if (stopped) return; // Check if manually stopped before starting
console.log("[Dzen Expander] Performing initial expansion check...");
try {
runExpansionCycle(); // Run one initial check immediately
} catch (e) {
console.error("[Dzen Expander] Error during initial run:", e);
}
console.log("[Dzen Expander] Setting up MutationObserver...");
// Create an observer instance linked to the callback function
observer = new MutationObserver(mutationCallback);
// Start observing the entire document body for added/removed nodes and subtree changes
// This is broad but necessary for dynamic content loading like comments
observer.observe(document.body, {
childList: true, // Watch for addition/removal of child nodes
subtree: true // Watch descendants as well
// We don't typically need 'attributes' or 'characterData' for this task
});
console.log("[Dzen Expander] MutationObserver is active. Waiting for DOM changes...");
}, INITIAL_DELAY_MS);
// --- Manual Stop Function ---
window.stopDzenExpander = () => {
stopScript("Manually stopped by user via console command.");
};
console.log("[Dzen Expander] Run `window.stopDzenExpander()` in the console to stop the script manually.");
// --- Safety Stop on Page Unload ---
// Ensure the observer is disconnected when navigating away or closing the tab
window.addEventListener('beforeunload', () => {
stopScript("Page unloading.");
});
})();