Visual StudioからAlexaスキルをデプロイする。C#で。

#はじめに
Alexaスキルの実際の処理を行うエンドポイントの作成には、一般的にはNode.jsかPythonが使われます。
これらの言語はAmazonよりSDKが提供されており、それを使うことで効率的に開発を行うことができますし、情報も多いのでAlexaスキルを作りたい場合はぜひNode.jsかPythonを選んでください。
情報量で言ったらNode.jsのほうが多いのでNode.jsがおすすめです。

しかし、ここではC#で関数を作成してデプロイする方法を紹介します。
なぜ、わざわざC#なのか、というと、Visual Studioのインテリセンスなどの便利機能の恩恵を目一杯受けられるからです。

素晴らしいですね！！

でもまあそのかわりネットでの情報量がものすごく少なくなってしまうのですが。

#環境
- Windows 10 Pro ver.1809
- Visual Studio Community 15.8

#手順概要
手順はざっくり以下のとおりです。

1. Visual StudioからAWSにアクセスする用のIAMユーザーの作成
2. AWS Toolkit for Visual Studioのインストール（拡張機能）
3. Alexa.NETのインストール（Nuget）
4. C#で実装
5. AWS Lambdaへデプロイ

#IAMユーザーの作成
以下の画像はAWS Toolkitをインストールしたあとのものですが、Visual StudioからはLambdaだけではなく様々なサービスにアクセスできるので、ここで作成するユーザーにはかなり大きな許可を与えることになります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/5aaac8f0-d3a0-977a-d2c1-0e4de77a6155.png)


###まずはAWSコンソールの「ユーザー」から、ユーザーを追加しましょう。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/059118be-0549-dec4-3014-f8bb8d774c1b.png)


###ユーザー名を入力し、次のステップへ進みます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/5a58c3e4-a31f-03da-fe24-b4d58e3afdfc.png)

###そうしたら既存のポリシーから「AdministratorAccess」にチェックをつけて、次のステップに進みます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/f4b3ad36-95c2-62b5-563a-354d6bdf0454.png)

###最後に設定を確認してユーザーを作成します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/946061b8-dfb9-60b0-747c-cea922c82478.png)


これで大きな権限持ったユーザーが作成されました。
このときに発行されるアクセスキーやシークレットアクセスキーを大事に保管しておきましょう。

###補足
ユーザーにポリシーを直接アタッチするのではなく、グループは作ってそこにポリシーをアタッチしておき、ユーザーをそのグループに所属させれば同じことになります。


#AWS Toolkit for Visual Studioのインストール
Visual StudioからAWSサービスにアクセスするための拡張機能がAmazonから提供されています。
それがAWS Toolkit for Visual Studioです。

まず、Visual Studioを起動します。
次に、`ツール`から`拡張機能と更新プログラム`を選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/2dc1517e-0688-6dac-d06e-779ad93cc634.png)

ウィンドウの左側のカラムから`オンライン`をクリックし、検索窓に`aws`と入力すると、目的の`AWS Toolkit for Visual Studio`が見つかります。
ダウンロードボタンを押すと、ダウンロードが行われ、ダウンロード完了後にVisual Studioを閉じると拡張機能がインストールされます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/7ee0783e-aee1-47a8-99fd-57c57f415aab.png)


###初回起動時の設定
インストール後の初回起動時には以下の設定タブが表示されます。
この`Access Key`と`Secret Key`にそれぞれ、先程作成したIAMユーザーの`アクセスキー`と`シークレットアクセスキー`を入力すると、「Save and Close」ボタンが有効になるのでクリックすると設定完了です。
`Profile Name`はそのままでも良いですし、好きな名前をつけても良いです。これはIAMユーザー名とは関係ないので、好きにして大丈夫です。
`Account Number`は空欄のままでOKです。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/4b3a1e4b-ddc0-b3f1-bfcd-c11acced34f8.png)

メニューの`表示`から`AWS Explorer`を表示することができます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/e6047fe3-603b-ca19-d6d8-4f589059c7aa.png)

AWS Explorerは冒頭でも示したとおりです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/5aaac8f0-d3a0-977a-d2c1-0e4de77a6155.png)


#Alexa.NETのインストール
Alexa.NETというのはC#でAlexaスキルのリクエストとレスポンスを扱いやすくしてくれる便利なライブラリで、NuGetで配布されています。
これはAmazonではなく有志の方が作成してくれているものです。ありがたや。

これはNuGetパッケージとしてされており、プロジェクトにライブラリとして追加して使用するものなので、まずはVisual Studioのプロジェクトを作成する必要があります。

##Lambdaプロジェクトの作成
`ファイル`→`新規作成`→`プロジェクト`を選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/6e030b6d-466b-d608-52ad-87a1305b5991.png)


プロジェクトの種類として`AWS Lambda Project(.NET Core)`を選択して、名前を決めたら「OK」ボタンを押します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/d1d38647-d5be-5725-4972-e12f15c978ec.png)

次にテンプレートを選択します。
Alexaスキルのテンプレートはないので、ここでは`Empty Function`を選択し、「Finish」ボタンを押します。
これでプロジェクトが作成されます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/4bd63e6c-0c6e-f7f3-97dd-1511587530f9.png)

作成直後のLambda関数本体である`Function.cs`は以下のとおりです。

```csharp:Function.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

using Amazon.Lambda.Core;

// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))]

namespace MyFirstCSharpSkill_Lambda
{
    public class Function
    {
        
        /// <summary>
        /// A simple function that takes a string and does a ToUpper
        /// </summary>
        /// <param name="input"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        public string FunctionHandler(string input, ILambdaContext context)
        {
            return input?.ToUpper();
        }
    }
}
```


##本当にAlexa.NETのインストール
プロジェクトが作成できたら、次はAlexa.NETをプロジェクトにインストールします。
`ツール`→`NuGetパッケージマネージャー`→`ソリューションのNuGetパッケージの管理`を選択します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/e27e3b3b-438f-3a12-83c9-4f307a151496.png)


NuGetタブの「参照」で検索窓に「alexa.net」と入力すると「Alexa.NET」が見つかります。
右カラムのインストール先プロジェクトにチェックをつけて、「インストール」ボタンを押してインストールします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/2e8d0231-d8d7-791c-33ad-0b0b83157907.png)



#C#での実装
いよいよC#でコーディングしていきます。
Function.csにはLambda関数の雛形が作られています。
この中の`Function`クラスの`FunctionHandler`メソッドがAlexaからのリクエストを受け取り、レスポンスを返す役割を担います。
しかし、この時点ではstring型を入力として受け取り、string型を返すものになっています。

まずはこの引数と返り値をAlexaスキル用に変更します。
具体的には、`SkillRequest`型を受け取り、`SkillResponse`型を返すようにします。


```csharp:Function.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Alexa.NET.Request;//追加しました。
using Alexa.NET.Response;//追加しました。
using Amazon.Lambda.Core;

// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))]

namespace MyFirstCSharpSkill_Lambda
{
    public class Function
    {
        
        /// <summary>
        /// A simple function that takes a string and does a ToUpper
        /// </summary>
        /// <param name="input"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        public SkillResponse FunctionHandler(SkillRequest input, ILambdaContext context)
        {
            var skillResponse=new SkillResponse();

            //ここでレスポンスを構築する

            return skillResponse;
        }
    }
}

```

Alexa.NETの便利なところは、Alexaから受け取ったJSON形式のリクエストが自動的に.NETのクラスにデシリアライズされ、それを返すときにJSON形式にシリアライズされる点です。
これによって入出力におけるデータ形式を考える必要がなく、`Request`オブジェクトから必要なデータを取得し、最終的に必要な`Response`オブジェクトを構築して`return`するだけですみ、コーディングがとてもシンプルになります。

##一番シンプルなAlexaスキルを作成
このままだとデプロイしたとしてもAlexaスキルとしての動作を確認できないので、最低限の応答を返すようにします。

```csharp:FunctionHandlerメソッド
        public SkillResponse FunctionHandler(SkillRequest input, ILambdaContext context)
        {
            //ResponseプロパティにAlexaに喋らせる内容などを突っ込んでいく
            var skillResponse = new SkillResponse
            {
                Version = "1.0",//お約束
                Response = new ResponseBody()//お約束
            };


            //リクエストタイプが「LaunchRequest」かそれ以外の「IntentRequest」か判別する
            //今回はスキル起動時に送られる「LaunchRequest」のときとそれ以外のときで異なるレスポンスを返すようにした。
            if (input.Request.Type == nameof(Alexa.NET.Request.Type.LaunchRequest))
            {
                skillResponse.Response.OutputSpeech = new PlainTextOutputSpeech
                {
                    Text = "スキルを起動しましたよ。"
                };
            }
            else
            {
                skillResponse.Response.OutputSpeech = new PlainTextOutputSpeech
                {
                    Text = "スキルを起動できませんでしたよ。"
                };
            }


            //セッション終了させる。
            skillResponse.Response.ShouldEndSession = true;

            return skillResponse;
        }
```

スキルが`呼び出し名`によって起動されたとき、リクエストのタイプは`LaunchRequest`になります。それ以外の場合は`IntentRequest`になります。

今回はLaunchRequestかどうかでAlexaに喋らせる内容を変えるシンプルな構成にしました。

#AWS Lambdaにデプロイ

`ソリューションエクスプローラ`からプロジェクト名(ここでは「MyFirstCSharpSkill_Lambda」)の上で右クリックして、コンテキストメニューから「Publish AWS Lambda...」を選択します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/698b89e8-6ffd-ce41-3a6c-2bdb5d25de14.png)

以下のウィンドウが表示されるので、`Function Name`にお好みの名前を入力して、お好みのリージョンを選択して「Next」ボタンを押してください。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/baf298b1-973d-1773-c608-8b3bfaec53bc.png)


次に表示されるウィンドウでは、`Role Name`の欄にプルダウンから`New role based on AWS managed policy: AWSLambdaBasicExecutionRole`を選択します。
ここでは今回デプロイするLambda関数に許可する権限を指定しています。
今回はAWSの他のサービスを呼び出すことはないので、「AWSLambdaBasicExecutionRole」を指定していますが、もしDynamoDBなどを使用する場合はDynamoDBの権限を与える必要があります。

「Upload」ボタンを押すとデプロイが始まり、進捗を表示するウィンドウが表示されます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/ef803f10-8053-d763-f728-b0da0023da3e.png)

デプロイが完了すると進捗ウィンドウが自動的に閉じて、Visual Studioに以下のタブが表示されます。
ここで、`Example Requests`の欄のプルダウンから`Alexa Start Session`を選択し、「Invoke」ボタンを押してください。
右側の`Response`欄に「スキルを起動しました」を含むJSON形式のデータが表示されれば、デプロイしたAlexaスキルが正しく動作しています。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/5d7bb19b-a879-245c-f05d-8c312d2cb336.png)

AWSコンソールのLambda関数のリストも確認してみましょう。
デプロイした関数が表示されています。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/d0e23aab-7d5f-74cc-58fc-c99a8fe41a03.png)


あとはAlexa開発者コンソール側での対話モデルの作成です。


#対話モデルの作成
「スキルの作成」ボタンを押します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/46c7b901-bd64-ddb9-fa74-88a0ad59a8d4.png)

スキル名を入力し、カスタムスキルを作成します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/90daf5b1-5298-6d05-54e9-b5cc2a2aec54.png)

呼び出し名を決めます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/07810392-f370-6715-4ad4-cf9e8b4e64f5.png)

少なくとも1つ以上のインテントを作らなければならないので、何かインテントを作成します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/5f029e07-f3c4-2590-44d0-550d92d45cad.png)

少なくとも1つ以上の発話も登録しなければならないので、何かサンプル発話を登録します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/c9e18bec-e2da-1ea9-db5f-6e36c071012d.png)

