Bot-IRC

 view release on metacpan or  search on metacpan

lib/Bot/IRC.pm  view on Meta::CPAN

package Bot::IRC;
# ABSTRACT: Yet Another IRC Bot

use 5.016;
use exact;

use Daemon::Device;
use Date::Format 'time2str';
use Encode qw( encode decode );
use IO::Socket::IP -register;
use IO::Socket::SSL;
use Time::Crontab;

our $VERSION = '1.48'; # VERSION

sub new {
    my $class = shift;
    my $self  = bless( {@_}, $class );

    croak('Odd number of elements passed to new()') if ( @_ % 2 );
    croak('connect/server not provided to new()')
        unless ( ref $self->{connect} eq 'HASH' and $self->{connect}{server} );

    $self->{spawn}    ||= 2;
    $self->{encoding} //= 'UTF-8';

    $self->{connect}{nick} //= 'bot';
    $self->{connect}{name} //= 'Yet Another IRC Bot';
    $self->{connect}{port} ||= ( $self->{connect}{ssl} ) ? 6670 : 6667;

    $self->{disconnect} //= sub {};

    $self->{daemon}           //= {};
    $self->{daemon}{name}     //= $self->{connect}{nick};
    $self->{daemon}{pid_file} //= $self->{daemon}{name} . '.pid';

    $self->{nick} = $self->{connect}{nick};

    $self->{hooks}    = [];
    $self->{ticks}    = [];
    $self->{helps}    = {};
    $self->{loaded}   = {};
    $self->{numerics} = [];

    $self->{send_user_nick} ||= 'on_parent';
    croak('"send_user_nick" optional value set to invalid value') if (
        $self->{send_user_nick} ne 'on_connect' and
        $self->{send_user_nick} ne 'on_parent' and
        $self->{send_user_nick} ne 'on_reply'
    );

    $self->load(
        ( ref $self->{plugins} eq 'ARRAY' ) ? @{ $self->{plugins} } : $self->{plugins}
    ) if ( $self->{plugins} );

    return $self;
}

sub run {
    my $self     = shift;
    my $commands = \@_;

    $self->{socket} = ( ( $self->{connect}{ssl} ) ? 'IO::Socket::SSL' : 'IO::Socket::IP' )->new(
        PeerAddr        => $self->{connect}{server},
        PeerPort        => $self->{connect}{port},
        Proto           => 'tcp',
        Family          => ( $self->{connect}{ipv6} ? AF_INET6 : AF_INET ),
        Type            => SOCK_STREAM,
        SSL_verify_mode => SSL_VERIFY_NONE,
    ) or die $!;

    if ( $self->{send_user_nick} eq 'on_connect' ) {
        $self->{socket}->print("PASS $self->{connect}{password}\r\n") if ( $self->{connect}{password} );
        $self->{socket}->print("NICK $self->{nick}\r\n");
        $self->{socket}->print("USER $self->{nick} 0 * :$self->{connect}{name}\r\n");
    }

    try {
        $self->{device} = Daemon::Device->new(
            parent     => \&_parent,
            child      => \&_child,
            on_message => \&_on_message,
            spawn      => $self->{spawn},
            daemon     => $self->{daemon},
            data       => {
                self     => $self,
                commands => $commands,
                passwd   => $self->{passwd},
            },
        );
    }
    catch ($e) {
        croak("Daemon device instantiation failure: $e");
    }

    $self->{device}->run;
}

sub note {
    my ( $self, $msg, $err ) = @_;
    chomp($msg);
    $msg = '[' . time2str( '%d/%b/%Y:%H:%M:%S %z', time() ) . '] ' . $msg . "\n";

    if ($err) {
        die $msg if ( $err eq 'die' );
        warn $msg;
    }
    else {
        print decode( $self->{encoding}, $msg );
    }

    return;
}

sub _parent {
    my ($device) = @_;
    my $self     = $device->data('self');
    my $session  = { start => time };
    my $delegate = sub {
        my $fresh_children = $device->children;
        $session->{children} = $fresh_children if (
            not $session->{children} or
            join( ',', sort @{ $session->{children} } ) ne join( ',', sort @$fresh_children )



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