使用 Bot Framework SDK 发送媒体附件

适用于:SDK v4

用户与机器人之间的消息交换可以包含媒体附件,例如图像、视频、音频和文件。 Bot Framework SDK 支持向用户发送富消息的任务。 要确定通道(Teams 等)支持的丰富消息类型,请参阅通道的文档以获取有关限制的信息。

重要

Bot Framework SDK 和 Bot Framework Emulator 已在 GitHub 上存档。 项目不再更新或维护。 自 2025 年 12 月 31 日起,Bot Framework SDK 的支持票证将不再提供服务。

若要使用所选的 AI 服务、业务流程和知识生成代理,请考虑使用 Microsoft 365 代理 SDK。 代理 SDK 对 C#、JavaScript 或 Python 具有语言支持。 可以在 aka.ms/agents 了解有关代理 SDK 的详细信息。 如果现有的机器人是使用 Bot Framework SDK 生成的,则可以将机器人更新到代理 SDK。 查看 Bot Framework SDK 到代理 SDK 迁移指南的核心更改和更新。

如果要构建设计为在 Microsoft Teams 中工作的协作代理,请考虑使用 Teams SDK。 它为在 Teams 环境中运行的代理提供 Teams 特定的 API、自适应卡支持和内置 AI 协同调度功能。 可以在 Teams SDK(Teams AI 库)中了解详细信息。

如果要查找基于 SaaS 的代理平台,请考虑 Microsoft Copilot Studio

先决条件

发送附件

若要向用户发送内容(如图像或视频),可以向消息添加附件或附件列表。

有关可用卡的示例,请参阅设计用户体验

另请参阅常见问题解答中的使用通道传输的文件的大小限制是多少?

本部分所示的所有源代码均基于处理附件示例。

Activity 对象的 Attachments 属性包含一个由 Attachment 对象组成的数组,这些对象表示附加到该消息的媒体附件和富卡。 若要向消息添加媒体附件,请为 Attachment 活动创建 reply 对象,并设置 ContentTypeContentUrlName 属性。

若要创建回复消息,请定义文本,然后设置附件。 将附件分配到回复的操作对于每个附加类型来说是相同的,但不同附件的设置和定义方法是不同的,如以下代码段所示。 以下代码为内联附件设置回复:

Bots/AttachmentsBot.cs

{
    reply = MessageFactory.Text("This is an inline attachment.");

接下来,我们查看附件的类型。 首先是内嵌附件:

Bots/AttachmentsBot.cs

{
    var imagePath = Path.Combine(Environment.CurrentDirectory, @"Resources", "architecture-resize.png");
    var imageData = Convert.ToBase64String(File.ReadAllBytes(imagePath));

    return new Attachment
    {
        Name = @"Resources\architecture-resize.png",
        ContentType = "image/png",
        ContentUrl = $"data:image/png;base64,{imageData}",
    };
}

然后是上传的附件:

Bots/AttachmentsBot.cs

{
    if (string.IsNullOrWhiteSpace(serviceUrl))
    {
        throw new ArgumentNullException(nameof(serviceUrl));
    }

    if (string.IsNullOrWhiteSpace(conversationId))
    {
        throw new ArgumentNullException(nameof(conversationId));
    }

    var imagePath = Path.Combine(Environment.CurrentDirectory, @"Resources", "architecture-resize.png");

    var connector = turnContext.TurnState.Get<IConnectorClient>() as ConnectorClient;
    var attachments = new Attachments(connector);
    var response = await attachments.Client.Conversations.UploadAttachmentAsync(
        conversationId,
        new AttachmentData
        {
            Name = @"Resources\architecture-resize.png",
            OriginalBase64 = File.ReadAllBytes(imagePath),
            Type = "image/png",
        },
        cancellationToken);

    var attachmentUri = attachments.GetAttachmentUri(response.Id);

    return new Attachment
    {
        Name = @"Resources\architecture-resize.png",
        ContentType = "image/png",
        ContentUrl = attachmentUri,
    };
}

最后,还有一个互联网附加内容:

Bots/AttachmentsBot.cs

    {
        // ContentUrl must be HTTPS.
        return new Attachment
        {
            Name = @"Resources\architecture-resize.png",
            ContentType = "image/png",
            ContentUrl = "https://docs.microsoft.com/bot-framework/media/how-it-works/architecture-resize.png",
        };
    }
}

如果附件是图像、音频或视频,连接器服务将以一种让通道可以在会话中呈现附件的方式将此附件数据传递给通道。 如果附件是文件,该文件 URL 将呈现为该会话中的超链接。

发送英雄卡

除了简单的图像或视频附件,还可以附加英雄卡 ,这样可以将图像和按钮组合在一个对象中,并将其发送给用户。 大多数文本字段支持 Markdown,但支持可能因通道而异。

若要撰写含英雄卡和按钮的消息,可以将 HeroCard 对象附加到消息中。

以下源代码来自处理附件示例。

Bots/AttachmentsBot.cs


