Bot para chat de Facebook (v2)

fijo

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.

Los números de 2014

Estándar

Los duendes de las estadísticas de WordPress.com prepararon un informe sobre el año 2014 de este blog.

Aquí hay un extracto:

Un teleférico de San Francisco puede contener 60 personas. Este blog fue visto por 1.700 veces en 2014. Si el blog fue un teleférico, se necesitarían alrededor de 28 viajes para llevar tantas personas.

Haz click para ver el reporte completo.

Functions autoloading in PHP

Estándar

Please excuse me for the typos, but I’m a Spanish-speaking person.

Intro

A few days ago I was seeing news of PHP 5.3 (too late, I know), and I saw some that I thought were important: namespaces and classes autoloading.

On these two issues I thought to investigate how to remake my old projects, in order to reduce the amount of code lines occupying the file import statements.

As usual, I usually create a class MyClass in a file MyClass.php, and a function my_function in a file my_function.php, making me life easy to recognize at first glance what that file is and getting an understandable and organized project.

What I’m trying to say? In this article I will use a similar file structure, in order to assist in the organization and see how using namespaces and classes autoloading can be much easier to do our work as PHP programmers.

But why the title says functions autoloading if you’re going to teach me classes autoloading? Well, because we are going to use classes autoloading and apply it to load functions through a class.

Hands on!

Now that you have more or less a vision of what is to be done, let’s start with the code.

fs-afnThe next image shows the file system structure that I will use throughout the article. Show as a picture facilitates our understanding, avoiding mistakes then.
As we can see, we have a main file index.php, in which we will carry out the tests. All PHP code required in this article is located within php folder, and as you might guess, inside it will be our classes, functions, and helpers folder haves the PHP script that will allow us to make functions autoloading.

I will start to show code for each file, to see the why I organized them in such manner.

<?php

namespace php\classes;
use php\helpers\Fn;

class Class1 {
    
    public $msg = "Hello, I'm first class.";
    
    public function say() {
        Fn::say($this->msg);
    }
    
}

?>
<?php

namespace php\classes;

class Class2 extends Class1 {
    
    public function __construct() {
        $this->msg = "Hi, I'm second class.";
    }
    
}

?>
<?php

namespace php\functions;

function say($var) {
    if (is_string($var) || is_numeric($var)) {
        echo $var;
    } elseif (is_bool($var)) {
        echo $var ? 'true' : 'false';
    } elseif (is_object($var) && method_exists($var, '__toString')) {
        // echo $var->__toString();
    } elseif (is_array($var)) {
        $s = array();
        foreach ($var as $k => $v) {
            $s[] = $k . ' = ' . $v;
        }
        echo implode('&', $s);
    } elseif (is_null($var)) {
        echo 'null';
    }
}

?>
<?php

namespace php\helpers;

/**
 * Autoloading functions in PHP.
 * 
 * @author Bryan Horna <bryanjhv@gmail.com>
 * @version 0.2
 */
class Fn {
    
    private function __construct() {}
    private function __wakeup() {}
    private function __clone() {}
    
    public static function __callStatic($fname, $args) {
        if (!function_exists($fn)) {
            $fn = "php\\functions\\$fn";
            require str_replace('\\', '/', $fn) . '.php';
        }
        return call_user_func_array($fn, $args);
    }
    
}

?>
<?php

# include_once 'php/autoload.php';
spl_autoload_register();

use php\helpers\Fn;
use php\classes\Class2;

# Using the created class
(new Class2())->say();

# Custom functions
Fn::say("Hello, world!");

# PHP built-in functions
echo Fn::str_replace(" ", "", "This is a text.");

?>

Am I missing something? Ah, yes, sorry. I forgot to include the code of php/autoload.php, but do you need that file? It’s not, but if you want to complicate things, you can create it and write <?php spl_autoload_register(); ?> and ready.

Code explanation

