SDN Project

"SDN" doesn't have a deep meaning...

2013.01.22 更新

PHP+OAuthでTwitterのBotを作ってみる

このエントリーをはてなブックマークに追加

最近は、誰でも簡単にTwitterのBotが作れるようになってきました。
ある程度プログラミングが出来る人にとっては、自分なりにわかりやすくプログラムを構築した方がいいという場合もあります。
ここでは、OAuthでタイムラインの取得、Twitterへの発言やリプライ、フォローからお気に入り登録まで簡単に説明します。

これはPHP+OAuthでTwitterでOAuth登録、PHPの準備が出来た前提での解説です。
もしまだの場合は先にそちらから。

まずはTwitterへOAuthアプリケーションを登録

いままでのBasic認証と違い、OAuth認証はTwitterへ登録してからでないと使うことは出来ません。
登録自体は簡単に出来るので時間はかかりません。
以下のページにて説明しています。

PHP+OAuthでTwitter

OAuthを少しだけ簡単に使うライブラリー的なもの

...と、ちょっとその前に、PHP+OAuthでTwitterでダウンロードした「twitteroauth.php」をちょっといじります。

「twitteroauth.php」の230行目にある「return $response;」を「return (object)array("Body"=>$response,"Code"=>$this->http_code);」にするとHTTPステータスコードも拾えます。

“自分なりに”簡単に扱えるライブラリー的なもの。
http://www.sdn-project.net/labo/twitter_bot.txt

文字コードはUTF-8、拡張子はphpにしてください。「twitter_bot.php」に。
※動作確認してないので正常に動かなかったらごめんなさい…

タイムライン取得からリプライや呟きに反応してPOSTするまで

先ほどのライブラリー的なものを使って簡単にタイムラインを取得してリプライに反応させてみる。

[追記]
Twitterのstatus_idが64bit仕様になったのに従って、id系を取得するときに後ろに「_str」を追加した方がいいかもしれません。

<?php
// 先ほどのtwitter_bot.phpを読み込む。パスはあなたが置いた適切な場所に変更してください
// CRONで動かす場合は、ここをサーバールートからのフルパスにする

require_once("twitter_bot.php");

// Botのユーザー名
$user = "Twitter_Bot";
// Consumer keyの値
$consumer_key = "consumer_key";
// Consumer secretの値
$consumer_secret = "consumer_secret";
// Access Tokenの値
$access_token = "access_token";
// Access Token Secretの値
$access_token_secret = "access_token_secret";

// オブジェクト生成
$Bot = new Twitter_Bot($user,$consumer_key,$consumer_secret,$access_token,$access_token_secret);
// 約15分毎に「followして」リプライの確認をしてフォローする(cronで2分間隔で動かす場合の例。2分間隔でないときはそれにあわせて変更してください)
$min = date("i");
if($min == 0 || $min == 16 || $min == 30 || $min == 46){
      $since_id_mentions = $Bot->Get_data("Mentions"); // 最後に取得したリプライのID
      $mentions = $Bot->Get_TL("mentions_timeline",$since_id_mentions); // Bot宛てのリプライ取得
      foreach($mentions as $reply){
            $tx = null;
            // $sid = $reply->id; // 呟きのID。int型
            $sid = $reply->id_str; // 呟きのID。string型
            // $uid = $reply->user->id; // ユーザーナンバー。int型
            $uid = $reply->user->id_str; // ユーザーナンバーstring型
            $screen_name = $reply->user->screen_name; // ユーザーID
            $name = $reply->user->name; // ユーザー名
            // 呟き内容。余分なスペースを消して、半角カナを全角カナに、全角英数を半角英数に変換。
            $text = mb_convert_kana(trim($reply->text),"rnKHV","utf-8");
            if(preg_match("/(follow ?|フォロー)して/i",$text)){
                  $result = $Bot->Follow($uid,true);
                  switch($result){
                        case "ok": $tx = $name."さんよろしくお願いします"; break;
                        case "already": $tx = "もうフォローしていますよ?"; break;
                        default: $tx = "時間を置いてもう一度やってくれる?";
                  }
            }
            if(preg_match("/(remove ?|リムーブ)して/i",$text)){
                  $result = $Bot->Follow($uid,false);
                  if($result == "error"){$tx = "時間を置いてもう一度やってくれる?";}
            }
            // $txが空でないのならPOST
            if($tx){$Bot->Post("@".$screen_name." ".$tx,$sid);}
      }
      // 次の呟き取得のために最後に取得した呟きを保存する
      $Bot->Save_data("Mentions",$sid);
}

