7月
10
2009
5

Twitter ボットの作り方 Perl 編 (3)

Twitter ボットの作り方解説第 3 弾。特定のワードが含まれたつぶやきに反応して、何らかの返信をするボット作ってみます。
以下は、第 2 弾までの状態を前提としています。

ボット作成の方針

基本的なボットの動作としては、まず、特定ワードの検索をして、検索結果に含まれるユーザに対して何らかの返信をするという方法になります。
ただ検索をする際、Twitter 全体を対象として検索すると、ワードによってはあり得ない件数の検索結果となってしまったり、プログラムに間違いがあった場合、不特定多数に迷惑をかけてしまう事になりかねません。
そこで検索対象としては、ボットに @ してくれた人の発言だけを対象とする事にします。
検索対象を全体に広げるのは、ボットの属性で考えたり、プログラムの正常動作を確認出来てからでいいでしょう。

また、ボットの動作は cron による一定の間隔になるので、1 回の動作の間に大量のつぶやきがあった場合、一度に取得できる検索結果件数には限度があるので、検索結果に漏れが出る可能性があります。
この漏れに対しても、厳密に検索を繰り返して遡っていく事は可能ですが、その部分には対応しない事にします。
さしあたってその程度の精度のボットを作ると思って下さい。まあ Twitter という文化からすれば許容出来る範囲でしょう。

あと返信の内容についてですが、何らかの反応があった方が面白いので、基本的にはオウム返しになりますが、「@username えー○○って( 艸`*)ププッ」(○○には相手の発言が入る) という感じにしましょう。

設定ファイルの追加

第 1 弾から使用している config.xml に対して、元々の username と password の後ろに、今回新たに 2 つの値を追加します。

query: "ダメ"
template: "えー「{text}」って( 艸`*)ププッ"

query が検索ワードで、template がそれに対する返信のひな形になります。
投げられた発言を埋め込む場所としてあらかじめ {text} を入れ込んでおいて、ここを置換します。

状態ファイルの準備

返信を送る際に、同じ人に何度も続けて返信をしてしまうと非常に失礼…というかこれはもうボットのバグなので、最後に返信をしたつぶやきを記録しておいて、次に検索する時はそれより後のつぶやきを検索するようにします。
最後に返信したつぶやきの id を次の検索時の検索条件として、since_id というもので指定するので、第 2 弾と同様、status.xml にそれを記録するようにしましょう。

sinceId: 0

初期値は最も小さい id、すなわち 0 を入れておきます。

実際のスクリプト

#!/usr/bin/perl

# 使用するモジュールの読み込み
use strict;
use warnings;
use Encode;
use FindBin;
use YAML::Tiny;
use Net::Twitter;

# yml ファイルの読み込みと、Twitter モジュールの初期化
my $config = (YAML::Tiny->read($FindBin::Bin . '/config.yml'))->[0];
my $status = (YAML::Tiny->read($FindBin::Bin . '/status.yml'))->[0];
my $twit = Net::Twitter->new(username => $config->{'username'}, password => $config->{'password'});

# 各種設定値の取得
my $username = $config->{'username'};
my $query = Encode::encode('utf8', $config->{'query'});
my $template = Encode::encode('utf8', $config->{'template'});
my $sinceId = $status->{'sinceId'};

# Twitter 検索。to: オプションでボット宛のつぶやきだけを絞り込み
my $response = $twit->search({q => 'to:' . $username . ' ' . $query, since_id => $sinceId});

# 検索結果を 1 件ずつ処理
my $result;
foreach $result ( reverse( @{$response->{'results'}} ) ) {
    my $text = Encode::encode('utf8', $result->{'text'});
    my $fromUser = $result->{'from_user'};
    my $statusId = $result->{'id'};
    my $reply = $template;
    $text =~ s/\s*\@${username}\s*//;
    $reply =~ s/{text}/$text/;
    $reply = '@' . "${fromUser} ${reply}";
    $twit->update({status => $reply, in_reply_to_status_id => $statusId});
    $sinceId = $statusId;
}

# 最後に処理したつぶやきの ID を保存
YAML::Tiny::DumpFile($FindBin::Bin . '/status.yml', {sinceId => $sinceId});

さて、ではポイントを絞って解説します。

