LoginSignup
2
2

More than 5 years have passed since last update.

Bot Builder v4 でボット開発 : ダイアログから高度なアダプティブカードを送る

Last updated at Posted at 2018-11-06

前回は基本的なアダプティブカードを使ってダイアログを作成しました。今回はより高度で複雑なものを試していきます。

アダプティブカードの要素

色々試す前に、まずアダプティブカードで出来ることを見ていきます。詳細は Card Schema を参照。

アダプティブカードには以下の要素があります。

  • Card Elements - テキストや画像などカード上に表示される要素
  • Containers - 複数の Card Elements を 1 つにまとめる箱
  • Actions - ボタンクリック時の動作
  • Inputs - データを入力するための機能
  • Speech - 音声用の情報

Card Elements

  • TextBlock - テキストの表示。フォントのサイズや色、スタイルを設定できる
  • Image - 画像の表示
  • Media - 動画や音声の再生ができる
    • MediaSource - メディアのソース定義

テキストのマークダウン

太字やイタリック、ブレットポイントなどの書式をマークダウンで指定可能

Containers

  • Container - 複数のアイテムをグループ化
  • ColumnSet - 複数の列を作成し、横並びに表示
    • Column - ColumnSet で使う 1 列の定義
  • FactSet - キー/値ペアをテーブル形式で表示
    • Fact - FactSet で使う 1 つのキー/値の定義
  • ImageSet - 複数イメージのグループ化

Actions

  • Action.OpenUrl - 指定した URL を開く
  • Action.Submit - Inputs で指定したデータを集めて送る
  • Action.ShowCard - 定義したアダプティブカードを開く

Inputs

  • Input.Text - テキストの入力。メールアドレスや URL などスタイルの指定や既定値などが設定可能
  • Input.Number - 数値の入力
  • Input.Date - 日付の入力
  • Input.Time - 時間の入力
  • Input.Toggle - 2 つのオプションのトグル
  • Input.ChoiceSet - 選択肢の提示
    • Input.Choice - ChoiseSet で使う選択肢の定義

プロファイルダイアログの拡張

前回プロファイルダイアログをアダプティブカード化しましたが、内容を拡張してみます。

1. まず Models フォルダの UserProfile.cs を以下の様に変更。追加の情報を持てるようにする。

using System;
using System.Collections.Generic;

public class UserProfile
{
    public string Name { get; set; }
    public int Age { get; set; }
    public DateTime BirthDay { get; set; }
    public string Email { get; set; }
    public string Phone { get; set; }
    public string CatNum { get; set; }
    public List<string> CatTypes { get; set; }
    public bool HasCat { get; set; }
    public bool PlayWithCat { get; set; }
}

2. 次に Profile.json を以下の様に変更。

  • Input.ChoiceSet を複数の方法で利用
  • Input.Text のスタイルを複数利用
{
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "type": "AdaptiveCard",
    "version": "1.0",
    "body": [
        {
            "type": "ColumnSet",
            "columns": [
                {
                    "type": "Column",
                    "width": 2,
                    "items": [   
                        {
                            "type": "TextBlock",
                            "text": "プロファイルを登録します。",
                            "weight": "bolder",
                            "size": "medium"
                        },
                        {
                            "type": "TextBlock",
                            "text": "ボットの利用にあたり、もう少し詳しくあなたの情報が必要です。",
                            "isSubtle": true,
                            "wrap": true
                        },
                        {
                            "type": "TextBlock",
                            "text": "名前",
                            "wrap": true
                        },
                        {
                            "type": "Input.Text",
                            "id": "name",
                            "placeholder": "姓名",
                            "style": "text"
                        },
                        {
                            "type": "TextBlock",
                            "text": "誕生日",
                            "wrap": true
                        },
                        {
                            "type": "Input.Date",
                            "id": "birthday"
                        },
                        {
                            "type": "TextBlock",
                            "text": "メールアドレス",
                            "wrap": true
                        },
                        {
                            "type": "Input.Text",
                            "id": "email",
                            "placeholder": "youremail@example.com",
                            "style": "email"
                        },
                        {
                            "type": "TextBlock",
                            "text": "電話番号"
                        },
                        {
                            "type": "Input.Text",
                            "id": "phone",
                            "placeholder": "(xx)-xxxx-xxxx",
                            "style": "tel"
                        },
                        {
                            "type": "TextBlock",
                            "text": "ネコ飼っていますか?"
                        },
                        {
                            "type": "Input.ChoiceSet",
                            "id": "hasCat",
                            "style": "expanded",
                            "value":"true",
                            "choices": [
                                {
                                    "title": "はい",
                                    "value": "true"
                                },
                                {
                                    "title": "いいえ",
                                    "value": "false"
                                }
                            ]
                        },
                        {
                            "type": "TextBlock",
                            "text": "飼っているネコの種類を教えてください。(複数選択可)"
                        },
                        {
                            "type": "Input.ChoiceSet",
                            "id": "catTypes",
                            "isMultiSelect": true,
                            "choices": [
                                {
                                    "title": "サバトラ",
                                    "value": "サバトラ"
                                },
                                {
                                    "title": "キジトラ",
                                    "value": "キジトラ"
                                },
                                {
                                    "title": "ハチワレ",
                                    "value": "ハチワレ"
                                },
                                {
                                    "title": "その他",
                                    "value": "その他"
                                }
                            ]
                        },
                        {
                            "type": "TextBlock",
                            "text": "全部で何匹いますか?"
                        },
                        {
                            "type": "Input.ChoiceSet",
                            "id": "catNum",
                            "style": "compact",
                            "value": "1",
                            "choices": [
                                {
                                    "title": "1",
                                    "value": "1"
                                },
                                {
                                    "title": "2",
                                    "value": "2"
                                },
                                {
                                    "title": "3",
                                    "value": "3"
                                },
                                {
                                    "title": "4",
                                    "value": "4"
                                },
                                {
                                    "title": "5",
                                    "value": "5"
                                },
                                {
                                    "title": "6匹以上",
                                    "value": "6+"
                                }
                            ]
                        },
                        {
                            "type": "TextBlock",
                            "text": "毎日ネコと遊んでいますか?"
                        },
                        {
                            "type": "Input.Toggle",
                            "title": "毎日ネコを遊んでいます。",
                            "valueOn": "true",
                            "valueOff": "false",
                            "id": "playWithCat"
                        }
                    ]
                },
                {
                    "type": "Column",
                    "width": 1,
                    "items": [
                        {
                            "type": "Image",
                            "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/1/15/Pumiforme.JPG/1280px-Pumiforme.JPG",
                            "size": "auto"
                        }
                    ]
                }
            ]
        }
    ],
    "actions": [
        {
            "type": "Action.Submit",
            "title": "登録"
        }
    ]
}

