在 Teams SDK 中调用和关闭对话

本文介绍如何使用 Teams SDK (Teams AI 库) 调用和关闭 (以前称为任务) 模块的对话。 在 Teams SDK 中,使用 TaskFetchAction 从自适应卡片操作调用对话框,并通过对话打开和提交类上的 App 事件进行处理。

事件注册因语言而异:

  • TypeScriptapp.on('dialog.open', ...)app.on('dialog.submit', ...)
  • C#teamsApp.OnTaskFetch(...)teamsApp.OnTaskSubmit(...)
  • Python@app.on_dialog_open@app.on_dialog_submit

对话内容可以是自适应卡片或基于 URL 的网页。

还可以通过其他方法调用对话框,具体取决于应用体系结构:

有关从 Bot Framework 迁移到 Teams SDK 的指南,请参阅 从 BotBuilder 迁移

下表总结了对话在 Teams SDK 中的工作方式:

步骤 具有自适应卡片的对话 包含网页 URL 的对话框
触发对话框 1. 向用户发送带有按钮的 TaskFetchAction 自适应卡片。 操作 value 的数据指定要打开的对话的类型。

2.当用户选择按钮时,Teams 会向应用发送任务提取调用。
1. 向用户发送带有按钮的 TaskFetchAction 自适应卡片。

2.当用户选择按钮时,Teams 会向应用发送任务提取调用。
处理对话框打开事件 3. 在对话打开处理程序中,返回一个任务模块继续响应,其中包含对话框元数据 (标题、维度和自适应卡片以显示) 。 在 C# 中,将 与 TaskInfo 包装一起使用 ContinueTask 。 在 TypeScript 中,使用 CardTaskModuleTaskInfo。 在 Python 中,在 中使用 CardTaskModuleTaskInfoTaskModuleContinueResponse 3. 在对话打开处理程序中,返回一个任务模块继续响应,其中包含具有指向网页的属性的对话元数据 url 。 URL 域必须位于 validDomains 应用清单的 数组中。 在 C# 中,使用 TaskInfo。 在 TypeScript 中,使用 UrlTaskModuleTaskInfo。 在 Python 中,在 中使用 UrlTaskModuleTaskInfoTaskModuleContinueResponse
处理对话框提交 4. 当用户按下 Action.Submit 某个按钮时,Teams 会向应用发送包含表单数据的任务提交调用。

5. 你可以通过以下方式做出响应:
• 不执行任何操作 (完成任务)
• 显示 (C#: 的消息: MessageTask,TypeScript/Python: TaskModuleMessageResponse)
• 链接到另一个对话框 (C#: ContinueTask、TypeScript/Python: TaskModuleContinueResponse)
4. 网页调用 Teams JS 客户端库以提交回数据。 Teams 使用结果向应用发送任务提交调用。

下一部分介绍定义对话框的内容和外观的对话元数据。

对话框元数据

对话元数据定义对话的内容和外观。 每种语言都使用自己的类型来表示此元数据:

  • C#TaskInfoMicrosoft.Teams.Api.TaskModules) (
  • TypeScriptCardTaskModuleTaskInfoUrlTaskModuleTaskInfo@microsoft/teams.api) (
  • PythonCardTaskModuleTaskInfoUrlTaskModuleTaskInfomicrosoft_teams.api) (

下表列出了所有语言的通用属性:

属性 类型 说明
title string 此属性显示在应用名称下方和应用图标右侧。
height number or string 此属性可以是表示对话框高度(以像素为单位)的数字,或者 smallmediumlarge。 在 C# 中,使用 Union<int, Size>
width number or string 此属性可以是表示对话框宽度(以像素为单位)的数字,或者 smallmediumlarge。 在 C# 中,使用 Union<int, Size>
url string 在对话框中作为 <iframe> 加载的页面的 URL。 URL 的域必须位于应用清单中应用的 validDomains 数组 中。 在 UrlTaskModuleTaskInfo TypeScript/Python 中使用,或在 C# 中设置 Url 属性 TaskInfo
card Attachment 要显示在对话框中的自适应卡片。 在 C# 中,使用 Attachment设置 Card 上的 TaskInfo 属性。 在 TypeScript 中,将 与 一起使用cardAttachment()CardTaskModuleTaskInfo。 在 Python 中,将 与 一起使用card_attachment(AdaptiveCardAttachment(...))CardTaskModuleTaskInfo

注意

对话框功能要求要加载的任何 URL 的域包含在应用清单的 validDomains 数组中。

下一部分指定对话框大小调整,使用户能够设置对话框的高度和宽度。

对话框大小调整

height 的值width设置对话框的高度和宽度(以像素为单位)。 根据 Teams 窗口的大小和屏幕分辨率,这些值可能会按比例减少,同时保持纵横比。

如果 widthheightsmallmediumlarge,则下图中红色矩形的大小是可用空间的比例,width 分别为 20%、50% 和 60%,height 分别为 20%、50% 和 66%:

对话框大小调整示例

下一部分提供了使用 Teams SDK 触发和处理对话的示例。

使用 TaskFetchAction 触发对话框

若要打开对话框,请发送带有按钮的 TaskFetchAction 自适应卡片。 当用户选择按钮时,Teams 会向应用发送任务提取调用。 每个按钮 value 的数据指定要打开 (对话框的类型, { "data": "AdaptiveCard" } 例如,) 。

using Microsoft.Teams.Api.Activities;
using Microsoft.Teams.Cards;

teamsApp.OnMessage(async (context) =>
{
    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" }
        }
    };

    await context.Send(new MessageActivity
    {
        Attachments = new List<Attachment>
        {
            new Attachment
            {
                ContentType = new ContentType("application/vnd.microsoft.card.adaptive"),
                Content = card
            }
        }
    });
});

