PHPで書いたLINE BOTからWatson ConversationのREST APIを叩く

<img src="https://cl.ly/201f1k0f410X/8_55.png" width=50%>

LINE Bot Awardsの締め切りも間もなくですね。
[LINE Bot Awards](https://botawards.line.me/ja/)

何かいい対話APIがないか調べていた所、Watson Conversationがかなりいい精度で学習機能もあるので、LINE BOTに組み込んでみることにしました。

# Watson Conversationの特徴
+ 対話シミュレーションを実現するシステム
+ 自然言語処理能力を備えている
+ インテント(意図)、エンティティ(対象)、ダイアログ(トリガー+レスポンス)を組み合わせて対話シナリオを作る
+ 対話のシナリオはブラウザで。割りと使いやすい。専門知識不要
+ 学習も同じく、ミスってるところをクリックして修正してやればすぐに反映
+ スクリプト書いて制御するようなことも可能。

# 登録

[Bluemix](https://console.ng.bluemix.net/)

のアカウントを作ってから、サービスの1つとしてConversationを紐付ける形。
「組織」を作る時に米国南部を選択しておかないと後でサービスが行方不明になるので気をつける。

結構面倒くさい。

# 対話シナリオの作成

[ここ](https://www.ibm.com/developerworks/community/wikis/home?lang=ja#!/wiki/%E3%81%93%E3%81%BF%E3%81%AD%E3%81%AE%E6%8A%80%E8%A1%93%E3%83%A1%E3%83%A2/page/Conversation%20%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%81%A7%E3%81%AE%E4%BC%9A%E8%A9%B1%E3%83%95%E3%83%AD%E3%83%BC%E4%BD%9C%E6%88%90%E5%AE%9F%E7%BF%92)に完璧にまとまっているのでこの通りにすれば簡単です。感謝。

# LINE BOTとの接続

LINE BOTをHerokuとPHPで作っていたのですが、Watson ConversationにはPHPのSDKがありません。Javaとかはあります。

ですがREST APIがあるのでこれを使って叩きます。

こんな感じ。

```php:index.php
<?php

require_once __DIR__ . '/vendor/autoload.php';

$httpClient = new \LINE\LINEBot\HTTPClient\CurlHTTPClient('YOUR_LINEBOT_ACCESS_TOKEN');
$bot = new \LINE\LINEBot($httpClient, ['channelSecret' => 'YOUR_LINEBOT_CHANNEL_SECRET']);

$signature = $_SERVER['HTTP_' . \LINE\LINEBot\Constant\HTTPHeader::LINE_SIGNATURE];
try {
  $events = $bot->parseEventRequest(file_get_contents('php://input'), $signature);
} catch(\LINE\LINEBot\Exception\InvalidSignatureException $e) {
  error_log('parseEventRequest failed. InvalidSignatureException => '.var_export($e, true));
} catch(\LINE\LINEBot\Exception\UnknownEventTypeException $e) {
  error_log('parseEventRequest failed. UnknownEventTypeException => '.var_export($e, true));
} catch(\LINE\LINEBot\Exception\UnknownMessageTypeException $e) {
  error_log('parseEventRequest failed. UnknownMessageTypeException => '.var_export($e, true));
} catch(\LINE\LINEBot\Exception\InvalidEventRequestException $e) {
  error_log('parseEventRequest failed. InvalidEventRequestException => '.var_export($e, true));
}

foreach ($events as $event) {
  if (!($event instanceof \LINE\LINEBot\Event\MessageEvent)) {
    error_log('Non message event has come');
    continue;
  }
  if (!($event instanceof \LINE\LINEBot\Event\MessageEvent\TextMessage)) {
    error_log('Non text message has come');
    continue;
  }
  
  $data = array('input' => array("text" => $event->getText()));
  
  if(getLastConversationData($event->getUserId()) !== PDO::PARAM_NULL) {
    $lastConversationData = getLastConversationData($event->getUserId());
    
    $data["context"] = array("conversation_id" => $lastConversationData["conversation_id"],
      "system" => array("dialog_stack" => array(array("dialog_node" => $lastConversationData["dialog_node"])), 
      "dialog_turn_counter" => 1,
      "dialog_request_counter" => 1));
  }

  // https://www.ibmwatsonconversation.com で取得可能
  $url = 'https://gateway.watsonplatform.net/conversation/api/v1/workspaces/' . 'YOUR_CONVERSATION_WORKSPACE_ID' . '/message?version=2016-09-20';
  $curl = curl_init($url);

  $options = array(
    CURLOPT_HTTPHEADER => array(
      'Content-Type: application/json',
    ),
    // https://console.ng.bluemix.net/ で取得可能
    CURLOPT_USERPWD => 'YOUR_BLUEMIX_USERNAME' . ':' . 'YOUR_BLUEMIX_PASSWORD',
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => json_encode($data),
    CURLOPT_RETURNTRANSFER => true,
  );

  curl_setopt_array($curl, $options);
  $jsonString = curl_exec($curl);
  $json = json_decode($jsonString, true);

  $conversationId = $json["context"]["conversation_id"];
  $dialogNode = $json["context"]["system"]["dialog_stack"][0]["dialog_node"];
  
  $conversationData = array('conversation_id' => $conversationId, 'dialog_node' => $dialogNode);
  setLastConversationData($event->getUserId(), $conversationData);
  
  $outputText = $json['output']['text'][count($json['output']['text']) - 1];
  
  replyTextMessage($bot, $event->getReplyToken(), $outputText);
}

function setLastConversationData($userId, $lastConversationData) {
  $conversationId = $lastConversationData['conversation_id'];
  $dialogNode = $lastConversationData['dialog_node'];

  // conversation_idとdialog_nodeをDBとかに保存
}

function getLastConversationData($userId) {
  // 保存しておいたconversation_idとdialog_nodeを取得
}

?>
```

conversation_idとdialog_nodeがないと過去の会話データが取れないので階層をまたぐ会話が出来ません。

# 結果

<img src="https://cl.ly/201f1k0f410X/8_55.png" width=50%>

LINE BOTから自分が作成したWatson Conversationのワークスペースにアクセスし、対話が出来るようになりました。

# まとめ

シナリオ対話型のチャットボットは、特に難しいことやらなくてもコンテンツやセンス次第で面白いボットが作れると思うので、何か作ってみたい方にはおすすめです。

尚、Bot Awardsにも対話エンジン部門があるようです。