Plagger::Plugin::Publish::Bloglines

sbmしたのを勝手に購読したいのです。
config.yamlの文字コードをutf8にしておけば日本語folder指定も可能です。複数feed提供されているentryの場合の選択処理(first/last/recent)は正直微妙です。
はてなはSBSとFeed Reader提供しているからいつかやるんだろうな、と結構前から思ってたのですが一向にその気配は感じられません。地味だしなあ。

package Plagger::Plugin::Publish::Bloglines;
use strict;
use base qw( Plagger::Plugin );

use WWW::Mechanize;
use DateTime;
use HTML::Entities;
use Encode;

sub register {
    my($self, $context) = @_;
    $context->register_hook(
        $self,
        'publish.init'        => \&initialize,
        'publish.entry.fixup' => \&publish_entry,
    );
}

sub initialize {
    my($self, $context, $args) = @_;
    $self->{mech} = WWW::Mechanize->new();
    $self->{mech}->get('http://bloglines.com/login');
    $self->{mech}->submit_form(
        form_name => "login",
        fields => {
            email    => $self->conf->{email},
            password => $self->conf->{password},
        },
    );

    my $error_message
         = 'You have entered an incorrect email address or password';
    if ($self->{mech}->content =~ /$error_message/) {
        $context->log(error => $error_message);
        return;
    }
}

sub publish_entry {
    my($self, $context, $args) = @_;

    $self->{mech}->get('http://www.bloglines.com/sub');
    $self->{mech}->submit_form(
        form_number => 2,
        fields      => {
            url         => $args->{entry}->link,
        },
        button      => 'submiturl',
    );
    my $error_message
        = 'No feeds were found. Please verify that the website publishes an RSS feed.';
    if ($self->{mech}->content =~ /$error_message/) {
        $context->log(error => $error_message);
        return;
    }

    my @feeds;
    my @site_ids;
    my $push_feed_data = sub {
        my $month_string_to_number = {
            'Jan' => 1,
            'Feb' => 2,
            'Mar' => 3,
            'Apr' => 4,
            'May' => 5,
            'Jun' => 6,
            'Jul' => 7,
            'Aug' => 8,
            'Sep' => 9,
            'Oct' => 10,
            'Nov' => 11,
            'Dec' => 12,
        };

        if ($#site_ids >= 7) {
            my ($a_day_of_the_week, $month, $day, $year, $hour, $min, $ampm, $id)
                = splice(@site_ids, 0, 8);
            my $feed_data = {
                id       => $id,
                datetime => DateTime->new(
                    year   => $year,
                    month  => $month_string_to_number->{$month},
                    day    => $day,
                    hour   => $hour,
                    minute => $min,
                ),
            };
            if ($ampm eq 'PM') { $feed_data->{datetime}->add(hours => 12); }
            push @feeds, $feed_data;
            return 1;
        }
        else {
            return 0;
        }
    };

    @site_ids = $self->{mech}->content =~ m!</a>\]<br>\n (.*?)\, (.*?) (\d*) (\d{4}) (\d*):(\d*) (AM|PM)\n<input type="checkbox" name="siteid\.(\d+)\.id"!g;
    if (&$push_feed_data == 0) {
        @site_ids = $self->{mech}->content =~ m!</a>\]<br>\n (.*?)\, (.*?) (\d*) (\d{4}) (\d*):(\d*) (AM|PM)\n<input type="hidden" name="siteid\.(\d+)\.id"!;
        if (&$push_feed_data == 0) {
            $context->log(warn => "This feed is already subscribed.");
            return;
        }
    }

    my $feed;
    my $select = $self->conf->{select} || 'first';
    if ($select eq 'last') {
        $feed = $feeds[-1];
    }
    elsif ($select eq 'recent') {
        @feeds = sort{ DateTime->compare($b->{datetime}, $a->{datetime}) } @feeds;
        $feed = $feeds[0];
    }
    else {
        # = first,etc
        $feed = $feeds[0];
    }

    my $folder_id = {};
    $folder_id->{'top_level'} = 0;   # root folder
    if ($self->{mech}->content =~ m!.*<select name="folder" onChange="checkSub\(\)">\n(.*)</select>.*!s) {
        my @folder_list = $1 =~ m!<option value="(\d*?)">(.*?)</option>!g;
        @folder_list = map {
            decode_entities($_);
            $_ = Encode::encode("utf8", $_);
        } @folder_list;

        while (@folder_list) {
            my ($value, $name) = splice(@folder_list, 0, 2);
            $folder_id->{$name} = $value;
        }
    } else {
        $context->log( debug => "folders not found." );
    }
    
    $self->{mech}->submit_form(
        form_name => 'subform',
        fields    => {
            'siteid.'.$feed->{id}.'.id'
                            =>  $feed->{id},
            'folder'        =>  $folder_id->{
                                    $self->conf->{folder} || 'top_level' },
            'NewItemsOnly'  =>  $self->updated_items_id(
                                    $self->conf->{updated_items} ),
            'Display'       =>  $self->display_preferences_id(
                                    $self->conf->{display_prefarences} ),
            'Ignore'        =>  $self->yesno_to_onoff(
                                    $self->conf->{monitered_by_bloglines_notifier} ),
            'DisplayMobile' =>  $self->yesno_to_onoff(
                                    $self->conf->{displayed_in_mobile} ),
            'Access'        =>  $self->access_id(
                                    $self->cond->{access} ),
            'Notes'         =>  $self->conf->{notes}
                                || "subscribed with Plagger::Plugin::Publish::Bloglines.",
        },
    );

    my $sleeping_time = $context->conf->{interval} || 3;
    $context->log( info => "Post entry success. sleep $sleeping_time." );
    sleep( $sleeping_time );
}