Now I will explain the code, but only the parts that are neccessary to explain. I.e., do not explain how inheritance works in PHP, just explain why the simplicity of the code.

  • Namespaces: Namespaces in PHP were introduced in PHP 5.3, and like that in other programming languages, they act encapsulating the code we have in "packages". You can find lots of info on the Internet about it.
  • Classes autoloading: The class autoloading is available in PHP since version 5.1.2 (the spl_autoload_register function). But the good news say that when we work with namespaces equal to our filesystem structure, becomes useless to create an autoload function, because PHP does this process automatically.
  • Fn class: This class will help us to dynamically load functions. The only change we make in our code is to add Fn:: before the call to one of our defined functions. Then I made __construct, __wakeup and __clone because they wouldn’t be needed in the class that we created. Moreover, the __callStatic method does it all.

Now, the million-dollar question: and what would help me this? Well, I tell you what I did with this. "I was doing a users system, when suddenly it ocurred to me to use sha1 and md5 to encrypt user passwords. When I gave a look at the PHP site, I realized that I should not use these functions for that. I thought: what do I then need? When I see the PHP site, there is a nice function called password_hash, which does this job for you. There are other functions that help this. If you clicked the link above, you’ll see that it was introduced in PHP 5.5, and my server does not have that version, haves PHP 5.4. I looked for a polyfill which convinced me. But how do you know if password_hash is in the PHP version you are using, because I don’t want to make a version check and also be checking if it exists whenever you need it. It was there where I decided to make Fn::password_hash and ready! If I run it in PHP 5.4, it will call the polyfill; if it is PHP 5.5 it will call PHP 5.5’s built-in function.".

I hope this has been helpful. As always, you can download the code used from here, and if you have any doubts, do not forget to comment in order to resolve them. Happy coding!

Autocarga de funciones en PHP

Estándar

Introducción

Hace algunos días anduve viendo las novedades de PHP 5.3 (muy tarde, lo sé), y vi algunas que me parecieron importantes: los espacios de nombres (namespaces) y la autocarga de clases (autoload).

De estos dos temas pensé en investigar cómo rehacer mis proyectos antiguos, para así reducir en cantidad las líneas de código que ocupaban las sentencias de importación de archivos.

Como es usual, acostumbro crear una clase MiClase en un archivo MiClase.php, y una función mi_funcion en un archivo mi_funcion.php, haciéndome así la vida fácil al reconocer a primera vista de qué se trata dicho archivo y obteniendo un proyecto entendible y organizado.

¿Qué estoy queriendo decir? Que en este artículo usaré una estructura de archivos similar, a fin de ayudar en la organización y ver cómo, usando los espacios de nombres y la autocarga de clases se puede hacer mucho más fácil nuestro trabajo como programadores PHP.

Pero, ¿por qué el título dice autocarga de funciones si me vas a enseñar autocarga de clases? Bien, porque vamos a usar la autocarga de clases y aplicarlo para cargar funciones mediante una clase.

¡Manos a la obra!

Ahora que ya se tiene más o menos una visión de lo que se va a hacer, comencemos con el código.

fs-afnLa imagen de al lado muestra la estructura del sistema de archivos que usaré a lo largo del artículo. Mostrarlo como imagen facilita nuestro entendimiento, evitando cometer errores luego.
Como podremos ver, tenemos un archivo principal index.php, en el cual realizaremos las pruebas. Todo el código PHP necesario para el artículo se encuentra dentro de la carpeta php, y como es de adivinarse, dentro de ella estarán nuestras clases (carpeta classes), funciones (carpeta functions) y en la carpeta helpers tenemos el script PHP que nos permitirá hacer la autocarga de funciones.

Comienzo a mostrar el código de cada archivo, para así ver el por qué los organicé de dicha forma.

<?php

namespace php\classes;
use php\helpers\Fn;

class Class1 {
    
    public $msg = "Hello, I'm first class.";
    
    public function say() {
        Fn::say($this->msg);
    }
    
}

?>
<?php

namespace php\classes;

class Class2 extends Class1 {
    
    public function __construct() {
        $this->msg = "Hi, I'm second class.";
    }
    
}

