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

[前回](https://qiita.com/kenakamu/items/f78e0e1579ae9c6b1606)は メッセージタイプのアクションを追加しました。今回はよく Power Virtual Agent 経由で呼び出されるイベントタイプのアクションを追加してみます。検証で使う Power Virtual Agents 環境はすでに持っていると想定していますが、手元にない場合は [Power Virtual Agents](https://powervirtualagents.microsoft.com/en-us/) より体験版を申し込んでみてください。

尚、PVA は現在英語にのみ対応しています。

# PVA にスキルを追加

まず現状のままで追加して動作を確認します。

## 登録先 Azure AD の確認と対応

現在開発しているボットの登録先 Azure AD と PVA の環境が同じ場合は、次のマニュフェスト更新手順まで飛ばしてください。

PVA に追加できるスキルは、同じ Azure AD テナントに登録されている必要があります。今回私は自社の Azure AD 上にアプリを登録しましたが、体験版の PVA を使っているためテナントが異なります。よって以下の手順で手動で登録を行います。尚、そちらのテナントに対する Azure サブスクリプションは不要です。

1\. PVA にログインした状態で、別のタブより [Azure ポータル](https://portal.azure.com) に接続。Azure AD を選択。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/6145017c-4458-cd0b-dbf6-ed197218d1ce.png)

2\. App registration を選択して「+ New registration」を選択。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/4b18079a-1673-c012-9395-1e62be3f6189.png)

3\. 以下のように情報を入力して「Register」をクリック。

- Name: スキル名(ここでは HelloSkill)
- Support account Type: Accounts in any organizational directory (Any Azure AD directory - Multitenant)
- Redirect URI: https://token.botframework.com/.auth/web/redirect

4\. Application ID を控える。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/48be7dfa-e466-6a8d-387c-5bb8bedf41f8.png)

5\.「Certificates ＆ Secrets」より「＋ New client secret」をクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/9805aab8-5a07-36bd-3dcf-c4d01c977e72.png)

6\. 任意の名前で登録し、表示されたキーを控える。

7\. Branding より Home page URL にボットのアドレスを指定。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/b95dd1f3-177e-4ef7-202f-c5a85b4a4e37.png)

8\. Bot をホストしている Azure より Web App の構成を変更。新しい MicrosoftAppId と MicrosoftAppPassword を入力。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/00d1cbeb-2f7e-5a82-0086-5fd2f9047263.png)

9\. Visual Studio に戻り、appsettings.json でも同様に MicrosoftAppId と MicrosoftAppPassword を更新。

## マニフェスト更新

現時点で PVA のマニフェストはバージョンが異なるため、個別に設定が必要です。

1\. manifest-1.0.json を開き、{YOUR_SKILL_URL} と {YOUR_SKILL_APPID} を差し替え。

2\. すべての変更を保存して Azure へ公開。

```powershell
.\Deployment\Scripts\publish.ps1 -name kenakamuhelloskill-om7pvdw -resourceGroup kenakamuhelloskill
```
3\. マニフェストのアドレスへアクセスして更新された内容を確認。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/f71a3383-c221-c72e-3c53-5dbd6045e4bd.png)

## PVA より追加

最後に PVA よりスキルを追加します。

1\. [PVA ポータル](https://powerva.microsoft.com/) よりボットを選択して、Skills を選択。「+ Add skill」をクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/7ae19ccf-32b8-00e6-67f8-c10b24ac23ac.png)

2\. Azure に公開した manifest-1.0.json のアドレスを入力して「Next」をクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/45cbb6b3-1d15-3bec-c42f-63f2d3160930.png)

3\. 登録情報が表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/6e1f746b-4de1-9a90-80ba-ffe6b8dac14c.png)

画面にある通り、メッセージタイプとイベントタイプのどちらも登録されました。

# トピックから利用

1\. Topics より「+ New topic」をクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/adb61574-f2bc-64b4-9b64-d19dec05c921.png)

2\. 任意の名前と指定し、トリガーフレーズを入力。Add をクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/c226654a-52f8-5e9d-450f-d4ece9fbad12.png)

3\.「Go to authoring canvas」をクリックすると、編集画面に遷移。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/87a3ee91-e831-992d-b63c-0bf1307471f5.png)

4\. 1 つ目の Message を作成して「Call an action」を選択。すぐにスキルが出ない場合があるので、その場合は何度か実行してみる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/834b3f34-470e-2934-6211-132591b50277.png)

5\. まずメッセージタイプのアクションを確認。「Retrieve the users utterance and ...」を選択。最後に「End of conversation」を追加して保存。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/8c542a51-5a96-a24c-30a2-b0b98d20dad5.png)

