Wednesday, May 30, 2007

IRC RSS Aggregator Bot

I wanted a IRC RSS Aggregator bot and did not find any on the allmighty web, so I had to create my own. It is written in Perl and uses POE::Component::IRC and POE::Component::RSSAggregator. Here is the code:
#!/usr/bin/perl
use strict;
use warnings;
use POE;
use POE qw(Component::IRC);
use POE::Component::RSSAggregator;
$|++;

# Config.
my $rss_config = 'rss.cfg';
my $rss_config_saved = 'rssc.cfg';
my $nick = 'rssbot';
my $server = 'irc.myircnet.net';
my $port = 6667;
my $name = 'IRC RSS aggregator bot, by septic';
my @channels = ();


my @feeds = ();
my %globalheadline;
my %counter;

open(CONFIG, "<$rss_config");
while(<CONFIG>){
next if(/^#/);
$_ =~ s/\n//;
my @vals = split / /, $_;
push @feeds, { url => $vals[0],
name => $vals[1],
delay => $vals[2],
};
}
close(CONFIG);

foreach(@feeds) {
print $_->{url}." ";
print $_->{name}." ";
print $_->{delay}."\n";
}

my $irc = POE::Component::IRC->spawn(
nick => $nick,
server => $server,
port => $port,
ircname => $name,
) or die "Oh noooo! $!";

POE::Session->create(
package_states => [
'main' => [ qw(_default _start irc_001 irc_public irc_msg) ],
],
heap => { irc => $irc },
);

POE::Session->create(
inline_states => {
_start => \&init_session,
handle_feed => \&handle_feed,
},
);


$poe_kernel->run();
exit 0;
sub init_session {
my ( $kernel, $heap, $session ) = @_[ KERNEL, HEAP, SESSION ];
$heap->{rssagg} = POE::Component::RSSAggregator->new(
alias => 'rssagg',
debug => 1,
callback => $session->postback("handle_feed"),
tmpdir => '/tmp', # optional caching
);
$kernel->post( 'rssagg', 'add_feed', $_ ) for @feeds;
}

sub handle_feed {
my ( $kernel, $feed ) = ( $_[KERNEL], $_[ARG1]->[0] );

for my $headline ( $feed->late_breaking_news ) {
my ($name,$feedname) = split /:/,$feed->name;
if(not exists $counter{$name}) {
$counter{$name} = 0;
}
$globalheadline{$name.($counter{$name}%5)} = $headline;
$irc->yield( privmsg => $name => ($counter{$name}%5)." ".$feedname.': '.$headline->headline);
$counter{$name} = $counter{$name} + 1;
}
}
#IRC
sub _start {
my ($kernel,$heap) = @_[KERNEL,HEAP];

# We get the session ID of the component from the object
# and register and connect to the specified server.
my $irc_session = $heap->{irc}->session_id();
$kernel->post( $irc_session => register => 'all' );
$kernel->post( $irc_session => connect => { } );
undef;
}

sub irc_001 {
my ($kernel,$sender) = @_[KERNEL,SENDER];

# Get the component's object at any time by accessing the heap of
# the SENDER
my $poco_object = $sender->get_heap();
print "Connected to ", $poco_object->server_name(), "\n";

# In any irc_* events SENDER will be the PoCo-IRC session
$kernel->post( $sender => join => $_ ) for @channels;

undef;
}

sub irc_public {
my ($kernel,$sender,$who,$where,$what) = @_[KERNEL,SENDER,ARG0,ARG1,ARG2];
my $nick = ( split /!/, $who )[0];
my $channel = $where->[0];

if ( my ($rot13) = $what =~ /^rot13 (.+)/ ) {
#$rot13 =~ tr[a-zA-Z][n-za-mN-ZA-M];
#$kernel->post( $sender => privmsg => $channel => "$nick: $rot13" );
#$irc->yield( privmsg => $nick => $rot13);
}

undef;
}
sub irc_msg {
my ($kernel,$sender,$who,$where,$what) = @_[KERNEL,SENDER,ARG0,ARG1,ARG2];
my $nick = ( split /!/, $who )[0];

print "$nick: $what\n";

if(my ($val) = $what =~ /^info (\d)/) {
if(exists $globalheadline{$nick.$val}){
$irc->yield( privmsg => $nick => $globalheadline{$nick.$val}->headline);
$irc->yield( privmsg => $nick => $globalheadline{$nick.$val}->description);
$irc->yield( privmsg => $nick => $globalheadline{$nick.$val}->url);
} else {
$irc->yield( privmsg => $nick => "There is no information about $val.");
}
} elsif(my ($val) = $what =~ /^add (.+)/) {
my @thing = split / /,$val;
if(@thing != 3) {
$irc->yield( privmsg => $nick => 'add <url> <desc> <delay>');
} else {
if ($thing[0] !~ /http:\/\/[\/\w\s\d\.]*/i) {
$irc->yield( privmsg => $nick => 'Invalid URL');
} elsif (($thing[2] !~ /\d/) && ($thing[2] > 60)) {
$irc->yield( privmsg => $nick => 'Bad delay, should more than 60');
} else {
$irc->yield( privmsg => $nick => "OK $thing[0] $thing[1] $thing[2]");
my $mojt = { url => $thing[0],
name => $nick.":".$thing[1],
delay => $thing[2],
};
push @feeds, $mojt;
$kernel->post( 'rssagg', 'add_feed', $mojt );

open(CONFIG, ">$rss_config_saved");
foreach(@feeds){
print CONFIG $_->{url}." ";
print CONFIG $_->{name}." ";
print CONFIG $_->{delay}."\n";
}
close(CONFIG);
}
}
} elsif($what =~ /^help/) {
$irc->yield( privmsg => $nick => "This is the rss aggregator bot");
$irc->yield( privmsg => $nick => "Add a feed");
$irc->yield( privmsg => $nick => "add <url> <desc> <delay>");
$irc->yield( privmsg => $nick => " ");
$irc->yield( privmsg => $nick => "Information about a post");
$irc->yield( privmsg => $nick => "info <id>");
} else {
$irc->yield( privmsg => $nick => 'Write "help" if you want help.');
}

undef;
}

# We registered for all events, this will produce some debug info.
sub _default {
my ($event, $args) = @_[ARG0 .. $#_];
my @output = ( "$event: " );

foreach my $arg ( @$args ) {
if ( ref($arg) eq 'ARRAY' ) {
push( @output, "[" . join(" ,", @$arg ) . "]" );
} else {
push ( @output, "'$arg'" );
}
}
print STDOUT join ' ', @output, "\n";
return 0;
}



The config file for the rss should look like this:
#url nick:name delay
http://www.slashdot.org/slashdot.rss mynick:slashdot 300