Plagger::Plugin::Publish::InfoseekMail

PlaggerのPlugin作ってみた。


取得したフィードをInfoseekのwebメールに送信。

package Plagger::Plugin::Publish::InfoseekMail;
use strict;
use warnings;
use base qw( Plagger::Plugin );

our $VERSION = '0.0.1';

use Plagger::Mechanize;
use Encode;

sub rule_hook { 'publish.feed' }

sub register {
    my($self, $context) = @_;
    $context->register_hook(
        $self,
        'publish.feed' => \&notify,
    );
}

sub init {
    my $self = shift;
    $self->SUPER::init(@_);

    $self->conf->{username} or Plagger->context->error("username is required");
    $self->conf->{password} or Plagger->context->error("password is required");
    $self->conf->{to} ||= $self->conf->{username};
}

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

    return if $args->{feed}->count == 0;

    my $cfg  = $self->conf;
    my $feed = $args->{feed};

    my $uri      = 'http://email.www.infoseek.co.jp/';
    my $username = $cfg->{username};
    my $password = $cfg->{password};

    my $to       = $cfg->{to};
    my $subject  = $feed->title || '(no-title)';
    my $body     = $self->templatize('infoseek_notify.tt', { feed => $feed });

    $context->log(info => "Sending $subject to $to");

    # トップページ取得
    my $mech = Plagger::Mechanize->new;
    $mech->get($uri);

    # ログイン
    $mech->submit_form(
        form_name => 'login',
        fields    => {
            username => $username,
            password => $password,
        },
    );

    # メール作成画面へ移動
    $mech->follow_link(url => $uri);
    $mech->submit_form(
        form_number => 3,
    );
    
    # 送信
    $mech->form_name('tform');
    $mech->set_fields(
        sto  => $to,
        ssbj => $self->to_eucjp($subject),
        sbdy => $self->to_eucjp($body),
    );
    $mech->click_button(number => 1);
}

sub to_eucjp {
    my ($self, $str) = @_;

    $str = encode('eucjp', $str);
    return $str;
}

1;

YAMLはこんな感じ。

  - module: Publish::InfoseekMail
    config:
      to: 送信先メールアドレス(指定しない場合はusername)
      username: Infoseekのメールアドレス
      password: パスワード

ほとんどPublish::Gmailのコピペ。
とりあえず送信はできました。




※ちゅういがき※
エラー処理とかしてません。
enclosureよくわからんかったので省いた。
テキストメールで送信しちゃうのでHTMLタグがそのまま表示されます。
rule_hookて何をするところなのかわかってません。

WebService::RakuAPIのソース読んだ(2)

ソースコードの中でこれいいなと思ったのが、
Resultクラスでやってる処理。

no strict 'refs';
*{String::CamelCase::decamelize($_)} = \&$_ for @Fields;

型グロブ使わずにやるならnewの中にこんな感じの処理をいれなきゃいけない。

my @accessors;
foreach (@Fields) {
  my $accessor = String::CamelCase::decamelize($_);
  $self->{$accessor} = $self->{$_};

  push @accessors, $accessor;
}
$class->mk_accessors(@accessors);

少し勉強になった。

WebService::RakuAPIのソース読んだ

勉強がてらRakuAPIのモジュール作ったわけですが、
伊藤さんのコードはどんな感じかな〜と読んでみました。


まず、知らないモジュールをいくつか発見。

  • Class::ErrorHandler
  • Readonly
  • URI
  • String::CamelCase

さっそくCPANに行ってこれらのモジュールを探してみる。


Class::ErrorHandler
どうやらエラーメッセージ用アクセサ?っぽい。
メソッドはセッタのerror($message)とアクセサのerrstr。
継承させてつかうみたい。


Readonly
use constantな働きをするモジュール。
use constantでは、

  • 配列とハッシュにはちょっと特殊な書き方しなくちゃいけない。
  • レキシカルスコープにできない。
  • コンパイル時にしか働かない?(Works only at compile time)
  • オーバーライド可能。

なのを、Readonlyではその辺カバーしてくれるらしい。
Perlベストプラクティス」に載ってるモジュールだそうな。


URI
これいいね。
今まで知らんかったのが残念。

my $uri = URI->new('http://rakuapi.ddo.jp/api');
$uri->query_form(
 keyword => 'PSP',
  sort => 'new',
  genre => 'book',
);

print $uri;

ってやると、

http://rakuapi.ddo.jp/api?keyword=PSP&sort=new&genre=book

こんな感じでURI生成してくれます。
超便利。


String::CamelCase
これもなかなか便利そう。
文字列を特定の形式に変換してくれます。
camelize($str)は、'some_keyword'を'SomeKeyword'に。
decamelize($str)は、'SomeKeyword'を'some_keyword'に。
wordsplit($str)は'some_keyword'を(some, keyword)、
もしくは'SomeKeyword'を(Some, Keyword)に。