まず重要なのが Twitter 検索の使い方です。
パラメータ q に検索文字列を指定して検索しますが、ここでは Twitter検索 で使用可能なオプションを指定することが出来ます。
検索時に指定出来るオプションについては、このあたりを参照して下さい。

パラメータ since_id は、つぶやきの id を指定することで、それより後のつぶやきを対象として検索します。
これがないと、重複返信をしてしまうのでとても重要なオプションです。

検索結果を 1 件ずつ処理するループでは、まず検索結果を配列として取得し、その順番を反転します。
検索結果は新しいものから順に返されるのですが、それに対する返信は古い順に行うからです。

検索結果の内容から、text, from_user, id を取り出します。
それぞれ、つぶやきの内容、つぶやいた人、つぶやき ID に相当します。

それ以降の部分は、ひな形の内容とつぶやきの内容を元に、返信する文章を組み立てています。
この部分まで詳しく解説するのは大変なので、詳細については別途 Perl について学んでみて下さい。

また、返信をつぶやく時の記述ですが、これまでになかった in_reply_to_status_id というオプションを指定しています。
このオプションは、どの発言に対する返信なのかを明示するもので、特に必須というわけではないです。
ですがこれをつけることで、返信から元の発言に対してリンクが張られるようになるので、返信の場合はつけておくのがマナーと言えます。

最後に動作確認が完了したら cron に登録しましょう。
あまり頻繁に検索すると API 制限にかかるので、5 分おきくらいにチェックするのが適当ではないでしょうか。

動作サンプル

otchy : @damedashi おれはもうダメだよ…
damedashi : @otchy えー「おれはもうダメだよ…」って( 艸`*)ププッ
otchy : @damedashi そんなダメとか繰り返さないでくれよ
damedashi : @otchy えー「そんなダメとか繰り返さないでくれよ」って( 艸`*)ププッ

※このボットは実在しません

改造のヒント

今回、ボットの返信に使うひな形は文章を 1 つだけにしましたが、第 2 弾で紹介した list.yml を使ったランダムテキストを組み込めば、バラエティーに富んだ返信をする事が可能です。
つぶやきの内容の一部を別の言葉に置換して返信しても面白そうですね。
list.yml の内容を配列ではなく、「キーワード:値」のペアにしておいて、「○○を△△に空目」ボットなんていうのも作れそうです。
また、検索自体のオプションを工夫すれば、もっと色々なボットも考えられるでしょう。

実は…

とここまで書いてきましたが、上記のスクリプトは期待したとおりの動作をしません。
それはなぜかというと、Twitter の検索 API では、日本語の検索結果がリアルタイムには検索出来ず、数時間の遅れがあるためです。
しかしながら、日本語の検索は 7 月末を目処に提供されるという話があるので、それまでは現状でリアルタイム検索が可能な英語表記でテストをしつつ待ちましょう。

さて、だんだんと本格的なボットになってきましたね。
第 4 弾以降では、ボット内だけで完結せず、外部の情報を読み込んで使用するようなボットを作ってみたいと思います。

Written by Otchy in: Development | タグ: ,
6月
29
2009
2

Twitter ボットの作り方 Perl 編 (2)

Twitter ボットの作り方解説第 2 弾。あらかじめ用意された定型文の中からランダムでつぶやくボットを作ってみます。
以下は、第 1 弾までの状態を前提としています。

状態ファイルの準備

完全なランダムでつぶやいてもいいのですが、それだと同じつぶやきが連続したり、なかなか出てこないつぶやきがあったりしてしまいますね。
ですので、どこまでつぶやき終わったかを保持して、一通りつぶやき終わるまでは重複しないように制御したいと思います。
そのために、現在の状態を保持するファイルを用意します。ここでは名前を status.yml にしましょう。
ファイルのフォーマットは、config.yml と同様です。
1 回つぶやくたびに状態を保存するため、ファイルには書き込み属性をつけておいて下さい。

lastIndex: 9999

lastIndex には、最後につぶやいた文のインデックスを保持するようにします。
初期データとしては、用意しようとしている定型文の数よりも十分に大きな数値を設定しておいて下さい。

データの準備

ボットがつぶやく定型文をあらかじめ準備しておきます。
設定ファイルと同様に、yml ファイルで作ります。ここでは名前を list.yml にしましょう。
下記のような感じです。日本語を含む場合は UTF-8 で保存して下さい。