?>
<?php

namespace php\functions;

function say($var) {
    if (is_string($var) || is_numeric($var)) {
        echo $var;
    } elseif (is_bool($var)) {
        echo $var ? 'true' : 'false';
    } elseif (is_object($var) && method_exists($var, '__toString')) {
        // echo $var->__toString();
    } elseif (is_array($var)) {
        $s = array();
        foreach ($var as $k => $v) {
            $s[] = $k . ' = ' . $v;
        }
        echo implode('&', $s);
    } elseif (is_null($var)) {
        echo 'null';
    }
}

?>
<?php

namespace php\helpers;

/**
 * Autoloading functions in PHP.
 * 
 * @author Bryan Horna <bryanjhv@gmail.com>
 * @version 0.2
 */
class Fn {
    
    private function __construct() {}
    private function __wakeup() {}
    private function __clone() {}
    
    public static function __callStatic($fname, $args) {
        if (!function_exists($fn)) {
            $fn = "php\\functions\\$fn";
            require str_replace('\\', '/', $fn) . '.php';
        }
        return call_user_func_array($fn, $args);
    }
    
}

?>
<?php

# include_once 'php/autoload.php';
spl_autoload_register();

use php\helpers\Fn;
use php\classes\Class2;

# Using the created class
(new Class2())->say();

# Custom functions
Fn::say("Hello, world!");

# PHP built-in functions
echo Fn::str_replace(" ", "", "This is a text.");

?>

¿Me estoy olvidando de algo? Ah, sí, lo siento. Olvidé incluir el código de php/autoload.php, pero ¿es necesario ese archivo? No lo es, pero si quieres complicarte la cosa, puedes crearlo y escribir <?php spl_autoload_register(); ?> y listo.

Explicación del código

Ahora pasaré a explicar el código, sólo en las partes que son necesarias explicar. Es decir, no explicaré cómo trabaja la herencia en PHP, sólo explicaré el por qué de la simplicidad del código.

  • Espacios de nombres: Los espacios de nombres en PHP fueron introducidos en PHP 5.3, y al igual que en otros lenguajes de programación, estos actúan encapsulando el código que tenemos en "paquetes". Puedes encontrar mucha información en Internet sobre ello.
  • Autocarga de clases: La autocarga de clases está disponible en PHP desde su versión 5.1.2 (la función spl_autoload_register). Pero las buenas nuevas dicen que cuando trabajamos con espacios de nombres iguales a nuestra estructura del sistema de archivos, se hace inútil crear una función autoload, pues PHP hace este proceso de forma automática.
  • La clase Fn: Esta clase es la que nos ayudará a cargar dinámicamente las funciones. La única modificación que deberemos hacer en nuestro código es agregar Fn:: antes de la llamada a una de nuestras funciones definidas. Luego, se hizo privados __construct, __wakeup y __clone porque no serían necesarios en la clase que creamos. Por lo demás, el método __callStatic lo hace todo.

Ahora, la pregunta del millón: ¿y para qué me serviría esto? Pues te cuento lo que hice con esto. "Estaba haciendo un sistema de usuarios, cuando de pronto se me ocurrió usar sha1 y md5 para encriptar las contraseñas de los usuarios. Cuando di un vistazo al sitio de PHP, me di cuenta que no debo usar estas funciones para eso. Pensé ¿qué es lo que entonces necesito? Cuando veo por el sitio de PHP, hay una función bella llamada password_hash, la cual hace este trabajo por ti. Además, hay otras funciones que ayudan a esta. Si entraste al enlace anterior, verás que fue introducida en PHP 5.5, y mi servidor no cuenta con dicha versión, pero sí con la 5.4. Busqué un polyfill el cual me convenció. Pero ¿cómo saber si password_hash está en la versión que esté usando de PHP, pues no quiero hacer un chequeo de versiones y tampoco estar verificando si existe cada vez que la necesite. Fue allí donde decidí hacer Fn::password_hash y listo. Si lo corro en PHP 5.4, llamará al polyfill; si es PHP 5.5, a la función que PHP incluye."

