Amazon Echo (Alexa) スキルをノンコーディング（AzureLogicApps）で開発する

#0. はじめに
[開発者待望のAlexaスキル内課金が米国で可能に！](https://robotstart.info/2018/05/04/amazon-alexa-in-skill-purchasing.html)など、2019年は日本でもスマートスピーカーのスキル開発がより注目される予感がしています。本記事では、超初心者にもハードルが低くなるよう、MicrosoftAzureLogicAppsを使ってノンコーディングでAmazonEchoのスキルを開発してみます。実家に帰省しないと会うことができない愛犬の声をいつでも気軽AmazonEchoから聞くことができるスキルを開発しました。

#1. 開発するスキル
今回開発するスキルのイメージはこちらです。呼び出されると、愛犬の声を再生するようにします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/133230/ff294912-bef6-1c55-7afb-a59eb0db5692.png)

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">鳴き声を使って、愛犬を <a href="https://twitter.com/hashtag/AmazonEcho?src=hash&amp;ref_src=twsrc%5Etfw">#AmazonEcho</a> のスキルで再現してみた。鳴き声の録音が難しい。笑 <a href="https://twitter.com/hashtag/VUI?src=hash&amp;ref_src=twsrc%5Etfw">#VUI</a> <a href="https://twitter.com/hashtag/%E9%A7%86%E3%81%91%E5%87%BA%E3%81%97%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%81%A8%E7%B9%8B%E3%81%8C%E3%82%8A%E3%81%9F%E3%81%84?src=hash&amp;ref_src=twsrc%5Etfw">#駆け出しエンジニアと繋がりたい</a> <a href="https://twitter.com/hashtag/100DaysOfCode?src=hash&amp;ref_src=twsrc%5Etfw">#100DaysOfCode</a> <a href="https://t.co/amaM7Hc6SK">pic.twitter.com/amaM7Hc6SK</a></p>&mdash; さいちゃん🦏 (@saisan78) <a href="https://twitter.com/saisan78/status/1080048762129571840?ref_src=twsrc%5Etfw">2019年1月1日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


#2. 開発の全体像と流れ
手順は、大きく以下の3ステップです。
①AlexaDeveloperConsoleでスキルの作成
②AzureLogicAppsで応答処理の作成
③自作音声ファイルの準備とDropboxでの共有

