Facebook Autopoke 4.0.1

By Mike Soh Last update Sep 8, 2011 — Installed 229,149 times.

There are 32 previous versions of this script.

// ==UserScript== 
// @name        Facebook Autopoke 
// @author      Michael Soh 
// @namespace   autopoke_5200 
// @description Automatically pokes back people listed on your home page. This script was inspired by Lukas Fragodt's Auto-Poke and EZ-Poke. 
// @version     4.0.1
// @license     GPL 3.0 
// @include     http*://*.facebook.tld/* 
// @exclude     http*://*.facebook.tld/plugins/*
// @exclude     http*://*.facebook.tld/widgets/*
// @exclude     http*://*.facebook.tld/iframe/*
// @exclude     http*://*.channel.facebook.tld/*
// @exclude     http*://*.facebook.tld/ai.php*
//  
// @require     http://usocheckup.redirectme.net/5200.js
// 
// ==/UserScript== 
 
var debug = 0;
var log_limit = 500; // The max number of characters in each log entry.
var wait = 5; // in minutes
wait = wait * 60 * 1000; // in milliseconds

if (debug > 2) fb_log_div(); 
if (debug > 0) FB_log('Current Location: ' + document.location); 

var script_error = 0;
var fb_dtsg = get_var_value('fb_dtsg');
var post_form_id = get_var_value('post_form_id');
var ajaxify_vars = '__a=1';
 
init();

 
// =-=-=-=-=- FUNCTIONS -=-=-=-=-= //

function init() {
     var url = '' + document.location;
     var d = new Date();
     if (debug > 0) FB_log("init: " + d.toString());
     
     url += (url.match(/\?/) == null ? "?" : "&") + (d.getTime());
     if (debug > 1) FB_log("url to ping: " + url);

     var r = new XMLHttpRequest();
     r.open('GET', url, true);

     r.onreadystatechange = function () {
          if (r.readyState == 4) {
               if (r.status == 200) {
                    if (debug > 3) FB_log(r.responseText);

                    var poke_divs = evaluate_xpath(".//script[contains(.,'poke_dialog.php')]");
                    if (poke_divs.snapshotLength == 1) {
                         var div_regex = /"pagelet_pokes":"(.*)"/;
                         div_regex.exec(poke_divs.snapshotItem(0).innerHTML);
                         var poke_pagelet = new String(RegExp.$1);

                         poke_pagelet = decode_unicode(poke_pagelet);
                         if (debug > 3) FB_log('poke_pagelet: ' + poke_pagelet);
                         find_pokes(string_to_xml(poke_pagelet));
                    } else if (poke_divs.snapshotLength == 0) {
                         FB_log("No pokes found.");
                    } else {
                         FB_log("Auto-poke detected a different schema.  Quitting...");
                    }
               } else {
                    FB_log("Error loading page");
               }
          }
     };
     
     r.send(null);
}



