LINE BOT API Trial を利用し PHP で BOT を作ってみた

# 概要
BOT API Trial Account がまだ登録出来たので、遊んでみました。
http://linecorp.com/ja/pr/news/ja/2016/1320

発言すると、内容がなんであろうとおいしそうなご飯の画像を返してくる BOT を作成しました。
自鯖に BOT と画像を仕込んでいます。

## 動作イメージ

![QS_20160505-194936.png](https://qiita-image-store.s3.amazonaws.com/0/124486/deeb1f42-80ef-5416-ebb8-bf4ed2a932a6.png)

## 処理イメージ
![スライド1.PNG](https://qiita-image-store.s3.amazonaws.com/0/124486/cf74a07a-9a17-2652-069a-02ed20b4f90f.png)

# 必要なもの
* Line developers アカウント
* PHP が動作し 443番ポートで https がお話しできるサーバ
    * オレオレ証明書は NG
    * Let's Encrypt は OK でした

# アカウント登録
[LINE Business Center](https://business.line.me/ja/) から BOT API Trial Account を取得しましょう。
先着1万人限定とのことですが 2016/05/05 でもアカウント登録出来ました。
LINE developers で以下のように登録されます。

![LINE developers.png](https://qiita-image-store.s3.amazonaws.com/0/124486/cd4f9382-bf1e-d44d-ad54-ee4f289e4412.png)

# アカウント情報の確認
作成したアカウントの Channel ID, Channel Secret, MID を確認しましょう。
後々利用しますので、控えておいてください。

# Callback URL の設定
アカウント情報画面右下の EDIT をクリックして Callback URL を設定します。
![LINE developers2.png](https://qiita-image-store.s3.amazonaws.com/0/124486/1e6ed14b-0352-ff9c-c1ac-ab9d344f355b.png)
この時 https にすることはもちろん、ポート番号 443 も忘れずに入れてください。
入れないと `URL or port number not sepcified.` となるらしいです(未検証)。

# Server IP Whitelist の設定
左ペインから Server IP Whitelist をクリックし API を呼び出す時、特定ソースIPのみ許可する設定を入れます。
入れないと弾かれます。
![LINE developers3.png](https://qiita-image-store.s3.amazonaws.com/0/124486/8809f8c4-9f66-4aa2-718d-2f3bd06a16ee.png)

# BOT の作成
```php:callback.php
<?php
// BOT の Channel ID, Channel Secret, MID を入力
$channel_id = "< Channel ID >";
$channel_secret = "< Channel Secret >";
$bot_mid = "< MID >";

//変数群
$log_file = "< ログファイルのパス && ファイル名 >";
$server_url = "< ServerName >";
$document_root = "< Documentroot >";
$pic_dir = "$document_root" . "< 画像が格納されているディレクトリ >";

//time zone
date_default_timezone_set('Asia/Tokyo');

// 関数群
//ユーザ情報取得する関数
function getDisplayName($to_mid){
    global $channel_id;
    global $channel_secret;
    global $bot_mid;
    $user_profiles_url = curl_init("https://trialbot-api.line.me/v1/profiles?mids=${to_mid}");
    curl_setopt($user_profiles_url, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($user_profiles_url, CURLOPT_HTTPHEADER, array(
        "X-Line-ChannelID: $channel_id",
        "X-Line-ChannelSecret: $channel_secret",
        "X-Line-Trusted-User-With-ACL: $bot_mid"
    ));
    $user_profiles_output = curl_exec($user_profiles_url);
    $user_json_obj = json_decode($user_profiles_output);
    global $displayname;
    $displayname = $user_json_obj->contacts{0}->displayName;
    curl_close($user_profiles_url);
}

//POSTするデータを作成する関数
function create_post_data($to_mid, $post_content){
    // toChannelとeventTypeは固定値なので、変更不要。
    global $post_data;
    $post_data = [
        "to"=>[$to_mid],
        "toChannel"=>"1383378250",
        "eventType"=>"138311608800106203",
        "content"=>$post_content
    ];
}

//相手に会話する内容をPOSTする関数
function post($post_data){
    global $channel_id;
    global $channel_secret;
    global $bot_mid;
    $post_url = curl_init("https://trialbot-api.line.me/v1/events");
    curl_setopt($post_url, CURLOPT_POST, true);
    curl_setopt($post_url, CURLOPT_CUSTOMREQUEST, 'POST');
    curl_setopt($post_url, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($post_url, CURLOPT_POSTFIELDS, json_encode($post_data));
    curl_setopt($post_url, CURLOPT_HTTPHEADER, array(
        'Content-Type: application/json; charser=UTF-8',
        "X-Line-ChannelID: $channel_id",
        "X-Line-ChannelSecret: $channel_secret",
        "X-Line-Trusted-User-With-ACL: $bot_mid"
    ));
    $result = curl_exec($post_url);
    curl_close($post_url);
}

// 相手からメッセージ受信
$recieve_json_string = file_get_contents('php://input');
$recieve_jsonObj = json_decode($recieve_json_string);
$to = $recieve_jsonObj->{"result"}[0]->{"content"}->{"from"};
$text = $recieve_jsonObj->{"result"}[0]->{"content"}->{"text"};
$content_type = $recieve_jsonObj->{"result"}[0]->{"content"}->{"contentType"};
$location = $recieve_jsonObj->{"result"}[0]->{"content"}->{"location"};
$op_type = $recieve_jsonObj->{"result"}[0]->{"content"}->{"opType"};
$params = $recieve_jsonObj->{"result"}[0]->{"content"}->{"params"};

//DisplayName 取得
//date, mid, displayName, text, contentType をログ出力
getDisplayName($to);
file_put_contents($log_file, date("Y/m/d H:i:s") . " mid:${to}, displayName:${displayname}, text:${text}, contentType:${content_type}" . PHP_EOL, FILE_APPEND);

//会話処理
if( $op_type === 4 ){
    // 友達登録時に会話する
    getDisplayName($params[0]);
    $response_format_text = ['contentType'=>1,"toType"=>1,"text"=>"飯テロ BOT の登録ありがとうございます, ${displayname}さん."];
    create_post_data($params[0], $response_format_text);
    post($post_data);
} else if( $op_type === 8 ){
    // ブロック時はなにもせず正常終了する
    exit(0);
} else {
    // 送信する画像をランダムで選ぶ
    // glob で取得した画像総数を max に代入し、ランダムで選ばれた配列番号を num_list に代入
    // パスを変換し、末尾に画像ファイル名を追加
    $pic_array  = glob ("${pic_dir}*.jpg");
    $max = count($pic_array);
    $num_list = array_rand(range(1,$max),1);
    $pic = str_replace($document_root,"$server_url",$pic_array[$num_list]);
    
    $response_format_image = ['contentType'=>2,"toType"=>1,'originalContentUrl'=>"$pic","previewImageUrl"=>"$pic"];
    create_post_data($to, $response_format_image);
    post($post_data);
}
```

# BOT 作成時のハマりポイント
ハマったポイントがありましたので紹介します。

* callback URL を設定したのに BOT が応答してくれない
    * callback URL 登録後、数時間経たないと反映されないようです。待ちましょう。
* Server IP Whitelist を設定したのに BOT が応答してくれない
    * callback URL ほどではありませんでしたが、時間経過しないと反映されないようです。待ちましょう。
それでもダメなら API のレスポンスをチェックしましょう。コードの $result 変数の中身を見るとか。

# 拡張したくなったら
[BOT API](https://developers.line.me/bot-api/overview) を参照ください。

# 最後に
ただ単にランダムで画像を返す BOT なので、あまり面白みがありませんでした。
しかし PHP を初めて触るには手頃な難易度でした。
今後は対話相手の情報や、時期に応じた内容を加味し対話するような BOT が作成できると思います。
スタンプが送られてきたら、それに近い XXX の画像を返す！とか出来たら、自分だったら面白がって何度も遊んでそうです。

また作成にあたり、下記ページを参考にさせていただきました。

* [LINE BOT APIを利用した地獄のミサワBOTの作り方](http://qiita.com/srea/items/58ba0f7d870a6ee3da2a)
* [LINE BOT API でのハマりどころ](http://yone098.hatenablog.com/entry/2016/04/10/032451)