![image.png](https://qiita-image-store.s3.amazonaws.com/0/133230/4480a3a3-ca71-165a-87fe-ca9e41e7cb1a.png)

##①AlexaDeveloperConsoleでスキルを作成
まず、[https://developer.amazon.com/ja/](https://developer.amazon.com/ja/)にて、スキルを作成しましょう。AmazonEchoスキルの開発に必要な概念は、[Amazon Echo (Alexa) のSkillの開発に必要な基本概念を押さえる](https://qiita.com/chroju/items/3f30fe424df36e4b4d80)を参照してください。今回は、「おさんぽ」とよんだら、音声を返すシンプルなスキルなので、インテントをひとつだけ登録します。おさんぽということで"walking"と命名しました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/133230/df260fee-974f-f88a-e2b7-9416869e66a6.png)

##②AzureLogicAppsで応答処理の作成
続けて、AzureLogicAppsにて、AlexaSkillConsoleからIntentを受け取り、応答を返すAPIサーバをAzureLogicAppsで作成します。
AlexaからJSONをうけとりJSONをかえすAPIサーバです。

「もっと丁寧にAzureLogicAppsの使いたいを知りたい！」という方は、以下のLINEBotの作成記事を参考にしてください。
[ノンコーディングで質問に自動回答するLINE BOTを作ってみよう](http://ascii.jp/elem/000/001/713/1713259/)

AzureLogicAppsで開発するAPIサーバの全体像ですが以下のイメージになります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/133230/d08ea271-e609-aacd-63cf-a1cc9964133f.png)

###②-1.HTTP要求の受信時のモジュールを作成
まずはAlexaからPOSTでJSONを受け取れるように、「HTTP要求の受信時」のモジュールを用意します。
メソッドをPSOTに設定し、JSONのスキーマを登録しておきます。
また、HTTP POSTのURLは、先程のAlexaDeveloperConsoleでエンドポイントに登録しておきます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/133230/3300e0d1-29b7-9207-b6f8-c1556da7cf2f.png)

<details><summary>「HTTP要求の受信時」のモジュールに登録するJSONスキーマ（クリックすると開きます）</summary><div>

```request.json
{
    "properties": {
        "request": {
            "properties": {
                "dialogState": {
                    "type": "string"
                },
                "intent": {
                    "properties": {
                        "confirmationStatus": {
                            "type": "string"
                        },
                        "name": {
                            "type": "string"
                        },
                        "slots": {
                            "properties": {
                                "anatanoslotname": {
                                    "properties": {
                                        "confirmationStatus": {
                                            "type": "string"
                                        },
                                        "name": {
                                            "type": "string"
                                        },
                                        "resolutions": {
                                            "properties": {
                                                "resolutionsPerAuthority": {
                                                    "items": {
                                                        "properties": {
                                                            "authority": {
                                                                "type": "string"
                                                            },
                                                            "status": {
                                                                "properties": {
                                                                    "code": {
                                                                        "type": "string"
                                                                    }
                                                                },
                                                                "type": "object"
                                                            },
                                                            "values": {
                                                                "items": {
                                                                    "properties": {
                                                                        "value": {
                                                                            "properties": {
                                                                                "id": {
                                                                                    "type": "string"
                                                                                },
                                                                                "name": {
                                                                                    "type": "string"
                                                                                }
                                                                            },
                                                                            "type": "object"
                                                                        }
                                                                    },
                                                                    "required": [
                                                                        "value"
                                                                    ],
                                                                    "type": "object"
                                                                },
                                                                "type": "array"
                                                            }
                                                        },
                                                        "required": [
                                                            "authority",
                                                            "status",
                                                            "values"
                                                        ],
                                                        "type": "object"
                                                    },
                                                    "type": "array"
                                                }
                                            },
                                            "type": "object"
                                        },
                                        "value": {
                                            "type": "string"
                                        }
                                    },
                                    "type": "object"
                                }
                            },
                            "type": "object"
                        }
                    },
                    "type": "object"
                },
                "locale": {
                    "type": "string"
                },
                "requestId": {
                    "type": "string"
                },
                "timestamp": {
                    "type": "string"
                },
                "type": {
                    "type": "string"
                }
            },
            "type": "object"
        },
        "version": {
            "type": "string"
        }
    },
    "type": "object"
}
```
</div></details>


###②-2.スキル起動時（IntentがLaunchRequestの際）の応答処理を作成
次に、スキル起動時（IntentがLaunchRequestの際）の応答処理を作成していきます。
Alexaから受信するJSONの中のtypeスキーマにLaunchRequestが含まれるので、それがあるかどうか条件式で判別しましょう。
Trueの場合の応答処理を応答モジュールで用意します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/133230/2abc74fc-96ef-8b4e-110a-2a4c72e89326.png)

<details><summary>応答用のJSON（クリックすると開きます）</summary><div>

```response1.json
{
  "response": {
    "outputSpeech": {
      "ssml": "<speak><prosody pitch='x-high'>ごんただよ！</prosody></speak>",
      "type": "SSML"
    },
    "reprompt": {
      "outputSpeech": {
        "ssml": "<speak><prosody pitch='x-high'>なにしてあそぶ？</prosody></speak>",
        "type": "SSML"
      }
    },
    "shouldEndSession": "False"
  },
  "version": "1.0"
}
```
</div></details>

###②-3.Intentごとの応答処理を作成
同様に、起動時以外のIntentごとの応答処理を作っていきます。
最小のnameというスキーマにwalkingという値が入って送られてくるので、条件式を用意して、応答処理を作ります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/133230/b1d69efa-4626-9948-243b-d7cd4c5128cc.png)

