#!/usr/bin/perl -w # Petr Baudis (c) 2004, public domain # Slightly inspired by Stefan "tommie" Tomanek's newsline.pl. # RSS->IRC gateway use strict; ### Configuration section. # In our example setup, we are going to deliver Slashdot headlines. use vars qw ($nick $server $port $channel $rss_url $refresh); $nick = 'slashrss'; $server = 'irc.freenode.net'; $port = 6667; $channel = '#irchacks'; $rss_url = 'http://www.slashdot.org/slashdot.rss'; $refresh = 30*60; # in seconds; Slashdot allows refresh max. once per 30 minutes ### Preamble. use POSIX; use Net::IRC; use LWP::UserAgent; use XML::RSS; ### Connection initialization. use vars qw ($irc $conn); $irc = new Net::IRC; print "Connecting to server ".$server.":".$port." with nick ".$nick."...\n"; $conn = $irc->newconn (Nick => $nick, Server => $server, Port => $port, Ircname => 'RSS->IRC gateway IRC hack'); # Connect event handler - we immediately try to join our channel. sub on_connect { my ($self, $event) = @_; print "Joining channel ".$channel."...\n"; $self->join ($channel); } $conn->add_handler ('welcome', \&on_connect); # Joined the channel, so log that. sub on_joined { my ($self, $event) = @_; print "Joined channel ".$channel."...\n"; } $conn->add_handler ('endofnames', \&on_joined); # It is a good custom to reply to the CTCP VERSION request. sub on_cversion { my ($self, $event) = @_; $self->ctcp_reply ($event->nick, 'VERSION RSS->IRS gateway IRC hack'); } $conn->add_handler ('cversion', \&on_cversion); ### The RSS feed use vars qw (@items); # Fetches the RSS from server and returns a list of RSS items. sub fetch_rss { my $ua = LWP::UserAgent->new (env_proxy => 1, keep_alive => 1, timeout => 30); my $request = HTTP::Request->new('GET', $rss_url); my $response = $ua->request ($request); return unless ($response->is_success); my $data = $response->content; my $rss = new XML::RSS ( ); $rss->parse($data); foreach my $item (@{$rss->{items}}) { # Make sure to strip any possible newlines and similiar stuff. $item->Feed Syndicated RSS News =~ s/\s/ /g; } return @{$rss->{items}}; } # Attempts to find some newly appeared RSS items. sub delta_rss { my ($old, $new) = @_; # If @$old is empty, it means this is the first run and we will therefore not do anything. return ( ) unless ($old and @$old); # We take the first item of @$old and find it in @$new. Then anything before its position in @$new are the newly appeared items which we return. my $sync = $old->[0]; # If it is at the start of @$new, nothing has changed. return ( ) if ($sync->Feed Syndicated RSS News eq $new->[0]->Feed Syndicated RSS News); my $item; for ($item = 1; $item < @$new; $item++) { # We are comparing the titles which might not be 100% reliable but RSS # streams really should not contain multiple items with same title. last if ($sync->Feed Syndicated RSS News eq $new->[$item]->Feed Syndicated RSS News); } return @$new[0 .. $item - 1]; } # Check RSS feed periodically. sub check_rss { my (@new_items); print "Checking RSS feed [".$rss_url."]...\n"; @new_items = fetch_rss ( ); if (@new_items) { my @delta = delta_rss (\@items, \@new_items); foreach my $item (reverse @delta) { $conn->privmsg ($channel, '"'.$item->Feed Syndicated RSS News.'" :: '.$item->{link}); } @items = @new_items; } alarm $refresh; } $SIG{ALRM} = \&check_rss; check_rss( ); # Fire up the IRC loop. $irc->start;