Chamet: código fuente del blogchat

¿Qué es Chamet?

Son muchas las personas que me han pedido que publique el blogchat (bautizado con el nombre de Chamet, ya que en un principio lo iba a desarrollar en COMET y no en AJAX, aunque al final se quedó en AJAX por motivos que ya explicaré en otro artículo). Así que después de retocarlo un poco, aunque no me ha quedado como a mí me gustaría, voy a publicar el código (aunque siempre ha estado disponible extrayéndolo de la web).

Chamet es una idea que me surgió hace algún tiempo, que consistía en poner un chat en mi blog, con la peculiaridad de que en lugar de salas o canales, los usuarios se dividían en artículos del blog. Es decir, un chat adaptado a la filosofía blog, un blogchat.

La demo de este código la podéis encontrar aquí, en este mismo artículo, un poco más abajo…

chametenaccion_400 

Modelo de datos de Chamet 

Antes de nada, veamos el modelo de datos necesario para usar Chamet. Por un lado, debemos tener una tabla con los artículos. En WordPress, por ejemplo, es la tabla wp_posts, y el id del artículo es el campo ID (al que llamaremos de ahora en adelante nid, por "news id"). Y por otro lado, deberemos crear nuestra tabla para guardar los mensajes de chat, que llamaremos wp_chat siguiendo el ejemplo de WordPress:

CREATE TABLE `wp_chat` (
  `chid` int(10) unsigned NOT NULL auto_increment,
  `nid` int(10) unsigned NOT NULL default '0',
  `created` int(10) unsigned NOT NULL default '0',
  `ip` int(10) unsigned NOT NULL default '0',
  `name` varchar(150) NOT NULL default '',
  `message` text NOT NULL,
  PRIMARY KEY  (`chid`)
);

Veamos una ligera descripción de los campos:

  • chid: id del mensaje de chat
  • nid: id del artículo (recordemos, en WordPress sería wp_posts.ID)
  • created: fecha y hora en formato timestamp del mensaje
  • ip: IP en formato long, que en PHP se obtiene con ip2long($_SERVER['REMOTE_ADDR'])
  • name: nick de quien envía el mensaje
  • message: mensaje

Código fuente de Chamet 

Requerimientos

Chamet usa AJAX y JSON. En el propio chamet-0.1.js implemento la función de llamadas AJAX, aunque no la he testeado aún. Este blog funciona usando la librería Prototype (pendiente de migrar todo a jQuery) aunque también con jQuery, que tampoco ha sido testeada.

La librería que sí es necesaria es JSON, tanto la de Javascript como la de PHP.

chamet-0.1.js

El principal fichero es chamet-0.1.js, el corazón del blogchat. Es una clase Javascript que debe ser instanciada al visualizar un artículo del blog, de la siguiente forma:

var chamet = new Chamet();

Después de instanciar la clase, es necesario modificar algunos atributos del objeto:

  • chamet.panel_id: id del elemento de la página donde se insertán los mensajes recibidos, normalmente un <DIV>. Más adelante veremos qué estilos debe tener ese div contenedor para que los mensajes se vean correctamente como si fuera una ventana de chat.
  • chamet.url_load: url del script (PHP en nuestro caso) que devolverá los nuevos mensajes. Más adelante veremos cómo hacer este script.
  • chamet.url_send: url del script que recibe los envíos de mensajes.

Después de definir los atributos, inicializaríamos Chamet con el método init(nid), donde nid es el id del artículo que estemos visualizando. Nuestro javascript continuaría así:

chamet.panel_id = 'chamet-container';
chamet.url_load = 'chamet_load.php';
chamet.url_send = 'chamet_send.php';
chamet.init(nid);

Con esto, hemos configurado Chamet para que visualice los mensajes en el elemento con id 'chamet-container', que llame mediante AJAX a chamet_load.php para obtener nuevos mensajes, y a chamet_send.php para enviar un mensaje al chat.

Desconozco cómo obtener el id de artículo en WordPress, pero tras echarle un vistazo, creo que es con the_ID(), dentro de la plantilla /wp-content/themes/default/page.php. Por lo que sería algo así como poner el Javascript dentro de esa plantilla:

chamet.init(<?=the_ID()?>);

HTML de Chamet

El HTML de Chamet debe contener básicamente dos cosas: el contenedor de los mensajes, y el formulario de envío de mensajes. El contenedor, como ya cité más arriba, podría ser un DIV con un id, el id que pondremos en chamet.panel_id. Siguiendo con el ejemplo de WordPress, habría que poner en la plantilla page.php lo siguiente:

<div id="chamet-container"></div>
<form onsubmit="chamet.form(this); return false;">
    <span>nick</span> <input type="text" name="chat_name" />
    <span>mensaje</span> <input type="text" name="chat_msg" onkeypress="chamet.update()" />
    <input type="submit" value="Enviar" />
</form>

Y a continuación, el código Javascript descrito arriba:

<script type="text/javascript" src="/js/chamet-0.1.js"></script>
<script type="text/javascript">
var chamet = new Chamet();
chamet.panel_id = 'chamet-container';
chamet.url_load = 'chamet_load.php';
chamet.url_send = 'chamet_send.php';
chamet.init(<?=the_ID()?>);
</script>

Es importante que el campo del formulario que contenga el nick se llame chat_name, y el que contenga el mensaje se llame chat_msg. Sé que debería haberlo hecho configurable, pero esto es aún una primera versión muy beta. En próximas versiones será mucho más configurable y fácil de implementar.

El evento onsubmit del formulario debe llamar a chamet.form(this), que es el método que se encargará de enviar el mensaje mediante AJAX al script chamet_send.php con los datos del formulario.