// 以下通常のTL取得
$since_id = $Bot->Get_data("Since"); // 最後に取得した呟きのID
$timeline = $Bot->Get_TL("home_timeline",$since_id,50); // タイムラインの取得
foreach($timeline as $status){
      $tx = null;
      // $sid = $status->id; // 呟きのID。int型
      $sid = $status->id_str; // 呟きのID。string型
      // $uid = $status->user->id; // ユーザーナンバー。int型
      $uid = $status->user->id_str; // ユーザーナンバーstring型
      $screen_name = $status->user->screen_name; // ユーザーID
      $name = $status->user->name; // ユーザー名
      // 呟き内容。余分なスペースを消して、半角カナを全角カナに、全角英数を半角英数に変換
      $text = mb_convert_kana(trim($status->text),"rnKHV","utf-8");
      // Botが自分自身の呟き、RT、QTに反応しないようにする
      if($screen_name == $user || preg_match("/(R|Q)T( |:)/",$text)){continue;}
      if(stristr($text,"@".$user)){ // Bot宛のリプライ
            if(preg_match("/おは(よ)?(う|ー|~|〜)/",$text)){$tx = "おはようございます、".$name."さん";}
            elseif(preg_match("/こんにち(は|わ)/",$text)){$tx = "こんにちは";}
            elseif(preg_match("/こんばん(は|わ)/",$text)){$tx = "こんばんは";}
            elseif(preg_match("/(可愛|かわい)い/",$text)){$tx = "あら、ありがとうございます";}
            elseif(!preg_match("/[一-龠ぁ-んァ-ヴー!?]/u",$text)){$tx = "ん? 日本語じゃないわね。何処の言葉かしら?";}
            else {continue;}
      } elseif(!strstr($text,"@")){ // リプライでない普通の呟き
            if(preg_match("/風邪(引|ひ)いた/",$text)){$tx = $name."さん大丈夫? お大事にね";}
            // $tx = Rrt(array("~","~",・・・,"~")); とすればランダムに台詞を一つ取り出します
            elseif(preg_match("/眠い|ねむい|ねむたい/",$text)){$tx = Rrt(array("無理しないでね","適度に休憩してね"));}
            else {continue;}
      } else { // フォロワー同士のリプライなど。通常はこういったものに反応されると嫌がられるかも
            continue;
      }
      // $txが空でないのならPOST
      if($tx){$Bot->Post("@".$screen_name." ".$tx,$sid);}
}

// 最後に呟きのIDを保存して終わり
$Bot->End($sid);
?>

...と、こんな感じです。あくまでサンプルなので、細かいことをやりたい場合は書き加えたりしてください。
今の私が作っているBotたちはこのような構造ではないのですが。

サーバーにアップロード、そしてCRONへ登録

Botは通常のPHPと同じようにサーバーにアップロードするだけです。
ただ、第三者に推測されにくい場所にアップロードすることをおすすめします。不正にアクセスされる可能性があるからです。.htaccessいじれば大丈夫ですが。

Botを作ってCRONで動かして運用するに当たって私はさくらインターネットの「さくらのレンタルサーバー スタンダード」をおすすめいたします。
やはり無料のCRONサービスと比べると安定性があり、安心です。
CRONへの登録は検索すればいくらでも解説ページが出てくるので割愛します。

他のBotとの会話を行うために

先ほどのサンプルでは、他のBotとの会話を行うことは難しいでしょう。そこで、さっきのをいじってこのようにするといいかも。

// 以下通常のTL取得
$since_id = $Bot->Get_data("Since"); // 最後に取得した呟きのID
$timeline = $Bot->Get_TL("home_timeline",$since_id,50); // タイムラインの取得
foreach($timeline as $status){
      $tx = null;
      // $sid = $status->id; // 呟きのID。int型
      $sid = $status->id_str; // 呟きのID。string型
      // $uid = $status->user->id; // ユーザーナンバー。int型
      $uid = $status->user->id_str; // ユーザーナンバーstring型
      $screen_name = $status->user->screen_name; // ユーザーID
      $name = $status->user->name; // ユーザー名
      // 呟き内容。余分なスペースを消して、半角カナを全角カナに、全角英数を半角英数に変換
      $text = mb_convert_kana(trim($status->text),"rnKHV","utf-8");
      // Botが自分自身の呟き、RT、QTに反応しないようにする
      if($screen_name == $user || preg_match("/(R|Q)T( |:)/",$text)){continue;}

      if($screen_name == "_UsaRen_"){ // 仮にユーザー名が @_UsaRen_ なBot向けの反応
            if(stristr($text,"@".$user)){ // @_UsaRen_ からきた自分のBot宛のリプライ
                  if(preg_match("/今夜の([0-9]+)時に(.+)の境界を見に行かない?$/",$text,$match)){$tx = "今夜の".$match[1]."時に".$match[2]."ね。遅刻しちゃ駄目よ?";}
                  if(strstr($text,"大丈夫?")){$tx = "ええ、大丈夫よ・・・";}
            } elseif(!strstr($text,"@")){ // リプライでない @_UsaRen_ の独り言
                  if(strstr($text,"お腹減ったわ")){$tx = "あら、じゃあいつものカフェテラスに行く?";}
            }

      } elseif(stristr($text,"@".$user)){ // Bot宛のリプライ
            ~
      } elseif(!strstr($text,"@")){ // リプライでない普通の呟き
            ~
      } else { // フォロワー同士のリプライなど。通常はこういったものに反応されると嫌がられるかも
            continue;
      }
      // $txが空でないのならPOST
      if($tx){$Bot->Post("@".$screen_name." ".$tx,$sid);}
}

こうすればいい感じになるかもしれないです。
ちょっといじって、特定のユーザーの発言を全て無視するなんてことも出来てしまいますが。

連絡先

何か御用がある場合は以下のメールアドレスにてお願いします。
メールアドレス