Alexaに嫁をなだめてもらうSkillを作ってみた。

先日[とあるプロジェクトでGASを使って予約フォームを作った](https://suzukigu.me)のですが、発生した不具合を解決できずになんとかごまかして乗り切りました。
「今度はGASを使わずにNode.jsでGmail/Googleスプレッドシートを操作する方法を試してみようかな、記事のネタにもなるし」と思っていたら[のびすけさんにやられてしまい](https://dotstud.io/blog/google-spreadsheets-from-nodejs/)、ネタに困る事態に……。
そんな時echo dotがやっと届いたのでAlexaといちゃいちゃした様子を記録したいと思います。

なおAWSにあまり慣れてなく、Lambdaも初めてだったのでかなり細かめにキャプチャ取りながらまとめてみました。

動機
---

* 妻帯者エンジニア共通の課題は新しく手に入れたガジェットといちゃいちゃしていると背後に危険な視線を感じること
* 直談判は効果が薄いことが多い
    * 第三者の仲裁（「旦那さんは頑張っているんですよ」的な）が効果的なことが多い

ということから第三者（Alexa）にかばってもらうことを考えました。天才的。

必要なもの
---

* echoシリーズなどAlexaがうごくもの
* Amazon Developerアカウント
* Amazon Web Servicesアカウント
* 嫁


3行まとめ
---

思いのほか長くなったので

* 公式ドキュメントを良く読もう、[せめてこれは最初に](https://developer.amazon.com/ja/docs/custom-skills/understanding-how-users-invoke-custom-skills.html)
* あまりNode.jsっぽくない話になってしまいました
* Alexaで嫁は攻略できない

手順
---

大まかな流れとして

1. AWS Lambdaにバックエンドを実装する
2. Amazon Skills Kit(Skill開発ダッシュボード的なもの)でスキル登録とバックエンドへの紐付けなどを設定
3. 実機テスト

という流れになります。

### バックエンドの作成

バックエンドにはLambdaを使います。

#### 関数の作成と設定

<img width="933" alt="001.png" src="https://qiita-image-store.s3.amazonaws.com/0/31037/f8ec46be-7426-33bb-6700-37c1680c3a15.png">

まずは **関数の作成** をクリック。

<img width="934" alt="002.png" src="https://qiita-image-store.s3.amazonaws.com/0/31037/739256bd-122e-5ab5-811e-3d4c74f372c1.png">

名前とロールを設定します。
Lambdaのロールについては適当にLamdba実行用のロールを作成しました。「カスタムロールの作成」を選択すると新しくロールを作成することが出来ます。

<img width="1069" alt="003.png" src="https://qiita-image-store.s3.amazonaws.com/0/31037/d82f9e4b-2560-f0c0-5abe-f03f12b1f238.png">

**関数の作成** をクリックして進むと、トリガー設定の画面に移ります。何をトリガーにして処理を呼び出すのかを設定するようです。
今回はAlexaから呼び出したいので、**Alexa Skills Kit**を選択してください。

<img width="1031" alt="004.png" src="https://qiita-image-store.s3.amazonaws.com/0/31037/6b20caed-d475-1810-d1fa-6d2d8e51e995.png">

トリガーの設定画面が出てきますが、特に設定項目もないので**追加**をクリック。
クリックすると画面右上部の**保存**がオレンジ色になるのでクリックして設定を保存してください。

<img width="1060" alt="005.png" src="https://qiita-image-store.s3.amazonaws.com/0/31037/96bcd8bb-3299-b34c-afc3-2274080495ed.png">

続けて関数本体の設定をするため中央上部の作成した関数名（ここでは`MyAppeaseSkill`）を選択してください。
コードエントリタイプは**.ZIPファイルをアップロード**を選択してください。


#### 関数本体の実装

ここまで来たらローカルで関数本体の実装に移ります。
Node.jsはnodebrewなりnvmなりでv6.10を用意してください。

まずは作業場所を確保。

```shell
$ mkdir my-appease-skill
$ cd my-appease-skill
$ git init
$ npm init
```

Alexa SDKを使うので

```shell
$ npm install --save alexa-sdk
```

全体像はこんな感じ。

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

// APP_ID : optionalらしくundefinedでも問題ないっぽい
const APP_ID = undefined;

//================
// alexaへのハンドラ登録
// ここは定型文で良さそう
//================
exports.handler = (event, context, callback) => {
  const alexa = Alexa.handler(event, context);
  alexa.APP_ID = APP_ID;
  alexa.registerHandlers(handlers);
  alexa.execute();
};

//================
// ハンドラ作成
// 入力したボイスメッセージに対応する実処理をここに実装する
//
// アロー関数使っていないのは呼び出し元で定義されるemit()関数を呼んでいるため
//================
const handlers = {
  'LaunchRequest': function() {
    // スキル呼び出し時の処理を実装する
    // 「（呼び出し名）を開いて」で呼ばれる

    // 別のインテントを呼び出したいときは
    // this.emit('AppeaseIntent');
    // のようにすると呼び出せます

    const speechOutput = 'どうかしましたか？';
    this.emit(':ask', speechOutput);
    },
  'AppeaseIntent': function () {
    // ここにインテントごとの処理を実装する
    
    const speechOutput = '旦那様は素晴らしい方です。こんな素晴らしい旦那様をお持ちになって奥様は幸せですね。';
    this.emit(':tell', speechOutput);
  },
  //以下については基本機能なのでメッセージ内容を変える程度のほぼ定型文で良さそう
  'AMAZON.HelpIntent': function () {
      const speechOutput = '私に頼りたい時は「なだめて」と、終わりたい時は「ありがとう」と言ってください。どうしますか？';
      const reprompt = 'どうしますか？';
      this.emit(':ask', speechOutput, reprompt);
  },
  'AMAZON.CancelIntent': function () {
      this.emit(':tell', 'さようなら、夫婦仲良く');
  },
  'AMAZON.StopIntent': function () {
      this.emit(':tell', 'さようなら、夫婦仲良く');
  }
};
```

ポイントはコメントに記載しましたが、結構カンタンに実装できるみたいです。

`this.emit()`しているところで、第1引数に`:ask`ハンドラを指定するとAlexaの応答後待ち受け状態に、`:tell`ハンドラを指定すると応答後スキルを終了します。
詳細は[このあたりに書いてあるよう](https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs#generating-your-own-responses)ですが、まだ試せていません。
`:tellwithcard`でAlexaアプリの履歴にも表示できる様子（タイトルと通知本文の引数を2つ追加するみたい）。他にもバリエーションがある様子です。[クラスメソッド様の記事](https://dev.classmethod.jp/cloud/alexa-skill-card/)でまとめられていました。


このソースコード`index.js`とパッケージディレクトリ`node_modules`をLambdaに登録するためにZIPにまとめます。

#### 実装を登録

再度Lambdaの設定画面に戻ります。

<img width="1063" alt="006.png" src="https://qiita-image-store.s3.amazonaws.com/0/31037/8d878526-8f5c-2b80-eee0-46c0f2d995a8.png">

作成したZIPファイルをアップロードして、**保存**します。
保存したら動作確認のために隣の**テスト**をクリックします。

<img width="854" alt="007.png" src="https://qiita-image-store.s3.amazonaws.com/0/31037/f64aebb8-2592-edf9-f2d1-8acc3e2fe24b.png">

テスト作成画面が出てくるので、イベントテンプレートに`Alexa Start Session`を選択し、適当な名前で**作成**してください。

<img width="1041" alt="008.png" src="https://qiita-image-store.s3.amazonaws.com/0/31037/fd7db012-952a-3bb9-e863-3160f9c8f5f2.png">

**テスト**をクリックするとテストが実行されます。画面のような緑色の結果が出ればOKです。
Skillと紐付けるために右上のARNの値をコピーしておいてください。

### スキルの作成

<img width="738" alt="009.png" src="https://qiita-image-store.s3.amazonaws.com/0/31037/265d10b3-bd49-922d-9df2-85ae17c50b24.png">

Amazon開発者コンソールからALEXAのメニューのAlexa Skills Kitを始めます。

<img width="1155" alt="010.png" src="https://qiita-image-store.s3.amazonaws.com/0/31037/93e7179a-82fe-89a6-40ef-6997938f2d6d.png">

**新しいスキルを追加する**をクリック。

<img width="1047" alt="011.png" src="https://qiita-image-store.s3.amazonaws.com/0/31037/02b05a60-bd9e-11b8-ead3-bab7e1242c3d.png">

* 言語は日本語
* スキル名は適当に
* 呼び出し名はスキルを起動するときに使う言葉

を設定して、**保存**をクリック。

<img width="1007" alt="012.png" src="https://qiita-image-store.s3.amazonaws.com/0/31037/a4a84a8c-24e0-9959-97a2-23bd0d7715db.png">

**保存**をクリックすると、**次へ**がクリックできるようになります。

<img width="1028" alt="013.png" src="https://qiita-image-store.s3.amazonaws.com/0/31037/3182c6bb-8a8c-1385-6d0a-ad99d30447ac.png">

対話モデルの設定では最初にこのスキルにどのような機能があるかを登録します。この機能1つ1つをインテントと呼びます。

インテントにはカスタムインテントと基本的な機能を用意した[一般的標準インテント](https://developer.amazon.com/ja/docs/custom-skills/standard-built-in-intents.html)があります。

とりあえず今回は以下の内容で設定します。

```json
{"intents": [
  {"intent": "AppeaseIntent"},
  {"intent": "AMAZON.HelpIntent"},
  {"intent": "AMAZON.StopIntent"},
  {"intent": "AMAZON.CancelIntent"}
]}
```

<img width="856" alt="014.png" src="https://qiita-image-store.s3.amazonaws.com/0/31037/5a97b74b-3d8c-0558-1879-a1304443239e.png">

次にサンプル発話の登録をします。ここでは呼び出し名に続けて呼びかける言葉（アクションと呼ばれれる）の登録をします。
出来る限り多くのバリエーションを登録することで融通の聞くコマンド入力ができるようになるようです。

形式としては`起動するインテント + 半角空白 + アクション`の形式になります。

ここでは以下のように登録します。

```
AppeaseIntent なだめて
AppeaseIntent 嫁をなだめて
AppeaseIntent 助けて
AppeaseIntent 嫁に言ってやって
```

これで「アレクサ、大変だ助けて」みたいに呼べばかばってくれるようになりそうです。

入力したら、**保存** → **次へ**をクリックします。

<img width="183" alt="015.png" src="https://qiita-image-store.s3.amazonaws.com/0/31037/ca75339b-b886-77d4-f8fe-b85104d55b2b.png">

対話モデルの更新を行うと、しばらくモデルの構築処理が走ります。モデルの構築には数分かかるのでそのまま設定を進めていきます。

<img width="1000" alt="016.png" src="https://qiita-image-store.s3.amazonaws.com/0/31037/fec1c4e7-6d80-5edd-a4ed-0b9415ec5191.png">

設定ではLambdaで作成した関数との紐付けを行います。コピーしたARNの値（arn:〜で始まる値）を設定してください。

入力したら**保存** → **次へ**をクリックします。

<img width="1029" alt="017.png" src="https://qiita-image-store.s3.amazonaws.com/0/31037/cb9ae48d-712a-842f-88c2-173ddc1eb3c5.png">

ここまで来るとテストが行なえます。
発話入力欄に呼びかけるメッセージを入力して動作確認してみてください。

**が、しかし実機で動作させる場合とは挙動が異なります。**

サービスシュミレータでのテストはかなり楽観的だと思ってください。ここで期待するレスポンスが返ってきても実機での保証にはなりません。

ひとまず先に進みます。

<img width="1019" alt="018.png" src="https://qiita-image-store.s3.amazonaws.com/0/31037/a301ce4d-2a29-5e54-1dd3-e76ea45c45cb.png">

<img width="826" alt="019.png" src="https://qiita-image-store.s3.amazonaws.com/0/31037/12e41bc2-5547-4ef9-9383-b117db61d65c.png">

<img width="1004" alt="020.png" src="https://qiita-image-store.s3.amazonaws.com/0/31037/f11ad6a2-895e-38b8-ebca-84eb26256599.png">

公開情報を登録します。ここは適当な値で入力しておけば良いと思います。

公開する国と地域は日本語しか設定しないのであれば`Japan`に限定しておいたほうが良いかもしれないです。

スキルの説明もそれっぽく入力します。

サンプルフレーズには実際にどう呼びかければ使えるかを入力します。

そしてアイコンの登録も必須なので適当に108x108、512x512のアイコン画像を用意しておきましょう。

<img width="1015" alt="021.png" src="https://qiita-image-store.s3.amazonaws.com/0/31037/7968718a-8d3d-5982-559b-cf5e472040d4.png">

プライバシーとコンプライアンスの設定はそれぞれ適切に設定しておきましょう。

ただし、13歳未満の子供を対象にしたスキルはこのあと出て来るSkills Beta Testing機能が利用できないので注意が必要です。

<img width="366" alt="022.png" src="https://qiita-image-store.s3.amazonaws.com/0/31037/8a7e7548-3ebb-5da9-a5da-a39b596e0afb.png">

ここまでの

* スキル情報
* 対話モデル
* 設定
* テスト
* 公開情報
* プライバシーとコンプライアンス

の全設定が完了するとSkills Beta Testing機能が使えるようになります。

これはスキルを一般公開せずにAlexa対応端末にスキルを適用する機能です。

<img width="1104" alt="023.png" src="https://qiita-image-store.s3.amazonaws.com/0/31037/05a17405-0c7a-40bb-0a4d-4bf244b5e5c0.png">

テスターのメールアドレスを入力して**テスターを追加**をクリックします。

<img width="1111" alt="024.png" src="https://qiita-image-store.s3.amazonaws.com/0/31037/8650b128-a8f8-1f40-0fcd-b7eec1873b96.png">

テスターが追加されるので**テストを開始**をクリックします。

<img width="1124" alt="025.png" src="https://qiita-image-store.s3.amazonaws.com/0/31037/55a2b6bc-7028-e001-c3ef-bc8290e4a3d8.png">

このような画面になればテストの準備はOKです。

<img width="791" alt="026.png" src="https://qiita-image-store.s3.amazonaws.com/0/31037/090556c9-34af-8309-26df-0729f692266c.png">

このようなメールが指定したメールアドレスに送信されているので、日本の利用者であれば下のリンク先に遷移します。

<img width="607" alt="027.png" src="https://qiita-image-store.s3.amazonaws.com/0/31037/d69445bb-49d5-de71-e3cb-59f188c5d1cd.png">

Alexaの設定画面にいき、スキルをテストするかどうかの確認ダイアログが出ますので、もちろん**スキルテスト**をクリック。

<img width="908" alt="028.png" src="https://qiita-image-store.s3.amazonaws.com/0/31037/668b42e8-b98a-2835-9b51-20aca78e6aa8.png">

**有効にする**をクリックすればecho実機でテストが出来るようになります！

動かない！！
---

がしかし「アレクサ、大変だ助けて！」と呼びかけても「すみません、私にはわかりません」

**アレクサに見捨てられた！**

なんでだ？ と思って調べたら公式サイトに[「ユーザーによるカスタムスキルの呼び出し」](https://developer.amazon.com/ja/docs/custom-skills/understanding-how-users-invoke-custom-skills.html)というドキュメントを見つけました。

これによると「呼び出し名」と「アクション」は特定のルールに則っていないと有効ではないようです。

「（呼び出し名）を起動して（アクション）」というルールがあったので、試しに「アレクサ、大変だを起動して助けて」と呼びかけたら無事かばってくれました。

ぐぬぬ……思っていたのと違う。

まとめ
---

* スキルの作成自体は思っていたよりもカンタン
    * 今回作った単発型のスキルだと特に
* [「ユーザーによるカスタムスキルの呼び出し」](https://developer.amazon.com/ja/docs/custom-skills/understanding-how-users-invoke-custom-skills.html)の内容を念頭に置かないと自然な起動呼びかけに設計できないので読み込み必須

他にもいろいろやれることがたくさんあるのでもっと触っていきたいですね。

[カラーコードかるた](https://suzukigu.me/colorcode/)の読み上げスキルとか作りたい。

で
---

嫁に向かってこれを使ったところ、 :fire: :oil: :name_badge: :oil: :fire: な感じでした。