LINEスマートスピーカー Clovaスキル(Custom Extension)をNode-REDで作る

LINEスマートスピーカーClovaとNode-REDとの連携サンプルです。[[ハンズオン] LINE Clova×IBM WatsonでAIを学ぶ](https://linedev.connpass.com/event/96271/ "[ハンズオン] LINE Clova×IBM WatsonでAIを学ぶ")でのIBM Cloud Node.jsでCustom Extensionを作成しているものを、Node-REDに移行しました。

# 必要なもの
- IBM Cloudのアカウント（無料のライトプランでOK）
 - [ここから取得できます](https://console.bluemix.net/ )
- LINE アカウント

# スキルの登録 & 対話モデルの作成
[[ハンズオン] LINE Clova×IBM WatsonでAIを学ぶ](https://linedev.connpass.com/event/96271/ "[ハンズオン] LINE Clova×IBM WatsonでAIを学ぶ")の資料[「LINE Clova Skill ハンズオン資料」](https://www.dropbox.com/s/547siacriaudy4f/CEK_Handson%1D_0729.pdf?dl=0)を30ページまで実施します(「Herokuの準備」手前まです)。

# Node-RED Starterサービスの作成
IBM Cloud に[ログイン](https://console.bluemix.net/ )してNode-RED Starterサービスを作成します。
こちら[「Node-RED で遊んでみる第一歩: Web ページを表示してみる」の簡単な始め方](https://qiita.com/yamachan360/items/e3c552c1d2e83e5bf602#%E7%B0%A1%E5%8D%98%E3%81%AA%E5%A7%8B%E3%82%81%E6%96%B9)を参照して、作成お願いします。

以下のNode-REDエディタが開いた状態まで進めます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/294516/04c2da09-ac5f-10c0-cf51-bc3859f2b3a8.png)


# Node-RED上でCustom Extensionの作成

1.　[こちら](https://raw.githubusercontent.com/kyokonishito/clova-node-red/master/node-red_custom_ext.json)　のコードをクリップボードにコピーします。
2. 　Node-REDメニューから「読み込み」→「クリップボード」をクリック。
3.　「JSON形式のデータを貼り付けてください」とかいてある枠に、1でコピーした内容を貼り付けし、「読み込み」をクリックする

![image.png](https://qiita-image-store.s3.amazonaws.com/0/294516/05e22459-c043-503d-ddcd-f21f709aede8.png)

下記のようなフローが読み込まれます：
![image.png](https://qiita-image-store.s3.amazonaws.com/0/294516/08800f12-4577-efa4-83e9-8931bd7b8696.png)


これは[[ハンズオン] LINE Clova×IBM WatsonでAIを学ぶ](https://linedev.connpass.com/event/96271/ "[ハンズオン] LINE Clova×IBM WatsonでAIを学ぶ")で使用している[app.js](https://github.com/taijihagino/cek-handson-fortune/blob/master/app.js)をNode-REDのフローにしたものです。

尚、このフローでは「リクエストの検証」は実装していませんのでご了承ください。

Node-REDではClovaのCEKのsdkは使用していないので、RESTのメッセージを直接受け取って、規定のレスポンスを返す形としています。CEKのドキュメント[Custom Extensionを作成する - Custom Extensionリクエストを処理する ](https://clova-developers.line.me/guide/#/CEK/Guides/Build_Custom_Extension.md#HandleCustomExtensionRequest)を参考にしてください。

　4. 　最後に右上の「デプロイ」をクリックしてデプロイを完了させてください。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/294516/bca5a0b2-cdc8-cc71-e6ae-4abee28587ca.png)

# スキルとの紐付け
[[ハンズオン] LINE Clova×IBM WatsonでAIを学ぶ](https://linedev.connpass.com/event/96271/ "[ハンズオン] LINE Clova×IBM WatsonでAIを学ぶ")の資料[「LINE Clova Skill ハンズオン資料」](https://www.dropbox.com/s/547siacriaudy4f/CEK_Handson%1D_0729.pdf?dl=0)の40ページ「スキルとの紐付け」から 

 - スキルのサーバーURLを指定
 
を実施してください。
 
ここで指定するURLは以下になります。
https://[Node-REDアプリ名].mybluemix.net/clova

[Node-REDアプリ名]は「Node-RED Starterサービスの作成」で設定しています。
または今開いているNode-REDエディターのURLの先頭部分の文字列です。

- 例: https://<font color="Red">clovasamplexxxxx</font>.mybluemix.net/clova
	<br>赤文字部分が[Node-REDアプリ名]です。自分のものを入れてください
![image.png](https://qiita-image-store.s3.amazonaws.com/0/294516/758aa900-c1b8-fc3e-f54a-09392fdbd30a.png)




# 動作確認・実機で使ってみる
これで動作するようになりました！！
引き続き[[ハンズオン] LINE Clova×IBM WatsonでAIを学ぶ](https://linedev.connpass.com/event/96271/ "[ハンズオン] LINE Clova×IBM WatsonでAIを学ぶ")の資料[「LINE Clova Skill ハンズオン資料」](https://www.dropbox.com/s/547siacriaudy4f/CEK_Handson%1D_0729.pdf?dl=0)、40ページ「スキルとの紐付け」から

 - 動作確認
 - 実機での体験

を実施して実際にClovaと会話してみましょう。

# フローの説明
![image.png](https://qiita-image-store.s3.amazonaws.com/0/294516/6eb4d65e-6d36-689f-d14f-59b089712222.png)
Clovaからのリクエストを取得します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/294516/cfa240ed-4911-e76a-e27c-f0c8d9cdcb6e.png)
決まり文句を定数にセットしています。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/294516/bd34ecb5-fccf-4f40-0b82-91d04c4cfae1.png)
msg.payload.request.typeに３種類のリクエストタイプがセットされますので、それによって処理を分けています。
３種類のリクエストタイプの説明はCEKのドキュメント[Custom Extensionを作成する - Custom Extensionリクエストを処理する 
](https://clova-developers.line.me/guide/#/CEK/Guides/Build_Custom_Extension.md#HandleCustomExtensionRequest)にあります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/294516/8ec22114-695e-ec73-2555-9530b4ab5081.png)
[LaunchRequest](https://clova-developers.line.me/guide/#/CEK/Guides/Build_Custom_Extension.md#HandleLaunchRequest)の処理をしています。
ここで「サンプル占いを起動して」と行った時に返す言葉を設定しています。
`flow.get("TEMPLATE_INQUIRY")`は最初の「set const value」で設定した定型文です。

```javascript

let speech;
speech = {
            lang: 'ja',
            type: 'PlainText',
            value: "「サンプル占い」が起動されました。 " + flow.get("TEMPLATE_INQUIRY")
          }
msg.responseSpeech = speech;`
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/294516/96590612-c944-7cf9-c784-bc1cb28149be.png)
[IntentRequest](https://clova-developers.line.me/guide/#/CEK/Guides/Build_Custom_Extension.md#HandleIntentRequest)の処理をしています。実際はその先のswitchノードでインテント別に分けてから処理をしています。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/294516/ebc1b50d-c747-ba6a-2e91-08742b315388.png)
msg.payload.request.intent.nameにClova側で判断したインテントの名前がセットされてますので、インテント別に処理を分けています。
ただし、Clova.YesIntentとClova.NoIntentとClova.CancelIntentは同じ処理に繋いでいます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/294516/193c763c-e5bb-c75d-65bc-7c90e3bdc181.png)
インテント別の処理を実装しています。これは[[ハンズオン] LINE Clova×IBM WatsonでAIを学ぶ](https://linedev.connpass.com/event/96271/ "[ハンズオン] LINE Clova×IBM WatsonでAIを学ぶ")で使用している[app.js](https://github.com/taijihagino/cek-handson-fortune/blob/master/app.js)の`switch (intent) {`　でインテント別の処理をしている部分とほぼ同じ処理をしています。
このフローを応用して別の対話モデルで別のスキルを作る場合はこの部分を変更していくことになります。
LaunchRequestと同様に`speech=`の部分で返答の言葉を設定しています。

```javascript

speech = {
    lang: 'ja',
    type: 'PlainText',
    value: `星座に誤りがあります。他の星座でお試し下さい。`
  }
```  

![image.png](https://qiita-image-store.s3.amazonaws.com/0/294516/25afc5ac-c22b-9c2d-e3b1-9f8b29f40546.png)
[SessionEndedRequest](https://clova-developers.line.me/guide/#/CEK/References/CEK_API.md#CustomExtSessionEndedRequest)の処理です。
ここで返答を返すことはできないのですが、何もレスポンスしないと終了したはずなのに、「サンプル占いを起動できませんでした。もう少し時間がたってからお試しください」のようなメッセージが流れてしまうため、とりあえず適当な返答をセットしておきます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/294516/783c8b57-978d-eef4-5fd3-4fd4dc0353bf.png)
最後に[規定のフォーマット](https://clova-developers.line.me/guide/#/CEK/References/CEK_API.md#CustomExtResponseMessage)でレスポンスメッセージを返します。

```javascript
clovaResponse = {};
clovaResponse.response = {};
clovaResponse.response.card = null;

clovaResponse.response.directives = []
clovaResponse.response.outputSpeech = {}
clovaResponse.response.outputSpeech.type = "SimpleSpeech";
clovaResponse.response.outputSpeech.values = {}
clovaResponse.response.outputSpeech.values = msg.responseSpeech;

clovaResponse.response.reprompt = {}
clovaResponse.response.reprompt.outputSpeech = {}
clovaResponse.response.reprompt.outputSpeech = msg.responseSpeech;

clovaResponse.response.shouldEndSession= false;
clovaResponse.version = msg.payload.version;
clovaResponse.sessionAttributes = null;
msg.payload = clovaResponse
return msg;
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/294516/b38ec39c-618f-12b5-363a-57972ed484d7.png)
Clovaにレスポンスを返しています。

# まとめ
Node-REDを使うとお気軽にLINEスピーカーのスキルの作成ができます。皆さんもぜひ作成してみてください！