root/trunk/plagger/lib/Plagger/Plugin/CustomFeed/Mixi.pm

Revision 447 (checked in by miyagawa, 14 years ago)

Added plugin_id method to mean object ID, not class ID. Fixes #95

  • Property svn:keywords set to Id Revision
Line 
1 package Plagger::Plugin::CustomFeed::Mixi;
2 use strict;
3 use base qw( Plagger::Plugin );
4
5 use DateTime::Format::Strptime;
6 use Encode;
7 use WWW::Mixi;
8 use Time::HiRes;
9
10 sub plugin_id {
11     my $self = shift;
12     $self->class_id . '-' . $self->conf->{email};
13 }
14
15 sub register {
16     my($self, $context) = @_;
17     $context->register_hook(
18         $self,
19         'subscription.load' => \&load,
20     );
21 }
22
23 sub load {
24     my($self, $context) = @_;
25     $self->{mixi} = WWW::Mixi->new($self->conf->{email}, $self->conf->{password});
26     $self->{mixi}->cookie_jar($self->cache->cookie_jar);
27
28     my $feed = Plagger::Feed->new;
29        $feed->aggregator(sub { $self->aggregate(@_) });
30     $context->subscription->add($feed);
31 }
32
33 sub aggregate {
34     my($self, $context, $args) = @_;
35
36     my $start_url = 'http://mixi.jp/new_friend_diary.pl';
37     my $response  = $self->{mixi}->get($start_url);
38
39     if ($response->content =~ /action=login\.pl/) {
40         $context->log(debug => "Cookie not foud. Logging in");
41         $response = $self->{mixi}->post("http://mixi.jp/login.pl", {
42             next_url => "/new_friend_diary.pl",
43             email    => $self->conf->{email},
44             password => $self->conf->{password},
45             sticky   => 'on',
46         });
47         if (!$response->is_success || $response->content =~ /action=login\.pl/) {
48             $context->log(error => "Login failed.");
49             return;
50         }
51
52         # meta refresh, ugh!
53         if ($response->content =~ m!"0;url=(.*?)"!) {
54             $response = $self->{mixi}->get($1);
55         }
56     }
57
58     my $feed = Plagger::Feed->new;
59     $feed->type('mixi');
60     $feed->title('マイミクシィ最新日記');
61     $feed->link('http://mixi.jp/new_friend_diary.pl');
62
63     my $format = DateTime::Format::Strptime->new(pattern => '%Y/%m/%d %H:%M');
64
65     my @msgs = $self->{mixi}->parse_new_friend_diary($response);
66     my $items = $self->conf->{fetch_items} || 20;
67
68     my $i = 0;
69     my $blocked = 0;
70     for my $msg (@msgs) {
71         next unless $msg->{image}; # external blog
72         last if $i++ >= $items;
73
74         my $entry = Plagger::Entry->new;
75         $entry->title( decode('euc-jp', $msg->{subject}) );
76         $entry->link($msg->{link});
77         $entry->author( decode('euc-jp', $msg->{name}) );
78         $entry->date( Plagger::Date->parse($format, $msg->{time}) );
79
80         if ($self->conf->{show_icon} && !$blocked) {
81             my $owner_id = ($msg->{link} =~ /owner_id=(\d+)/)[0];
82             my $link = "http://mixi.jp/show_friend.pl?id=$owner_id";
83             $context->log(info => "Fetch icon from $link");
84
85             my $item = $self->cache->get_callback(
86                 "outline-$owner_id",
87                 sub {
88                     Time::HiRes::sleep( $self->conf->{fetch_body_interval} || 1.5 );
89                     my($item) = $self->{mixi}->get_show_friend_outline($link);
90                     $item;
91                 },
92                 '12 hours',
93             );
94             if ($item && $item->{image} !~ /no_photo/) {
95                 # prefer smaller image
96                 my $image = $item->{image};
97                    $image =~ s/\.jpg$/s.jpg/;
98                 $entry->icon({
99                     title => decode('euc-jp', $item->{name}),
100                     url   => $image,
101                     link  => $link,
102                 });
103             }
104         }
105
106         if ($self->conf->{fetch_body} && !$blocked && $msg->{link} =~ /view_diary/) {
107             $context->log(info => "Fetch body from $msg->{link}");
108             my $item = $self->cache->get_callback(
109                 "item-$msg->{link}",
110                 sub {
111                     Time::HiRes::sleep( $self->conf->{fetch_body_interval} || 1.5 );
112                     my($item) = $self->{mixi}->get_view_diary($msg->{link});
113                     $item;
114                 },
115                 '1 hour',
116             );
117             if ($item) {
118                 my $body = decode('euc-jp', $item->{description});
119                    $body =~ s!\n!<br />!g;
120                 for my $image (@{ $item->{images} }) {
121                     # xxx this should be $entry->enclosures
122                     $body .= qq(<div><a href="$image->{link}"><img src="$image->{thumb_link}" style="border:0" /></a></div>);
123                 }
124                 $entry->body($body);
125             } else {
126                 $context->log(warn => "Fetch body failed. You might be blocked?");
127                 $blocked++;
128             }
129         }
130
131         $feed->add_entry($entry);
132     }
133
134     $context->update->add($feed);
135 }
136
137 1;
138
139 __END__
140
141 =head1 NAME
142
143 Plagger::Plugin::CustomFeed::Mixi -  Custom feed for mixi.jp
144
145 =head1 SYNOPSIS
146
147     - module: CustomFeed::Mixi
148       config:
149         email: email@example.com
150         password: password
151         fetch_body: 1
152         show_icon: 1
153
154 =head1 DESCRIPTION
155
156 This plugin fetches your friends diary updates from mixi
157 (L<http://mixi.jp/>) and creates a custom feed.
158
159 =head1 CONFIGURATION
160
161 =over 4
162
163 =item email, password
164
165 Credential you need to login to mixi.jp.
166
167 =item fetch_body
168
169 With this option set, this plugin fetches entry body HTML, not just a
170 link to the entry. Defaults to 0.
171
172 =item fetch_body_interval
173
174 With C<fetch_body> option set, your Plagger script is recommended to
175 wait for a little, to avoid mixi.jp throttling. Defaults to 1.5.
176
177 =item show_icon: 1
178
179 With this option set, this plugin fetches users buddy icon from
180 mixi.jp site, which makes the output HTML very user-friendly.
181
182 =back
183
184 =head1 SCREENSHOT
185
186 L<http://blog.bulknews.net/mt/archives/plagger-mixi-icon.gif>
187
188 =head1 AUTHOR
189
190 Tatsuhiko Miyagawa
191
192 =head1 SEE ALSO
193
194 L<Plagger>, L<WWW::Mixi>
195
196 =cut
Note: See TracBrowser for help on using the browser.