* Core Bot formatting

* Simplified Core Bot Appliation DI
This commit is contained in:
tracyboehrer 2021-03-10 09:21:38 -06:00 коммит произвёл GitHub
Родитель 3b23c785a8
Коммит 2e577c2383
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
10 изменённых файлов: 198 добавлений и 160 удалений

Просмотреть файл

@ -89,11 +89,6 @@
<artifactId>bot-dialogs</artifactId>
<version>4.6.0-preview9</version>
</dependency>
<dependency>
<groupId>com.microsoft.bot</groupId>
<artifactId>bot-ai-qna</artifactId>
<version>4.6.0-preview9</version>
</dependency>
<dependency>
<groupId>com.microsoft.bot</groupId>
<artifactId>bot-ai-luis-v3</artifactId>

Просмотреть файл

@ -5,9 +5,7 @@ package com.microsoft.bot.sample.core;
import com.microsoft.bot.builder.Bot;
import com.microsoft.bot.builder.ConversationState;
import com.microsoft.bot.builder.Storage;
import com.microsoft.bot.builder.UserState;
import com.microsoft.bot.dialogs.Dialog;
import com.microsoft.bot.integration.AdapterWithErrorHandler;
import com.microsoft.bot.integration.BotFrameworkHttpAdapter;
import com.microsoft.bot.integration.Configuration;
@ -36,8 +34,10 @@ import org.springframework.context.annotation.Import;
* override methods in order to provide custom implementations.
*/
public class Application extends BotDependencyConfiguration {
/**
* The start method.
*
* @param args The args.
*/
public static void main(String[] args) {
@ -48,56 +48,21 @@ public class Application extends BotDependencyConfiguration {
* Returns the Bot for this application.
*
* <p>
* The @Component annotation could be used on the Bot class instead of this method
* with the @Bean annotation.
* The @Component annotation could be used on the Bot class instead of this method with the
* @Bean annotation.
* </p>
*
* @return The Bot implementation for this application.
*/
@Bean
public Bot getBot(
Configuration configuration,
UserState userState,
ConversationState conversationState
) {
Storage storage = this.getStorage();
UserState userState = this.getUserState(storage);
ConversationState conversationState = this.getConversationState(storage);
Dialog rootDialog = this.getRootDialog();
return new DialogAndWelcomeBot(conversationState, userState, rootDialog);
}
/**
* Returns a FlightBookingRecognizer object.
* @return The FlightBookingRecognizer.
*/
@Bean
public FlightBookingRecognizer getFlightBookingRecognizer() {
Configuration configuration = this.getConfiguration();
return new FlightBookingRecognizer(configuration);
}
/**
* Returns a BookingDialog object.
* @return The BookingDialog.
*/
@Bean
public BookingDialog getBookingDialog() {
return new BookingDialog();
}
/**
* Returns the starting Dialog for this application.
*
* <p>
* The @Component annotation could be used on the Dialog class instead of this method
* with the @Bean annotation.
* </p>
*
* @return The Dialog implementation for this application.
*/
@Bean
public Dialog getRootDialog() {
FlightBookingRecognizer flightBookingRecognizer = this.getFlightBookingRecognizer();
BookingDialog bookingDialog = this.getBookingDialog();
return new MainDialog(flightBookingRecognizer, bookingDialog);
FlightBookingRecognizer recognizer = new FlightBookingRecognizer(configuration);
MainDialog dialog = new MainDialog(recognizer, new BookingDialog());
return new DialogAndWelcomeBot<>(conversationState, userState, dialog);
}
/**

Просмотреть файл

@ -7,12 +7,14 @@ package com.microsoft.bot.sample.core;
* The model class to retrieve the information of the booking.
*/
public class BookingDetails {
private String destination;
private String origin;
private String travelDate;
/**
* Gets the destination of the booking.
*
* @return The destination.
*/
public String getDestination() {
@ -22,6 +24,7 @@ public class BookingDetails {
/**
* Sets the destination of the booking.
*
* @param withDestination The new destination.
*/
public void setDestination(String withDestination) {
@ -30,6 +33,7 @@ public class BookingDetails {
/**
* Gets the origin of the booking.
*
* @return The origin.
*/
public String getOrigin() {
@ -38,6 +42,7 @@ public class BookingDetails {
/**
* Sets the origin of the booking.
*
* @param withOrigin The new origin.
*/
public void setOrigin(String withOrigin) {
@ -46,6 +51,7 @@ public class BookingDetails {
/**
* Gets the travel date of the booking.
*
* @return The travel date.
*/
public String getTravelDate() {
@ -54,6 +60,7 @@ public class BookingDetails {
/**
* Sets the travel date of the booking.
*
* @param withTravelDate The new travel date.
*/
public void setTravelDate(String withTravelDate) {

Просмотреть файл

@ -23,6 +23,7 @@ import java.util.concurrent.CompletableFuture;
* The class containing the booking dialogs.
*/
public class BookingDialog extends CancelAndHelpDialog {
private final String destinationStepMsgText = "Where would you like to travel to?";
private final String originStepMsgText = "Where are you traveling from?";
@ -54,7 +55,9 @@ public class BookingDialog extends CancelAndHelpDialog {
if (bookingDetails.getDestination().isEmpty()) {
Activity promptMessage =
MessageFactory.text(destinationStepMsgText, destinationStepMsgText, InputHints.EXPECTING_INPUT);
MessageFactory.text(destinationStepMsgText, destinationStepMsgText,
InputHints.EXPECTING_INPUT
);
PromptOptions promptOptions = new PromptOptions();
promptOptions.setPrompt(promptMessage);
return stepContext.prompt("TextPrompt", promptOptions);
@ -71,7 +74,8 @@ public class BookingDialog extends CancelAndHelpDialog {
if (bookingDetails.getOrigin().isEmpty()) {
Activity promptMessage =
MessageFactory.text(originStepMsgText, originStepMsgText, InputHints.EXPECTING_INPUT);
MessageFactory
.text(originStepMsgText, originStepMsgText, InputHints.EXPECTING_INPUT);
PromptOptions promptOptions = new PromptOptions();
promptOptions.setPrompt(promptMessage);
return stepContext.prompt("TextPrompt", promptOptions);
@ -100,9 +104,13 @@ public class BookingDialog extends CancelAndHelpDialog {
bookingDetails.setTravelDate(stepContext.getResult().toString());
String messageText =
String.format("Please confirm, I have you traveling to: %s from: %s on: %s. Is this correct?",
bookingDetails.getDestination(), bookingDetails.getOrigin(), bookingDetails.getTravelDate());
Activity promptMessage = MessageFactory.text(messageText, messageText, InputHints.EXPECTING_INPUT);
String.format(
"Please confirm, I have you traveling to: %s from: %s on: %s. Is this correct?",
bookingDetails.getDestination(), bookingDetails.getOrigin(),
bookingDetails.getTravelDate()
);
Activity promptMessage = MessageFactory
.text(messageText, messageText, InputHints.EXPECTING_INPUT);
PromptOptions promptOptions = new PromptOptions();
promptOptions.setPrompt(promptMessage);
@ -114,7 +122,6 @@ public class BookingDialog extends CancelAndHelpDialog {
private CompletableFuture<DialogTurnResult> finalStep(WaterfallStepContext stepContext) {
if ((Boolean) stepContext.getResult()) {
BookingDetails bookingDetails = (BookingDetails) stepContext.getOptions();
return stepContext.endDialog(bookingDetails);
}

Просмотреть файл

@ -18,11 +18,13 @@ import java.util.concurrent.CompletableFuture;
* The class in charge of the dialog interruptions.
*/
public class CancelAndHelpDialog extends ComponentDialog {
private final String helpMsgText = "Show help here";
private final String cancelMsgText = "Cancelling...";
/**
* The constructor of the CancelAndHelpDialog class.
*
* @param id The dialog's Id.
*/
public CancelAndHelpDialog(String id) {
@ -30,13 +32,13 @@ public class CancelAndHelpDialog extends ComponentDialog {
}
/**
* Called when the dialog is _continued_, where it is the active dialog and the
* user replies with a new activity.
* Called when the dialog is _continued_, where it is the active dialog and the user replies
* with a new activity.
*
* @param innerDc innerDc The inner {@link DialogContext} for the current turn of conversation.
* @return A {@link CompletableFuture} representing the asynchronous operation.
* If the task is successful, the result indicates whether the dialog is
* still active after the turn has been processed by the dialog. The
* result may also contain a return value.
* @return A {@link CompletableFuture} representing the asynchronous operation. If the task is
* successful, the result indicates whether the dialog is still active after the turn has been
* processed by the dialog. The result may also contain a return value.
*/
@Override
protected CompletableFuture<DialogTurnResult> onContinueDialog(DialogContext innerDc) {
@ -55,16 +57,19 @@ public class CancelAndHelpDialog extends ComponentDialog {
switch (text) {
case "help":
case "?":
Activity helpMessage = MessageFactory.text(helpMsgText, helpMsgText, InputHints.EXPECTING_INPUT);
Activity helpMessage = MessageFactory
.text(helpMsgText, helpMsgText, InputHints.EXPECTING_INPUT);
return innerDc.getContext().sendActivity(helpMessage)
.thenCompose(sendResult ->
CompletableFuture.completedFuture(new DialogTurnResult(DialogTurnStatus.WAITING)));
CompletableFuture
.completedFuture(new DialogTurnResult(DialogTurnStatus.WAITING)));
case "cancel":
case "quit":
Activity cancelMessage = MessageFactory
.text(cancelMsgText, cancelMsgText, InputHints.IGNORING_INPUT);
return innerDc.getContext()
.sendActivity(cancelMessage).thenCompose(sendResult -> innerDc.cancelAllDialogs());
.sendActivity(cancelMessage)
.thenCompose(sendResult -> innerDc.cancelAllDialogs());
default:
break;
}

Просмотреть файл

@ -75,16 +75,12 @@ public class DateResolverDialog extends CancelAndHelpDialog {
return stepContext.prompt("DateTimePrompt", promptOptions);
}
DateTimeResolution dateTimeResolution = new DateTimeResolution() {
{
setTimex(timex);
}
};
List<DateTimeResolution> dateTimeResolutions = new ArrayList<DateTimeResolution>() {
{
add(dateTimeResolution);
}
};
DateTimeResolution dateTimeResolution = new DateTimeResolution() {{
setTimex(timex);
}};
List<DateTimeResolution> dateTimeResolutions = new ArrayList<DateTimeResolution>() {{
add(dateTimeResolution);
}};
return stepContext.next(dateTimeResolutions);
}
@ -93,8 +89,9 @@ public class DateResolverDialog extends CancelAndHelpDialog {
return stepContext.endDialog(timex);
}
private static CompletableFuture<Boolean> dateTimePromptValidator(PromptValidatorContext<List<DateTimeResolution>>
promptContext) {
private static CompletableFuture<Boolean> dateTimePromptValidator(
PromptValidatorContext<List<DateTimeResolution>> promptContext
) {
if (promptContext.getRecognized().getSucceeded()) {
// This value will be a TIMEX. And we are only interested in a Date so grab the first result and drop the
// Time part. TIMEX is a format that represents DateTime expressions that include some ambiguity.

Просмотреть файл

@ -24,30 +24,38 @@ import java.util.concurrent.CompletableFuture;
/**
* The class containing the welcome dialog.
*
* @param <T> is a Dialog.
*/
public class DialogAndWelcomeBot<T extends Dialog> extends DialogBot {
/**
* Creates a DialogBot.
*
* @param withConversationState ConversationState to use in the bot
* @param withUserState UserState to use
* @param withDialog Param inheriting from Dialog class
*/
public DialogAndWelcomeBot(ConversationState withConversationState, UserState withUserState, T withDialog) {
public DialogAndWelcomeBot(
ConversationState withConversationState, UserState withUserState, T withDialog
) {
super(withConversationState, withUserState, withDialog);
}
/**
* When the {@link #onConversationUpdateActivity(TurnContext)} method receives a
* conversation update activity that indicates one or more users other than the
* bot are joining the conversation, it calls this method.
* @param membersAdded A list of all the members added to the conversation,
* as described by the conversation update activity
* @param turnContext The context object for this turn.
* When the {@link #onConversationUpdateActivity(TurnContext)} method receives a conversation
* update activity that indicates one or more users other than the bot are joining the
* conversation, it calls this method.
*
* @param membersAdded A list of all the members added to the conversation, as described by the
* conversation update activity
* @param turnContext The context object for this turn.
* @return A task that represents the work queued to execute.
*/
@Override
protected CompletableFuture<Void> onMembersAdded(List<ChannelAccount> membersAdded, TurnContext turnContext) {
protected CompletableFuture<Void> onMembersAdded(
List<ChannelAccount> membersAdded, TurnContext turnContext
) {
return turnContext.getActivity().getMembersAdded().stream()
.filter(member -> !StringUtils
.equals(member.getId(), turnContext.getActivity().getRecipient().getId()))
@ -55,10 +63,13 @@ public class DialogAndWelcomeBot<T extends Dialog> extends DialogBot {
// Greet anyone that was not the target (recipient) of this message.
// To learn more about Adaptive Cards, see https://aka.ms/msbot-adaptivecards for more details.
Attachment welcomeCard = createAdaptiveCardAttachment();
Activity response = MessageFactory.attachment(welcomeCard, null, "Welcome to Bot Framework!", null);
Activity response = MessageFactory
.attachment(welcomeCard, null, "Welcome to Bot Framework!", null);
return turnContext.sendActivity(response).thenApply(sendResult -> {
return Dialog.run(getDialog(), turnContext, getConversationState().createProperty("DialogState"));
return Dialog.run(getDialog(), turnContext,
getConversationState().createProperty("DialogState")
);
});
})
.collect(CompletableFutures.toFutureList())
@ -67,14 +78,17 @@ public class DialogAndWelcomeBot<T extends Dialog> extends DialogBot {
// Load attachment from embedded resource.
private Attachment createAdaptiveCardAttachment() {
try (InputStream inputStream = Thread.currentThread().
getContextClassLoader().getResourceAsStream("cards/welcomeCard.json")) {
String adaptiveCardJson = IOUtils.toString(inputStream, StandardCharsets.UTF_8.toString());
try (
InputStream inputStream = Thread.currentThread().
getContextClassLoader().getResourceAsStream("cards/welcomeCard.json")
) {
String adaptiveCardJson = IOUtils
.toString(inputStream, StandardCharsets.UTF_8.toString());
return new Attachment() {{
setContentType("application/vnd.microsoft.card.adaptive");
setContent(Serialization.jsonToTree(adaptiveCardJson));
}};
return new Attachment() {{
setContentType("application/vnd.microsoft.card.adaptive");
setContent(Serialization.jsonToTree(adaptiveCardJson));
}};
} catch (IOException e) {
e.printStackTrace();

Просмотреть файл

@ -14,15 +14,17 @@ import org.slf4j.LoggerFactory;
import java.util.concurrent.CompletableFuture;
/**
* This Bot implementation can run any type of Dialog. The use of type parameterization is to allow multiple
* different bots to be run at different endpoints within the same project. This can be achieved by defining
* distinct Controller types each with dependency on distinct Bot types. The ConversationState is used by
* the Dialog system. The UserState isn't, however, it might have been used in a Dialog implementation,
* and the requirement is that all BotState objects are saved at the end of a turn.
* This Bot implementation can run any type of Dialog. The use of type parameterization is to allow
* multiple different bots to be run at different endpoints within the same project. This can be
* achieved by defining distinct Controller types each with dependency on distinct Bot types. The
* ConversationState is used by the Dialog system. The UserState isn't, however, it might have been
* used in a Dialog implementation, and the requirement is that all BotState objects are saved at
* the end of a turn.
*
* @param <T> parameter of a type inheriting from Dialog
*/
public class DialogBot<T extends Dialog> extends ActivityHandler {
private Dialog dialog;
private BotState conversationState;
private BotState userState;
@ -83,11 +85,14 @@ public class DialogBot<T extends Dialog> extends ActivityHandler {
/**
* Creates a DialogBot.
*
* @param withConversationState ConversationState to use in the bot
* @param withUserState UserState to use
* @param withDialog Param inheriting from Dialog class
* @param withUserState UserState to use
* @param withDialog Param inheriting from Dialog class
*/
public DialogBot(ConversationState withConversationState, UserState withUserState, T withDialog) {
public DialogBot(
ConversationState withConversationState, UserState withUserState, T withDialog
) {
this.conversationState = withConversationState;
this.userState = withUserState;
this.dialog = withDialog;
@ -95,6 +100,7 @@ public class DialogBot<T extends Dialog> extends ActivityHandler {
/**
* Saves the BotState objects at the end of each turn.
*
* @param turnContext
* @return
*/
@ -107,6 +113,7 @@ public class DialogBot<T extends Dialog> extends ActivityHandler {
/**
* This method is executed when the turnContext receives a message activity.
*
* @param turnContext
* @return
*/

Просмотреть файл

@ -21,10 +21,12 @@ import java.util.concurrent.CompletableFuture;
* The class in charge of recognizing the booking information.
*/
public class FlightBookingRecognizer implements Recognizer {
private LuisRecognizer recognizer;
/**
* The constructor of the FlightBookingRecognizer class.
*
* @param configuration The Configuration object to use.
*/
public FlightBookingRecognizer(Configuration configuration) {
@ -35,11 +37,13 @@ public class FlightBookingRecognizer implements Recognizer {
LuisApplication luisApplication = new LuisApplication(
configuration.getProperty("LuisAppId"),
configuration.getProperty("LuisAPIKey"),
String.format("https://%s", configuration.getProperty("LuisAPIHostName")));
String.format("https://%s", configuration.getProperty("LuisAPIHostName"))
);
// Set the recognizer options depending on which endpoint version you want to use.
// More details can be found in
// https://docs.microsoft.com/en-gb/azure/cognitive-services/luis/luis-migration-api-v3
LuisRecognizerOptionsV3 recognizerOptions = new LuisRecognizerOptionsV3(luisApplication) {
LuisRecognizerOptionsV3 recognizerOptions = new LuisRecognizerOptionsV3(
luisApplication) {
{
setIncludeInstanceData(true);
}
@ -51,6 +55,7 @@ public class FlightBookingRecognizer implements Recognizer {
/**
* Verify if the recognizer is configured.
*
* @return True if it's configured, False if it's not.
*/
public Boolean isConfigured() {
@ -59,6 +64,7 @@ public class FlightBookingRecognizer implements Recognizer {
/**
* Return an object with preformatted LUIS results for the bot's dialogs to consume.
*
* @param context A {link TurnContext}
* @return A {link RecognizerResult}
*/
@ -69,16 +75,20 @@ public class FlightBookingRecognizer implements Recognizer {
/**
* Gets the From data from the entities which is part of the result.
*
* @param result The recognizer result.
* @return The object node representing the From data.
*/
public ObjectNode getFromEntities(RecognizerResult result) {
String fromValue = "", fromAirportValue = "";
if (result.getEntities().get("$instance").get("From") != null) {
fromValue = result.getEntities().get("$instance").get("From").get(0).get("text").asText();
fromValue = result.getEntities().get("$instance").get("From").get(0).get("text")
.asText();
}
if (!fromValue.isEmpty() && result.getEntities().get("From").get(0).get("Airport") != null) {
fromAirportValue = result.getEntities().get("From").get(0).get("Airport").get(0).get(0).asText();
if (!fromValue.isEmpty()
&& result.getEntities().get("From").get(0).get("Airport") != null) {
fromAirportValue = result.getEntities().get("From").get(0).get("Airport").get(0).get(0)
.asText();
}
ObjectMapper mapper = new ObjectMapper().findAndRegisterModules();
@ -90,6 +100,7 @@ public class FlightBookingRecognizer implements Recognizer {
/**
* Gets the To data from the entities which is part of the result.
*
* @param result The recognizer result.
* @return The object node representing the To data.
*/
@ -99,7 +110,8 @@ public class FlightBookingRecognizer implements Recognizer {
toValue = result.getEntities().get("$instance").get("To").get(0).get("text").asText();
}
if (!toValue.isEmpty() && result.getEntities().get("To").get(0).get("Airport") != null) {
toAirportValue = result.getEntities().get("To").get(0).get("Airport").get(0).get(0).asText();
toAirportValue = result.getEntities().get("To").get(0).get("Airport").get(0).get(0)
.asText();
}
ObjectMapper mapper = new ObjectMapper().findAndRegisterModules();
@ -110,8 +122,10 @@ public class FlightBookingRecognizer implements Recognizer {
}
/**
* This value will be a TIMEX. And we are only interested in a Date so grab the first result and drop the Time part.
* TIMEX is a format that represents DateTime expressions that include some ambiguity. e.g. missing a Year.
* This value will be a TIMEX. And we are only interested in a Date so grab the first result and
* drop the Time part. TIMEX is a format that represents DateTime expressions that include some
* ambiguity. e.g. missing a Year.
*
* @param result A {link RecognizerResult}
* @return The Timex value without the Time model
*/
@ -132,6 +146,7 @@ public class FlightBookingRecognizer implements Recognizer {
/**
* Runs an utterance through a recognizer and returns a generic recognizer result.
*
* @param turnContext Turn context.
* @return Analysis of utterance.
*/

Просмотреть файл

@ -30,13 +30,15 @@ import org.apache.commons.lang3.StringUtils;
* The class containing the main dialog for the sample.
*/
public class MainDialog extends ComponentDialog {
private final FlightBookingRecognizer luisRecognizer;
private final Integer plusDayValue = 7;
/**
* The constructor of the Main Dialog class.
*
* @param withLuisRecognizer The FlightBookingRecognizer object.
* @param bookingDialog The BookingDialog object with booking dialogs.
* @param bookingDialog The BookingDialog object with booking dialogs.
*/
public MainDialog(FlightBookingRecognizer withLuisRecognizer, BookingDialog bookingDialog) {
super("MainDialog");
@ -57,9 +59,10 @@ public class MainDialog extends ComponentDialog {
}
/**
* First step in the waterfall dialog. Prompts the user for a command.
* Currently, this expects a booking request, like "book me a flight from Paris to Berlin on march 22"
* Note that the sample LUIS model will only recognize Paris, Berlin, New York and London as airport cities.
* First step in the waterfall dialog. Prompts the user for a command. Currently, this expects a
* booking request, like "book me a flight from Paris to Berlin on march 22" Note that the
* sample LUIS model will only recognize Paris, Berlin, New York and London as airport cities.
*
* @param stepContext A {@link WaterfallStepContext}
* @return A {@link DialogTurnResult}
*/
@ -78,16 +81,19 @@ public class MainDialog extends ComponentDialog {
String messageText = stepContext.getOptions() != null
? stepContext.getOptions().toString()
: String.format("What can I help you with today?\n"
+ "Say something like \"Book a flight from Paris to Berlin on %s\"", weekLaterDate);
Activity promptMessage = MessageFactory.text(messageText, messageText, InputHints.EXPECTING_INPUT);
+ "Say something like \"Book a flight from Paris to Berlin on %s\"", weekLaterDate);
Activity promptMessage = MessageFactory
.text(messageText, messageText, InputHints.EXPECTING_INPUT);
PromptOptions promptOptions = new PromptOptions();
promptOptions.setPrompt(promptMessage);
return stepContext.prompt("TextPrompt", promptOptions);
}
/**
* Second step in the waterfall. This will use LUIS to attempt to extract the origin, destination and travel dates.
* Then, it hands off to the bookingDialog child dialog to collect any remaining details.
* Second step in the waterfall. This will use LUIS to attempt to extract the origin,
* destination and travel dates. Then, it hands off to the bookingDialog child dialog to collect
* any remaining details.
*
* @param stepContext A {@link WaterfallStepContext}
* @return A {@link DialogTurnResult}
*/
@ -106,53 +112,65 @@ public class MainDialog extends ComponentDialog {
ObjectNode toEntities = luisRecognizer.getToEntities(luisResult);
// Show a warning for Origin and Destination if we can't resolve them.
return showWarningForUnsupportedCities(stepContext.getContext(), fromEntities, toEntities)
return showWarningForUnsupportedCities(
stepContext.getContext(), fromEntities, toEntities)
.thenCompose(showResult -> {
// Initialize BookingDetails with any entities we may have found in the response.
// Initialize BookingDetails with any entities we may have found in the response.
BookingDetails bookingDetails = new BookingDetails();
bookingDetails.setDestination(toEntities.get("airport").asText());
bookingDetails.setOrigin(fromEntities.get("airport").asText());
bookingDetails.setTravelDate(luisRecognizer.getTravelDate(luisResult));
// Run the BookingDialog giving it whatever details we have from the LUIS call,
// it will fill out the remainder.
return stepContext.beginDialog("BookingDialog", bookingDetails);
}
);
BookingDetails bookingDetails = new BookingDetails();
bookingDetails.setDestination(toEntities.get("airport").asText());
bookingDetails.setOrigin(fromEntities.get("airport").asText());
bookingDetails.setTravelDate(luisRecognizer.getTravelDate(luisResult));
// Run the BookingDialog giving it whatever details we have from the LUIS call,
// it will fill out the remainder.
return stepContext.beginDialog("BookingDialog", bookingDetails);
}
);
case "GetWeather":
// We haven't implemented the GetWeatherDialog so we just display a TODO message.
String getWeatherMessageText = "TODO: get weather flow here";
Activity getWeatherMessage = MessageFactory
.text(getWeatherMessageText, getWeatherMessageText, InputHints.IGNORING_INPUT);
stepContext.getContext().sendActivity(getWeatherMessage);
break;
.text(
getWeatherMessageText, getWeatherMessageText,
InputHints.IGNORING_INPUT
);
return stepContext.getContext().sendActivity(getWeatherMessage)
.thenCompose(resourceResponse -> stepContext.next(null));
default:
// Catch all for unhandled intents
String didntUnderstandMessageText = String.format("Sorry, I didn't get that. Please "
+ " try asking in a different way (intent was %s)", luisResult.getTopScoringIntent().intent);
String didntUnderstandMessageText = String.format(
"Sorry, I didn't get that. Please "
+ " try asking in a different way (intent was %s)",
luisResult.getTopScoringIntent().intent
);
Activity didntUnderstandMessage = MessageFactory
.text(didntUnderstandMessageText, didntUnderstandMessageText, InputHints.IGNORING_INPUT);
stepContext.getContext().sendActivity(didntUnderstandMessage);
break;
.text(
didntUnderstandMessageText, didntUnderstandMessageText,
InputHints.IGNORING_INPUT
);
return stepContext.getContext().sendActivity(didntUnderstandMessage)
.thenCompose(resourceResponse -> stepContext.next(null));
}
return stepContext.next(null);
});
}
/**
* Shows a warning if the requested From or To cities are recognized as entities
* but they are not in the Airport entity list.
* In some cases LUIS will recognize the From and To composite entities as a valid cities
* but the From and To Airport values
* will be empty if those entity values can't be mapped to a canonical item in the Airport.
* @param turnContext A {@link WaterfallStepContext}
* Shows a warning if the requested From or To cities are recognized as entities but they are
* not in the Airport entity list. In some cases LUIS will recognize the From and To composite
* entities as a valid cities but the From and To Airport values will be empty if those entity
* values can't be mapped to a canonical item in the Airport.
*
* @param turnContext A {@link WaterfallStepContext}
* @param fromEntities An ObjectNode with the entities of From object
* @param toEntities An ObjectNode with the entities of To object
* @param toEntities An ObjectNode with the entities of To object
* @return A task
*/
private static CompletableFuture<Void> showWarningForUnsupportedCities(TurnContext turnContext,
ObjectNode fromEntities,
ObjectNode toEntities) {
private static CompletableFuture<Void> showWarningForUnsupportedCities(
TurnContext turnContext,
ObjectNode fromEntities,
ObjectNode toEntities
) {
List<String> unsupportedCities = new ArrayList<String>();
if (StringUtils.isNotBlank(fromEntities.get("from").asText())
@ -166,41 +184,49 @@ public class MainDialog extends ComponentDialog {
}
if (!unsupportedCities.isEmpty()) {
String messageText = String.format("Sorry but the following airports are not supported: %s",
String.join(", ", unsupportedCities));
Activity message = MessageFactory.text(messageText, messageText, InputHints.IGNORING_INPUT);
turnContext.sendActivity(message).thenApply(sendResult -> null);
String messageText = String.format(
"Sorry but the following airports are not supported: %s",
String.join(", ", unsupportedCities)
);
Activity message = MessageFactory
.text(messageText, messageText, InputHints.IGNORING_INPUT);
return turnContext.sendActivity(message)
.thenApply(sendResult -> null);
}
return CompletableFuture.completedFuture(null);
}
/**
* This is the final step in the main waterfall dialog.
* It wraps up the sample "book a flight" interaction with a simple confirmation.
* This is the final step in the main waterfall dialog. It wraps up the sample "book a flight"
* interaction with a simple confirmation.
*
* @param stepContext A {@link WaterfallStepContext}
* @return A {@link DialogTurnResult}
*/
private CompletableFuture<DialogTurnResult> finalStep(WaterfallStepContext stepContext) {
CompletableFuture<Void> stepResult = CompletableFuture.completedFuture(null);
// If the child dialog ("BookingDialog") was cancelled,
// the user failed to confirm or if the intent wasn't BookFlight
// the Result here will be null.
if (stepContext.getResult() instanceof BookingDetails) {
// Now we have all the booking details call the booking service.
// If the call to the booking service was successful tell the user.
BookingDetails result = (BookingDetails) stepContext.getResult();
TimexProperty timeProperty = new TimexProperty(result.getTravelDate());
String travelDateMsg = timeProperty.toNaturalLanguage(LocalDateTime.now());
String messageText = String.format("I have you booked to %s from %s on %s",
result.getDestination(), result.getOrigin(), travelDateMsg);
Activity message = MessageFactory.text(messageText, messageText, InputHints.IGNORING_INPUT);
stepContext.getContext().sendActivity(message).thenApply(sendResult -> null);
result.getDestination(), result.getOrigin(), travelDateMsg
);
Activity message = MessageFactory
.text(messageText, messageText, InputHints.IGNORING_INPUT);
stepResult = stepContext.getContext().sendActivity(message).thenApply(sendResult -> null);
}
// Restart the main dialog with a different message the second time around
String promptMessage = "What else can I do for you?";
return stepContext.replaceDialog(getInitialDialogId(), promptMessage);
return stepResult
.thenCompose(result -> stepContext.replaceDialog(getInitialDialogId(), promptMessage));
}
}