用户与机器人之间的消息交换可以包含媒体附件,例如图像、视频、音频和文件。 Bot Framework SDK 支持向用户发送富消息的任务。 要确定通道(Teams 等)支持的丰富消息类型,请参阅通道的文档以获取有关限制的信息。
本部分所示的所有源代码均基于处理附件示例。
Activity
对象的 Attachments
属性包含一组 Attachment
对象,表示媒体附件和附加到消息的富卡。 若要向消息添加媒体附件,请为 reply
活动创建 Attachment
对象,并设置 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,
};
}
最后是 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",
};
}
}
以下源代码来自处理附件示例。
若要使用附件,请在机器人中包括以下库:
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'
本部分所示的源代码基于处理附件示例。
Activity
对象的 getAttachments()
方法包含一组 Attachment
对象,表示媒体附件和附加到消息的丰富格式卡。 若要向消息添加媒体附件,请为 reply
活动创建 Attachment
对象,并设置 ContentType
、ContentUrl
和 Name
属性。
若要创建回复消息,请定义文本,然后设置附件。 将附件分配到回复的操作对于每个附加类型来说是相同的,但不同附件的设置和定义方法是不同的,如以下代码段所示。 以下代码为内联附件设置回复:
AttachmentsBot.java
result = getInlineAttachment()
.thenApply(attachment -> {
Activity reply = MessageFactory.text("This is an inline attachment.");
reply.setAttachment(attachment);
return reply;
});
接下来,我们查看附件的类型。 首先是内联附件:
AttachmentsBot.java
// 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.
private CompletableFuture<Attachment> getInlineAttachment() {
return getEncodedFileData("architecture-resize.png")
.thenApply(encodedFileData -> {
Attachment attachment = new Attachment();
attachment.setName("architecture-resize.png");
attachment.setContentType("image/png");
attachment.setContentUrl("data:image/png;base64," + encodedFileData);
return attachment;
});
}
然后是上传的附件:
AttachmentsBot.java
private CompletableFuture<Attachment> getUploadedAttachment(TurnContext turnContext, String serviceUrl, String conversationId) {
if (StringUtils.isEmpty(serviceUrl)) {
return Async.completeExceptionally(new IllegalArgumentException("serviceUrl"));
}
if (StringUtils.isEmpty(conversationId)) {
return Async.completeExceptionally(new IllegalArgumentException("conversationId"));
}
ConnectorClient connector = turnContext.getTurnState()
.get(BotFrameworkAdapter.CONNECTOR_CLIENT_KEY);
Attachments attachments = connector.getAttachments();
return getFileData("architecture-resize.png")
.thenCompose(fileData -> {
AttachmentData attachmentData = new AttachmentData();
attachmentData.setName("architecture-resize.png");
attachmentData.setType("image/png");
attachmentData.setOriginalBase64(fileData);
return connector.getConversations().uploadAttachment(conversationId, attachmentData)
.thenApply(response -> {
String attachmentUri = attachments.getAttachmentUri(response.getId());
Attachment attachment = new Attachment();
attachment.setName("architecture-resize.png");
attachment.setContentType("image/png");
attachment.setContentUrl(attachmentUri);
return attachment;
});
});
}
最后是 Internet 附件:
AttachmentsBot.java
// Creates an Attachment to be sent from the bot to the user from a HTTP URL.
private static Attachment getInternetAttachment() {
// ContentUrl must be HTTPS.
Attachment attachment = new Attachment();
attachment.setName("architecture-resize.png");
attachment.setContentType("image/png");
attachment.setContentUrl("https://docs.microsoft.com/en-us/bot-framework/media/how-it-works/architecture-resize.png");
return attachment;
}
以下源代码来自处理附件示例。
若要创建回复消息,请定义文本,然后设置附件。 将附件分配到回复的操作对于每个附加类型来说是相同的,但不同附件的设置和定义方法是不同的,如以下代码段所示。
以下代码为内联附件设置回复:
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",
)
除了简单的图像或视频附件,还可以附加英雄卡 ,这样可以将图像和按钮组合在一个对象中,并将其发送给用户。 大多数文本字段支持 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
private static CompletableFuture<Void> displayOptions(TurnContext turnContext) {
// Create a HeroCard with options for the user to interact with the bot.
HeroCard card = new HeroCard();
card.setText("You can upload an image or select one of the following choices");
// 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.setButtons(
new CardAction(ActionTypes.IM_BACK, "1. Inline Attachment", "1"),
new CardAction(ActionTypes.IM_BACK, "2. Internet Attachment", "2"),
new CardAction(ActionTypes.IM_BACK, "3. Uploaded Attachment", "3")
);
Activity reply = MessageFactory.attachment(card.toAttachment());
return turnContext.sendActivity(reply).thenApply(resourceResponse -> null);
}
若要撰写含英雄卡和按钮的消息,可以将 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"
),
],
)
有关所有可用卡的示例,请参阅使用卡示例。
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
public static HeroCard getHeroCard() {
HeroCard heroCard = new HeroCard();
heroCard.setTitle("BotFramework Hero Card");
heroCard.setSubtitle("Microsoft Bot Framework");
heroCard.setText("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.");
heroCard.setImages(new CardImage("https://sec.ch9.ms/ch9/7ff5/e07cfef0-aa3b-40bb-9baa-7c9ef8ff7ff5/buildreactionbotframework_960.jpg"));
heroCard.setButtons(new CardAction(ActionTypes.OPEN_URL, "Get Started", "https://docs.microsoft.com/bot-framework"));
return heroCard;
}
Cards.java
public static SigninCard getSigninCard() {
SigninCard signinCard = new SigninCard();
signinCard.setText("BotFramework Sign-in Card");
signinCard.setButtons(new CardAction(ActionTypes.SIGNIN, "Sign-in", "https://login.microsoftonline.com/"));
return signinCard;
}
有关所有可用卡的示例,请参阅使用卡示例。
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 帮助器类,用于自动执行创建步骤。
若要使用自适应卡片,请务必添加 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 并将其添加为附件。
public static Attachment createAdaptiveCardAttachment() {
Attachment adaptiveCardAttachment = new Attachment();
try (
InputStream inputStream = adaptiveCardAttachment.getClass().getClassLoader()
.getResourceAsStream("adaptiveCard.json")
) {
String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
adaptiveCardAttachment.setContentType("application/vnd.microsoft.card.adaptive");
adaptiveCardAttachment.setContent(new ObjectMapper().readValue(result, ObjectNode.class));
return adaptiveCardAttachment;
} catch (Throwable t) {
throw new CompletionException(t);
}
}
以下源代码来自使用卡示例。
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
首先,创建回复并将附件定义为列表。
// Cards are sent as Attachments in the Bot Framework.
// So we need to create a list of attachments for the reply activity.
List<Attachment> attachments = new ArrayList<>();
// Reply to the activity we received with an activity.
Activity reply = MessageFactory.attachment(attachments);
然后添加附件并将布局类型设置为轮播。
在这里,我们一次添加一个附件,但你可以根据自己的偏好来添加卡片,对列表随意进行操作。
// Display a carousel of all the rich card types.
reply.setAttachmentLayout(AttachmentLayoutTypes.CAROUSEL);
reply.getAttachments().add(Cards.createAdaptiveCardAttachment());
reply.getAttachments().add(Cards.getAnimationCard().toAttachment());
reply.getAttachments().add(Cards.getAudioCard().toAttachment());
reply.getAttachments().add(Cards.getHeroCard().toAttachment());
reply.getAttachments().add(Cards.getOAuthCard().toAttachment());
reply.getAttachments().add(Cards.getReceiptCard().toAttachment());
reply.getAttachments().add(Cards.getSigninCard().toAttachment());
reply.getAttachments().add(Cards.getThumbnailCard().toAttachment());
reply.getAttachments().add(Cards.getVideoCard().toAttachment());
添加附件以后,即可发送回复,就像发送任何其他内容一样。
// Send the card(s) to the user as an attachment to the activity
return stepContext.getContext().sendActivity(reply)
此处所示的源代码基于使用卡示例。
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 的最后一个括号之前添加以下代码,将文本输入和按钮功能添加到现有的自适应卡片:
验证器使用 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));
每当创建新的选项提示时,这会调用验证器来查找自适应卡片输入。
打开 mainDialog.js 并找到 run 方法 async run(turnContext, accessor)
。此方法处理传入的活动。
紧接在 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 语句来完成此操作,因此请将:
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"));
to:
// 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))
to:
self.add_dialog(ChoicePrompt(CARD_PROMPT, MainDialog.choice_validator))
每当创建新的选项提示时,这会调用验证器来查找自适应卡片输入。