PerlでTwitterのBotを作ってみる

Perlなんて触ったことない人ですが、とりあえず動いたのでメモ書き。とは言え、 「作ってみる」なんてのは嘘で、ほぼコピペです。すみませんm(_ _)m

基本方針

基本的にここのスクリプトを動かすことしかしてません。いようつさんありがとうございます。

さて、Twitterから呼びかけを読み込むには「API」「Webページ」「IM」の3通りがあります。 APIは制限がうざい、Webページは変更の可能性がある、ということでとりあえずIM経由。 つまり、BotのアカウントでIMを登録しておき、そこからメッセージを取得することで スクリプト内に発言をげとしてしまう感じです。

続いてBotが発言する方はAPIを利用します。こちらもIM経由にしたり、Webページにしても いいのかも知れませんが、Perl歴1日では無理ですので、元ネタ通りにまずはいきましょう。 ちなみに、Follow返しの方はまだいじってませんので、今回は手動でFollowして実験してますorz

というわけでおおまかな流れは以下。

  • CPANの準備
  • Jabber.jpでアカウント取得
  • TwitterでBotアカウント取得&IM設定
  • Perlスクリプト動かす

うーむ、自分では1行もコードを書かないというダメっぷり。まぁこれからがんばります。

動作環境

  • Ubuntu 7.10 サーバ版
  • Perl 5.8.8

CPAN

まずは、Perlのモジュール管理CPANを使うところから始めます。 これはDebianでいうaptみたいなもんで、Perlの便利なモジュールが コマンド一つでインスコできる優れもの。

ここを参考に入れました。OS入れて間もないので、makeとかが無かったので とりあえずapt-get

$ sudo apt-get install make lynx gcc g++

すごい適当です。とりあえず入れたって感じ。これで準備できたので、コマンドで CPAN起動。

$ sudo cpan

これで初期設定に入ります。基本Enter連打で、以下だけ変更。

Policy on building prerequisites (follow, ask or ignore)? [ask] follow
Parameters for the 'make install' command? [] UNINST=1

なぜかは知りませんorz とりあえず。地域を選択すればFTPサーバの選択になると 思いますので、適当に選択。しばらくすればプロンプトが返ってくるはず。 ちなみに、この設定は後でも変えれるみたいなので、あまり考えずサクサクやりました。

cpan>

試しにインストール。

cpan> install Jcode

これでしばらくすれば入ったはず。途中、dankogaiの文字が見えました^^ 他、参考サイトに書いてあった以下のモジュールをインストールしました。

cpan> install Bundle::LWP
cpan> install Bundle::CPAN
cpan> install Crypt::SSLeay

結構時間かかるかも。あとはスクリプト動かしてみて、エラー(そんなモジュール無いよ)と 言われた時に適時インスコすればよし。

Jabber.jp

次はIMのアカウント取り。Jabberってのがある。よくわかんないけど、自分でサーバ立てること とかも可能らしいし、GTalkがJabberと同じプロトコルらしい。が、そこまではよく分からんので、 Jabber.jpという所が動かしているサーバを利用させてもらいます。

いいやり方はよく分かりませんが、Jabber.jpにあるPandionという IMクライアントをインスコして、サイトのチュートリアルに従ってアカウントを 作っちゃって下さい。バージョンが違うっぽくて微妙に画面が違いますが、まぁ なんとかなるでしょう。ここは省略。とりあえず、これでJabberの情報がそろいました。

  • Jabberサーバ
    • jabber.jp
  • Jabberユーザ名(例)
    • jabberuser
  • Jabberパスワード(例)
    • jabberpass

Twitter

続いて、Bot用のアカウントをTwitterで取得します。これは説明不要でしょう。

  • Twitterユーザ名(例)
    • botuser
  • Twitterパスワード(例)
    • botpass

そうしたら、そのアカウントでSettingを開き、IM通知の設定をします。 プルダウンからJabberを選択し、ユーザ名に「jabberuser@jabber.jp」と 入れればOK。その後のページで表示される緑色の文字列を メモしておきます。

続いて、先ほどインスコしたPandionを使って、友達としてTwitterを 登録します。アドレスは「twitter@twitter.com」です。そうしたら、 この人に向けて、先ほどメモした文字列を発言しましょう。これで 登録完了。BotがFollowしている人の発言(IM通知ONにした人のみ)が twitter@twitter.comの発言として流れてくるようになります。

スクリプト

さて、仕上げです。スクリプト自体は最初のページの方が公開されている ソースをほぼそのまま流用させてもらっています。ハックしたのは以下。

  • Botに向けた発言を抜き出す正規表現のところで、Botのユーザ名を変数にした
  • ダイスを振る部分はそっくり消去して、オウム返しするだけにした
  • 日本語で発言するときにUTF-8にエンコードするようにした

というわけで、とりあえずソースコード。

#!/usr/bin/perl -w
use strict;
use warnings;
use utf8;
use Jcode;
use Net::Jabber;
use YAML;
use Net::Twitter;

my $server = 'jabber.jp';
my $jabbername = 'jabberuser';#jabber.jpのユーザネーム
my $jabberpass = 'jabberpass';#jabber.jpのパスワード
my $resource = '';
my $twittername = 'botuser';#TwitterのBot用ユーザネーム
my $twitterpass = 'botpass';#TwitterのBot用パスワード