Podemos ver también el evento onkeypress del campo chat_msg. Esto es para hacer que Chamet actualice más rápidamente el panel de mensajes cuando un usuario empiece a escribir algo. Chamet decrementa progresivamente el tiempo de actualización cada vez que envía una petición para obtener nuevos mensajes (a chamet_load.php) y no obtiene ninguno nuevo.

CSS para el contenedor de mensajes

Crearemos una sencilla regla CSS para el contenedor de mensajes. Lo más importante es que debemos poner un height concreto, y el atributo overflow a auto, con lo que conseguiremos que aparezca una caja con scroll. Chamet se encargará de que aparezca el scroll siempre abajo, con el último mensaje.

#chamet-container {
    border: 1px solid #a00;
    height: 250px;
    overflow: auto;
}

chamet_load.php

Este script es el encargado de devolver en formato JSON los últimos mensajes recibidos en el servidor. Es llamado por Chamet cada chamet.update_interval milisegundos, añadiendo los nuevos mensajes al panel (el DIV que creamos antes en la plantilla). Los parámetros POST que recibe son:

  • chid: El id del mensaje (recordemos, tabla wp_chat, campo chid) a partir del cual recibir nuevos mensajes. Si su valor es null, el script deberá devolver los últimos, digamos, 50 mensajes (no recomiendo que sean más). Si no es null, entonces se devolverán los mensajes con un valor más alto.

Debe devolver en formato JSON un array de objetos (un objeto por mensaje), con los siguientes campos:

  • chid: id del mensaje
  • name: nick del usuario que envió el mensaje
  • message: el mensaje
  • created: texto con la fecha y hora del mensaje
  • title: título del artículo donde escribió el mensaje
  • url: url al artículo donde se escribió el mensaje
  • nid: id del artículo (en WordPress, wp_posts.ID)

El script, por lo tanto, debería hacer una consulta SQL a la base de datos, tal que así:

<?php
$chid = intval($_POST['chid']);
$sql = "
    SELECT
        chat.chid, chat.name, chat.message, chat.created
        posts.post_title, posts.ID
    FROM wp_chat as chat
    JOIN wp_posts as posts ON (posts.ID = chat.nid)
";
if ($chid) {
    $sql .= " WHERE chat.chid > $chid ";
}
$sql .= " ORDER BY chat.chid DESC LIMIT 50";

Y almacenar en una estructura los datos a devolver:

<?php
$msgs = array();
$data = $db->fetchAll($sql);
foreach ($data as $reg) {
    $msgs[] = array(
       'chid'      => $reg['chid'],
       'name'   => $reg['name'],
       'message'=>$reg['message'],
       'created'   => date("d/m/Y H:i",$reg['created']),
       'title'      => $reg['post_title'],
       'url'      => 'http://miblog.com/?p=' . $reg['ID'],
       'nid'      => $reg['ID']
    );
}

Devolviendo la estructura $msgs mediante JSON:

<?php
$json = new Service_JSON();
headers('Content-Type: text/x-json');
die($json->encode(array_reverse($msgs)));

Sirva el código como mero ejemplo, que ni siquiera me he molestado a probar. Mi blog usa un sistema MVC, el Zend Framework, y sería mucho más largo y complejo explicarlo tal y como lo tengo yo, que con este pequeño ejemplo.

chamet_send.php

Este script es llamado por Chamet cuando el usuario envía el formulario del que hablamos más arriba. Debe tener como entrada las siguientes variables POST:

  • name: nick del usuario
  • m: mensaje de chat
  • nid: id del artículo donde envía el chat

Y como salida, una simple estructura con dos campos:

  • ok: true si el mensaje se almacenó correctamente en el servidor, o false si hubo algún error
  • msg: el mensaje de error, si ok == false

Y volvamos a poner un ejemplo:

<?php
$name = trim(addsashes(striptags($_POST['name'])));
$message = trim(addsashes(striptags($_POST['m'])));
$nid = intval($_POST['nid']);
$ok = true;
$msg = '';

if (empty($name)) {
    $ok = false;
    $msg = 'Debe escribir un nick';
}
if (empty($message)) {
    $ok = false;
    $msg = 'Debe escribir un mensaje';
}
if (!$nid) {
    $ok = false;
    $msg = 'Debe escribir un mensaje desde un artículo';
}

if ($ok) {
    $ip = ip2long($_SERVER['REMOTE_ADDR']);
    $created = time();
    $sql = "
        INSERT INTO wp_chat (nid,ip,name,message,created)
        VALUES ($nid, $ip, '$name', '$message', $created)
    ";
    $db->query($sql);
}

$json = new Service_JSON();
header('Content-Type: text/x-json');
die($json->encode(array(
    'ok'   => $ok,
    'msg'=> $msg
)));

Chamet es GPL

Libero Chamet bajo la Licencia Pública GNU (GPL), lo que a grandes rasgos quiere decir que cualquiera puede usar Chamet a su antojo, exigiendo únicamente que si alguien crea otra versión a partir de Chamet, que la libere también bajo la GPL.

Así mismo, espero que los que probéis Chamet me enviéis vuestros feedbacks y/o parches para mejorar el software. Para lo cual disponéis de los comentarios en este blog y de mi email (a pié de página), así como para hacer cualquier consulta o sugerencia.

5 comentarios

  1. Juan Benjumea

    Hola Javier, me pregunto si puedes compartir el codigo conmigo, veo que el post es de hace mas de una año y ya no lo encuentro en la url que pusite

Escribe un comentario

*