[ 永远的UNIX::UNIX技术资料的宝库 ]

首页 > 编程技术 > Perl > 正文

用Perl写你自己的IRC Robot

作者:cnhackTNT bbs.s8s8.net (2005-04-06 13:24:40)

 
以前写的,代码中的rss源有些不可用了,你可以自己找喜欢的rss源,现在很多站点都提供rss源的。
----------------------------------------------------------------

+=用Perl写你自己的IRC Robot=+
---------------------------------
http://www.s8s8.net
http://www.perlchina.org
Author: cnhackTNT
Date: 3/6/2004 2:41 PM
---------------------------------


我想这应该是个有趣的事情,既然是有趣的事情,那么应该把它拿出来让更多的人从中找到乐趣。Perl之所以有如此多的支持者,也正是因为这种共享将乐趣最大限度的扩大化了。所以,也希望当你碰到有趣的事情的时候,能把它告诉我们。:)

$Chapter_1: 了解IRC和IRC Robot、RSS

$sec_1: IRC和IRC Robot

IRC是Internet Relay Chat的缩写,通过IRC协议,大家可以连到一台或者多台IRC服务器上进行聊天,他给你的直观印象就是和传统的Web聊天很像,但是IRC的功能多很多。
(国内有很多的IRC服务器,比如irc.sunnet.org,irc.pchome.org。我们的perlchina也搭建了自己的IRC服务器irc.perlchina.org
你可以下载mirc这个IRC聊天工具来登陆irc服务器,然后便可以选择自己感兴趣的频道加入讨论。)
IRC Robot简写为IRCbot。顾名思义,它是一个由程序编写的IRC机器人,它可以像你一样登陆事先指定的IRC服务器,然后等待命令或者干其他的预先设定好的事情。你可以通过IRC给他下达指令,比如询问它今天的天气情况啊,要它看看自己管理的远程系统是不是正常啊,闲暇时可以让他给你挖掘点新闻过来看看,甚至有些黑客编写了一些通过IRC来进行控制的木马。

其实,这都是IRCbot。

$sec_2:RSS

RSS?什么是RSS?RSS=Really Simple Syndication 或者 RDF Site Summary 或者Rich Site Summary。
RSS是随着博客的兴起而逐渐兴起的,它是站点用来和其他站点之间共享内容的简易方式,通常我们把它称作“聚合内容”,你可以简单的把它当作是一个格式化了的站点内容列表。
有了RSS我们可以轻松的将目的站点的内容抓过来。由于RSS是一个标准的XML文档,所以我们可以用Perl的XML::Parser模块来对它进行解析,但在这个例子中,我们不直接使用XML::Parser,而是用一个专门针对RSS的模块XML::RSS,你可以从CPAN找到它,下载它,然后安装它。

$Chapter_2: 准备工作

(你肯定已经安装了perl对吧?)

首先我们要安装XML::RSS模块。
如果你在UNIX或者Linux系统下,可以通过perl –MCPAN –e “install XML::RSS”命令来安装,如果你在Windows下,请先到微软或者perlchina的论坛上找到nmake.exe,下载它放到一个目录中,然后在第一次通过perl –MCPAN –e shell命令进行初始化的时候指定nmake.exe的路径。(关于安装Perl模块,如果有不懂的可以查看perl的文档how to install module from cpan或者到perlchina的论坛上询问)
接下来,我们需要下载一个码表文件gb2312.enc(53k),你可以从下面的地址的到它:
http://eaa13.dns0755.net/~atzone/source/gb2312.enc

下载后,将它放到‘perlpath(你的perl安装目录)/lib/XML/Parser/Encoding’目录下,这样你的XML::Parser就可以分析包含中文的XML文档了(XML::RSS通过XML::Parser来对RSS文档进行解析)。
现在,我们从如下地址下载Net::IRC模块:

http://ppm.activestate.com/PPMPackages/zip...ows/Net-IRC.zip

解压后用Activestate的ppm进行安装。
OK,准备完毕。

$Chapter_3: 编写IRCbot程序

我们要通过这个IRCbot来攫取一些站点上的新闻标题,比如我向聊天室或者这个ircbot发送”grab slashdot”命令的时候它要给我攫取slashdot的新闻标题。
当然还有其他一些命令,比如当我发送的消息包含这个ircbot在irc中的昵称的时候,它要
主动和我联系,并说“Do u want read news or blogs?type 'list\ for available rss seeds.”。
当我输入list命令,它要返回给我一个事先定义的rss文档列表
然后我输入grab <rss文档名>,它要给我抓取新闻.
等等.