$SIG{HUP} = \&Stop;
$SIG{KILL} = \&Stop;
$SIG{TERM} = \&Stop;
$SIG{INT} = \&Stop;

my $Connection = new Net::Jabber::Client(
  debuglevel => 1,
  debugfile  => "jabber.log",
);

$Connection->SetCallBacks(
  message=>\&InMessage,
  presence=>\&InPresence,
  iq=>\&InIQ);

my $status = $Connection->Connect(
  hostname=>$server
);

if (!(defined($status)))
{
  print "ERROR:  Jabber server is down or connection was not allowed.\n";
  print "        ($!)\n";
  exit(0);
}

my @result = $Connection->AuthSend(
  username=>$jabbername,
  password=>$jabberpass,
  resource=>$resource);

if ($result[0] ne "ok")
{
  print "ERROR: Authorization failed: $result[0] - $result[1]\n";
  exit(0);
}

print "Logged in to $server...\n";

$Connection->RosterGet();

print "Getting Roster to tell server to send presence info...\n";

$Connection->PresenceSend();

print "Sending presence to tell world that we are logged in...\n";

while(defined($Connection->Process())) { }

print "ERROR: The connection was killed...\n";

exit(0);

sub Stop
{
  print "Exiting...\n";
  $Connection->Disconnect();
  exit(0);
}

sub InMessage
{
  my $sid = shift;
  my $message = shift;

  my $type = $message->GetType();
  my $fromJID = $message->GetFrom("jid");

  my $from = $fromJID->GetUserID();
  my $resource = $fromJID->GetResource();
  my $subject = $message->GetSubject();
  my $body = $message->GetBody();
  print "===\n";
  print "Message ($type)\n";
  print "  From: $from ($resource)\n";
  print "  Subject: $subject\n";
  print "  Body: $body\n";
#  print "===\n";
#  print $message->GetXML(),"\n";
  print "===\n";

  if ($body =~ /([^:]+):.*\@$twittername *(.*)/i) {
    ProcMessage($1, $2);
  }
}


sub InIQ
{
  my $sid = shift;
  my $iq = shift;

  my $from = $iq->GetFrom();
  my $type = $iq->GetType();
  my $query = $iq->GetQuery();
  my $xmlns = $query->GetXMLNS();
  print "===\n";
  print "IQ\n";
  print "  From $from\n";
  print "  Type: $type\n";
  print "  XMLNS: $xmlns";
#  print "===\n";
#  print $iq->GetXML(),"\n";
  print "===\n";
}

sub InPresence
{
  my $sid = shift;
  my $presence = shift;

  my $from = $presence->GetFrom();
  my $type = $presence->GetType();
  my $status = $presence->GetStatus();
  print "===\n";
  print "Presence\n";
  print "  From $from\n";
  print "  Type: $type\n";
  print "  Status: $status\n";
#  print "===\n";
#  print $presence->GetXML(),"\n";
  print "===\n";
}

sub ProcMessage
{
  my $from = shift();#差出人Twitterユーザ名
  my $body = shift();#発言内容

  my $twit = Net::Twitter->new(
    username=>$twittername
  , password=>$twitterpass
  );

  my $mes = '@'. "$from $body". "とか言って、バッカじゃないの!";

  Jcode::convert(\$mes, 'utf8');
  my $res = $twit->update($mes);
  print "$mes \n";
}

細かいところはまだよく分かってないですが、まぁなんとなく分かったのでパス。 これを適当な場所において

$ perl bot.pl

で実行。おそらくモジュールが無いよというエラーが返ってくるはず。 そうしたら、その無いと言われたモジュールをシコシコインスコしましょう。

cpan> install Net::Twitter
cpan> install Web::Scraper
cpan> install Net::XMPP
cpan> install Net::Jabber
cpan> install JSON::XS

なんかこの辺を入れました。途中Net::Plaggerを入れたりして遊んでますので どれがホントにいるんだかよく分かりませんorz ただ、Net::Jabberは一発で 上手く入らなくて、困っていたところ、一回Net::XMPPを入れてから、 もう一回実行したらうまくいきました。その辺は適当にやってください。 JSON::XSはNet::Twitterで日本語がうまくいかない時に要るかもで 入れましたが、もしかしたらJcodeで変換するだけでいいのかも知れません。

何度かエラーを潜り抜けて、スクリプトを実行に成功すると、 Jabberへの認証のあと、IMにTwitterがログインしていることが 表示されます。その状態で、BotがFollowしている人(hogehogeさん)から

@botuser ほげほげ

と発言すると、そのメッセージが来たことがターミナルに表示され、Botは

@hogehoge ほげほげとか言って、バッカじゃないの!

というツンツンしたお返事を返します。ヤター!

これ以後は、Botの挙動を自由にできます。スクリプト内のProcMessageという ルーチンの中をいじれば如何様にも。すばらしいです、いようつさん。感謝感謝。

TODO

  • 自動Follow返しも実験

    • メールでのFollow通知を利用する ← Gmailで専用アカウント作るか
  • 発言を受けて、何かのサイトから情報を取得し返すようなBotをやってみる

    • APIのあるサイトや、無いサイトでHTMLなどから情報を取る練習も兼ねて
  • タイムラインの取得や発言をWebページ経由で出来ないか探る

    • IMやAPIは抜けがありそうなので、いずれ。