Teams SDK でのダイアログの呼び出しと解除

この記事では、Teams SDK (Teams AI ライブラリ) を使用してダイアログ (旧称タスク モジュール) を呼び出したり閉じたりする方法について説明します。 Teams SDK では、ダイアログは TaskFetchAction を使用してアダプティブ カード アクションから呼び出され、 App クラスのダイアログを開くイベントと送信イベントを通じて処理されます。

イベントの登録は言語によって異なります。

  • TypeScript: app.on('dialog.open', ...)およびapp.on('dialog.submit', ...)
  • C#: teams.OnTaskFetch(...) と teams.OnTaskSubmit(...)
  • Python: @app.on_dialog_open と @app.on_dialog_submit

ダイアログ コンテンツは、アダプティブ カードまたは URL ベースの Web ページにすることができます。

ダイアログは、アプリのアーキテクチャに応じて、他の方法で呼び出すこともできます。

Bot Framework から Teams SDK への移行ガイダンスについては、「 BotBuilder からの移行」を参照してください。

次の表は、Teams SDK でのダイアログの動作をまとめたものです。

手順 アダプティブ カードを使用したダイアログ Web ページ URL を含むダイアログ
ダイアログをトリガーする 1. TaskFetchAction ボタン付きのアダプティブ カードをユーザーに送信します。 アクションの value データは、開くダイアログの種類を指定します。

2. ユーザーがボタンを選択すると、Teams はタスク取得呼び出しをアプリに送信します。
1. TaskFetchAction ボタン付きのアダプティブ カードをユーザーに送信します。

2. ユーザーがボタンを選択すると、Teams はタスク取得呼び出しをアプリに送信します。
ダイアログ オープン イベントを処理する 3. ダイアログ オープン ハンドラーで、ダイアログ メタデータ (タイトル、寸法、表示するアダプティブ カード) を含むタスク モジュールの続行応答を返します。 C# では、ContinueTask ラッパーと共に TaskInfo を使用します。 TypeScript では、 CardTaskModuleTaskInfo を使用します。 Python では、TaskModuleContinueResponse内で CardTaskModuleTaskInfo を使用します。 3. ダイアログ オープン ハンドラーで、Web ページを指す url プロパティを持つダイアログ メタデータを含むタスク モジュールの続行応答を返します。 URL ドメインは、アプリ マニフェストの validDomains 配列に含まれている必要があります。 C# では、 TaskInfo を使用します。 TypeScript では、 UrlTaskModuleTaskInfo を使用します。 Python では、TaskModuleContinueResponse内で UrlTaskModuleTaskInfo を使用します。
ダイアログ送信を処理する 4. ユーザーが Action.Submit ボタンを押すと、Teams はフォーム データを含むタスク送信呼び出しをアプリに送信します。

