Last active
August 2, 2024 06:04
-
-
Save baptx/1e61eef2e1ec200b6e7b32409f79c07f to your computer and use it in GitHub Desktop.
Instagram API: view and backup direct messages from a web browser
This file contains 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
/* | |
Instagram API: view and backup direct messages from a web browser | |
Since April 2020, Instagram has a web version to send and read direct messages so my Instagram scripts are not longer needed and I would not recommend using them unless you really need it, to avoid being banned | |
(never happened to me with Instagram but WhatsApp is owned by Facebook also and they did it to users registering from an unofficial app like yowsup: https://github.com/tgalal/yowsup/commit/88b8ad9581fa22dac330ee3a05fec4e485dfa634#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5) | |
1) Log in on Instagram web version and go to your profile page | |
(the home page will not work because it loads data when scrolling down and the direct messages will be displayed at the bottom of the page) | |
2) Modify HTTP headers with a browser addon like Header Editor (https://addons.mozilla.org/en-US/firefox/addon/header-editor/) | |
Content-Security-Policy (response header on domain www.instagram.com): original data with https://i.instagram.com/ added to connect-src | |
report-uri https://www.instagram.com/security/csp_report/; default-src 'self' https://www.instagram.com; img-src https: data: blob:; font-src https: data:; media-src 'self' blob: https://www.instagram.com https://*.cdninstagram.com https://*.fbcdn.net; manifest-src 'self' https://www.instagram.com; script-src 'self' https://instagram.com https://www.instagram.com https://*.www.instagram.com https://*.cdninstagram.com wss://www.instagram.com https://*.facebook.com https://*.fbcdn.net https://*.facebook.net 'unsafe-inline' 'unsafe-eval' blob:; style-src 'self' https://*.www.instagram.com https://www.instagram.com 'unsafe-inline'; connect-src 'self' https://instagram.com https://www.instagram.com https://i.instagram.com https://*.www.instagram.com https://graph.instagram.com https://*.graph.instagram.com https://*.cdninstagram.com https://api.instagram.com wss://www.instagram.com wss://edge-chat.instagram.com https://*.facebook.com https://*.fbcdn.net https://*.facebook.net chrome-extension://boadgeojelhgndaghljhdicfkmllpafd blob:; worker-src 'self' blob: https://www.instagram.com; frame-src 'self' https://instagram.com https://www.instagram.com https://staticxx.facebook.com https://www.facebook.com https://web.facebook.com https://connect.facebook.net https://m.facebook.com; object-src 'none'; upgrade-insecure-requests | |
User-Agent (request header on domain i.instagram.com) | |
Instagram 85.0.0.21.100 Android (23/6.0.1; 538dpi; 1440x2560; LGE; LG-E425f; vee3e; en_US) | |
3) Run the following JavaScript code in the web console of your browser or in a tool like Firefox scratchpad. | |
The function InstagramDMthreads should be used first, then you have to comment the call to it and uncoment the call to InstagramDMmessages, | |
which takes a thread ID as parameter (found with the previous function InstagramDMthreads). | |
A textarea will appear to the bottom of the page so you can copy-paste to backup data (if you don't see it, you have to close the web console or scroll down). | |
If you want to send direct messages without using Android / iOS and the official proprietary app, you can use open source desktop apps like IG:dm, do cURL requests to the API or use my web browser script instagram-api_send_message.js. | |
*/ | |
/* Usage */ | |
//InstagramDMthreads(); | |
//InstagramDMmessages("XXX"); // parameter needs to be a string since it is too big for an integer | |
/* CONFIG */ | |
var backup = true; // if backup is set to false, only the last threads and messages are displayed, in browser console instead of textarea (number can be configured in limit variable) | |
var limit = 100; // 20 threads and messages by default, per API request | |
/* END CONFIG */ | |
var start; | |
var data; | |
var usernames; | |
// backup will return: thread ID, date + time + timezone, userid, username, last message | |
function InstagramDMthreads() | |
{ | |
initVariables(); // clear variables to allow reusing functions several time | |
var xhr = new XMLHttpRequest(); | |
xhr.open("GET", "https://i.instagram.com/api/v1/direct_v2/inbox/" + "?limit=" + limit); | |
xhr.withCredentials = true; | |
xhr.addEventListener("load", function() { | |
var response = JSON.parse(xhr.responseText); | |
if (backup) { | |
scrollInbox(response); | |
} | |
else { | |
parseInbox(response); | |
console.log(data); | |
} | |
}); | |
xhr.send(); | |
} | |
function parseInbox(response) | |
{ | |
var length = response.inbox.threads.length; | |
for (var i = 0; i < length; ++i) { | |
// length is 0 for conversations with yourself | |
var userid = response.inbox.threads[i].users.length != 0 ? response.inbox.threads[i].users[0].pk : response.viewer.pk; | |
var username = response.inbox.threads[i].users.length != 0 ? response.inbox.threads[i].users[0].username : response.viewer.username; | |
data[start + i] = '"""' + response.inbox.threads[i].thread_id + '"""\t' | |
+ new Date(response.inbox.threads[i].last_activity_at / 1000).toString() + '\t' | |
+ userid + '\t' | |
+ username + '\t' | |
+ '"' + getMessageText(response.inbox.threads[i].items[0]).replace(/"/g, '""') + '"'; | |
// allow multiline CSV string with tab separator and escape double quote string delimiter | |
} | |
start += length; | |
} | |
function scrollInbox(response) | |
{ | |
parseInbox(response); | |
var cursor = response.inbox.oldest_cursor; | |
if (cursor) { | |
var xhr = new XMLHttpRequest(); | |
xhr.open("GET", "https://i.instagram.com/api/v1/direct_v2/inbox/?cursor=" + cursor + "&limit=" + limit); | |
xhr.withCredentials = true; | |
xhr.addEventListener("load", function() { | |
var response = JSON.parse(xhr.responseText); | |
scrollInbox(response); | |
}); | |
xhr.send(); | |
} | |
else { | |
displayData(); | |
} | |
} | |
// backup will return: date + time + timezone, username, message | |
function InstagramDMmessages(thread) | |
{ | |
initVariables(); // clear variables to allow reusing functions several time | |
var xhr = new XMLHttpRequest(); | |
xhr.open("GET", "https://i.instagram.com/api/v1/direct_v2/threads/" + thread + "/" + "?limit=" + limit); | |
xhr.withCredentials = true; | |
xhr.addEventListener("load", function() { | |
var response = JSON.parse(xhr.responseText); | |
var xhr2 = new XMLHttpRequest(); | |
xhr2.open("GET", "https://i.instagram.com/api/v1/users/" + response.thread.viewer_id + "/info/"); | |
xhr2.withCredentials = true; | |
xhr2.addEventListener("load", function() { | |
var response2 = JSON.parse(xhr2.responseText); | |
usernames[response.thread.viewer_id] = response2.user.username; | |
usernames[response.thread.users[0].pk] = response.thread.users[0].username; | |
if (backup) { | |
scrollThread(thread, response); | |
} | |
else { | |
parseThread(thread, response); | |
console.log(data); | |
} | |
}); | |
xhr2.send(); | |
}); | |
xhr.send(); | |
} | |
function parseThread(thread, response) | |
{ | |
var length = response.thread.items.length; | |
for (var i = 0; i < length; ++i) { | |
data[start + i] = new Date(response.thread.items[i].timestamp / 1000).toString() + '\t' | |
+ usernames[response.thread.items[i].user_id] + '\t' | |
+ '"' + getMessageText(response.thread.items[i]).replace(/"/g, '""') + '"'; | |
} | |
start += length; | |
} | |
function scrollThread(thread, response) | |
{ | |
parseThread(thread, response); | |
if (response.thread.prev_cursor != "MINCURSOR") { | |
var cursor = response.thread.oldest_cursor; | |
var xhr = new XMLHttpRequest(); | |
xhr.open("GET", "https://i.instagram.com/api/v1/direct_v2/threads/" + thread + "/?cursor=" + cursor + "&limit=" + limit); | |
xhr.withCredentials = true; | |
xhr.addEventListener("load", function() { | |
var response = JSON.parse(xhr.responseText); | |
scrollThread(thread, response); | |
}); | |
xhr.send(); | |
} | |
else { | |
displayData(); | |
} | |
} | |
function getMessageText(item) | |
{ | |
var type = item.item_type; | |
var text; | |
switch (type) { | |
case "text": | |
text = item.text; | |
break; | |
case "link": // used if the message contains a link | |
text = item.link.text; | |
break; | |
case "action_log": // used when someone likes a message | |
text = item.action_log.description; | |
break; | |
case "like": // used when someone sends a like | |
text = item.like; | |
break; | |
case "media": | |
if (item.media.media_type == 1) { | |
text = item.media.image_versions2.candidates[0].url; | |
} | |
else { // media_type is 1 for images and 2 for videos | |
text = item.media.video_versions[0].url; | |
} | |
break; | |
case "animated_media": // for GIF files | |
text = item.animated_media.images.fixed_height.url; | |
break; | |
case "voice_media": // for audio recordings | |
text = item.voice_media.media.audio.audio_src; | |
break; | |
} | |
return text; | |
} | |
function initVariables() | |
{ | |
start = 0; | |
data = []; | |
usernames = []; | |
} | |
function displayData() | |
{ | |
var box = document.createElement("textarea"); | |
box.value = data.join("\n"); | |
document.body.appendChild(box); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment