fastladder で各種RSSとMixiの新着を見るようになって、うはうはなのですが、うはうはなりに気になるところが出てきます。
そこで、fastladder-crawler の CustomFeed::Mixi に手を入れて、トピック単位にfeedができるようにしてみました。
Index: lib/Plagger/Plugin/Store/Fastladder.pm
===================================================================
--- lib/Plagger/Plugin/Store/Fastladder.pm (リビジョン 2024)
+++ lib/Plagger/Plugin/Store/Fastladder.pm (作業コピー)
@@ -57,6 +57,7 @@
title => $args->{feed}->title || '',
description => $args->{feed}->description || '',
});
+ $feed->modified_on( $args->{feed}->updated || $now );
$feed->updated_on( $args->{feed}->updated || $now );
$feed->created_on( $now ) unless $feed->in_storage;
$feed->insert_or_update;
Index: lib/Plagger/Plugin/CustomFeed/MixiScraper.pm
===================================================================
--- lib/Plagger/Plugin/CustomFeed/MixiScraper.pm (リビジョン 2024)
+++ lib/Plagger/Plugin/CustomFeed/MixiScraper.pm (作業コピー)
@@ -85,7 +85,11 @@
my($self, $context, $args) = @_;
for my $type (@{$self->conf->{feed_type} || ['FriendDiary']}) {
$context->error("$type not found") unless $MAP->{$type};
- $self->aggregate_feed($context, $type, $args);
+ if ($type eq 'BBS') {
+ $self->aggregate_feed_bbs($context, $type, $args);
+ } else {
+ $self->aggregate_feed($context, $type, $args);
+ }
}
}
sub aggregate_feed {
@@ -115,7 +119,9 @@
$entry->link($msg->{link});
$entry->author($msg->{name});
$entry->date( Plagger::Date->parse($format, $msg->{time}) );
-
+ if ($entry->date) {
+ $entry->date->set_time_zone('Asia/Tokyo');
+ }
if ($self->conf->{show_icon} && !$blocked && defined $MAP->{$type}->{icon}) {
my $owner_id = $msg->{link}->query_param($MAP->{$type}->{icon});
$context->log(info => "Fetch icon of id=$owner_id");
@@ -161,16 +167,16 @@
my $body = $item->{description};
$body =~ s!(\r\n?|\n)!<br />!g;
for my $image (@{ $item->{images} || [] }) {
- $body .= qq(<div><a href="$image->{link}"><img src="$image->{thumb_link}" style="border:0" /></a></div>);
- my $enclosure = Plagger::Enclosure->new;
- $enclosure->url($image->{thumb_link});
- $enclosure->auto_set_type;
- $enclosure->is_inline(1);
- $entry->add_enclosure($enclosure);
+ my $imagelink = $image->{link};
+ $imagelink =~ s!.*(show_.*?_picture\.pl.*?)'.*!http://mixi.jp/$1!;
+ $body .= qq(<a href="$imagelink"><object type="text/html" data="$imagelink" >$imagelink</object></a>);
}
$entry->body($body);
$entry->date( Plagger::Date->parse($format, $item->{time}) );
+ if ($entry->date) {
+ $entry->date->set_time_zone('Asia/Tokyo');
+ }
if ($self->conf->{fetch_comment}) {
for my $comment (@{ $item->{comments} || [] }) {
my $c = Plagger::Entry->new;
@@ -179,6 +185,9 @@
$c->link($comment->{link});
$c->author($comment->{name});
$c->date( Plagger::Date->parse($format, $comment->{time}) );
+ if ($c->date) {
+ $c->date->set_time_zone('Asia/Tokyo');
+ }
push @comments, $c;
}
}
@@ -197,6 +206,124 @@
$context->update->add($feed);
}
+sub aggregate_feed_bbs {
+ my($self, $context, $type, $args) = @_;
+
+
+ my $format = DateTime::Format::Strptime->new(pattern => '%Y-%m-%d %H:%M');
+
+ my $meth = $MAP->{$type}->{get_list};
+ my @msgs = $self->{mixi}->$meth->parse;
+ my $items = $self->conf->{fetch_items} || 20;
+ $self->log(info => 'fetch ' . scalar(@msgs) . ' entries');
+
+
+ my $i = 0;
+ my $blocked = 0;
+ for my $msg (@msgs) {
+ last if $i++ >= $items;
+
+ my $feed = Plagger::Feed->new;
+ $feed->type('mixi');
+ my $subject = $msg->{subject};
+ $subject =~ s/\([0-9]*\)$//;
+ $feed->title($subject);
+ $feed->description('Mixi: ' . $msg->{name});
+ my $link = $msg->{link};
+ $link =~ s/&comment_count=[0-9]*//;
+ $feed->link($link);
+
+ my $entry = Plagger::Entry->new;
+ $entry->title($msg->{subject});
+ $entry->link($msg->{link});
+ $entry->author($msg->{name});
+ $entry->date( Plagger::Date->parse($format, $msg->{time}) );
+ if ($entry->date) {
+ $entry->date->set_time_zone('Asia/Tokyo');
+ }
+ if ($self->conf->{show_icon} && !$blocked && defined $MAP->{$type}->{icon}) {
+ my $owner_id = $msg->{link}->query_param($MAP->{$type}->{icon});
+ $context->log(info => "Fetch icon of id=$owner_id");
+
+ my $item = $self->cache->get_callback(
+ "outline-$owner_id",
+ sub {
+ Time::HiRes::sleep( $self->conf->{fetch_body_interval} || 1.5 );
+ my $item = $self->{mixi}->show_friend->parse(id => $owner_id)->{outline};
+ $item;
+ },
+ '12 hours',
+ );
+ if ($item && $item->{image} !~ /no_photo/) {
+ # prefer smaller image
+ my $image = $item->{image};
+ $image =~ s/\.jpg$/s.jpg/;
+ $entry->icon({
+ title => $item->{name},
+ url => $image,
+ link => $item->{link},
+ });
+ }
+ }
+
+ my @comments;
+ if ($self->conf->{fetch_body} && !$blocked && $msg->{link} =~ /view_/ && defined $MAP->{$type}->{get_detail}) {
+ # view_enquete is not implemented and probably
+ # won't be implemented as it seems redirected to
+ # reply_enquete
+ next if $msg->{link} =~ /view_enquete/;
+ $context->log(info => "Fetch body from $msg->{link}");
+ my $item = $self->cache->get_callback(
+ "item-".$msg->{link},
+ sub {
+ Time::HiRes::sleep( $self->conf->{fetch_body_interval} || 1.5 );
+ my $item = $self->{mixi}->parse($msg->{link});
+ $item;
+ },
+ '12 hours',
+ );
+ if ($item) {
+ my $body = $item->{description};
+ $body =~ s!(\r\n?|\n)!<br />!g;
+ $body =~ s!<a href=.*?'(.*?)'.*?</a>!<object type="text/html" data="http://mixi.jp/$1">http://mixi.jp/$1</object>!g;
+ $body =~ s!&!&!g;
+ $entry->body($body);
+
+ $entry->date( Plagger::Date->parse($format, $item->{time}) );
+ if ($entry->date) {
+ $entry->date->set_time_zone('Asia/Tokyo');
+ }
+ if ($self->conf->{fetch_comment}) {
+ for my $comment (@{ $item->{comments} || [] }) {
+ my $c = Plagger::Entry->new;
+ $c->title($entry->title . ': '. $comment->{subject});
+ $body = $comment->{description};
+ $body =~ s!<a href=.*?'(.*?)'.*?</a>!<object type="text/html" data="http://mixi.jp/$1">http://mixi.jp/$1</object>!g;
+ $body =~ s!&!&!g;
+ $c->body($body);
+ $c->link($comment->{link});
+ $c->author($comment->{name});
+ $c->date( Plagger::Date->parse($format, $comment->{time}) );
+ if ($c->date) {
+ $c->date->set_time_zone('Asia/Tokyo');
+ }
+ push @comments, $c;
+ }
+ }
+ } else {
+ $context->log(warn => "Fetch body failed. You might be blocked?");
+ $blocked++;
+ }
+ }
+
+ $feed->add_entry($entry);
+ for my $comment ( @comments ) {
+ $feed->add_entry($comment);
+ }
+ $context->update->add($feed);
+ }
+}
+
1;
__END__
やっていることは、以下です。
- Store::Fastladderで、feedsのmodified_onを更新するように。(これは別に不要だけど)
- CustomFeed::MixiScraperで、feed_typeが'BBS'のときだけaggregate_feedじゃなくてaggregate_feed_bbsを呼ぶように。(汚くてごめんなさいごめんなさいごめんなさい)
- ところどころでset_time_zone('Asia/Tokyo')してるのは、作者さんに「yamlが正しければ不要なはず」、と言われたんだけど何故か私の環境では必要だったので。
- aggregate_feedをコピーしてaggregate_feed_bbsを作成し、ループの中でPlagger::Feedを作るように変更。
- gmailじゃなくてfastladderで読むので、画像はenclosureではなくてobjectタグでリンクを埋め込み(これも、plaggerの思想的にあり得ないよね。enclosureも残しつつうまくやれるように別のフィルタでも書くべきかなあ)
最初、画像の部分は寝ぼけてimgタグを使おうとしたんだけど、mixiの画像表示cgiは画像そのものではなくてhtmlを返すので、うまくいかない。
で、苦肉の策でiframeかobjectタグでhtmlを埋め込もうと考えたわけだけど、mixiは画像のサイズを教えてくれないので、objectタグにwidthやheightが書けない。
もちっと詳しく書くと、たとえばコミュニティの掲示板の場合、画像は
<a href="javascript:void(0);" onClick="MM_openBrWindow('show_bbs_comment_picture.pl?bbs_id=xxx&id=yyy&comm_id=zzz&number=nnn','pict','width=650,height=660,toolbar=no,scrollbars=yes,left=5,top=5')"><img src="http://ic61.mixi.jp/p/RANDOM_URL.jpg" border="0" alt="" /></a>
のような形式になっている。
gmailに送っていたときは、img src に書いてあるサムネイル画像を、Filter::FetchEnclosureで取ってきてメールに添付していたのだけれど、Store::Fastladderではそう言うわけにはいかないので、対応方法は二つ。
- FetchEnclosureと同じようにローカルに画像をfetchして、fastladderと同じサーバで閲覧できるようにURLを生成する。
- 画像はfastladderで見るときにmixiのサーバに取りに行く
で、今回は後者の方法を採用した。
mixiの画像のURLは一時的なものなので、imgタグを書けないとすると、iframeやobjectタグでshow_bbs_comment_picture.plを埋め込むしかない。
しかし、上記URLには画像のサイズの情報が入っていないし、show_bbs_comment_picture.plが返すhtmlにもサイズの情報は入っていない。
結局、画像のサイズが欲しかったら一度show_bbs_comment_picture.plを呼び出して、その結果をscrapeして、実際の画像ファイルのURLを取り出して、画像を取得して、までやらないと画像のサイズがわからない。(サムネイルのサイズは1段階少なくて取得できるけど、サムネイルを表示させるURLは存在しないので)
そこまでやるのは面倒だったので、今回はサイズ指定なし。これだと、Firefoxだと適当なサイズで表示されてスクロールバーが出る。IEだと表示されないそうだ。(試していないけど)
で、ここまでやったらfastladderで写真が見れると思ったんだけど、何も表示されない。
なんでかな~と思ってfastladderを見ていたら、lib/string_utils.rb の scrub_htmlでALLOW_TAGSにあるタグ以外は消しているように見える。
ALLOW_TAGSはconfig/initializers/constants.rb で定義されていたので、
===================================================================
--- constants.rb (revision 28)
+++ constants.rb (working copy)
@@ -1,8 +1,8 @@
-MAX_UNREAD_COUNT = 10
+MAX_UNREAD_COUNT = 200
DEFAULT_FAVICON = "#{RAILS_ROOT}/public/img/icon/default.png"
SUBSCRIBE_LIMIT = 5000
SAVE_PIN_LIMIT = 100
CRAWL_INTERVAL = 60
-ALLOW_TAGS = %w(a i u b em strong table tr td th tbody font center div pre code blockquote ins del img br p hr ul li ol dl dt dd)
-ALLOW_ATTRIBUTES = %w(src width height border alt title href color size align)
+ALLOW_TAGS = %w(a i u b em strong table tr td th tbody font center div pre code blockquote ins del img br p hr ul li ol dl dt dd object)
+ALLOW_ATTRIBUTES = %w(src width height border alt title href color size align type data)
みたいな感じでobjectタグとtype, data属性を許可してみた。
objectタグを許可するのは果てしなくセキュリティホールな気がするので、もっと良いやり方がありそうだけど。
とりあえずマイミクの日記の写真と、コミュニティの掲示板のコメントの写真は見れることを確認したけど、このソースだとコミュニティのトピック自身の画像が見れない気がするな。