แก้ไข

Use dialogs with bots

Invoke dialogs (referred as task modules in TeamsJS v1.x) from Microsoft Teams bots using TaskFetchAction buttons on Adaptive Cards. Dialogs provide a focused interaction by opening a pop-up window for the user, making them ideal for complex forms or multi-step workflows.

There are two ways of invoking dialogs:

  • A new invoke message task/fetch: Using the Action.Execute card action for Adaptive Cards with task/fetch, either an HTML or Adaptive Card-based dialog is fetched dynamically from your bot.
  • Deep link URLs: Using the deep link syntax for dialogs, you can use the Action.OpenUrl card action for Adaptive Cards. With deep link URLs, the dialog URL or Adaptive Card body is already known to avoid a server round-trip relative to task/fetch.

Important

Each url and fallbackUrl must implement the HTTPS encryption protocol.

Note

In Teams client v1, dialogs were called task modules. They may occasionally be used synonymously.

Create a dialog launcher

To invoke a dialog from a bot, send an Adaptive Card with TaskFetchAction buttons. Each button includes data that your bot uses to determine which dialog content to return.

Warning

Microsoft's cloud services, including web versions of Teams, Outlook, and Microsoft 365 domains, are migrating to the *.cloud.microsoft domain. Perform the following steps as soon as possible to ensure your app continues to render on supported Microsoft 365 web client hosts:

  1. Update TeamsJS library to v.2.19.0 or later. You must call microsoftTeams.app.initialize() to avoid seeing a warning in the new domain. For more information about the latest release of TeamsJS, see Microsoft Teams JavaScript client library.

  2. If you've defined Content Security Policy (CSP) headers for your app, update the frame-ancestors directive to include the *.cloud.microsoft domain. To ensure backward compatibility during the migration, retain the existing frame-ancestors values in your CSP headers. This approach ensures that your app continues to work across both existing and future Microsoft 365 host applications and minimizes the need for subsequent changes.

Update the following domain in the frame-ancestors directive of your app's CSP headers:

https://*.cloud.microsoft

task/fetch request or response

The following steps provide instructions on how to invoke a dialog (referred as task module in TeamsJS v1.x) using task/fetch:

  1. This image shows an Adaptive Card with a Buy Action.Execute card action. The value of the type property is task/fetch and the rest of the data object can be of your choice.

  2. The bot receives a card.action activity. In the Teams SDK, you handle this using the OnAdaptiveCardAction handler. For more information, see Executing Actions.

  3. The bot creates an ActionResponse object and returns it. For more information on schema for responses, see the discussion on task/submit. The following code provides an example of the response body that contains a TaskInfo object embedded in a wrapper object:

    {
      "task": {
        "type": "continue",
        "value": {
          "title": "Task module title",
          "height": 500,
          "width": "medium",
          "url": "https://contoso.com/msteams/taskmodules/newcustomer",
          "fallbackUrl": "https://contoso.com/msteams/taskmodules/newcustomer"
        }
      }
    }
    

    The task/fetch event and its response for bots is similar to the microsoftTeams.tasks.startTask() function in the Microsoft Teams JavaScript client library (TeamsJS).

  4. Microsoft Teams displays the dialog.

The next section provides details on submitting the result of a dialog.

Submit the result of a dialog

When the user finishes with the dialog, the result is submitted back to your app. How submission works depends on the dialog content type:

  • Adaptive Card (TaskInfo.card): When the user selects an Action.Submit button, Teams sends a dialog submit event to your app. Your dialog submit handler receives the form data from the card. In C#, use the [TaskSubmit] attribute. In TypeScript, use app.on('dialog.submit', ...). In Python, use @app.on_dialog_submit.
  • Webpage (TaskInfo.url): The webpage calls microsoftTeams.tasks.submitTask(formData) from the TeamsJS client library, which triggers the same dialog submit event in your app.

Handle dialog submit events

When the user submits a dialog, the bot receives a task/submit invoke message. You have several options when responding:

Response type Scenario
No response The simplest response is no response at all. Your bot isn't required to respond when the user finishes with the dialog.
MessageTask Teams displays a message in a pop-up message box in the dialog.
ContinueTask Allows you to chain sequences of Adaptive Cards together in a wizard or multi-step experience.

The following tabs show how to handle dialog submit events in .NET, TypeScript, and Python:

