├── icon.png ├── CHANGELOG.md ├── manifest.json ├── README.md ├── get_fb_id.js ├── popup ├── popup.html └── popup.js └── background.js /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sowdust/searchbook-chrome/HEAD/icon.png -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Version 0.4 (2019-07-10) 2 | 3 | * Fixed to work with Facebook changes 4 | 5 | # Version 0.3 (2019-07-09) 6 | 7 | * Update notification system 8 | 9 | # Version 0.2 (2019-06-17) 10 | 11 | * Added feature to search for photos and videos elements -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Graph search tool for Facebook", 3 | "manifest_version": 2, 4 | "name": "SearchBook", 5 | "version": "0.4", 6 | 7 | "permissions": [ 8 | "activeTab", 9 | "webRequest", 10 | "webRequestBlocking", 11 | "https://www.facebook.com/" 12 | ], 13 | 14 | "browser_action": { 15 | "default_icon": "icon.png", 16 | "default_title": "SearchBook", 17 | "default_popup": "popup/popup.html" 18 | }, 19 | 20 | "background": { 21 | "scripts": ["background.js"] 22 | } 23 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SearchBook ![](https://raw.githubusercontent.com/sowdust/searchbook/master/icon.png) 2 | 3 | ```diff 4 | - Aug 1st: The extension is currently not working: unencrypted queries are no more accepted by the endpoint. 5 | ``` 6 | 7 | A Chrome extension for executing some Graph-like searches against Facebook. 8 | 9 | Related article: [techblog.mediaservice.net/2019/06/facebook-graphs-not-dead/](https://techblog.mediaservice.net/2019/06/facebook-graphs-not-dead/) 10 | 11 | ## Disclaimer 12 | 13 | Make sure to read Facebook [Terms of Services](https://www.facebook.com/apps/site_scraping_tos_terms.php). 14 | All information and code are provided for educational purposes only. Using this code might be against Facebook Terms of Service or possibly even illegal. The authors are in no way responsible for any misuse of the information or the code provided. 15 | 16 | ## About 17 | 18 | Chrome version of https://github.com/sowdust/searchbook/ 19 | 20 | Ported by using [Justin Seitz](https://twitter.com/jms_dot_py)'s [instructions](https://twitter.com/jms_dot_py/status/1152277562531430401) 21 | 22 | ## Installation 23 | 24 | * Clone the repository or download it as zip file and then extract it on your filesystem 25 | * In Chrome, open a new tab and visit the URL `chrome://extensions` 26 | * Turn on the "developer mode" switch in the top right corner 27 | * Select Load Unpacked Extension and open the searchbook folder 28 | 29 | ## Usage 30 | 31 | Please refer to [this README](https://github.com/sowdust/searchbook/blob/master/README.md) for usage instructions. 32 | 33 | ## License 34 | 35 | This code is free 36 | 37 | -------------------------------------------------------------------------------- /get_fb_id.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SearchBook 3 | * 4 | * @author sowdust 5 | * @url https://github.com/sowdust/searchbook 6 | * 7 | * License: free 8 | * 9 | */ 10 | 11 | // needs some fixing. sometimes the html is not updated 12 | 13 | //var html = ''; 14 | 15 | function get_html() { 16 | var html = document.documentElement.innerHTML; 17 | get_fb_id(html); 18 | } 19 | 20 | window.addEventListener("load", get_html(), false); 21 | 22 | function get_fb_id(html) { 23 | 24 | //var html = document.documentElement.innerHTML; 25 | //var html = DOMtoString(document); 26 | var regex = new RegExp('fb\:\/\/(page|profile|group)\/([0-9]+)'); 27 | var regexUID = new RegExp('\"uid\"\:([0-9]+)'); 28 | var regexTitle = new RegExp('(.*?)<\/title>'); 29 | 30 | // getting profile id and profile type 31 | try { 32 | pieces = html.match(regex); 33 | profileType = pieces[1]; 34 | profileID = pieces[2]; 35 | } catch(err) { 36 | console.log(err); 37 | profileType = "None"; 38 | profileID = "-1"; 39 | } 40 | 41 | if(profileID == "-1") { 42 | try { 43 | pieces = html.match(regexUID); 44 | profileID = pieces[1]; 45 | profileType = "unknown"; 46 | } catch(err) { 47 | console.log(err); 48 | profileType = "None"; 49 | profileID = "-1"; 50 | } 51 | 52 | if(profileID == "-1") { 53 | console.log("Error while getting Facebook id"); 54 | } 55 | } 56 | 57 | console.log(profileID); 58 | 59 | html = ''; 60 | 61 | chrome.runtime.sendMessage({ 62 | command: "set_facebook_id", 63 | facebook_id: profileID, 64 | facebook_type: profileType 65 | }); 66 | } 67 | 68 | 69 | -------------------------------------------------------------------------------- /popup/popup.html: -------------------------------------------------------------------------------- 1 | <!-- 2 | * SearchBook 3 | * 4 | * @author sowdust<mattia.vinci@mediaservice.net> 5 | * @url https://github.com/sowdust/searchbook 6 | * 7 | * License: free 8 | --> 9 | <!DOCTYPE html> 10 | <html> 11 | <head> 12 | <meta charset="utf-8"> 13 | <link rel="stylesheet" href="popup.css"/> 14 | <style type="text/css"> 15 | body { 16 | font-family:"lucida grande",tahoma,verdana,arial,sans-serif; 17 | font-size: 11px; 18 | } 19 | #header { 20 | text-align: right; 21 | padding: 2px; 22 | } 23 | #header a, #header a:hover, #header a:visited { 24 | color: black; 25 | } 26 | #disclaimer { 27 | display: none; 28 | } 29 | #current_query { 30 | display: block; 31 | padding-top: 3px; 32 | } 33 | #fb_id { 34 | background: none; 35 | border: none; 36 | color: black; 37 | margin: 1px; 38 | padding: 2px; 39 | text-align: center; 40 | } 41 | #query { 42 | padding: 4px; 43 | } 44 | #updates { 45 | display: none; 46 | background: red; 47 | color: white; 48 | font-weight: bold; 49 | margin: 0; 50 | padding: 2px; 51 | font-size: 1.2em; 52 | } 53 | #updates a, #updates a:hover, #updates a:visited { 54 | color: white; 55 | } 56 | input[type=text] { height: 1.2em; } 57 | input[type=button] { height: 1.6em; } 58 | </style> 59 | </head> 60 | <body> 61 | <div id="updates">The new version <span id="new-version"></span> is available!<br /> Click <a href="https://github.com/sowdust/searchbook" target="_blank">HERE</a> for downloads and installation instructions!</div> 62 | <div id="header"> 63 | <a href="https://github.com/sowdust/searchbook" target="_blank">Info</a> · 64 | <a href="https://github.com/sowdust/searchbook" target="_blank">Disclaimer</a> · 65 | by <span id="sowdust"></span> 66 | </div> 67 | <h1>SearchBook</h1> 68 | <input id="query" type="text" name="query" placeholder="insert query here" size="40"> 69 | <input type="button" name="set" value="set" class="set"> 70 | <input type="button" name="del" value="remove" class="rem"> 71 | <span id="current_query"></span> 72 | <span id="facebook_id"></span> 73 | <script src="popup.js" type="text/javascript"></script> 74 | <div id="disclaimer"> 75 | This tool is provided for educational purpose only.<br /> 76 | Make sure to read <a href="https://www.facebook.com/terms.php">Facebook Terms of Service</a>.<br /> 77 | The authors of this page will not be held responsible for any misuse. 78 | </div> 79 | </body> 80 | </html> 81 | -------------------------------------------------------------------------------- /popup/popup.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SearchBook 3 | * 4 | * @author sowdust<mattia.vinci@mediaservice.net> 5 | * @url https://github.com/sowdust/searchbook 6 | * 7 | * License: free 8 | * 9 | */ 10 | 11 | 12 | function set_query() { 13 | query = document.getElementById('query').value; 14 | chrome.runtime.sendMessage({ 15 | command: "set_query", 16 | query: document.getElementById('query').value 17 | }); 18 | window.localStorage.setItem('facebook-search-query',query); 19 | var text = '<span style="width:40em"><b>Current query:</b></span> ' + document.getElementById('query').value; 20 | document.getElementById('current_query').innerHTML = text; 21 | } 22 | 23 | function clear_query() { 24 | console.log('Removing query'); 25 | chrome.runtime.sendMessage({ command: "clear_query" }); 26 | window.localStorage.setItem('facebook-search-query',''); 27 | document.getElementById('current_query').innerHTML = ''; 28 | document.getElementById('query').value = ''; 29 | } 30 | 31 | function fill_values() { 32 | var query = window.localStorage.getItem('facebook-search-query'); 33 | if(query) { 34 | document.getElementById('query').value = query; 35 | var text = '<b>Current query:</b> ' + query; 36 | document.getElementById('current_query').innerHTML = text; 37 | } 38 | } 39 | 40 | function set_facebook_id(facebook_id,facebook_type) { 41 | if(-1 == facebook_id) { 42 | var html = ''; 43 | } else { 44 | var html = '<span style="width:70px">'; 45 | html += '<b>' + facebook_type.charAt(0).toUpperCase() + facebook_type.slice(1) + ' ID:</b> '; 46 | html += '</span>'; 47 | html += '<input id="fb_id" type="text" value="' + facebook_id + '" size="' + (facebook_id.length+1) + '"> '; 48 | html += '[ <a class="cpy" title="copy to clipboard" href="#">copy</a> ]'; 49 | } 50 | document.getElementById('facebook_id').innerHTML = html; 51 | } 52 | 53 | function email_link() { 54 | div = document.getElementById('sowdust'); 55 | div.innerHTML = atob("PGEgaHJlZj0ibWFpbHRvOm1hdHRpYS52aW5jaUBtZWRpYXNlcnZpY2UubmV0Ij5zb3dkdXN0PC9hPg=="); 56 | } 57 | 58 | function show_updates(new_version) { 59 | var update_div = document.getElementById('updates'); 60 | update_div.style.display = 'block'; 61 | var new_version_div = document.getElementById('new-version'); 62 | new_version_div.innerHTML = new_version; 63 | } 64 | 65 | function check_updates() { 66 | var last_update_check = window.localStorage.getItem('searchbook-last-update-check'); 67 | var curr_time = Date.now(); 68 | var manifest = chrome.runtime.getManifest(); 69 | var current_version = manifest.version; 70 | if(!last_update_check || (curr_time - last_update_check) > (12*60*60*1000)) { 71 | console.log('Checking updates...'); 72 | var update_url = 'https://raw.githubusercontent.com/sowdust/searchbook/master/manifest.json'; 73 | var http = new XMLHttpRequest(); 74 | http.open("GET", update_url); 75 | http.send(); 76 | var new_manifest = ''; 77 | http.onreadystatechange = (e) => { 78 | if(http.readyState === 4 && http.status === 200) { 79 | new_manifest = JSON.parse(http.responseText); 80 | if(new_manifest.version > current_version) { 81 | show_updates(new_manifest.version); 82 | window.localStorage.setItem('searchbook-latest-version',new_manifest.version); 83 | } 84 | window.localStorage.setItem('searchbook-last-update-check',curr_time); 85 | } 86 | } 87 | }else{ 88 | var new_vers_av = window.localStorage.getItem('searchbook-latest-version'); 89 | if( new_vers_av > current_version) { 90 | show_updates(new_vers_av) 91 | } 92 | return; 93 | } 94 | } 95 | 96 | // fill Entity ID and current query 97 | document.onload = fill_values(); 98 | // add email address 99 | document.onload = email_link(); 100 | // check for updates 101 | document.onload = check_updates(); 102 | 103 | 104 | document.addEventListener("click", (e) => { 105 | 106 | if (e.target.classList.contains("set")) { 107 | set_query(); 108 | } 109 | if (e.target.classList.contains("rem")) { 110 | clear_query(); 111 | } 112 | if (e.target.classList.contains("cpy")) { 113 | // copy current id to clipboard 114 | var selector = document.getElementById('fb_id'); 115 | selector.select(); 116 | document.execCommand('copy'); 117 | } 118 | if (e.target.classList.contains("tos")) { 119 | var disclaimer = div.getElementById('disclaimer'); 120 | disclaimer.style.display = 'block'; 121 | } 122 | }); 123 | 124 | // set listener for printing Facebook IDs 125 | chrome.runtime.onMessage.addListener((message) => { 126 | if (message.command === "set_facebook_id") { 127 | facebook_id = message.facebook_id; 128 | facebook_type = message.facebook_type; 129 | set_facebook_id(facebook_id,facebook_type); 130 | } 131 | }); 132 | 133 | // trying to get facebook ID of element 134 | chrome.tabs.executeScript({ file: '/get_fb_id.js' }); 135 | -------------------------------------------------------------------------------- /background.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SearchBook 3 | * 4 | * @author sowdust<mattia.vinci@mediaservice.net> 5 | * @url https://github.com/sowdust/searchbook 6 | * 7 | * License: free 8 | * 9 | */ 10 | 11 | var query = window.localStorage.getItem('facebook-search-query'); 12 | var original_query = ''; 13 | var pattern = "https://www.facebook.com/ajax/pagelet/generic.php/BrowseScrollingSetPagelet*"; 14 | //var query_regex = /%7B%5C%22bqf%5C%22%3A%5C%22(.+?)%5C%22%2C%5C%22browse_sid%5C%22/; 15 | var query_regex = /encoded_query%22%3A%22(.+?)%22%2C%22/; 16 | var cursor_regex = /cursor%22%3A%22(.+?)%22/; 17 | var page_regex = /%22page_number%22%3A(\d+)%2C/; 18 | var view_regex = /%7B%22view%22%3A%22([a-z]+)%22/; 19 | var require_grid = ['photos','videos']; 20 | var page_number = 1; 21 | 22 | console.log('SearchBook Extension loaded'); 23 | 24 | function error(error) { 25 | console.log(error); 26 | } 27 | 28 | function add_warning() { 29 | var alert_code = "var div = document.createElement('div');"; 30 | alert_code += "div.id='warning_message';"; 31 | alert_code += "div.style.position = 'fixed';"; 32 | alert_code += "div.style.verticalAlign = 'middle';"; 33 | alert_code += "div.style.padding = '0 auto';"; 34 | alert_code += "div.style.textAlign = 'center';"; 35 | alert_code += "div.style.backgroundColor = '#f44336';"; 36 | alert_code += "div.style.color = 'white';"; 37 | alert_code += "div.style.fontWeight = 'bold';"; 38 | alert_code += "div.style.bottom = 0;"; 39 | alert_code += "div.style.width = '100%';"; 40 | alert_code += "div.style.zIndex = '999';"; 41 | alert_code += "div.innerHTML = 'Intercepting';"; 42 | alert_code += "document.body.appendChild(div);"; 43 | alert_code += "console.log('warning');"; 44 | alert_code += "var q = '" + query + "';"; 45 | alert_code += "var o = '" + original_query + "';"; 46 | alert_code += "var t = ' <tt>Intercepting requests</tt><br />';"; 47 | alert_code += "t += '<tt>Replacing with query: ' + q + '</tt><br />';"; 48 | alert_code += "div.innerHTML = t;"; 49 | chrome.tabs.executeScript({ code: alert_code }); 50 | } 51 | 52 | function remove_old_results() { 53 | var clear_code = "console.log('trying to remove old results');"; 54 | clear_code += "var query=\"" + query + "\";"; 55 | clear_code += "var d = document.getElementById('pagelet_loader_u_ps_0_3_0_browse_result_below_fold');"; 56 | clear_code += "var e = document.getElementById('BrowseResultsContainer');"; 57 | clear_code += "var f = document.getElementById('bootstrap_entity_module');"; 58 | clear_code += "var g = document.getElementById('u_ps_jsonp_3_3_0_browse_result_below_fold');"; 59 | clear_code += "if(d) { d.innerHTML = '<h3 style=\"padding: 3px\">Results for ' + query + '</h3>'; }"; 60 | clear_code += "if(e) { e.innerHTML = ''; }"; 61 | clear_code += "if(f) { f.innerHTML = ''; }"; 62 | clear_code += "if(g) { g.innerHTML = ''; }"; 63 | chrome.tabs.executeScript({ code: clear_code }); 64 | } 65 | 66 | function redirect(requestDetails) { 67 | 68 | if(!query) { 69 | error('Query not set'); 70 | return; 71 | } 72 | 73 | modifiedUrl = requestDetails.url; 74 | 75 | if(1 == page_number) { 76 | 77 | // remove the cursor reference 78 | var q = cursor_regex.exec(modifiedUrl); 79 | if(q.length == 2) { 80 | 81 | modifiedUrl = modifiedUrl.replace(q[1],''); 82 | } 83 | 84 | // remove results related to original query 85 | remove_old_results(); 86 | } 87 | 88 | // avoid infinite loops 89 | if(modifiedUrl.endsWith('&modified=true')) { 90 | console.log('Request already modified'); 91 | return; 92 | } else { 93 | modifiedUrl = modifiedUrl + '&modified=true'; 94 | } 95 | 96 | // replace the original query with ours 97 | var m = query_regex.exec(modifiedUrl); 98 | if(m.length != 2) { 99 | error('Query pattern matching error'); 100 | return; 101 | } 102 | original_query = m[1]; 103 | modifiedUrl = modifiedUrl.replace(m[1],encodeURI(query)); 104 | 105 | // replace page number 106 | var p = page_regex.exec(modifiedUrl); 107 | if(!p || p.length != 2) { 108 | error('Page pattern matching error'); 109 | return; 110 | } 111 | modifiedUrl = modifiedUrl.replace('%22page_number%22%3A'+ p[1] +'%2C', '%22page_number%22%3A'+ page_number +'%2C'); 112 | page_number = page_number + 1; 113 | 114 | // set the right view mode 115 | for(var i=0; i<require_grid.length; ++i) { 116 | if(query.includes(require_grid[i])) { 117 | var v = view_regex.exec(modifiedUrl); 118 | if(v.length == 2) { 119 | modifiedUrl = modifiedUrl.replace('%7B%22view%22%3A%22'+ v[1] +'%22', '%7B%22view%22%3A%22'+ 'grid' +'%22'); 120 | } 121 | } 122 | } 123 | 124 | // warn the user we are intercepting 125 | if(!document.getElementById('warning_message')) { 126 | add_warning(); 127 | } 128 | 129 | return { 130 | redirectUrl: modifiedUrl 131 | }; 132 | } 133 | 134 | // set listener for redirection 135 | chrome.webRequest.onBeforeRequest.addListener( 136 | redirect, 137 | {urls:[pattern]}, 138 | ["blocking"] 139 | ); 140 | 141 | // set listener for messages from the popup interface 142 | chrome.runtime.onMessage.addListener((message) => { 143 | if (message.command === "set_query") { 144 | console.log('Setting query to ' + message.query); 145 | query = message.query; 146 | page_number = 1; 147 | } else if (message.command === "clear_query") { 148 | console.log('Removing query'); 149 | query = ''; 150 | } 151 | }); 152 | --------------------------------------------------------------------------------