Alexa Skills Kitで、カスタムスキルと独自ウェブサービスとを連携させる

# 概要
Alexa Skills Kitで、カスタムスキルと、ローカルで作成した独自ウェブサービスとを連携させてみました。
独自ウェブサービスについて、今回は「Node.js」を用いて作成します。

# 前提条件
当記事をご覧になる前に、Alexa Skills Kitや、Alexaの仕組みについて目を通しておくことをおすすめします。
[Alexa | アレクサ | Alexaスキル開発トレーニング](https://developer.amazon.com/ja/alexa-skills-kit/training/building-a-skill)

また、Node.jsについての知識も必要になりますので、以下のサイトなどで事前に勉強しておくとよいでしょう。
[Node.js入門 (全16回) - プログラミングならドットインストール](https://dotinstall.com/lessons/basic_nodejs)

# 背景
Alexa Skills Kitでスキル開発するにあたり、必ずエンドポイントを設定する必要があります。
基本的には「AWS Lambda」を選択することが推奨されており、Amazon Alexaの公式サイトでも、
AWS Lambdaを使っての開発方法が紹介されています。
ただし、AWS Lambdaは従量課金制となっており、大規模サービスともなれば料金がネックになるものです。詳細は以下のURLを参考にしてください。
[料金 - AWS Lambda（サーバーレスでコードを実行）｜AWS](https://aws.amazon.com/jp/lambda/pricing/)

そこで、もうひとつの「HTTPS」を使っての開発方法があるが、情報がいかんせん少ない。。
そこで、Alexa Skills Kitで、カスタムスキルと独自ウェブサービスとを連携させる方法を解説します。

# Node.jsのインストール
ウェブサービスの開発に使用します。
公式サイトからインストールします。
[Download | Node.js](https://nodejs.org/en/download/)

# ngrokのインストール
ローカルのウェブサービスに外部からアクセスできるようにするために使用します。
公式サイトからインストールします。
[ngrok - download](https://ngrok.com/download)

# Amazon Developerへの登録
スキルを作成するために、Amazon Developerへ登録します。
以下のサイトが参考になります。
[失敗しないAlexa開発者アカウントの作り方](https://dev.classmethod.jp/voice-assistant/how-to-safety-create-alexa-developers-account/)

# スキルの作成
以下のサイトの「ステップ2. スキルの作成」を参考にスキルを作成します。
[Alexaスキル開発トレーニングシリーズ 第1回 初めてのスキル開発](https://developer.amazon.com/ja/blogs/alexa/post/6e716e5c-55b0-445b-b936-9cfac4712e7b/training-1)

## スキル情報
| 項目 | 設定内容 |
|:--|:--|
| スキルの種類 | Custom |
| 言語 | Japanese |
| スキル名 | あいさつスキル |
| 呼び出し名 | あいさつ |

## 対話モデル
Skill Builderを使って以下の通り作成します。

| 項目 | 設定内容 |
|:--|:--|
| Intent Name | GreetingIntent |
| Sample Utterances | {FirstName} にあいさつ |
| Intent Slots | FirstName |
| Slot Name | FirstName |
| Slot Types | ※AMAZON.FirstName |
※「Use existing slot type from Alexa's built-in library」の「List Types」より選択

## 設定
| 項目 | 設定内容 |
|:--|:--|
| サービスエンドポイントのタイプ | HTTPS |
| デフォルト | ウェブサービスのURL（後ほど設定） |

# Webサービスの実装
以下のサイトを参考に、プロジェクトフォルダを任意の位置に作成、初期化します。
[Installing Express](https://expressjs.com/en/starter/installing.html)

entry pointは「app.js」とします。

```
mkdir greeting-skill
cd greeting-skill
npm init
```
必要なモジュール群をインストールしていきます。

```
npm install --save express
npm install --save body-parser
npm install --save alexa-sdk
```
そして、app.jsを以下の通りに編集します。

```javascript:app.js
"use strict";

const Alexa = require('alexa-sdk');
const bodyParser = require('body-parser');
const express = require('express');
const app = express();

app.use(bodyParser.urlencoded({
    extended: true
}));
app.use(bodyParser.json());

app.post('/greeting', (req, res) => {
    let context = {
        'succeed' : function(result) {
            res.json(result);
        },
        'fail' : function(err) {
            res.json(err);
        }
    };

    let handlers = {
        'GreetingIntent' : function() {
            let name = req.body.request.intent.slots.FirstName.value;
            this.emit(':tell', 'おはよう、' + name);
        }
    };

    let alexa = Alexa.handler(req.body, context);
    alexa.appId = req.body.session.application.applicationId;
    alexa.registerHandlers(handlers);
    alexa.execute();
});

app.listen(3000, () => {
    console.log('Example app listening on port 3000!');
});
```
このうち、メインとなる部分は以下になります。

```
let context = {
    'succeed' : function(result) {
        res.json(result);
    },
    'fail' : function(err) {
        res.json(err);
    }
};

let handlers = {
    'GreetingIntent' : function() {
        let name = req.body.request.intent.slots.FirstName.value;
        this.emit(':tell', 'おはよう、' + name);
    }
};

let alexa = Alexa.handler(req.body, context);
alexa.appId = req.body.session.application.applicationId;
alexa.registerHandlers(handlers);
alexa.execute();
```
実装方法は、AWS Lambdaとおおまかには似ていますが、
contextを自前で定義する必要があるのと、eventの代わりにreq.bodyを使うのが異なる点です。
AWS Lambdaでの実装方法については、以下のサイトの「2-2. Alexa SDKによるAWS Lambda関数の実装」が参考になります。
[Alexaスキル開発トレーニングシリーズ 第2回 対話モデルとAlexa SDK : Alexa Blogs](https://developer.amazon.com/ja/blogs/alexa/post/07377568-2815-4028-8c21-409dd8e84fa2/alexa-training-jp-2nd)

# 動作確認
## ローカルサーバの起動
以下のコマンドを実行します。

```
node app.js
```
起動に成功すると、以下のログが表示され、ポート番号3000番でローカルサーバが起動します。

```
Example app listening on port 3000!
```

## ローカルサーバを外部公開
以下のコマンドを実行します。

```
ngrok http 3000
```
ローカルサーバはポート番号3000番で起動しているので、３０００と指定します。
すると、以下のように表示されます。

```
ngrok by @inconshreveable                                       (Ctrl+C to quit)
                                                                                
Session Status                online                                            
Session Expires               7 hours, 20 minutes                               
Version                       2.2.8                                             
Region                        United States (us)                                
Web Interface                 http://127.0.0.1:4040                             
Forwarding                    http://xxxxxxxx.ngrok.io -> localhost:3000        
Forwarding                    https://xxxxxxxx.ngrok.io -> localhost:3000       
                                                                                
Connections                   ttl     opn     rt1     rt5     p50     p90       
                              6       0       0.00    0.00    5.04    5.07
```
https://xxxxxxxx.ngrok.io
が、外部に公開されているURLとなります。
ただし、ngrokは起動するたびにURLが変わるので注意が必要です。
（会員登録して有料プランにすれば、固定URLにすることも可能です）

## デフォルトの設定
「アマゾン アプリ 開発者ポータル」の「設定」に戻り、デフォルトに以下のURLを設定します。
https://xxxxxxxx.ngrok.io/greeting

## サービスシミュレーターによる動作確認
以下のサイトの、「ステップ5. サービスシミュレーターによる動作確認」を参考に動作確認します。
[Alexaスキル開発トレーニングシリーズ 第1回 初めてのスキル開発](https://developer.amazon.com/ja/blogs/alexa/post/6e716e5c-55b0-445b-b936-9cfac4712e7b/training-1)

例えば、「太郎にあいさつ」と入力し、「サービスレスポンス」に以下のように表示されたらOKです。

```
{
  "version": "1.0",
  "response": {
    "outputSpeech": {
      "ssml": "<speak> おはよう、太郎 </speak>",
      "type": "SSML"
    },
    "speechletResponse": {
      "outputSpeech": {
        "ssml": "<speak> おはよう、太郎 </speak>"
      },
      "shouldEndSession": true
    }
  },
  "sessionAttributes": {}
}
```
Amazon Echoなどがあれば、そちらでも動作確認可能です。
（動作確認方法については今回は割愛します）

# 今回作成したソースコード
以下にありますので、ご自由にお使いください。
[hiesiea/greeting-skill](https://github.com/hiesiea/greeting-skill)

# ngrokをNode.jsで起動させる
ngrokをNode.jsで起動させることで、より簡単になります。
以下に記事としてまとめました。
[ngrokをNode.jsで起動させる](https://qiita.com/hiesiea/items/aba770ea9cafb0d171bb)