using System.Text.Json;
using Microsoft.Teams.Api.TaskModules;
using Microsoft.Teams.Apps;
using Microsoft.Teams.Apps.Activities.Invokes;
using Microsoft.Teams.Apps.Annotations;
using Microsoft.Teams.Common.Logging;

[TaskSubmit]
public async Task<Microsoft.Teams.Api.TaskModules.Response> OnTaskSubmit([Context] Tasks.SubmitActivity activity, [Context] IContext.Client client, [Context] ILogger log)
{
    var data = activity.Value?.Data as JsonElement?;
    if (data == null)
    {
        log.Info("[TASK_SUBMIT] No data found in the activity value");
        return new Microsoft.Teams.Api.TaskModules.Response(
            new Microsoft.Teams.Api.TaskModules.MessageTask("No data found in the activity value"));
    }

    var submissionType = data.Value.TryGetProperty("submissiondialogtype", out var submissionTypeObj) && submissionTypeObj.ValueKind == JsonValueKind.String
        ? submissionTypeObj.ToString()
        : null;

    string? GetFormValue(string key)
    {
        if (data.Value.TryGetProperty(key, out var val))
        {
            if (val is JsonElement element)
                return element.GetString();
            return val.ToString();
        }
        return null;
    }

    switch (submissionType)
    {
        case "simple_form":
            var name = GetFormValue("name") ?? "Unknown";
            await client.Send($"Hi {name}, thanks for submitting the form!");
            return new Microsoft.Teams.Api.TaskModules.Response(
                new Microsoft.Teams.Api.TaskModules.MessageTask("Form was submitted"));
        default:
            return new Microsoft.Teams.Api.TaskModules.Response(
                new Microsoft.Teams.Api.TaskModules.MessageTask("Unknown submission type"));
    }
}

Multi-step dialog chaining

You can chain Adaptive Cards into a multi-step wizard by returning a ContinueTask response from the submit handler. Each step returns a new card, and the final step returns a MessageTask to close the dialog.

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

// Add these cases to your OnTaskSubmit method
case "webpage_dialog_step_1":
    var nameStep1 = GetFormValue("name") ?? "Unknown";
    var nextStepCardJson = $$"""
    {
        "type": "AdaptiveCard",
        "version": "1.4",
        "body": [
            {
                "type": "TextBlock",
                "text": "Email",
                "size": "Large",
                "weight": "Bolder"
            },
            {
                "type": "Input.Text",
                "id": "email",
                "label": "Email",
                "placeholder": "Enter your email",
                "isRequired": true
            }
        ],
        "actions": [
            {
                "type": "Action.Submit",
                "title": "Submit",
                "data": {"submissiondialogtype": "webpage_dialog_step_2", "name": "{{nameStep1}}"}
            }
        ]
    }
    """;

    var nextStepCard = JsonSerializer.Deserialize<AdaptiveCard>(nextStepCardJson)
        ?? throw new InvalidOperationException("Failed to deserialize next step card");

    var nextStepTaskInfo = new TaskInfo
    {
        Title = $"Thanks {nameStep1} - Get Email",
        Card = new Attachment
        {
            ContentType = new ContentType("application/vnd.microsoft.card.adaptive"),
            Content = nextStepCard
        }
    };

    return new Response(new ContinueTask(nextStepTaskInfo));

case "webpage_dialog_step_2":
    var nameStep2 = GetFormValue("name") ?? "Unknown";
    var emailStep2 = GetFormValue("email") ?? "No email";
    await client.Send($"Hi {nameStep2}, thanks for submitting the form! We got that your email is {emailStep2}");
    return new Response(new MessageTask("Multi-step form completed successfully"));

Bot Framework card actions vs. Adaptive Card Action.Submit actions

The schema for Bot Framework card actions is different from Adaptive Card Action.Submit actions and the way to invoke dialogs is also different. The data object in Action.Submit contains a msteams object so it doesn't interfere with other properties in the card. The following table shows an example of each card action:

Bot Framework card action Adaptive Card Action.Submit action
{
"type": "invoke",
"title": "Buy",
"value": {
"type": "task/fetch",
<...>
}
}
{
"type": "Action.Submit",
"id": "btnBuy",
"title": "Buy",
"data": {
<...>,
"msteams": {
"type": "task/fetch"
}
}
}

Code sample

Sample name Description .NET Node.js Manifest Python
Dialog sample bots-V4 This sample app demonstrate how to use Dialogs (referred as task modules in TeamsJS v1.x) using Bot Framework v4. View View NA View

See also