6\. テストより動作を確認。初めに入力した「sample dialog」がそのままスキルに引き渡され処理される。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/b5575c75-12be-7514-c07a-18631447cc88.png)

7\. 次にイベントタイプのアクションに差し替え。こちらはインプットとアウトプットがあるため、以下のような構成を行う。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/907786c4-78fa-48f2-5363-424901d5258a.png)

8\. テストしてアクションが呼ばれたことを確認。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/aa8b4b40-ec68-9a45-5e83-d0aec2fdb72e.png)


# アクションの追加

次に現在時刻を返すイベント型のアクションを追加します。

## マニフェストの更新

1\. activities に getTimeAction を追加。

```json
"getTimeAction": {
  "description": "Get Time action which return current time.",
  "type": "event",
  "name": "GetTimeAction",
  "resultValue": {
    "$ref": "#/definitions/timeActionResponseObject"
  }
},
```

2\. definitions に出力を追加。

```json
"timeActionResponseObject": {
  "type": "object",
  "properties": {
    "currentTime": {
      "type": "string",
      "description": "Current Time."
    }
  }
}
```
## Dialog の追加

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

```csharp:GetTimeAction.cs
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Newtonsoft.Json;

namespace HelloSkill.Dialogs
{
    public class GetTimeActionOutput
    {
        [JsonProperty("currentTime")]
        public string CurrentTime { get; set; }
    }

    public class GetTimeAction : SkillDialogBase
    {
        public GetTimeAction(
            IServiceProvider serviceProvider,
            IBotTelemetryClient telemetryClient)
            : base(nameof(GetTimeAction), serviceProvider, telemetryClient)
        {
            var getTimeWaterfall = new WaterfallStep[]
            {                
                GetCurrentTime,
            };

            AddDialog(new WaterfallDialog(nameof(GetTimeAction), getTimeWaterfall));
            InitialDialogId = nameof(GetTimeAction);
        }

        private async Task<DialogTurnResult> GetCurrentTime(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            var actionResponse = new GetTimeActionOutput();
            actionResponse.CurrentTime = DateTime.Now.ToString();
            return await stepContext.EndDialogAsync(actionResponse);
        }
    }
}
```

2\. Startup.cs にダイアログを追加。

```csharp
services.AddTransient<GetTimeAction>();
```
3\. MainDialog.cs のクラスプロパティに GetTimeAction を追加。

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

```csharp
_getTimeAction = serviceProvider.GetService<GetTimeAction>();
AddDialog(_getTimeAction);
```
5\. RouteStepAsync のイベントタイプ処理に GetTime を追加。

```csharp
case "GetTimeAction":
    {
        return await stepContext.BeginDialogAsync(nameof(GetTimeAction));
    }
```

## 公開してテスト

1\. スキルをスクリプトで Azure に公開。

```shell
.\Deployment\Scripts\publish.ps1 -name kenakamuhelloskill-om7pvdw -resourceGroup kenakamuhelloskill
```
2\. PVA は登録済スキルの更新が現状できないため、トピックとスキルと削除。(トピックを削除しなくても依存しているフローだけ削除でもOK)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/7e52a803-2f80-952e-2c27-2662ada1754c.png)

3\. 再びスキルを追加。新しく GetTimeAction が追加されている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/0ce6139c-2e22-ae0b-d3f2-6197b42c81ff.png)

4\. 詳細も確認。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/ac62f3dd-519d-b11d-e8b9-db55e88767f4.png)

5\. 新しいトピックを追加。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/11ae446b-8538-0be1-b2d9-1dba44e43bcf.png)

6\. GetTimeAction を利用したフローを作成。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/d5d83742-ed2a-b328-b008-060f2500c3b6.png)

7\. 出力結果は移行のアクションで利用できる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/62e1ca24-442f-188b-cc78-a848eb3c985d.png)

8\. テストを実行。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/ea16dabb-8b25-a5bd-5f4f-2b9fc825ee8c.png)

# Dispatch で呼び出し

アシスタントボットなどから呼び出す場合はイベントアクティビティを送るか、/event:{} を使って送れます。
使用したいアクションを Name に、入力パラメーターがある場合は Value に値を渡します。

```csharp
stepContext.Context.Activity.Text = 
@"/event:{ ""Name"": ""GetTimeAction"", ""Value "": """" }";
```
詳細は [Events](https://microsoft.github.io/botframework-solutions/virtual-assistant/handbook/events/) を参照してください。

# まとめ

今回は イベントアクションを追加して、PVA から呼び出してみました。スキルアプリケーションが PVA と同じテナントに存在する必要があったり、マニフェストが分かれていたり、いくつか注意点がありました。

次回はユニットテストについて確認します。

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