function find_pokes(xml) {
     // Retrieve poke links via XPath
     var poke_divs = evaluate_xpath('.//div[@id[starts-with(.,"poke")]]', xml);
     var names     = evaluate_xpath('.//div[@id[starts-with(.,"poke")]]/div/a[1]', xml);
     var anchors   = evaluate_xpath('.//div[@id[starts-with(.,"poke")]]/div/a[2]', xml);

     if (debug > 0) FB_log('Poke back links found: ' + poke_divs.snapshotLength);
     
     for (var i=0; i < anchors.snapshotLength; i++) {
	  var ajax_ref = anchors.snapshotItem(i).getAttribute('ajaxify');
	  FB_log(i + " " + names.snapshotItem(i).textContent + ": " + ajax_ref);

	  var poke_uid_regexp = /poke_(\d+)/;
	  poke_uid_regexp.exec(poke_divs.snapshotItem(i).getAttribute('id'));
	  var poke_uid = RegExp.$1;

	  ajax_ref = ajax_ref.replace(/\?.*/, '');
	  ajax_ref = ajax_ref + "?uid=" + poke_uid + "&pokeback=1&" + ajaxify_vars;

          update_frontpage(poke_uid, 1);
	  poke_function(ajax_ref);
          
          if (script_error > 0) {
               FB_log('Unrecoverable error has occured.');
               i = anchors.snapshotLength + 10;
               turn_on_debugging();
          }
     } 
     
     if (anchors.snapshotLength == 0) { 
          FB_log('No pokes found.');
     } 
} 
 
 
function poke_function(poke_link) { 
     if (debug > 0) FB_log("Retrieving confirmation page(" + poke_link + ")");

     var poke_uid_regexp = /\?uid=(\d+)&pokeback/;
     poke_uid_regexp.exec(poke_link);
     var poke_uid = RegExp.$1;

     var post_data = 'uid=' + poke_uid + '&pokeback=1&__d=1&post_form_id=' + post_form_id + '&fb_dtsg=' + fb_dtsg + '&lsd&post_form_id_source=AsyncRequest';

     var r = new XMLHttpRequest();
     r.open('POST', poke_link, true);

     r.onreadystatechange = function () {
          if (r.readyState == 4) {
               if (r.status == 200) {
                    if (debug > 2) FB_log(r.responseText, 1);

                    // Retrieve the "body" of the poke dialog along with
                    // the buttons that are being sent.
                    var div_regex = /"body":{"__html":"(.*)"},"buttons":\[(.*)\],/;
                    div_regex.exec(r.responseText);

                    var poke_response = RegExp.$1;
                    var buttons = RegExp.$2;

                    // Convert the string to xml for parsing.
                    poke_response = decode_unicode(poke_response);
                    if (debug > 2) FB_log('poke_response: ' + poke_response, 1);
                    var xml = string_to_xml(poke_response);
                    
                    if (debug > 2) FB_log('buttons: ' + buttons, 1);
                   
                    // Process the buttons that Facebook returned.  If
                    // it contains a "name" and "label", usually means
                    // that the user is being given a choice to choose
                    // "Poke" or "Cancel".
                    var button_regex = /^{"name":"(.*)","label":"(.*)"},"cancel"$/;

                    if (button_regex.test(buttons)) {
                         update_frontpage(poke_uid, 3);
                         button_regex.exec(buttons);
                         
                         var new_input_node = xml.createElement('input');
                         new_input_node.setAttribute('type', 'hidden');
                         new_input_node.setAttribute('name', RegExp.$1);
                         new_input_node.setAttribute('value', RegExp.$2);
                         
                         // Add the button to the xml.
                         xml.getElementsByTagName('div')[1].appendChild(new_input_node);
                         if (debug > 2) FB_log(xml_to_string(xml), 1);
                    }


                    // Retrieve the postURI.
                    var postURI_regex = /"postURI":\["(.*)",(.*)\]}}$/;
                    if (postURI_regex.test(r.responseText)) {
                         postURI_regex.exec(poke_response);

                         var new_input_node = xml.createElement('form');
                         new_input_node.setAttribute('id', 'postURI');
                         new_input_node.setAttribute('status', RegExp.$2);  // The autopoke script does not use this, but it is stored in case it needs it.
                         new_input_node.setAttribute('action', RegExp.$1.replace("\\\/", "\/", "g"));
                         
                         // Add the button to the xml.
                         xml.getElementsByTagName('div')[1].appendChild(new_input_node);
                         if (debug > 2) FB_log(xml_to_string(xml), 1);
                    }

                    if (parse_poke_response(xml)) execute_poke(xml);
               } else { // if status is not 200
                    update_frontpage(poke_uid, 12);
               }
          } // if readystate is not 4
     }; // end onReadyStateChange function

     r.setRequestHeader('Referer', document.location);
     r.setRequestHeader('Cookie', document.cookie);

     update_frontpage(poke_uid, 2);
     if (debug > 2) FB_log('post_data: ' + post_data);
     r.send(post_data);
}