Espero esto te haya sido de utilidad. Como siempre, puedes descargar el código usado desde aquí, y si tienes dudas, no olvides comentar para así poder resolverlas. Happy coding!

Enum en Java

Estándar

El día de hoy tuve que lidiar con un nuevo tipo de dato en el lenguaje de programación Java: el tipo Enum.

Conociendo los enum

Según la documentación de Java sobre este tipo de dato, se dice que: “un tipo enum es un tipo de dato especial que permite a una variable ser un conjunto de constantes predefinidas”. En otras palabras, se podría decir que un tipo enum es un tipo de dato que el usuario define, el cual sólo puede tomar valores de una lista (véase lista como cualquier otra cosa menos un tipo List).

Y nos podemos preguntar: ¿para qué me serviría un enum? Bueno, respondo con preguntas: ¿cuántas veces necesitaste tener a la mano una lista de los días de la semana, los puntos cardinales?, ¿o alguna vez creaste un programa el cual trabaja con licencias, y quieres saber de qué tipo es la que tiene el usuario final? Bien, más claro no puedo decir: un tipo enum es un array de constantes, que tienen el mismo tipo que el enum.

¿Un ejemplo? Bien, hagamos un enum con los puntos cardinales:

package enums.punto;

public enum PuntoCardinal {
    NORTE,
    SUR,
    ESTE,
    OESTE
}

Ok, ese es un ejemplo simple. Y vemos algo nuevo: ya no tenemos nuestro “class”, en su lugar tenemos un “enum”. Deducimos entonces que un enum es una clase, así que debemos tratarla como tal. Ahora veamos cómo probarlo:

package enums.punto;

public class PruebaEnumPuntoCardinal {
    
    public static void main(String[] args) {
        
        PuntoCardinal p = PuntoCardinal.SUR;
        
        // Forma fácil de hacerlo, son constantes
        if (p == PuntoCardinal.SUR) {
            System.out.println("Perú queda en el SUR.");
        }
        
        // Forma POO para trabajar
        if (PuntoCardinal.SUR.equals(p)) {
            System.out.println("Perú queda en el SUR.");
        }
        
    }
    
}

Como podremos ver, podemos usar la POO para comprobar valores enum, o sino lo que aprendimos mucho antes y vemos en cualquier lenguaje: nuestro querido “==” :)

Un enum no es instanciable

Perfecto, pero ahora queremos hacer algo mucho más robusto, pues no es divertido aprender los puntos cardinales con Java. Entonces, ¿qué tal si asignamos un número a cada día de la semana?

package enums.semana;

public enum DiaSemana {
    
    DOMINGO(0),
    LUNES(1),
    MARTES(2),
    MIERCOLES(3),
    JUEVES(4),
    VIERNES(5),
    SABADO(6);
    
    public final int nroDia;
    
    private DiaSemana(int nroDia) {
        this.nroDia = nroDia;
    }
    
    public int getNroDia() {
        return nroDia;
    }
    
    public String getNombreDia() {
        return name();
    }
    
}

Según el código, podemos observar que a pesar que un enum es una clase, no es instanciable. Por lo tanto, si deseas prueba a hacer un “new DiaSemana(4)” y no podrás. Luego, otra pregunta que puede surgir es: ¿dónde declaraste el método name() al que llamas desde getNombreDia()? Mi respuesta es que todo enum hereda de la clase Enum, y viendo en la documentación de Java sabremos más. Bueno, probemos el código anterior:

package enums.semana;

public class PruebaEnumDiaSemana {
    
    public static void main(String[] args) {
        
        DiaSemana d = DiaSemana.DOMINGO;
        
        StringBuilder sb = new StringBuilder();
        sb.append("Hoy es ");
        sb.append(d.getNombreDia());
        sb.append(" y es número ");
        sb.append(d.getNroDia());
        sb.append(".");
        
        System.out.println(sb);
        
    }
    
}

Métodos heredados de Enum

