diff --git a/background.js b/background.js new file mode 100644 index 0000000..9a019d2 --- /dev/null +++ b/background.js @@ -0,0 +1,349 @@ +var cookiesURL = "http://www.google.com"; + +var autosaveInterval = "30"; + +var ghostModeState = false; + +var autosaveState = true; + +var watchList; + + +var settings; + + +function updateSettings(){ + + chrome.storage.local.get({'savedSettings': []}, function(data) { + settings = data.savedSettings; + + if(settings.interval){ + autosaveState = settings.autosaver; + autosaveInterval = settings.interval; + ghostModeState = settings.ghostMode; + + if(chrome.extension.getViews({ type: "popup" }).length > 0){ + chrome.runtime.sendMessage({command: "updatePopup"}, function(response) { + //console.log("settings and popup updated."); + }); + } + + + }else{ + settings = {autosaver:autosaveState, interval:autosaveInterval, ghostMode:ghostModeState}; // default settings + chrome.storage.local.set({'savedSettings': settings}, function() { + //console.log("done."); + }); + } + }); +} +updateSettings(); + +chrome.webRequest.onBeforeRequest.addListener( + () => ({ + cancel: ghostModeState + }), + { urls: [ '*://*.instagram.com/stories/reel/seen*' ] }, + [ 'blocking' ] +); + + + +chrome.runtime.onMessage.addListener( + function(request, sender, sendResponse) { + + if (request.command == "updateSettings"){ + updateSettings(); + sendResponse({resp: "ok"}); + return true; + } + if (request.command == "updatelist"){ + refreshWatchlist(); + sendResponse({resp: "ok"}); + return true; + } + if (request.command == "check"){ + getIdByUsername(request.username, function(id){ + if(id != "0"){ + check(id, false); + }else{ + alert("Invalid User!"); + } + sendResponse({resp: "ok"}); + }); + return true; + } + if (request.command == "checkId"){ + check(request.id, false); + sendResponse({resp: "ok"}); + return true; + } + + }); + + + + +//refresh watchlist +function refreshWatchlist(){ + + var oldWList = watchList; + + chrome.storage.local.get({'userWatchlist': []}, function(data) { + watchList = data.userWatchlist; + + if(watchList != oldWList){ + autosave(); + } + }); +} +refreshWatchlist(); + + + + + + +// Automated Save Function ********************** +function autosave(){ + if(autosaveState){ // only if enabled + if(watchList && watchList.length > 0){ + //console.log('autosave Called'); + for (var i = 0; i < watchList.length; i++) { + check(watchList[i].id, true); + } + } + } +} + +autosave(); + +setInterval(function(){ + autosave(); +}, (parseInt(autosaveInterval)*1000)); + +//*********************************************** + + + + + +function getIdByUsername(username, callback){ + if(callback){ + var RqstUrl = "https://www.instagram.com/" + username + "/?__a=1"; + + var xhr = new XMLHttpRequest(); + xhr.onload = function() { + var json = xhr.responseText; // Response + json = json.replace(/^[^(]*\(([\S\s]+)\);?$/, '$1'); // Turn JSONP in JSON + json = JSON.parse(json); + + if(json.graphql){ + var id = json.graphql.user.id; + callback(id); + }else{ + callback("0"); + } + }; + xhr.open('GET', RqstUrl); + xhr.send(); + } +} + + +function getCookie(name, callback) { + chrome.cookies.get({"url": cookiesURL, "name": name}, function(cookie) { + if(callback) { + if(cookie){ + callback(cookie.value); + }else{ + chrome.cookies.set({"url": cookiesURL, "name": name, "value": "0"}); + callback("0"); + } + } + }); +} + + + +function check(targetId, isSilent){ + var xhr = new XMLHttpRequest(); + xhr.onload = function() { + var json = xhr.responseText; // Response + json = json.replace(/^[^(]*\(([\S\s]+)\);?$/, '$1'); // Turn JSONP in JSON + json = JSON.parse(json); + + var allStories = json.data.user.feed_reels_tray.edge_reels_tray_to_reel.edges; + + var storiesOfTarget = null; + + for(var x in allStories){ + if(allStories[x].node.id == targetId){ + storiesOfTarget = allStories[x]; + } + } + // we got stories of target + + if(storiesOfTarget == null){ // but there is no stories + if(!isSilent){ + alert("Nothing shared!"); + } + }else{ // there are stories but we have to check if we have them allready + getCookie(targetId, function(lastOnWe) { + //we downloaded lastly 'lastOnWe' + //console.log(lastOnWe); + + //check if last story of target downloaded + var lastOnTarget = storiesOfTarget.node.latest_reel_media; + //console.log(storiesOfTarget.node.latest_reel_media); + + if(lastOnTarget > parseInt(lastOnWe)){ + //there is news + //console.log("Second JSON request sent"); + //save them + + + var xhr2 = new XMLHttpRequest(); + xhr2.onload = function() { + var json2 = xhr2.responseText; // Response + json2 = json2.replace(/^[^(]*\(([\S\s]+)\);?$/, '$1'); // Turn JSONP in JSON + json2 = JSON.parse(json2); + + // we got all we need which what is on tagets story circle + //console.log(json2); + + var storyDataOfTarget = json2.data.reels_media[0].items; // all stories curently in circle + var storyCountOnCircle = json2.data.reels_media[0].items.length; + lastOnTarget = json2.data.reels_media[0].latest_reel_media; + var targetUsername = json2.data.reels_media[0].owner.username; + + var lastWeHaveOnCircle = -1; + + for(var x in storyDataOfTarget){ + if(storyDataOfTarget[x].taken_at_timestamp == lastOnWe){ + lastWeHaveOnCircle = x; + } + } + + if(lastWeHaveOnCircle == -1){ // if we dont have any of circle stories, start saving from 0 + //console.log("we dont have any of circle stories"); + + for(var x in storyDataOfTarget){ + if(storyDataOfTarget[x].is_video == true){ + //save as video + var fileName = storyDataOfTarget[x].taken_at_timestamp; + var videoRes = storyDataOfTarget[x].video_resources; + var fileURL = videoRes[videoRes.length-1].src; + + chrome.downloads.download({ + url: fileURL, + filename: "autosavedstories/" + targetUsername + "/" + targetUsername + "_" + fileName + ".mp4" + }); + + }else{ + //save as photo + var fileName = storyDataOfTarget[x].taken_at_timestamp; + var photoRes = storyDataOfTarget[x].display_resources; + var fileURL = photoRes[photoRes.length-1].src; + + chrome.downloads.download({ + url: fileURL, + filename: "autosavedstories/" + targetUsername + "/" + targetUsername + "_" + fileName + ".jpg" + }); + + } + } + + + + + }else{ // if we have some allready then start from where we stay + //console.log("last we have on circle: " + lastWeHaveOnCircle); + + //console.log(storyCountOnCircle); + + var xyz = parseInt(lastWeHaveOnCircle) + 1; + + for(var x = xyz; x < storyCountOnCircle; x++){ + if(storyDataOfTarget[x].is_video == true){ + //save as video + var fileName = storyDataOfTarget[x].taken_at_timestamp; + var videoRes = storyDataOfTarget[x].video_resources; + var fileURL = videoRes[videoRes.length-1].src; + + chrome.downloads.download({ + url: fileURL, + filename: "autosavedstories/" + targetUsername + "/" + targetUsername + "_" + fileName + ".mp4" + }); + + }else{ + //save as photo + var fileName = storyDataOfTarget[x].taken_at_timestamp; + var photoRes = storyDataOfTarget[x].display_resources; + var fileURL = photoRes[photoRes.length-1].src; + + chrome.downloads.download({ + url: fileURL, + filename: "autosavedstories/" + targetUsername + "/" + targetUsername + "_" + fileName + ".jpg" + }); + + } + } + + + + + } + + + //after save, set cookie + chrome.cookies.set({"url": cookiesURL, "name": targetId, "value": lastOnTarget.toString()}); + if(!isSilent){ + alert("Saved all"); + } + + }; + + var targetStoriesUrl = "https://www.instagram.com/graphql/query/?query_hash=5ec1d322b38839230f8e256e1f638d5f&variables=%7B%22reel_ids%22%3A%5B%22" + targetId + "%22%5D%2C%22tag_names%22%3A%5B%5D%2C%22location_ids%22%3A%5B%5D%2C%22highlight_reel_ids%22%3A%5B%5D%2C%22precomposed_overlay%22%3Afalse%2C%22show_story_viewer_list%22%3Atrue%2C%22story_viewer_fetch_count%22%3A50%2C%22story_viewer_cursor%22%3A%22%22%2C%22stories_video_dash_manifest%22%3Afalse%7D"; + + + + xhr2.open('GET', targetStoriesUrl); + xhr2.send(); + + }else{ + //nothing new + if(!isSilent){ + alert("Allready have those!"); + } + } + + + + + }); + } + + }; + xhr.open('GET', 'https://www.instagram.com/graphql/query/?query_hash=04334405dbdef91f2c4e207b84c204d7&variables=%7B%22only_stories%22%3Atrue%2C%22stories_prefetch%22%3Afalse%2C%22stories_video_dash_manifest%22%3Afalse%7D'); + xhr.send(); +} + + + + + + + + + + + + + + + + + + + diff --git a/icon.png b/icon.png new file mode 100644 index 0000000..01f57e2 Binary files /dev/null and b/icon.png differ diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..358b7ce --- /dev/null +++ b/manifest.json @@ -0,0 +1,29 @@ +{ + "manifest_version": 2, + + "name": "Instagram Black", + "description": "Autosave instagram stories anonymously, or surf on instagram as ghost.", + "version": "1.0", + + "browser_action": { + "default_icon": "icon.png", + "default_popup": "popup.html" + }, + "options_ui": { + "page": "options.html", + "open_in_tab": false + }, + "permissions": [ + "https://*.instagram.com/*", + "webRequest", + "webRequestBlocking", + "https://www.instagram.com/graphql/query/*", + "http://www.google.com/*", + "cookies", + "storage", + "downloads" + ], + "background": { + "scripts": ["background.js"] + } +} \ No newline at end of file diff --git a/options.html b/options.html new file mode 100644 index 0000000..172be21 --- /dev/null +++ b/options.html @@ -0,0 +1,60 @@ + + + + + Instagram Black Options + + + + +
+ + +
+ + +
+ + + + +
+
+ + + + + + \ No newline at end of file diff --git a/options.js b/options.js new file mode 100644 index 0000000..db12bbb --- /dev/null +++ b/options.js @@ -0,0 +1,36 @@ +var settings; + +var autosaverInput = document.getElementById("autosaverState"); +var intervalInput = document.getElementById("interval"); +var ghostModeInput = document.getElementById("ghostMode"); + + +chrome.storage.local.get({'savedSettings': []}, function(data) { + settings = data.savedSettings; + + if(settings.interval){ + autosaverInput.checked = settings.autosaver; + intervalInput.value = settings.interval; + ghostModeInput.checked = settings.ghostMode; + + }else{ + settings = {autosaver:true, interval:"30", ghostMode:false}; // default settings + chrome.storage.local.set({'savedSettings': settings}, function() { + //console.log("done."); + }); + } +}); + +var saveBtn = document.getElementById("saveData"); +saveBtn.addEventListener('click', function() { + + settings = {autosaver:autosaverInput.checked, interval:intervalInput.value, ghostMode:ghostModeInput.checked}; // default settings + chrome.storage.local.set({'savedSettings': settings}, function() { + //console.log("saved."); + chrome.runtime.sendMessage({command: "updateSettings"}, function(response) { + //console.log(response.farewell); + }); + }); + window.close(); + +}, false); \ No newline at end of file diff --git a/popup.html b/popup.html new file mode 100644 index 0000000..17575ec --- /dev/null +++ b/popup.html @@ -0,0 +1,181 @@ + + + + + + + + + + + + + +
+ +

Ghost Mode Active

+

Instagram Black

+ + + Add + Check + +
+ + + + + + + diff --git a/popup.js b/popup.js new file mode 100644 index 0000000..bb2241c --- /dev/null +++ b/popup.js @@ -0,0 +1,301 @@ + +var watchList; + +var ghostModeState; + + +chrome.runtime.onMessage.addListener( + function(request, sender, sendResponse) { + + if (request.command == "updatePopup"){ + updatePopup(); + sendResponse({resp: "ok"}); + return true; + } + + }); + + + +document.getElementById("ghostBtn").addEventListener('click', function() { + updateGhostMode(!ghostModeState); +}); + + + +document.getElementById("go-to-options").addEventListener('click', function() { + if (chrome.runtime.openOptionsPage) { + chrome.runtime.openOptionsPage(); + } else { + window.open(chrome.runtime.getURL('options.html')); + } +}); + + +function syncList(){ + chrome.storage.local.get({'userWatchlist': []}, function(data) { + watchList = data.userWatchlist; + + //TODO: what if watchlist not defined yet ? But works so far =) probably I'm not gonna touch + + for (var i = 0; i < watchList.length; i++) { + var li = document.createElement("li"); + var t = document.createTextNode(watchList[i].username); + li.appendChild(t); + document.getElementById("userListUL").appendChild(li); + li.setAttribute("id", watchList[i].id); + li.setAttribute('title', "Click to check now."); + + var span = document.createElement("SPAN"); + var txt = document.createTextNode("\u00D7"); + span.className = "close"; + span.setAttribute('title', "Click to remove."); + span.appendChild(txt); + li.appendChild(span); + + var close = document.getElementsByClassName("close"); + for (var i = 0; i < close.length; i++) { + close[i].onclick = function() { + var div = this.parentElement; + div.style.display = "none"; + var closeId = div.getAttribute("id"); + removeFromWatchlist(closeId); + div.parentNode.removeChild(div); + } + } + } + }); + updatePopup(); +} + +syncList(); + +function addUserToList(usernameIn, idIn){ + + var newUser = {username:usernameIn, id:idIn}; + + chrome.storage.local.get({'userWatchlist': []}, function(data) { + watchList = data.userWatchlist; + }); + + watchList.push(newUser); + + chrome.storage.local.set({'userWatchlist': watchList}, function() { + //console.log("done."); + refreshListOnBack(); + }); +} + +function removeFromWatchlist(id){ + + chrome.storage.local.get({'userWatchlist': []}, function(data) { + watchList = data.userWatchlist; + + // get index of object with provided id + var removeIndex = watchList.map(function(item) { return item.id; }).indexOf(id); + + // remove object + watchList.splice(removeIndex, 1); + + //set new watch list + chrome.storage.local.set({'userWatchlist': watchList}, function() { + //console.log("done."); + refreshListOnBack(); + }); + + }); + +} + + + +function refreshListOnBack(){ + chrome.runtime.sendMessage({command: "updatelist"}, function(response) { + //console.log(response.ok); + }); +} + + + + +// Check when clicking on a list item +var list = document.querySelector('ul'); +list.addEventListener('click', function(ev) { + if (ev.target.tagName === 'LI') { + var selectedId = ev.target.getAttribute("id"); + chrome.runtime.sendMessage({command: "checkId", id: selectedId}, function(response) { + //console.log(response.ok); + }); + } +}, false); + + + +// Add listener for buttons +var addBtn = document.getElementById("addBtn"); +addBtn.addEventListener('click', function(ev) { + newElement(); +}, false); + +var chkBtn = document.getElementById("chkBtn"); +chkBtn.addEventListener('click', function(ev) { + + var inputValue = document.getElementById("usernameInput").value; + + chrome.runtime.sendMessage({command: "check", username: inputValue}, function(response) { + //console.log(response.ok); + }); + + document.getElementById("usernameInput").value = ""; + +}, false); + + + +function getIdByUsername(username, callback){ + if(callback){ + var RqstUrl = "https://www.instagram.com/" + username + "/?__a=1"; + + var xhr = new XMLHttpRequest(); + xhr.onload = function() { + var json = xhr.responseText; // Response + json = json.replace(/^[^(]*\(([\S\s]+)\);?$/, '$1'); // Turn JSONP in JSON + json = JSON.parse(json); + + if(json.graphql){ + var id = json.graphql.user.id; + callback(id); + }else{ + callback("0"); + } + + + }; + xhr.open('GET', RqstUrl); + xhr.send(); + } +} + +// Create a new list item when clicking on the "Add" button +function newElement() { + + var inputValue = document.getElementById("usernameInput").value; + + if (inputValue === '') { + alert("You must enter username!"); + } else { + chrome.storage.local.get({'userWatchlist': []}, function(data) { + watchList = data.userWatchlist; + + if(watchList.find(x => x.username === inputValue)){ + alert("Already on list!"); + }else{ + //check if username valid + getIdByUsername(inputValue, function(id){ + if(id != "0"){ + //let's add to list + addUserToList(inputValue, id); + + var li = document.createElement("li"); + var t = document.createTextNode(inputValue); + li.appendChild(t); + document.getElementById("userListUL").appendChild(li); + li.setAttribute("id", id); + li.setAttribute('title', "Click to check now."); + + document.getElementById("usernameInput").value = ""; + + var span = document.createElement("SPAN"); + var txt = document.createTextNode("\u00D7"); + span.className = "close"; + span.setAttribute('title', "Click to remove."); + span.appendChild(txt); + li.appendChild(span); + + var close = document.getElementsByClassName("close"); + for (var i = 0; i < close.length; i++) { + close[i].onclick = function() { + var div = this.parentElement; + div.style.display = "none"; + var closeId = div.getAttribute("id"); + removeFromWatchlist(closeId); + div.parentNode.removeChild(div); + } + } + updatePopup(); + }else{ + document.getElementById("usernameInput").value = ""; + alert("Invalid User!"); + } + }); + } + }); + } +} + + +function updateGhostMode(ghostmode){ + chrome.storage.local.get({'savedSettings': []}, function(data) { + settings = data.savedSettings; + + if(settings.interval){ + + settings.ghostMode = ghostmode; + + chrome.storage.local.set({'savedSettings': settings}, function() { + //console.log("saved."); + chrome.runtime.sendMessage({command: "updateSettings"}, function(response) { + //console.log(response.ok); + }); + }); + } + }); +} + + +function updatePopup(){ + + var settings; + + chrome.storage.local.get({'savedSettings': []}, function(data) { + settings = data.savedSettings; + + if(settings.interval){ + var autosaveState = settings.autosaver; + ghostModeState = settings.ghostMode; + + if(ghostModeState){ + document.getElementById("ghostText").classList.remove("off"); + document.getElementById("ghostBtn").style.color = "limegreen"; + }else{ + document.getElementById("ghostText").classList.add("off"); + document.getElementById("ghostBtn").style.color = "white"; + } + + if(!autosaveState){ + var element = document.getElementsByTagName("LI"); + + for(var i = 0; i < element.length; i++) + { + element[i].classList.add('inactive'); + element[i].setAttribute('title', "Autosave Disabled!"); + } + + }else{ + var element = document.getElementsByTagName("LI"); + + for(var i = 0; i < element.length; i++) + { + element[i].classList.remove('inactive'); + element[i].setAttribute('title', "Click to check now."); + } + } + + + + } + }); + +} +updatePopup(); +