代码

#!/usr/bin/perl
#perlchina.org
#cnhackTNT[at]4red.org

use strict;
use Net::IRC;                #加载Net::IRC模块
use XML::RSS;             #加载XML::RSS模块
use LWP::Simple;          #加载LWP::Simple,我们将通过它来获取网络站点的rss文档
use Encode;                  #加载Encode模块,由它提供对中文编码的转换

$SIG{INT} = \&quit;    #当收到中断信号(比如当你按下Ctrl+C时)的时候执行子程序&quit

#=====================================================================
#     用一个哈希表%rss保存几个网站的rss文档的url
#=====================================================================

my %rss = (
   slashdot        => 'http://slashdot.org/index.rss',
   googleblog      => 'http://google.blogspace.com/index.xml',
   freshmeat       => 'http://freshmeat.net/backend/fm.rdf',
   blogcn          => 'http://www.blogcn.com/user_rss.asp',
   cnblog          => 'http://www.cnblog.org/blog/cache/cnblog.xml',
   AmericanInChina   => 'http://www.blogcn.com/rss.asp?uid=aradosh',
   AnorwegiansLife   => 'http://www.blogcn.com/rss.asp?uid=houshuang'
);
#=====================================================================
#     定义一个哈希表%init来保存将要登陆的IRC的服务器,频道,端口,昵称,
#     IRC名称等信息。
#     在运行程序的时候你可以传递两个参数给程序,这两个参数分别是Server和
#     Channel。
#     假设这个程序的名字是ircbot.pl,那么你可以这样运行:
#            perl ircbot.pl irc.perlchina.org #perlchina
#     这样便可以登陆irc.perlchina.org并加入#perlchina频道,如果你直接运行:
#            perl ircbot.pl
#     那么它回按照%init中定义的默认值连接到css.sunnet.org并加入#rssGrabber.pm
#     频道。
#=====================================================================

my %init = (
   Nick    => 'Grabber',
   Server  => shift || 'css.sunnet.org',
   Port    => 6667,
   Ircname => 'I am a rss grabber .~',
   Channel => shift || '#rssGrabber.pm'
);
my $irc  = new Net::IRC;           #创建Net::IRC对象

#=====================================================================
#     以下通过调用newconn方法,根据初始哈希表%init来创建一个新的irc连接,并将返
#     回的对象给$conn,注意:可以创建多个irc连接,连接创建好后调用start方法来
#     执行这个连接。
#     然后分别调用add_global_handler和add_handler来设定触发事件
#     add_global_handler和add_handler的区别在于,前者影响所有的连接(如果你有创建多
#     个连接的话,后者只影响由newconn方法创建的并由初始哈希表%init定义的单个连接。
#     ‘376’,’disconnect’,’public’,’msg’等都是触发信号的数字代码或者名字,你可以从
#     Net::IRC::Event模块的源代码中读到那些数字代码所代表的意思,也可以
#     perldoc Net::IRC::Event来查看’disconnect’,’public’,’msg’分别代表什么
#     这里
#     ’376’代表连接上irc服务器并执行完MOTD命令,然后触发&on_conn事件
#     ‘disconnect’代表断开irc连接,然后触发&on_disconnect事件
#     ‘public’代表当有人在公共聊天窗口发送消息,然后触发&on_line事件
#     ‘msg’代表当友人在私聊窗口给你消息,然后触发&on_line事件  
#=====================================================================

my $conn = $irc->newconn(%init);    
$conn->add_global_handler( '376',        \&on_conn );
$conn->add_global_handler( 'disconnect', \&on_disconnect );
$conn->add_handler( 'public', \&on_line );
$conn->add_handler( 'msg',    \&on_line );
$irc->start;

#=====================================================================
#     当连接上的时候,显示”Joining 频道”
#     然后在公共聊天窗口发送消息"Hi!I am the rss grabber.~"
#=====================================================================

sub on_conn {
   print "Joining $init{'Channel'}...\n";
   my $self = shift;
   $self->join("$init{'Channel'}");
   $self->privmsg( "$init{'Channel'}",
       "Hi!I am the rss grabber.~" );
}

#=====================================================================
#     当进入频道后就触发了on_line事件
#     默认数组@_保存了两个元素:一个是先前调用newconn方法返回的连接对象
#     一个是模块内部传过来的event对象,在这里event对象比较有用
#     通过$event->nick取得说话人的昵称,$self->nick则是你自己的昵称
#     $event->args则返回了说话的内容等元素。
#=====================================================================

