Skip to content

Instantly share code, notes, and snippets.

@james-harper
Last active February 28, 2024 05:11
Show Gist options
  • Save james-harper/7e076763312db9be30d3f33fcaa1472c to your computer and use it in GitHub Desktop.
Save james-harper/7e076763312db9be30d3f33fcaa1472c to your computer and use it in GitHub Desktop.
Instagram.com - Unsend All Messages
// instagram.com utilities
//
// currently I only have unsend all messages in thread implemented.
// but may add more if I need them
// (this won't work in incognito due to localStorage, session, & cookie values being required)
//
// To use, navigate to the thread in a web browser (I only tested with Chrome)
// and copy and paste the code into a Developer tools console
// "threadId" will need to be updated with the appropriate value
class InstagramHelper {
constructor(userId = null) {
this.userId = userId || this.getCookie("ds_user_id");
this.prevCursor = "";
this.oldestCursor = "";
}
/**
* Pause execution for n milliseconds
* This is useful for avoiding Rate Limiting errors
* @param {number} ms Number of ms to pause for
*/
sleep(ms) {
const end = Date.now() + ms;
while (Date.now() < end) { continue };
}
/**
* Get cookie value if it exists,
* Return empty string otherwise
* @param {string} name Key to lookup
* @returns {string} Value of cookie
*/
getCookie(name) {
if (document.cookie.length > 0) {
let start = document.cookie.indexOf(name + "=");
if (start != -1) {
start = start + name.length + 1;
let end = document.cookie.indexOf(";", start);
if (end == -1) {
end = document.cookie.length;
}
return unescape(document.cookie.substring(start, end));
}
}
return "";
}
/**
* Unsend all messages in the given thread
* (May need to be run a few times to completely remove everything)
* (I'm too lazy to find the optimal interval to avoid 429 errors - rate limits)
*
* @param {string?} threadId The thread ID
* @param {number} delay Number of ms between each delete request
* @returns {boolean} Were all messages successfully deleted
*/
async unsendAll(threadId = undefined, delay = 3500) {
if (threadId == null || threadId == undefined) {
// Get the chat id automatically from the url
var threadId = window.location.href.split('/')[5];
console.warn("Starting deleting from thread Id : " + threadId);
}
const threadLink = "https://www.instagram.com/direct/t/" + threadId;
// Array of message ids
const itemIds = [];
// Array of delete message ids
const deletedItemIds = [];
// Messages hash (keyed by id)
const messages = {};
while (this.prevCursor != "MINCURSOR") {
let getMessageAPIUrl = "https://i.instagram.com/api/v1/direct_v2/threads/" + threadId + "/";
if (this.oldestCursor != undefined && this.oldestCursor != null && this.oldestCursor.length > 0) {
getMessageAPIUrl = getMessageAPIUrl + "?cursor=" + this.oldestCursor + "";
}
const requestOptions = {
"credentials": "include",
"headers": {
"accept": "*/*",
"accept-encoding": "gzip, deflate, br",
"accept-language": "en-GB,en-US;q=0.9,en;q=0.8",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-site",
"x-ig-app-id": localStorage.getItem("instagramWebFBAppId"),
"x-ig-www-claim": sessionStorage.getItem("www-claim-v2"),
"x-requested-with": "XMLHttpRequest",
"user-agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Instagram 105.0.0.11.118 (iPhone11,8; iOS 12_3_1; en_US; en-US; scale=2.00; 828x1792; 165586599)"
},
"referrer": threadLink,
"referrerPolicy": "no-referrer-when-downgrade",
"body": null,
"method": "GET",
"mode": "cors"
};
await fetch(
getMessageAPIUrl,
requestOptions
)
.then((response) => {
if (response.status != 200) {
throw response.status;
}
return response.json();
}).then(async (data) => {
console.log("getting messages...");
data.thread.items.forEach(element => {
if (element.user_id.toString() == this.userId.toString()) {
if (!itemIds.includes(element.item_id.toString())) {
// Keep track of just ids
itemIds.push(element.item_id.toString());
// Keep track of the full message in case we need it later
messages[element.item_id.toString()] = element;
}
}
});
console.log('About to delete', itemIds)
//#region Deleting Messages
for (let itemIdIndex = 0; itemIdIndex < itemIds.length; itemIdIndex++) {
const messageItemId = itemIds[itemIdIndex];
let alreadyDeleted = false;
let rateLimitReached = false;
const unsendRequestOptions = {
"credentials": "include",
"headers": {
"accept": "*/*",
"accept-encoding": "gzip, deflate, br",
"accept-language": "en-GB,en-US;q=0.9,en;q=0.8",
"content-type": "application/x-www-form-urlencoded",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-site",
"x-csrftoken": this.getCookie("csrftoken"),
"x-ig-app-id": localStorage.getItem("instagramWebFBAppId"),
"x-ig-www-claim": sessionStorage.getItem("www-claim-v2"),
"x-requested-with": "XMLHttpRequest",
"user-agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Instagram 105.0.0.11.118 (iPhone11,8; iOS 12_3_1; en_US; en-US; scale=2.00; 828x1792; 165586599)"
},
"referrer": threadLink,
"referrerPolicy": "no-referrer-when-downgrade",
"body": null,
"method": "POST",
"mode": "cors"
};
alreadyDeleted = deletedItemIds.includes(messageItemId);
if (!alreadyDeleted) {
await fetch(
"https://i.instagram.com/api/v1/direct_v2/threads/" + threadId + "/items/" + messageItemId + "/delete/",
unsendRequestOptions
).then((response) => {
const message = messages[messageItemId];
rateLimitReached = response.status === 429;
if (response.status != 200) {
console.error(`Message skipped... ${messageItemId}: ${message.text}`);
console.log(response);
} else {
console.info(`Deleting... ${messageItemId}: ${message.text}`);
deletedItemIds.push(messageItemId);
}
if (rateLimitReached) {
// Add item to end of list so it can be retried later
itemIds.push(messageItemId);
}
}).catch((error) => {
console.error(error);
});
} else {
console.log(`Skipping: ${messageItemId} - Already deleted`)
}
let sleepFor = rateLimitReached ? 6000 : (alreadyDeleted ? 0 : delay);
this.sleep(sleepFor);
}
//#endregion Deleting Messages
console.log('Done deleting batch');
this.oldestCursor = data.thread.oldest_cursor;
this.prevCursor = data.thread.prev_cursor;
});
}
console.warn("All messages deleted.");
return itemIds.length === deletedItemIds.length;
}
}
// Navigate to your inbox on Instagram and go to the thread you want to delete
// The URL should be something like:
// https://www.instagram.com/direct/t/12345678901234567890
// The long list of numbers at the end is the thread id
// Because it is such a long number,
// it needs to be used as a string in this script
let threadId = "123456789012345678901234567890123456789";
let Instagram = new InstagramHelper();
Instagram.unsendAll(threadId, 1500);
// If no threadId is passed, we will try and guess it from the url
// So if this code is executed on the page containing the messages that need
// deleting it is sufficient to just use the following:
// const ig = new InstagramHelper; ig.unsendAll();
@umrashrf
Copy link

The API URL has been changed from i.instagram.com to www.instagram.com

@umrashrf
Copy link

Also getting threads is not possible without sending CSRF token.

@umrashrf
Copy link

Also there is not "x-ig-app-id" in local storage and I hard coded it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment