Bot Builder v4 でボット開発 : スキルを開発する - その 4 メッセージタイプのアクション追加

[前回](https://qiita.com/kenakamu/items/0d6a85da25390a425054)までテンプレートで作成されたソリューションを見てきました。今回は新しくメッセージタイプのアクションを追加してみます。

# 追加するアクション

現在時刻を返すアクションを追加します。

## LUIS の更新

メッセージタイプは LUIS で判定を行うため、まずは LUIS の更新から行います。

1\. Deployment\Resources\LU\en-us\HelloSkill.lu を開き、以下を追加。

```shell
## GetTime
- what time is it now?
- tell me current time
- show me current time
- Do you know what time it is now?
- get current time
```

2\. PowerShell よりモデルを更新。

```powershell
cd <プロジェクトフォルダ>
.\Deployment\Scripts\update_cognitive_models.ps1
```

3\. 結果を確認。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/c59849ad-9f30-2032-5ba1-8446196d43fe.png)

4\. LUIS 側でも確認。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/a14f396a-e857-4e8a-2c04-f745be2fa3b5.png)

5\. Services\HelloSkillLuis.cs に新しいインテントがあることを確認。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/93f3bee0-a5a5-4769-af71-5a362fdb9eda.png)

## Response の追加

次にメッセージを追加します。本来は全言語追加しますが、ここでは英語のみとします。

1\. Responses フォルダに GetTimeResponses.lg を追加。

```text
> ------------ Activity templates ------------

# CurrentTimeMessage
[Activity
    Text = @{CurrentTimeText()}
    InputHint = acceptingInput
]


> ------------ Text templates ------------

# CurrentTimeText
- It's @{CurrentTime}
```

2\. 言語ファイルはサポートする分必要なため、ファイルをコピーして拡張子をそれぞれの言語に設定。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/e0fcc7f4-c170-3f21-f0b4-840c21c256db.png)

3\. Startup.cs でテンプレートに追加。

```csharp
var templateFiles = new List<string>() { "MainResponses", "SampleResponses", "GetTimeResponses" };
```
4\. Visual Studio よりそれぞれのファイルのプロパティで「Copy if newer」を選択。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/d3824db9-b95a-e526-f484-6856d7427ac4.png)

## マニフェストの更新

dispatchModels\intents に GetTime を追加するだけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/736b2511-5a15-b6cb-541f-8d9d9a78cabb.png)

## GetTimeDialog の追加

1\. Dialogs フォルダに GetTimeDialog.cs を追加。

```csharp:GetTimeDialog.cs
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;

namespace HelloSkill.Dialogs
{
    public class GetTimeDialog : SkillDialogBase
    {
        public GetTimeDialog(
            IServiceProvider serviceProvider,
            IBotTelemetryClient telemetryClient)
            : base(nameof(GetTimeDialog), serviceProvider, telemetryClient)
        {
            var getTimeWaterfall = new WaterfallStep[]
            {
                GetCurrentTime,
                End,
            };

            AddDialog(new WaterfallDialog(nameof(GetTimeDialog), getTimeWaterfall));

            InitialDialogId = nameof(GetTimeDialog);
        }

        private async Task<DialogTurnResult> GetCurrentTime(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            dynamic currentTime = new { CurrentTime = DateTime.Now.ToString() };
            var response = TemplateEngine.GenerateActivityForLocale("CurrentTimeMessage", currentTime);
            await stepContext.Context.SendActivityAsync(response);
            return await stepContext.NextAsync();
        }

        private Task<DialogTurnResult> End(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            return stepContext.EndDialogAsync();
        }
    }
}
```
2\. Startup.cs にダイアログを追加。

```csharp
// Register dialogs
services.AddTransient<SampleDialog>();
services.AddTransient<SampleAction>();
services.AddTransient<GetTimeDialog>();
services.AddTransient<MainDialog>();
```

3\. MainDialog.cs で MainDialog クラスプロパティにGetTimeDialog を追加。

```csharp:MainDialog.cs
private GetTimeDialog _getTimeDialog;
```
4\. コンストラクタでインスタンスを取得。

```csharp:MainDialog.cs
_getTimeDialog = serviceProvider.GetService<GetTimeDialog>();
AddDialog(_getTimeDialog);
```
5\. RouteStepAsync メソッドで分岐を追加。

```csharp:MainDialog.cs
switch (intent)
{
    case HelloSkillLuis.Intent.Sample:
        {
            return await stepContext.BeginDialogAsync(_sampleDialog.Id);
        }
    case HelloSkillLuis.Intent.GetTime:
        {
            return await stepContext.BeginDialogAsync(_getTimeDialog.Id);
        }
    case HelloSkillLuis.Intent.None:
    default:
        {
            // intent was identified but not yet implemented
            await stepContext.Context.SendActivityAsync(_templateEngine.GenerateActivityForLocale("UnsupportedMessage"));
            return await stepContext.NextAsync();
        }
}
```
# 動作確認

実装が終わったため、早速テストしてみます。

## ローカルでのテスト

実装が終わったのでまず単体でテストします。F5 を押下してデバッグ実行。エミュレーターで動作を確認。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/b442ec92-1073-206b-e2c5-546a98482e41.png)

## 公開してテスト

今回はアシスタントボット経由でのテストを行います。

1\. publish.ps1 スクリプトでスキルを公開。

```shell
.\Deployment\Scripts\publish.ps1 -name kenakamuhelloskill-om7pvdw -resourceGroup kenakamuhelloskill
```
2\. 変更されたマニフェストを確認。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/a03db349-9a78-a4c6-fb54-c92b0fb9321c.png)

3\. アシスタントボット側も botskills update でスキルを更新。

```powershell
botskills update --remoteManifest "https://kenakamuhelloskill-om7pvdw.azurewebsites.net/manifest/manifest-1.1.json" --cs --luisFolder "<HelloSkill へのパス>\Deployment\Resources\LU\"
```
4\. アシスタントボット用の Dispatch LUIS アプリで HellSkill に新しい例文が追加されたことを確認。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/ce596177-1517-a528-8dbf-da908790928f.png)

5\. アシスタントボットも Azure に発行して動作を確認。

```powershell
.\Deployment\Scripts\publish.ps1 -name kenakamumyassistant-qbp5igb -resourceGroup kenakamumyassistant
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/b32dacd5-3378-28b2-acc3-67beeffaffaa.png)

# まとめ

メッセージタイプのスキルは、これまでの知識でほぼ動作が成り立ちます。次回はイベントタイプのスキルについて見ていきます。

[次の記事へ](https://qiita.com/kenakamu/items/b0493a618ddfcbcf0571)
[目次に戻る](https://qiita.com/kenakamu/items/6dc043cfc1f199032883)