My messages aren't sent in the right order in BotFramework - Java

faceID :

I have two main methods in my main controller, which are :

public synchronized void sendQuestion(Activity activity, PostQuestion pq) {
    Map<String, Object> map = allConversations.get(activity.getConversation().getId());
    map.put("command", pq.getQuestionId()); //It will run a command at next user input
    allConversations.put(activity.getConversation().getId(), map); //Store all conversation data
    sendMessage(activity, pq.getQuestionLabel()); //QuestionLabel is a string
}

public boolean sendMessage(Activity activity, String msg) {
    Activity reply = activity.createReply(msg);
    try {
        this.connector.getConversations()
                .sendToConversation(
                        activity.getConversation().getId(),
                        reply);
        return true;
    } catch (Exception e) {
        return false;
    }
}

My bot is a basic QnA bot. It retrieves data from APIs, and send the results. It asks questions after answers. My problem is : If I send for example two messages with sendMessage method, and then a message with sendQuestion (who send a message too):

sendMessage(activity, "Hello"); sendMessage(activity, "World!"); sendQuestion(activity, PostQuestion.askHello())

it happens sometimes that I have the following order "Message - Question (message) - Message" instead of "Message - Message - Question".

Is my problem about threads ? (I don't do multithreading in my application). I tried with synchronized keyword for my two methods, but my problem is still here.

Thanks for you help !

Kyle Delaney :

Since a bot is a web app and you'll be dealing with REST API calls etc., there's really no way to not have asynchrony. For example, every time you use sendToConversation to send a message from the bot to the user, that is an asynchronous operation. The Bot Builder Java SDK uses completable futures to handle asynchrony, and you should too. Have a look at the Welcome User sample to see how completable futures can be used to make sure your asynchronous operations execute one after another, waiting for each one to complete in turn:

/**
 * This will prompt for a user name, after which it will send info about the conversation.  After sending
 * information, the cycle restarts.
 *
 * @param turnContext The context object for this turn.
 * @return A future task.
 */
@Override
protected CompletableFuture<Void> onMessageActivity(TurnContext turnContext) {
    // Get state data from UserState.
    StatePropertyAccessor<WelcomeUserState> stateAccessor = userState.createProperty("WelcomeUserState");
    CompletableFuture<WelcomeUserState> stateFuture = stateAccessor.get(turnContext, WelcomeUserState::new);

    return stateFuture.thenApply(thisUserState -> {
        if (!thisUserState.getDidBotWelcomeUser()) {
            thisUserState.setDidBotWelcomeUser(true);

            String userName = turnContext.getActivity().getFrom().getName();
            return turnContext.sendActivities(
                MessageFactory.text(FIRST_WELCOME_ONE),
                MessageFactory.text(String.format(FIRST_WELCOME_TWO, userName))
            );
        } else {
            String text = turnContext.getActivity().getText().toLowerCase();
            switch (text) {
                case "hello":
                case "hi":
                    return turnContext.sendActivities(MessageFactory.text("You said " + text));

                case "intro":
                case "help":
                    return sendIntroCard(turnContext);

                default:
                    return turnContext.sendActivity(WELCOMEMESSAGE);
            }
        }
    })
        // make the return value happy.
        .thenApply(resourceResponse -> null);
}

Also remember that the Java SDK is still in preview and so you will risk running into problems even if you do everything right.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=323354&siteId=1