Вызов и закрытие диалоговых окон в пакете SDK для Teams

В этой статье описывается, как вызывать и закрывать диалоговые окна (ранее известные как модули задач) с помощью пакета SDK (библиотека ИИ Teams). В пакете SDK для Teams диалоговые окна вызываются из действий адаптивной карточки с помощью 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-адреса.

Диалоговые окна также можно вызывать другими способами в зависимости от архитектуры приложения:

Инструкции по миграции из Bot Framework в Teams SDK см. в статье Миграция из BotBuilder.

В следующей таблице обобщено, как работают диалоговые окна в пакете SDK для Teams:

Шаг Диалоговое окно с адаптивной карточкой Диалоговое окно с URL-адресом веб-страницы
Запуск диалогового окна 1. Отправьте пользователю адаптивную карточку с TaskFetchAction кнопкой. Данные действия value определяют тип открываемого диалогового окна.

2. Когда пользователь нажимает кнопку, Teams отправляет вызов задачи в приложение.
1. Отправьте пользователю адаптивную карточку с TaskFetchAction кнопкой.

2. Когда пользователь нажимает кнопку, Teams отправляет вызов задачи в приложение.
Обработка события открытия диалогового окна 3. В обработчике открытия диалогового окна верните ответ модуля задач, содержащий метаданные диалогового окна (название, измерения и адаптивную карточку для отображения). В C# используйте TaskInfo с оболочкой ContinueTask . В TypeScript используйте CardTaskModuleTaskInfo. В Python используйте CardTaskModuleTaskInfo в TaskModuleContinueResponse. 3. В обработчике открытия диалогового окна верните ответ модуля задачи, содержащий метаданные диалогового окна со свойством url , указывающим на веб-страницу. URL-домен должен находиться в массиве validDomains в манифесте приложения. В C# используйте TaskInfo. В TypeScript используйте UrlTaskModuleTaskInfo. В Python используйте UrlTaskModuleTaskInfo в TaskModuleContinueResponse.
Обработка отправки диалогового окна 4. Когда пользователь нажимает Action.Submit кнопку, Teams отправляет вызов отправки задачи в ваше приложение с данными формы.

5. Вы можете ответить:
• Ничего не делать (задача выполнена)
• Отображение сообщения (C#: MessageTask, TypeScript/Python: TaskModuleMessageResponse)
• Цепочка с другим диалоговым окном (C#: ContinueTask, TypeScript/Python: TaskModuleContinueResponse)
4. Веб-страница вызывает клиентскую библиотеку JS Teams для отправки данных обратно. 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 URL-адрес страницы, загруженной <iframe> в диалоговом окне. Домен URL-адреса должен находиться в массиве приложения validDomains в манифесте приложения. Используйте UrlTaskModuleTaskInfo в TypeScript/Python или установите свойство Url в TaskInfo C#.
card Вложение Адаптивная карточка для отображения в диалоговом окне. В C# задайте свойство Card с TaskInfo помощью Attachment. В TypeScript используйте cardAttachment() с CardTaskModuleTaskInfo. В Python используйте card_attachment(AdaptiveCardAttachment(...)) с CardTaskModuleTaskInfo.

Примечание.

Функция диалогового окна требует, чтобы домены всех URL-адресов, которые вы хотите загрузить, были включены в validDomains массив в манифесте вашего приложения.

В следующем разделе указаны размеры диалоговых окон, позволяющие пользователю задать высоту и ширину диалогового окна.

Изменение размеров диалогового окна

Значения диалогового width окна и height установка высоты и ширины в пикселях. В зависимости от размера окна Teams и разрешения экрана эти значения могут быть пропорционально уменьшены при сохранении пропорций.

Если width и height представляют собой small, mediumили large, размер красного прямоугольника на следующем изображении пропорционален размерам доступного пространства, а именно 20 %, 50 % и 60 % для width и 20 %, 50 % и 66 % для height:

Пример выбора размера диалогового окна

В следующем разделе приводятся примеры запуска и обработки диалоговых окон с помощью пакета SDK для Teams.

Запуск диалогового окна с помощью 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 отправляет вызов задачи, ваше приложение возвращает содержимое диалогового окна. Содержимое может быть адаптивной карточкой или URL-адресом веб-страницы. В C# оберните метаданные диалогового окна в ответ.ContinueTask В TypeScript верните a TaskModuleResponse с type: 'continue'помощью . В Python верните содержащий InvokeResponse .TaskModuleContinueResponse

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();
});

Рекомендации по использованию клавиатуры и специальных возможностей

Для диалоговых окон на основе URL-адресов, загружающих HTML-содержимое, обеспечьте специальные возможности клавиатуры:

  • Используйте атрибут tabindex в тегах HTML для управления тем, на каких элементах можно сосредоточить внимание, и для определения последовательной навигации с помощью клавиатуры с помощью клавиш TAB и SHIFT-TAB .
  • Правильно обработайте клавишу ESC в JavaScript для вашей диалоговой страницы.

Microsoft Teams обеспечивает правильную навигацию с помощью клавиатуры из заголовка диалогового окна в HTML и наоборот.

Пример кода

Название примера Описание .NET Node.js Python
Модули задач бота В этом примере приложения демонстрируется, как использовать диалоговые окна (называемые модулями задач в TeamsJS v1.x) с помощью пакета SDK для ИИ в Teams. Просмотр Просмотр Просмотр

Следующий этап

См. также