Googleで「はらへった」と検索するとピザが届くようにするまで

日曜は、Plagger を初めてちゃんとさわってみました。

Plagger の概略は、miyagawaさん自身による このYAPC::NAのPDF がわかりやすいです。要するに、 『ネットからフィードを集める → (ごにょごにょする) → 出力する』をするものです。

plagger

この「集める」や「ごにょごにょ」や「出力」の部分に、ブロックを重ねるようにしてモジュールを重ね、自分の好きな起承転結にできるという点が実に Perl ぽい着想です。風がふけば桶屋がもうかるフレームワークと呼んでいます。

Subscription::Bloglines / Publish::Gmail

まあ、まずは普通に使ってみようということで、「Bloglines から集める」→ (エントリごとに分割、RSS 広告除去、本文の内容をできるだけ収集→ Bloglines、Delicious へのリンク付与、はてブ登録数付与) → Gmail に送信 をやってみました。

Plaggerはこれを、以下のような設定を書くだけでできるのがかっこいい。モジュールをたたた、と重ねていく感じ。

global:
  timezone: Asia/Tokyo
  log:
    level: error

plugins:
  - module: Subscription::Bloglines
    config:
      username: my-blogline-id@example.com
      password: p4ssw0rd
      mark_read: 1 
      fetch_meta: 1
   
  - module: Filter::BreakEntriesToFeeds
  - module: Filter::StripRSSAd
  - module: Filter::ResolveRelativeLink
  - module: Filter::EntryFullText
  
  - module: Widget::BloglinesSubscription
  - module: Widget::Delicious
  - module: Widget::HatenaBookmarkUsersCount
  
  - module: Publish::Gmail
    config:
      mailto:   your-gmail-id+plagger@gmail.com
      mailfrom: your-gmail-id@gmail.com
      mailroute:
        via: sendmail

Bloglines で読むのと比べて、データを後から検索できるという頼もしさがあり、大量の未読がたまったときの圧迫感がだいぶなくなりました。

ちなみに、Bloglines の「未読にする」や、LDRのピンに相当するものとして、★を使ってます。Gmail でショートカットキーをONにしてると、s で★を付けられます。jn で読み進めつつ、あとで読もうと思うときは s。これでスターフォルダにためて、スターフォルダで見おわったら★を外してます。

CustomFeed::GoogleSearchHistory / Notify::Pizza

Plagger の開発に関係している Perl ハカーたちがかなり熱いのと、Plagger Plugin が簡単に書けることから、おもしろい Plagger ネタがいろいろ出てきています。

たとえば、出力の部分で、CDトレイを開けるモジュールに渡して、「新しいニュースが入る → CDトレイがガチャーと開いてお知らせ!」とか、入力部分に MacBook に付いてる急な動きを察知するセンサーを使ったモジュールや、カメラやメールや「しゃべる」とかシャットダウンするというプラグインを出力部分に持ってきて、「置いといたMacBookが盗まれそうになったらカメラでそいつの写真を撮り、自分の携帯に送信、こら!と言った後シャットダウン」みたいなネタもあります。ていうか両方とも同じ人ですね。

さっそくワタクシも、ネタをやってみました。

  • 「集める」の部分に、Google Search History からデータを持ってくるモジュールを書き、自分の Google での検索履歴を集めるようにし、
  • もし最近「はらへった」というキーワードが検索されたら、
  • 「出力する」の部分に、 出前館 を使って自宅にピザを届けさせるモジュールを書いてそれにパス!

設定 YAML はこんな感じ

plugins:
  - module: CustomFeed::GoogleSearchHistory
    config:
      username: google-id
      password: p4ssw0rd
  
  - module: Filter::BreakEntriesToFeeds
    config:
      use_entry_title: 1

  - module: Filter::Rule
    rule:
      - module: Deduped
      - module: Fresh
        duration: 10 
  
  - module: Notify::Pizza
    rule:
      expression: $args->{feed}->title =~ /^はらへった/
    config:
      username: my-demae-can-id
      password: p4ssw0rd
      order: 1

