Plagger::Plugin::Subscription::LivedoorReader - Synchronize livedoor Reader with JSON API


Plagger documentation Contained in the Plagger distribution.

Index


Code Index:

NAME

Top

Plagger::Plugin::Subscription::LivedoorReader - Synchronize livedoor Reader with JSON API

SYNOPSIS

Top

  - module: Subscription::LivedoorReader
    config:
      username: your-livedoor-id
      password: your-password
      mark_read: 1

DESCRIPTION

Top

This plugin allows you to synchronize your subscription using Livedoor Reader JSON API.

CONFIGURATION

Top

username, password

Your username & password to use with livedoor Reader.

Note that you don't have to supply username and password if you set global cookie_jar in your configuration file and the cookie_jar contains a valid login session there, such as:

  global:
    user_agent:
      cookies: /path/to/cookies.txt

See Plagger::Cookies for details.

mark_read

mark_read specifies whether this plugin marks as read the items you synchronize. With this option set to 0, you will get the duplicated updates every time you run Plagger, until you mark them unread using Livedoor Reader web interface.

AUTHOR

Top

Tatsuhiko Miyagawa

SEE ALSO

Top

Plagger, Plagger::Plugin::Subscription::Bloglines, http://reader.livedoor.com/


Plagger documentation Contained in the Plagger distribution.

package Plagger::Plugin::Subscription::LivedoorReader;
use strict;
use base qw( Plagger::Plugin );

use JSON::Syck;
use URI;
use Plagger::Mechanize;
use Plagger::Util;

sub plugin_id {
    my $self = shift;
    $self->class_id . '-' . $self->conf->{username};
}

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

    $self->init_reader;
    $context->register_hook(
        $self,
        'subscription.load' => \&notifier,
    );
}

sub init_reader {
    my $self = shift;
    $self->{mech} = Plagger::Mechanize->new(cookie_jar => $self->cookie_jar);

    unless (defined($self->conf->{username}) && defined($self->conf->{password})) {
        Plagger->context->error("username and/or password is missing");
    }
}

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

    $self->{mech}->get("http://rpc.reader.livedoor.com/notify?user=" . $self->conf->{username});
    my $content = $self->{mech}->content;

    # copied from WebService/Bloglines.pm

    # |A|B| where A is the number of unread items
    $content =~ /\|([\-\d]+)|(.*)|/
	or $context->error("Bad Response: $content");

    my($unread, $url) = ($1, $2);

    # A is -1 if the user email address is wrong.
    if ($unread == -1) {
	$context->error("Bad username: " . $self->conf->{username});
    }

    return unless $unread;

    $context->log(info => "You have $unread unread item(s) on livedoor Reader.");

    my $feed = Plagger::Feed->new;
    $feed->aggregator(sub { $self->sync(@_) });
    $context->subscription->add($feed);
}

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

    my $mark_read = $self->conf->{mark_read};
       $mark_read = 1 unless defined $mark_read;

    $self->login_reader();

    my $subs = $self->_request("/api/subs", { unread => 1 }) || [];

    for my $sub (@$subs) {
        $context->log(debug => "get unread items of $sub->{subscribe_id}");
        my $data = $self->_request("/api/unread", { subscribe_id => $sub->{subscribe_id} }) or next;

        my $feed = Plagger::Feed->new;
        $feed->type('livedoorReader');
        $feed->title($data->{channel}->{title});
        $feed->link($data->{channel}->{link});
        $feed->url($data->{channel}->{feedlink});
        $feed->image({ url => $data->{channel}->{image} || $sub->{icon} });
        $feed->meta->{livedoor_reader_id} = $sub->{subscribe_id};
        $feed->meta->{rate} = $sub->{rate};
        $feed->add_tag($_) for @{$sub->{tags}};
        $feed->add_tag($sub->{folder}) if $sub->{folder};
        $feed->updated( Plagger::Date->from_epoch($sub->{modified_on}) ) if $sub->{modified_on};
        $feed->description($data->{channel}->{description});
        $feed->meta->{livedoor_reader_subscribers_count} = $data->{channel}->{subscribers_count};

        for my $item ( @{$data->{items}} ) {
            my $entry = Plagger::Entry->new;
            $entry->title($item->{title});
            $entry->author($item->{author}) if $item->{author};
            $entry->link($item->{link});
            # TODO support enclosure
            $entry->tags([ $item->{category} ]) if $item->{category};
            $entry->date( Plagger::Date->from_epoch($item->{modified_on}) )
                if $item->{modified_on};
            $entry->meta->{livedoor_reader_item_id} = $item->{id};
            $entry->feed_link($feed->link);
            $entry->body($item->{body});

            $feed->add_entry($entry);
        }

        $self->_request("/api/touch_all", { subscribe_id => $sub->{subscribe_id} })
            if $mark_read;

        $context->update->add($feed);
    }
}

sub login_reader {
    my $self = shift;

    local $^W; # input type="search" warning
    $self->{mech}->get("http://reader.livedoor.com/reader/");

    if ($self->{mech}->content =~ /name="loginForm"/) {
        Plagger->context->log(debug => "Logging in to Livedoor Reader");
        $self->{mech}->submit_form(
            form_name => 'loginForm',
            fields => {
                livedoor_id => $self->conf->{username},
                password    => $self->conf->{password},
            },
        );

        if ( $self->{mech}->content =~ /class="headcopy"/ ) {
            Plagger->context->error("Failed to login using username & password");
        }
    }

    $self->{mech}->cookie_jar->scan(
        sub {
            my($key, $val) = @_[1,2];
            if ($key =~ /_sid/) {
                $self->{apikey} = $val;
                return;
            }
        },
    );
}

sub _request {
    my($self, $method, $param) = @_;

    my $uri = URI->new_abs($method, "http://reader.livedoor.com/");
    $uri->query_form(%$param, ApiKey => $self->{apikey});

    $self->{mech}->get($uri->as_string);

    if ($self->{mech}->status == 200) {
        return JSON::Syck::Load($self->{mech}->content);
    }

    return;
}

1;

__END__