Last active
January 9, 2025 00:39
-
-
Save gerwld/6f767a238ad7510497167c56b0aecce0 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* This script automates the process of deleting your own Instagram saved posts. | |
* | |
* Tested on 12429 posts - around 4h non-stop working script on the background tab. Result: Success! 0 saved. | |
* | |
* WARNING: This function directly manipulates the DOM and depends on the current HTML | |
* structure of Instagram's website to work. If Instagram implements changes to the | |
* activity page layout, structure, or functionality, this script may break or cause | |
* unexpected behavior. Use at your own risk and always review code before running it. | |
* | |
* How to use: | |
* 1. Navigate to the Instagram posts page by going to: | |
* https://www.instagram.com/your_nickname/saved/all-posts/ | |
* 2. Open the developer console in your web browser: | |
* - Chrome/Firefox: Press Ctrl+Shift+J (Windows/Linux) or Cmd+Option+J (Mac) | |
* - Safari: Enable the Develop menu in Safari's Advanced preferences, then press Cmd+Option+C | |
* 3. Copy and paste this entire script into the console and press Enter to run it. | |
* | |
* How to navigate to the posts page on instagram.com: | |
* 1. Log in to Instagram on a desktop browser. | |
* 2. Go to https://www.instagram.com/your_nickname/saved/all-posts/ | |
* 3. Follow the usage steps above to run this script. | |
*/ | |
; (async function () { | |
// Constants | |
/** @const {number} - The number of posts to delete in each batch. */ | |
const DELETION_BATCH_SIZE = 14 | |
/** @const {number} - The delay between first action in milliseconds. */ | |
const DELAY_BETWEEN_ACTION_MS = 1000 | |
/** @const {number} - The delay after post was opened before it was unsaved */ | |
const DELAY_BEFORE_UNSAVE_AFTER_OPENED_MS = 200 | |
/** @const {number} - The delay before next post opening */ | |
const DELAY_BEFORE_NEXT_POST_OPEN_MS = 200 | |
/** @const {number} - The amount of scroll down events to fetch more posts. Bigger is better, but takes more time. */ | |
const SCROLL_FETCH_AMOUNT = 5 | |
let counter = 0; | |
/** | |
* Disables all images and videos to reduce the GPU usage | |
*/ | |
const style = document.createElement('style'); | |
style.innerHTML = ` | |
img, | |
video { | |
display: none!important; | |
} | |
`; | |
// comment this line if you want to see images & videos | |
// document.head.appendChild(style); | |
/** | |
* Utility function that delays execution for a given amount of time. | |
* @param {number} ms - The milliseconds to delay. | |
* @returns {Promise<void>} A promise that resolves after the specified delay. | |
*/ | |
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)) | |
/** | |
* Utility function that clicks on a given element. | |
* @param {Element} element - The element to click. | |
* @throws {Error} If the element is not found. | |
*/ | |
const clickElement = (element) => { | |
return new Promise((resolve, reject) => { | |
if (!element) { | |
console.error('Element not found'); | |
reject(new Error('Element not found')); | |
} else if (typeof element.click !== 'function') { | |
reject(new Error('Element does not have a click method')); | |
} else { | |
counter += 1; | |
element.click(); | |
resolve('Element clicked successfully'); | |
} | |
}); | |
}; | |
/** | |
* Utility function that scroll pages SCROLL_FETCH_AMOUNT times to fetch posts. | |
*/ | |
async function fetchPostsNTimes() { | |
console.log('Fetching all saved posts by scrolling the page. Please wait.'); | |
const scrollTimes = SCROLL_FETCH_AMOUNT; // Number of times to scroll | |
const scrollDelay = 4000; // Delay in milliseconds (4 seconds) between scrolls | |
for (let i = 0; i < scrollTimes; i++) { | |
window.scrollBy(0, window.innerHeight); // Scroll down by one viewport height | |
console.log(`Fetching all saved posts before unsaving them. Scrolled ${i + 1} times`); | |
await new Promise(resolve => setTimeout(resolve, scrollDelay)); // Wait 4 seconds before the next scroll | |
} | |
console.log('Finished scrolling'); | |
} | |
/** | |
* Deletes all user saved posts by opening each one post and unsaving it. | |
* @returns {Promise<void>} A promise that resolves when all saved posts marks are deleted. | |
*/ | |
const deleteActivity = async () => { | |
try { | |
while (true) { | |
let allPosts = document.querySelectorAll('.x1lliihq.x1n2onr6.xh8yej3.x4gyw5p.x2pgyrj.x56m6dy.x1ntc13c.xn45foy.x9i3mqj [role="link"]') | |
await delay(DELAY_BETWEEN_ACTION_MS) | |
for (let i = 0; i < Math.min(DELETION_BATCH_SIZE, allPosts.length); i++) { | |
await clickElement(allPosts[i]) | |
await delay(500); | |
// queryselector of the opened post => save button | |
const currentSavedButtonUnsaved = document.querySelectorAll('[aria-label="Save"]'); | |
if (currentSavedButtonUnsaved.length === 0) { | |
const currentSavedButton = document.querySelector('[aria-label="Remove"]') | |
const event = new MouseEvent('click', { | |
bubbles: true, // Ensures the event bubbles up (if needed) | |
cancelable: true, // Allows the event to be canceled | |
view: window // Links the event to the window object | |
}); | |
// Dispatch the event on the element | |
currentSavedButton.dispatchEvent(event); | |
try { | |
await clickElement(currentSavedButton); | |
} catch (e) { } | |
console.log("Unsaved post, " + counter); | |
await delay(DELAY_BEFORE_UNSAVE_AFTER_OPENED_MS); | |
allPosts[i].remove() | |
await delay(DELAY_BEFORE_NEXT_POST_OPEN_MS) | |
} else { | |
console.log("Post already unsaved. Skipping"); | |
allPosts[i].remove(); | |
} | |
if (allPosts.length < 5) { | |
allPosts = document.querySelectorAll('.x1lliihq.x1n2onr6.xh8yej3.x4gyw5p.x2pgyrj.x56m6dy.x1ntc13c.xn45foy.x9i3mqj [role="link"]') | |
const closeButton = document.querySelector('[role="button"]:has([aria-label="Close"])') | |
await clickElement(closeButton); | |
await delay(5000); | |
console.log("Finished all fetched post. Restarting.\n"); | |
await fetchPostsNTimes(); | |
await deleteActivity(); | |
} | |
} | |
} | |
} catch (error) { | |
console.error('Error in deleteActivity:', error.message) | |
} | |
} | |
// Start the deletion process | |
try { | |
// TODO: I'm bit tired now to make it in loop properly. Any pull-requests are higly appreciated. | |
await fetchPostsNTimes(); | |
await delay(2000); | |
await deleteActivity(); | |
console.log('Activity deletion completed') | |
} catch (error) { | |
console.error('Fatal error:', error.message) | |
} | |
})() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment