ishiducaの日記 このページをアンテナに追加 RSSフィード

2010-08-22

[][][]今日の予定を教えてくれる僕だけの秘書をつくろう 15:04 今日の予定を教えてくれる僕だけの秘書をつくろう - ishiducaの日記 を含むブックマーク はてなブックマーク - 今日の予定を教えてくれる僕だけの秘書をつくろう - ishiducaの日記 今日の予定を教えてくれる僕だけの秘書をつくろう - ishiducaの日記 のブックマークコメント

GoogleCalendarのメール通知を利用すれば、本日の予定でも3日後の予定でも細やかに通知してくれるんですが、「今日の予定って?」って尋ねたら教えてくれる秘書がいたらいいですよね^ ^

要するに通知botのことですが、GoogleCalendarとTwitterを使って作ってみました。

流れとしては、

  1. 秘書ボット宛のmentionsの一覧を取得
  2. mentionsの一覧から僕(@ishiduca)のツイートで「今日の予定」というキーワードを含むものを探す
  3. 探し当てた場合で、今回初出のツイートの場合は GoogleCalendarから今日の予定を抽出して僕(@ishiduca)宛に、ツイートする

というもの。

GoogleCalendarからイベントを取得するには

  1. webページ右上あたりの「設定」→「カレンダーの設定」で、「カレンダーの設定」ページに移動
  2. 「カレンダー」タブを選択。僕が閲覧できるカレンダーの一覧が出るのでその中から、使いたいカレンダーを選択して「xxの詳細」ページへ移動
  3. 「カレンダーのアドレス」で[xml]を選択して、URLを取得したら、LWP::UserAgentでxmlを取得。XML::SimpleのXMLinで解析

実際の運用時には、下記の my_secreatary.pl を cron で実行(mentionsの一覧を取得)します。そうすると探し出したツイートと重複したツイートが出てくるので、それに反応しないようにするために、今回も memcached を使います。

また、前回作った My::Twitter モジュールを使います。(mentions一覧を取得するのに get_mentionsメソッド追加しましたけど)

あと直近のツイートと同じツイートを連続すると403エラーが返ってくるので、リプライを投げる時刻をくっつけて誤魔化してます。

my_secretary.pl

#!/usr/bin/env perl
BEGIN {
    # cronで動かすのに暫時的に指定する
    push @INC, '/Users/ishiduca/MyApp/';
}
use strict;
use warnings;
use utf8;
use Encode;
use My::Twitter;
use LWP::UserAgent;
use XML::Simple;
use DateTime;
use Cache::Memcached::Fast;

my $cache = Cache::Memcached::Fast->new({
    servers => [ "localhost:11211" ]
});

my $secreatary = Twitter->new;
$secreatary->set_oauth_consumer(
    consumer_key    => 'Your Secreatary Consumer Key',
    consumer_secret => 'Your Secreatary Consumer Secret',
);
$secreatary->set_access_token(
    token  => 'Your Screatary Access Token',
    secret => 'Your Screatary Access Secret',
);

my $mentions = $secreatary->get_mentions;
for (@{$mentions}) {
    my $id     = $_->{id};
    my $tweet  = $_->{text};
    my $friend = $_->{user}{screen_name};

    if (! $cache->get($id)) {
        $cache->set($id, $_);
        if ($friend =~ /^ishiduca$/ and $tweet =~ /今日の予定/) {
            &hoge;
        }
    }
}

exit 0;


sub hoge {
    my $today = DateTime->now;
    $today->set_time_zone( 'Asia/Tokyo' );
    my $today_ymd = $today->ymd; # ex 2010-08-22
    my $now       = $today->hms; # ex 10:22:33

    my $ua = LWP::UserAgent->new;

    my $calendar_url = "http://www.google.com/calendar/feeds/.../full";
    my $response = $ua->get( $calendar_url );
    die $response->status_line if ! $response->is_success;

    my $parser = XML::Simple->new;
    my $events = $parser->XMLin($response->{_content});

    my $c = 0;
    for my $key (keys %{$events->{entry}}) {
        my $date = $events->{entry}{$key}{'gd:when'}{startTime};

        if ($date =~ /$today_ymd/) {
            $c++;
            my $content = $events->{entry}{$key}{title}{content};
            $date =~ s/:[0-9][0-9]\..+$//;
            $date =~ s/^20[0-9][0-9]-//;
            $date =~ s/-/\./g;
            $date =~ s/T/ /;
            $secreatary->tweet(encode_utf8(
                substr(join(" ", '@ishiduca', "$date", "$content at $now"), 0, 140)));
        }
    }

    if ($c == 0) {
        my $murmur = encode_utf8('今日は何も無いから一緒にお出かけしようよっ');
        $secreatary->tweet(join " ", '@ishiduca', "$murmur at $now");
    }

    exit 0;
}

日付の処理が汚いですね >_<

f:id:ishiduca:20100822150006p:image

こんな感じに。

トラックバック - http://perl.g.hatena.ne.jp/ishiduca/20100822

2010-08-20

[][]「ターミナルからツイートする」の追試 16:06 「ターミナルからツイートする」の追試 - ishiducaの日記 を含むブックマーク はてなブックマーク - 「ターミナルからツイートする」の追試 - ishiducaの日記 「ターミナルからツイートする」の追試 - ishiducaの日記 のブックマークコメント

まずは

OAuth::Lite::ConsumerとLWP::UserAgentでTwitterにポストする

なんだけれども。



Twitter用のbotを作るに当たり複数のアカウントでツイートする(あるいはタイムラインを取得する)のに、同じコードをなんども書くのはさすがに効率が悪いので、モジュール化してみた。

ついでにOOPな方向にチャレンジ

My::Twitter.pm

package My::Twitter;
use strict;
use warnings;
use Carp;
use OAuth::Lite::Consumer;
use LWP::UserAgent;
use JSON;

my $ua = LWP::UserAgent->new;

sub new {
    my $class = shift;
    my %args = @_;
    bless(\%args, $class);
}

sub set_oauth_consumer {
    my $self = shift;
    my %hash = @_;
    $self->{consumer} = OAuth::Lite::Consumer->new(
        consumer_key    => $hash{consumer_key},
        consumer_secret => $hash{consumer_secret},
    );
}

sub set_access_token {
    my $self = shift;
    my %hash = @_;
    $self->{access_token} = OAuth::Lite::Token->new(
        token  => $hash{token},
        secret => $hash{secret},
    );
}

sub tweet {
    my $self = shift;
    my $murmur = join " ", @_;
    my $request = $self->{consumer}->gen_oauth_request(
        method => "POST",
        url    => 'http://twitter.com/statuses/update.json',
        token  => $self->{access_token},
        params => { status => $murmur },
    );
    my $response = $ua->request($request);
    die $response->status_line unless $response->is_success;
    $murmur;
}

sub get_timeline {
    my $self = shift;
    my $request = $self->{consumer}->gen_oauth_request(
        method => 'GET',
        url    => 'http://api.twitter.com/1/statuses/home_timeline.json',
        token  => $self->{access_token},
    );
    my  $response = $ua->request($request);
    die $response->status_line unless $response->is_success;
    decode_json($response->{_content});
}

1;

mytweet.pl

#!/usr/bin/env perl
use strict;
use warnings;
use Encode;
use DateTime;
use DateTime::Format::DateParse;
use My::Twitter;

@ARGV or die "not found your tweet", "\n";
my $status = join " ", @ARGV;

my $me = Twitter->new;
$me->set_oauth_consumer(
    consumer_key    => 'YOUR_CONSUMER_KEY',
    consumer_secret => 'YOUR_CONSUMER_SECRET',
);
$me->set_access_token(
    token  => 'YOUR_ACCESS_TOKEN',
    secret => 'YOUR_ACCESS_SECRET',
);

$me->tweet( $status );

my $timeline = $me->get_timeline();

for (@{$timeline}) {
    my $friend = $_->{user}{screen_name};
    my $tweet  = $_->{text};
    my $datetime = DateTime::Format::DateParse->parse_datetime(
        $_->{created_at}
    );
    $datetime->set_time_zone( 'Asia/Tokyo' );
    my $date = $datetime->strftime('%m.%d %H:%M');
    print $date, encode_utf8(" <$friend>\t$tweet\n");
}

exit 0;

やりたいことは実行できてるんだけど。

IceIce2011/06/05 12:36You’re the one with the brains here. I’m wtacihng for your posts.

kbdbutsxykbdbutsxy2011/06/06 22:47nYuvjN , [url=http://btdywumckled.com/]btdywumckled[/url], [link=http://esqtcbemvmwr.com/]esqtcbemvmwr[/link], http://afsjhcrktgtu.com/

sdqjagvyksdqjagvyk2011/06/07 18:26AL4If2 <a href="http://isdiszydngwi.com/">isdiszydngwi</a>

pxmzvpwpxmzvpw2011/06/09 18:57z7t78J , [url=http://voxbdfctfdvo.com/]voxbdfctfdvo[/url], [link=http://mtdaaezmxscf.com/]mtdaaezmxscf[/link], http://qicmpotpztkm.com/

トラックバック - http://perl.g.hatena.ne.jp/ishiduca/20100820

2010-07-27

[][][]はじめてのTwitter bot 設定とか 17:18 はじめてのTwitter bot 設定とか - ishiducaの日記 を含むブックマーク はてなブックマーク - はじめてのTwitter bot 設定とか - ishiducaの日記 はじめてのTwitter bot 設定とか - ishiducaの日記 のブックマークコメント

前回のはじめてのTwitter botを実際に自動化するのにいくつかハードルを越えなくちゃいけないので、その辺をフォローします。ただし、僕のMacBookOS 10.6)上での話。

おさらいとして、Twitter bot を作るまでの流れ

  • bot用のアイコンを作っておく
  • bot用のアカウントを取得しておく
  • bot用のアカウントで使うアプリケーション(この場合はbot.pl)用にOAuth登録を済ませておく
  • memcachedをインストールしておく
  • 起動時にmemcachedをデーモンプロセスで作動するように設定する
  • botのスクリプトを作る
  • cronでbotを作動させるcrontabの設定をする

bot用のアイコンを作っておく

ここは流す。(僕は /Icon Generator - Generate CS5 or Web 2.0 style icon を使いました)


ボット用のアカウントを取得しておく

ここも流す。


bot用のアカウントで使うアプリケーション(この場合はbot.pl)用にOAuth登録を済ませておく

前出ですが「実用! PerlでコマンドラインからTwitter投稿(OAuth対応) - perl-mongers.org」を参照して取得してください。


memcachedをインストールしておく

OS 10.6 の場合、macport からインストールできる

sudo ports install memcached

起動時にmemcachedをデーモンプロセスで作動するように設定する

memcachedをインストールしたときに作成されたplistを利用する

sudo launchctl load -w /Library/LaunchDaemons/org.macports.memcached.plist

細かく設定したい場合は、plistをいじる


botのスクリプトを作る

前回記事をみてください。


cronでbotを作動させるcrontabの設定をする

ここで困るのが、perlbrew+cpanmな環境でcpanモジュールを使ったperlスクリプトを動かそうとしても、うまく作動しないことがあるということだ。具体的にはperlbrewでインストールしたperlを使わずにデフォルトでインストールされているperlを使おうとしてしまう。

なので、cronを実行するときには

crontab -e

として、環境変数を追加する

PATH=/Users/ユーザー名/perl5/perlbrew/bin:/usr/bin:/usr/sbin:/bin:sbin

あとは、適当な間隔(例だと3min)で動くように設定する

*/3 * * * * perl /Users/ユーザー名/スクリプトのあるディレクトリ名/bot.pl

できあがり

http://twitter.com/tkdhoncho_bot

treeboatreeboa2010/07/27 18:27ホォオオオ!cronはそのためのやつだったのか
bot作るだけでも案外たいへんそう
うまくまとめられてて サスガ^^b

SarahSarah2013/02/02 03:18Peecrft shot! Thanks for your post!

vfvvalkovfvvalko2013/02/02 19:28kGFiT1 <a href="http://ymammxedvual.com/">ymammxedvual</a>

iecqvvlvriecqvvlvr2013/02/04 12:42J0vNbo <a href="http://bxzqjxelvbss.com/">bxzqjxelvbss</a>

トラックバック - http://perl.g.hatena.ne.jp/ishiduca/20100727

2010-07-22

[][][]はじめてのTwitter bot 16:19 はじめてのTwitter bot - ishiducaの日記 を含むブックマーク はてなブックマーク - はじめてのTwitter bot - ishiducaの日記 はじめてのTwitter bot - ishiducaの日記 のブックマークコメント

TwitterAPIから任意のハッシュタグのついたツイートを拾ってRTするbotを作ってみました。

巷にTwitter botは溢れているから今更な感じしますが、僕は初めて!(だから問題ない)


流れとしては

  1. TwitterAPIに検索ワードを投げる
  2. レスポンスをパースする
  3. 前回の検索で引っかかったツイートまたは、それをもとにRTしたツイートしたツイートと、それら以外のツイート(今回の検索で引っかかったツイート)を分ける
  4. 今回RTするツイートを整形する
  5. 今回RTするツイートをmemcachedに記憶する
  6. RTする

拾ってきたツイートをRTするだけだと、重複があったり、またbot自身がRTしたツイートを再度ツイートしてしまってスパムボットになってしまうので、それらを弾くためにキャッシュを使って前回までのポストを記憶しておくと楽チンです。今回は memcached を使って実装してみました。


事前にやっておくこと

  • bot用のアイコンを作っておくこと(これ重要w)
  • bot用のアカウントを取得しておく
  • bot用のアカウントで使うアプリケーション(この場合は bot.pl )用にOAuth登録をしておく(consumer key, consumer serect, access token, access token secret)
  • memcachedをインストール、起動しておく

(bot.pl)

use strict;
use warnings;
use LWP::UserAgent;
use JSON;
use OAuth::Lite::Consumer;
use Cache::Memcached::Fast;
use Encode;

my $keyword = '%23tkdhoncho';
my $user_id = 'Bot UserID';

my $cache = Cache::Memcached::Fast->new({
    servers => [ "localhost:11211" ]
});

my $consumer = OAuth::Lite::Consumer->new(
    consumer_key    => 'Bot Consumer Key',
    consumer_secret => 'Bot Consumer Serect',
);
my $access_token = OAuth::Lite::Token->new(
    token  => 'Bot Access Token',
    secret => 'Bot Access Token Secret',
);

my $ua = LWP::UserAgent->new;
$ua->timeout(10);

my $uri = "http://search.twitter.com/search.json?q=$keyword";

my $response = $ua->get( $uri );
die $response->decoded_content unless $response->is_success;

my $result = decode_json($response->{_content});

for (@{$result->{results}}) {
    my $id = $_->{id};

    if ((! $cache->get($id)) and ($_->{from_user_id} ne $user_id)) {
        my %stat = (
            friend => $_->{from_user},
            tweet  => $_->{text},
        );

        $cache->set($id, \%stat);

        $stat{tweet} =~ s/@/!/g;
        my $tweet  = 'RT !' . $stat{friend} . '>>' . $stat{tweet};
        $tweet = substr $tweet, 0, 140;

        my $request = $consumer->gen_oauth_request(
            method => "POST",
            url    => 'http://twitter.com/statuses/update.json',
            token  => $access_token,
            params => { status => encode_utf8($tweet) },
        );
        my $response = $ua->request($request);

        die $response->status_line unless $response->is_success;
 
        warn join(" ", $id, encode_utf8($tweet)) . "\n";
    }
}

exit 0;

あとはcronを使って定期的に実行させます。

参照

追記

memcachedの導入やらcronの設定を「はじめてのTwitter bot 設定とか - ishiducaの日記 - Hatena::Group::Perl」に書きました。

トラックバック - http://perl.g.hatena.ne.jp/ishiduca/20100722