-
-
Save james-harper/7e076763312db9be30d3f33fcaa1472c to your computer and use it in GitHub Desktop.
| // 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(); |
The API URL has been changed from i.instagram.com to www.instagram.com
Also getting threads is not possible without sending CSRF token.
Also there is not "x-ig-app-id" in local storage and I hard coded it.
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();
Hi. I didnt understand well. If you have time can you explain me better ?Thank you very much. My mail mhsayli2@gmail.com