root/branches/feature-server/plagger/lib/Plagger/Plugin/CustomFeed/Frepa.pm

Revision 856 (checked in by miyagawa, 3 years ago)

merge from trunk to plagger-server for Enclosures support and such. Sorry for the big commit

Line 
1 package Plagger::Plugin::CustomFeed::Frepa;
2 use strict;
3 use base qw( Plagger::Plugin );
4
5 use DateTime::Format::Strptime;
6 use Encode;
7 use Time::HiRes;
8 use UNIVERSAL::require;
9 use Plagger::Mechanize;
10
11 sub plugin_id {
12     my $self = shift;
13     $self->class_id . '-' . $self->conf->{livedoor_id};
14 }
15
16 sub register {
17     my ($self, $context) = @_;
18     $context->register_hook(
19         $self,
20         'subscription.load' => \&load,
21     );
22 }
23
24 sub load {
25     my ($self, $context) = @_;
26
27     $self->{mech} = Plagger::Mechanize->new(cookie_jar => $self->cookie_jar);
28
29     my $feed = Plagger::Feed->new;
30     $feed->aggregator(sub { $self->aggregate(@_) });
31     $context->subscription->add($feed);
32 }
33
34 sub aggregate {
35     my ($self, $context, $args) = @_;
36
37     unless ($self->login(livedoor_id => $self->conf->{livedoor_id}, password => $self->conf->{password})) {
38         $context->log(error => "Login to frepa failed.");
39         return;
40     }
41
42     $context->log(info => 'Login to frepa succeeded.');
43
44     my $feed_type = $self->conf->{feed_type} || [ qw(FriendDiary FriendStatus RecentComment) ];
45     for my $plugin (@$feed_type) {
46         my $plugin = (ref $self || $self) . "::$plugin";
47         $plugin->require or $context->error($@);
48         $self->aggregate_by_plugin($context, $plugin, $args);
49     }
50 }
51
52 sub aggregate_by_plugin {
53     my ($self, $context, $plugin, $args) = @_;
54
55     my $feed = Plagger::Feed->new;
56     $feed->type('frepa');
57     $feed->title($plugin->title);
58     $feed->link($plugin->start_url);
59
60     my $format = DateTime::Format::Strptime->new(pattern => '%Y/%m/%d %H:%M');
61
62     my @msgs = $plugin->get_list($self->{mech}, $self);
63     my $items = $self->conf->{fetch_items} || 20;
64
65     my $i = 0;
66     my $blocked = 0;
67     for my $msg (@msgs) {
68         last if $i++ >= $items;
69
70         my $entry = Plagger::Entry->new;
71         $entry->title($msg->{subject});
72         $entry->link($msg->{link});
73         $entry->author($msg->{name});
74         $entry->date( Plagger::Date->parse($format, $msg->{time}) );
75
76         if ($self->conf->{fetch_body} && !$blocked and $plugin->can('get_detail')) {
77             $context->log(info => "Fetch body from $msg->{link}");
78             my $item = $self->cache->get_callback(
79                 "item-$msg->{link}",
80                 sub {
81                     Time::HiRes::sleep( $self->conf->{fetch_body_interval} || 1.5 );
82                     $plugin->get_detail($msg->{link}, $self->{mech});
83                 },
84                 "1 hour",
85             );
86             if ($item) {
87                 my $body = $item->{description};
88                    $body =~ s!<br>!<br />!g;
89                 $entry->body($body);
90                 $entry->title($item->{subject}); # replace with full title
91             } else {
92                 $context->log(warn => "Fetch body failed. You might be blocked?");
93                 $blocked++;
94             }
95         }
96
97         if ($self->conf->{show_icon} && !$blocked) {
98             my $item = $self->fetch_icon($msg->{user_link});
99             if ($item && $item->{image} !~ /no_photo/) {
100                 $entry->icon({
101                     title => $item->{name},
102                     url   => $item->{image},
103                     link  => $msg->{user_link},
104                 });
105             }
106         }
107
108         $feed->add_entry($entry);
109     }
110
111     $context->update->add($feed);
112 }
113
114 sub fetch_icon {
115     my($self, $url) = @_;
116
117     Plagger->context->log(info => "Fetch icon from $url");
118     $self->cache->get_callback(
119         "icon-$url",
120         sub { $self->get_top($url) },
121         '1 day',
122     );
123 }
124
125 sub login {
126     my $self = shift;
127     my %args = @_;
128
129     my $start_url = 'http://www.frepa.livedoor.com/';
130     my $res = $self->{mech}->get($start_url);
131     return unless $self->{mech}->success;
132
133     if ($self->{mech}->content =~ /loginside/) {
134         unless ($args{livedoor_id} && $args{password}) {
135             Plagger->context->log(error => "Error logging in using existent Cookies. Your User-Agent (" . $self->{mech}->agent . ") should strictly match with the UA used with the Cookies.");
136             return;
137         }
138
139         Plagger->context->log(debug => "cookie not found. logging in");
140         $self->{mech}->submit_form(
141             fields => {
142                 livedoor_id => $args{livedoor_id},
143                 password    => $args{password},
144                 auto_login  => 'on',
145             },
146         );
147         $self->{mech}->submit;
148         return unless $self->{mech}->success;
149         return if $self->{mech}->content =~ /loginside/;
150     }
151
152     return 1;
153 }
154
155 sub get_top {
156     my $self = shift;
157     my $link = shift;
158
159     my $item = {};
160     my $res = $self->{mech}->get($link);
161     return $item unless $self->{mech}->success;
162
163     my $html = decode('euc-jp', $self->{mech}->content);
164
165     chomp( my $re  = decode('utf-8', $self->top_re) );
166     if ($html =~ /$re/s) {
167         $item->{image} = $1;
168         $item->{name}  = $2;
169     }
170
171     return $item;
172 }
173
174 sub top_re {
175     return <<'RE';
176 <a href="http://(?:frepa\.livedoor\.com/.*?/|www\.frepa\.livedoor\.com/)"(?: rel="popup")?><img src="(http://img\d+\.(?:ico\.frepa\.livedoor\.com/member_photo/|bbs\.frepa\.livedoor\.com/community_board/).*?\.(?:jpe?g|JPE?G|gif|GIF|png|PNG))" border="0"></a>
177 </small>
178 .*?
179 <div id="namebody"><small><strong>(.*?)....</strong>
180 RE
181 }
182
183 1;
184
185 __END__
186
187 =head1 NAME
188
189 Plagger::Plugin::CustomFeed::Frepa - Custom feed for livedoor Frepa
190
191 =head1 SYNOPSIS
192
193   - module: CustomFeed::Frepa
194     config:
195       livedoor_id: your-id
196       password: password
197       fetch_body: 1
198       show_icon: 1
199       feed_type:
200         - FriendDiary
201         - FriendStatus
202         - RecentComment
203
204 =head1 DESCRIPTION
205
206 This plugin fetches your friend blog updates from livedoor Frepa
207 (L<http://frepa.livedoor.com/>) and creates a custom feed.
208
209 =head1 CONFIGURATION
210
211 See L<Plagger::Plugin::CustomFeed::Mixi> for C<fetch_body>,
212 C<fetch_body_interval> and C<show_icon>.
213
214 Note that you don't have to supply livedoor_id and password if you set
215 global cookie_jar in your configuration file and the cookie_jar
216 contains a valid login session there, such as:
217
218   global:
219     user_agent:
220       cookies: /path/to/cookies.txt
221
222 See L<Plagger::Cookies> for details.
223
224
225 =head1 AUTHOR
226
227 Kazuhiro Osawa
228
229 Tokuhiro Matsuno
230
231 Tatsuhiko Miyagawa
232
233 =head1 SEE ALSO
234
235 L<Plagger>, L<Plagger::Plugin::CustomFeed::Mixi>, L<Plagger::Mechanize>,
236 L<http://frepa.livedoor.com/>
237
238 =cut
Note: See TracBrowser for help on using the browser.