次にモデルをビルドします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/817fa905-a083-0a46-3c1c-bd76120575a5.png)

エンドポイントの設定を行います。
このエンドポイントというのが、先程C#で作成したLambda関数になります。
`デフォルトの地域`の欄にそのLambda関数のARN（AWSにおける一意の識別子）を指定することで、この対話モデルとLambda関数が結び付けられます。
![dadjoiasesoifjaso.png](https://qiita-image-store.s3.amazonaws.com/0/245438/43071457-3ec7-0f98-8c2c-2a1eb849b59a.png)

この対話モデルにも当然ARNがありまして、まずは対話モデルのARNをLambda関数側に登録する必要があります。
ちょっとややこしいですが、このエンドポイントのページ内の「クリップボードにコピー」をクリックしてこの対話モデルのARNをコピーします。
そして、一旦このまま開いたままにして、AWSのコンソールへ移ります。
AWS Lambdaの先程作成した関数を開き、この関数のトリガーとして「Alexa Skills Kit」を選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/6852f05d-ed75-50c7-c88d-933c4cbe95ad.png)

次に、その下に「トリガーの設定」と表示されるので、「スキルID検証」を「有効」にして、スキルIDの欄に対話モデルのARNを貼り付けて、「追加」ボタンを押します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/9992fb66-2e2c-d6ae-ac47-43a0d7f681e7.png)


ページ右上の「保存」ボタンを押して変更を保存します。
これでLambda関数側に対話モデルのARNを設定できました。
そして、今度は対話モデルにこのLambda関数のARNを設定するので、この「保存」ボタンの上のARNを選択してコピーしておきます。
塗りつぶしていますが、「arn:」で始まる部分です。
![adjfalsdfjweioajflask.png](https://qiita-image-store.s3.amazonaws.com/0/245438/7720eea1-c63e-3928-e11b-5392e13d4320.png)


では、開きっぱなしにしておいた対話モデル側のエンドポイントのページで、`デフォルトの地域`の欄にLambda関数のARNを貼り付け、「エンドポイントを保存」ボタンを押してください。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/59d982d2-93c3-f56a-173b-9cd7fedc7149.png)

これで対話モデルとLambda関数を結びつけることができました。


#スキルのテスト
対話モデルの方で、「テスト」タブを開いてスキルのテストを有効にして、入力欄に「サンプル呼び出し」と入力してスキルを呼び出してみましょう。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/e5b61be6-c982-8878-652a-e5c84a04d4c6.png)

すると、以下のようにAlexaが応答します。
C#で作成したLambda関数が正しく動いているようですね。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/78b2abfc-1013-47c1-b20c-a9d5c24f6057.png)

ちなみに、LaunchRequestに対するレスポンスではセッションを終了するように指定しているので、ここでスキルは終了します。

じゃあ形式上仕方なく作ったインテントはどうやって呼び出すのかというと、スキルの呼び出しとインテントの指定を一度にやるとOKです。
「サンプル呼び出し」が呼び出し名で、「サンプルを呼び出して」が`SampleIntent`を呼び出す発話になります。
そうすると、リクエストのタイプが`LaunchRequest`ではなく`IntentRequest`になり、もう一方のレスポンスが帰ってきます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/ead8cc07-3186-408e-1902-373c726b6388.png)

Lambda関数に送られるリクエストの内容を見てみると、`type`が`IntentRequest`で`Intent`の`name`が`SampleIntent`になっていることがわかります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/8bd4abca-cb1a-d227-a8a4-12b019ccbd46.png)



#おわりに
これでC#でAlexaスキルを実装するはじめの一歩を踏み出せるのではないかと思います。
慣れ親しんだC#で、Visual Studioのインテリセンスの恩恵を受けて開発するのもいいのではないでしょうか。

今回は`LaunchRequest`かそれ以外かで応答を変えていましたが、次回は複数のインテントに対して、異なる応答を返すようにしてみます。

次回は[こちら](https://qiita.com/shuhey/items/1dd8412130458e868f5e)。