5. 次の方法で応答できます。
• 何もしない (タスク完了)
• メッセージの表示 (C#: MessageTask、TypeScript/Python: TaskModuleMessageResponse)
• 別のダイアログへのチェーン (C#: ContinueTask、TypeScript/Python: TaskModuleContinueResponse)
4. Web ページは、Teams JS クライアント ライブラリを呼び出してデータを再送信します。 Teams は、タスクを送信し、呼び出しを、結果とともにアプリに送信します。

次のセクションでは、ダイアログの内容と外観を定義するダイアログ メタデータについて説明します。

ダイアログ メタデータ

ダイアログ メタデータは、ダイアログの内容と外観を定義します。 各言語は、このメタデータを表すために独自の型を使用します。

  • C#: TaskInfo ( Microsoft.Teams.Api.TaskModules から)
  • TypeScript: CardTaskModuleTaskInfo または UrlTaskModuleTaskInfo ( @microsoft/teams.api から)
  • Python: CardTaskModuleTaskInfo または UrlTaskModuleTaskInfo ( microsoft_teams.api から)

次の表に、すべての言語に共通するプロパティを示します。

属性 種類 説明
title string この属性は、アプリ名の下とアプリ アイコンの右側に表示されます。
height number または string この属性には、ダイアログの高さ (ピクセル単位) を表す数値、 small、 medium、 large を指定することができます。 C# では、 Union<int, Size> を使用します。
width number または string この属性には、ダイアログの幅 (ピクセル単位) を表す数値、 small、 medium、 large を指定することができます。 C# では、 Union<int, Size> を使用します。
url string ダイアログ内に <iframe> として読み込まれたページの URL。 URL のドメインは、アプリ マニフェスト内のアプリの validDomains 配列 に含まれている必要があります。 TypeScript/Python で UrlTaskModuleTaskInfo を使用するか、C# で TaskInfo に Url プロパティを設定します。
card 添付ファイル ダイアログに表示するアダプティブ カード。 C# で、Attachment を使用して TaskInfo に Card プロパティを設定します。 TypeScript では、CardTaskModuleTaskInfo で cardAttachment() を使用します。 Python では、CardTaskModuleTaskInfo で card_attachment(AdaptiveCardAttachment(...)) を使用します。

注:

ダイアログ機能を使用するには、読み込む URL のドメインがアプリのマニフェストの validDomains 配列に含まれている必要があります。

次のセクションでは、ユーザーがダイアログの高さと幅を設定できるダイアログのサイズ設定を指定します。

ダイアログのサイズ変更

width と height の値は、ダイアログの高さと幅をピクセル単位で設定します。 Teams ウィンドウのサイズと画面の解像度に応じて、これらの値は縦横比を維持しながら比例して小さくなる場合があります。

width と height が small、medium、または large の場合、次の図の赤い四角形のサイズは、使用可能な領域の割合、width の場合は 20%、50%、60% で、height の場合は 20%、50%、66% です:

ダイアログのサイズ変更の例

次のセクションでは、Teams SDK を使用してダイアログをトリガーおよび処理する例を示します。

TaskFetchAction でダイアログをトリガーする

ダイアログを開くには、 TaskFetchAction ボタンを使用してアダプティブ カードを送信します。 ユーザーがボタンを選択すると、Teams はタスク取得呼び出しをアプリに送信します。 各ボタンの value データによって、開くダイアログの種類 ( { "data": "AdaptiveCard" }など) が指定されます。

using System.Text.Json;
using Microsoft.Teams.Apps;
using Microsoft.Teams.Apps.Schema;
using Microsoft.Teams.Cards;

teams.OnMessage(async (context, cancellationToken) =>
{
    var card = new AdaptiveCard
    {
        Body = new List<CardElement>
        {
            new TextBlock("Task Module Invocation from Adaptive Card")
            {
                Weight = TextWeight.Bolder,
                Size = TextSize.Large
            }
        },
        Actions = new List<Action>
        {
            new TaskFetchAction(new Dictionary<string, object?> { { "data", "AdaptiveCard" } })
            { Title = "Adaptive Card" },
            new TaskFetchAction(new Dictionary<string, object?> { { "data", "CustomForm" } })
            { Title = "Custom Form" },
            new TaskFetchAction(new Dictionary<string, object?> { { "data", "MultiStep" } })
            { Title = "Multi-step Form" }
        }
    };

    TeamsAttachment attachment = TeamsAttachment.CreateBuilder()
        .WithAdaptiveCard(JsonSerializer.SerializeToElement(card))
        .Build();

    await context.SendAsync(new MessageActivityInput().AddAttachment(attachment), cancellationToken);
});

ダイアログ オープン イベントを処理する

Teams がタスクのフェッチ呼び出しを送信すると、アプリはダイアログ コンテンツを返します。 コンテンツには、アダプティブ カードまたは Web ページの URL を指定できます。 C# では、ダイアログ メタデータを ContinueTask 応答でラップします。 TypeScript で、type: 'continue' を使用してTaskModuleResponseを返します。 Python では、TaskModuleContinueResponseを含むInvokeResponseを返します。

using System.Text.Json;
using Microsoft.Teams.Apps;
using Microsoft.Teams.Apps.Schema;
using Microsoft.Teams.Apps.TaskModules;
using Microsoft.Teams.Cards;

teams.OnTaskFetch(async (context, cancellationToken) =>
{
    await Task.CompletedTask;

    // The button's Action.Submit payload arrives on the invoke value, nested under "data".
    string? data = context.Activity.Value?.Data is JsonElement value
        && value.TryGetProperty("data", out JsonElement dataProp)
            ? dataProp.GetString()
            : null;

    if (data == "CustomForm")
    {
        return TaskModuleResponse.CreateBuilder()
            .WithType(TaskModuleResponseTypes.Continue)
            .WithTitle("Custom Form")
            .WithWidth(510)
            .WithHeight(450)
            .WithUrl($"{botEndpoint}/customform")
            .WithFallbackUrl($"{botEndpoint}/customform")
            .Build();
    }

    if (data == "MultiStep")
    {
        var step1Card = new AdaptiveCard
        {
            Version = "1.5",
            Body = new List<CardElement>
            {
                new TextBlock("Multi-step Form: Step 1") { Wrap = true },
                new TextInput { Id = "name", Label = "Name" }
            },
            Actions = new List<Microsoft.Teams.Cards.Action>
            {
                new SubmitAction { Title = "Next" }
            }
        };

        TeamsAttachment step1Attachment = TeamsAttachment.CreateBuilder()
            .WithAdaptiveCard(JsonSerializer.SerializeToElement(step1Card))
            .Build();

        return TaskModuleResponse.CreateBuilder()
            .WithType(TaskModuleResponseTypes.Continue)
            .WithTitle("Multi-step Form")
            .WithWidth(400)
            .WithHeight(300)
            .WithCard(step1Attachment)
            .Build();
    }

    var dialogCard = new AdaptiveCard
    {
        Version = "1.5",
        Body = new List<CardElement>
        {
            new TextInput { Id = "usertext", Label = "Enter text" }
        },
        Actions = new List<Microsoft.Teams.Cards.Action>
        {
            new SubmitAction { Title = "Submit" }
        }
    };

    TeamsAttachment attachment = TeamsAttachment.CreateBuilder()
        .WithAdaptiveCard(JsonSerializer.SerializeToElement(dialogCard))
        .Build();

    return TaskModuleResponse.CreateBuilder()
        .WithType(TaskModuleResponseTypes.Continue)
        .WithTitle("Adaptive Card: Inputs")
        .WithWidth(400)
        .WithHeight(200)
        .WithCard(attachment)
        .Build();
});

ダイアログ送信を処理する

ユーザーがダイアログで Action.Submit を押すと、Teams はタスク送信呼び出しをアプリに送信します。 タスクを完了するか、メッセージを表示するか、別のダイアログを開く (たとえば、マルチステップ フォームをチェーンする場合) ことで応答できます。

using System.Text.Json;
using Microsoft.Teams.Apps.TaskModules;
using Microsoft.Teams.Cards;

teams.OnTaskSubmit(async (context, cancellationToken) =>
{
    var activity = context.Activity;
    var json = JsonSerializer.Deserialize<JsonElement>(JsonSerializer.Serialize(activity));
    var submitData = JsonSerializer.Deserialize<Dictionary<string, object>>(
        json.GetProperty("value").GetProperty("data").GetRawText());
    var submissionType = submitData?.GetValueOrDefault("submissiontype")?.ToString();

    if (submissionType == "multi_step_1")
    {
        var name = submitData["name"]?.ToString();
        var step2Card = new AdaptiveCard
        {
            Body = new List<CardElement>
            {
                new TextBlock("Step 2 of 2 - Your Email") { Size = TextSize.Large, Weight = TextWeight.Bolder },
                new TextInput { Id = "email", Label = "Email", Placeholder = "Enter your email", IsRequired = true }
            },
            Actions = new List<Action>
            {
                new SubmitAction().WithTitle("Submit").WithData(
                    new Union<string, SubmitActionData>(new SubmitActionData
                    {
                        NonSchemaProperties = new Dictionary<string, object?>
                        {
                            { "submissiontype", "multi_step_2" },
                            { "name", name! }
                        }
                    }))
            }
        };

        TeamsAttachment step2Attachment = TeamsAttachment.CreateBuilder()
            .WithAdaptiveCard(JsonSerializer.SerializeToElement(step2Card))
            .Build();

        return TaskModuleResponse.CreateBuilder()
            .WithType(TaskModuleResponseTypes.Continue)
            .WithTitle("Multi-step Form: Step 2")
            .WithWidth(400)
            .WithHeight(300)
            .WithCard(step2Attachment)
            .Build();
    }

    if (submissionType == "multi_step_2")
    {
        await context.SendAsync($"Hi {submitData["name"]}, thanks for submitting! Your email is {submitData["email"]}", cancellationToken);
        return TaskModuleResponse.CreateBuilder()
            .WithType(TaskModuleResponseTypes.Message)
            .WithMessage("Multi-step form completed!")
            .Build();
    }

    var usertext = submitData?.GetValueOrDefault("usertext")?.ToString();
    await context.SendAsync($"You submitted: {usertext}", cancellationToken);
    return TaskModuleResponse.CreateBuilder()
        .WithType(TaskModuleResponseTypes.Message)
        .WithMessage("Thanks for submitting!")
        .Build();
});

キーボードとアクセシビリティのガイドライン

HTML コンテンツを読み込む URL ベースのダイアログの場合は、キーボードのアクセシビリティを確保します。

  • HTML タグの tabindex 属性 を使用して、フォーカスできる要素を制御し、 Tab キーと Shift-Tab キーで連続するキーボード ナビゲーションを定義します。
  • ダイアログ ページの JavaScript で Esc キーを適切に処理します。

Microsoft Teams は、キーボード ナビゲーションがダイアログ ヘッダーから HTML に、またはその逆に適切に機能することを保証します。

コード サンプル

サンプルの名前 説明 .NET Node.js Python
ボット タスク モジュール このサンプル アプリでは、Teams AI SDK を使用してダイアログ (TeamsJS v1.x ではタスク モジュールと呼ばれます) を使用する方法を示します。 表示 表示 表示

次の手順

関連項目