=39= $_[KERNEL]->yield("heartbeat"); # start heartbeat
####
my %FOLLOWS = (
ACCESS => "/var/log/messages",
);
my $IRC_ALIAS = $nick;
my $SKIPPING = 0; # if skipping, how many we've done
my $SEND_QUEUE; # cache
my $CHANNEL = $channel;
POE::Session->create(
package_states => [
main => [ qw(
_default
_start irc_public irc_join
my_add my_tailed my_heartbeat
)
],
],
);
$poe_kernel->run();
sub _start {
my $irc = POE::Component::IRC::State->spawn(
nick => $nick,
Username => $username,
ircname => $desc,
server => $server_address,
);
$_[HEAP]{dns} = POE::Component::Client::DNS->spawn;
$_[HEAP]{irc} = $irc;
$irc->plugin_add(
'NickServID',
POE::Component::IRC::Plugin::NickServID->new(
Password => 'iamrobot'
),
);
$irc->plugin_add(
'AutoJoin',
POE::Component::IRC::Plugin::AutoJoin->new(
Channels => $channel,
),
);
$irc->yield( register => 'all' );
$irc->yield( 'connect' );
$irc->yield("my_heartbeat"); # start heartbeat
$irc->yield("my_add", $_) for keys %FOLLOWS; # start following
return;
}
sub my_add {
my $trailing = $_[ARG0];
my $session = $_[SESSION];
POE::Session->create(
inline_states => {
_start => sub {
$_[HEAP]->{wheel} = POE::Wheel::FollowTail->new(
Filename => $FOLLOWS{$trailing},
InputEvent => 'got_line',
);
},
got_line => sub {
$_[KERNEL]->post(
$session => my_tailed =>
time, $trailing, $_[ARG0]
);
},
},
);
}
sub my_tailed {
my ($time, $file, $line) = @_[ARG0..ARG2];
## $time will be undef on a probe, or a time value if a real line
## PoCo::IRC has throttling built in, but no external visibility
## so this is reaching "under the hood"
$SEND_QUEUE ||=
$_[KERNEL]->alias_resolve($IRC_ALIAS)->get_heap->{send_queue};
## handle "no need to keep skipping" transition
if ($SKIPPING and @$SEND_QUEUE < 1) {
$_[KERNEL]->post($IRC_ALIAS => privmsg => $CHANNEL =>
"[discarded $SKIPPING messages]");
$SKIPPING = 0;
}
## handle potential message display
if ($time) {
if ($SKIPPING or @$SEND_QUEUE > 3) { # 3 msgs per 10 seconds
$SKIPPING++;
}
else {
my @time = localtime $time;
$_[KERNEL]->post(
$IRC_ALIAS => privmsg =>
$CHANNEL => sprintf "%02d:%02d:%02d: %s: %s",
($time[2] + 11) % 12 + 1, $time[1], $time[0], $file, $line);
}
}
## handle re-probe/flush if skipping
if ($SKIPPING) {
$_[KERNEL]->delay($_[STATE] => 0.5); # $time will be undef
}
}
sub my_heartbeat {
$_[KERNEL]->yield(my_tailed => time, "heartbeat", "beep");
$_[KERNEL]->delay($_[STATE] => 10);
}
# Default event handler, for some reason, stops output after logged in
sub _default {
my ($event, $args) = @_[ARG0 .. $#_];
my @output = ( "$event: " );
for my $arg (@$args) {
if (ref $arg eq 'ARRAY') {
push @output, '[' . join ', ', @$arg . ']';
}
else {
push @output, "'$arg'";
}
}
print join ' ', @output, "\n";
return;
}
# Event when a public message is broadcast to a channel
sub irc_public {
my ($sender, $where, $what) = @_[SENDER, ARG1, ARG2];
my $nick = parse_nick $_[ARG0];
my $irc = $_[SENDER]->get_heap;
}
# Event when someone joins a channel
sub irc_join {
my $nick = parse_nick $_[ARG0];
my $channel = $_[ARG1];
my $irc = $_[SENDER]->get_heap;
if ($nick eq $irc->nick_name) {
$irc->yield(privmsg => $channel, "I am online and responding to commands");
}
}
##
##
#!/usr/bin/perl
use 5.010;
use strict;
use warnings;
use POE;
use Config::Any;
use File::HomeDir;
use POE::Wheel::FollowTail;
use Getopt::Long::Descriptive;
use POE::Component::IRC::State;
use POE::Component::IRC::Plugin::AutoJoin;
use POE::Component::IRC::Plugin::NickServID;
sub parse_nick { (split /!/, shift)[0] }
my $home = File::HomeDir->my_home;
# take options from command line
my ($opt, $usage) = describe_options(
"\%c \%o",
[ "server-address|s=s" => 'IRC Server Address', ],
[ "server-port|p=s" => 'IRC Server Port [default: 6667]', ],
[ "protocol|P=s" => 'IRC Server Protocol [default: TCP]', ],
[ "timeout|t=s" => 'IRC Server Connection Timeout (in seconds) [default: 30]', ],
[ "channel|c=s" => 'IRC Channel', ],
[ "config-name|n=s" => 'Config File Name',
{ default => 'testbot' }, ],
[ "nick|N=s" => 'IRC Nick', ],
[ "password|A=s" => 'IRC Password', ],
[ "username|u=s" => 'IRC Username, defaults to Nick if unset', ],
[ "hostname|h=s" => 'IRC Hostname', ],
[ "desc|d=s" => 'IRC User Desc.', ],
[],
[ "verbose|v" => 'Print Status Messages' ],
[ "help|h" => 'Print This message and Exit', ],
);
( print $usage->text ), exit if $opt->help;
# get config name
my $config = $opt->config_name;
# look for config file
my $cfg = Config::Any->load_stems(
{
stems => ["/etc/$config", "$home/etc/$config", "$home/etc/$config", "./etc/$config", "./$config", ],
use_ext => 1,
}
);
# take only the last loaded config file
my $last_config = shift @$cfg;
my ($file_name, $loaded_config);
if ($last_config) {
($file_name, $loaded_config) = %$last_config;
}
else {
($file_name, $loaded_config) = ('NONE', {} );
}
say "[*] Loaded config file $file_name" if $opt->verbose;
# Thinking about config file design,
my $server_address = $opt->server_address || $loaded_config->{'server'}->{'address'} || 'myserver';
my $server_port = $opt->server_port || $loaded_config->{'server'}->{'port'} || '6667';
my $server_protocol = $opt->protocol || $loaded_config->{'server'}->{'protocol'} || 'tcp';
my $timeout = $opt->timeout || $loaded_config->{'server'}->{'timeout'} || '30';
my $channel = $opt->channel || $loaded_config->{'server'}->{'channel'} || [ '#foo',];
my $nick = $opt->nick || $loaded_config->{'user'}->{'nick'} || 'testbot2';
my $password = $opt->password || $loaded_config->{'user'}->{'password'} || '';
my $username = $opt->username || $loaded_config->{'user'}->{'username'} || $nick;
my $hostname = $opt->hostname || $loaded_config->{'user'}->{'hostname'} || 'foo.test.irc';
my $desc = $opt->desc || $loaded_config->{'user'}->{'desc'};
my %FOLLOWS = (
ACCESS => "/var/log/messages",
);
my $IRC_ALIAS = $nick;
my $SKIPPING = 0; # if skipping, how many we've done
my $SEND_QUEUE; # cache
my $CHANNEL = $channel;
POE::Session->create(
package_states => [
main => [ qw(
_default
_start irc_public irc_join
my_add my_tailed my_heartbeat
)
],
],
);
$poe_kernel->run();
sub _start {
my $irc = POE::Component::IRC::State->spawn(
nick => $nick,
Username => $username,
ircname => $desc,
server => $server_address,
);
$_[HEAP]{dns} = POE::Component::Client::DNS->spawn;
$_[HEAP]{irc} = $irc;
$irc->plugin_add(
'NickServID',
POE::Component::IRC::Plugin::NickServID->new(
Password => 'iamrobot'
),
);
$irc->plugin_add(
'AutoJoin',
POE::Component::IRC::Plugin::AutoJoin->new(
Channels => $channel,
),
);
$irc->yield( register => 'all' );
$irc->yield( 'connect' );
$irc->yield("my_heartbeat"); # start heartbeat
$irc->yield("my_add", $_) for keys %FOLLOWS; # start following
return;
}
sub my_add {
my $trailing = $_[ARG0];
my $session = $_[SESSION];
POE::Session->create(
inline_states => {
_start => sub {
$_[HEAP]->{wheel} = POE::Wheel::FollowTail->new(
Filename => $FOLLOWS{$trailing},
InputEvent => 'got_line',
);
},
got_line => sub {
$_[KERNEL]->post(
$session => my_tailed =>
time, $trailing, $_[ARG0]
);
},
},
);
}
sub my_tailed {
my ($time, $file, $line) = @_[ARG0..ARG2];
## $time will be undef on a probe, or a time value if a real line
## PoCo::IRC has throttling built in, but no external visibility
## so this is reaching "under the hood"
$SEND_QUEUE ||=
$_[KERNEL]->alias_resolve($IRC_ALIAS)->get_heap->{send_queue};
## handle "no need to keep skipping" transition
if ($SKIPPING and @$SEND_QUEUE < 1) {
$_[KERNEL]->post($IRC_ALIAS => privmsg => $CHANNEL =>
"[discarded $SKIPPING messages]");
$SKIPPING = 0;
}
## handle potential message display
if ($time) {
if ($SKIPPING or @$SEND_QUEUE > 3) { # 3 msgs per 10 seconds
$SKIPPING++;
}
else {
my @time = localtime $time;
$_[KERNEL]->post(
$IRC_ALIAS => privmsg =>
$CHANNEL => sprintf "%02d:%02d:%02d: %s: %s",
($time[2] + 11) % 12 + 1, $time[1], $time[0], $file, $line);
}
}
## handle re-probe/flush if skipping
if ($SKIPPING) {
$_[KERNEL]->delay($_[STATE] => 0.5); # $time will be undef
}
}
sub my_heartbeat {
$_[KERNEL]->yield(my_tailed => time, "heartbeat", "beep");
$_[KERNEL]->delay($_[STATE] => 10);
}
# Default event handler, for some reason, stops output after logged in
sub _default {
my ($event, $args) = @_[ARG0 .. $#_];
my @output = ( "$event: " );
for my $arg (@$args) {
if (ref $arg eq 'ARRAY') {
push @output, '[' . join ', ', @$arg . ']';
}
else {
push @output, "'$arg'";
}
}
print join ' ', @output, "\n";
return;
}
# Event when a public message is broadcast to a channel
sub irc_public {
my ($sender, $where, $what) = @_[SENDER, ARG1, ARG2];
my $nick = parse_nick $_[ARG0];
my $irc = $_[SENDER]->get_heap;
}
# Event when someone joins a channel
sub irc_join {
my $nick = parse_nick $_[ARG0];
my $channel = $_[ARG1];
my $irc = $_[SENDER]->get_heap;
if ($nick eq $irc->nick_name) {
$irc->yield(privmsg => $channel, "I am online and responding to commands");
}
}