<details><summary>応答用のJSON（クリックすると開きます）</summary><div>

```response2.json
{
  "response": {
    "outputSpeech": {
      "ssml": "<speak><audio src='https://dl.dropboxusercontent.com/s/あなたの音声ファイルのURL/あなたの音声ファイル.mp3' /></speak>",
      "type": "SSML"
    },
    "reprompt": {
      "outputSpeech": {
        "ssml": "<speak><prosody pitch='x-high'>ほかになにしてあそぶ？</prosody></speak>",
        "type": "SSML"
      }
    },
    "shouldEndSession": "False"
  },
  "version": "1.0"
}
```
</div></details>

##③自作音声ファイルの準備とDropboxでの共有
それでは、AmazonEchoのスキルで使う音声ファイルを用意しましょう。今回はiPhoneで再生した筆者の愛犬のなき声を利用します。

###③-1.AmazonEchoで利用可能な形式へ「FFmpeg」を用いて変換
利用可能な形式が限られているため、音声ファイルを変換します。
　-　90秒以内である
　-　特定のビットレート・周波数にフォーマットされている
　-　個人情報や機密情報を含まない

今回は、「FFmpeg」という無料ソフトウェアで対応する形式に変換しました。
参考　[AlexaスキルでMP3ファイルを流そう！効果音を入れてワンランクアップのスキル開発](https://dotstud.io/blog/nodejs-alexa-skill-mp3/#%E8%87%AA%E5%88%86%E3%81%A7%E7%94%A8%E6%84%8F%E3%81%97%E3%81%9Fmp3%E9%9F%B3%E6%BA%90%E3%82%92%E4%BD%BF%E3%81%86%E5%A0%B4%E5%90%88)

```
$ ffmpeg -y -i before.mp3 -ar 16000 -ab 48k -codec:a libmp3lame -ac 1 after.mp3
```

###③-2.dropboxでMP3ファイルを共有し、URLを作成する
次に、完成した音声ファイル（MP3ファイル）をオンライン上に共有します。今回は無料で利用可能なDropboxに保存します。
ファイルをアップロードし、共有を行うと、以下のような共有URLが取得できます。

```編集前
https://www.dropbox.com/s/[取得したURL]?dl=0
```

しかしこのままではAmazonEchoのスキル上で再生できないので、以下のように少しURLを編集します。このURLを先程のAzureLogicAppsの中のSSML文の中に入れてあげると、Dropboxの音声ファイルをAmazonEchoのスキル上で再生することができるようになります。

```変換後
https://dl.dropboxusercontent.com/s/[取得したURL]
```

###公式のサウンドライブラリを使う / 音声ファイルを用意できない方へ
今回、自分で用意できない場合は、以下の公式のサウンドライブラリから音源を使うことも可能です。
[サウンドライブラリ - Animal Sounds（動物の声）](https://developer.amazon.com/ja/docs/custom-skills/animal-sounds.html)

```音声のSSMLサンプル
<speak>
    これはアレクサの通常音声です。音響効果「クマのうなり声（1）」を再生します。
    <audio src='soundbank://soundlibrary/animals/amzn_sfx_bear_groan_roar_01'/>
</speak>
```

#3. まとめ
今回はAzureLogicAppsを使ってAmazonEchoのスキルを作ってみました。一般的にはAWSのLambdaを使った開発が多いですが、JSONの受け渡しができるAPIサーバを用意できれば簡単にAmazonEchoのスキルを開発することができます。ぜひみなさんも作ってみてくださいね！

AzureLogicAppsはオフィス系のモジュール(JiraやSalesforceなど)が多いので、オフィスでのスマートスピーカーアプリ開発にもぴったりです。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/133230/f22e4dee-d22c-e6e3-b1fa-0aaf4bbba55d.png)