Blog
Blog

Blog

LINE BOT AWARDSにエントリーしてみました!(後半)

2017年3月07日

haino

こんにちは。クリエイティブディレクターの灰野です。今日は「LINE BOT AWARDSにエントリーしてみました!」の後半をお届けします。前半ではherokuの各種設定、またLINE BUSSINESS CENTERとぐるなびAPIのアカウント取得などについて扱いました。今回はいよいよコーディングに入っていきたいと思います。

こんなものを作ってみます。前回も紹介させてもらいましたが「ラーメンにゃんこ先生」というボットです。

「LINE BOT AWARDSにエントリーしてみました!」の後半では、位置情報からラーメン屋の店舗情報をカルーセルで返すというコードを書いていきます。

まず、composerが必要となりますので、インストールされていないようでしたら、まずそちらから行ないます。ターミナルを起動して下記コマンドを入力してみてください。

brew install homebrew/php/composer

次いで、composerでSDKを導入します。前回作成したディレクトリに移動してから下記コマンドを入力します。

composer require linecorp/line-bot-sdk

導入に成功すると、下記のような二つのファイルと一つのフォルダが現れます。

では、コーディングに入っていきましょう!

<とりあえずおうむ返しbotを作ってみよう!>

いきなり作り込む前に、とりあえずテキストメッセージを送った時に、そのままおうむ返しするテストコードを書いてみましょう。

index.php

events[0];
$http_client = new \LINE\LINEBot\HTTPClient\CurlHTTPClient(getenv('CHANNEL_ACCESS_TOKEN'));
$bot = new \LINE\LINEBot($http_client, ['channelSecret' => getenv('CHANNEL_SECRET')]);

// メッセージ識別子を取得
$event_type = $event->type;
$event_message_type = $event->message->type;

// メッセージの場合
if ('message' == $event_type) {

	// テキストメッセージの場合
	if ('text' == $event_message_type) {

		// メッセージの取得
		$text = $event->message->text;

		// メッセージを受け取ったらメッセージをそのまま返す
		$text_message_builder = new \LINE\LINEBot\MessageBuilder\TextMessageBuilder($text);
		$response = $bot->replyMessage($event->replyToken, $text_message_builder);
	}
}

// デバッグ
echo $response->getHTTPStatus() . ' ' . $response->getRawBody();

さあ、どうでしょう。おうむ返しするでしょうか。早速herokuにデプロイしてみましょう!

下記のようなgitコマンドでデプロイします。

heroku login
git add .
git commit -am "make it better"
git push heroku master

LINEアプリ上での試し方ですが、LINE BUSSINESS CENTERにログインして、前回設定したアカウント(bot)の「LINE developers」の画面に表示されているQRコードからbotと友達になることができます。

「あれ?あれ?…うんともすんとも言わないじゃないかぁ!!」

…と思われたはずです。そう。これを実行するにはSDKをほんの少しだけ改修しないといけないのです。自分はこのために何時間も無駄にしてしまいました…。

LINEのbotはIPが固定でないと動かないので、herokuにFixieを導入したわけですが、Fixieのプロキシを通さないとダメなのです。下記はそのための改修というわけです。

