Greasy Fork

Greasy Fork is available in English.

Facebook Activity Auto Deleter (2025) - Fixed

Automatically deletes Facebook activity log entries with improved error handling and permanent skipping of problematic items.

当前为 2025-04-06 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Facebook Activity Auto Deleter (2025) - Fixed
// @namespace    http://greasyfork.icu/en/users/1454546-shawnfrost13
// @version      5.01
// @description  Automatically deletes Facebook activity log entries with improved error handling and permanent skipping of problematic items.
// @author       shawnfrost13 (fixed by Claude)
// @license      MIT
// @match        https://www.facebook.com/*/allactivity*
// @grant        none
// @run-at       document-end
// ==/UserScript==

(function () {
    'use strict';

    console.log("🔥 FB Auto Deleter 4.04 loaded");

    let isRunning = false;
    let deletionCount = 0;
    let failureCount = 0;
    let currentlyProcessingItem = null;
    // Use a map to store problem items and their failure count
    const problemItems = new Map();

    function getRandomDelay(min = 1200, max = 2300) {
        return Math.floor(Math.random() * (max - min + 1)) + min;
    }

    function logStatus(text) {
        let el = document.getElementById('fb-auto-delete-status');
        if (!el) {
            el = document.createElement('div');
            el.id = 'fb-auto-delete-status';
            el.style.position = 'fixed';
            el.style.bottom = '50px';
            el.style.right = '10px';
            el.style.background = '#111';
            el.style.color = 'lime';
            el.style.padding = '10px';
            el.style.borderRadius = '10px';
            el.style.fontFamily = 'monospace';
            el.style.zIndex = '9999';
            document.body.appendChild(el);
        }
        el.textContent = `🧹 ${text}`;
    }

    function createToggleButton() {
        const toggle = document.createElement('button');
        toggle.textContent = '▶️ Start Deleter';
        toggle.style.position = 'fixed';
        toggle.style.bottom = '95px';
        toggle.style.right = '10px';
        toggle.style.zIndex = '10000';
        toggle.style.padding = '8px 12px';
        toggle.style.background = '#222';
        toggle.style.color = '#fff';
        toggle.style.border = '1px solid lime';
        toggle.style.borderRadius = '8px';
        toggle.style.cursor = 'pointer';
        toggle.style.fontFamily = 'monospace';
        toggle.addEventListener('click', () => {
            isRunning = !isRunning;
            toggle.textContent = isRunning ? '⏸️ Pause Deleter' : '▶️ Start Deleter';
            if (isRunning) deleteNext();
            logStatus(isRunning ? 'Script running...' : 'Script paused');
        });
        document.body.appendChild(toggle);
        
        const reset = document.createElement('button');
        reset.textContent = '🔄 Reset Skip List';
        reset.style.position = 'fixed';
        reset.style.bottom = '140px';
        reset.style.right = '10px';
        reset.style.zIndex = '10000';
        reset.style.padding = '8px 12px';
        reset.style.background = '#222';
        reset.style.color = '#fff';
        reset.style.border = '1px solid orange';
        reset.style.borderRadius = '8px';
        reset.style.cursor = 'pointer';
        reset.style.fontFamily = 'monospace';
        reset.addEventListener('click', () => {
            problemItems.clear();
            logStatus('Skip list reset. Will try all items again.');
            console.log("🔄 Skip list reset");
        });
        document.body.appendChild(reset);
        
        // Add statistics display
        const stats = document.createElement('div');
        stats.id = 'fb-auto-delete-stats';
        stats.style.position = 'fixed';
        stats.style.bottom = '185px';
        stats.style.right = '10px';
        stats.style.background = '#111';
        stats.style.color = 'white';
        stats.style.padding = '8px';
        stats.style.borderRadius = '8px';
        stats.style.fontFamily = 'monospace';
        stats.style.zIndex = '10000';
        stats.style.fontSize = '12px';
        stats.style.textAlign = 'right';
        document.body.appendChild(stats);
        
        // Update stats every second
        setInterval(() => {
            if (stats) {
                stats.innerHTML = `
                <div>Items Deleted: <span style="color:lime">${deletionCount}</span></div>
                <div>Skipped Items: <span style="color:orange">${problemItems.size}</span></div>
                `;
            }
        }, 1000);
    }

    function getItemSignature(element) {
        if (!element) return null;
        
        // Find the parent container of the menu button
        let container = element.closest('[data-visualcompletion="ignore-dynamic"]');
        if (!container) {
            // Try to find the closest parent that might be a post or activity item
            container = element.closest('div[role="article"]') || 
                       element.closest('div[role="row"]') ||
                       element.closest('div[data-pagelet*="FeedUnit"]');
            
            if (!container) {
                // Fallback to parent element
                container = element.parentElement;
                if (!container) return null;
            }
        }
        
        // Get inner text content
        const textContent = container.innerText.slice(0, 100).replace(/\s+/g, ' ').trim();
        
        // Try to find timestamps or other identifiers
        const timestamp = container.querySelector('abbr[data-utime]');
        const timeValue = timestamp ? timestamp.getAttribute('data-utime') : '';
        
        // Use position as part of signature
        const parentElement = container.parentElement;
        const position = parentElement && parentElement.children ? 
            Array.from(parentElement.children).indexOf(container) : -1;
            
        // Create a signature that combines content and structure info
        return `${timeValue}-${textContent}-${position}`;
    }

    function checkForErrorPopups() {
        // Look for error popups of various types
        const errorTexts = [
            "Something went wrong",
            "Please try again",
            "An error occurred",
            "We couldn't process",
            "Action blocked"
        ];
        
        // Check dialog alerts
        const alertPopups = Array.from(document.querySelectorAll('[role="alert"], [role="dialog"], [role="alertdialog"]'));
        for (const popup of alertPopups) {
            if (errorTexts.some(text => popup.innerText.includes(text))) {
                console.log("⚠️ Found error popup: " + popup.innerText.slice(0, 50));
                return true;
            }
        }
        
        // Check status notifications (bottom popups)
        const statusNotifications = Array.from(document.querySelectorAll('[role="status"]'));
        for (const notification of statusNotifications) {
            if (errorTexts.some(text => notification.innerText.includes(text))) {
                console.log("⚠️ Found error notification: " + notification.innerText.slice(0, 50));
                return true;
            }
        }
        
        // Look for generic red-colored error messages
        const errorMessages = Array.from(document.querySelectorAll('div[style*="color: rgb(244, 33, 46)"], div[style*="color:#f4212e"], div[style*="color:red"]'));
        if (errorMessages.length > 0) {
            console.log("⚠️ Found error message with red text");
            return true;
        }
        
        return false;
    }

    function closeAllErrorPopups() {
        let anyClosed = false;
        
        // Close dialog alerts
        const alertPopups = Array.from(document.querySelectorAll('[role="alert"], [role="dialog"], [role="alertdialog"]'));
        for (const popup of alertPopups) {
            if (popup.innerText.includes("Something went wrong") || popup.innerText.includes("try again")) {
                const closeBtn = popup.querySelector('[aria-label="Close"], [aria-label="Dismiss"], button, div[role="button"]');
                if (closeBtn) {
                    closeBtn.click();
                    anyClosed = true;
                    console.log("🚫 Closed error popup");
                }
            }
        }
        
        // Close status notifications
        const statusNotifications = Array.from(document.querySelectorAll('[role="status"]'));
        for (const notification of statusNotifications) {
            if (notification.innerText.includes("Something went wrong") || notification.innerText.includes("try again")) {
                const closeBtn = notification.querySelector('div[role="button"], button');
                if (closeBtn) {
                    closeBtn.click();
                    anyClosed = true;
                    console.log("🚫 Closed notification popup");
                }
            }
        }
        
        // As a last resort, try to click on any X button in visible dialogs
        if (!anyClosed) {
            const anyDialogs = document.querySelectorAll('[role="dialog"], [role="alertdialog"]');
            for (const dialog of anyDialogs) {
                if (dialog.offsetParent !== null) { // is visible
                    const closeButtons = dialog.querySelectorAll('svg[aria-label="Close"]');
                    for (const btn of closeButtons) {
                        const clickableParent = btn.closest('div[role="button"]') || btn.closest('button');
                        if (clickableParent) {
                            clickableParent.click();
                            anyClosed = true;
                            console.log("🚫 Closed dialog using SVG close button");
                        }
                    }
                }
            }
        }
        
        return anyClosed;
    }

    function findMenuButtons() {
        return Array.from(document.querySelectorAll('[role="button"]')).filter(btn => {
            const label = btn.getAttribute('aria-label') || '';
            return (
                btn.offsetParent !== null &&
                (label.toLowerCase().includes("activity options") ||
                 label.toLowerCase().includes("action options"))
            );
        });
    }

    function autoConfirmPopups() {
        const dialogs = Array.from(document.querySelectorAll('[role="dialog"], [role="alertdialog"]'));
        dialogs.forEach(dialog => {
            const deleteBtn = Array.from(dialog.querySelectorAll('div[role="button"], button'))
                .find(btn =>
                    btn.offsetParent !== null &&
                    btn.innerText.trim().toLowerCase() === "delete"
                );
            if (deleteBtn) {
                console.log("✅ Auto-confirming DELETE dialog");
                deleteBtn.click();
                logStatus("Auto-confirmed delete popup");
            }
        });
    }

    function autoScrollAndRetry() {
        console.log("🔄 Scrolling to load more activity...");
        logStatus("Scrolling to load more items...");
        window.scrollTo({
            top: document.body.scrollHeight,
            behavior: 'smooth'
        });
        setTimeout(() => deleteNext(), 2500);
    }

    function markCurrentItemAsProblem() {
        if (currentlyProcessingItem) {
            const signature = getItemSignature(currentlyProcessingItem);
            if (signature) {
                // Add to problematic items or increment failure count
                const currentCount = problemItems.get(signature) || 0;
                problemItems.set(signature, currentCount + 1);
                
                console.log(`🚫 Marking item as problematic: "${signature.substring(0, 30)}..." (attempt ${currentCount + 1})`);
                
                // If we've tried this item 2 times, log it permanently
                if (currentCount + 1 >= 2) {
                    console.log(`🔒 PERMANENTLY skipping problematic item: "${signature.substring(0, 30)}..."`);
                }
            }
        }
    }

    function shouldSkipItem(btn) {
        const signature = getItemSignature(btn);
        if (!signature) return false;
        
        const failCount = problemItems.get(signature) || 0;
        return failCount >= 2; // Skip if we've failed 2 or more times
    }

    function deleteNext() {
        if (!isRunning) return;

        // Reset tracking of current item
        currentlyProcessingItem = null;
        
        // First check for errors from previous operations
        if (checkForErrorPopups()) {
            console.log("❌ Error popup detected from previous operation");
            markCurrentItemAsProblem();
            closeAllErrorPopups();
            
            // Wait a bit before continuing
            setTimeout(deleteNext, getRandomDelay());
            return;
        }

        // Clean up any dialogs
        autoConfirmPopups();

        const buttons = findMenuButtons();

        if (buttons.length === 0) {
            logStatus('No deletable buttons found. Trying to scroll...');
            autoScrollAndRetry();
            return;
        }

        // Filter out items we should skip
        const validButtons = buttons.filter(btn => !shouldSkipItem(btn));
        
        if (validButtons.length === 0) {
            logStatus('Only skippable items found. Scrolling for more...');
            autoScrollAndRetry();
            return;
        }

        const btn = validButtons[0];
        currentlyProcessingItem = btn;
        
        btn.scrollIntoView({ behavior: 'smooth', block: 'center' });
        // Wait a bit after scrolling before clicking
        setTimeout(() => {
            btn.click();
            logStatus(`Opened menu for item #${deletionCount + 1}`);
            console.log(`📂 Opened menu for item #${deletionCount + 1}`);

            setTimeout(() => {
                const menuItems = Array.from(document.querySelectorAll('[role="menuitem"]'));
                const deleteOption = menuItems.find(el =>
                    el.innerText.includes("Move to Recycle bin") ||
                    el.innerText.includes("Delete") ||
                    el.innerText.includes("Remove") ||
                    el.innerText.includes("Unlike") ||
                    el.innerText.includes("Remove reaction") ||
                    el.innerText.includes("Remove tag")
                );

                if (deleteOption) {
                    deleteOption.click();
                    logStatus(`Clicked delete on item #${deletionCount + 1}`);
                    console.log(`🗑️ Attempted delete on item #${deletionCount + 1}`);

                    // Start checking for errors with a series of checks
                    // We'll check multiple times to catch errors that might appear with a delay
                    const errorCheckIntervals = [800, 1600, 2400];
                    let checkIndex = 0;
                    
                    function performErrorCheck() {
                        if (checkIndex >= errorCheckIntervals.length) {
                            // If we've checked multiple times and found no errors, consider it successful
                            deletionCount++;
                            logStatus(`✅ Deleted item #${deletionCount}`);
                            
                            // Move to next item after a delay
                            setTimeout(deleteNext, getRandomDelay());
                            return;
                        }
                        
                        // Check for errors
                        if (checkForErrorPopups()) {
                            console.log("❌ Error detected during deletion");
                            markCurrentItemAsProblem();
                            closeAllErrorPopups();
                            
                            // Move to next item after a delay
                            setTimeout(deleteNext, getRandomDelay());
                            return;
                        }
                        
                        // Schedule next check
                        checkIndex++;
                        setTimeout(performErrorCheck, errorCheckIntervals[checkIndex-1]);
                    }
                    
                    // Start the first error check
                    setTimeout(performErrorCheck, errorCheckIntervals[0]);
                    
                } else {
                    // If no delete option found, mark as problematic and move on
                    console.log("⚠️ No delete option found");
                    markCurrentItemAsProblem();
                    
                    // Click elsewhere to close the menu
                    document.body.click();
                    
                    // Move to next item
                    setTimeout(deleteNext, getRandomDelay());
                }
            }, 1300);
        }, 500);
    }

    // Initial setup
    createToggleButton();
    
    // Regular maintenance tasks
    setInterval(autoConfirmPopups, 1000);
    setInterval(() => {
        // Periodically check for and close error popups that might be blocking progress
        if (isRunning) {
            closeAllErrorPopups();
        }
    }, 2000);
})();