sub updated_items_id {
    my($self, $value) = @_;
    my $table = {
        'ignore'         => 1,
        'display_as_new' => 0,
    };
    return $table->{$value || 'display_as_new'};
}

sub display_preferences_id {
    my($self, $value) = @_;
    my $table = {
        'default'                 => 0,
        'complete_entries'        => 1,
        'summaries_if_availables' => 2,
        'titles'                  => 3,
    };
    return $table->{$value || 'default'};
}

sub access_id {
    my($self, $value) = @_;
    my $table = {
        'public'  => 1,
        'private' => 0,
    };
    return $table->{$value || 'public'};
}

sub yesno_to_onoff {
    my($self, $value) = @_;
    my $table = {
        'yes' => 'on',
        'no'  => 'off',
    };
    return $table->{$value || 'on'};
}


1;

__END__

=head1 NAME

Plagger::Plugin::Publish::Bloglines - Subscribe feeds with Bloglines

=head1 SYNOPSIS

plugins
  - module: Publish::Bloglines
    config:
      email: your-email
      password: your-password
      interval: 2
      folder: recent_subscribe
      updated_items: dispaly_as_new
      display_prefarences: default
      monitered_by_bloglines_notifier: yes
      displayed_in_mobile: yes
      access: public
      notes: hoge hoge fuga hoge
      select: last

=head1 DESCRIPTION

This plugin subscribes feed with Bloglines.

=over 4

=item email, password

Your login and password for logging in Bloglines.

=item interval

Interval (as seconds) to sleep after posting each feed. Defaults to 3.

=item folder

Options. "Folder". Defaults to "top_level".

=item updated_items

Options. "Updated Items". Defaults to "display_as_new".

=item display

Options. "Display Prefarences". Defaults to "default".

=item ignore

Options. "Moniterd By Bloglines Notifier". Defaults to "yes".

=item display_mobile

Options. "Display in Mobile". Defaults to "yes".

=item access

Options. "Access". Defaults to "public".

=item notes

Options. "Notes". Defaults to "subscribed with Plagger::Plugin::Publish::Bloglines.".

=item select

Subscribing feed select method if some feeds found in an url.

Values are first,last, and recent. Defaults to 'first'.

first: Select first found feed.

last: Select last found feed.

recent: Select recent updated feed.

=cut

=head1 AUTHOR

Tsutomu Koyachi

=head1 SEE ALSO

L<Plagger>, L<WWW::Mechanize>, L<http://bloglines.com/>

=cut