19 septiembre 2007

Creando un cliente de chat IRC con Indy (y III)

Después de haber visto la parte básica del componente IdIRC pasemos a ver algunas cosas que hay que controlar en un cliente de chat.

QUIEN SALE Y QUIEN ENTRA A UN CANAL

Mientras permanezcamos en un canal entrarán y saldrán usuarios constantemente, lo cual nos obliga a refrescar la lista de usuarios y notificar los cambios en la misma ventana del canal:

Ha entrado el usuario lola33
Ha salido el usuario carlos21

Para controlar los usuarios que entran a un canal utilizaremos el evento OnJoin:

procedure TFormulario.IRCJoin( Sender: TObject; AUser: TIdIRCUser; AChannel: TIdIRCChannel );
begin
if Assigned( AChannel ) and Assigned( AUser ) then
if AChannel.Name = CanalActual.Text then
begin
Canal.Lines.Add( 'Ha entrado el usuario ' + AUser.Nick );

if Usuarios.Items.IndexOf( AUser.Nick ) = -1 then
Usuarios.Items.Add( AUser.Nick );
end;
end;

Después de informar en el canal actual que ha aparecido un nuevo usuario también lo hemos dado de alta en la lista de usuarios, controlando que no se repitan, ya que a veces suele coincidir que se ejecuta el comando NAMES del servidor y a la vez entra un usuario nuevo.

Y cuando un usuario abandona el canal entonces utilizamos el evento OnPart:

procedure TFormulario.IRCPart( Sender: TObject; AUser: TIdIRCUser; AChannel: TIdIRCChannel );
var iUsuario: Integer;
begin
if Assigned( AChannel ) and Assigned( AUser ) then
if AChannel.Name = CanalActual.Text then
begin
Canal.Lines.Add( 'Ha salido el usuario ' + AUser.Nick );
iUsuario := Usuarios.Items.IndexOf( AUser.Nick );

if iUsuario > -1 then
Usuarios.Items.Delete( iUsuario );
end;
end;

La única dificultad que hay que controlar es que si tenemos varias ventanas (una por canal) hay que llevar las notificaciones a la ventana correspondiente.

Los canales de chat tienen dos clases de usuarios: operadores y usuarios normales. Los operadores son usuarios que tienen permisos especiales: pueden hacer operadores a usuarios normales o echar a un usuario de un canal si esta incumpliendo las normas del mismo.

Cuando un operador echa fuera a un usuario entonces nuestro componente IdIRC provoca el evento OnKick:

procedure TFormulario.IRCKick( Sender: TObject; AUser, AVictim: TIdIRCUser; AChannel: TIdIRCChannel );
var iUsuario: Integer;
begin
if Assigned( AChannel ) and Assigned( AUser ) then
if AChannel.Name = CanalActual.Text then
begin
Canal.Lines.Add( 'El usuario ' + Auser.Nick + ' ha expulsado a usuario ' + AVictim.Nick );
iUsuario := Usuarios.Items.IndexOf( AUser.Nick );

if iUsuario > -1 then
Usuarios.Items.Delete( iUsuario );
end;
end;

Este evento nos informa del nombre del operador, el de la victima y del canal de donde ha sido expulsado.

Y por último tenemos el problema de que un usuario puede cambiarse el apodo (nick) en cualquier momento. Para ello utilizaremos el evento OnNickChange para notificarlo:

procedure TFormulario.IRCNickChange( Sender: TObject; AUser: TIdIRCUser; ANewNick: String );
var iUsuario: Integer;
begin
Canal.Lines.Add( 'El usuario ' + AUser.Nick + ' ahora se llama ' + ANewNick );
iUsuario := Usuarios.Items.IndexOf( AUser.Nick );

if iUsuario > -1 then
Usuarios.Items[iUsuario] := ANewNick;
end;

Aparte de notificarlo le hemos cambiado el nombre en nuestra lista de usuarios.

INTERCEPTANDO LOS MENSAJES DEL SISTEMA

A pesar de todos los eventos de los que dispone el componente de la clase TIdIRC también podemos interceptar a pelo los mensajes del sistema a través del evento OnSystem, el cual nos dice el usuario, el código de comando y el contenido del mensaje del sistema. Por ejemplo estos son algunos códigos de comando del servidor:

353 -> Comienzo del comando NAMES
366 -> Fin del comando NAMES
376 -> Mensaje del día
etc.

Para más información hay que ver el documento RFC perteneciente al protocolo IRC que mencione en el artículo anterior. Por ejemplo, para averiguar los datos sobre un usuario en concreto hay que mandarle al servidor el comando:

WHOIS maria

Y el servidor devolverá lo siguiente:

