sábado, 14 de enero de 2012

Twitter Automatización


Twitter Automatización

Qué es Twitter? Ni idea. Supongamos que tenemos que escribir unos scripts para manejar la información de una cuenta de Twitter o incluso gestionarla, miramos entonces algunos ejemplos https://dev.twitter.com/docs/open-source-examples. Mmmh, creo que Perl estaría bien. Pero hay alguna librería ya escrita? https://dev.twitter.com/docs/twitter-libraries. Aquí se menciona a Net::Twitter, una librería que implementa la interface a la API de Twitter para hacer cosas como por ejemplo: twittear, enviar mensajes directos, seguir amigos, crear lista, bloquear usuarios, buscar ubicaciones, y todo lo que se puede hacer en Twitter. Pero buscando en http://search.cpan.org/ encuentro otras muchas librerías, en esa lista destaco a AnyEvent::Twitter::Stream, escrita por Tatsuhiko Miyagawa y que sirve para recibir el stream de Twitter en un loop.

Manos a la obra

Twitter no es solo una red social, es un medio de comunicación, el más rápido. Es información compartida en tiempo real. Por eso vamos a leer los tweets en tiempo real con el siguiente ejemplo:

Qué necesitamos?

Creamos una aplicacion nueva en https://dev.twitter.com/apps/new con nuestra cuenta de twitter. La aplicacion tendrá un nombre y una descripción que son obligatorios (la descripción de 10 caracteres o mas), una Website también obligatoria pero puede ponerse "https://dev.twitter.com/apps/new" si no se tiene una, la URL Callback puede dejarse en blanco. Una vez creada nos muestra la información de la aplicación y su Consumer key. Debemos generar un access token ahora con el botón Crear mi Access Token. Luego en la pestaña Configuración podemos cambiar los derechos de acceso: Lectura, Lectura y Escritura, Lectura, Escritura y Mensajes Directos.
Una vez seguidos estos pasos ya podemos escribir el siguiente script y ver como aparecen nuestros tweets y los de nuestros amigos. Necesitamos la Consumer Key y Secret, el Token Access y Secret.

use Data::Dumper;
use AnyEvent::Twitter::Stream;
use strict;

run() unless caller();
1;

sub run{
  my $consumer_key= 'TU-CONSUMER-KEY';
  my $consumer_secret= 'TU-CONSUMER-SECRET';
  my $token= 'TU-TOKEN';
  my $token_secret= 'TU-TOKEN-SECRET';
  read_twitter($consumer_key,$consumer_secret,$token,$token_secret);
  return;
}

sub read_twitter{
   my ($consumer_key,$consumer_secret,$token,$token_secret)=@_;
   my $debug=0;
   local $ENV{'ANYEVENT_TWITTER_STREAM_SSL'} = 'https';
   my $cv= AnyEvent->condvar;
   my $listener = AnyEvent::Twitter::Stream->new(
      consumer_key      => $consumer_key,
      consumer_secret   => $consumer_secret,
      token             => $token,
      token_secret      => $token_secret,
      # "firehose" for everything, "sample" for sample timeline
      method   => "userstream",  
      on_tweet => sub {
         my $tweet = shift;
         print Dumper($tweet) if $debug;
         if ($tweet->{'delete'}{'direct_message'}){
            my $tweet_id= $tweet->{'delete'}{'direct_message'}{'id'};
            my $user_id= $tweet->{'delete'}{'direct_message'}{'user_id'};
            warn "Borrado tweet_id: $tweet_id - user_id: $user_id\n";
         }
         elsif ($tweet->{'direct_message'}){
            my $sender_name= $tweet->{direct_message}{sender_screen_name};
            print "Mensaje Directo: $sender_name\n";
         }
         elsif($tweet->{'friends'}){
            print Dumper(\$tweet);
         }
         elsif($tweet->{'event'}){
            print "Evento: $tweet->{event}\n";
         }
         elsif($tweet->{'user'}){
            my $user_name= $tweet->{user}{screen_name};
            print "Tweet Nuevo: $user_name: $tweet->{text}\n";
            print "Mirando el tweet por dentro:\n".Dumper($tweet)."\n";
         } else{
            print 'Otra cosa';
         }
      },
      on_keepalive => sub {
         warn "ping\n";
      },
      on_connect => sub {
         print Dumper(@_);
      },
      on_error => sub {
         print Dumper(@_);
         $cv->send;
      },
      on_eof=> $cv,
      on_delete => sub {
         # callback executed when twitter send a delete notification
         my ($tweet_id, $user_id) = @_; 
         warn "Borrado tweet_id: $tweet_id - user_id: $user_id\n";
      },
      timeout => 45000,
   );

   $cv->recv;

   print "\n";
   return;
}

sábado, 17 de diciembre de 2011

Hola Mundo en Go

Hola Mundo

Mi primer programa en Go:

package main
import fmt "fmt" 
func main() {
    fmt.Printf("Hola Mundo!\n")
}

Las partes del programa son las siguientes: sección package, sección import y sección de funciones.

Declaramos a qué paquete pertenece este archivo, en este caso al package main. 

El import lo que hace es traer paquetes al paquete actual para poder usarlos, por ejemplo el paquete fmt trae la función Printf que permite imprimir un texto o string en la salida standard.

Este programa comienza a ejecutar en la función main, por eso declaramos una función con ese nombre. Su única línea es un printf que es la esencia de este programa. Imprimir un HOLA MUNDO! para nosotros. Las funciones se declaran con la palabra clave func.