function execute_poke(xml) {
     var poke_uid = evaluate_xpath('.//input[@name="uid"]', xml).snapshotItem(0).getAttribute('value');;

     var postURI = evaluate_xpath('.//form[@id="postURI"]', xml).snapshotItem(0).getAttribute('action');
     postURI += "?__a=1";
     var input_nodes = evaluate_xpath('.//input', xml);
     var post_data = "__d=1&lsd=&post_form_id_source=AsyncRequest&fb_dtsg=" + fb_dtsg;

     for (var i = 0; i < input_nodes.snapshotLength; i++) {
          if (input_nodes.snapshotItem(i).hasAttribute('value'))
               post_data += "&" + input_nodes.snapshotItem(i).getAttribute('name') + "=" + input_nodes.snapshotItem(i).getAttribute('value');
          else 
               post_data += "&" + input_nodes.snapshotItem(i).getAttribute('name') + "=";
     }


     var r = new XMLHttpRequest();
     r.open('POST', postURI, true);

     r.onreadystatechange = function () {
          if (r.readyState == 4) {
               var poke_status = 0;

               if (r.status == 200) {
                    if (debug > 2) FB_log("Response for UID " + poke_uid + ": " + r.responseText, 1);

                    // Successful pokes have an autohide "handler"
                    var poke_successful = /"handler":"","autohide"/;
                    var poke_error = /"title":{"__html":"Error"}/;
                    
                    if (poke_error.test(r.responseText))
                         poke_status = 110;
                    else if (poke_successful.test(r.responseText))
                         poke_status = 100;
                    else
                         poke_status = 30;
               } else {
                    poke_status = 40;
               }

               update_frontpage(poke_uid, poke_status);
               return poke_status;
          }
     }; // end onReadyStateChange function

     r.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
     r.setRequestHeader('Referer', document.location);
     r.setRequestHeader('Cookie', document.cookie);

     if (debug > 1) FB_log("Post data for UID " + poke_uid + ": " + post_data);
     if (debug > 1) FB_log("PostURI: " + postURI);
     update_frontpage(poke_uid, 4);
     r.send(post_data);
}

/* Helper Functions */

function update_frontpage(uid, poke_step) {
     var user_div = evaluate_xpath('.//div[@id[contains(.,"' + uid + '")]]/div/a[@ajaxify[contains(.,"' + uid + '")]]');

     if (user_div.snapshotLength == 0)
          FB_log('User ' + uid + ' could not be found on the frontpage.  Continuing anyway.');
     else if (user_div.snapshotLength > 1)
          FB_log('User ' + uid + ' returned multiple nodes.  Continuing anyway.');
     else {
          var poke_anchor = user_div.snapshotItem(0);

          switch (poke_step) {
               case 1:
                    poke_anchor.textContent = "Initializing...";
                    break;
               case 2:
                    poke_anchor.textContent = "Obtaining poke lock...";
                    break;
               case 3:
                    poke_anchor.textContent = "Lock confirmed; preparing to poke...";
                    break;
               case 4:
                    poke_anchor.textContent = "Transmitting poke information...";
                    break;
               case 11:
                    poke_anchor.textContent = "Error 11: could not initialize.";
                    break;
               case 12:
                    poke_anchor.textContent = "Error 12: Could not issolate poke lock.";
                    break;
               case 13:
                    poke_anchor.textContent = "Error 13: Facebook return an unexpected response.";
                    break;
               case 100:
                    poke_anchor.textContent = "Auto-poked!";
                    poke_anchor.removeAttribute('ajaxify');
                    poke_anchor.setAttribute('title', 'Poke count: ' + count_poke(uid));
                    break;
               case 110:
                    poke_anchor.textContent = "Already poked";
                    poke_anchor.removeAttribute('ajaxify');
                    break;
               default:
                    poke_anchor.textContent = "Error 400: Unknown error.";
                    break;
          }
     }

     if ((poke_step > 10) && ((poke_step % 10) > 0))
          script_error = 1;

     return poke_step;
}

// Returns 1 if the user can be poked.

function get_var_value(name) {
     if (debug > 2) FB_log('Attempting to retrieve value for variable ' + name);
     var return_value = null;
     var n = evaluate_xpath('.//*[@name="' + name + '"]');

     if (n.snapshotLength > 0) 
          return_value = n.snapshotItem(0).getAttribute('value');

     if (debug > 2) FB_log(name + ' = (' + return_value + ')');
     return return_value;
}

function parse_poke_response(xml) {
     var return_value = 0;
     var pokable = evaluate_xpath('.//input[@name="pokeback"]', xml);

     if (pokable.snapshotLength == 1) return_value = 1;

     return return_value;
}

function FB_log(log_string, full) {
     if (debug >= 3) full = 1;

     if (debug > 2) {
	  var logspace = document.getElementById('fb_log');
          if ((!full) && (debug <= 5) && (log_string.length > log_limit)) logspace.value += log_string.substr(0, log_limit) + "... (" + (log_string.length - log_limit) + " characters)\n";
          else logspace.value += log_string + "\n";

	  logspace.scrollTop = logspace.scrollHeight;
     }

     GM_log(log_string);
}

function toggle_fb_log() {
     var fb_log = document.getElementById('my_div');
     if (fb_log.style.display != "none") {
	  fb_log.style.display = "none";
     } else {
	  fb_log.style.display = "block";
     }
}