Como ya dije antes, todo tipo enum hereda de la clase Enum de Java. Los métodos que hereda de ella son:

  • public final boolean equals(Object other): Devuelve TRUE si el objeto especificado es igual a esta constante. Sobrescribe el método “equals” de Object.
  • public final String name(): Devuelve en nombre de esta constante, tal y como fue declarada.
  • public final int ordinal(): Devuelve la posición de la constante según la declaración de éstas. A la primera constante declarada se le asigna 0.
  • public String toString(): Devuelve lo mismo que name(), pero sobrescribe “equals” de Object.
  • public static enumConstant[] values(): Devuelve un array con las constantes declaradas.

El porqué hice esto

Me animé a leer esto luego de tener que lidiar con algún código sacado de Zend Studio 10.5, el cual expongo:

package com.zend.php.core.core.key;

public enum SKU {
    
    SUBSCRIPTION(1, "Zend Studio Subscription"),
    IBM(2, "Zend Studio Standard for IBM-I"),
    PERPETUAL(3, "Zend Studio Perpetual");
    
    public final int index;
    public final String description;
    
    private SKU(int index, String description) {
        this.index = index;
        this.description = description;
    }
    
    public static final SKU byIndex(int index) {
        SKU[] values = values();
        for (SKU sku : values) {
            if (sku.index == index) {
                return sku;
            }
        }
        return null;
    }
    
}

Y luego de ver un código tipo “parche” en el que el creador intentó igualar el enum con una clase, preferí aprender algo sobre los enum antes que igualarlo con clases.

Bien, eso ha sido todo. Como siempre, los archivos usados están en este link.

Solución a la práctica de Algoritmos I – Grupo A

Estándar

La práctica tomada por el profesor el día de hoy nos sorprendió un poco con el tiempo dado para resolverla. Aún así, logré ver que la mayoría logró resolverla en una escala media.

El trabajo que ahora tenemos consta en desarrollar la práctica. Y como es una oportunidad para mejorar lo que hicimos durante el tiempo dado, pues aquí expongo la mejora de mi código (el que hice en clase me lo reservo).

Codificación

public class Fraccion {
    
    private int num;
    private int den;
    
    /**
     * Crea una nueva Fraccion.
     */
    public Fraccion() {
        num = 0;
        den = 1;
    }
    
    /**
     * Copia la Fraccion en f a esta Fraccion.
     * @param f Fraccion a copiar
     */
    public Fraccion(Fraccion f) {
        num = f.num;
        den = f.den;
    }
    
    /**
     * Inicializa los valores de esta Fraccion.
     * @param num numerador
     * @param den denominador
     */
    public Fraccion(int num, int den) {
        this.num = num;
        this.den = den;
    }
    
    /**
     * Obtiene el numerador de esta Fraccion.
     * @return numerador
     */
    public int getNum() {
        return num;
    }
    
    /**
     * Obtiene el denominador de esta Fraccion.
     * @return denominador
     */
    public int getDen() {
        return den;
    }
    
    /**
     * Asigna el numerador de esta Fraccion.
     * @param num numerador a asignar
     */
    public void setNum(int num) {
        this.num = num;
    }
    
    /**
     * Asigna el denominador de esta Fraccion.
     * @param den denominador a asignar
     */
    public void setDen(int den) {
        this.den = den;
    }
    
    /**
     * Suma dos Fracciones.
     * @param f segunda Fraccion a sumar
     * @return Fraccion suma
     */
    public Fraccion sumar(Fraccion f) {
        return new Fraccion(num * f.den + f.num * den, den * f.den);
    }
    
    /**
     * Multiplica dos Fracciones.
     * @param f segunda Fraccion a multiplicar
     * @return Fraccion producto
     */
    public Fraccion multiplicar(Fraccion f) {
        return new Fraccion(num * f.num, den * f.den);
    }
    