sub on_line {
   my ( $self, $event ) = @_;
   my ( $nick, $mynick ) = ( $event->nick, $self->nick );
   my ($arg) = ( $event->args );

   print "<$nick> $arg\n";                       #打印某某说了什么
   if ( $arg =~ /grab\s+(\w+)/i ) {            #假如某人说“grab xxx”那么通过正则表达式取得
       my $key = $1;                                   #字符串xxx,这个就是rss文档名
       my $topic;

       if ( exists $rss{$key} ) {                     #假如事先有定义这个rss文档,就告诉我它正在
           $self->privmsg( $nick,          #抓取新闻,并输出抓到的新闻标题,否则报错
" 4 Plz wait for awhile,I am grabbing news from 12&shy; $rss{$key}!!"      
           );
           $topic = &grab($key);
           if ($topic) {
               $self->privmsg( $nick, "-" x 80 );
               for ( keys %$topic ) {
                   $self->privmsg( $nick,
                           encode( "euc-cn", $_ ) . ' 7| '     #中文编码转换
                         . encode( "euc-cn", $topic->{$_} ) );       #中文编码转换
                   sleep 2;
               }
               $self->privmsg( $nick, "-" x 80 );
               $self->privmsg( $nick, 'Done!' );
           }
           else {
               $self->privmsg( $nick, ' 4Empty?!mybe the server\'s problem' );
           }
       }
       else {
           $self->privmsg( $nick, ' 4I dont\'t have this server\'s rss url' );
       }
   }

   if ( $arg =~ m/$mynick/i ) {         #假如有人发的消息包含这个ircbot的昵称
       $self->privmsg( $nick,          #那么发送如下的发送消息给他
'Do u want read news or blogs?type \'list\' for available rss seeds.'
       );
   }

   if ( $arg =~ m/list/i ) {                 #处理list命令
       $self->privmsg( $nick, ' 4the present rss seeds includes:' );
       $self->privmsg( $nick, '-' x 80 );
       $self->privmsg( $nick, ' 12' . $_ ) for keys %rss;
       $self->privmsg( $nick, '-' x 80 );
       $self->privmsg( $nick, 'Done!' );
   }

if ( $arg =~ /go home/i ) {           #假如有人说go home,那么和所有人说BYEBYE
       $self->privmsg( $init{'Channel'}, 'BYE BYE~~' );       #并自动断开连接
       $self->quit;
       exit 0;
   }

}


#=====================================================================
#假如未连接上,或者被踢出,触发on_disconnect事件然后自动重连
#=====================================================================

sub on_disconnect {
   my ( $self, $event ) = @_;
   print "Disconnected from ", $event->from(), " (", ( $event->args() )[0],
     "). Attempting to reconnect...\n";
   $self->connect();
}

sub quit {
   print "Somebody force me exit:-(\n";
   exit 0;
}

#=====================================================================
#通过该子程序抓取指定的rss文档并提取其中的新闻标题
#=====================================================================
sub grab {
   my $key     = shift;
   my $topic   = {};
   my $o       = new XML::RSS;
   my $content = get( $rss{$key} )
     or return ( { Error => "Can\'t grab news from $rss{$key} !!" } );
   $o->parse($content);
   foreach my $item ( @{ $o->{'items'} } ) {
       next unless defined( $item->{'title'} ) && defined( $item->{'link'} );
       $topic->{ ' 4' . $item->{'title'} } = ' 12&shy;' . $item->{'link'};
   }
   return $topic;
}





结束语:

OK!还等什么?赶快去试试吧。

这个程序是很简单的,实现的功能也不多,你可以自己扩展它的功能,比如用它来管理自己的远程机器,通过RPC(远程过程调用)来更新自己的blog什么的。

这,取决于你的想象能力与动手能力了……

(哎 ~四方凳子坐得我的肩膀好痛,关于Perl的讨论请来www.perlchina.org或者www.s8s8.net的网络编程板块或者发封email 到mailto:ask-subscribe@perlchina.org加入我们的邮件列表,如果你有什么有趣的东西,别忘了告诉我们)

(http://www.fanqiang.com)

原文链接:http://bbs.s8s8.net/forums/index.php?showtopic=17782

 相关文章
中文man手册:dircolors - 设置‘ls

★  感谢所有的作者为我们学习技术知识提供了一条捷径  ★
www.fanqiang.com