/vendor/linecorp/line-bot-sdk/src/HTTPClient/CurlHTTPClient.php
86行目(改修前)

    private function sendRequest($method, $url, array $additionalHeader, array $reqBody)
    {
        $curl = new Curl($url);

        $headers = array_merge($this->authHeaders, $this->userAgentHeader, $additionalHeader);

        $options = [
            CURLOPT_CUSTOMREQUEST => $method,
            CURLOPT_HTTPHEADER => $headers,
            CURLOPT_HEADER => false,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_BINARYTRANSFER => true,
            CURLOPT_HEADER => true,
        ];

/vendor/linecorp/line-bot-sdk/src/HTTPClient/CurlHTTPClient.php
86行目(改修後)

    private function sendRequest($method, $url, array $additionalHeader, array $reqBody)
    {
        $curl = new Curl($url);

        $headers = array_merge($this->authHeaders, $this->userAgentHeader, $additionalHeader);

        $options = [
            CURLOPT_CUSTOMREQUEST => $method,
            CURLOPT_HTTPHEADER => $headers,
            CURLOPT_HEADER => false,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_BINARYTRANSFER => true,
            CURLOPT_HEADER => true,
            // 以下を追加します。
            CURLOPT_HTTPPROXYTUNNEL => true,
            CURLOPT_PROXYPORT => '80',
            CURLOPT_PROXY => getenv('FIXIE_URL'),
        ];

さぁ、今度はどうでしょうか?うまくいきましたか?

<店舗情報を表示してみよう!>

さて次は、いよいよぐるなびAPIを使って店舗情報を取得し、その内容をカルーセルに表示するコードを書いていきます。

先ほど書いたおうむ返しのコードを下記のように改修してみましょう!

index.php

events[0];
$http_client = new \LINE\LINEBot\HTTPClient\CurlHTTPClient(getenv('CHANNEL_ACCESS_TOKEN'));
$bot = new \LINE\LINEBot($http_client, ['channelSecret' => getenv('CHANNEL_SECRET')]);

// メッセージ識別子を取得
$event_type = $event->type;
$event_message_type = $event->message->type;

// メッセージの場合
if ('message' == $event_type) {

	// テキストメッセージの場合
	if ('text' == $event_message_type) {

		// メッセージの取得
		$text = $event->message->text;

		// メッセージを受け取ったらメッセージをそのまま返す
		$text_message_builder = new \LINE\LINEBot\MessageBuilder\TextMessageBuilder($text);
		$response = $bot->replyMessage($event->replyToken, $text_message_builder);
	}

	// 位置情報メッセージの場合
	else if ('location' == $event_message_type) {
		// 緯度経度情報の取得
		$latitude = $event->message->latitude;
		$longitude = $event->message->longitude;

		// ぐるなびWebサービス利用するためのURLの組み立て
		$url = buildGnaviUrl($latitude, $longitude);

		// ぐるなびAPI実行
		$json = file_get_contents($url);
		$results = resultsParse($json);

		// 店舗情報の取得内容に応じて処理
		if($results != null) {

			// いつも同じ結果にならないように配列をシャッフル
			shuffle($results);

			// カルーセル生成を5以下に制限
			if (count($results) > 5) {
				$max = 5;
			} else {
				$max = count($results);
			}

			// カルーセル生成
			$columns = [];
			for ($i = 0; $i < $max; $i++) {
				// カルーセルに付与するボタンを作る
				$action = new \LINE\LINEBot\TemplateActionBuilder\UriTemplateActionBuilder('店舗詳細', $results[$i]['url']);

				// PRは空欄であることが多いが、あれば出力
				$info ='';
				if ($results[$i]['pr']) {
					$info = $results[$i]['address'] . "\n" . $results[$i]['pr'] . "\n" . 'Powered by ぐるなび';
				} else {
					$info = $results[$i]['address'] . "\n" . 'Powered by ぐるなび';
				}

				// カルーセルのカラムを生成
				$column = new \LINE\LINEBot\MessageBuilder\TemplateBuilder\CarouselColumnTemplateBuilder($results[$i]['name'], $info, $results[$i]['image_url'], [$action]);
				$columns[] = $column;
			}

			// カラム配列からカルーセル生成
			$carousel_template_builder = new \LINE\LINEBot\MessageBuilder\TemplateBuilder\CarouselTemplateBuilder($columns);
			$template_message = new \LINE\LINEBot\MessageBuilder\TemplateMessageBuilder('店舗情報一覧(5件)', $carousel_template_builder);
			$message = new \LINE\LINEBot\MessageBuilder\MultiMessageBuilder();
			$message->add($template_message);
			$response = $bot->replyMessage($event->replyToken, $message);

		} else {
			// 検索結果なしの場合
			$text_message_builder = new \LINE\LINEBot\MessageBuilder\TextMessageBuilder('ごめんにゃ。近くにラーメン屋さんはないにゃ。。。');
			$response = $bot->replyMessage($event->replyToken, $text_message_builder);
		}
	}

	// テキスト、位置情報以外のメッセージの場合
	else {
		$text_message_builder = new \LINE\LINEBot\MessageBuilder\TextMessageBuilder('スタンプを送ってくれたのかにゃ?でも反応できないにゃ。ごめんにゃ????');
		$response = $bot->replyMessage($event->replyToken, $text_message_builder);
	}
}

// お友達追加時
else if ('follow' == $event_type) {
	// お友達追加時のメッセージ
	$text_message_builder = new \LINE\LINEBot\MessageBuilder\TextMessageBuilder('お友達追加ありがとにゃ!よろしくにゃ????');
	$response = $bot->replyMessage($event->replyToken, $text_message_builder);
}

// グループ追加時
else if ('join' == $event_type) {
	// グループ追加時のメッセージ
	$text_message_builder = new \LINE\LINEBot\MessageBuilder\TextMessageBuilder('ようこそ!ラーメンについて話そうにゃ????よろしくにゃ????');
	$response = $bot->replyMessage($event->replyToken, $text_message_builder);
}

// その他のアクセス(ブラウザからのリクエストなど)
else {
	$text_message_builder = new \LINE\LINEBot\MessageBuilder\TextMessageBuilder('不正なアクセス');
	$response = $bot->replyMessage($event->replyToken, $text_message_builder);

	echo $response->getHTTPStatus() . ' ' . $response->getRawBody();
}

// ぐるなびAPI用のURLを生成
function buildGnaviUrl($latitude, $longitude) {

	// ぐるなびAPI設定
	$gnavi_uri = 'http://api.gnavi.co.jp/RestSearchAPI/20150630/';
	$gnavi_acckey = getenv('GNAVI_API_KEY');
	$gnavi_format = 'json';
	$gnavi_range = 3;
	$gnavi_category = 'RSFST08008'; // ラーメン

	// URL組み立て
	$url  = sprintf('%s%s%s%s%s%s%s%s%s%s%s%s%s', $gnavi_uri, '?format=', $gnavi_format, '&keyid=', $gnavi_acckey, '&latitude=', $latitude, '&longitude=', $longitude, '&range=', $gnavi_range, '&category_s=', $gnavi_category);

	return $url;
}

// ぐるなびAPIの結果をパース
function resultsParse($json) {
	$obj  = json_decode($json);

	// 連想配列初期化
	$results = [];

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

	if ($total_hit_count !== null) {
		$n = 0;
		foreach($obj->{'rest'} as $val) {

			// 店名
			if (checkString($val->{'name'})) {
				$results[$n]['name'] = $val->{'name'};
			}

			// 住所
			if (checkString($val->{'address'})) {
				$results[$n]['address'] = $val->{'address'};
			}

			// ぐるなびURL
			if (checkString($val->{'url'})) {
				$results[$n]['url'] = $val->{'url'};
			}

			// 店舗画像
			if (checkString($val->{'image_url'}->{'shop_image1'})) {
				$results[$n]['image_url'] = $val->{'image_url'}->{'shop_image1'};
			} else {
				$results[$n]['image_url'] = '※※※ 任意の画像URL ※※※';
			}

			// PR
			if (checkString($val->{'pr'})) {
				$results[$n]['pr'] = $val->{'pr'};
			} else {
				$results[$n]['pr'] = '';
			}

			$n++;
		}
	}
	return $results;
}

// 文字列であるかをチェック
function checkString($input) {
	if(isset($input) && is_string($input)) {
		return true;
	} else {
		return false;
	}
}

詳細はコードを読んでいただきたいと思いますが、いくつか注意点があるので補足します。ところどころgetenv関数で環境変数を読んでいますので、herokuの環境変数としてあらかじめ登録しておいてください。詳細は前回のエントリーをご覧ください。

店舗情報をカルーセルで表示するようにコードを記述していますが、APIの仕様で5件までしか表示されないようになっているようです。それで、配列の数を5までに制限しないといけません。

また、カルーセル上部には店舗画像が表示されるようになっていますが、ぐるなびに登録されている店舗情報の画像がおおむね空で帰ってくることが多かったので、画像がないときのための「No Image」が必要となります。「※※※ 任意の画像URL ※※※」の部分にご自身で準備された画像URLと置き換えるようになさってください。適当な画像をherokuに置けばいいと思います。

いかがだったでしょうか?$gnavi_categoryという変数内で定義しているぐるなびの店舗カテゴリ識別子をあれこれ変えれば、いろんなお店を参照できそうですね!また上記のコードはテキストメッセージが来た時におうむ返ししてしまうので、ご自身でカスタマイズされると面白いボットが作れそうですね!!

みなさんも是非チャレンジしてみてください。

それでは今日はこの辺で。

<参考URL>
herokuとphpでオウム返しlinebotを作る(Mac限定)
http://qiita.com/sio-funmatsu/items/719cd63dd01693303ff2
line-bot-sdk-phpでLineBotを動かしてみる
http://godan09.hatenablog.com/entry/2016/10/02/112111
【LINE BOT API】位置情報を送ると近くのラーメン屋さんを教えてくれるBOTを作ってみました
http://qiita.com/kota2_0/items/17893b0d442ef27a1691

※とても参考なりました。この場を借りてお礼申しあげます。