3. AdaptiveCardResponseValidators.cs の ValidateInput メソッドを以下の様に変更。

public static Task<bool> ValidateInput(PromptValidatorContext<string> promptContext, CancellationToken cancellationToken)
{
    // 文字は返ってこないため Succeeded の場合は false
    if (promptContext.Recognized.Succeeded)
    {
        return Task.FromResult(false);
    }

    // オプションの検証プロパティから NumberRange を取得。
    NumberRange range = promptContext.Options.Validations is NumberRange ?
        (NumberRange)promptContext.Options.Validations :
        new NumberRange() { MinValue = 0, MaxValue = 120 };

    // 一旦 JObject として中身をパース
    var input = JObject.Parse(promptContext.Context.Activity.Value.ToString());
    // CatTypes がある場合、対応する UserProfile クラスのプロパティが 、
    // List<string> のため、JArray に変換。
    if(input.ContainsKey("catTypes"))
        input["catTypes"] = new JArray(input["catTypes"].ToString().Split(','));

    // 誕生日から年齢を計算し新しいプロパティとして追加
    var birthday = DateTime.Parse(input["birthday"].ToString());
    var age = DateTime.Now.Year - birthday.Year;
    if (DateTime.Now < birthday.AddYears(age)) 
        age--;
    input["age"] = age;
    // 0 より小さいか 120 より大きい場合は False
    if (age < range.MinValue ||age > range.MaxValue)
    {
        promptContext.Context.SendActivityAsync($"年齢が{age}歳になります。ただしい誕生日を入れてください。");
        return Task.FromResult(false);
    }
    // 値を入れ替え
    promptContext.Context.Activity.Value = input;
    return Task.FromResult(true);
}

4. ProfileDialog.cs の SummaryStepAsync をシンプルに変更。

private async Task<DialogTurnResult> SummaryStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    var input = JsonConvert.DeserializeObject<UserProfile>(stepContext.Context.Activity.Value.ToString());
    await stepContext.Context.SendActivityAsync(MessageFactory.Text("プロファイルを保存します。"));
    await accessors.UserProfile.SetAsync(stepContext.Context, input, cancellationToken);
    return await stepContext.EndDialogAsync(cancellationToken: cancellationToken);
}

5. 挨拶などにペットの情報を適当に入れてみる。

await turnContext.SendActivityAsync(MessageFactory.Text($"ようこそ '{userProfile.Name}' さん!"));
if (userProfile.HasCat)
    await turnContext.SendActivityAsync(MessageFactory.Text($"{userProfile.CatNum}匹の猫は元気ですか?"));

テスト

1. F5 キーを押下してデバッグ実行。エミュレーターで接続。「はい」をクリック。
image.png

2. アダプティブカードの中身を埋めて、「登録」をクリック。
image.png

3. 意図した通りプロファイルが取得できることを確認。
image.png

4. Restart conversation をクリックして挨拶が変わることを確認。
image.png

もっと色々ためす

アダプティブカードはもっと沢山の事が出来ます。以下のサンプル集の JSON も試してください。ボタンをクリックした際に追加で内容を表示するなど、色々できます。
アダプティブカード : サンプル
image.png

C# でアダプティブカード

C# ですべてを書きたい場合は、AdaptiveCards NuGet パッケージを使います。
GitHub : AdaptiveCards
ただしこの記事を書いた時点では問題があり、うまく動作しません。どうしても C# で書きたい場合は古いライブラリですが、Microsoft.AdaptiveCards NuGet パッケージを使ってください。

まとめ

アダプティブカードを使うと表現の自由が上がるだけでなく、入力フォームとしてまとめて質問する事もできるため、是非色々試してください。次回はボットの多言語サポートを見ていきます。

次の記事へ
目次へ戻る

この記事のサンプルコード

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2