Facebook Activity Auto Deleter (2025) - Optimized

Fast and efficient Facebook activity log cleaner with improved item detection and progressive skipping.

  1. // ==UserScript==
  2. // @name Facebook Activity Auto Deleter (2025) - Optimized
  3. // @namespace https://greasyfork.org/en/users/1454546-shawnfrost13
  4. // @version 5.03
  5. // @description Fast and efficient Facebook activity log cleaner with improved item detection and progressive skipping.
  6. // @author shawnfrost13 (optimized by Claude)
  7. // @license MIT
  8. // @match https://www.facebook.com/*/allactivity*
  9. // @grant none
  10. // @run-at document-end
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. 'use strict';
  15.  
  16. // ===== CONFIGURATION =====
  17. const CONFIG = {
  18. // Delays (in ms)
  19. menuClickDelay: 300, // Time to wait after clicking menu button
  20. deleteClickDelay: 300, // Time to wait after clicking delete option
  21. betweenItemsDelay: 800, // Random base delay between deletion attempts
  22. randomDelayMax: 400, // Additional random delay to avoid detection
  23. errorCheckDelay: 800, // How long to wait to check for errors (increased)
  24. scrollDelay: 1500, // How long to wait after scrolling
  25. // UI
  26. uiUpdateInterval: 500, // How often to update stats display
  27. // Error handling
  28. maxRetries: 3, // Maximum retries before skipping item permanently (increased)
  29. skipIncrement: 1, // How many items to skip when encountering problems (NEW)
  30. maxSkipAhead: 5, // Maximum items to skip ahead at once (NEW)
  31. errorTexts: [ // Text patterns that indicate errors
  32. "Something went wrong",
  33. "Please try again",
  34. "An error occurred",
  35. "We couldn't process",
  36. "Action blocked",
  37. "You're temporarily restricted" // Added this common error
  38. ],
  39. // Progressive operation (NEW)
  40. resetSkipCountAfterSuccess: 3, // Reset skip counter after this many successful operations
  41. };
  42.  
  43. // ===== STATE =====
  44. const STATE = {
  45. isRunning: false,
  46. deletionCount: 0,
  47. skipCount: 0,
  48. currentItem: null,
  49. problemItems: new Map(), // Map of signatures to retry counts
  50. lastActionTime: 0, // For monitoring performance
  51. processingTime: [], // Track how long each deletion takes
  52. // Progressive operation (NEW)
  53. consecutiveSuccesses: 0, // Count consecutive successful deletions
  54. currentSkipAhead: 1, // How many items to skip if problems persist
  55. lastSuccessTimestamp: 0, // When was the last successful deletion
  56. };
  57.  
  58. // ===== DOM ELEMENT REFERENCES =====
  59. const DOM = {
  60. statusDisplay: null,
  61. statsDisplay: null,
  62. toggleButton: null,
  63. resetButton: null,
  64. speedSlider: null
  65. };
  66.  
  67. // ===== UTILITY FUNCTIONS =====
  68. function log(message, type = 'info') {
  69. const prefix = {
  70. 'info': '📋',
  71. 'success': '✅',
  72. 'error': '❌',
  73. 'warn': '⚠️',
  74. 'skip': '⏭️',
  75. 'scroll': '🔄'
  76. }[type] || '🔹';
  77. console.log(`${prefix} ${message}`);
  78. }
  79.  
  80. function getRandomDelay() {
  81. return CONFIG.betweenItemsDelay + Math.floor(Math.random() * CONFIG.randomDelayMax);
  82. }
  83.  
  84. function updateStatus(text, type = 'normal') {
  85. if (!DOM.statusDisplay) return;
  86. // Set color based on type
  87. const colors = {
  88. 'normal': 'lime',
  89. 'error': '#ff5555',
  90. 'warning': '#ffaa00',
  91. 'success': '#55ff55'
  92. };
  93. DOM.statusDisplay.textContent = text;
  94. DOM.statusDisplay.style.color = colors[type] || colors.normal;
  95. }
  96.  
  97. function updateStats() {
  98. if (!DOM.statsDisplay) return;
  99. // Calculate average processing time
  100. let avgTime = 0;
  101. if (STATE.processingTime.length > 0) {
  102. avgTime = STATE.processingTime.reduce((sum, time) => sum + time, 0) / STATE.processingTime.length;
  103. }
  104. DOM.statsDisplay.innerHTML = `
  105. <div style="font-weight:bold;margin-bottom:5px;border-bottom:1px solid #444;padding-bottom:3px;">FB CLEANER STATS</div>
  106. <div>Deleted: <span style="color:#55ff55">${STATE.deletionCount}</span></div>
  107. <div>Skipped: <span style="color:#ffaa00">${STATE.skipCount}</span></div>
  108. <div>Skip Ahead: <span style="color:#55aaff">${STATE.currentSkipAhead}</span></div>
  109. <div>Avg Time: <span style="color:#55aaff">${Math.round(avgTime)}ms</span></div>
  110. `;
  111. }
  112.  
  113. // ===== UI CREATION =====
  114. function createUI() {
  115. const css = `
  116. .fb-cleaner-ui {
  117. position: fixed;
  118. bottom: 20px;
  119. right: 20px;
  120. background: rgba(10, 10, 10, 0.85);
  121. border: 1px solid #444;
  122. border-radius: 10px;
  123. color: white;
  124. font-family: 'Segoe UI', Tahoma, Geneva, sans-serif;
  125. z-index: 999999;
  126. backdrop-filter: blur(5px);
  127. box-shadow: 0 4px 12px rgba(0,0,0,0.2);
  128. overflow: hidden;
  129. transition: all 0.3s ease;
  130. width: 180px;
  131. }
  132. .fb-cleaner-header {
  133. background: #222;
  134. padding: 8px 12px;
  135. font-weight: bold;
  136. border-bottom: 1px solid #444;
  137. display: flex;
  138. justify-content: space-between;
  139. align-items: center;
  140. }
  141. .fb-cleaner-title {
  142. font-size: 14px;
  143. color: #fff;
  144. }
  145. .fb-cleaner-version {
  146. font-size: 10px;
  147. color: #aaa;
  148. background: #333;
  149. padding: 2px 6px;
  150. border-radius: 10px;
  151. }
  152. .fb-cleaner-body {
  153. padding: 10px;
  154. }
  155. .fb-cleaner-status {
  156. margin-bottom: 10px;
  157. padding: 5px;
  158. background: rgba(0,0,0,0.2);
  159. border-radius: 5px;
  160. font-size: 13px;
  161. color: lime;
  162. min-height: 20px;
  163. }
  164. .fb-cleaner-stats {
  165. font-size: 12px;
  166. margin: 10px 0;
  167. line-height: 1.5;
  168. }
  169. .fb-cleaner-button {
  170. padding: 8px;
  171. border: none;
  172. border-radius: 5px;
  173. cursor: pointer;
  174. font-weight: bold;
  175. font-size: 12px;
  176. transition: all 0.2s;
  177. width: 100%;
  178. margin-bottom: 8px;
  179. }
  180. .fb-cleaner-button:hover {
  181. transform: translateY(-2px);
  182. box-shadow: 0 2px 5px rgba(0,0,0,0.2);
  183. }
  184. .fb-cleaner-button.start {
  185. background: #4CAF50;
  186. color: white;
  187. }
  188. .fb-cleaner-button.pause {
  189. background: #FF9800;
  190. color: black;
  191. }
  192. .fb-cleaner-button.reset {
  193. background: #f44336;
  194. color: white;
  195. }
  196. .fb-cleaner-speed {
  197. margin-top: 5px;
  198. display: flex;
  199. flex-direction: column;
  200. font-size: 12px;
  201. }
  202. .fb-cleaner-speed-label {
  203. display: flex;
  204. justify-content: space-between;
  205. margin-bottom: 5px;
  206. }
  207. .fb-cleaner-slider {
  208. width: 100%;
  209. cursor: pointer;
  210. }
  211. `;
  212. // Add CSS
  213. const styleEl = document.createElement('style');
  214. styleEl.textContent = css;
  215. document.head.appendChild(styleEl);
  216. // Create main container
  217. const container = document.createElement('div');
  218. container.className = 'fb-cleaner-ui';
  219. // Create header
  220. const header = document.createElement('div');
  221. header.className = 'fb-cleaner-header';
  222. const title = document.createElement('div');
  223. title.className = 'fb-cleaner-title';
  224. title.textContent = 'FB Cleaner';
  225. const version = document.createElement('div');
  226. version.className = 'fb-cleaner-version';
  227. version.textContent = 'v5.03';
  228. header.appendChild(title);
  229. header.appendChild(version);
  230. // Create body
  231. const body = document.createElement('div');
  232. body.className = 'fb-cleaner-body';
  233. // Status display
  234. const status = document.createElement('div');
  235. status.className = 'fb-cleaner-status';
  236. status.textContent = 'Ready to start';
  237. DOM.statusDisplay = status;
  238. // Stats display
  239. const stats = document.createElement('div');
  240. stats.className = 'fb-cleaner-stats';
  241. DOM.statsDisplay = stats;
  242. // Toggle button
  243. const toggleBtn = document.createElement('button');
  244. toggleBtn.className = 'fb-cleaner-button start';
  245. toggleBtn.textContent = '▶️ Start Cleaning';
  246. toggleBtn.addEventListener('click', toggleRunning);
  247. DOM.toggleButton = toggleBtn;
  248. // Reset button
  249. const resetBtn = document.createElement('button');
  250. resetBtn.className = 'fb-cleaner-button reset';
  251. resetBtn.textContent = '🔄 Reset Skip List';
  252. resetBtn.addEventListener('click', () => {
  253. STATE.problemItems.clear();
  254. STATE.skipCount = 0;
  255. STATE.currentSkipAhead = 1;
  256. updateStatus('Skip list cleared', 'success');
  257. updateStats();
  258. });
  259. DOM.resetButton = resetBtn;
  260. // Speed control
  261. const speedControl = document.createElement('div');
  262. speedControl.className = 'fb-cleaner-speed';
  263. const speedLabel = document.createElement('div');
  264. speedLabel.className = 'fb-cleaner-speed-label';
  265. const speedText = document.createElement('span');
  266. speedText.textContent = 'Speed:';
  267. const speedValue = document.createElement('span');
  268. speedValue.textContent = 'Normal';
  269. speedLabel.appendChild(speedText);
  270. speedLabel.appendChild(speedValue);
  271. const speedSlider = document.createElement('input');
  272. speedSlider.type = 'range';
  273. speedSlider.min = '1';
  274. speedSlider.max = '3';
  275. speedSlider.value = '2';
  276. speedSlider.className = 'fb-cleaner-slider';
  277. speedSlider.addEventListener('input', () => {
  278. const value = parseInt(speedSlider.value);
  279. const labels = ['Careful', 'Normal', 'Speedy'];
  280. speedValue.textContent = labels[value - 1];
  281. // Adjust delays based on speed setting
  282. const multiplier = value === 1 ? 1.5 : value === 2 ? 1 : 0.6;
  283. CONFIG.menuClickDelay = 300 * multiplier;
  284. CONFIG.deleteClickDelay = 300 * multiplier;
  285. CONFIG.betweenItemsDelay = 800 * multiplier;
  286. CONFIG.errorCheckDelay = 600 * multiplier;
  287. });
  288. DOM.speedSlider = speedSlider;
  289. speedControl.appendChild(speedLabel);
  290. speedControl.appendChild(speedSlider);
  291. // Assemble UI
  292. body.appendChild(status);
  293. body.appendChild(stats);
  294. body.appendChild(toggleBtn);
  295. body.appendChild(resetBtn);
  296. body.appendChild(speedControl);
  297. container.appendChild(header);
  298. container.appendChild(body);
  299. document.body.appendChild(container);
  300. // Initialize stats display
  301. updateStats();
  302. // Make the UI draggable (simple implementation)
  303. let isDragging = false;
  304. let offsetX, offsetY;
  305. header.addEventListener('mousedown', (e) => {
  306. isDragging = true;
  307. offsetX = e.clientX - container.getBoundingClientRect().left;
  308. offsetY = e.clientY - container.getBoundingClientRect().top;
  309. });
  310. document.addEventListener('mousemove', (e) => {
  311. if (!isDragging) return;
  312. container.style.left = (e.clientX - offsetX) + 'px';
  313. container.style.top = (e.clientY - offsetY) + 'px';
  314. container.style.right = 'auto';
  315. container.style.bottom = 'auto';
  316. });
  317. document.addEventListener('mouseup', () => {
  318. isDragging = false;
  319. });
  320. }
  321.  
  322. // ===== CORE FUNCTIONALITY =====
  323. function toggleRunning() {
  324. STATE.isRunning = !STATE.isRunning;
  325. if (STATE.isRunning) {
  326. DOM.toggleButton.textContent = '⏸️ Pause Cleaning';
  327. DOM.toggleButton.className = 'fb-cleaner-button pause';
  328. updateStatus('Running...', 'normal');
  329. deleteNext();
  330. } else {
  331. DOM.toggleButton.textContent = '▶️ Start Cleaning';
  332. DOM.toggleButton.className = 'fb-cleaner-button start';
  333. updateStatus('Paused', 'warning');
  334. }
  335. }
  336.  
  337. // IMPROVED: More reliable item signature generation
  338. function getItemSignature(element) {
  339. if (!element) return null;
  340. // Find the containing item - search wider for containers
  341. let container = element.closest('[data-visualcompletion="ignore-dynamic"]') ||
  342. element.closest('div[role="article"]') ||
  343. element.closest('div[data-pagelet*="Feed"]') ||
  344. element.closest('[data-testid]') || // Added support for more Facebook selectors
  345. element.parentElement?.parentElement?.parentElement;
  346. if (!container) {
  347. container = element.parentElement;
  348. if (!container) return null;
  349. }
  350. // Get text content - use less text for the signature to reduce false uniqueness
  351. let textContent = '';
  352. try {
  353. // Try to get first 30 chars of cleaned text content (was 50 before)
  354. textContent = container.innerText.replace(/\s+/g, ' ').trim().substring(0, 30);
  355. } catch (e) {
  356. // Fallback if innerText fails
  357. textContent = (container.textContent || '').replace(/\s+/g, ' ').trim().substring(0, 30);
  358. }
  359. // Look for timestamps but don't make them a critical part of the signature
  360. const timestamp = container.querySelector('abbr[data-utime]');
  361. const timeValue = timestamp ? timestamp.getAttribute('data-utime').slice(-5) : ''; // Only use last 5 digits
  362. // Use rough position info
  363. const rect = container.getBoundingClientRect();
  364. const posInfo = `${Math.round(rect.width/10)*10}`; // Round to nearest 10px for less sensitivity
  365. return `${textContent}:${posInfo}:${timeValue}`;
  366. }
  367.  
  368. // IMPROVED: Better menu button detection
  369. function findMenuButtons() {
  370. // Start with standard buttons
  371. const standardButtons = Array.from(document.querySelectorAll('[role="button"]')).filter(btn => {
  372. const label = btn.getAttribute('aria-label') || '';
  373. return (
  374. btn.offsetParent !== null &&
  375. (label.toLowerCase().includes("activity options") ||
  376. label.toLowerCase().includes("action options") ||
  377. label.toLowerCase().includes("more options") || // Added more option keywords
  378. label.toLowerCase().includes("menu"))
  379. );
  380. });
  381. // If we found standard buttons, return them
  382. if (standardButtons.length > 0) return standardButtons;
  383. // Fallback to visual detection
  384. return Array.from(document.querySelectorAll('*')).filter(el => {
  385. if (!el.offsetParent) return false; // Must be visible
  386. // Look for elements that resemble menu buttons
  387. const rect = el.getBoundingClientRect();
  388. const isSquarish = Math.abs(rect.width - rect.height) < 5;
  389. const isReasonableSize = rect.width >= 20 && rect.width <= 40;
  390. // Check if it's in a reasonable place in the document
  391. const isInViewport = rect.top >= 0 && rect.top <= window.innerHeight;
  392. // Has some content that might indicate a button
  393. const hasIconContent = el.innerHTML.includes('svg') ||
  394. el.innerHTML.includes('img') ||
  395. el.textContent.includes('...') ||
  396. el.textContent.includes('⋮');
  397. return isSquarish && isReasonableSize && isInViewport && hasIconContent;
  398. });
  399. }
  400.  
  401. function checkForErrorPopups() {
  402. // Check for any error popups or notifications
  403. for (const errorText of CONFIG.errorTexts) {
  404. const containsError = Array.from(document.querySelectorAll('body *')).some(
  405. el => el.offsetParent !== null && // is visible
  406. el.innerText &&
  407. el.innerText.includes(errorText)
  408. );
  409. if (containsError) return true;
  410. }
  411. // Additional check for specific Facebook error elements
  412. const errorElements = document.querySelectorAll('[role="alert"], [role="status"]');
  413. for (const el of errorElements) {
  414. if (el.offsetParent !== null && CONFIG.errorTexts.some(txt => el.innerText.includes(txt))) {
  415. return true;
  416. }
  417. }
  418. return false;
  419. }
  420.  
  421. function closeAllErrorPopups() {
  422. // Find all close buttons in error popups
  423. const errorPopups = Array.from(document.querySelectorAll('[role="alert"], [role="dialog"], [role="status"]')).filter(
  424. popup => CONFIG.errorTexts.some(txt => popup.innerText && popup.innerText.includes(txt))
  425. );
  426. let closed = false;
  427. errorPopups.forEach(popup => {
  428. // Look for close buttons
  429. const closeBtn = popup.querySelector('[aria-label="Close"], [aria-label="Dismiss"], [aria-label="OK"]') ||
  430. popup.querySelector('div[role="button"]') ||
  431. popup.querySelector('button');
  432. if (closeBtn) {
  433. closeBtn.click();
  434. closed = true;
  435. log("Closed error popup", "info");
  436. }
  437. });
  438. return closed;
  439. }
  440.  
  441. // IMPROVED: Smarter handling of problem items
  442. function markCurrentItemAsProblem() {
  443. if (!STATE.currentItem) return;
  444. const signature = getItemSignature(STATE.currentItem);
  445. if (!signature) return;
  446. // Get current count or default to 0
  447. const currentCount = STATE.problemItems.get(signature) || 0;
  448. STATE.problemItems.set(signature, currentCount + 1);
  449. // If this is a permanent skip, increment skip counter
  450. if (currentCount + 1 >= CONFIG.maxRetries) {
  451. STATE.skipCount++;
  452. log(`Permanently skipping item: "${signature.substring(0, 30)}..."`, "skip");
  453. // Increase the skip ahead count for progressive skipping
  454. STATE.currentSkipAhead = Math.min(
  455. STATE.currentSkipAhead + CONFIG.skipIncrement,
  456. CONFIG.maxSkipAhead
  457. );
  458. // Reset consecutive successes
  459. STATE.consecutiveSuccesses = 0;
  460. } else {
  461. log(`Marking item for retry: "${signature.substring(0, 30)}..."`, "warn");
  462. }
  463. }
  464.  
  465. function shouldSkipItem(btn) {
  466. const signature = getItemSignature(btn);
  467. if (!signature) return false;
  468. const failCount = STATE.problemItems.get(signature) || 0;
  469. return failCount >= CONFIG.maxRetries;
  470. }
  471.  
  472. function autoConfirmPopups() {
  473. const dialogs = Array.from(document.querySelectorAll('[role="dialog"], [role="alertdialog"]'));
  474. dialogs.forEach(dialog => {
  475. // Find the delete button
  476. const deleteBtn = Array.from(dialog.querySelectorAll('div[role="button"], button'))
  477. .find(btn =>
  478. btn.offsetParent !== null &&
  479. (btn.innerText.trim().toLowerCase() === "delete" ||
  480. btn.innerText.trim().toLowerCase() === "delete post" ||
  481. btn.innerText.trim().toLowerCase() === "delete activity" || // Added variants
  482. btn.innerText.trim().toLowerCase() === "remove" ||
  483. btn.innerText.trim().toLowerCase() === "confirm" ||
  484. btn.innerText.trim().toLowerCase() === "ok")
  485. );
  486. if (deleteBtn) {
  487. deleteBtn.click();
  488. log("Auto-confirmed deletion dialog", "success");
  489. }
  490. });
  491. }
  492.  
  493. function autoScrollAndRetry() {
  494. log("Scrolling to load more content", "scroll");
  495. updateStatus("Scrolling to find more items...", "normal");
  496. // Scroll down
  497. window.scrollTo({
  498. top: document.body.scrollHeight,
  499. behavior: 'smooth'
  500. });
  501. // Wait and then try again
  502. setTimeout(deleteNext, CONFIG.scrollDelay);
  503. }
  504.  
  505. function deleteNext() {
  506. if (!STATE.isRunning) return;
  507. const startTime = performance.now();
  508. STATE.lastActionTime = startTime;
  509. // Reset current item
  510. STATE.currentItem = null;
  511. // Clean up any dialogs and popups
  512. closeAllErrorPopups();
  513. autoConfirmPopups();
  514. // Find menu buttons
  515. const buttons = findMenuButtons();
  516. if (buttons.length === 0) {
  517. updateStatus("No items found, scrolling...", "warning");
  518. autoScrollAndRetry();
  519. return;
  520. }
  521. // Filter out buttons we should skip
  522. const validButtons = buttons.filter(btn => !shouldSkipItem(btn));
  523. if (validButtons.length === 0) {
  524. updateStatus("Only skippable items found, scrolling...", "warning");
  525. autoScrollAndRetry();
  526. return;
  527. }
  528. // NEW: Get a button based on the current skip-ahead setting
  529. let buttonIndex = 0;
  530. if (STATE.currentSkipAhead > 1 && validButtons.length >= STATE.currentSkipAhead) {
  531. buttonIndex = STATE.currentSkipAhead - 1; // -1 because array is 0-indexed
  532. log(`Skipping ahead ${STATE.currentSkipAhead} items due to previous errors`, "skip");
  533. }
  534. const btn = validButtons[buttonIndex];
  535. STATE.currentItem = btn;
  536. // Scroll to the button and click it
  537. btn.scrollIntoView({ block: "center", behavior: "instant" });
  538. setTimeout(() => {
  539. // Click the menu button
  540. btn.click();
  541. log(`Opened menu for item`, "info");
  542. updateStatus("Opened menu...", "normal");
  543. setTimeout(() => {
  544. // Find and click the delete option
  545. const menuItems = Array.from(document.querySelectorAll('[role="menuitem"], [role="button"]'));
  546. const deleteOption = menuItems.find(el =>
  547. el.offsetParent !== null && // Must be visible
  548. (
  549. el.innerText.includes("Move to Recycle bin") ||
  550. el.innerText.includes("Delete") ||
  551. el.innerText.includes("Remove") ||
  552. el.innerText.includes("Unlike") ||
  553. el.innerText.includes("Remove reaction") ||
  554. el.innerText.includes("Remove tag") ||
  555. el.innerText.includes("Hide") || // Added more options
  556. el.innerText.includes("Remove from profile")
  557. )
  558. );
  559. if (deleteOption) {
  560. deleteOption.click();
  561. log(`Clicked delete option: "${deleteOption.innerText}"`, "info");
  562. updateStatus("Deleting...", "normal");
  563. // Check for errors after a short delay
  564. setTimeout(() => {
  565. if (checkForErrorPopups()) {
  566. // Error detected
  567. log("Error detected during deletion", "error");
  568. updateStatus("Error detected, skipping item", "error");
  569. markCurrentItemAsProblem();
  570. closeAllErrorPopups();
  571. // Record processing time
  572. const endTime = performance.now();
  573. STATE.processingTime.push(endTime - startTime);
  574. // Reset consecutive successes
  575. STATE.consecutiveSuccesses = 0;
  576. // Move to next item
  577. setTimeout(deleteNext, getRandomDelay());
  578. } else {
  579. // Success!
  580. STATE.deletionCount++;
  581. STATE.consecutiveSuccesses++;
  582. STATE.lastSuccessTimestamp = Date.now();
  583. log(`Successfully deleted item #${STATE.deletionCount}`, "success");
  584. updateStatus(`Deleted item #${STATE.deletionCount}`, "success");
  585. // NEW: Reduce skip-ahead after consecutive successes
  586. if (STATE.consecutiveSuccesses >= CONFIG.resetSkipCountAfterSuccess && STATE.currentSkipAhead > 1) {
  587. STATE.currentSkipAhead = Math.max(1, STATE.currentSkipAhead - 1);
  588. log(`Reduced skip-ahead to ${STATE.currentSkipAhead} after ${STATE.consecutiveSuccesses} successes`, "info");
  589. STATE.consecutiveSuccesses = 0; // Reset the counter
  590. }
  591. // Record processing time
  592. const endTime = performance.now();
  593. STATE.processingTime.push(endTime - startTime);
  594. // Keep only the last 10 times for the average
  595. if (STATE.processingTime.length > 10) {
  596. STATE.processingTime.shift();
  597. }
  598. // Move to next item
  599. setTimeout(deleteNext, getRandomDelay());
  600. }
  601. }, CONFIG.errorCheckDelay);
  602. } else {
  603. // No delete option found - click elsewhere to close the menu
  604. document.body.click();
  605. // Wait a bit and look for confirmation dialogs that might have appeared
  606. setTimeout(() => {
  607. autoConfirmPopups();
  608. // Still no success, mark as problem
  609. log("No delete option found", "warn");
  610. updateStatus("No delete option found, trying next", "warning");
  611. markCurrentItemAsProblem();
  612. // Record processing time
  613. const endTime = performance.now();
  614. STATE.processingTime.push(endTime - startTime);
  615. // Move to next item
  616. setTimeout(deleteNext, getRandomDelay());
  617. }, 300);
  618. }
  619. }, CONFIG.menuClickDelay);
  620. }, 100); // Very short delay after scrolling
  621. }
  622.  
  623. // ===== INITIALIZATION =====
  624. function initialize() {
  625. log("Facebook Activity Auto Deleter v5.03 loaded", "info");
  626. createUI();
  627. // Start periodic update of the UI
  628. setInterval(updateStats, CONFIG.uiUpdateInterval);
  629. // Set up periodic error popup checker
  630. setInterval(() => {
  631. if (STATE.isRunning) {
  632. closeAllErrorPopups();
  633. autoConfirmPopups();
  634. }
  635. }, 1000);
  636. // NEW: Add occasional complete page refresh to fix stuck states
  637. setInterval(() => {
  638. // If running and no successful deletion in 2 minutes, refresh
  639. if (STATE.isRunning &&
  640. STATE.lastSuccessTimestamp > 0 &&
  641. Date.now() - STATE.lastSuccessTimestamp > 120000) {
  642. log("No successful deletions for 2 minutes, refreshing page", "warn");
  643. updateStatus("Stuck detected, refreshing page...", "warning");
  644. // Save state data to sessionStorage
  645. sessionStorage.setItem('fbCleanerState', JSON.stringify({
  646. deletionCount: STATE.deletionCount,
  647. skipCount: STATE.skipCount,
  648. problemItems: Array.from(STATE.problemItems.entries())
  649. }));
  650. // Reload page
  651. setTimeout(() => {
  652. window.location.reload();
  653. }, 1000);
  654. }
  655. }, 30000); // Check every 30 seconds
  656. // Try to restore state from previous session
  657. try {
  658. const savedState = sessionStorage.getItem('fbCleanerState');
  659. if (savedState) {
  660. const parsedState = JSON.parse(savedState);
  661. STATE.deletionCount = parsedState.deletionCount || 0;
  662. STATE.skipCount = parsedState.skipCount || 0;
  663. if (parsedState.problemItems && Array.isArray(parsedState.problemItems)) {
  664. STATE.problemItems = new Map(parsedState.problemItems);
  665. }
  666. log(`Restored state: ${STATE.deletionCount} deleted, ${STATE.skipCount} skipped`, "info");
  667. updateStats();
  668. // Clear the saved state
  669. sessionStorage.removeItem('fbCleanerState');
  670. }
  671. } catch (err) {
  672. console.error("Error restoring state:", err);
  673. }
  674. }
  675. // Start the script
  676. initialize();
  677. })();