AnySan

 view release on metacpan or  search on metacpan

lib/AnySan/Provider/IRC.pm  view on Meta::CPAN

package AnySan::Provider::IRC;
use strict;
use warnings;
use base 'AnySan::Provider';
our @EXPORT = qw(irc);
use AnySan;
use AnySan::Receive;
use AnyEvent::IRC::Client;
use AnyEvent::IRC::Util qw/mk_msg/;
use Encode;

sub irc {
    my($host, %config) = @_;

    my $self = __PACKAGE__->new(
        client => undef,
        config => \%config,
        LAST_SEND_TIME => 0,
        SEND_QUEUE     => [],
        SEND_TIMER     => 0,
    );
    $self->{config}{wait_queue_size} ||= 100;

    my $port         = $config{port}     || 6667;
    my $nickname     = $config{nickname};
    my $instance_key = $config{key}      || "$host:$port";
    $self->{config}{interval} = defined $config{interval} ? $config{interval} : 2;
    $self->{config}{interval} = 2 unless $self->{config}{interval} =~ /\A[0-9]+\z/;

    my %recive_commands = map {
        uc($_) => 1,
    } @{ $config{recive_commands} || [ 'PRIVMSG' ] };

    my $con = AnyEvent::IRC::Client->new;
    $self->{client} = $con;

    my $on_connect = $config{on_connect} ||= sub {
        my ($con, $err) = @_;
        if (defined $err) {
            warn "connect error: $err\n";
            return;
        }
    };
    $con->reg_cb( connect => sub {
        my ($con, $err) = @_;
        $on_connect->($con, $err);
        return if defined $err;

        # join channels
        my @channels = keys %{ $config{channels} };
        if ( @channels ) {
            my $join_on_connect; $join_on_connect = AnyEvent->timer(
                after    => $self->{config}{interval},
                interval => $self->{config}{interval},
                cb       => sub {
                    my $channel = shift @channels;
                    warn "join channel: $channel";
                    $self->join_channel( $channel, $config{channels}->{$channel}->{key} );
                    if ( !@channels ) {
                        undef $join_on_connect;
                    }
                }
            );
        }
    } );
    if ( $config{on_disconnect} ) {
        $con->reg_cb( disconnect => $config{on_disconnect} );
    }

    $con->reg_cb (
        'irc_*' => sub {
            my(undef, $param) = @_;
            return if $param->{command} =~ /\A[0-9]+\z/;
            return unless $recive_commands{uc($param->{command})};
            my($channel, $message) = @{ $param->{params} };
            my($nickname, ) = split '!', ($param->{prefix} || '');

            my $receive; $receive = AnySan::Receive->new(
                provider      => 'irc',
                event         => 'privmsg',
                message       => $message,
                nickname      => $config{nickname},
                from_nickname => $nickname,
                attribute     => {
                    channel    => $channel,
                    command    => $param->{command},
                    raw_params => $param,
                },
                cb            => sub { $self->event_callback($receive, @_) },
            );
            AnySan->broadcast_message($receive);
        }
    );

    $con->enable_ssl if $config{enable_ssl}; # enable ssl

    # connect server
    $con->connect ($host, $port, {
        nick     => $nickname,
        user     => $config{user},
        password => $config{password},
    });

    return $self;
}


sub event_callback {
    my($self, $receive, $type, @args) = @_;

    if ($type eq 'reply') {
        my $cmd = $receive->attribute('send_command') || 'NOTICE';
        my $send = '';
        my $msg = $args[0];
        $msg = encode( utf8 => $msg ) if Encode::is_utf8($msg);
        if ($receive->nickname eq $receive->attribute('channel')) {
            $send = mk_msg undef, $cmd => $receive->from_nickname, $msg;
        } else {
            $send = mk_msg undef, $cmd => $receive->attribute('channel'), $msg;
        }
        $self->_send_raw($send);
    }
}

sub _run {
    my($self, $cb) = @_;
    if (scalar(@{ $self->{SEND_QUEUE} }) >= $self->{config}{wait_queue_size}) {
        return;
    }
    if (time() - $self->{LAST_SEND_TIME} <= 0 || $self->{SEND_TIMER}) {
        $self->{SEND_TIMER} ||= AnyEvent->timer(
            after    => 1,
            interval => $self->{config}{interval},
            cb       => sub {
                (shift @{ $self->{SEND_QUEUE} })->();
                $self->{LAST_SEND_TIME} = time();
                $self->{SEND_TIMER} = undef unless @{ $self->{SEND_QUEUE} };
            },
        );
        push @{ $self->{SEND_QUEUE} }, $cb;
        return;
    }
    $cb->();
    $self->{LAST_SEND_TIME} = time();
}

sub _send_raw {
    my($self, $send, %args) = @_;
    $self->_run(sub {
        $self->{client}->send_raw($send);
    });
}

sub send_message {
    my($self, $message, %args) = @_;
    $self->_run(sub {
        my $type = $args{privmsg} ? 'PRIVMSG' : 'NOTICE';
        $self->{client}->send_chan(
            $args{channel},
            $type,
            $args{channel},
            $message,
        );
    });
}

sub join_channel {
    my($self, $channel, $key) = @_;
    $self->{client}->send_srv( JOIN => $channel, $key );
}

sub leave_channel {
    my($self, $channel) = @_;
    $self->{client}->send_srv( PART => $channel );
}

1;
__END__

=head1 NAME

AnySan::Provider::IRC - AnySan provide IRC protocol

=head1 SYNOPSIS

  use AnySan;
  use AnySan::Provider::IRC;

  my $irc = irc
      'chat.example.net', # irc servername *required
      port     => 6667, # default is 6667



( run in 0.934 second using v1.01-cache-2.11-cpan-39bf76dae61 )