AlexaにQiitaの最新記事のタイトルを言わせるスキルを開発してみた

タイムセールに乗せられてamazon echo plusを買ったので、Alexaスキル開発をちょっとやってみました。
とはいっても、公式の[Alexaスキル開発トレーニング](https://developer.amazon.com/ja/alexa-skills-kit/training/building-a-skill#%E7%AC%AC1%E5%9B%9E%20%E5%88%9D%E3%82%81%E3%81%A6%E3%81%AE%E3%82%B9%E3%82%AD%E3%83%AB%E9%96%8B%E7%99%BA)を読めばできるんですけどね！

今回はそれを読みつつ、Qiitaの最新記事のタイトルを言わせるところまでやります。
（公開しようとしたら、「第三者の商標またはブランドが使用されていたため、認定を却下いたしました。」って言われちまったよチクショウ！）

# 準備
必要なものは以下の通りです。

1. Amazon Developerアカウント
1. AWSアカウント

Amazon Developerアカウントは、[こちら](https://developer.amazon.com/ja)に遷移して右上の「サインイン」から、お持ちのAmazonアカウントでログインしてください。
詳しくは、[Alexaスキル開発トレーニングシリーズ 第1回 初めてのスキル開発](https://developer.amazon.com/ja/blogs/alexa/post/6e716e5c-55b0-445b-b936-9cfac4712e7b/training-1)の「ステップ 1. アカウントの作成」を参照してみてください。。。

AWSアカウントは、持っている人は少ないでしょうから作りましょう。私は作りました。
https://aws.amazon.com/jp/register-flow/ こちらを見ながら作成します。

# Alexaスキルを作成する
面倒なので、ここからは公式の画像を交えながら説明します。
### 開発者コンソールにサインインします
ここから
![amazon-developer-console.png](https://qiita-image-store.s3.amazonaws.com/0/179043/e9eba2e8-b1ae-3080-ce4f-23356d6ea723.png)

ここをクリック
![amazon-developer-console-menu.png](https://qiita-image-store.s3.amazonaws.com/0/179043/c54b4a08-5d64-bd51-fd75-6255169255f2.png)

すると、以下のような画面になります。（自分の画面です）
![ASK1.png](https://qiita-image-store.s3.amazonaws.com/0/179043/b4a0b1f9-99c4-1a43-301e-cc1bb4d02790.png)

### 「スキルの作成」ボタンを押します
すでに完成形がありますが、今回は1から作る前提なので気にしない。

### スキルの名称を決めます
名称はなんでもいいですが、ここでは便宜上「qiita_newtopic」と入力し、「次へ」をクリックします。
![ASK2.png](https://qiita-image-store.s3.amazonaws.com/0/179043/b9be1895-33ca-49cf-e97c-1c24b4830020.png)

### スキルに追加するモデルに「カスタム」を選択して「スキルを作成」をクリックします
作成に成功すると、以下のような画面になると思います。
![ASK3.png](https://qiita-image-store.s3.amazonaws.com/0/179043/96cfad26-f716-0fcf-f629-dbb0e9ce18b7.png)

### 呼び出し名を決めます
左側の「呼び出し名」をクリックして、呼び出し名に「キータの新着」とでも入れておきます。
![ASK4.png](https://qiita-image-store.s3.amazonaws.com/0/179043/d8f6f61a-91c3-ad70-95b6-f3d384babf86.png)

入れたら「モデルの保存」をクリックして保存します。

### インテントを作成します
インテントは、簡単に言えばメソッド名とそれに対応するフレーズの組み合わせのことだと思っておけばOKだと思います。
左側の「インテント」の右側にある「＋追加」ってところをクリックし、インテントの名称に「getIntent」とでも入れます。
この名称は、後で出てくるAWSLambdaで使用する関数名となります。
![ASK5.png](https://qiita-image-store.s3.amazonaws.com/0/179043/26c9afda-90d6-18d2-ac15-c2f47e990d17.png)

### サンプル発話を決めます
何をしゃべりかけたらそのインテントが発動するかを決めます。
赤枠内に文言を入れてエンターを押すと、その下のところに登録されていきます。
このサンプルでは「教えて」と「聞かせて」を登録しています。
![ASK6.png](https://qiita-image-store.s3.amazonaws.com/0/179043/4211941a-c81d-30f8-d8ce-673ecfd50f63.png)

ちなみに「スロット」とはいわゆる変数ですが、今回は使用しません。
登録し終えたら、「モデルを保存」を押しておきましょう。

ここで、いったんスキル開発の準備を終わります。
次に、AWSLambdaの準備です。

# AWSLambdaで関数を作成する

お次はAWSにログインします。

### タイムゾーンを設定します
もし東京になっていなかったら、赤枠内を変更してください。
![ASK7.png](https://qiita-image-store.s3.amazonaws.com/0/179043/906a88b7-6d27-b939-b9c4-8eb8cbeb225c.png)

### AWSLambda関数を作成します
AWSサービスのところに、「lambda」と入力します
そうすると、出てきますのでこれをクリックします。
![aws-menu-lambda.png](https://qiita-image-store.s3.amazonaws.com/0/179043/393b64f1-89df-7756-04f2-c11b54f42819.png)

AWS Lambdaの初期画面が表示されたら、「関数の作成」をクリックします。 
<img width="392" alt="aws-lambda-top.png" src="https://qiita-image-store.s3.amazonaws.com/0/179043/5a2de752-db43-623c-8517-b7338e2e6fb5.png">

### 関数作成時の設定
真ん中の「設計図」を選択し、フィルターに「fact」と入れてエンターキーを押すと「alexa-skill-kit-sdk-factskill」が出ますので、これを選択して「設定」をクリックします。
![aws-select-mode.png](https://qiita-image-store.s3.amazonaws.com/0/179043/8d8fa737-90ae-5e71-dec7-27aa1d1e8781.png)

関数の名称は便宜上、「qiita_newtopic」とでもしておきます。
（私の場合はすでにあるため入力できません・・・）
次の画面でロールのドロップダウンリストから、「カスタムロールの作成」を選択するといきなり別タブが開くと思います。
![ASK8.png](https://qiita-image-store.s3.amazonaws.com/0/179043/3a33092e-3fda-8e3d-565e-0b52e47e978e.png)

別タブが開いたら、何も変えずに「許可」をクリックします。
![ASK9.png](https://qiita-image-store.s3.amazonaws.com/0/179043/0834e86e-1bd3-f7e8-0dc8-e0e236310a1f.png)

戻ってきたらそのまま「関数の作成」をクリックします。
![ASK10.png](https://qiita-image-store.s3.amazonaws.com/0/179043/c4b67dc3-c1a1-7e17-7bbc-a33d1ca01558.png)

これで初期状態で作成完了！

### AWSLambdaの画面
最初は以下のような画面だったはず。
![ASK11.png](https://qiita-image-store.s3.amazonaws.com/0/179043/e4dfe6ce-0fd2-999e-9399-2d429d4c6978.png)

### トリガーを追加します
左側から、「Alexa Skills Kit」をクリックします。
そうするとトリガーの設定が開くので、スキルIDは「無効」にして、追加します。
スキルIDはAlexaスキルの作成のところで確認できますが、特に公開するわけでもないので今回は設定しません。
![ASK12.png](https://qiita-image-store.s3.amazonaws.com/0/179043/cfc1a613-e131-051c-88e0-fea1876a483b.png)

追加したら、保存しましょう。
![ASK13.png](https://qiita-image-store.s3.amazonaws.com/0/179043/06b521af-9e9a-2c23-de1b-471393199139.png)

### コードを書きます
まずは関数名のところをクリックします。
そうすると、コードエディターが開きます。
![ASK14.png](https://qiita-image-store.s3.amazonaws.com/0/179043/354f7071-4048-b3a7-8f46-fc1f4f853198.png)

index.jsとなって開いているところに、以下のコードを貼り付けてください。（キャプチャでは貼り付け後です）

```javascript:index.js
'use strict';
const Alexa = require('alexa-sdk');
const https = require('https');

var APP_ID = undefined;
var HELP_MESSAGE = "助けは来ない";
var HELP_REPROMPT = "どうしますか？";
var STOP_MESSAGE = "さようなら";

exports.handler = function(event, context, callback) {
    var alexa = Alexa.handler(event, context);
    alexa.APP_ID = APP_ID;
    alexa.registerHandlers(handlers);
    alexa.execute();
};

var handlers = {
    'LaunchRequest': function () {
        this.emit('getIntent');
    },
    'getIntent': function () {
        var self = this;
        var url = 'https://qiita.com/api/v2/items?page=1&per_page=3';
        var start = '新着の、3件のタイトルを読み上げます。';
        var message = '';
        https.get(url, function(res) {
            var body = '';
            res.setEncoding('utf8');
            res.on('data', function(chunk) {
                body += chunk;
            });
            res.on('end', function(res) {
                var data_list = JSON.parse(body);
                for (var i = 0; i < data_list.length; i++) {
                    var num = i + 1;
                    message += num + 'つ目。' + data_list[i]['title'] + '。';
                }
                self.emit(':tell', start + message);
                return;
            });
        }).on('error', function(e) {
            self.emit(':tell', '通信に問題が発生しました');
        });
    },
    'AMAZON.HelpIntent': function () {
        var speechOutput = HELP_MESSAGE;
        var reprompt = HELP_REPROMPT;
        this.emit(':ask', speechOutput, reprompt);
    },
    'AMAZON.CancelIntent': function () {
        this.emit(':tell', STOP_MESSAGE);
    },
    'AMAZON.StopIntent': function () {
        this.emit(':tell', STOP_MESSAGE);
    },
    'SessionEndedRequest': function () {
        // Nothing to do
    }
};
```

元がサンプルのソースで、それを今回アレンジして使っているのでよく分からない項目がたくさんあります(笑)
貼り付けたら、また保存しましょう。
ちなみに、getIntent というメソッド名がキモです。（Alexaスキルのインテント名と同じにします）

### テストを実行します
キャプチャではすでに設定があるのですが、新しく作成するために「テストイベントの設定」を選択します。
![ASK15.png](https://qiita-image-store.s3.amazonaws.com/0/179043/de99a37f-9d29-ac61-a742-42a5c6a0b03c.png)

「新しいテストイベントの作成」を選択し、イベントテンプレートに「Alexa Start Session」を選択します。
イベント名は適当でOKです。
![ASK16.png](https://qiita-image-store.s3.amazonaws.com/0/179043/4390f72f-3edc-2399-c048-8afcdbd0ff45.png)

「作成」を押すと準備完了です。
「テスト」をクリックすると、以下のようになると思います。
![ASK17.png](https://qiita-image-store.s3.amazonaws.com/0/179043/be0caa24-52ac-c201-52c2-b7d1745990fc.png)

テストに成功したら、AWSLambdaの作成は完了です。やったね！
最後に、画面右上上部の「ARN」のところにある文字列をコピーしておきます。

# Alexaスキルとのリンク
Alexaスキルを作った画面に戻ります。

### エンドポイントを設定します
画面左側の「エンドポイント」をクリックして、先ほどコピーしたARNを貼り付け、保存します。
![ASK18.png](https://qiita-image-store.s3.amazonaws.com/0/179043/368f413f-f570-fbc7-2709-5c2d2633013f.png)

### 今の状態を確認
左側の「カスタム」をクリックしてトップに戻ると、あとはビルドだけになっていると思います。あともうちょい！
![ASK19.png](https://qiita-image-store.s3.amazonaws.com/0/179043/e71ef5b1-dc28-009c-f98e-83b6da471441.png)

### ビルドします
上のキャプチャで、グレーアウトしている「3．モデルをビルド」をクリックすればビルドが始まります。
待っていると勝手に終わります。
![ASK20.png](https://qiita-image-store.s3.amazonaws.com/0/179043/2140841c-2280-2643-fb38-1c0869f150e2.png)

# 動作確認
ついにあと一息です。
### テストを有効にします
サクッと有効化します。
![ASK21.png](https://qiita-image-store.s3.amazonaws.com/0/179043/805386ba-a403-85a3-8dc3-33fcd89b0977.png)

### 動かしてみます
赤枠内に、「キータの新着を開いて教えて」と入力します。
すると、応答があります！！
![ASK22.png](https://qiita-image-store.s3.amazonaws.com/0/179043/e9ec47cf-6521-bcc9-3b34-214f95e2b88c.png)

成功ですね！
ちなみに、amazon echo plusを買ったアカウントとAmazon Developerのアカウントが同じなら、amazon echo plusに対して、「アレクサ、キータの新着を開いて教えて」と呼びかけるとちゃんと応答してくれます（＾ω＾）
これは、テストを有効化すると、アカウントに紐づいたAlexa端末でもテスト可能になるためです。（たしか）

# 終わりに
長々とお疲れ様でした！

ただ、最後の最後に不思議に思ったかもしれません。
なぜ呼び出し時、「開いて」を間に入れないといけないのか。
これがないと、正しく動作しません。理由はよく分かっていません。。。誰か教えて。