このファイルはランダムでつぶやくようにするため、後ほどプログラムでシャッフルします。
シャッフルしたあと、このファイルに上書きするようにするので、書き込み属性をつけておいて下さい。

- つぶやきA
- つぶやきB
- つぶやきC
- つぶやきD
- つぶやきE

なお、行頭にある半角ハイフンは、YAML の文法における配列の表現になります。

実際のスクリプト

#!/usr/bin/perl

# 使用するモジュールの読み込み
use strict;
use warnings;
use FindBin;
use List::Util;
use Encode;
use YAML::Tiny;
use Net::Twitter;

# yml ファイルの読み込みと、Twitter モジュールの初期化
my $config = (YAML::Tiny->read($FindBin::Bin . '/config.yml'))->[0];
my $status = (YAML::Tiny->read($FindBin::Bin . '/status.yml'))->[0];
my @list = @{(YAML::Tiny->read($FindBin::Bin . '/list.yml'))->[0]};
my $twit = Net::Twitter->new(username => $config->{'username'}, password => $config->{'password'});

# 最後につぶやいたインデックスと、定型文リストのサイズ
my $lastIndex = $status->{lastIndex};
my $listSize = @list;

# つぶやきが一周していたら、定型文をシャッフルしてファイルに保存
if ($lastIndex >= ($listSize - 1)) {
        @list = List::Util::shuffle(@list);
        YAML::Tiny::DumpFile($FindBin::Bin . '/list.yml', [@list]);
        $lastIndex = -1;
}

# 実際のつぶやき
my $update = $list[++$lastIndex];
$update = Encode::encode('utf8', $update);
$twit->update($update);
YAML::Tiny::DumpFile($FindBin::Bin . '/status.yml', {lastIndex => $lastIndex});

大体の動作に関しては、スクリプト内のコメントに書いたとおりですが、いくつか補足します。

FindBin は、スクリプトの絶対パスを取得するために使用しています。
第 1 弾のスクリプトでは、設定ファイルなどを相対パスで指定していましたが、ボットとして cron に登録する際は、絶対パスでの動作が求められます。
スクリプトの位置を変えるたびに、設定ファイルのパスを書き換えなくて済むように、スクリプト自体の絶対パスを取得して、同ディレクトリにある設定ファイルを読み書きするようにしています。

List::Util は、配列をランダムにシャッフルするために使用しています。
必要時のみ、List::Util::shuffle で配列をシャッフルしてその内容をファイルに保存しています。

Encode は、yml ファイルから読み取った日本語文字列を適切に扱うために使用しています。
*.yml ファイル自体を UTF-8 で記述していても、YAML::Tiny での UTF-8 の扱いが微妙なため、この変換が必要になっています。
※”use utf8; use encoding ‘utf-8′;” 等があるとかえってうまくいきませんでした。

YAML::Tiny::DumpFile は、各種データを *.yml ファイルに書き込むために使用しています。
渡すデータの形式に少しクセがあるので、このように使うもの、と覚えてしまった方が良いでしょう。

cron への登録

上記のスクリプトの動作が確認出来たら、後は自動的につぶやくように設定するだけです。
つぶやきの自動化には、cron を使います。

例えば以下のように設定すると、5 分おきにつぶやきます。

*/5 * * * * /home/username/twitbot/twitbot.pl

Twitter API は 1 分あたり 100回まで (2009-06-29 現在) しか実行出来ないという制限があるので、あまり頻度を上げすぎない方が良いでしょう。
もちろん、ボットの属性によって色々と変更した方が良いのは、言うまでもありません。

cron の設定に関して分からない点があれば、このあたりを参照して下さい。

さあ、ごく簡単なボットですがこれで自動的につぶやき続けるボットを作る事が出来ました。
第 3 弾以降では、より高度なボットの作成を行っていきます。

Written by Otchy in: Development | タグ: ,
6月
29
2009
2

Twitter ボットの作り方 Perl 編 (1)

Twitter ボットの作り方解説第 1 弾。前提条件と、下準備までを解説します。

前書き

Twitter をある程度使っていると、突然見ず知らずのアカウントから reply をもらったりして、それがボットだという事は良くあります。
そんなとき、自分だったらこういうボットを作るのに!っていう思いがあっても、なかなか作れない人も多いのではないでしょうか?
ここでは、そのまま動作するサンプルを提示する事で、Twitter のボットを作る方法を解説します。
Perl とか詳しくなくても、Linux の知識がある程度あれば、自由にボットを作れるようになるはずです。

目標

だんだんとレベルを上げながら、以下のようなボットを作る事を目標にします。

  • あらかじめ用意された定型文の中からランダムでつぶやくボット
  • 特定のワードに反応して、reply するボット
  • 特定の RSS の内容をつぶやき続けるボット
  • ボットに対するメッセージを Google Yahoo で検索して結果を返すボット

用意するもの

  • ボット用のアカウント
  • Perl 5.8.x が動作する Linux サーバ
  • Perl の CPAN を利用出来る権限 (通常は root 権限)
  • perl スクリプトにパスを通して実行可能にする権限
  • cron を設定出来る権限
  • UTF-8 で編集/保存が出来るエディタ
  • 上記を理解出来る知識

方針

  • CPAN で可能な事はなるべく CPAN でやる
  • DB は使わずデータはファイルで持つ
  • ゆるい感じで厳密なエラー処理とかはしない

ではここからいよいよ本編です!

下準備

Twitter のボットを作成するにあたっては、当然 twitter サーバとのやりとりが必要になるのですが、そのための CPAN ライブラリを導入します。
また、今後のボットを作成するにあたっては、各種設定やデータなどを別ファイルに保存した方がやりやすいのですが、読み書きのしやすさと使いやすさを考慮して YAML を採用します。
まずはこれらの CPAN ライブラリを導入するところからです。

各モジュールのインストール中は結構時間がかかりますが、気長に待ちましょう。
各種オプションは、意味を理解した上であえて変えようとしない限り全てデフォルト (そのまま Enter) で構いません。

$ su -
# perl -MCPAN -e shell
cpan> install YAML::Tiny
cpan> install Net::Twitter

※ CPAN の利用について不明な点があればこういったところを参照して下さい。

CPAN モジュールの追加が完了したら、とりあえず簡単に動作確認をしておきましょう。
まず、config.yml というファイルを作成します。内容は以下のような感じです。

username: "twitter_user_name"
password: "twitter_password"

ここでは、Twitter で使用するアカウントのユーザ名とパスワードを記述しておきます。
プレーンテキストでパスワードが記載されるので、ファイルの権限には気をつけて下さい。

次に、twitbot.pl というファイルを作成します。内容は以下のような感じです。

#!/usr/bin/perl

use strict;
use warnings;
use YAML::Tiny;
use Net::Twitter;

my $config = (YAML::Tiny->read('config.yml'))->[0];
my $twit = Net::Twitter->new(username => $config->{'username'}, password => $config->{'password'});

$twit->update('Perl から Twiitter を更新するテストですよー');

これを UTF-8 で保存し、実行属性を与えます。perl のパスは環境に合わせて修正して下さい。

ここまで準備が出来たら、おもむろにスクリプトを起動します。
config.yml の指定が相対パスになっているので、カレントディレクトリから起動して下さい。

$ ./twitbot.pl

エラーにならず実行が完了したら、Twitter の TL を確認して下さい。
スクリプトからの更新が反映されていたら成功です。

さあ、ここまで出来れば下準備は完了です。
いよいよ次から、実際のボットを作っていきます。

Written by Otchy in: Development | タグ: ,
6月
17
2009
2

テキストファイルを URL エンコードして保存する perl のワンライナー

cat list.txt | perl -ne 's/([^\w ])/"%".unpack("H2",$1)/eg;s/ /\+/g;print;print "\n";' > result.txt

上記の例は、タイトル通り list.txt の内容を result.txt として保存する例ですが、標準入力の内容を URL エンコードして標準出力に吐いているので、他にも応用が利くかと思います。

Linux で使う事がほとんどだと思うので、Linux 版です。
Windows の場合、引数をダブルクォートでくくったほうがうまくいったのですが、Linux 上では、逆にシングルクォートじゃないとダメでした。

Written by Otchy in: Development | タグ:

Powered by WordPress | Aeros Theme | TheBuckmaker.com