Alexaスキル作成におけるLambda側の基本

#はじめに
Alexaスキルの作成は大きく分けて2つからなります。

- 対話モデルの作成
- スキルの実際の処理部の作成

対話モデルはAlexa開発者コンソールからグラフィカルに作成するのが一般的です。
（画像は[公式のチュートリアル](https://developer.amazon.com/ja/blogs/alexa/post/07377568-2815-4028-8c21-409dd8e84fa2/alexa-training-jp-2nd)より）
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/7a192377-af6e-62d7-4e93-7dc44a8eb34c.png)

作成された対話モデルに従ってインテントが作成され、それがJSON形式でスキルの処理部分に渡されます。
このスキルの処理部分がインテントハンドラとしてAWS Lambda関数として実装される部分になります。

実装に使うことができる言語はいくつかありますが、公式のチュートリアルではNode.jsで説明されているので素直にNode.jsを選ぶと良いでしょう。

この記事でもNode.jsで話をします。

さて、この記事ではLambda関数として作成するインテントハンドラの基本的な構成について説明したいと思います。

コード部分はぱっと見だと複雑そうですが、少し落ち着いて見てみると意外とシンプルな構成であることがわかります。

基本的な構成がわかれば、自分でカスタマイズするのが格段に容易になると思います。

#環境
- Alexa SDK for Node.js Ver.2

Alexa SDK for Node.jsはVer.2になりました。公式のチュートリアルもVer.2を用いたものになりました。
Ver.1は、以下のような`emit`が使われているバージョンです。
Ver.2になると`emit`が使われず、`canHandle`や`handle`といったメソッドが使われます。


#　index.jsにインテントハンドラを書く
Alexa開発者コンソールで作成したインテントに対応するインテントハンドラをここに書き連ねていきます。

なので、`index.js`の基本的な構成はこんな感じです。

```javascript:index.js

インテントハンドラ1の定義

インテントハンドラ2の定義

インテントハンドラ3の定義

・・・

インテントハンドラ10の定義



インテントハンドラ1～10の登録

```

というように、インテントハンドラを定義し、Alexaからのリクエストに対してそれらのインテントハンドラが機能するように登録する、というのが一連の流れ。

#インテントハンドラの定義と基本形
まずはインテントハンドラの定義の仕方について説明します。
定義の基本的な形は以下のとおりです。
`canHandle`メソッドで、そのインテントハンドラが応答する条件を記述します。
基本形は`リクエストのタイプ`と`インテント名`のチェックを行って、`True`か`False`をreturnします。
ここでTrueを返すと、`handle`メソッド内の処理が行われます。

```javascript
//例：「ほげほげIntent」というインテントに対応するインテントハンドラ
const ほげほげIntentHandler = {//名前は何でもいいけれど、対応がわかりやすい名前を。
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && handlerInput.requestEnvelope.request.intent.name === 'ほげほげIntent';//受け取ったインテントのタイプが「IntentRequest」で、インテント名が「ほげほげIntent」のときに、このインテントハンドラの処理を実行する。
  },
  handle(handlerInput) {//実行される部分

    //何かしらの処理を書く
    //何かしらの処理というのは主にAlexaに喋らせる情報を取得する処理
　　//気象情報とか、電車の運行情報とか

    //Alexaへ返すレスポンスを作る。
    return handlerInput.responseBuilder
      .speak('Alexaに喋らせる内容')
      .reprompt('次の問いかけ')//続いてユーザーに問いかける場合に使う。なければここでスキルが終了する。
      .withSimpleCard('カードタイトル','カード本文')//Echo Spotなどのディスプレイ付きのデバイスに表示させる場合に使う。
      .getResponse();//最後にかならずつける。
  },
};
```

これを1セットとして、インテントの数だけ作る。
`LaunchIntent`や`HelpIntent`などスキルに必ず実装しなければならない特別なインテントもあるけれど、基本的には同じです。


#インテントハンドラの登録
上で作成したインテントハンドラですが、これはインテントハンドラを定義しただけなので、呼び出されることはありません。
これらのインテントハンドラが機能するためには、それを登録する必要があります。
それが以下の部分です。

```javascript
const skillBuilder = Alexa.SkillBuilders.custom();//お約束

exports.handler = skillBuilder
  .addRequestHandlers(//機能させたいインテントハンドラを引数として与える
    LaunchRequestHandler, //ビルトイン
    ほげほげIntentHandler, //ユーザー定義
    もがもがIntentHandler, //ユーザー定義
    おろおろIntentHandler, //ユーザー定義
    HelpIntentHandler,//ビルトイン
    CancelAndStopIntentHandler,//ビルトイン
    SessionEndedRequestHandler //ビルトイン
  )
  .addErrorHandlers(ErrorHandler) //エラーハンドラ
  .lambda();　//必ずつける
```

機能させたいインテントハンドラをここで登録します。
登録しないと「あれ？応答しないぞ」ということになります。
また、当然のことながら定義していないインテントハンドラを登録すると、プログラム的にエラーになります。


#まとめ
つまり、以下のような流れです。

Alexa開発者コンソールで対話モデルを作成します。

ここで、例えば`HogeHogeIntent`を作成したとしましょう。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/957c7b3b-1744-6654-ac3f-f1feda42e08c.png)

そうしたら、この`HogeHogeIntent`に対応するインテントハンドラをAWS Lambda側の`index.js`内に定義します。
名前は対応がわかりやすいように`HogeHogeIntentHandler`とするのが良いでしょう。

```javascript
const HogeHogeIntentHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && handlerInput.requestEnvelope.request.intent.name === 'HogeHogeIntent';
  },
  handle(handlerInput) {
    var speechText = 'ほげほげ';

    //Alexaに「ほげほげ」喋らせてスキルが終了する。
    //repromptを書かないでおく。
    return handlerInput.responseBuilder
      .speak(speechText)//Alexaに「ほげほげ」喋らせる。
      .withSimpleCard('Alexaほげほげ', speechText)//カードのタイトルと、メッセージ
      .getResponse();
  },
};

```

そして、定義した`HogeHogeIntentHandler`を登録します。

```javascript
const skillBuilder = Alexa.SkillBuilders.custom();//お約束

exports.handler = skillBuilder
  .addRequestHandlers(
    LaunchRequestHandler,
    HogeHogeIntentHandler,//←　これ
    HelpIntentHandler,
    CancelAndStopIntentHandler,
    SessionEndedRequestHandler
  )
  .addErrorHandlers(ErrorHandler)
  .lambda();　//必ずつける
```


以上です。