package App::Mobirc::Plugin::Component::IRCClient; use strict; use MooseX::Plaggerize::Plugin; use POE; use POE::Sugar::Args; use POE::Component::IRC; use Encode; use Carp; use App::Mobirc::Model::Message; use App::Mobirc::Util; has ping_delay => ( is => 'ro', isa => 'Int', default => 30, ); has reconnect_delay => ( is => 'ro', isa => 'Int', default => 10, ); has incode => ( is => 'ro', isa => 'Str', default => 'UTF-8', ); has nick => ( is => 'ro', isa => 'Str', required => 1, ); has username => ( is => 'ro', isa => 'Str', default => 'mobirc user', ); has desc => ( is => 'ro', isa => 'Str', default => '', ); has server => ( is => 'ro', isa => 'Str', required => 1, ); has port => ( is => 'ro', isa => 'Int', required => 1, ); has password => ( is => 'ro', isa => 'Str', ); hook process_command => sub { my ( $self, $global_context, $command, $channel ) = @_; my $irc_incode = $self->incode; if ( $command && $channel->name =~ /^[#*%]/ ) { if ( $command =~ m{^/} ) { DEBUG "SENDING COMMAND"; $command =~ s!^/!!g; my @args = map { encode( $irc_incode, $_ ) } split /\s+/, $command; $poe_kernel->post( 'mobirc_irc', @args ); } else { DEBUG "NORMAL PRIVMSG"; $poe_kernel->post( 'mobirc_irc', privmsg => encode( $irc_incode, $channel->name ) => encode( $irc_incode, $command ) ); DEBUG "Sending command $command"; $channel->add_message( App::Mobirc::Model::Message->new( who => decode( $irc_incode, $poe_kernel->alias_resolve('irc_session') ->get_heap->{irc}->nick_name ), body => $command, class => 'public', ) ); } return true; } return false; }; hook 'run_component' => sub { my ( $self, $global_context ) = @_; DEBUG "initialize ircclient"; # irc component my $irc = POE::Component::IRC->spawn( Alias => 'mobirc_irc', Nick => $self->nick, Username => $self->username, Ircname => $self->desc, Server => $self->server, Port => $self->port, Password => $self->password, ); POE::Session->create( heap => { seen_traffic => false, disconnect_msg => true, config => $self, irc => $irc, global_context => $global_context, }, inline_states => { _start => \&on_irc_start, _default => \&on_irc_default, irc_001 => \&on_irc_001, irc_join => \&on_irc_join, irc_part => \&on_irc_part, irc_public => \&on_irc_public, irc_notice => \&on_irc_notice, irc_topic => \&on_irc_topic, irc_332 => \&on_irc_topicraw, irc_ctcp_action => \&on_irc_ctcp_action, irc_kick => \&on_irc_kick, irc_snotice => \&on_irc_snotice, autoping => \&do_autoping, connect => \&do_connect, irc_disconnected => \&on_irc_reconnect, irc_error => \&on_irc_reconnect, irc_socketerr => \&on_irc_reconnect, } ); $global_context->add_channel( App::Mobirc::Model::Channel->new( $global_context, U('*server*') ) ); }; # ------------------------------------------------------------------------- sub on_irc_default { DEBUG "ignore unknown event: $_[ARG0]"; } sub on_irc_start { my $poe = sweet_args; DEBUG "START"; $poe->kernel->alias_set('irc_session'); DEBUG "input charset is: " . $poe->heap->{config}->{incode}; $poe->heap->{irc}->yield( register => 'all' ); $poe->heap->{irc}->yield( connect => {} ); } sub on_irc_001 { my $poe = sweet_args; DEBUG "CONNECTED"; my $channel = $poe->heap->{global_context}->get_channel( decode( 'utf8', '*server*' ) ); $channel->add_message( App::Mobirc::Model::Message->new( who => undef, body => decode( 'utf8', 'Connected to irc server!' ), class => 'connect', ) ); $poe->heap->{disconnect_msg} = true; $poe->kernel->delay( autoping => $poe->heap->{config}->{ping_delay} ); } sub on_irc_join { my $poe = sweet_args; DEBUG "JOIN"; my ( $who, $channel_name ) = _get_args($poe); $who =~ s/!.*//; # chop off after the gap (bug workaround of madoka) $channel_name =~ s/ .*//; $channel_name = normalize_channel_name($channel_name); # create channel my $channel = $poe->heap->{global_context}->get_channel($channel_name); unless ($channel) { $channel = App::Mobirc::Model::Channel->new( $poe->heap->{global_context}, $channel_name, ); $poe->heap->{global_context}->add_channel($channel); } my $irc = $poe->heap->{irc}; unless ( $who eq $irc->nick_name ) { $channel->add_message( App::Mobirc::Model::Message->new( who => undef, body => $who . U(" joined"), class => 'join', ) ); } $poe->heap->{seen_traffic} = true; $poe->heap->{disconnect_msg} = true; } sub on_irc_part { my $poe = sweet_args; my ( $who, $channel_name, $msg ) = _get_args($poe); $who =~ s/!.*//; # chop off after the gap (bug workaround of POE::Filter::IRC) $channel_name =~ s/ .*//; $channel_name = normalize_channel_name($channel_name); my $irc = $poe->heap->{irc}; if ( $who eq $irc->nick_name ) { $poe->heap->{global_context}->delete_channel($channel_name); } else { my $message = "$who leaves"; if ($msg) { $message .= "($msg)"; } my $channel = $poe->heap->{global_context}->get_channel($channel_name); $channel->add_message( App::Mobirc::Model::Message->new( who => undef, body => $message, class => 'leave', ) ); } $poe->heap->{seen_traffic} = true; $poe->heap->{disconnect_msg} = true; } sub on_irc_public { my $poe = sweet_args; DEBUG "IRC PUBLIC"; my ( $who, $channel_name, $msg ) = _get_args($poe); $who =~ s/!.*//; $channel_name = $channel_name->[0]; $channel_name = normalize_channel_name($channel_name); my $channel = $poe->heap->{global_context}->get_channel($channel_name); $channel->add_message( App::Mobirc::Model::Message->new( who => $who, body => $msg, class => 'public', ) ); my $irc = $poe->heap->{irc}; if ( $who eq $irc->nick_name ) { DEBUG "CLEAR UNREAD"; $channel->clear_unread; } $poe->heap->{seen_traffic} = true; $poe->heap->{disconnect_msg} = true; } sub on_irc_notice { my $poe = sweet_args; my ( $who, $channel_name, $msg ) = _get_args($poe); DEBUG "IRC NOTICE $who $channel_name $msg"; if ( $poe->heap->{global_context} ->run_hook_first( 'on_irc_notice', $poe, $who, $channel_name, $msg ) ) { return; } $who =~ s/!.*//; $channel_name = $channel_name->[0]; $channel_name = normalize_channel_name($channel_name); my $channel = $poe->heap->{global_context}->get_channel($channel_name); $channel->add_message( App::Mobirc::Model::Message->new( who => $who, body => $msg, class => 'notice', ) ); $poe->heap->{seen_traffic} = true; $poe->heap->{disconnect_msg} = true; } sub on_irc_topic { my $poe = sweet_args; my ( $who, $channel_name, $topic ) = _get_args($poe); $who =~ s/!.*//; DEBUG "SET TOPIC"; $channel_name = normalize_channel_name($channel_name); my $channel = $poe->heap->{global_context}->get_channel($channel_name); $channel->topic($topic); $channel->add_message( App::Mobirc::Model::Message->new( who => undef, body => "$who set topic: $topic", class => 'topic', ) ); $poe->heap->{seen_traffic} = true; $poe->heap->{disconnect_msg} = true; } sub on_irc_topicraw { my $poe = sweet_args; my ( $x, $y, $dat ) = _get_args($poe); my ( $channel, $topic ) = @{$dat}; DEBUG "SET TOPIC RAW: $channel => $topic"; $poe->heap->{global_context} ->get_channel( normalize_channel_name($channel) )->topic($topic); $poe->heap->{seen_traffic} = true; $poe->heap->{disconnect_msg} = true; } sub on_irc_ctcp_action { my $poe = sweet_args; my ( $who, $channel_name, $msg ) = _get_args($poe); $who =~ s/!.*//; $channel_name = $channel_name->[0]; my $channel = $poe->heap->{global_context}->get_channel($channel_name); my $body = sprintf( decode( 'utf8', "* %s %s" ), $who, $msg ); $channel->add_message( App::Mobirc::Model::Message->new( who => undef, body => $body, class => 'ctcp_action', ) ); $poe->heap->{seen_traffic} = true; $poe->heap->{disconnect_msg} = true; } sub on_irc_kick { my $poe = sweet_args; DEBUG "DNBKICK"; my ( $kicker, $channel_name, $kickee, $msg ) = _get_args($poe); $msg ||= 'Flooder'; $kicker =~ s/!.*//; $poe->heap->{global_context}->get_channel($channel_name)->add_message( App::Mobirc::Model::Message->new( who => undef, body => "$kicker has kicked $kickee($msg)", class => 'kick', ) ); $poe->heap->{seen_traffic} = true; $poe->heap->{disconnect_msg} = true; } sub do_connect { my $poe = sweet_args; $poe->heap->{irc}->yield( connect => {} ); } sub do_autoping { my $poe = sweet_args; $poe->kernel->post( mobirc_irc => time ) unless $poe->heap->{seen_traffic}; $poe->heap->{seen_traffic} = false; $poe->kernel->delay( autoping => $poe->heap->{config}->{ping_delay} ); } sub on_irc_snotice { my $poe = sweet_args; my ( $message, ) = _get_args($poe); DEBUG "getting snotice : $message"; my $channel = $poe->heap->{global_context}->get_channel( U('*server*') ); $channel->add_message( App::Mobirc::Model::Message->new( who => undef, body => $message, class => 'snotice', ) ); } sub on_irc_reconnect { my $poe = sweet_args; DEBUG "!RECONNECT! " . $poe->heap->{disconnect_msg}; if ( $poe->heap->{disconnect_msg} ) { my $channel = $poe->heap->{global_context} ->get_channel( decode( 'utf8', '*server*' ) ); $channel->add_message( App::Mobirc::Model::Message->new( who => undef, body => decode( 'utf8', 'Disconnected from irc server, trying to reconnect...' ), class => 'reconnect', ) ); } $poe->heap->{disconnect_msg} = false; $poe->kernel->delay( connect => $poe->heap->{config}->{reconnect_delay} ); } # FIXME: I want more cool implement sub _get_args { my $poe = shift; my @ret; for my $elem ( @{ $poe->args } ) { if ( ref $elem && ref $elem eq 'ARRAY' ) { push @ret, [ map { decode( $poe->heap->{config}->{incode}, $_ ) } @$elem ]; } else { push @ret, decode( $poe->heap->{config}->{incode}, $elem ); } } return @ret; } 1;