Add SelectHandler class to register callbacks for selecting and reading handles; updating StdinReader to use SelectHandler

This commit is contained in:
Pragmatic Software 2014-03-14 05:51:15 +00:00
parent 1831d0775d
commit 4a110848e9
4 changed files with 121 additions and 47 deletions

View File

@ -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
#-----------------------------------------------------------------------------------

69
PBot/SelectHandler.pm Normal file
View File

@ -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;

View File

@ -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, "</dev/tty" or die $!;
my $tty_fd = fileno(TTY);
my $foreground = (tcgetpgrp($tty_fd) == getpgrp()) ? 1 : 0;
sub check_stdin {
# make sure we're in the foreground first
$foreground = (tcgetpgrp($tty_fd) == getpgrp()) ? 1 : 0;
return if not $foreground;
if ($stdin->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, "</dev/tty" or die $!;
$self->{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;

View File

@ -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",
};