Una ventaja de este moderno lenguaje es que las constantes String pueden contener caracteres Unicode codificados en UTF-8 (de hecho los archivos fuente de GO se codifican en UTF-8).



sábado, 25 de septiembre de 2010

Pensando el plugging para jQuery

La operación de agregar filas nuevas a la tabla y la operación de ordenarla son diferentes. Entonces serán métodos diferentes?
Este es un ejemplo de uso de la librería jQuery.airtable.js:
<script language="javascript" src="./jquery.js"></script>
<script language="javascript" src="./jquery.airtable.js"></script>
<script>
$(document).ready(function(){
  $("#myTable").airtable({addRows: [['Gomez','Manuel','gm@gmail.com','50.85','http://gm.com/index.html']]});
});
</script>
<table id="myTable">
<thead>
<tr>
 <th>Apellido</th>
 <th>Nombres</th>
 <th>Emilio</th>
 <th>Cuenta</th>
 <th>Web</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
La opción addRows es un array de arrays porque debe manejar varias filas, y cada fila tiene varias celdas.
El método airtable() agrega las filas ordenadas a la tabla y la inicializa para ordenar y la reordena si es necesario. Por lo tanto vamos a dejar un solo método.
Nota: La columna 0 es la primer columna, la columna 1 es la segunda columna, y así siguiendo.
La librería utilizará la opción sortList que indique la columna a ordenar, si es orden ascendente o descendente y el tipo del dato que contiene: integer o string. Por ejemplo [[0,0,'string']] significa que la columna 0 se ordena en forma ascendente(0) y se comparan strings; [[2,1,'integer'],[1,1,'string']] significa que se ordena la columna 2 en forma descendente(1) y se comparan números, si son iguales se fija en el orden de la columna 1 en orden descendente(1) comparando strings. Otro ejemplo: [[0,0,'string'],[1,0,false]]; acá indica que la segunda columna no es comparable(false), porque no tiene un tipo.
Complicado? Intentaré arreglarlo.
Esta es la primer versión de mi librería jQuiery.airtable.js:
(function($) {
  $.fn.airtable = function(opciones_user){

  // OPCIONES
  // Ponemos la variable de opciones antes de la iteración (each) para ahorrar recursos
  opc = $.extend( $.fn.airtable.defaults, opciones_user );
  d= opc.debug;
  // FUNCIONES PRIVADAS
  // agregar filas al final de la tabla
  function addTableRows(table,rows){
    trs=''
    for (r in rows){
      td='';
      td+= Array.join(rows[r],'');
      td= ''+td+'';
      trs+= '\n'+td+'';
    }
    if (d) { alert(trs); }
    if($('tbody', table).length > 0){
      $('tbody', table).append(trs);
    }else {
        $(table).append(trs);
    }
    return;
  }
  // ordenar filas
  function sortRows(rows,orden){
    function sortme(a,b,f){
      ff= {
        'integer': function (a,b){ return a-b; }
        ,
        'string': function (a,b){
              if (a==b) { return 0; }
              v= new Array(a,b);
              v.sort();
              return ((a==v[1])?1:-1);
            }
      };
      fff= ff[f];
      return fff(a,b);
    }
    if (orden.length){
      rows.sort(  function (a,b){
        j=0;
        for (o in orden){
          if (!orden[o][2]) { continue; }   // si el tipo es false, la columna no es ordenable
          if (orden[o][1]){
            j= sortme(b[orden[o][0]],a[orden[o][0]],orden[o][2]);
          } else {
            j= sortme(a[orden[o][0]],b[orden[o][0]],orden[o][2]);
          }  
          if (j) { break; }
        }
        return j;
      });
    }
    return rows;
  }

  // DESARROLLO DEL PLUGGING
// Devuelvo la lista de objetos jQuery
  return this.each( function(){
    var t = $(this);  // convierto this (elemento DOM) a jQuery
    var ar= opc.addRows;  // filas a agregar a la tabla
    n= ar.length;
    if (n>0) {
      // ordeno el array de filas
      ar= sortRows(ar,opc.sortList);
      // inserto en la tabla
      addTableRows(t,ar);
    }
  });

};

  // OPCIONES DEFAULTS
$.fn.airtable.defaults = {
        addRows:      [],
        sortList:     [],   //sin orden
        debug:        false
      };

})(jQuery);

jueves, 23 de septiembre de 2010

Tablas Ordenadas con JQuery

Estoy intentando programar un pluggin de jQuery que ordene una tabla por una columna o varias y que permita cambiar el tamaño de las columnas.
Por ahora voy a crear el archivo jquery.airtable.js para colocar el código del pluggin y agregarlo a mi html. El código esta encapsulado en (function($) {})(jQuery); para poder usar la función $ sin repetir la palabra jQuery todo el tiempo.
El método tendrá como opción la columna o columnas a ordenar, el tipo de orden ascendente o descendente y un array con las filas que se quieran agregar a la tabla antes de ordenarla. Este array serviría para llenar una tabla vacía por medio de un AJAX por ejemplo.
Cómo funciona?
El método es aplicado a un objeto jQuery tabla que se encarga de agregar a la tabla las filas mandadas como parametro en la opcion addRows previamente ordenadas. Luego bindea en las celdas header los metodos click para ordenar la tabla.
El usuario ejecuta un click en una celda header y la función ejecutada re-ordena la tabla con el nuevo orden. Un click entonces cambia la ordenación en esa columna y produce un re-ordenamiento de las filas.