使用 Bot Framework SDK 发送媒体附件

适用于:SDK v4

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

注意

Bot Framework JavaScript、C# 和 Python SDK 将继续受支持,但 Java SDK 即将停用,最终长期支持将于 2023 年 11 月结束。

使用 Java SDK 构建的现有机器人将继续正常运行。

要生成新的机器人,请考虑使用 Microsoft Copilot Studio 并阅读选择正确的助理解决方案

有关详细信息,请参阅机器人构建的未来

先决条件

发送附件

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

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

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

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

Activity 对象的 Attachments 属性包含一组 Attachment 对象,表示媒体附件和附加到消息的富卡。 若要向消息添加媒体附件,请为 reply 活动创建 Attachment 对象,并设置 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,
    };
}

最后是 Internet 附件:

Bots/AttachmentsBot.cs

    {
        // ContentUrl must be HTTPS.
        return new Attachment
        {
            Name = @"Resources\architecture-resize.png",
            ContentType = "image/png",
            ContentUrl = "https://docs.microsoft.com/en-us/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);

处理资讯卡中的事件

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

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

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

类型 说明
call 拨打电话。 电话呼叫的目标采用 tel:123123123123 格式。
downloadFile 下载一个文件。 要下载的文件的 URL。
imBack 向机器人发送一条消息,并在聊天中发布一个可见的响应。 要发送的消息文本。
返回消息 表示将通过聊天系统发送的文本响应。 要包含在生成的消息中的可选编程值。
openUrl 在内置浏览器中打开一个 URL。 要打开的 URL。
playAudio 播放音频。 要播放的音频的 URL。
playVideo 播放视频。 要播放的视频的 URL。
postBack 向机器人发送一条消息,可能不在聊天中发布一个可见的响应。 要发送的消息文本。
showImage 显示图像。 要显示的图像的 URL。
signin 启动 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.microsoftonline.com/") },
    };

    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 方法放置于右大括号后的“使用卡”示例中,用于公开声明 MainDialog:

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

to:

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

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

测试“使用卡片”机器人

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

后续步骤