Bot para chat de Facebook (v2)

Estándar

Hace un tiempo publiqué un bot para el chat de Facebook, el cual en su tiempo (diciembre de 2013) funcionaba a la perfección.
Hace unos días anduve dando una repasada al blog (ya no publico por falta de tiempo), y vi que no funcionaba más.

Durante el tiempo que anduve desaparecido, aprendí que jQuery no vale (al menos para mí no, eviten críticas en esto), y que JavaScript puede hacer peticiones web usando AJAX y parseo del DOM con DOMParser. Fue entonces cuando con más razón decidí refactorizar el código de mi bot, actualizarlo a cómo Facebook trabaja hoy en día, y optimizar el código. Así que, a ver el código:

Código del bot

/**
 * Sends a message to all your online Facebook contacts.
 *
 * @author Bryan Horna <bryanjhv@gmail.com>
 * @version 2.0
 */


/* Constants */

/**
 * The message you want to send.
 *
 * @type {String}
 */
var MESSAGE = "Hi! I'm trying my Facebook Chat Bot v2. Would you like to " +
    "build your own? Go to: http://wp.me/p40uwy-1E",
  TO_ALL = false;

/**
 * Wrapper for unnecessary functions.
 */
(function (g) {

  /* Helpers */

  /**
   * Iterates over an Array.
   *
   * @param {Array|NodeList} arr
   * @param {Function} callback
   */
  var forEach = function (arr, callback) {
    [].forEach.call(arr, callback);
  };

  /**
   * Parses HTML.
   *
   * @param {String} html
   * @returns {HTMLDocument}
   */
  var parseHtml = function (html) {
    var parser = new DOMParser();
    return parser.parseFromString(html, "text/html");
  };

  /**
   * Gets Object keys.
   *
   * @type {Function}
   */
  var keys = Object.keys;

  /**
   * Makes an AJAX request.
   *
   * Options object:
   * - url: The URL to fetch.
   * - data: Data as hash. (optional)
   * - method: HTTP verb. (default "GET")
   * - success: Callback on success.
   * - error: Callback on error.
   *
   * @param {Object} options
   */
  var request = function (options) {
    var nop = function () {},
      url = options.url || "",
      data = options.data || {},
      method = options.method || "GET",
      success = options.success || nop,
      error = options.error || nop;
    if (!url) {
      return;
    }

    var params = [],
      eUC = encodeURIComponent;
    forEach(keys(data), function (key) {
      params.push(eUC(key) + "=" + eUC(data[key]));
    });
    var textData = params.join("&");

    var xhr = new XMLHttpRequest();
    xhr.open(method, url);
    xhr.onreadystatechange = function () {
      if (xhr.readyState === 4) {
        if (xhr.status === 200) {
          success(xhr.response, data);
        } else {
          error(xhr.status, data);
        }
      }
    };
    xhr.send(method === "GET" ? null : textData);
  };

  /**
   * Fetchs the full name given an ID.
   *
   * @param {Object} fields
   * @param {Function} callback
   */
  var userName = function (fields, callback) {
    var id = "";
    forEach(keys(fields), function (field) {
      if (field.indexOf("ids") + 1) {
        id = fields[field];
      }
    });
    var success = function (data) {
      data = JSON.parse(data);
      callback(data.name, id);
    };
    if (id) {
      request({
        url:     "https://graph.facebook.com/" + id,
        success: success
      });
    }
  };

  /**
   * Notifies events.
   *
   * @param {String} msg
   */
  var notify = function (msg) {
    alert(msg + ".");
  };


  /* Callbacks */

  /**
   * Gets called when message was sent.
   *
   * @param {String} response
   * @param {Object} fields Fields sent.
   */
  var msgSuccess = function (response, fields) {
    userName(fields, function (name) {
      setTimeout(function () {
        notify("Message sent to: " + name);
      }, 5000);
    });
  };

  /**
   * Gets called when message failed.
   *
   * @param {Number} status
   * @param {Object} fields Fields sent.
   */
  var msgError = function (status, fields) {
    userName(fields, function (name) {
      setTimeout(function () {
        notify("Couldn't send message to: " + name);
      }, 5000);
    })
  };

  /**
   * Called when buddy page was loaded.
   *
   * @param {String} buddyHtml The HTML of the page.
   */
  var buddySuccess = function (buddyHtml) {
    var doc = parseHtml(buddyHtml),
      form = doc.querySelector("#composer_form"),
      inputs = form.querySelectorAll("input"),
      fields = {};
    forEach(inputs, function (input) {
      fields[input.name] = input.value;
    });
    delete fields.send_photo;
    fields.body = MESSAGE;
    var exec = function () {
      request({
        url:     form.action,
        data:    fields,
        method:  "POST",
        success: msgSuccess,
        error:   msgError
      });
    };
    if (TO_ALL) {
      exec();
    } else {
      userName(fields, function (name) {
        if (confirm("Send message to: " + name + "?")) {
          exec();
        }
      });
    }
  };

  /**
   * If fetching buddy page failed.
   */
  var buddyError = function () {
    notify("Couldn't fetch buddy page");
  };

  /**
   * Called when buddylist was retrieved.
   *
   * @param {String} listHtml HTML content.
   */
  var listSuccess = function (listHtml) {
    var doc = parseHtml(listHtml),
      tables = doc.querySelectorAll("#root div table"),
      buddies;
    forEach(tables, function (table) {
      buddies = table.querySelectorAll("td div a");
      if (buddies) {
        forEach(buddies, function (buddy) {
          request({
            url:     buddy.href,
            success: buddySuccess,
            error:   buddyError
          });
        });
      }
    });
  };

  /**
   * If buddylist failed.
   */
  var listError = function () {
    notify("Couldn't fetch buddy list");
  };

  /**
   * Main program.
   *
   * @param {Boolean} all True to send message to all connected buddies.
   */
  g.bot = function (all) {
    if (all) {
      TO_ALL = true;
    }
    request({
      url:     "/buddylist.php",
      success: listSuccess,
      error:   listError
    });
  };

})(window);

Modo de uso

Aquí no voy a suponer que sabes cómo usar lo de arriba, así que detallaré cada paso de forma que todo quede claro. No explicaré el código pues hace lo mismo que el de la versión 1, sólo que está mejorado y actualizado.

  1. Inicia Mozilla Firefox. Si desgraciadamente no lo tienes instalado, tendrás que hacerlo.
  2. Ve a la página móvil de Facebook, e inicia sesión si aún no lo hiciste.
  3. Presiona F12, haz clic en Consola, pega el código (luego de colocar el puntero del mouse en la parte inferior) y presiona ENTER.
  4. Edita el mensaje. Escribe allí mismo MESSAGE = '[tu mensaje]' y presiona de nuevo ENTER.
  5. Llegó la hora de enviar tu mensaje. Si quisieras enviarlo a todos tus contactos, escribe bot(true) y si quieres escoger cada uno, bot(). (y luego de cualquiera un ENTER)
  6. Espera a tus amigos preguntando qué fue lo que enviaste. ¡Listo!

El código puede ser descargado desde este enlace.

Anuncios

¿Y tú qué opinas?

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s