319 - WHOIS - maria is on #amistades
312 - WHOIS - maria is using jupiter2.irc-hispano.org Servidor IRC de LLeida Networks
318 - WHOIS - maria :End of /WHOIS list

Nos esta diciendo que maria está en el canal #amistades y que está conectada utilizando el servidor jupiter2. Si estuviera conectada a más canales también lo diría (así sabemos también sus otras aficiones).

¿Cómo nos comemos todo eso? Pues supongamos que si yo hago doble clic sobre un usario quiero que me devuelva información sobre el mismo en un campo Memo cuyo nombre es DatosUsuario. Lo primero sería procesar el evento OnDblClick en la lista de usuarios:

procedure TFormulario.UsuariosDblClick( Sender: TObject );
begin
if Usuarios.ItemIndex > -1 then
IRC.Raw( 'WHOIS ' + Usuarios.Items[Usuarios.ItemIndex] );
end;

Y ahora en el evento OnSystem del componente IdIRC esperamos a que nos llegue la información:

procedure TFormulario.IRCSystem( Sender: TObject; AUser: TIdIRCUser; ACmdCode: Integer; ACommand, AContent: string );
begin
case ACmdCode of
312, 318, 319: DatosUsuario.Lines.Add( AContent );
end;
end;

Así, estudiando un poco el protocolo IRC y sabiendo algunos comandos podemos hacer más cosas de las que permite el componente IdIRC.

ENVIO Y RECEPCION DE ARCHIVOS POR ACCESO DIRECTO

El protocolo IRC permite establecer el envío y recepción de archivos con conexión punto a punto entre la IP del remitente y la IP del receptor. Esto ya no se suele utilizar hoy en día por las siguiente razones:

- Debido a que casi todas las redes locales están detrás de un router se hace necesario abrir y redireccionar puertos en el mismo, cosa que no todo el mundo sabe hacer.

- En cuanto intentemos establecer conexión por puertos que no sean los estandar los cortafuegos saltarán como liebres pidiendo permiso para conectar. Los novatos siempre dirán NO por si acaso.

- Hay robots automáticos programados en MIRC mediante scripts que no paran de realizar ataques continuamente a conexiones DCC.

Por ello no voy a comentar aquí como utilizarlo (para ello tenemos rapidshare, megaupload y demás hierbas que superan con creces este tipo de conexiones y además son más seguros).

Con esto finalizamos lo más importante del componente IdIRC de la paleta de componentes Indy.

Pruebas realizadas en Delphi 7.

5 comentarios:

Anónimo dijo...

Felicitaciones por Su Blog Amigo lo tengo en mi lista de favoritos y hasta he pensado ponerlo como pagina de inicio jeje ....

¿ Cuando haras un Tuto para un servidor IRC? aunque con este ejemplo y la RFC tengo mas que suficiente para emprender un pryecto ...

Saludos ...

Administrador dijo...

Hace tiempo intenté hacer uno, pero como la documentación de los componentes Indy es tan escasa no hubo manera de que funcionara y eso que utilizaba MIRC como cliente.

Una cosa es tener las especificaciones con los RFC y otra hacer que los Indy te hagan caso de lo que le mandas (se cuelgan mucho).

¿Habrá por ahí alguna alternativa a los Indy?

Anónimo dijo...

TIdSocketMaster esta a mitad de camino entre las indy y las apis winsock, yo he estado haciendo pruebas con cliente y servidor en las Indy y poco a poco le he agarrado el hilo ....

Administrador dijo...

Lo malo que tienen los componentes Indy es que aparte de no tener prácticamente documentación, los muy cabrones cambian los parámetros de los métodos en cada versión, creando incompatibilidades hacia atrás a lo bestia.

Por ejemplo, si pasas de Delphi 2007 a Delphi 2009 o Delphi 2010, aunque sigan siendo los Indy 10, han cambiado los eventos con otros parámetros. También he probado una implementación no oficial llamada Kelvin Indy que están más actualizados y corregidos que los Indy originales pero el soporte el nulo.

Por ponerte un ejemplo, en los componentes Indy que trae Delphi 2007 no funciona el evento OnFTPWork y tube que instalar los Kelvin para solucionar dicho problema.

Embarcadero debe revisar tanto el IDE como los componentes que vende. A ver si se creen que pueden cobrar 3.000 pavos por vender chatarra. Lo que estoy viendo es que de aquí a cinco años, Lazarus se los va a comer con patatas.

Saludos.

Anónimo dijo...

Hola me gusta mucho tu blog eh aprendido mucho gracias a usted. Si le es posible podria enviarme el codigo fuente de un cliente irc en indy. Pero por favor que sea lo mas sencillo posible asi como su tutoriar. Gracias de antemano. Mi correo es luigymax@hotmail.com / luigymax@gmail.com

Publicidad