Core Bot formatting (#1045)
* Core Bot formatting * Simplified Core Bot Appliation DI
This commit is contained in:
Родитель
3b23c785a8
Коммит
2e577c2383
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче