【问题标题】:How can I implement a simple IRC client in Perl?如何在 Perl 中实现一个简单的 IRC 客户端?
【发布时间】:2011-04-19 14:22:54
【问题描述】:

我正在开发一个需要将 IRC 消息发送到内部 IRC 频道的工具。这不是一个持续运行的程序,而是一个偶尔会被调用的工具,并且需要能够在调用时通过几条消息通知通道。

我查看了Net::IRC,但它自 2004 年以来就已经死了。所以我查看了它列出的替代方案(Bot::BasicBotPOE::Component::IRC),但它们都需要在 POE 及其事件下运行环形。 Net::Async::IRC 之类的东西也会出现同样的问题,因为它需要在 IO::Async 事件循环中运行。

我不是在编写一个需要与任何东西交互的成熟机器人,我只是想登录到一个 irc 服务器,加入一个频道,发布一些快速消息然后离开。我不想为了做到这一点而重新编写整个程序以适应某些框架的事件循环。

那么,对于不会让我重写整个应用程序的简单 IRC 客户端库有什么建议吗?

【问题讨论】:

  • POE 可能有点重量级,但是 AnyEvent::IRC 应该可以完全在函数的上下文中运行。不确定 IO::Async 是否能够做到这一点

标签: perl cpan irc


【解决方案1】:

使用 MkV 的答案,我能够创建一个有效的命令行脚本:

#!/usr/bin/perl

use strict;
use warnings;
use AnyEvent;
use AnyEvent::IRC::Client;
use Data::Dumper ();
use Getopt::Long;

my %opt = (
    channel => '#ircmsgtest',
    nick    => "ircmsg$$",
    port    => 6667,
    server  => 'irc.freenode.net',
    verbose => undef,
);

GetOptions(\%opt,'channel','nick', 'port', 'server', 'verbose|v');
my $message = shift() || "test message @{[ scalar localtime() ]}";
if ($opt{verbose}) {
    warn "message is: '$message'";
    warn Data::Dumper->Dump([\%opt], [qw(*opt)]);
}

my $c = AnyEvent->condvar;
my $con = AnyEvent::IRC::Client->new;

$con->reg_cb(
    join => sub {
        my ($con, $nick, $channel, $is_myself) = @_; 
        if ($is_myself && $channel eq $opt{channel}) {
            $con->send_chan($channel, PRIVMSG => $channel, $message);
            $c->send;
        }
    }
);

$con->connect($opt{server}, $opt{port}, { nick => $opt{nick} } );
$con->send_srv(JOIN => $opt{channel});
$c->wait;
$con->disconnect;

它连接、发送消息,然后断开连接,非常适合我的需求。

【讨论】:

    【解决方案2】:

    请注意,实际上没有什么可以阻止您使用 PoCo-IRC 或 BasicBot。是的,它们在 POE 下运行,但 POE必须 控制您的整个应用程序。

    如果您只是连接、在 IRC 上做一些事情、断开连接和做其他事情,您可以确保 IRC 会话在完成后自行销毁——当没有剩余会话时,POE::Kernel->run 将将控制权交还给您的程序。

    如果您正在处理寿命较长的连接,但仍想扭转控制,POE 提供了run_one_timeslicerun_while 方法,让您可以很好地控制 POE 运行的时间和位置。当然,您必须安排它运行的频率至少足以响应任何服务器 PING 并防止套接字缓冲区被填满。

    实际上,Net::IRC 在使用自己的事件循环接管您的应用程序时做的事情完全相同——除了它不像 POE 或 AnyEvent 那样命名——它只是“Net::IRC 事件循环”。这并不意味着您需要完全重写才能使用 Net::IRC,也不意味着您需要完全重写才能使用 POE :)

    【讨论】:

      【解决方案3】:

      使用AnyEvent::IRC::Client,当它使用AnyEvent“事件循环”时,你不必重写你的应用程序来使用它,你可以这样做:

      use AnyEvent;
      use AnyEvent::IRC::Client;
      

      与模块的其余部分一起使用;和类似的东西

      sub do_irc {
          my $log_chan = '#foobar';
          my $timer; 
          my $c = AnyEvent->condvar;
          my $con = new AnyEvent::IRC::Client;
      
          $con->reg_cb( join => sub {
              my ($con, $nick, $channel, $is_myself) = @_; 
              if ($is_myself && $channel eq $log_chan) {
                 $con->send_chan( $channel, PRIVMSG => ($channel, 'my information') );
                 $timer = AnyEvent->timer ( 
                      after => 1,
                      cb => sub {
                          undef $timer;
                          $con->disconnect('done');
                      });
              }
          });
      
          $con->connect($irc_serv, 6667, { nick => $your_nick } );
          $con->send_srv( JOIN => ($log_chan) );
          $c->wait;
          $con->disconnect;
      
      }
      

      要进行连接,它只会在完成后返回(为简洁起见,省略了错误处理)。您的应用中的其他任何内容都不需要使用 AnyEvent。

      【讨论】:

      • 这与我正在寻找的内容很接近,只是我想避免在每条消息上都连接和断开连接。每次运行此工具可能需要几分钟,我想在过程中的各个点发送消息,例如进度更新。有什么方法可以在不重写以使用事件循环的情况下做到这一点?
      • 问题在于,如果您不及时回复 IRC PING 请求,IRC 服务器将断开您的连接,这由事件循环处理。您可以在代码中添加对 AnyEvent::one_event 的调用,但这会阻塞,直到您获得传入的 IRC 数据(或计时器到期)。
      • 我认为这段代码缺少一些东西。似乎$c->wait(不应该是$c->recv?)调用需要相应的$c->send调用,否则它将永远挂起
      • 这样我的主程序就不会阻塞必须为每条消息连接/断开连接,我决定使用多进程分支,父进程进行它的工作,但通过管道发送消息到使用 AnyEvent::IRC::Client 将消息发送到 IRC 通道的子级。感谢您对此的启发。
      【解决方案4】:

      Net::IRC 仍然完全可用,如果您想做的只是像您描述的那样简单,我认为它就可以了。

      也就是说,习惯 POE 并不是一个坏主意,尤其是 IRC 机器人的功能会随着时间的推移而不断增长 :)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-09-06
        • 2012-03-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-07-10
        • 1970-01-01
        相关资源
        最近更新 更多