PHP×LINE Messaging APIで作るチャットボット

#１）はじめに

<img src="https://qiita-image-store.s3.amazonaws.com/0/95865/f1a2de48-8165-8bd8-fe10-23c73819d1a8.jpeg" width="200">

概要は公式のページで確認しましょう。

Messaging APIの概要はこちら
https://developers.line.me/ja/docs/messaging-api/overview/

#２）チャネルを作成する
Messaging APIを利用するには
https://developers.line.me/ja/docs/messaging-api/getting-started/

公式のドキュメント（上記）を読みながら、

- コンソールへのログイン
- 開発者登録
- 新規プロバイダー作成
- チャネル作成

まで進めます。

■アクセストークン（ロングターム）
後ほどプログラム内で使うのでコピーしておきましょう。

■Web hook URL
実際に作ったプログラムを設置するURLです。※SSL必須

#３）プログラムを作る
今回は簡単に仕組みを理解するため、PHPを使います。

##イベント種別
イベント種別にはメッセージ・フォロー・フォロー解除・参加・退出・ポストバック・ビーコン・アカウント連携など様々ありますが、今回は

- メッセージイベント（メッセージが送信されると発生）
- ポストバックイベント（ポストバックオプションに返事されると送信）

のみを使います。

Botに対してテキストなどを送信すると、下記のようなJSON形式でWeb hook URLにデータが送られるので、更にJSON形式でデータを送り返すことで、ユーザーに反応が帰ります。

```json:テキストメッセージの例
{
    "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
    "type": "message",
    "timestamp": 1462629479859,
    "source": {
        "type": "user",
        "userId": "U4af4980629..."
    },
    "message": {
        "id": "325708",
        "type": "text",
        "text": "Hello, world!"
    }
}
```

```json:ポストバックイベントの例
{
    "replyToken": "b60d432864f44d079f6d8efe86cf404b",
    "type": "postback",
    "timestamp": 1513669370317,
    "source": {
        "userId": "U91eeaf62d...",
        "type": "user"
    },
    "postback": {
        "data": "storeId=12345",
        "params": {
            "datetime": "2017-12-25T01:00"
        }
    }
}
```

実際にPHPでコールバックするプログラムを組んでみます。
今回は仕組みの理解が目的なので、[公式のSDK](https://github.com/line/line-bot-sdk-php)は使わずにやってみたいと思います。

今回作るのは、下記のような仕様とします。

| ユーザーの動作      | Botの動作         |
|:-----------------|:------------------|
| テキストを送ると    | テキストを返す（オウム返し）      |
| スタンプを送ると    | スタンプを返す      |
| 画像を送ると       | 画像を返す          |
| 位置情報を送ると   | 皇居の位置情報を返す       |

※ ユーザーに送信する画像のURLはHTTPSでないといけません
※ 元からアプリに入っている、[LINEの基本的なスタンプ（PDF）](https://developers.line.me/media/messaging-api/sticker_list.pdf)のみ使えます

| ユーザーの動作      | Botの動作         |
|:-----------------|:------------------|
| 「予約」と送ると    | 選択メニューを出す   |

▼選択メニューの動作

| ユーザーの動作      | Botの動作         |
|:-----------------|:------------------|
| 「予約しない」     | 「何もしませんでした。」|
| 「期日を指定」     | 「【2018-01-01】にご予約を承りました。」|

##フォルダ構成
/line/
　├ callback.php
　├ original_image.png
　└ prev_image.png

##完成イメージ
<img src="https://qiita-image-store.s3.amazonaws.com/0/95865/f7ac7857-82a4-92d4-aef2-6dda4b45e618.png" width="400">

```php:callback.php
$access_token = '[取得したアクセストークン]';

// APIから送信されてきたイベントオブジェクトを取得
$json_string = file_get_contents('php://input');

// 受け取ったJSON文字列をデコード
$json_obj = json_decode($json_string);

// このイベントへの応答に使用するトークン
$reply_token = $json_obj->{'events'}[0]->{'replyToken'};

// イベント種別（今回は2種類のみ）
// message（メッセージが送信されると発生）
// postback（ポストバックオプションに返事されると送信）
$type = $json_obj->{'events'}[0]->{'type'};

// メッセージオブジェクト（今回は4種類のみ）
// text（テキストを受け取った時）
// sticker（スタンプを受け取った時）
// image（画像を受け取った時）
// location（位置情報を受け取った時）
$msg_obj = $json_obj->{'events'}[0]->{'message'}->{'type'};

if($type === 'message') {
	// メッセージ受け取り時
	if($msg_obj === 'text') {
		// テキストを受け取った時
		$msg_text = $json_obj->{'events'}[0]->{'message'}->{'text'};
		if($msg_text === '予約') {
			$message = array(
				'type' => 'template',
				'altText' => 'いつのご予約ですか？',
				'template' => array(
					'type' => 'confirm',
					'text' => 'いつのご予約ですか？',
					'actions' => array(
						array(
							'type' => 'postback',
							'label' => '予約しない',
							'data' => 'action=back'
						), array(
							'type' => 'datetimepicker',
							'label' => '期日を指定',
							'data' => 'datetemp',
							'mode' => 'date'// date：日付を選択します。time：時刻を選択します。datetime：日付と日時を選択します。
						)
					)
				)
			);
		} else {
			$message = array(
				'type' => 'text',
				'text' => '【'.$msg_text.'】とは何ですか？'
			);
		}
	} elseif($msg_obj === 'sticker') {
		// スタンプを受け取った時
		$message = array(
			'type' => 'sticker',
			'packageId' => '1',
			'stickerId' => '3'
		);
	} elseif($msg_obj === 'image') {
		// 画像を受け取った時
		$message = array(
			'type' => 'image',
			// オリジナル画像（タップしたら表示される画像）
			'originalContentUrl' => 'https://XXXXXXXXXX/line/original_image.png',
			// サムネイル画像（トーク中に表示される画像）
			'previewImageUrl' => 'https://XXXXXXXXXX/line/preview_image.png'
		);
	} elseif($msg_obj === 'location') {
		// 位置情報を受け取った時
		$message = array(
			'type' => 'location',
			'title' => '皇居',
			'address' => '〒100-8111 東京都千代田区千代田１−１',
			'latitude' => 35.683798,
			'longitude' => 139.754182
		);
	}
} else if($type === 'postback') {
	// ポストバック受け取り時

	// 送られたデータ
	$postback = $json_obj->{'events'}[0]->{'postback'}->{'data'};

	if($postback === 'datetemp') {
		// 日にち選択時
		$message = array(
			'type' => 'text',
			'text' => '【'.$json_obj->{'events'}[0]->{'postback'}->{'params'}->{'date'}.'】にご予約を承りました。'
		);
	} elseif($postback === 'action=back') {
		// 戻る選択時
		$message = array(
			'type' => 'text',
			'text' => '何もしませんでした。'
		);
	}
}

$post_data = array(
	'replyToken' => $reply_token,
	'messages' => array($message)
);

// CURLでメッセージを返信する
$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 ' . $access_token
));
$result = curl_exec($ch);
curl_close($ch);
```