#!/usr/bin/perl
#
# MyRSSMerger - read multiple RSS feeds, post new entries to Movable Type.
# http://disobey.com/d/code/ or contact morbus@disobey.com.
#
# This code is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#
# 1.1 (2004-11-19)
#   * Moved date routines to Date::Manip.
#   * Quickie hack to support 2.0's <pubDate>.
#
# 1.0 (2003-05-27)
#   * Initial release.
#

use warnings;
use strict; $|++;
my $VERSION = "1.1";
use Date::Manip;
use Getopt::Long;
my %opts;

# make sure we've got the modules we need, else die peacefully.
eval("use LWP::Simple;");  die "[err] LWP::Simple is not installed.\n" if $@;
eval("use Net::Blogger;"); die "[err] Net::Blogger is not installed.\n" if $@;
eval("use XML::RSS;");     die "[err] XML::RSS is not installed.\n" if $@;

# define our command line flags (long and short versions).
GetOptions(\%opts, 'server|s=s',      # the POP3 server to use.
                   'username|u=s',    # the POP3 username to use.
                   'password|p=s',    # the POP3 password to use.
                   'blogid|b=i',      # unique ID of your blog.
                   'catid|c=i',       # unique ID for posting category.
                   'showcategories',  # list categories for blog.
                   'filter|f=s',      # per item filter for posting?
);

# at the very least, we need our login information.
die "[err] XML-RPC URL missing, use --server or -s.\n" unless $opts{server};
die "[err] Username missing, use --username or -u.\n"  unless $opts{username};
die "[err] Password missing, use --password or -p.\n"  unless $opts{password};
die "[err] BlogID missing, use --blogid or -b.\n"      unless $opts{blogid};

# every request past this point requires
# a connection, so we'll go and do so.
print "-" x 76, "\n"; # visual separator.
my $mt = Net::Blogger->new(engine=>"movabletype");
$mt->Proxy($opts{server});       # the servername.
$mt->Username($opts{username});  # the username.
$mt->Password($opts{password});  # the... ok. self-
$mt->BlogId($opts{blogid});      # explanatory!

# show existing categories.
if ($opts{showcategories}) {

    # get the list of categories from the server.
    my $cats = $mt->mt()->getCategoryList()
      or die "[err] ", $mt->LastError(), "\n";

    # and print 'em.
    if (scalar(@$cats) > 0) {
        print "The following blog categories are available:\n\n";
        foreach (sort { $a->{categoryId} <=> $b->{categoryId} } @$cats) {
            print " $_->{categoryId}: $_->{categoryName}\n";
        }
    } else { print "There are no selectable categories available.\n"; }

    # done this request, so exit.
    print "\nCategory ID's can be used for --catid or -c.\n";
    print "-" x 76, "\n"; exit; # call me again, again!

}

# now, check for passed URLs for new-item-examination.
die "[err] No RSS URLs were passed for processing.\n" unless @ARGV;

# loop through each RSS URL.
foreach my $rss_url (@ARGV) {

    # download whatever we've got coming.
    print "Downloading RSS feed at ", substr($rss_url, 0, 40), "...\n";
    my $data = get($rss_url) or print " [err] Data could not be downloaded!\n";
    next unless $data; # move onto the next URL in our list, if any.

    # parse it and then
    # count the number of items.
    # move on if nothing parsed.
    my $rss = new XML::RSS; $rss->parse($data);
    my $item_count = scalar(@{$rss->{items}});
    unless ($item_count) { print " [err] No parsable items.\n"; next; }

    # sandwich our post between a preface/anteface.
    my $clink = $rss->{channel}->{"link"}; # shorter variable.
    my $ctitle = $rss->{channel}->{title}; # shorter variable.
    my $preface = "From <a href=\"$clink\">$ctitle</a>:\n\n<blockquote>";
    my $anteface = "</blockquote>\n\n"; # new items as quotes.

    # and look for items dated today.
    foreach my $item (@{$rss->{items}}) {

        # no description or date for our item? move on.
        unless ($item->{description} or $item->{dc}->{date}) {
          print " Skipping (no description/date): '$item->{title}'.\n"; next;
        }

        # compare the item's date with today's date.
        my $date = $item->{dc}->{date} || $item->{pubDate};
        my $rss_date  = UnixDate($date, '%s');
        my $right_now = UnixDate('today', '%s');
        my $timeframe = $right_now - $rss_date;

        # and post if it's within an hour.
        if ($timeframe <= 86400) {

            # shorter variable. we're lazy.
            my $creator = $item->{dc}->{creator};

            # if there's a filter, check for goodness.
            if ($opts{filter} and $item->{description} !~ /$opts{filter}/i) {
                print " Skipping (failed filter): '$item->{title}'.\n"; next;
            }

            # we found an item to post, so make a 
            # final description from various parts.
            my $description = "$preface$item->{description} ";
            $description   .= "($creator) " if $creator;
            $description   .= "<a href=\"$item->{link}\">Read " .
                              "more from this post.</a>$anteface";

            # now, post to the passed blog info.
            print " Publishing item: '$item->{title}'.\n";
            my $id = $mt->metaWeblog()->newPost(
                              title       => $item->{title},
                              description => $description,
                              publish     => 1)
                     or die "[err] ", $mt->LastError(), "\n";

            # set the category?
            if ($opts{catid}) {
                $mt->mt()->setPostCategories(
                              postid     => $id,
                              categories => [ {categoryId => $opts{catid}}])
                or die " [err] ", $mt->LastError(), "\n";

                # "edit" the post with no changes so 
                # that our category change activates.
                $mt->metaWeblog()->editPost( 
                              title       => $item->{title},
                              description => $description,
                              postid      => $id,
                              publish     => 1)
                     or die " [err] ", $mt->LastError(), "\n";
            }
        } else { print " Skipping (failed date check): '$item->{title}'.\n"; }
    }
    print "-" x 76, "\n"; # visual separator.
}

exit;