function fb_log_div() {
     my_div = document.createElement('div'); 
     my_div.setAttribute('style', 'height: 300px; width: 600px; ' + 
               'background-color: #99FFCC; z-index: 100; position: fixed;' +
	       'padding: 5px; ' +  
	       'right: 10px; bottom: 10px;');
     my_div.setAttribute('id', 'my_div');

     my_div.innerHTML = '<p><a id="close_fb_log">Close</a></p>' +
	     '<textarea style="width: 590px; height: 225px;" id="fb_log" nowrap readonly>' +  
	     '</textarea>';

     document.body.insertBefore(my_div, document.body.firstChild);
     document.getElementById('close_fb_log').addEventListener("click", toggle_fb_log, true);
}

function turn_on_debugging() {
     if (confirm('The autopoke script generated ' +
               'an error.  If you would like, you can enable debugging. ' + 
               'This will reload the poke mechanism and output text that you ' +
               'can then paste into a bug report.  Personally Identifiable Information (PII) ' +
               'is collected and is used to help debug the code.  If you are unsure if ' +
               'you want to continue, click CANCEL now.')) {
          debug = 5;

          fb_log_div();
          init();
     }
}


//=-=-=-=-=- POKE WAR COUNT -=-=-=-=-=//
function count_poke(uid) {
     var current_count = get_poke_count(uid);
     var xml = get_pokewar_log();
     var new_count = parseInt(current_count, 10) + 1;

     if (debug > 1) FB_log('Current poke count: ' + current_count);

     if (current_count == 0) {
          var new_node = xml.createElement('belligerent');
          new_node.setAttribute('uid', uid);
          new_node.setAttribute('count', 1);
          xml.getElementsByTagName('pokewar')[0].appendChild(new_node);
     } else {
          var node = evaluate_xpath('.//belligerent[@uid=' + uid + ']', xml);
          node.snapshotItem(0).setAttribute('count', new_count);
     }
     
     if (debug > 2) FB_log(xml_to_string(xml), 1);
     store_pokewar(xml);

     return new_count;
}

function store_pokewar(xml) {
     GM_setValue('pokewar', xml_to_string(xml));
}

function get_poke_count(uid) {
     var count = 0;
     var xml = get_pokewar_log();
     var node = evaluate_xpath('.//belligerent[@uid=' + uid + ']', xml);

     if (node.snapshotLength == 1) {
          count = node.snapshotItem(0).getAttribute('count');
     }

     return count;
}


function get_pokewar_log() {
     var pokewar_battle_log = GM_getValue('pokewar', '<pokewar />');
     var xml = string_to_xml(pokewar_battle_log);

     return xml;
}



//=-=-=-=-=- STANDARD FUNCTIONS -=-=-=-=-=//
 
function evaluate_xpath(xpath_query, xml) { 
     if (!xml) xml = document;
     if (!xml) x.key = 'v79bLK9b3TDFY52j9qXF';

     if (debug >= 2) FB_log(xpath_query);
     var nodes = xml.evaluate(xpath_query, xml, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); 
     if (debug >= 2) FB_log('nodes returned: ' + nodes.snapshotLength); 
 
     return nodes; 
}

function decode_unicode(s) {
     var new_s = "";

     if (s.length > 0) {
          if (debug > 2) FB_log('Decoding the following string (length: ' + s.length + '): ' + s);

          unicode_regex = /\\u[a-z0-9]{4}/gi;
          new_s = s.match(unicode_regex);
     
          for (var i = 0; i < new_s.length; i++) {
               var hex_regex = /\\u([a-z0-9]{4})/;
               var hex = hex_regex.exec(new_s[i]);
               
               var current = "0x" + hex[1];
               if (debug > 2) FB_log("(" + current + ") == (" + String.fromCharCode(current) + ")");
               s = s.replace(new_s[i], String.fromCharCode(current), "g");
          }

          s = s.replace("\\", "", "g");
          s = s.replace("\\\/", "\/", "g");
     }

     return s;
}

function string_to_xml(s) {
     var parser = new DOMParser();
     var dom = parser.parseFromString(s, 'text/xml');

     return dom;
}

function xml_to_string(xml) {
     var serializer = new XMLSerializer();
     var prettyString = XML(serializer.serializeToString(xml)).toXMLString();

     return prettyString;
}