POEでIRC bot
Yokohama.pmで、kanさんの発表を聞いて、MooseX::POEはなかなか良さそうだと思い、少し使ってみようと思っていたのですが、そもそもPOEのことを全然わかってないので、POE::Component::IRCを使って簡単なbotを作ってみました。
http://github.com/dann/yaircbot/tree/master
bot用のコマンドは、Shell::Amazon::S3で使ったCommand拡張の仕組みを使ったので、Commandは簡単に拡張できるようになっています。YAIRCBot::Command以下にファイルを置けば、Commandが追加できるようになっています。
さて、POEを触ってわかってことですが、
- Componentはeventをhookするだけで実装できるようになっている。定型処理はcomponent側で実装されている
- 引数はPOE::Sugar::Argsで。生で扱うと引数がとても汚くなる
- $poe->kernel->postなどと全体的に処理が長くなる
- デバッグは、以下のようにしてKernelのデバッグを出しておくと、少しやりやすい
sub POE::Kernel::ASSERT_DEFAULT () {1} sub POE::Kernel::TRACE_DEFAULT () {1}
- POE::Componenet::IRCをspwanしてから、POE::Kernel->runというのが定型処理
大体処理の流れはわかったので、次はMooseX::POEを使って、Mooooooooooooose化して遊んでみようかなと思ってます。今はお遊びスクリプトですが、最終的にはIRC botを簡単に作る仕組みをMooseベースで作ってみたいかなと。
起動スクリプト
#!/usr/bin/env perl use strict; use warnings; use FindBin::libs; use YAIRCBot; my $nick = 'commandbot'; my $server = 'localhost'; my $port = 6667; my $bot = YAIRCBot->new( nick => $nick, server => $server, port => $port ); $bot->run; __END__
main class
package YAIRCBot; use Moose; use POE::Component::IRC; use POE::Session; use POE::Kernel; use YAIRCBot::Component::IRCClient; use YAIRCBot::CommandDispatcher; our $VERSION = '0.01'; has 'nick' => ( isa => 'Str', is => 'rw', ); has 'server' => ( isa => 'Str', is => 'rw', ); has 'port' => ( isa => 'Int', is => 'rw', ); sub run { my $self = shift; POE::Component::IRC->spawn( alias => 'bot', nick => $self->nick, server => $self->server, port => $self->port, ); POE::Session->create( inline_states => { _start => \&YAIRCBot::Component::IRCClient::irc_start, irc_001 => \&YAIRCBot::Component::IRCClient::irc_001, irc_public => \&YAIRCBot::Component::IRCClient::irc_public, irc_error => \&YAIRCBot::Component::IRCClient::irc_reconnect, } ); POE::Kernel->sig( INT => sub { POE::Kernel->stop } ); POE::Kernel->run; } 1; __END__
Component
package YAIRCBot::Component::IRCClient; use strict; use POE qw( Sugar::Args Component::IRC ); use YAIRCBot::CommandDispatcher; use Perl6::Say; #sub POE::Kernel::ASSERT_DEFAULT () {1} #sub POE::Kernel::TRACE_DEFAULT () {0} sub parse_input { my ($line) = @_; my @tokens = split( /\s/, $line ); return unless @tokens >= 1; my $command = shift @tokens; return ( $command, \@tokens ); } sub execute_command { my ( $command, $args ) = @_; my $dispatcher = YAIRCBot::CommandDispatcher->new; my $result = eval { $dispatcher->dispatch( $command, $args ); }; return $result; } sub irc_start { my $poe = sweet_args; $poe->kernel->post( bot => register => 'all' ); $poe->kernel->post( bot => connect => {} ); } sub irc_001 { my $poe = sweet_args; $poe->kernel->post( $poe->sender => join => '#test' ); } sub irc_public { my $poe = sweet_args; my $who = $poe->args->[0]; my $where = $poe->args->[1]; my $what = $poe->args->[2]; my $nick = ( split /!/, $who )[0]; my $channel_name = $where->[0]; my $self = $_[0]->[OBJECT]; if ( $what =~ /\!(.*)/ ) { my $command_line = $1; my ( $command, $tokens ) = parse_input($command_line); my $result = execute_command( $command, $tokens ); $poe->kernel->post( $poe->sender => notice => $channel_name => $result ); } } sub irc_connected { say 'irc_connected'; } sub irc_error { my $p = sweet_args; my ($error) = @{ $p->args }; say 'irc_error:' . $error; } sub irc_reconnect { say 'irc_reconnect'; } 1;