    /**
     * Simplifica una Fraccion.
     * @return Fraccion simplificada
     */
    public Fraccion simplificar() {
        int n = num;
        int d = den;
        int res;
        // Calculamos el MCD
        while (d != 0) {
            res = n % d;
            n = d;
            d = res;
        }
        return new Fraccion(num / n, den / n);
    }
    
    /**
     * Compara fracciones.
     * @param f Fraccion a comparar
     * @return true si esta Fraccion es mayor a f.
     */
    public boolean esMayor(Fraccion f) {
        if (num * f.den > f.num * den) {
            return true;
        }
        return false;
    }
    
    /**
     * Devuelve la Fraccion como cadena.
     * @return Fraccion como String
     */
    public String toString() {
        return num + "/" + den;
    }
    
}
import java.util.Random;
import java.util.Scanner;

public class PruebaFraccion {
    
    private static void generar(Fraccion[] v) {
        Random r = new Random();
        for (int i = 0; i < v.length; i++) {
            int num = r.nextInt(10);
            int den = r.nextInt(10);
            // El denominador no puede ser 0
            while (den == 0) {
                den = r.nextInt(10);
            }
            v[i] = new Fraccion(num, den);
        }
    }
    
    private static void mostrar(Fraccion[] v) {
        System.out.println();
        for (int i = 0; i < v.length; i++) {
            // No es necesario llamar a toString()
            // pues lo llama de manera automatica
            System.out.println(v[i]);
        }
        System.out.println();
    }
    
    private static Fraccion mayor(Fraccion[] v) {
        Fraccion mayor = new Fraccion(v[0]);
        // i = 1 porque ya asignamos arriba
        // la fraccion mayor a 0
        for (int i = 1; i < v.length; i++) {
            if (v[i].esMayor(mayor)) {
                mayor = new Fraccion(v[i]);
            }
        }
        return mayor;
    }
    
    private static Fraccion suma(Fraccion[] v) {
        Fraccion suma = new Fraccion(v[0]);
        // i = 1 tambien
        for (int i = 1; i < v.length; i++) {
            suma = suma.sumar(v[i]);
        }
        return suma;
    }
    
    private static Fraccion producto(Fraccion[] v) {
        Fraccion producto = new Fraccion(v[0]);
        // i = 1 tambien
        for (int i = 1; i < v.length; i++) {
            producto = producto.multiplicar(v[i]);
        }
        return producto;
    }
    
    public static void main(String[] args) {
        
        Scanner sc = new Scanner(System.in);
        
        System.out.print("Numero de elementos: ");
        int n = sc.nextInt();
        sc.close();
        
        Fraccion[] v = new Fraccion[n];
        
        generar(v);
        mostrar(v);
        
        // Tener en cuenta que el toString()
        // ya no debe ser llamado porque Java
        // automaticamente sabe llamarlo
        System.out.println("Fraccion mayor: " + mayor(v));
        System.out.println("Suma: " + suma(v).simplificar());
        System.out.println("Producto: " + producto(v).simplificar());
        
    }
    
}

Espero sus comentarios, observaciones, y otros :)

Bot para chat de Facebook

Estándar

NOTA: El código aquí no funciona más. Ve a este enlace para una versión actualizada.

Muchas veces necesitamos comunicar algo a todos nuestros contactos en Facebook. Por ejemplo, para celebrar nuestra fiesta de cumpleaños, salir a pasear un rato, o tal vez para un evento masivo.

Pero sabemos que crear un “evento” en Facebook no es lo mejor: nadie los ve (me incluyo). Facebook se tomó la molestia de colocar a la barra lateral su panel de eventos, sabiendo que cuando visitan el sitio, la mayoría ve al centro. No sé lo que pensaría su creador, pero bueno, eso no es de mi incumbencia.

Ahora, ¿de qué forma hacemos llegar nuestro mensaje a todos nuestros amigos? Pues de ninguna. Siempre verás que cuando creas un evento, quienes primero lo ven son los que están online. Entonces, ¿para qué enviar a los demás el mensaje si ni siquiera lo van a ver?