private static async Task DisplayOptionsAsync(ITurnContext turnContext, CancellationToken cancellationToken)
{
    // Create a HeroCard with options for the user to interact with the bot.
    var card = new HeroCard
    {
        Text = "You can upload an image or select one of the following choices",
        Buttons = new List<CardAction>
        {
            // Note that some channels require different values to be used in order to get buttons to display text.
            // In this code the emulator is accounted for with the 'title' parameter, but in other channels you may
            // need to provide a value for other parameters like 'text' or 'displayText'.
            new CardAction(ActionTypes.ImBack, title: "1. Inline Attachment", value: "1"),
            new CardAction(ActionTypes.ImBack, title: "2. Internet Attachment", value: "2"),
            new CardAction(ActionTypes.ImBack, title: "3. Uploaded Attachment", value: "3"),
        },
    };

    var reply = MessageFactory.Attachment(card.ToAttachment());
    await turnContext.SendActivityAsync(reply, cancellationToken);

处理富卡片中的事件

若要处理丰富格式卡中的事件,请使用卡操作对象指定当用户选择按钮或单击卡的某个部分时应发生的情况。 每个卡片操作都具有 typevalue 属性。

若要正常运行,请为英雄卡上的每个可单击项指定一种操作类型。 下表列出并描述了可用的操作类型以及应该存在于相关联的值属性中的内容。

messageBack 卡片操作的含义比其他卡片操作更广泛。 有关 和其他卡操作类型的详细信息,请参阅活动架构messageBack部分。

类型 说明
调用 拨打电话。 电话呼叫目标使用以下格式:tel:123123123123
下载文件 下载一个文件。 要下载的文件的 URL。
imBack 向机器人发送一条消息,并在聊天中发布一个可见的响应。 要发送的消息文本。
返回消息 表示要通过聊天系统发送的文本响应。 包含在生成消息中的可选程序值。
openUrl 在内置浏览器中打开一个 URL。 要打开的 URL。
playAudio 播放音频。 要播放的音频的 URL。
播放视频 播放视频。 要播放的视频的 URL。
postBack 向机器人发送一条消息,可能不在聊天中发布一个可见的响应。 要发送的消息文本。
showImage 显示图像。 要显示的图像的 URL。
登录 启动 OAuth 登录过程。 要启动的 OAuth 流的 URL。

使用各种事件类型的英雄卡

以下代码显示了使用各种富卡事件的示例。

有关所有可用卡的示例,请参阅使用卡示例。

Cards.cs

public static HeroCard GetHeroCard()
{
    var heroCard = new HeroCard
    {
        Title = "BotFramework Hero Card",
        Subtitle = "Microsoft Bot Framework",
        Text = "Build and connect intelligent bots to interact with your users naturally wherever they are," +
               " from text/sms to Skype, Slack, Office 365 mail and other popular services.",
        Images = new List<CardImage> { new CardImage("https://sec.ch9.ms/ch9/7ff5/e07cfef0-aa3b-40bb-9baa-7c9ef8ff7ff5/buildreactionbotframework_960.jpg") },
        Buttons = new List<CardAction> { new CardAction(ActionTypes.OpenUrl, "Get Started", value: "https://docs.microsoft.com/bot-framework") },
    };

    return heroCard;
}

Cards.cs

public static SigninCard GetSigninCard()
{
    var signinCard = new SigninCard
    {
        Text = "BotFramework Sign-in Card",
        Buttons = new List<CardAction> { new CardAction(ActionTypes.Signin, "Sign-in", value: "https://login.partner.microsoftonline.cn/") },
    };

    return signinCard;
}

发送自适应卡片

虽然可以使用消息工厂创建包含附件(任何类型)的消息,但自适应卡片是一种特定类型的附件。 不是所有通道都支持自适应卡片,有些通道可能只是部分支持自适应卡片。 例如,如果在 Facebook 中发送自适应卡片,则按钮可能不起作用,而文本和图像可正常使用。 消息工厂是一个 Bot Framework SDK 帮助器类,用于自动执行创建步骤。

自适应卡片是一种开放的卡片交换格式,可以让开发人员通过常见且一致的方式交换 UI 内容。 但是,并非所有通道都支持自适应卡片。

自适应卡片设计器为创作自适应卡片提供丰富的交互式设计时体验。

注意

应该使用机器人将使用的通道测试此功能,以确定这些通道是否支持自适应卡。

若要使用自适应卡片,请务必添加 AdaptiveCards NuGet 包。

以下源代码来自使用卡片示例。

Cards.cs

此示例从某个文件读取自适应卡片 JSON 并将其添加为附件。

public static Attachment CreateAdaptiveCardAttachment()
{
    // combine path for cross platform support
    var paths = new[] { ".", "Resources", "adaptiveCard.json" };
    var adaptiveCardJson = File.ReadAllText(Path.Combine(paths));

    var adaptiveCardAttachment = new Attachment()
    {
        ContentType = "application/vnd.microsoft.card.adaptive",
        Content = JsonConvert.DeserializeObject(adaptiveCardJson),
    };

    return adaptiveCardAttachment;
}

消息还可以包含多个以轮播布局展示的附件,这种布局会将附件并排显示,并允许用户横向滚动浏览。

以下源代码来自使用卡片示例。

Dialogs/MainDialog.cs

首先,创建回复并将附件定义为列表。

// Cards are sent as Attachments in the Bot Framework.
// So we need to create a list of attachments for the reply activity.
var attachments = new List<Attachment>();

// Reply to the activity we received with an activity.
var reply = MessageFactory.Attachment(attachments);

然后添加附件并将布局类型设置为轮播。 这里我们一次添加一张卡片,不过你也可以按自己喜欢的方式调整这个列表来添加卡片。

// Display a carousel of all the rich card types.
reply.AttachmentLayout = AttachmentLayoutTypes.Carousel;
reply.Attachments.Add(Cards.CreateAdaptiveCardAttachment());
reply.Attachments.Add(Cards.GetAnimationCard().ToAttachment());
reply.Attachments.Add(Cards.GetAudioCard().ToAttachment());
reply.Attachments.Add(Cards.GetHeroCard().ToAttachment());
reply.Attachments.Add(Cards.GetOAuthCard().ToAttachment());
reply.Attachments.Add(Cards.GetReceiptCard().ToAttachment());
reply.Attachments.Add(Cards.GetSigninCard().ToAttachment());
reply.Attachments.Add(Cards.GetThumbnailCard().ToAttachment());
reply.Attachments.Add(Cards.GetVideoCard().ToAttachment());

添加附件以后,即可发送回复,就像发送任何其他内容一样。

// Send the card(s) to the user as an attachment to the activity
await stepContext.Context.SendActivityAsync(reply, cancellationToken);

用于处理 Adaptive Card 输入的代码示例

以下示例显示了在机器人对话类中使用 Adaptive Card 输入的一种方法。 它通过验证来自响应客户端的文本字段中收到的输入来扩展英雄卡示例。 首先需要在 resources 文件夹中的 adaptiveCard.json 的最后一个括号之前添加以下代码,将文本输入和按钮功能添加到现有的自适应卡片:

"actions": [
  {
    "type": "Action.ShowCard",
    "title": "Text",
    "card": {
      "type": "AdaptiveCard",
      "body": [
        {
          "type": "Input.Text",
          "id": "text",
          "isMultiline": true,
          "placeholder": "Enter your comment"
        }
      ],
      "actions": [
        {
          "type": "Action.Submit",
          "title": "OK"
        }
      ]
    }
  }
]

文本输入字段的 ID 设置为“text”。 当用户选择“确定”时,自适应卡片生成的消息将具有 value 属性,该属性具有名为 text 的属性,其中包含用户在文本输入字段中输入的信息。

我们的验证器使用 Newtonsoft.json 先将其转换为 JObject,然后再生成一个去除首尾空白后的文本字符串用于比较。 因此,请添加:

using System;
using System.Linq;
using Newtonsoft.Json.Linq;

转到 MainDialog.cs,并安装 Newtonsoft.Json 的最新稳定版 NuGet 包。 在验证程序代码中,我们将逻辑流添加到代码注释中。 此 ChoiceValidator 方法放在 Using cards 示例中,紧接在 MainDialog 的 public 声明结束的大括号之后:

private async Task ChoiceValidator(
    PromptValidatorContext promptContext,
    CancellationToken cancellationToken)
{
    // Retrieves Adaptive Card comment text as JObject.
    // looks for JObject field "text" and converts that input into a trimmed text string.
    var jobject = promptContext.Context.Activity.Value as JObject;
    var jtoken = jobject?["text"];
    var text = jtoken?.Value().Trim();

    // Logic: 1. if succeeded = true, just return promptContext
    //        2. if false, see if JObject contained Adaptive Card input.
    //               No = (bad input) return promptContext
    //               Yes = update Value field with JObject text string, return "true".
    if (!promptContext.Recognized.Succeeded && text != null)
    {
        var choice = promptContext.Options.Choices.FirstOrDefault(
        c => c.Value.Equals(text, StringComparison.InvariantCultureIgnoreCase));
        if (choice != null)
        {
            promptContext.Recognized.Value = new FoundChoice
            {
                Value = choice.Value,
            };
            return true;
        }
    }
    return promptContext.Recognized.Succeeded;
}

现在,修改上面的 MainDialog 声明:

// Define the main dialog and its related components.
AddDialog(new ChoicePrompt(nameof(ChoicePrompt)));

收件人:

// Define the main dialog and its related components.
AddDialog(new ChoicePrompt(nameof(ChoicePrompt), ChoiceValidator));

每当创建新的选项提示时,这会调用验证器来查找自适应卡片输入。

测试“Using Cards”机器人

  1. 在本地运行“使用卡片”示例,并在 Bot Framework Emulator 中打开机器人。
  2. 依照机器人中的提示显示卡类型,例如自适应卡片。

后续步骤