で、これを cron で 5分単位とかで動かす。

100個とか届いたらやだなーと、かなりびくびくしながら Google に…

はらへったよー

すると、「【出前館】ご利用ありがとうございました!」 メールが! 嬉々

届いたピザ。今日のあさごはんにもなりました。雨の中、配達ご苦労様でした。

Notify::Pizza

CustomFeed::GoogleSearchHistory

書いたプラグインや、内部の説明。

実は Google Search History は RSS で取れるので、それこそ Plagger の本領発揮。別に新たにプラグインを書く必要なく、Subscription::Config プラグインとかで指定して取ってくるのが順等です。このRSS、Basic認証がかかるのですが、https://user:pass@... の形式で設定すればいける。

ただ、どうしても日本語の検索キーワードが文字化けしてしまう。いろいろ調べると、Google Search History の RSS は UserAgent がブラウザぽくないと charaset=utf-8 じゃなく、charset=ISO-8859-1 を返してきやがる。で、Plaggerが内部で使っている URI::Fetch は、現在の仕様では UserAgent は URI::Fetch 決め打ちなので、どうもむずかしい。そんなわけで CustomFeed::GoogleSearchHistory という独自のプラグインを書いた。

→ のですが、これは修正済。なので、今回ぼくが書いた yaml ではなく、Plaggerオリジナルの Plugin だけで同じことができます。くわしくはこちらにまとめました

Filter::BreakEntriesToFeeds の config: use_entry_title

これは、今回「フィードのタイトル」ではなく、「エントリのタイトル」で Filter::Rule するスマートなやり方がわからなくて、適当にパッチを当ててみたものです。

--- lib/Plagger/Plugin/Filter/BreakEntriesToFeeds.pm    (リビジョン 1122)
+++ lib/Plagger/Plugin/Filter/BreakEntriesToFeeds.pm    (作業コピー)
@@ -17,7 +17,8 @@
     my $feed = $args->{feed}->clone;
     $feed->clear_entries;
     $feed->add_entry($args->{entry});
-
+    $feed->title($args->{entry}->title) if $self->conf->{use_entry_title};
+    
     push @{$self->{feeds}}, $feed;
 }

もちろん、$args->{feed}{entries}[0]{title} とかでもいいと思うのですが。。追記:このパッチも、すでに対応してもらっています。

Notify::Pizza

Plagger::Plugin::Notify::Pizza

これは、もう完全にネタですw
出前を頼むときはよく使っている出前館に、WWW::Mechanize でボットアクセスしてみました。出前館でピザーラを頼むと、ポテトが付いてきておトクです。

WWW::Mechanize 用スクリプト部分は、こんなネタのためにめんどうなことしたくないので、 HTTP::Recorder を使ってサクっとやりました。HTTP::Recorder は、こんなのを書いて、

use strict;
use HTTP::Proxy;
use HTTP::Recorder;

my $proxy = new HTTP::Proxy(host => '127.0.0.1', port => 5656);
my $agent = new HTTP::Recorder(showwindow => 0, file => 'proxy.log');

$proxy->agent($agent);
$proxy->start();

ブラウザのプロキシを 127.0.0.1:5656 (おなかすいてたから)にすると、ブラウザでの動きをそのまま proxy.log に WWW::Mechanize 用スクリプトとして記述してくれるつう、すぐれものです。ただいろいろとバギーなのと、frame 処理とかイマイチで、そのへん考えてあげる必要あるのですが、下地造りとして便利。

というか、まあ ピザでお知らせ、というのはネタだけど、Googleに「あとで○○をする」とか入れるとそれが会社にメールされたりとか、実用的なアイディアがありそうな気が。Googleへの検索窓って、最近はブラウザにくっついてるから、楽だし。

実用編 へ続きます。