Bueno, dejando ya de criticar a Facebook. Muchas veces me vi en aprietos al querer comunicar algo a todos los conectados, y tuve que ir de ventanita en ventanita haciendo “Ctrl+C“, “Ctrl+V” en cada uno de los campos. Y si tengo 40 conectados, créanme que no es nada bonito cansarse tocando tanto el teclado sólo para eso.

Vi en varios foros alguna solución al respecto, y veo que se quedaron con el Facebook del año 2011 (23 de abril y 10 de junio). La verdad me dispuse a probar cada uno de ellos. ¡Ninguno me funcionó! Se complica tanto la cosa cuando intentan hacer expresiones regulares y uno no entiende absolutamente ni mierda nada.

¡Venga el código!

Visto esto, y con la necesidad de informar masivamente, es que decidí hacer algo yo mismo. Pues con las experiencias que tengo viendo código en Internet, supe que no iba a encontrar algo que acertara. ¡Y pues aquí tengo un código JavaScript que funcionará para comunicar a todos los conectados del chat, a modo de bot para chat de Facebook!

/**
 * Send a message to all your online Facebook contacts
 *
 * @author Bryan Horna &lt;bryanjhv@gmail.com&gt;
 * @version 1.0
 */

// Your message
var message = &quot;Hi! I'm trying my new Facebook Chat Bot.&quot; +
&quot; Would you like how to build your own?&quot; +
&quot; Go to https://bryanjhvtk.wordpress.com/2013/12/02/bot-chat-facebook/&quot;;

