【LINE Messaging API】PHP素人がコピペを駆使してラーメン屋BOTを作ってみた。

# 概要
LINE BOTを使って、位置情報を送ると周辺のラーメン情報を答えさせます。
先人達が数々のすばらしい記事をあげているので、
何番煎じだ？というところですが自分メモとして投稿します。

結果としてとりあえず動いていますが、
ソースを完全に理解できていない部分もあり、まだまだ勉強中です。
不備不足をご指摘いただけると助かります。

# 環境や利用サービス
windows7 64bit
[LINE API](https://business.line.me/ja/services/bot)
[ぐるなびAPI](APIhttp://api.gnavi.co.jp/api/)
PHP
heroku

# 参考記事
- [【LINE BOT API】位置情報を送ると近くのラーメン屋さんを教えてくれるBOTを作ってみました](http://qiita.com/kota2_0/items/17893b0d442ef27a1691)
- [LINE BOT API Trialでできる全ての事を試してみた](http://qiita.com/betchi/items/8e5417dbf20a62f2239d)
- [【LINE Bot】Heroku+PHP+雑談対話APIで会話Bot作ってみた](http://qiita.com/shanonim/items/f90ee13180ee70a1903d)
- [ぐるナビAPIのサンプルコードでポエム書いた](http://qiita.com/mpyw/items/8dd7f39e8e297db4ae02)
- [わずか5分。新LINE Messaging APIでbotを作ってみた手順全公開](https://bita.jp/dml/line-messaging-api-exp)

# 実装手順
1. LINEBOTアカウント取得
1. ぐるなびAPI取得
1. herokuアカウント取得
1. [こちらの記事](https://bita.jp/dml/line-messaging-api-exp)を参考にサンプルプログラムソースをherokuにデプロイ
1. [Heroku CLI.](https://devcenter.heroku.com/articles/heroku-cli)をインストールする
1. リモートリポジトリをクローンする
1. [こちらの記事](http://qiita.com/kota2_0/items/17893b0d442ef27a1691)を参考にcallback.phpを編集する（ソースは後述）
1. 編集したcallback.phpをherokuにPUSHする
1. herokuの環境変数※1.にぐるなびAPIとLINEのアクセスキー（gnavi_access_key、LINE_CHANNEL_ACCESS_TOKEN）を登録する
1. LINE管理者メニュー※2.にコールバックURLを設定（例：https://sample.herokuapp.com/callback.php)

※1. heroku環境変数 (setting→Config Variables)
※2. LINE管理者メニュー (LINE BUSINESS CENTER→LINE Developers→Webhook URL)

PHPというかWeb開発はほぼ素人だったため、理屈は置いといて
まずはサンプルプログラムが動くようにしました（1.~4.）。
そこから少しずつ改修→テスト→改修といった流れで実装していきました。

#ソースファイル
```php:callback.php
<?php
// アクセスキー
$gnaviaccesskey = getenv('gnavi_access_key');
$accessToken = getenv('LINE_CHANNEL_ACCESS_TOKEN');

//ユーザーからのメッセージ取得
$json_string = file_get_contents('php://input');
$jsonObj = json_decode($json_string);

$type = $jsonObj->{"events"}[0]->{"message"}->{"type"};
//メッセージ取得
$text = $jsonObj->{"events"}[0]->{"message"}->{"text"};
//ReplyToken取得
$replyToken = $jsonObj->{"events"}[0]->{"replyToken"};

//緯度取得
$latitude = $jsonObj->{"events"}[0]->{"message"}->{"latitude"};
//経度取得
$longitude = $jsonObj->{"events"}[0]->{"message"}->{"longitude"};

//エンドポイントのURIとフォーマットパラメータを変数に入れる
$uri   = "http://api.gnavi.co.jp/RestSearchAPI/20150630/";
//APIアクセスキーを変数に入れる
$acckey= $gnaviaccesskey;
//返却値のフォーマットを変数に入れる
$format= "json";
//緯度・経度、範囲を変数に入れる

// 業態がラーメン屋さんを意味するぐるなびのコード(大業態マスタ取得APIをコールして調査)
$category_s = "RSFST08008";

$hit_per_page = "5";

//緯度経度は日本測地系で日比谷シャンテのもの。範囲はrange=2で500m以内を指定している。
$range = 2;

//URL組み立て
$url  = sprintf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", $uri, "?format=", $format, "&keyid=", $acckey, "&latitude=", $latitude,"&longitude=",$longitude,"&category_s=",$category_s,"&range=",$range,"&hit_per_page=",$hit_per_page);
//API実行
$json = file_get_contents($url);
//取得した結果をオブジェクト化
$obj  = json_decode($json);

$total_hit_count = $obj->{'total_hit_count'};
$result = "";

//店舗情報の格納配列
$i = 1;

//イケてないけど、$response_format_textにループで配列データ格納しようとしても
//失敗してしまうのでベタうちにする。
$get_name1 ="該当なし";
$get_url1 ="http://www.yahoo.co.jp/";

$get_name2 ="該当なし";
$get_url2 ="http://www.yahoo.co.jp/";

$get_name3 ="該当なし";
$get_url3 ="http://www.yahoo.co.jp/";

$get_name4 ="該当なし";
$get_url4 ="http://www.yahoo.co.jp/";

$get_name5 ="該当なし";
$get_url5 ="http://www.yahoo.co.jp/";


//結果をパース
if ($total_hit_count === null) {
    $result .= "近くにラーメン屋さんはありません。";
}else{
    $result .= "近くにあるラーメン屋さんです。\n\n";

    foreach((array)$obj as $key => $val){
      if(strcmp($key, "rest") == 0){
          foreach((array)$val as $restArray){
              //最寄駅入れようかと思ったけど、任意位置で計測するから駅近とは限らないことに気づいた
              //$station ="";
              //$station .= $restArray->{"access"}->{"station"};
              //$station .= $restArray->{"access"}->{"exit"};
              //$station .= $restArray->{"access"}->{"walk"} . "分";

              //switch文だとLineにレスが返らないのでif文で対応
                if($i===1){
                  $get_name1 = $restArray->{"name"};
                  $get_url1 = $restArray->{"url"};
                  $get_address1 = $restArray->{"address"};
                }

                if($i===2){
                  $get_name2 =$restArray->{"name"};
                  $get_url2 =$restArray->{"url"};
                  $get_address2 = $restArray->{"address"};
                }

                if($i===3){
                  $get_name3 =$restArray->{"name"};
                  $get_url3 =$restArray->{"url"};
                  $get_address3 = $restArray->{"address"};
                }

                if($i===4){
                  $get_name4 =$restArray->{"name"};
                  $get_url4 =$restArray->{"url"};
                  $get_address4 = $restArray->{"address"};
                }

                if($i===5){
                  $get_name5 =$restArray->{"name"};
                  $get_url5 =$restArray->{"url"};
                  $get_address5 = $restArray->{"address"};
                }

              $i++;
          }
     
          }
    }
}

//返信データ作成
$response_format_text = [
  "type" => "template",
  "altText" => "候補をご案内しています。(Powered by ぐるなび)",
  "template" => [
    "type" => "carousel",
    "columns" => [
        [
          "title" => $get_name1,
          "text" => "住所:".$get_address1,
          "actions" => [
            [
                "type" => "uri",
                "label" => "URL",
                "uri" => $get_url1
            ]
          ]
        ],
        [
          "title" => $get_name2,
          "text" => "住所:".$get_address2,
          "actions" => [
            [
                "type" => "uri",
                "label" => "URL",
                "uri" => $get_url2
            ]
          ]
        ],
        [
          "title" => $get_name3,
          "text" => "住所:".$get_address3,
          "actions" => [
            [
                "type" => "uri",
                "label" => "URL",
                "uri" => $get_url3
            ]
          ]
        ],
        [
          "title" => $get_name4,
          "text" => "住所:".$get_address4,
          "actions" => [
            [
                "type" => "uri",
                "label" => "URL",
                "uri" => $get_url4
            ]
          ]
        ],
        [
          "title" => $get_name5,
          "text" => "住所:".$get_address5,
          "actions" => [
            [
                "type" => "uri",
                "label" => "URL",
                "uri" => $get_url5
            ]
          ]
        ]
    ]
  ]
];


$post_data = [
	"replyToken" => $replyToken,
	"messages" => [$response_format_text]
	];

$ch = curl_init("https://api.line.me/v2/bot/message/reply");
    curl_setopt($ch, CURLOPT_POST,true);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_data));
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
      'Content-Type: application/json; charser=UTF-8',
      'Authorization: Bearer ' . $accessToken
    ));
  $result = curl_exec($ch);
  curl_close($ch);
```

# はまったところ（未解決）
ぐるなびAPIから取得できる[PR]や[画像]を持って来ようとするとBOTが反応しなくなりました。
恐らく取得したデータの形式が制約に引っかかってると思われます。
（必須なのにNULLとか画像サイズが大きすぎるとか）

- PRは文字列が長すぎるかも？ということで[pr_long]から[pr_short]にしてもダメ。
- 画像はサイズ等があっていないかも？ということで[いらすとやさん](http://www.irasutoya.com/)の画像をダウンロードしてGithub上に配置し、取得しようとしてもダメ。

諦めてぐるなびAPIから取得する項目は[店名][住所][URL]のみに絞りました。

あとたぶんPHPが理解できていないだけだと思いますが、
switch文を使うと反応しなくなったので諦めてif文で対応しました。

# 次やりたいこと
- 勉強もかねてPOSTデータのテスト環境を構築したい。（上記は毎回スマホでテストしていたので大変だった）
- ぐるなびAPIのレスポンスが一部だけ無い場合でも、LINE側にレスポンスが返せるようにする。
- [Docomo雑談対話API](https://dev.smt.docomo.ne.jp/?p=docs.api.page&api_name=dialogue&p_name=api_1)の実装
- 今回省略したGithubとCloud9とherokuを連携する方式についても記事化する。

# 感想
「ちょっとLINE BOT遊びで作ってみたんすよ」と言えるのは楽しいです。