适用于:SDK v4
用户与机器人之间的消息交换可以包含媒体附件,例如图像、视频、音频和文件。 Bot Framework SDK 支持向用户发送富消息的任务。 若要确定某个通道(Facebook、Slack 等)支持的富消息的类型,请查看该通道的文档,了解存在哪些限制。
先决条件
发送附件
若要向用户发送内容(如图像或视频),可以向消息添加附件或附件列表。
有关可用卡的示例,请参阅设计用户体验。
另请参阅常见问题解答中的使用通道传输的文件的大小限制是多少?。
本部分所示的所有源代码均基于处理附件示例。
Activity
对象的 Attachments
属性包含一组 Attachment
对象,表示媒体附件和附加到消息的富卡。 若要向消息添加媒体附件,请为 Attachment
活动创建 reply
对象,并设置 ContentType
、ContentUrl
和 Name
属性。
若要创建回复消息,请定义文本,然后设置附件。 将附件分配到回复的操作对于每个附加类型来说是相同的,但不同附件的设置和定义方法是不同的,如以下代码段所示。 以下代码为内联附件设置回复:
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/en-us/bot-framework/media/how-it-works/architecture-resize.png",
};
}
}
以下源代码来自处理附件示例。
若要使用附件,请在机器人中包括以下库:
bots/attachmentsBot.js
const { ActivityHandler, ActionTypes, ActivityTypes, CardFactory } = require('botbuilder');
若要创建回复消息,请定义文本,然后设置附件。 将附件分配到回复的操作对于每个附加类型来说是相同的,但不同附件的设置和定义方法是不同的,如以下代码段所示。 以下代码为内联附件设置回复:
bots/attachmentsBot.js
*/
const firstChar = turnContext.activity.text[0];
if (firstChar === '1') {
若要向用户发送单个内容,如图片或视频,可以采用多种不同的方式发送媒体。 首先,用作内联附件:
bots/attachmentsBot.js
* Returns an inline attachment.
*/
getInlineAttachment() {
const imageData = fs.readFileSync(path.join(__dirname, '../resources/architecture-resize.png'));
const base64Image = Buffer.from(imageData).toString('base64');
return {
name: 'architecture-resize.png',
contentType: 'image/png',
contentUrl: `data:image/png;base64,${ base64Image }`
然后是上传的附件:
bots/attachmentsBot.js
* @param {Object} turnContext
*/
async getUploadedAttachment(turnContext) {
const imageData = fs.readFileSync(path.join(__dirname, '../resources/architecture-resize.png'));
const connectorFactory = turnContext.turnState.get(turnContext.adapter.ConnectorFactoryKey);
const connector = await connectorFactory.create(turnContext.activity.serviceUrl);
const conversationId = turnContext.activity.conversation.id;
const response = await connector.conversations.uploadAttachment(conversationId, {
name: 'architecture-resize.png',
originalBase64: imageData,
type: 'image/png'
});
// Retrieve baseUri from ConnectorClient for... something.
const baseUri = connector.baseUri;
const attachmentUri = baseUri + (baseUri.endsWith('/') ? '' : '/') + `v3/attachments/${ encodeURI(response.id) }/views/original`;
return {
name: 'architecture-resize.png',
contentType: 'image/png',
最后是包含在 URL 中的 Internet 附件:
bots/attachmentsBot.js
* Returns an attachment to be sent to the user from a HTTPS URL.
*/
getInternetAttachment() {
// NOTE: The contentUrl must be HTTPS.
return {
name: 'architecture-resize.png',
contentType: 'image/png',
contentUrl: 'https://docs.microsoft.com/en-us/bot-framework/media/how-it-works/architecture-resize.png'
本部分所示的源代码基于处理附件示例。
getAttachments()
对象的 Activity
方法包含一组 Attachment
对象,表示附加到消息的媒体附件和丰富卡片。 若要向消息添加媒体附件,请为 Attachment
活动创建 reply
对象,并设置 ContentType
、ContentUrl
和 Name
属性。
若要创建回复消息,请定义文本,然后设置附件。 将附件分配到回复的操作对于每个附加类型来说是相同的,但不同附件的设置和定义方法是不同的,如以下代码段所示。 以下代码为内联附件设置回复:
AttachmentsBot.java
警告
看起来您要查找的示例似乎已经被移动了! 放心,我们正在努力解决此问题。
接下来,我们查看附件的类型。 首先是内联附件:
AttachmentsBot.java
警告
看来您要找的示例已经被移动了! 放心,我们正在努力解决此问题。
然后是上传的附件:
AttachmentsBot.java
警告
你要查找的示例似乎已移动! 放心,我们正在努力解决此问题。
最后是互联网附件:
AttachmentsBot.java
警告
看来你正在寻找的示例已经被移动了! 放心,我们正在努力解决此问题。
以下源代码来自处理附件示例。
若要创建回复消息,请定义文本,然后设置附件。 将附件分配到回复的操作对于每个附加类型来说是相同的,但不同附件的设置和定义方法是不同的,如以下代码段所示。
以下代码为内联附件设置回复:
bots/attachments_bot.py
reply.text = "This is an inline attachment."
reply.attachments = [self._get_inline_attachment()]
若要向用户发送单个内容,如图片或视频,可以采用多种不同的方式发送媒体。 首先,用作内联附件:
bots/attachments_bot.py
def _get_inline_attachment(self) -> Attachment:
"""
Creates an inline attachment sent from the bot to the user using a base64 string.
Using a base64 string to send an attachment will not work on all channels.
Additionally, some channels will only allow certain file types to be sent this way.
For example a .png file may work but a .pdf file may not on some channels.
Please consult the channel documentation for specifics.
:return: Attachment
"""
file_path = os.path.join(os.getcwd(), "resources/architecture-resize.png")
with open(file_path, "rb") as in_file:
base64_image = base64.b64encode(in_file.read()).decode()
return Attachment(
name="architecture-resize.png",
content_type="image/png",
content_url=f"data:image/png;base64,{base64_image}",
)
然后是上传的附件:
bots/attachments_bot.py
async def _get_upload_attachment(self, turn_context: TurnContext) -> Attachment:
"""
Creates an "Attachment" to be sent from the bot to the user from an uploaded file.
:param turn_context:
:return: Attachment
"""
with open(
os.path.join(os.getcwd(), "resources/architecture-resize.png"), "rb"
) as in_file:
image_data = in_file.read()
connector = await turn_context.adapter.create_connector_client(
turn_context.activity.service_url
)
conversation_id = turn_context.activity.conversation.id
response = await connector.conversations.upload_attachment(
conversation_id,
AttachmentData(
name="architecture-resize.png",
original_base64=image_data,
type="image/png",
),
)
base_uri: str = connector.config.base_url
attachment_uri = (
base_uri
+ ("" if base_uri.endswith("/") else "/")
+ f"v3/attachments/{response.id}/views/original"
)
return Attachment(
name="architecture-resize.png",
content_type="image/png",
content_url=attachment_uri,
)
最后是包含在 URL 中的 Internet 附件:
bots/attachments_bot.py
def _get_internet_attachment(self) -> Attachment:
"""
Creates an Attachment to be sent from the bot to the user from a HTTP URL.
:return: Attachment
"""
return Attachment(
name="architecture-resize.png",
content_type="image/png",
content_url="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);
若要撰写含英雄卡和按钮的消息,可以将 HeroCard
对象附加到消息中。
以下源代码来自处理附件示例。
bots/attachmentsBot.js
* @param {Object} turnContext
*/
async displayOptions(turnContext) {
const reply = { type: ActivityTypes.Message };
// 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'.
const buttons = [
{ type: ActionTypes.ImBack, title: '1. Inline Attachment', value: '1' },
{ type: ActionTypes.ImBack, title: '2. Internet Attachment', value: '2' },
{ type: ActionTypes.ImBack, title: '3. Uploaded Attachment', value: '3' }
];
const card = CardFactory.heroCard('', undefined,
buttons, { text: 'You can upload an image or select one of the following choices.' });
reply.attachments = [card];
若要撰写含英雄卡和按钮的消息,可以将 HeroCard
对象附加到消息中。
以下源代码来自处理附件示例。
AttachmentsBot.java
警告
看起来您所寻找的示例已经被移动了! 放心,我们正在努力解决此问题。
若要撰写含英雄卡和按钮的消息,可以将 HeroCard
对象附加到消息中。
以下源代码来自处理附件示例。
bots/attachments_bot.py
async def _display_options(self, turn_context: TurnContext):
"""
Create a HeroCard with options for the user to interact with the bot.
:param turn_context:
:return:
"""
# 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'.
card = HeroCard(
text="You can upload an image or select one of the following choices",
buttons=[
CardAction(
type=ActionTypes.im_back, title="1. Inline Attachment", value="1"
),
CardAction(
type=ActionTypes.im_back, title="2. Internet Attachment", value="2"
),
CardAction(
type=ActionTypes.im_back, title="3. Uploaded Attachment", value="3"
),
],
)
处理资讯卡中的事件
若要处理丰富格式卡中的事件,请使用卡操作对象指定当用户选择按钮或单击卡的某个部分时应发生的情况。 每个卡操作都有一个 type 和 value 属性。
若要正常运行,请为英雄卡上的每个可单击项指定一种操作类型。 下表列出并描述了可用的操作类型以及应该存在于相关联的值属性中的内容。
messageBack
卡操作比其他卡操作的含义更泛性。 有关 和其他卡操作类型的详细信息,请参阅活动架构的messageBack
部分。
类型 |
说明 |
值 |
call |
拨打电话。 |
电话呼叫的目的地采用 tel:123123123123 格式。 |
downloadFile |
下载一个文件。 |
要下载的文件的 URL。 |
imBack |
向机器人发送一条消息,并在聊天中发布一个可见的响应。 |
要发送的消息文本。 |
messageBack |
表示将通过聊天系统发送的文本响应。 |
要包含在生成的消息中的可选编程值。 |
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;
}
有关所有可用卡的示例,请参阅使用卡示例。
dialogs/mainDialog.js
createHeroCard() {
return CardFactory.heroCard(
'BotFramework Hero Card',
CardFactory.images(['https://sec.ch9.ms/ch9/7ff5/e07cfef0-aa3b-40bb-9baa-7c9ef8ff7ff5/buildreactionbotframework_960.jpg']),
CardFactory.actions([
{
type: 'openUrl',
title: 'Get started',
value: 'https://docs.microsoft.com/en-us/azure/bot-service/'
}
])
);
}
dialogs/mainDialog.js
createOAuthCard() {
return CardFactory.oauthCard(
'OAuth connection', // Replace with the name of your Azure AD connection
'Sign In',
'BotFramework OAuth Card'
);
}
有关所有可用卡的示例,请参阅使用卡示例。
Cards.java
警告
似乎您要查找的示例已被移动! 放心,我们正在努力解决此问题。
Cards.java
警告
你要查找的示例似乎已移动! 放心,我们正在努力解决此问题。
有关所有可用卡的示例,请参阅使用卡示例。
dialogs/main_dialog.py
def create_hero_card(self) -> Attachment:
card = HeroCard(
title="",
images=[
CardImage(
url="https://sec.ch9.ms/ch9/7ff5/e07cfef0-aa3b-40bb-9baa-7c9ef8ff7ff5/buildreactionbotframework_960.jpg"
)
],
buttons=[
CardAction(
type=ActionTypes.open_url,
title="Get Started",
value="https://docs.microsoft.com/en-us/azure/bot-service/",
)
],
)
return CardFactory.hero_card(card)
dialogs/main_dialog.py
def create_oauth_card(self) -> Attachment:
card = OAuthCard(
text="BotFramework OAuth Card",
connection_name="OAuth connection", # Replace it with the name of your Azure AD connection.
buttons=[
CardAction(
type=ActionTypes.signin,
title="Sign in",
value="https://example.org/signin",
)
],
)
return CardFactory.oauth_card(card)
发送自适应卡
虽然可以使用消息工厂创建包含附件(任何类型)的消息,但自适应卡片是一种特定类型的附件。 不是所有通道都支持自适应卡片,有些通道可能只是部分支持自适应卡片。 例如,如果在 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;
}
若要使用自适应卡片,请务必添加 adaptivecards
npm 包。
以下源代码来自使用卡示例。
dialogs/mainDialog.js
此示例从某个文件读取自适应卡片 JSON 并创建附有卡片的消息活动。
const AdaptiveCard = require('../resources/adaptiveCard.json');
createAdaptiveCard() {
return CardFactory.adaptiveCard(AdaptiveCard);
}
以下源代码来自使用卡示例。
Cards.java
此示例从某个文件读取自适应卡片 JSON 并将其添加为附件。
警告
似乎你要查找的样本已经移动了! 放心,我们正在努力解决此问题。
以下源代码来自使用卡片示例。
bots/main_dialog.py
此示例从某个文件读取自适应卡片 JSON 并创建附有卡片的消息活动。
from .resources.adaptive_card_example import ADAPTIVE_CARD_CONTENT
def create_adaptive_card(self) -> Attachment:
return CardFactory.adaptive_card(ADAPTIVE_CARD_CONTENT)
发送采用轮播布局的卡
消息还可以包含多个附件,并以轮播布局的形式并排放置,用户可以左右滚动浏览这些附件。
以下源代码来自使用卡示例。
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);
以下源代码来自使用卡示例。
dialogs/mainDialog.js
添加附件并将布局类型设置为轮播。
添加附件以后,即可发送回复,就像发送任何其他内容一样。
await stepContext.context.sendActivity({
attachments: [
this.createAdaptiveCard(),
this.createAnimationCard(),
this.createAudioCard(),
this.createHeroCard(),
this.createOAuthCard(),
this.createReceiptCard(),
this.createSignInCard(),
this.createThumbnailCard(),
this.createVideoCard()
],
attachmentLayout: AttachmentLayoutTypes.Carousel
});
以下源代码来自使用卡示例。
MainDialog.java
首先,创建回复并将附件定义为列表。
警告
看来您要查找的示例已被移走! 放心,我们正在努力解决此问题。
然后添加附件并将布局类型设置为轮播。
在这里,我们一次添加一个附件,但你可以根据自己的偏好来添加卡片,对列表随意进行操作。
警告
你要查找的示例似乎已移动! 放心,我们正在努力解决此问题。
添加附件以后,即可发送回复,就像发送任何其他内容一样。
警告
你要查找的示例似乎已经被移动了! 放心,我们正在努力解决此问题。
此处所示的源代码基于使用卡示例。
dialogs/main_dialog.py
首先,创建回复并将附件定义为列表。
reply = MessageFactory.list([])
然后添加附件并将布局类型设置为轮播。
在这里,我们一次添加一个附件,但你可以根据自己的偏好来添加卡片,对列表随意进行操作。
reply.attachment_layout = AttachmentLayoutTypes.carousel
reply.attachments.append(self.create_adaptive_card())
reply.attachments.append(self.create_animation_card())
reply.attachments.append(self.create_audio_card())
reply.attachments.append(self.create_hero_card())
reply.attachments.append(self.create_oauth_card())
reply.attachments.append(self.create_receipt_card())
reply.attachments.append(self.create_signin_card())
reply.attachments.append(self.create_thumbnail_card())
reply.attachments.append(self.create_video_card())
添加附件以后,即可发送回复,就像发送任何其他内容一样。
# Send the card(s) to the user as an attachment to the activity
await step_context.context.send_activity(reply)
以下示例显示了在机器人对话类中使用 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)));
更改为:
// Define the main dialog and its related components.
AddDialog(new ChoicePrompt(nameof(ChoicePrompt), ChoiceValidator));
每当创建新的选项提示时,这会调用验证器来查找自适应卡片输入。
打开 mainDialog.js 并找到 run 方法 。此方法处理传入的活动。
紧接在 dialogSet.add(this);
调用后面添加以下内容:
// The following check looks for a non-existent text input
// plus Adaptive Card input in _activity.value.text
// If both conditions exist, the Activity Card text
// is copied into the text input field.
if(turnContext._activity.text == null
&& turnContext._activity.value.text != null) {
this.logger.log('replacing null text with Activity Card text input');
turnContext._activity.text = turnContext._activity.value.text;
}
如果此检查发现客户端中有不存在的文本输入,它将查看是否有来自自适应卡的输入。
如果在 _activity.value.text
处存在自适应卡片输入,它会将其复制到常规文本输入字段中。
验证器使用 com.microsoft.bot.schema 中的 Serialization 帮助器首先将其转换为 JsonNode
,然后创建一个修剪过的文本字符串进行比较。 我们还需要导入其他几个模块来完成此操作,因此请加入:
import com.fasterxml.jackson.databind.JsonNode;
import com.microsoft.bot.dialogs.prompts.PromptValidator;
import com.microsoft.bot.schema.Serialization;
import java.util.Optional;
import org.apache.commons.lang3.StringUtils;
添加到 MainDialog.java。
在验证程序代码中,我们将逻辑流添加到代码注释中。
此 PromptValidator
表达式放置于右大括号后的“使用卡”示例中,用于公开声明 MainDialog:
PromptValidator<FoundChoice> validator = (promptContext) -> {
// Retrieves Adaptive Card comment text as JObject.
// looks for JObject field "text" and converts that input into a trimmed text
// string.
JsonNode jsonNode = Serialization.getAs(promptContext.getContext().getActivity().getValue(), JsonNode.class);
JsonNode textNode = jsonNode != null ? jsonNode.get("text") : null;
String text = textNode != null ? textNode.textValue() : "";
// 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.getRecognized().getSucceeded() && text != null) {
Optional<Choice> choice = promptContext.getOptions()
.getChoices()
.stream()
.filter(c -> StringUtils.compareIgnoreCase(c.getValue(), text) == 0)
.findFirst();
if (choice.isPresent()) {
promptContext.getRecognized().setValue(new FoundChoice() {
{
setValue(choice.get().getValue());
}
});
return CompletableFuture.completedFuture(true);
}
}
return CompletableFuture.completedFuture(promptContext.getRecognized().getSucceeded());
};
现在,在 MainDialog
声明的上面,更改:
// Define the main dialog and its related components.
addDialog(new ChoicePrompt("ChoicePrompt"));
更改为:
// Define the main dialog and its related components.
addDialog(new ChoicePrompt("ChoicePrompt", validator, null));
每当创建新的选项提示时,这会调用验证器来查找自适应卡片输入。
创建包含建议操作的活动并将其发送给用户。
此 choice_validator
方法放置于右大括号后的“使用卡”示例中,用于公开声明 MainDialog
:
@staticmethod
async def choice_validator(prompt_context: PromptValidatorContext) -> bool:
if prompt_context.context.activity.value:
text = prompt_context.context.activity.value["text"].lower()
if not prompt_context.recognized.succeeded and text:
matching_choices = [choice for choice in prompt_context.options.choices if choice.value.lower() == text]
if matching_choices:
choice = matching_choices[0]
prompt_context.recognized.value = FoundChoice(
value=choice.value,
index=0,
score=1.0
)
return True
return prompt_context.recognized.succeeded
现在,在 MainDialog
声明的上面,更改:
self.add_dialog(ChoicePrompt(CARD_PROMPT))
更改为:
self.add_dialog(ChoicePrompt(CARD_PROMPT, MainDialog.choice_validator))
每当创建新的选项提示时,这会调用验证器来查找自适应卡片输入。
测试“使用卡片”机器人
- 在本地运行“使用卡片”示例,并在 Bot Framework Emulator 中打开机器人。
- 依照机器人中的提示显示卡类型,例如自适应卡片。
后续步骤