// Only load jQuery if it isn't present yet
if ( !window.jQuery ) {
  var _jQ = document.createElement( &quot;script&quot; );
  _jQ.setAttribute( &quot;type&quot;, &quot;text/javascript&quot; );
  _jQ.setAttribute( &quot;id&quot;, &quot;_jQ&quot; );
  _jQ.setAttribute( &quot;src&quot;, &quot;//code.jquery.com/jquery-1.10.2.min.js&quot; + &quot;?r=&quot; + ( new Date() ).getTime() );
  document.getElementsByTagName( &quot;head&quot; )[ 0 ].appendChild( _jQ );
  _jQ.onload = function() {

    // If jQuery is now loaded, send the message
    sendAll( message );
  };
}

// When DOM is ready for work
var sendAll = function( msg ) {
  jQuery( document ).ready(function( $ ) {

    var fb_ = &quot;https://m.facebook.com&quot;;

    // Get all online contacts
    $.ajax({
      url: fb_ + &quot;/buddylist.php&quot;,
      success: function( src ) {
        var buddies = $( &quot;.buddylistItem a&quot;, $.parseHTML( src ) );
        for ( var i = 0; i &lt; buddies.length; i++ ) {
          var oneBuddy = $( buddies[ i ] ).attr( &quot;href&quot; );
          var fields = {};

          // Get contact by contact
          $.ajax({
            url: fb_ + oneBuddy,
            success: function( buddyPage ) {
              var form = $( &quot;#composer_form&quot;, $.parseHTML( buddyPage ) )[ 0 ];
              var inputs = $( &quot;input&quot;, form );
              for ( var j = 0; j &lt; inputs.length; j++ ) {
                fields[ $( inputs[ j ] ).attr( &quot;name&quot; ) ] = $( inputs[ j ] ).attr( &quot;value&quot; );
              }

              // But &quot;Reply&quot; button isn't important now
              delete fields[ &quot;send&quot; ];

              // Add our message
              fields[ $( $( &quot;textarea&quot;, form )[ 0 ] ).attr( &quot;name&quot; ) ] = msg;

              // Now, send the POST message
              $.ajax({
                url: fb_ + $( form ).attr( &quot;action&quot; ),
                type: &quot;POST&quot;,
                data: fields,
                success: function( wasSent ) {

                  // Do what you want here
                  alert( &quot;Message successfully sent to &quot; + oneBuddy.match( /[0-9]+/i ) + &quot;.&quot; );
                },
                error: function( code ) {
                  // Handle message not sent
                }
              });
            },
            error: function( code ) {
              // Handle get one buddy failed
            }
          });
        }
      },
      error: function( code ) {
        // Handle get buddies list failed
      }
    });
  });
};

// If jQuery has loaded, send now
sendAll( message );

Explicación del código

El código aquí expuesto aún está en fase beta, por lo que no garantizo su funcionamiento hasta ver resultados en otros usuarios. Pues tengo Facebook en inglés, y tal vez cambie algo, pero no creo que mucho. De todas maneras, explico su funcionamiento:

  • Primero que nada, verificamos si jQuery ya está cargado en la página (tal vez algo innecesario, porque Facebook no lo usa, o al menos nunca lo pude apreciar, pero tal vez también usas Greasemonkey y algún script ya lo cargó).
  • Luego se crea la función sendAll(), la cual como indica el nombre, envía el mensaje a todos los conectados. Dentro de ella, podemos destacar:
    • Se hace una petición al sitio móvil de Facebook en la pestaña de contactos.
    • Se parsea con jQuery el HTML obtenido con el GET, a fin de obtener cada ítem que aparece en dicha lista en el momento de la petición.
    • Iniciamos un bucle, a fin de ir enviando contacto por contacto el mensaje. Por ello, si tienes muchos contactos, tal vez sientas que el script se pone un poco lento. Es normal, tampoco quieras que XMLHttpRequest sea de gran velocidad como Comet o XMPP.
    • Obtenemos el enlace de cada contacto, pues ese es el que nos llevará a la página del usuario y permitirá obtener los campos del formulario de envío de mensajes móviles.
    • Hacemos una segunda petición GET, esta vez al enlace de cada contacto. Si ya tenías un historial de conversación, te lleva a una página (el script no hace eso, la redirección es automática). Sino, te lleva al enlace que se colocó.
    • Iniciamos buscando los campos que hay en el formulario, luego de parsear la página HTML con jQuery. Todos estos son guardados en el objeto JavaScript “field”. Luego de eso, eliminamos la clave “send”, pues es la correspondiente al botón de submit del formulario, y no nos interesa si enviaremos la petición vía POST.
    • Finalmente, hacemos una petición del tipo POST a la dirección obtenida del “action” del formulario (verás que varía si ya tienes historial de conversación o no). Los datos enviados son los que están en “field”, incluido nuestro mensaje.
  • Y finamente se hace la llamada a la función.

Cabe destacar que aún falta optimizar un poco esto, para que tal vez haga el trabajo más rápido, o haga menos peticiones AJAX y se reduzca un gran número de variables, o tal vez usando expresiones regulares en vez de jQuery. Si logro mejorarlo, iré publicando las actualizaciones.

Además, faltaría tener la capacidad de seleccionar los contactos a los que quieres enviar el mensaje, pues tuve que disculparme con muchos de mis amigos por los mensajes que les llegaron mientras probaba en mi cuenta. El script funciona hasta el día de hoy, sin saber su funcionamiento o validez en un futuro, pero asegurando que si ya no sirve, lo actualizaré.

Dime en los comentarios si te logró funcionar, para así poder publicar el código como un script en Greasemonkey.

P.D.: En cuanto tenga tiempo, lo subo a un servidor para que lo puedan incluir sin abrir la consola del navegador (F12), sino en la barra de direcciones. Además, poder pasarle como parámetro a la URL el mensaje que se quiere enviar, en lugar de modificar el código.

Actualización al 03/12/2013

Anduve probando en varias cuentas de Facebook de mis amigos, en la página web para escritorio, y lamentablemente no se envían los mensajes, se complican las conexiones AJAX hasta lograr perderse. Luego, hice un diagnóstico y concluí en que se debe ejecutar este código en una ventana abierta con la página móvil de Facebook (https://m.facebook.com) y ponerlo en la consola.

También tuve un problema en Google Chrome con la extensión Tampermonkey, así que tuve que ejecutarlo en Mozilla Firefox, por lo que debe ser ejecutado en Mozilla Firefox para funcionar.