2012/04/05

電力予報

全面的にTwitterBotPHP掲示板の天気予報機能を参考にしています……いたと思います。

EasyBotter.phpの改造ですが、改造箇所が複数に亘るのでご注意ください。
また、Yahoo!デベロッパーズネットワークの電気予報APIを使用しますので、前もってアプリケーションIDを取得しておいてください。

class EasyBotter {
  // 途中省略

  // まるっと関数を新造
  function makePowerForecastTweet($text){
    $api_key_yahoo = '取得したアプリケーションID';
    
    // いつの?
    $today = date("Ymd", time());
    $tomorrow = date("Ymd", time()+(24*3600));
    $dayaftertomorrow = date("Ymd", time()+24*3600*2);

    $w_day = array(
      "今日"=> $today, "きょう"=> $today,
      "明日"=> $tomorrow, "あす"=> $tomorrow, "あした"=> $tomorrow,
      "明後日"=> $dayaftertomorrow, "あさって"=> $dayaftertomorrow
    );
    foreach( $w_day as $w_time => $w_timeNo){
      if(preg_match( "/".$w_time."/u", $text)) {
        $timeName = $w_time;
        break;
      }
    }
    if(empty($timeName)) {
      $timeName = $today;
    }
    $day = $w_day["$timeName"];

    // どこの?
    $w_city = array(
      "東北"=>"tohoku", 
      "東京"=>"tokyo", "関東"=>"tokyo", "東電"=>"tokyo", 
      "中部"=>"chubu", "名古屋"=>"chubu",
      "大阪"=>"kansai", "関西"=>"kansai", "関電"=>"kansai",
      "九州"=>"kyushu", "福岡"=>"kyushu"
    );
    foreach( $w_city as $w_area => $w_areaNo){
      if(preg_match( "/".$w_area."/u", $text)) {
        $cityName = $w_area;
        break;
      }
    }
    if( empty($cityName) ){
      $w_flg = FALSE;
      $cityName = "東京";
    }
    $where = $w_city["$cityName"];

    // Yahoo!電力予報APIからPHP切片を取得
    $powerforecast_url 
      = "http://setsuden.yahooapis.jp/v1/Setsuden/electricPowerForecast?appid="
      .$api_key_yahoo."&area=tokyo&date=".$day;
    $forecastxml = simplexml_load_file($powerforecast_url);

    // 配列に押し込む
    $i = 0;
    $powers = array();
    foreach($forecastxml->Forecast as $forecast){
      if(!is_array($forecast)) {
        $hour[$i] = intval($forecast->Hour);
        $percent[$i] = round((intval($forecast->Usage)/intval($forecast->Capacity))*100);
        $i++;
      }
    }
    // 最大値検出
    array_multisort($percent, SORT_DESC, $hour, SORT_ASC);

    // メッセージ整形
    $maxs = array();
    $times = "";
    for($i = 0; $i < count($hour); $i++) {
      if($percent[$i] === $percent[0]) {
        array_push($maxs, $hour[$i]."時");
      } else {
        break;
      }
    }
    if($day === $today) {
      $timeName .= "のこれから";
    }
    if($i > 1) {
      $times = "のだいたい".$i."回";
    }
    $status = $timeName."の電力最大使用率は".$percent[0]."%で、ピークは"
      .join($maxs, "、").$times."だってさ。";
    return $status;
  }

  // 途中省略

