Envío de datos adjuntos multimedia con Bot Framework SDK
Artículo
SE APLICA A: SDK v4
Un intercambio de mensajes entre el usuario y el bot puede contener datos adjuntos con elementos multimedia, como imágenes, vídeo, audio y archivos. Bot Framework SDK es compatible con la tarea de enviar mensajes enriquecidos al usuario. Para determinar el tipo de mensajes enriquecidos que admite un canal (Facebook, Slack, etc.), consulte la documentación del canal para más información sobre las limitaciones.
Nota:
Los SDK de JavaScript, C# y Python de Bot Framework seguirán siendo compatibles, pero el SDK de Java se va a retirar con la compatibilidad final a largo plazo que finaliza en noviembre de 2023.
Los bots existentes creados con el SDK de Java seguirán funcionando.
Todo el código fuente que se muestra en esta sección se basa en el ejemplo Handling attachments.
La propiedad Attachments del objeto Activity contiene una matriz de objetos Attachment que representan los datos adjuntos con elementos multimedia y las tarjetas enriquecidas adjuntas al mensaje. Para agregar datos adjuntos con elementos multimedia a un mensaje, cree un objeto Attachment para la actividad reply y establezca las propiedades ContentType, ContentUrl y Name.
Para crear el mensaje de respuesta, defina el texto y, a continuación, configure los datos adjuntos. La asignación de los datos adjuntos a la respuesta es la misma para cada tipo de archivo adjunto, aunque los distintos datos adjuntos se configuran y definen de manera diferente, tal como se muestra en los siguientes fragmentos de código. El código siguiente configura la respuesta para los datos adjuntos insertados:
Bots/AttachmentsBot.cs
{
reply = MessageFactory.Text("This is an inline attachment.");
A continuación, nos centramos en los tipos de datos adjuntos. Primero, datos adjuntos insertados:
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}",
};
}
Seguidamente, datos adjuntos cargados:
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 último, un archivo adjunto 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 crear el mensaje de respuesta, defina el texto y, a continuación, configure los datos adjuntos. La asignación de los datos adjuntos a la respuesta es la misma para cada tipo de archivo adjunto, aunque los distintos datos adjuntos se configuran y definen de manera diferente, tal como se muestra en los siguientes fragmentos de código. El código siguiente configura la respuesta para los datos adjuntos insertados:
bots/attachmentsBot.js
*/
const firstChar = turnContext.activity.text[0];
if (firstChar === '1') {
Para enviar al usuario un único fragmento de contenido, como una imagen o un vídeo, puede enviar elementos multimedia de varias maneras diferentes. Primero, como datos adjuntos insertados:
Por último, un archivo adjunto de Internet incluido en una dirección URL:
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'
El código fuente que se muestra en esta sección se basa en el ejemplo Handling attachments.
El método getAttachments() del objeto Activity contiene una matriz de objetos Attachment que representan los datos adjuntos con elementos multimedia y las tarjetas enriquecidas adjuntas al mensaje. Para agregar datos adjuntos con elementos multimedia a un mensaje, cree un objeto Attachment para la actividad reply y establezca las propiedades ContentType, ContentUrl y Name.
Para crear el mensaje de respuesta, defina el texto y, a continuación, configure los datos adjuntos. La asignación de los datos adjuntos a la respuesta es la misma para cada tipo de archivo adjunto, aunque los distintos datos adjuntos se configuran y definen de manera diferente, tal como se muestra en los siguientes fragmentos de código. El código siguiente configura la respuesta para los datos adjuntos insertados:
AttachmentsBot.java
result = getInlineAttachment()
.thenApply(attachment -> {
Activity reply = MessageFactory.text("This is an inline attachment.");
reply.setAttachment(attachment);
return reply;
});
A continuación, nos centramos en los tipos de datos adjuntos. Primero, datos adjuntos insertados:
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 crear el mensaje de respuesta, defina el texto y, a continuación, configure los datos adjuntos. La asignación de los datos adjuntos a la respuesta es la misma para cada tipo de archivo adjunto, aunque los distintos datos adjuntos se configuran y definen de manera diferente, tal como se muestra en los siguientes fragmentos de código.
El código siguiente configura la respuesta para los datos adjuntos insertados:
bots/attachments_bot.py
reply.text = "This is an inline attachment."
reply.attachments = [self._get_inline_attachment()]
Para enviar al usuario un único fragmento de contenido, como una imagen o un vídeo, puede enviar elementos multimedia de varias maneras diferentes. Primero, como datos adjuntos insertados:
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}",
)
Seguidamente, datos adjuntos cargados:
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 último, un archivo adjunto de Internet incluido en una dirección 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",
)
Si los datos adjuntos son una imagen, un audio o un vídeo, el servicio del conector comunicará los datos adjuntos al canal de una manera que permita que el canal presente esos datos adjuntos dentro de la conversación. Si los datos adjuntos son un archivo, la dirección URL del archivo se presentará como un hipervínculo dentro de la conversación.
Envío de una tarjeta de imagen prominente
Además de los datos adjuntos de imagen o vídeo sencillos, puede adjuntar una tarjeta de imagen prominente que le permite combinar las imágenes y los botones en un objeto y los envían al usuario. Markdown es compatible con la mayoría de los campos de texto, pero la compatibilidad puede variar según el 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 redactar un mensaje con un botón y una tarjeta de imagen prominente, puede adjuntar un objeto HeroCard a un mensaje.
* @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];
Para redactar un mensaje con un botón y una tarjeta de imagen prominente, puede adjuntar un objeto HeroCard a un mensaje.
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 redactar un mensaje con un botón y una tarjeta de imagen prominente, puede adjuntar un objeto HeroCard a un mensaje.
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"
),
],
)
Procesamiento de eventos dentro de tarjetas enriquecidas
Para procesar eventos dentro de tarjetas enriquecidas, use los objetos de acción de tarjeta para especificar qué debe ocurrir cuando el usuario selecciona un botón o pulsa una sección de la tarjeta. Cada acción de la tarjeta tiene una propiedad de tipo y valor.
Para que el funcionamiento sea correcto, asigne un tipo de acción a cada elemento en el que se puede hacer clic en la tarjeta de elemento principal. Esta tabla enumera y describe los tipos de acciones disponibles y lo que debería estar en la propiedad de valor asociada.
La acción de la tarjeta messageBack tiene un significado más generalizado que las demás acciones de tarjeta. Consulte la sección Acción de tarjeta del Esquema de actividad para obtener más información sobre el messageBack y otros tipos de acción de tarjeta.
Tipo
Descripción
Valor
call
Inicia una llamada de teléfono.
Destino de una llamada telefónica con este formato: tel:123123123123.
DownloadFile
Descarga un archivo.
La dirección URL del archivo que se va a descargar.
imBack
Envía un mensaje al bot y publica una respuesta visible en el chat.
Texto del mensaje para enviar.
messageBack
Representa una respuesta de texto que se debe enviar a través del sistema de chat.
Valor de programación opcional que se va a incluir en los mensajes generados.
openUrl
Se abre una dirección URL en el explorador integrado.
Dirección URL que se va a abrir.
playAudio
Reproduce audio.
La dirección URL del audio que se va a reproducir.
playVideo
Reproduce un vídeo.
La dirección URL del vídeo que se va a reproducir.
postBack
Envía un mensaje al bot y puede no publicar una respuesta visible en el chat.
Texto del mensaje para enviar.
showImage
Muestra una imagen.
La dirección URL de la imagen que se va a mostrar.
signin
Inicia un proceso de inicio de sesión de OAuth.
La dirección URL del flujo de OAuth que se va a iniciar.
Tarjeta de imagen prominente a través de varios tipos de evento
El código siguiente muestra ejemplos que usan varios eventos de tarjeta enriquecida.
Para obtener ejemplos de todas las tarjetas disponibles, consulte el ejemplo de uso de tarjetas.
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 obtener ejemplos de todas las tarjetas disponibles, consulte el ejemplo de uso de tarjetas.
createOAuthCard() {
return CardFactory.oauthCard(
'OAuth connection', // Replace with the name of your Azure AD connection
'Sign In',
'BotFramework OAuth Card'
);
}
Para obtener ejemplos de todas las tarjetas disponibles, consulte el ejemplo de uso de tarjetas.
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 obtener ejemplos de todas las tarjetas disponibles, consulte el ejemplo de uso de tarjetas.
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)
Envío de una tarjeta adaptable
Aunque puede usar el generador de mensajes para crear un mensaje que contenga datos adjuntos (de cualquier tipo), una tarjeta adaptable es un tipo específico de datos adjuntos. No todos los canales admiten Tarjetas adaptables, y algunos canales pueden admitir solo parcialmente Tarjetas adaptables. Por ejemplo, si envía una tarjeta adaptable en Facebook, los botones no funcionarán, pero los textos y las imágenes funcionan bien. La fábrica de mensajes es una clase auxiliar del SDK de Bot Framework que se utiliza para automatizar los pasos de creación por usted.
Las tarjetas adaptables son un formato abierto de intercambio de tarjetas que permite a los desarrolladores intercambiar contenido de la interfaz de usuario de una manera común y coherente. Sin embargo, no todos los canales admiten las tarjetas adaptables.
El diseñador de tarjetas adaptables ofrece una experiencia enriquecida e interactiva en tiempo de diseño para crear tarjetas adaptables.
Nota:
Debe probar esta característica con los canales que el bot usará para determinar si esos canales admiten las tarjetas adaptables.
Los mensajes también pueden incluir varios datos adjuntos en un diseño de carrusel, que coloca los datos adjuntos en paralelo y permite que el usuario se desplace por ellos.
El siguiente código fuente procede del ejemplo Uso de tarjetas.
Dialogs/MainDialog.cs
En primer lugar, se crea la respuesta y se definen los datos adjuntos como una 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);
A continuación, agregue los datos adjuntos y establezca el tipo de diseño en carrusel.
Aquí se agregan uno cada vez, pero, si quiere, puede manipular la lista para agregar las tarjetas.
// 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());
Una vez que se han agregado los datos adjuntos, puede enviar la respuesta como cualquier otra.
// Send the card(s) to the user as an attachment to the activity
await stepContext.Context.SendActivityAsync(reply, cancellationToken);
El siguiente código fuente procede del ejemplo Uso de tarjetas.
dialogs/mainDialog.js
Agregue los datos adjuntos y establezca el tipo de diseño en carrusel.
Una vez que se han agregado los datos adjuntos, puede enviar la respuesta como cualquier otra.
El siguiente código fuente procede del ejemplo Uso de tarjetas.
MainDialog.java
En primer lugar, se crea la respuesta y se definen los datos adjuntos como una 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);
A continuación, agregue los datos adjuntos y establezca el tipo de diseño en carrusel.
Aquí se agregan uno cada vez, pero, si quiere, puede manipular la lista para agregar las tarjetas.
// 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());
Una vez que se han agregado los datos adjuntos, puede enviar la respuesta como cualquier otra.
// Send the card(s) to the user as an attachment to the activity
return stepContext.getContext().sendActivity(reply)
El código fuente que se muestra a continuación se basa en el ejemplo Uso de tarjetas.
dialogs/main_dialog.py
En primer lugar, se crea la respuesta y se definen los datos adjuntos como una lista.
reply = MessageFactory.list([])
A continuación, agregue los datos adjuntos y establezca el tipo de diseño en carrusel.
Aquí se agregan uno cada vez, pero, si quiere, puede manipular la lista para agregar las tarjetas.
Una vez que se han agregado los datos adjuntos, puede enviar la respuesta como cualquier otra.
# Send the card(s) to the user as an attachment to the activity
await step_context.context.send_activity(reply)
Ejemplo de código para procesar la entrada de tarjeta adaptable
El siguiente ejemplo muestra una forma de usar las entradas de tarjeta adaptable dentro de una clase de cuadro de diálogo de bot.
Extiende el ejemplo .e las tarjetas de elemento principal actual mediante la validación de la entrada recibida en el campo de texto desde el cliente que responde.
Primero tiene que añadir la funcionalidad de entrada de texto y botón a la tarjeta adaptable existente añadiendo el siguiente código justo antes de la llave final de adaptiveCard.json, situada en la carpeta de recursos:
El identificador del campo de entrada de texto se establece en "text". Cuando el usuario selecciona Aceptar, el mensaje que genera la tarjeta adaptable tendrá una propiedad de valor que tiene una propiedad denominada text que contiene la información especificada por el usuario en el campo de entrada de texto de la tarjeta.
El validador usa Newtonsoft.json para convertirlo primero en un elemento JObject y, a continuación, crea una cadena de texto recortado para la comparación. Por tanto, agregue:
using System;
using System.Linq;
using Newtonsoft.Json.Linq;
a MainDialog.cs e instale el paquete de nuget estable más reciente de Newtonsoft.Json.
En el código del validador, agregamos el flujo de lógica a los comentarios del código.
Este método ChoiceValidator se coloca en el ejemplo Uso de tarjetas justo después de la llave de cierre pública para la declaración de 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;
}
Arriba en la declaración MainDialog, cambie:
// Define the main dialog and its related components.
AddDialog(new ChoicePrompt(nameof(ChoicePrompt)));
a:
// Define the main dialog and its related components.
AddDialog(new ChoicePrompt(nameof(ChoicePrompt), ChoiceValidator));
Esto invocará el validador para buscar entrada de tarjeta adaptable cada vez que se crea un nuevo elemento ChoicePrompt.
Abra mainDialog.js y busque el método async run(turnContext, accessor). Este método controla la actividad de entrada.
Justo después de la llamada dialogSet.add(this); añada lo siguiente:
// 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;
}
Si esta comprobación encuentra una entrada de texto inexistente del cliente, busca para ver si hay una entrada de una tarjeta adaptable.
Si ya existe una entrada de tarjeta adaptable en _activity.value.text, la copia en el campo de entrada de texto normal.
Nuestro validador usa el asistente de serialización de com.microsoft.bot.schema para convertirlo primero en JsonNode y, a continuación, crear una cadena de texto recortada para la comparación. También necesitaremos algunas otras importaciones para completar esto, así que agregue lo siguiente:
a MainDialog.java.
En el código del validador, agregamos el flujo de lógica a los comentarios del código.
Esta expresión PromptValidator se coloca en el ejemplo Uso de tarjetas justo después de la llave de cierre pública para la declaración de 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());
};
Arriba en la declaración MainDialog, cambie:
// Define the main dialog and its related components.
addDialog(new ChoicePrompt("ChoicePrompt"));
a:
// Define the main dialog and its related components.
addDialog(new ChoicePrompt("ChoicePrompt", validator, null));
Esto invocará el validador para buscar entrada de tarjeta adaptable cada vez que se crea un nuevo elemento ChoicePrompt.
Crea y envía una actividad con acciones sugeridas al usuario.
Este método choice_validator se coloca en la muestra Uso de tarjetas justo después de la llave de cierre pública para la declaración 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