处理对话框打开事件

当 Teams 发送任务提取调用时,你的应用将返回对话内容。 内容可以是自适应卡片或网页 URL。 在 C# 中,将对话框元数据包装在响应中 ContinueTask 。 在 TypeScript 中,返回 带有 TaskModuleResponsetype: 'continue'。 在 Python 中,返回 InvokeResponse 包含 的 TaskModuleContinueResponse

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

teamsApp.OnTaskFetch(async (context) =>
{
    var activity = context.Activity;
    var json = JsonSerializer.Deserialize<JsonElement>(JsonSerializer.Serialize(activity));
    var data = json.GetProperty("value").GetProperty("data").GetProperty("data").GetString();

    TaskInfo taskInfo;

    if (data == "CustomForm")
    {
        taskInfo = new TaskInfo
        {
            Title = "Custom Form",
            Width = new Union<int, Size>(510),
            Height = new Union<int, Size>(450),
            Url = $"{botEndpoint}/customform",
            FallbackUrl = $"{botEndpoint}/customform"
        };
    }
    else if (data == "MultiStep")
    {
        var step1Card = new AdaptiveCard
        {
            Body = new List<CardElement>
            {
                new TextBlock("Step 1 of 2 - Your Name") { Size = TextSize.Large, Weight = TextWeight.Bolder },
                new TextInput { Id = "name", Label = "Name", Placeholder = "Enter your name", IsRequired = true }
            },
            Actions = new List<Action>
            {
                new SubmitAction().WithTitle("Next").WithData(
                    new Union<string, SubmitActionData>(new SubmitActionData
                    {
                        NonSchemaProperties = new Dictionary<string, object?> { { "submissiontype", "multi_step_1" } }
                    }))
            }
        };

        taskInfo = new TaskInfo
        {
            Title = "Multi-step Form",
            Width = new Union<int, Size>(400),
            Height = new Union<int, Size>(300),
            Card = new Attachment
            {
                ContentType = new ContentType("application/vnd.microsoft.card.adaptive"),
                Content = step1Card
            }
        };
    }
    else
    {
        var dialogCard = new AdaptiveCard
        {
            Body = new List<CardElement>
            {
                new TextBlock("Enter Text Here") { Weight = TextWeight.Bolder },
                new TextInput { Id = "usertext", Placeholder = "add some text and submit", IsMultiline = true }
            },
            Actions = new List<Action> { new SubmitAction { Title = "Submit" } }
        };

        taskInfo = new TaskInfo
        {
            Title = "Adaptive Card: Inputs",
            Width = new Union<int, Size>(400),
            Height = new Union<int, Size>(200),
            Card = new Attachment
            {
                ContentType = new ContentType("application/vnd.microsoft.card.adaptive"),
                Content = dialogCard
            }
        };
    }

    return new Response(new ContinueTask(taskInfo));
});

处理对话框提交

当用户在对话框中按下 Action.Submit 时,Teams 会向应用发送任务提交调用。 可以通过完成任务、显示消息或打开另一个对话框(例如,将多步骤表单链接) ) (做出响应。

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

teamsApp.OnTaskSubmit(async (context) =>
{
    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! }
                        }
                    }))
            }
        };

        var taskInfo = new TaskInfo
        {
            Title = "Multi-step Form: Step 2",
            Width = new Union<int, Size>(400),
            Height = new Union<int, Size>(300),
            Card = new Attachment
            {
                ContentType = new ContentType("application/vnd.microsoft.card.adaptive"),
                Content = step2Card
            }
        };

        return new Response(new ContinueTask(taskInfo));
    }

    if (submissionType == "multi_step_2")
    {
        await context.Send($"Hi {submitData["name"]}, thanks for submitting! Your email is {submitData["email"]}");
        return new Response(new MessageTask("Multi-step form completed!"));
    }

    var usertext = submitData?.GetValueOrDefault("usertext")?.ToString();
    await context.Send($"You submitted: {usertext}");
    return new Response(new MessageTask("Thanks for submitting!"));
});

键盘和辅助功能指南

对于加载 HTML 内容的基于 URL 的对话,请确保键盘可访问性:

  • 使用 HTML 标记中的 tabindex 属性 可以控制哪些元素可以聚焦,并使用 TabShift-Tab 键定义顺序键盘导航。
  • 在对话框页的 JavaScript 中适当处理 Esc 键。

Microsoft Teams 可确保键盘导航从对话框标题正确进入 HTML,反之亦然。

代码示例

示例名称 Description .NET Node.js Python
机器人任务模块 此示例应用演示如何使用 Teams AI SDK (TeamsJS v1.x) 中称为任务模块的对话。 View View View

后续步骤

另请参阅