  //リプライを作る
  function makeReplyTweets($replies, $replyFile, $replyPatternFile){
    if(empty($this->_replyPatternData[$replyPatternFile]) && !empty($replyPatternFile)){
      $this->_replyPatternData[$replyPatternFile] = $this->readPatternFile($replyPatternFile);
    }
    $replyTweets = array();
    foreach($replies as $reply){
      $status = "";
      // ここから追加
      if(strpos((string)$reply->text, "電力") !== FALSE) {
        $status = $this->makePowerForecastTweet((string)$reply->text);
      }
      // ここまで追加
      // リプライパターンと照合
      if(empty($status) && !empty($this->_replyPatternData[$replyPatternFile])){

  // 以降省略
}

特定のキーワードでリスト追加/削除

お前はガンダムだ。

SRWZ2再世編発売日ですし、EasyBotter.phpを改造して、botが勝手にリスト追加・削除をするようにします。
リストIDはAPIからXMLを確認するなどでご確認ください。

class EasyBotter {
  // 最初のプライベート変数宣言群に追加
  private $_list_id = リストID;

  // 途中省略

  //リストに追加
  private function addMemberToList($user_id) {
    //リストID
    $list_id = $this->_list_id;
    //API実行
    $response = $this->createListMember($list_id, $user_id);
    if(empty($response->error)) {
      // 追加成功のリプライ
      return "分かった……お前はガンダムだ。";
    } else {
      // 追加失敗のリプライ
      return "お前はガンダムにはなれない。";
    }
  }

  //リストから削除
  private function removeMemberFromList($user_id) {
    //リストID
    $list_id = $this->_list_id;
    //API実行
    $response = $this->destroyListMember($list_id, $user_id);
    if(empty($response->error)) {
      // 削除成功のリプライ
      return "お前はガンダムではない……そういうことか。";
    } else {
      // 削除失敗のリプライ
      return "そうか……。";
    }
  }

  // 途中省略

  //リプライを作る
  private function makeReplyTweets($replies, $replyFile, $replyPatternFile){
    if(empty($this->_replyPatternData[$replyPatternFile]) && !empty($replyPatternFile)){
      $this->_replyPatternData[$replyPatternFile] = $this->readPatternFile($replyPatternFile);
    }
    $replyTweets = array();
    foreach($replies as $reply){
      $status = "";
      // ここから変更
      if(strpos((string)$reply->text, "俺がガンダムだ") !== FALSE) {
        //リスト追加
        $status = $this->addMemberToList((string)$reply->user->id);
      } else if(strpos((string)$reply->text, "俺はガンダムにはなれない") !== FALSE) {
        //リスト削除
        $status = $this->removeMemberFromList((string)$reply->user->id);
      } else if(!empty($this->_replyPatternData[$replyPatternFile])){
      // ここまで変更
        // リプライパターンと照合
        foreach($this->_replyPatternData[$replyPatternFile] as $pattern => $res){
          if(preg_match("@".$pattern."@u",$reply->text, $matches) === 1){
            $status = $res[array_rand($res)];
            for($i=1;$i <count($matches);$i++){
              $p = "$".$i;
              $status = str_replace($p,$matches[$i],$status);
            }
            break;
          }
        }
      }
    // 以下省略
  }

  // 途中省略

  // 基本的なAPIを叩く
  // 以下のメソッドを最下部に追加
  private function createListMember($list_id, $user_id) {
    $url = "https://api.twitter.com/1/lists/members/create.xml";
    return $this->_setData($url, array("list_id" => $list_id, "user_id" => $user_id));
  }
  private function destroyListMember($list_id, $user_id) {
    $url = "https://api.twitter.com/1/lists/members/destroy.xml";
    return $this->_setData($url, array("list_id" => $list_id, "user_id" => $user_id));
  }
}

相手のキーフレーズ+定型文で作文

reply_pattern.phpにキーワードが見つからない場合に、相手の発言の中から重要らしい単語を抜き出して、定型文と組み合わせてリプライを作成します。

EasyBotter.phpの改造ですが、改造箇所が複数に亘るのでご注意ください。
また、Yahoo!デベロッパーズネットワークのWEB APIキーフレーズ抽出を使用しますので、前もってアプリケーションIDを取得しておいてください。

class EasyBotter {

  // 最初のプライベート変数宣言群に追加
  private $_api_key_yahoo = '取得したアプリケーションID';
  private $_api_url_yahoo_keyphrase = 'http://jlp.yahooapis.jp/KeyphraseService/V1/extract?appid=';

  // 途中省略

  // キーフレーズを元にした発言を作るメソッドを丸ごと追加
  // $text:キーフレーズ取得対象文字列(つまり相手のツイート)
  private function makeKeyphraseTweet($text) {
    $api_key_yahoo = $this->_api_key_yahoo;
    $api_url_yahoo_keyphrase = $this->_api_url_yahoo_keyphrase;
    $keyphrase = "";
    
    // 文字列から@とハッシュタグとURLを取り去る
    $text = preg_replace('/(#)(\w+)/', '', $text);
    $text = preg_replace('/(@)(\w+)/', '', $text);
    $text = preg_replace('/(https?|ftp)(:\/\/[-_.!~*\'()a-zA-Z0-9;\/?:\@&=+\$,%#]+)/', '', $text);

    // 文字列をUTF-8でエンコードし、さらにURLエンコード
    $sentence = urlencode(mb_convert_encoding($text, 'utf-8', 'auto'));
    
    //Yahoo!キーフレーズ抽出からXMLを取得
    $keyphrase_url = $api_url_yahoo_keyphrase.$api_key_yahoo."&sentence=".$sentence."&output=xml";
    $keyphrase_xml = simplexml_load_file($keyphrase_url);

    // キーフレーズの個数
    $keyphrase_num = count($keyphrase_xml->Result);
    
    for($i=0; $i<$keyphrase_num; $i++){
      $result = $keyphrase_xml->Result[$i];
      // スコア100のフレーズだけ持って出る
      if(htmlspecialchars($result->Score, ENT_QUOTES) == 100) {
        $keyphrase = htmlspecialchars($result->Keyphrase, ENT_QUOTES);
        break;
      }
    }

    // 返却バリエーションをつける
    $fix_sentence = array(
      "{keyphrase}について興味が湧いた。少し話を聞かせてくれないか。",
      "{keyphrase}……? それはいったい何のことだ?",
      "{keyphrase}がどうかしたのか?",
      "俺は{keyphrase}に興味なんてない。",
      "{keyphrase}については、俺も耳にしたことがある。",
      "{keyphrase}に関するデータ……オールクリア……。",
    );
    if($keyphrase !== "") {
      // 別途使うのでword.csvに保存しておく
      $fp = fopen('word.csv', 'a') or die('ファイルが開けません');
      fwrite($fp, "\"".$keyphrase."\"\n");
      fclose($fp);
      // 定型文をランダム選択
      $key = array_rand($fix_sentence);
      return str_replace("{keyphrase}", $keyphrase, $fix_sentence[$key]);
    } else {
      // キーフレーズがひとつも見つからなかった場合は[[END]]を返す
      return "[[END]]";
    }
    return "[[END]]";
  }

  // 途中省略

  //リプライを作る
  private function makeReplyTweets($replies, $replyFile, $replyPatternFile){
    // 途中省略
    if(empty($status) && !empty($replyFile)){
    //パターンになかった場合はランダムに
      // ここから変更
      if(rand(0,2) == 0){
        $status = $this->makeTweet($replyFile);
      } else {
        $status = $this->makeKeyphraseTweet((string)$reply->text);
        // 救済措置
        if($status == "[[END]]") {
          $status = $this->makeTweet($replyFile);
        }
      }
    // ここまで変更
    }
  }

  // 以降省略

}

よく考えると勝手にfunctionの前にprivateとか足してますね……。

使用関数

  • simplexml_load_file(url)
    指定したURLにアクセスしてXMLデータを取得する。

相手の場所を含む発言

EasyBotter.phpの下記部分を編集します。

//文章を変換する
private function convertText($text, $reply = FALSE){
  // 一部省略
  if(strpos($text,"{tweet}") !== FALSE && !empty($reply)){
    $tweet = preg_replace("@\.?\@[a-zA-Z0-9-_]+\s@u","",$reply->text);
    $text = str_replace("{tweet}",$tweet,$text);
  }
  //この下から追加
  if(strpos($text,"{place}") !== FALSE) {
    if(!empty($reply)){
      if(!empty($reply->user->location)) {
        $text = str_replace("{place}",$reply->user->location,$text);
      } else {
        $text = str_replace("{place}","目標施設",$text);
      }
    }else{
      $randomTweet = $this->getRandomTweet();
      if(!empty($randomTweet->user->location)) {
        $text = str_replace("{place}",$randomTweet->user->location,$text);
      } else {
        $text = str_replace("{place}","目標施設",$text);
      }
    }
  }
  //ここまで追加

  //フッターを追加
  $text .= $this->_footer;
  return $text;
}

文中の「目標施設」というのは、相手の場所が取得できなかった場合のデフォルトですので、適宜botのイメージに合うように置き換えるといいと思います。

使用データ

  • user->location
    相手の「場所」が格納されている。

定刻からずれて動作するようにcronを設定

cron自体が何かについては、Wikipediaなどでお調べください。

毎時0分、3分、6分、9分、12分、15分……57分に動作させる場合は、以下のように設定します。

0-59/3 * * * *

毎時1分、4分、7分、10分、13分、16分……58分に動作させる場合には、以下のように設定します。

1-58/3 * * * *

当botは同一サーバでホストしているので、サーバに一度に負荷がかからないように、上記のように1分ずらして実行させています。かけあい重視のbot同士の設定にも使えるかもしれません。