Search code examples
c#azurebotframeworkdirect-line-botframework

BotFramework Message Controller set variable via Backchannel


I am trying to receive an int number (1-4) via Backchannel and then hand it over to the first dialog.

My message controller looks like this:

       private int option = 1;
    /// <summary>
    /// POST: api/Messages
    /// Receive a message from a user and reply to it
    /// </summary>
    public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
    {
        if (activity.Type == ActivityTypes.Message)
        {
            try
            {

                var connector = new ConnectorClient(new Uri(activity.ServiceUrl));
                Activity isTypingReply = activity.CreateReply();
                isTypingReply.Type = ActivityTypes.Typing;
                await connector.Conversations.ReplyToActivityAsync(isTypingReply);

                await Conversation.SendAsync(activity, () => new Dialogs.MenuDialog(option));


            }
            catch (Exception e)
            {
                //SendEmail(e);
            }
        }
        else
        {
            await HandleSystemMessage(activity);
        }
        var response = Request.CreateResponse(HttpStatusCode.OK);
        return response;
    }

    private async Task HandleSystemMessage(Activity message)
    {
        if (message.Type == ActivityTypes.DeleteUserData)
        {
            // Implement user deletion here
            // If we handle user deletion, return a real message
        }
        else if (message.Type == ActivityTypes.ConversationUpdate)
        {

        }
        else if (message.Type == ActivityTypes.ContactRelationUpdate)
        {
            // Handle add/remove from contact lists
            // Activity.From + Activity.Action represent what happened
        }
        else if (message.Type == ActivityTypes.Typing)
        {
            // Handle knowing tha the user is typing
        }
        else if (message.Type == ActivityTypes.Ping)
        {
        }
        else if (message.Type == ActivityTypes.Event && message.Name == "option")
        {

           // var reply = message.CreateReply();
            //reply.Text = message.Value.ToString();
           // ConnectorClient connector = new ConnectorClient(new Uri(message.ServiceUrl));
           // await connector.Conversations.ReplyToActivityAsync(reply);

            if (message.Value.ToString() == "1")
            {
                option = 1;

            }
            else if (message.Value.ToString() == "2")
            {
                option = 2;
            }
            else if (message.Value.ToString() == "3")
            {
                option = 3;
            }
            else if (message.Value.ToString() == "4")
            {
                option = 4;
            }
            else
            {
                option = 1;
            }

        }


        return;
    }

The Backchannel method is called right and the option value is set when I print it at the end of the function. But then when the first message comes the Bot is always using the default "1" value. It was working before but now it stopped working and I don't understand why.


Solution

  • private int option = 1;
    

    Is scoped to the MessageController, and will be refreshed on every call. You can use PrivateConversationData to preserve the 'option' between the Event and Message calls:

    public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
    {
        if (activity.Type == ActivityTypes.Message)
        {
            //retrieve the option value before processing the message
            string optionValue = string.Empty;
            using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity))
            {
                var botData = scope.Resolve<IBotData>();
                await botData.LoadAsync(CancellationToken.None);
                optionValue = botData.PrivateConversationData.GetValue<string>("option");
            }
    
            await Conversation.SendAsync(activity, () => new ParameterizedRootDialog(optionValue));
    
        }
        else if (activity.Type == ActivityTypes.Event)
        {
            var eventActivity = activity.AsEventActivity();
            if (string.Equals(eventActivity.Name, "option", StringComparison.InvariantCultureIgnoreCase))
            {
                //save the option into PrivateConversationData
                string optionValue = eventActivity.Value.ToString();
                using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity))
                {
                    var botData = scope.Resolve<IBotData>();
                    await botData.LoadAsync(CancellationToken.None);
                    botData.PrivateConversationData.SetValue("option", optionValue);
                    await botData.FlushAsync(CancellationToken.None);
                }                    
            }
        }
    
        return Request.CreateResponse(HttpStatusCode.OK);
    }
    

    Also of note: with this method, it is not necessary to send in the option to the dialog as a parameter. You could retrieve the value from within the dialog itself, using IDialogContext.PrivateConversationData. Like this:

    var optionFromContext = context.PrivateConversationData.GetValue<string>("option");