A troca de mensagens entre usuário e bot pode conter anexos de mídia como imagens, vídeo, áudio e arquivos. O SDK do Bot Framework oferece suporte à tarefa de envio de mensagens avançadas para o usuário. Para determinar o tipo de mensagens avançadas compatíveis com um canal (Facebook, Slack etc.), consulte a documentação do canal para obter informações sobre as limitações.
A propriedade Attachments do objeto Activity contém uma matriz de objetos Attachment que representam os anexos de mídia e os cartões avançados na mensagem. Para adicionar um anexo de mídia a uma mensagem, crie um objeto Attachment para a atividade reply e defina as propriedades ContentType, ContentUrl e Name.
Para criar a mensagem de resposta, defina o texto e, em seguida, configure os anexos. A atribuição dos anexos à resposta é a mesma para cada tipo de anexo, no entanto vários anexos são configurados e definidos de forma diferente, como visto nos snippets de código a seguir. O código a seguir é a configuração da resposta para um anexo embutido:
Bots/AttachmentsBot.cs
{
reply = MessageFactory.Text("This is an inline attachment.");
Em seguida, vamos examinar os tipos de anexos. O primeiro é um anexo embutido:
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}",
};
}
Em seguida, um anexo carregado:
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,
};
}
Por fim, um anexo de 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",
};
}
}
Para criar a mensagem de resposta, defina o texto e, em seguida, configure os anexos. A atribuição dos anexos à resposta é a mesma para cada tipo de anexo, no entanto vários anexos são configurados e definidos de forma diferente, como visto nos snippets de código a seguir. O código a seguir é a configuração da resposta para um anexo embutido:
bots/attachmentsBot.js
const reply = { type: ActivityTypes.Message };
reply.text = 'This is an inline attachment.';
reply.attachments = [this.getInlineAttachment()];
Para enviar ao usuário uma única parte do conteúdo, como uma imagem ou um vídeo, é possível enviar mídia de algumas maneiras diferentes. Primeiro, como um anexo embutido:
O getAttachments() método do Activity objeto contém uma matriz de Attachment objetos que representam os anexos de mídia e os cartões avançados anexados à mensagem. Para adicionar um anexo de mídia a uma mensagem, crie um objeto Attachment para a atividade reply e defina as propriedades ContentType, ContentUrl e Name.
Para criar a mensagem de resposta, defina o texto e, em seguida, configure os anexos. A atribuição dos anexos à resposta é a mesma para cada tipo de anexo, no entanto vários anexos são configurados e definidos de forma diferente, como visto nos snippets de código a seguir. O código a seguir é a configuração da resposta para um anexo embutido:
AttachmentsBot.java
result = getInlineAttachment()
.thenApply(attachment -> {
Activity reply = MessageFactory.text("This is an inline attachment.");
reply.setAttachment(attachment);
return reply;
});
Em seguida, vamos examinar os tipos de anexos. O primeiro é um anexo embutido:
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;
});
}
// 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;
}
Para criar a mensagem de resposta, defina o texto e, em seguida, configure os anexos. A atribuição dos anexos à resposta é a mesma para cada tipo de anexo, no entanto vários anexos são configurados e definidos de forma diferente, como visto nos snippets de código a seguir.
O código a seguir é a configuração da resposta para um anexo embutido:
bots/attachments_bot.py
reply.text = "This is an inline attachment."
reply.attachments = [self._get_inline_attachment()]
Para enviar ao usuário uma única parte do conteúdo, como uma imagem ou um vídeo, é possível enviar mídia de algumas maneiras diferentes. Primeiro, como um anexo embutido:
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}",
)
Em seguida, um anexo carregado:
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,
)
Por fim, um anexo de internet contido em uma URL:
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",
)
Se um anexo for uma imagem, áudio ou vídeo, o serviço do Connector comunicará os dados do anexo ao canal de modo a permitir que o canal renderize esse anexo na conversa. Se o anexo for um arquivo, a URL do arquivo será renderizada como um hiperlink na conversa.
Enviar um cartão hero
Além de anexos de vídeo ou imagem simples, é possível anexar um cartão hero, o que permite a você combinar imagens e botões em um objeto e depois enviá-los ao usuário. O markdown tem suporte na maioria dos campos de texto, mas o suporte pode variar por canal.
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);
Para redigir uma mensagem com um cartão hero e um botão, você pode anexar um HeroCard objeto a uma mensagem.
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];
await turnContext.sendActivity(reply);
}
Para redigir uma mensagem com um cartão hero e um botão, você pode anexar um HeroCard objeto a uma mensagem.
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);
}
Para redigir uma mensagem com um cartão hero e um botão, você pode anexar um HeroCard objeto a uma mensagem.
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"
),
],
)
Processar eventos em cartões avançados
Para processar eventos em cartões avançados, use objetos de ação de cartão para especificar o que deve acontecer quando o usuário seleciona um botão ou toca em uma seção do cartão. Cada ação de cartão tem uma propriedade de tipo e valor .
Para funcionar corretamente, atribua um tipo de ação a cada item clicável em um cartão hero. Esta tabela lista e descreve os tipos de ação disponíveis e quais devem estar na propriedade de valor associada.
A messageBack ação do cartão tem um significado mais generalizado do que as outras ações de cartão. Consulte a seção ação Cartão do esquema Atividade para obter mais informações sobre os messageBack tipos de ação de cartão e outros tipos de ação de cartão.
Tipo
Descrição
Valor
chamada
Inicia uma chamada telefônica.
Destino de uma chamada telefônica nesse formato: tel:123123123123.
downloadFile
Baixa um arquivo.
A URL do arquivo para download.
imBack
Envia uma mensagem para o bot e posta uma resposta visível no bate-papo.
Texto da mensagem a ser enviada.
messageBack
Representa uma resposta de texto a ser enviada por meio do sistema de chat.
Um valor programático opcional a ser incluído nas mensagens geradas.
openUrl
Abre uma URL no navegador interno.
A URL para abrir.
playAudio
Reproduz áudio.
A URL do áudio para reproduzir.
playVideo
Reproduz um vídeo.
A URL do vídeo para reproduzir.
postBack
Envia uma mensagem para o bot e não pode postar uma resposta visível no bate-papo.
Texto da mensagem a ser enviada.
showImage
Exibe uma imagem.
A URL da imagem para exibir.
signin
Inicia um processo de entrada do OAuth.
A URL do fluxo de OAuth para iniciar.
Cartão hero usando vários tipos de evento
O código a seguir mostra exemplos que usam diversos eventos de cartão avançados.
Para obter exemplos de todos os cartões disponíveis, consulte o exemplo usando cartões .
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;
}
Para obter exemplos de todos os cartões disponíveis, consulte o exemplo usando cartões .
createOAuthCard() {
return CardFactory.oauthCard(
'OAuth connection', // Replace with the name of your Azure AD connection
'Sign In',
'BotFramework OAuth Card'
);
}
Para obter exemplos de todos os cartões disponíveis, consulte o exemplo usando cartões .
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;
}
Para obter exemplos de todos os cartões disponíveis, consulte o exemplo usando cartões .
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)
Enviar um cartão adaptável
Embora você possa usar o fábrica de mensagens para criar uma mensagem que contenha um anexo (de qualquer tipo), um Cartão Adaptável é um tipo específico de anexo. Observe que alguns canais não dão suporte a Cartões Adaptáveis e os canais que dão suporte apenas parcialmente a eles. Por exemplo, se você enviar um Cartão Adaptável no Facebook, os botões não funcionarão se textos e imagens funcionarem bem. O fábrica de mensagens é uma classe auxiliar do SDK do Bot Framework usada para automatizar as etapas de criação para você.
Cartões Adaptáveis são um formato aberto de troca de cartões que permite aos desenvolvedores trocar o conteúdo da interface do usuário de uma maneira comum e consistente. No entanto, nem todos os canais dão suporte a Cartões Adaptáveis.
O Designer de Cartões Adaptáveis fornece uma experiência avançada e interativa em tempo de design para criar cartões adaptáveis.
Observação
Você deve testar esse recurso com os canais de que bot será usado para determinar se esses canais dão suporte a cartões adaptáveis.
O código-fonte mostrado aqui se baseia no exemplo Usando cartões.
Dialogs/MainDialog.cs
Primeiro, crie a resposta e defina os anexos como uma lista.
// 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);
Em seguida, adicione os anexos e defina o tipo de layout como carrossel.
Aqui estamos adicionando-os um de cada vez, mas sinta-se à vontade para manipular a lista e adicionar os cartões como preferir.
// 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());
Depois que os anexos forem adicionados, você poderá enviar a resposta como faria com qualquer outra.
// Send the card(s) to the user as an attachment to the activity
await stepContext.Context.SendActivityAsync(reply, cancellationToken);
O código-fonte mostrado aqui se baseia no exemplo Usando cartões.
dialogs/mainDialog.js
Adicione os anexos e defina o tipo de layout como carrossel.
Depois que os anexos forem adicionados, você poderá enviar a resposta como faria com qualquer outra.
O código-fonte mostrado aqui se baseia no exemplo Usando cartões.
MainDialog.java
Primeiro, crie a resposta e defina os anexos como uma lista.
// 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);
Em seguida, adicione os anexos e defina o tipo de layout como carrossel.
Aqui estamos adicionando-os um de cada vez, mas sinta-se à vontade para manipular a lista e adicionar os cartões como preferir.
// 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());
Depois que os anexos forem adicionados, você poderá enviar a resposta como faria com qualquer outra.
// Send the card(s) to the user as an attachment to the activity
return stepContext.getContext().sendActivity(reply)
Primeiro, crie a resposta e defina os anexos como uma lista.
reply = MessageFactory.list([])
Em seguida, adicione os anexos e defina o tipo de layout como carrossel.
Aqui estamos adicionando-os um de cada vez, mas sinta-se à vontade para manipular a lista e adicionar os cartões como preferir.
Depois que os anexos forem adicionados, você poderá enviar a resposta como faria com qualquer outra.
# Send the card(s) to the user as an attachment to the activity
await step_context.context.send_activity(reply)
Exemplo de código para processar entrada do Cartão Adaptável
O exemplo a seguir mostra uma maneira de usar entradas de Cartão Adaptável em uma classe de diálogo de bot.
Ele estende o exemplo de cartões hero validando a entrada recebida no campo de texto do cliente que responde.
Primeiro, você precisa adicionar a funcionalidade de botão e entrada de texto ao cartão adaptável existente adicionando o seguinte código pouco antes do colchete final de adaptiveCard.json, localizado na pasta de recursos:
Observe que a ID do campo de entrada de texto está definida como "text". Quando o usuário seleciona OK, a mensagem gerada pelo Cartão Adaptável terá uma propriedade de valor que tem uma propriedade nomeada "text" que contém as informações inseridas pelo usuário no campo de entrada de texto do cartão.
Nosso validador usa Newtonsoft.json para converter isso primeiro em um JObjecte, em seguida, criar uma cadeia de caracteres de texto cortada para comparação. Portanto, adicione:
using System;
using System.Linq;
using Newtonsoft.Json.Linq;
para MainDialog.cs e instale o pacote NuGet estável mais recente do Newtonsoft.Json.
No código do validador, adicionamos o fluxo de lógica aos comentários do código.
Esse ChoiceValidator método é colocado no exemplo usando cartões logo após o público da chave fechada para declaração do 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;
}
Agora acima na alteração da MainDialog declaração:
// Define the main dialog and its related components.
AddDialog(new ChoicePrompt(nameof(ChoicePrompt)));
para:
// Define the main dialog and its related components.
AddDialog(new ChoicePrompt(nameof(ChoicePrompt), ChoiceValidator));
Isso invocará o validador para procurar a entrada do Cartão Adaptável sempre que um novo prompt de escolha for criado.
Abra mainDialog.js e localize o método async run(turnContext, accessor) de execução Este método manipula a atividade de entrada.
Logo após a chamada dialogSet.add(this); , adicione o seguinte:
// 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;
}
Se essa verificação encontrar uma entrada de texto inexistente do cliente, ela verificará se há entrada de um Cartão Adaptável.
Se uma entrada de Cartão Adaptável existir, _activity.value.textela copiará isso para o campo de entrada de texto normal.
Nosso validador usa o auxiliar serialização de com.microsoft.bot.schema para converter primeiro isso em um JsonNodee, em seguida, criar uma cadeia de caracteres de texto cortada para comparação. Também precisaremos de algumas outras importações para concluir isso, portanto, adicione:
para MainDialog.java.
No código do validador, adicionamos o fluxo de lógica aos comentários do código.
Essa PromptValidator expressão é colocada no exemplo usando cartões logo após a chave fechada pública para declaração do 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());
};
Agora acima na alteração da MainDialog declaração:
// Define the main dialog and its related components.
addDialog(new ChoicePrompt("ChoicePrompt"));
para:
// Define the main dialog and its related components.
addDialog(new ChoicePrompt("ChoicePrompt", validator, null));
Isso invocará o validador para procurar a entrada do Cartão Adaptável sempre que um novo prompt de escolha for criado.
Crie e envie uma atividade com ações sugeridas para o usuário.
Esse choice_validator método é colocado no exemplo usando cartões logo após o público da chave fechada para declaração de 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
Agora acima na alteração da MainDialog declaração:
Isso invocará o validador para procurar a entrada do Cartão Adaptável sempre que um novo prompt de escolha for criado.
Para testar seu código, depois que um Cartão Adaptável for exibido, selecione o botão Texto , insira uma seleção válida, como o Cartão Herói , e selecione o botão OK .
A primeira entrada será ser usada para iniciar uma nova caixa de diálogo.
Selecione o botão OK novamente e essa entrada será usada para selecionar um novo cartão.