#!/usr/bin/perl # booktalk # -------- # Finds if anyone has been talking about any of your favourite # or current books. # # Would be nice to be able to retrieve: # 1) user book list for *all* book types (completed, purchased, etc) # 2) book *comments* (recorded in allconsuming) as well as blog mentions # # Thoughts: # How can we get the user's book list(s) in the nicest way? SOAP? # If they're available via RSS/XML, and generated as files on the # server and sit statically there, I could use a sensitive (ETag/Last- # Modified) HTTP GET instead. # # (c) DJ Adams 2003 # See http://www.allconsuming.net/news/000012.html # Changes # 2003-01-24 dja fix ASIN() to always return a hashref use Getopt::Std; use SOAP::Lite +autodispatch => uri => 'http://www.allconsuming.net/AllConsumingAPI', proxy => 'http://www.allconsuming.net/soap.cgi', on_fault => sub { my($soap, $res) = @_; die ref $res ? $res->faultstring : $soap->transport->status; }; use vars qw/$VERSION $SPLIT/; $VERSION = '0.1b'; $SPLIT = "\n\n"; our($opt_f, $opt_u); getopt('fu'); die usage() unless defined $opt_u; my $rssfile = defined $opt_f ? $opt_f : "$opt_u.rss"; my $api = new AllConsumingAPI; # Amalgamate book list(s) keyed on ASIN my %mybooks = ( %{ASIN($api->GetCurrentlyReadingList($opt_u))}, %{ASIN($api->GetFavoriteBooksList($opt_u))}, ); # Books (weblog-)commented upon in last hour my %list = %{ ASIN($api->GetHourlyList()) }; # Search for my books mentioned my @commented; # (Perhaps do the other way round?) foreach my $asin (keys %mybooks) { if (exists($list{$asin})) { debug("Comment for ".$mybooks{$asin}->{title}); push @commented, { asin => $asin, title => $mybooks{$asin}->{title}, excerpt => $list{$asin}->{excerpt}, url => $list{$asin}->{url}, allconsuming_url => $mybooks{$asin}->{allconsuming_url}, }; } } # Need to update the feed if we have newly commented books if (@commented) { my($header, $items, $footer); # Have a feed already? if (-f "$rssfile") { # Yes - parse my $content; open(RSS, "<$rssfile") or die "Can't open $rssfile: $!"; { local $/ = undef; $content = ; } close(RSS); ($header, $items, $footer) = split(/$SPLIT/, $content, 3); } else { # No - create from scratch ($header, $items, $footer) = (rssHeader($opt_u), "", rssFooter()); } # Add newly mentioned items foreach my $book (@commented) { my $travelURL = travelURL($book->{url}, $book->{asin}); $items = "\n" . "" . escape($book->{title}) . "\n" . "" . $book->{allconsuming_url} . "\n" . "" . escape($book->{excerpt} . "[$book->{url}]") . "\n" . "\n\n" . $items; } # (Re)write open(RSS, ">$rssfile") or die "Can't open $rssfile: $!"; print RSS join($SPLIT, $header, $items, $footer); close(RSS); } debug(scalar @commented . " books mentioned"); sub ASIN { my $data = shift; return {} unless defined (my $asinlist = $data->{asins}); $asinlist = [$asinlist] unless ref $asinlist eq 'ARRAY'; return {map { $_->{asin} => $_ } @{$asinlist} }; } sub travelURL { my ($url, $asin) = @_; return "http://allconsuming.net/travel.cgi?url=$url#$asin"; } sub escape { my $string = shift; # Standard entities $string =~ s/&/&/g; $string =~ s/"/"/g; $string =~ s//>/g; # Hmmm $string =~ s/(.)/ord($1)<160?$1:"&#".ord($1).";"/eg; return $string; } sub debug { my $s = shift; print STDERR $s."\n"; } sub usage { return < Mentions of ${user}'s favourite books http://allconsuming.net Allconsuming.net-sourced info: weblog-mentions of ${user}'s favourite books EO_HEADER } sub rssFooter { return < EO_FOOTER }