您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Fast and efficient Facebook activity log cleaner with smart error handling and problematic item skipping.
当前为
// ==UserScript== // @name Facebook Activity Auto Deleter (2025) - Optimized // @namespace https://gf.qytechs.cn/en/users/1454546-shawnfrost13 // @version 5.02 // @description Fast and efficient Facebook activity log cleaner with smart error handling and problematic item skipping. // @author shawnfrost13 (optimized by Claude) // @license MIT // @match https://www.facebook.com/*/allactivity* // @grant none // @run-at document-end // ==/UserScript== (function () { 'use strict'; // ===== CONFIGURATION ===== const CONFIG = { // Delays (in ms) menuClickDelay: 300, // Time to wait after clicking menu button deleteClickDelay: 300, // Time to wait after clicking delete option betweenItemsDelay: 800, // Random base delay between deletion attempts randomDelayMax: 400, // Additional random delay to avoid detection errorCheckDelay: 600, // How long to wait to check for errors scrollDelay: 1500, // How long to wait after scrolling // UI uiUpdateInterval: 500, // How often to update stats display // Error handling maxRetries: 2, // Maximum retries before skipping item permanently errorTexts: [ // Text patterns that indicate errors "Something went wrong", "Please try again", "An error occurred", "We couldn't process", "Action blocked" ] }; // ===== STATE ===== const STATE = { isRunning: false, deletionCount: 0, skipCount: 0, currentItem: null, problemItems: new Map(), // Map of signatures to retry counts lastActionTime: 0, // For monitoring performance processingTime: [], // Track how long each deletion takes }; // ===== DOM ELEMENT REFERENCES ===== const DOM = { statusDisplay: null, statsDisplay: null, toggleButton: null, resetButton: null, speedSlider: null }; // ===== UTILITY FUNCTIONS ===== function log(message, type = 'info') { const prefix = { 'info': '📋', 'success': '✅', 'error': '❌', 'warn': '⚠️', 'skip': '⏭️', 'scroll': '🔄' }[type] || '🔹'; console.log(`${prefix} ${message}`); } function getRandomDelay() { return CONFIG.betweenItemsDelay + Math.floor(Math.random() * CONFIG.randomDelayMax); } function updateStatus(text, type = 'normal') { if (!DOM.statusDisplay) return; // Set color based on type const colors = { 'normal': 'lime', 'error': '#ff5555', 'warning': '#ffaa00', 'success': '#55ff55' }; DOM.statusDisplay.textContent = text; DOM.statusDisplay.style.color = colors[type] || colors.normal; } function updateStats() { if (!DOM.statsDisplay) return; // Calculate average processing time let avgTime = 0; if (STATE.processingTime.length > 0) { avgTime = STATE.processingTime.reduce((sum, time) => sum + time, 0) / STATE.processingTime.length; } DOM.statsDisplay.innerHTML = ` <div style="font-weight:bold;margin-bottom:5px;border-bottom:1px solid #444;padding-bottom:3px;">FB CLEANER STATS</div> <div>Deleted: <span style="color:#55ff55">${STATE.deletionCount}</span></div> <div>Skipped: <span style="color:#ffaa00">${STATE.skipCount}</span></div> <div>Avg Time: <span style="color:#55aaff">${Math.round(avgTime)}ms</span></div> `; } // ===== UI CREATION ===== function createUI() { const css = ` .fb-cleaner-ui { position: fixed; bottom: 20px; right: 20px; background: rgba(10, 10, 10, 0.85); border: 1px solid #444; border-radius: 10px; color: white; font-family: 'Segoe UI', Tahoma, Geneva, sans-serif; z-index: 999999; backdrop-filter: blur(5px); box-shadow: 0 4px 12px rgba(0,0,0,0.2); overflow: hidden; transition: all 0.3s ease; width: 180px; } .fb-cleaner-header { background: #222; padding: 8px 12px; font-weight: bold; border-bottom: 1px solid #444; display: flex; justify-content: space-between; align-items: center; } .fb-cleaner-title { font-size: 14px; color: #fff; } .fb-cleaner-version { font-size: 10px; color: #aaa; background: #333; padding: 2px 6px; border-radius: 10px; } .fb-cleaner-body { padding: 10px; } .fb-cleaner-status { margin-bottom: 10px; padding: 5px; background: rgba(0,0,0,0.2); border-radius: 5px; font-size: 13px; color: lime; min-height: 20px; } .fb-cleaner-stats { font-size: 12px; margin: 10px 0; line-height: 1.5; } .fb-cleaner-button { padding: 8px; border: none; border-radius: 5px; cursor: pointer; font-weight: bold; font-size: 12px; transition: all 0.2s; width: 100%; margin-bottom: 8px; } .fb-cleaner-button:hover { transform: translateY(-2px); box-shadow: 0 2px 5px rgba(0,0,0,0.2); } .fb-cleaner-button.start { background: #4CAF50; color: white; } .fb-cleaner-button.pause { background: #FF9800; color: black; } .fb-cleaner-button.reset { background: #f44336; color: white; } .fb-cleaner-speed { margin-top: 5px; display: flex; flex-direction: column; font-size: 12px; } .fb-cleaner-speed-label { display: flex; justify-content: space-between; margin-bottom: 5px; } .fb-cleaner-slider { width: 100%; cursor: pointer; } `; // Add CSS const styleEl = document.createElement('style'); styleEl.textContent = css; document.head.appendChild(styleEl); // Create main container const container = document.createElement('div'); container.className = 'fb-cleaner-ui'; // Create header const header = document.createElement('div'); header.className = 'fb-cleaner-header'; const title = document.createElement('div'); title.className = 'fb-cleaner-title'; title.textContent = 'FB Cleaner'; const version = document.createElement('div'); version.className = 'fb-cleaner-version'; version.textContent = 'v5.02'; header.appendChild(title); header.appendChild(version); // Create body const body = document.createElement('div'); body.className = 'fb-cleaner-body'; // Status display const status = document.createElement('div'); status.className = 'fb-cleaner-status'; status.textContent = 'Ready to start'; DOM.statusDisplay = status; // Stats display const stats = document.createElement('div'); stats.className = 'fb-cleaner-stats'; DOM.statsDisplay = stats; // Toggle button const toggleBtn = document.createElement('button'); toggleBtn.className = 'fb-cleaner-button start'; toggleBtn.textContent = '▶️ Start Cleaning'; toggleBtn.addEventListener('click', toggleRunning); DOM.toggleButton = toggleBtn; // Reset button const resetBtn = document.createElement('button'); resetBtn.className = 'fb-cleaner-button reset'; resetBtn.textContent = '🔄 Reset Skip List'; resetBtn.addEventListener('click', () => { STATE.problemItems.clear(); STATE.skipCount = 0; updateStatus('Skip list cleared', 'success'); updateStats(); }); DOM.resetButton = resetBtn; // Speed control const speedControl = document.createElement('div'); speedControl.className = 'fb-cleaner-speed'; const speedLabel = document.createElement('div'); speedLabel.className = 'fb-cleaner-speed-label'; const speedText = document.createElement('span'); speedText.textContent = 'Speed:'; const speedValue = document.createElement('span'); speedValue.textContent = 'Normal'; speedLabel.appendChild(speedText); speedLabel.appendChild(speedValue); const speedSlider = document.createElement('input'); speedSlider.type = 'range'; speedSlider.min = '1'; speedSlider.max = '3'; speedSlider.value = '2'; speedSlider.className = 'fb-cleaner-slider'; speedSlider.addEventListener('input', () => { const value = parseInt(speedSlider.value); const labels = ['Careful', 'Normal', 'Speedy']; speedValue.textContent = labels[value - 1]; // Adjust delays based on speed setting const multiplier = value === 1 ? 1.5 : value === 2 ? 1 : 0.6; CONFIG.menuClickDelay = 300 * multiplier; CONFIG.deleteClickDelay = 300 * multiplier; CONFIG.betweenItemsDelay = 800 * multiplier; CONFIG.errorCheckDelay = 600 * multiplier; }); DOM.speedSlider = speedSlider; speedControl.appendChild(speedLabel); speedControl.appendChild(speedSlider); // Assemble UI body.appendChild(status); body.appendChild(stats); body.appendChild(toggleBtn); body.appendChild(resetBtn); body.appendChild(speedControl); container.appendChild(header); container.appendChild(body); document.body.appendChild(container); // Initialize stats display updateStats(); // Make the UI draggable (simple implementation) let isDragging = false; let offsetX, offsetY; header.addEventListener('mousedown', (e) => { isDragging = true; offsetX = e.clientX - container.getBoundingClientRect().left; offsetY = e.clientY - container.getBoundingClientRect().top; }); document.addEventListener('mousemove', (e) => { if (!isDragging) return; container.style.left = (e.clientX - offsetX) + 'px'; container.style.top = (e.clientY - offsetY) + 'px'; container.style.right = 'auto'; container.style.bottom = 'auto'; }); document.addEventListener('mouseup', () => { isDragging = false; }); } // ===== CORE FUNCTIONALITY ===== function toggleRunning() { STATE.isRunning = !STATE.isRunning; if (STATE.isRunning) { DOM.toggleButton.textContent = '⏸️ Pause Cleaning'; DOM.toggleButton.className = 'fb-cleaner-button pause'; updateStatus('Running...', 'normal'); deleteNext(); } else { DOM.toggleButton.textContent = '▶️ Start Cleaning'; DOM.toggleButton.className = 'fb-cleaner-button start'; updateStatus('Paused', 'warning'); } } function getItemSignature(element) { if (!element) return null; // Find the containing item let container = element.closest('[data-visualcompletion="ignore-dynamic"]') || element.closest('div[role="article"]') || element.closest('div[data-pagelet*="Feed"]'); if (!container) { // Fallback to parent element container = element.parentElement; if (!container) return null; } // Get text content let textContent = ''; try { // Try to get first 50 chars of text content textContent = container.innerText.replace(/\s+/g, ' ').trim().substring(0, 50); } catch (e) { // Fallback if innerText fails textContent = (container.textContent || '').replace(/\s+/g, ' ').trim().substring(0, 50); } // Try to find unique identifiers like timestamps const timestamp = container.querySelector('abbr[data-utime]'); const timeValue = timestamp ? timestamp.getAttribute('data-utime') : ''; // Use position or size information to add uniqueness const rect = container.getBoundingClientRect(); const posInfo = `${Math.round(rect.width)}_${Math.round(rect.height)}`; return `${timeValue}:${textContent}:${posInfo}`; } 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 checkForErrorPopups() { // Check for any error popups or notifications for (const errorText of CONFIG.errorTexts) { const containsError = Array.from(document.querySelectorAll('body *')).some( el => el.offsetParent !== null && // is visible el.innerText && el.innerText.includes(errorText) ); if (containsError) return true; } // Additional check for specific Facebook error elements const errorElements = document.querySelectorAll('[role="alert"], [role="status"]'); for (const el of errorElements) { if (el.offsetParent !== null && CONFIG.errorTexts.some(txt => el.innerText.includes(txt))) { return true; } } return false; } function closeAllErrorPopups() { // Find all close buttons in error popups const errorPopups = Array.from(document.querySelectorAll('[role="alert"], [role="dialog"], [role="status"]')).filter( popup => CONFIG.errorTexts.some(txt => popup.innerText && popup.innerText.includes(txt)) ); let closed = false; errorPopups.forEach(popup => { // Look for close buttons const closeBtn = popup.querySelector('[aria-label="Close"], [aria-label="Dismiss"]') || popup.querySelector('div[role="button"]') || popup.querySelector('button'); if (closeBtn) { closeBtn.click(); closed = true; log("Closed error popup", "info"); } }); return closed; } function markCurrentItemAsProblem() { if (!STATE.currentItem) return; const signature = getItemSignature(STATE.currentItem); if (!signature) return; // Get current count or default to 0 const currentCount = STATE.problemItems.get(signature) || 0; STATE.problemItems.set(signature, currentCount + 1); // If this is a permanent skip, increment skip counter if (currentCount + 1 >= CONFIG.maxRetries) { STATE.skipCount++; log(`Permanently skipping item: "${signature.substring(0, 30)}..."`, "skip"); } else { log(`Marking item for retry: "${signature.substring(0, 30)}..."`, "warn"); } } function shouldSkipItem(btn) { const signature = getItemSignature(btn); if (!signature) return false; const failCount = STATE.problemItems.get(signature) || 0; return failCount >= CONFIG.maxRetries; } function autoConfirmPopups() { const dialogs = Array.from(document.querySelectorAll('[role="dialog"], [role="alertdialog"]')); dialogs.forEach(dialog => { // Find the delete button const deleteBtn = Array.from(dialog.querySelectorAll('div[role="button"], button')) .find(btn => btn.offsetParent !== null && (btn.innerText.trim().toLowerCase() === "delete" || btn.innerText.trim().toLowerCase() === "delete post" || btn.innerText.trim().toLowerCase() === "remove") ); if (deleteBtn) { deleteBtn.click(); log("Auto-confirmed deletion dialog", "success"); } }); } function autoScrollAndRetry() { log("Scrolling to load more content", "scroll"); updateStatus("Scrolling to find more items...", "normal"); // Scroll down window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' }); // Wait and then try again setTimeout(deleteNext, CONFIG.scrollDelay); } function deleteNext() { if (!STATE.isRunning) return; const startTime = performance.now(); STATE.lastActionTime = startTime; // Reset current item STATE.currentItem = null; // Clean up any dialogs and popups closeAllErrorPopups(); autoConfirmPopups(); // Find menu buttons const buttons = findMenuButtons(); if (buttons.length === 0) { updateStatus("No items found, scrolling...", "warning"); autoScrollAndRetry(); return; } // Filter out buttons we should skip const validButtons = buttons.filter(btn => !shouldSkipItem(btn)); if (validButtons.length === 0) { updateStatus("Only skippable items found, scrolling...", "warning"); autoScrollAndRetry(); return; } // Get the first valid button const btn = validButtons[0]; STATE.currentItem = btn; // Scroll to the button and click it btn.scrollIntoView({ block: "center", behavior: "instant" }); setTimeout(() => { // Click the menu button btn.click(); log(`Opened menu for item`, "info"); updateStatus("Opened menu...", "normal"); setTimeout(() => { // Find and click the delete option 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(); log(`Clicked delete option`, "info"); updateStatus("Deleting...", "normal"); // Check for errors after a short delay setTimeout(() => { if (checkForErrorPopups()) { // Error detected log("Error detected during deletion", "error"); updateStatus("Error detected, skipping item", "error"); markCurrentItemAsProblem(); closeAllErrorPopups(); // Record processing time const endTime = performance.now(); STATE.processingTime.push(endTime - startTime); // Move to next item setTimeout(deleteNext, getRandomDelay()); } else { // Success! STATE.deletionCount++; log(`Successfully deleted item #${STATE.deletionCount}`, "success"); updateStatus(`Deleted item #${STATE.deletionCount}`, "success"); // Record processing time const endTime = performance.now(); STATE.processingTime.push(endTime - startTime); // Keep only the last 10 times for the average if (STATE.processingTime.length > 10) { STATE.processingTime.shift(); } // Move to next item setTimeout(deleteNext, getRandomDelay()); } }, CONFIG.errorCheckDelay); } else { // No delete option found log("No delete option found", "warn"); updateStatus("No delete option found, skipping", "warning"); markCurrentItemAsProblem(); // Click elsewhere to close the menu document.body.click(); // Record processing time const endTime = performance.now(); STATE.processingTime.push(endTime - startTime); // Move to next item setTimeout(deleteNext, getRandomDelay()); } }, CONFIG.menuClickDelay); }, 100); // Very short delay after scrolling } // ===== INITIALIZATION ===== function initialize() { log("Facebook Activity Auto Deleter v5.02 loaded", "info"); createUI(); // Start periodic update of the UI setInterval(updateStats, CONFIG.uiUpdateInterval); // Set up periodic error popup checker setInterval(() => { if (STATE.isRunning) { closeAllErrorPopups(); autoConfirmPopups(); } }, 1000); } // Start the script initialize(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址