モジュールの使い方だけでも大変勉強になりました。
続きは明日。

ウェブ進化論:梅田望夫

ウェブ進化論 本当の大変化はこれから始まる (ちくま新書)

ウェブ進化論 本当の大変化はこれから始まる (ちくま新書)

RakuAPI.pm

なんかいろいろおかしかったので修正。

package RakuAPI;
use strict;
use warnings;

use XML::TreePP;
use base qw(Class::Accessor::Fast);

__PACKAGE__->mk_accessors(qw/tpp base_url/);

sub new {
 my $class = shift;
 my $self = {
  tpp => XML::TreePP->new(force_array => ['Result']),
  base_url => 'http://rakuapi.ddo.jp/api?keyword=',
 };
 return bless $self, $class;
}

sub search {
 my ($self, $keyword, $option) = @_;

 my @params;
 if ( $option ) {
  foreach ( keys %{$option} ) {
   my $param = $_ . '=' . $option->{$_};
   push  @params, $param;
  }
 }

 $keyword =~ s/(\W)/'%' . unpack('H2', $1)/eg;

 my $reqest_url = $self->base_url . $keyword;
 $reqest_url .=  '&' . join('&', @params) if ( $option );

 my $parsed_xml = $self->tpp->parsehttp(GET => $reqest_url);

 my @results;
 foreach my $result( @{$parsed_xml->{ResultSet}->{Result}} ) {
  push @results, RakuAPI::Result->new($result);
 }

 return @results;
}

1;

package RakuAPI::Result;
use strict;
use warnings;

use base qw(Class::Accessor::Fast);

sub new {
 my ($class, $result) = @_;

 my $self = $class->SUPER::new($result);
 $class->mk_accessors(keys %{$result});

 return bless $self, $class;
}

1;

まだいろいろおかしいような気ガス。
一応動く。

use strict;
use warnings;
use RakuAPI;

my $app = RakuAPI->new;

my @results = $app->search('PSP', {sort => 'high'});

foreach my $result ( @results ) {
 print $result->Title, "\n";
 print $result->Url, "\n";
}

上の実行結果。

新品!カルピジャーニ ソフトクリームフリーザー AES403PSP/S
http://item.rakuten.co.jp/recyclemart/cpjheaaa19010n/
新品!カルピジャーニ ソフトクリームフリーザー AES403PSP/S
http://www.rakuten.co.jp/recyclemart/451778/452834/593927/#693600
新品!カルピジャーニ ソフトクリームフリーザー AES261PSP/S
http://item.rakuten.co.jp/recyclemart/cpjheaaa19000n/
新品!カルピジャーニ ソフトクリームフリーザー AES261PSP/S
http://www.rakuten.co.jp/recyclemart/451778/452834/593927/#693494
新品!カルピジャーニ ソフトクリームフリーザー 191BAR/PSP
http://item.rakuten.co.jp/recyclemart/cpjheaaa19040n/
新品!カルピジャーニ ソフトクリームフリーザー 191BAR/PSP
http://www.rakuten.co.jp/recyclemart/451778/452834/593927/#694419
中古:カルピジャーニ ソフトクリームフリーザー(ソフトクリーマー) AES-403PSP
http://item.rakuten.co.jp/recyclemart/cpjhe95a21650u/
SBDB001 【25%OFF!】 スプリングドライブ SEIKO PRPSPEX[プロスペックス]
http://www.rakuten.co.jp/iget/655089/727155/655180/661745/#618078MUSE RESEARCH Receptor XT
http://www.rakuten.co.jp/ikebe/443913/505946/622909/#633263
SBDS001 【25%OFF!】 SEIKO PRPSPEX[プロスペックス]
http://www.rakuten.co.jp/iget/655089/727155/655180/656917/#613772

同一ファイル内にクラス複数作る時の書き方はあれでいいのかな。

楽天のAPI

RakuAPI - 楽天市場 非公式ウェブサービス


id:naoya さんのところで知った。
さっそくモジュール作ってるし。すごいなー。


勉強がてらこのAPIのモジュール作ってみるか。
作ったらnaoyaさんのソース読んで一人反省会しよう。

Class::Accessor

よく

use base qw(Class::Accessor::Fast);
__PACKAGE__->accessors(@accessors);

ってみかけるけど、

package Accessor;
use base qw(Class::Accessor::Fast);

sub new {
  my ($class, $hash_ref) = @_;

  $class->mk_accessors( keys %{$hash_ref} );
  return $class->SUPER::new($hash_ref);
}

こんなクラス作って、これを継承させたら
ハッシュリファレンスつっこむだけでアクセサ付のオブジェクト作れるなあと思ったりした。

あんま使い道なさそうだけど。