diff --git a/PBot/PBot.pm b/PBot/PBot.pm index 05b690a1..e5846a68 100644 --- a/PBot/PBot.pm +++ b/PBot/PBot.pm @@ -21,6 +21,7 @@ STDOUT->autoflush(1); use Carp (); use PBot::Logger; +use PBot::SelectHandler; use PBot::StdinReader; use PBot::IRC; @@ -106,8 +107,10 @@ sub initialize { $self->{commands} = PBot::Commands->new(pbot => $self); $self->{timer} = PBot::Timer->new(timeout => 10); - $self->{admins} = PBot::BotAdmins->new(pbot => $self, filename => $admins_file); + $self->{select_handler} = PBot::SelectHandler->new(pbot => $self); + $self->{stdin_reader} = PBot::StdinReader->new(pbot => $self); + $self->{admins} = PBot::BotAdmins->new(pbot => $self, filename => $admins_file); $self->admins->load_admins(); $self->admins->add_admin($self->{botnick}, '.*', "$self->{botnick}!stdin\@localhost", 60, 'admin'); $self->admins->login($self->{botnick}, "$self->{botnick}!stdin\@localhost", 'admin'); @@ -215,8 +218,8 @@ sub do_one_loop { # process IRC events $self->irc->do_one_loop(); - # process STDIN events - $self->check_stdin(); + # process SelectHandler + $self->{select_handler}->do_select(); } sub start { @@ -231,29 +234,6 @@ sub start { } } -sub check_stdin { - my $self = shift; - - my $input = PBot::StdinReader::check_stdin(); - - return if not defined $input; - - $self->logger->log("---------------------------------------------\n"); - $self->logger->log("Read '$input' from STDIN\n"); - - my ($from, $text); - - if($input =~ m/^~([^ ]+)\s+(.*)/) { - $from = $1; - $text = "$self->{trigger}$2"; - } else { - $from = "$self->{botnick}!stdin\@localhost"; - $text = "$self->{trigger}$input"; - } - - return $self->interpreter->process_line($from, $self->{botnick}, "stdin", "localhost", $text); -} - #----------------------------------------------------------------------------------- # Getters/Setters #----------------------------------------------------------------------------------- diff --git a/PBot/SelectHandler.pm b/PBot/SelectHandler.pm new file mode 100644 index 00000000..f6287270 --- /dev/null +++ b/PBot/SelectHandler.pm @@ -0,0 +1,69 @@ +package PBot::SelectHandler; + +use warnings; +use strict; + +use vars qw($VERSION); +$VERSION = '1.0.0'; + +use IO::Select; +use Carp (); + +sub new { + if(ref($_[1]) eq 'HASH') { + Carp::croak("Options to SelectHandler should be key/value pairs, not hash reference"); + } + + my ($class, %conf) = @_; + + my $self = bless {}, $class; + $self->initialize(%conf); + return $self; +} + +sub initialize { + my ($self, %conf) = @_; + + $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference in SelectHandler"); + $self->{select} = IO::Select->new(); + $self->{readers} = {}; +} + +sub add_reader { + my ($self, $handle, $sub) = @_; + $self->{select}->add($handle); + $self->{readers}->{$handle} = $sub; +} + +sub do_select { + my ($self) = @_; + my @ready = $self->{select}->can_read(.5); + foreach my $fh (@ready) { + my $ret = sysread($fh, my $buf, 4096); + + if(not defined $ret) { + $self->{pbot}->logger->log("Error with $fh: $!\n"); + $self->{select}->remove($fh); + delete $self->{readers}->{$fh}; + next; + } + + if($ret == 0) { + $self->{pbot}->logger->log("done with $fh\n"); + $self->{select}->remove($fh); + delete $self->{readers}->{$fh}; + next; + } + + chomp $buf; + $self->{pbot}->logger->log("read from $fh: [$buf]\n"); + + if(not exists $self->{readers}->{$fh}) { + $self->{pbot}->logger->log("Error: no reader for $fh\n"); + } else { + $self->{readers}->{$fh}->($buf); + } + } +} + +1; diff --git a/PBot/StdinReader.pm b/PBot/StdinReader.pm index 07b01cae..aaf9da3b 100644 --- a/PBot/StdinReader.pm +++ b/PBot/StdinReader.pm @@ -4,31 +4,56 @@ use warnings; use strict; use vars qw($VERSION); - $VERSION = '1.0.0'; -use IO::Select; use POSIX qw(tcgetpgrp getpgrp); # to check whether process is in background or foreground +use Carp (); -# used to listen for STDIN in non-blocking mode -my $stdin = IO::Select->new(); -$stdin->add(\*STDIN); - -# used to check whether process is in background or foreground, for stdin reading -open TTY, "can_read(.5)) { - sysread(STDIN, my $input, 1024); - chomp $input; - return $input; +sub new { + if(ref($_[1]) eq 'HASH') { + Carp::croak("Options to StdinReader should be key/value pairs, not hash reference"); } + + my ($class, %conf) = @_; + my $self = bless {}, $class; + $self->initialize(%conf); + return $self; +} + +sub initialize { + my ($self, %conf) = @_; + + $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference in StdinReader"); + + # used to check whether process is in background or foreground, for stdin reading + open TTY, "{tty_fd} = fileno(TTY); + $self->{foreground} = (tcgetpgrp($self->{tty_fd}) == getpgrp()) ? 1 : 0; + + $self->{pbot}->{select_handler}->add_reader(\*STDIN, sub { $self->stdin_reader(@_) }); +} + +sub stdin_reader { + my ($self, $input) = @_; + + # make sure we're in the foreground first + $self->{foreground} = (tcgetpgrp($self->{tty_fd}) == getpgrp()) ? 1 : 0; + return if not $self->{foreground}; + + $self->{pbot}->logger->log("---------------------------------------------\n"); + $self->{pbot}->logger->log("Read '$input' from STDIN\n"); + + my ($from, $text); + + if($input =~ m/^~([^ ]+)\s+(.*)/) { + $from = $1; + $text = "$self->{pbot}->{trigger}$2"; + } else { + $from = "$self->{pbot}->{botnick}!stdin\@localhost"; + $text = "$self->{pbot}->{trigger}$input"; + } + + return $self->{pbot}->interpreter->process_line($from, $self->{pbot}->{botnick}, "stdin", "localhost", $text); } 1; diff --git a/PBot/VERSION.pm b/PBot/VERSION.pm index b5f382b9..8ef4bb3a 100644 --- a/PBot/VERSION.pm +++ b/PBot/VERSION.pm @@ -13,7 +13,7 @@ use warnings; # These are set automatically by the build/commit script use constant { BUILD_NAME => "PBot", - BUILD_REVISION => 523, + BUILD_REVISION => 524, BUILD_DATE => "2014-03-13", };