2
votes

I am developing a chat bot using the bot framework in Azure, I am currently trying to use my chat bots response as a user trigger word to store the users question in table storage, for example when the bot responds with "I’m Sorry, I don’t have an answer for you. Please try and rephrase your question" it logs the users first question for example "How do I fly?".

Any help with this would be much appreciated!

1
when the bot responds with "I’m Sorry, I don’t have an answer for you. Please try and rephrase your question" it logs the users first question for example "How do I fly?". Do you integrate the bot application with QnA maker service? When and what condition would your bot respond with that specific message?Fei Han
Yes I do, the bot would respond with this message when the knowledge base does not contain a response to the user query. I would like to save this so that I can add the users query when the bot does not find a response. @FeiHanuser10190803

1 Answers

1
votes

This is one way to accomplish this. You can store every question text in a Dictionary and send it to permanent storage if the query is not correctly answered.

First, create a static dictionary to hold the values:

public static class Utils
{
    public static Dictionary<string, string> MessageDictionary = new Dictionary<string, string>();
}

Second, in your messages controller, you can store every message from every user when you bot receives it like this:

    public async Task<HttpResponseMessage> Post([FromBody] Activity activity)
    {
        if (activity.Type == ActivityTypes.Message)
        {
            var userId = activity.From.Id;
            var message = activity.Text;
            if (!Utils.MessageDictionary.ContainsKey(userId))
            {
                ConnectorClient connector = new ConnectorClient(new System.Uri(activity.ServiceUrl));
                var reply = activity.CreateReply();

                //save all incoming messages to a dictionary
                Utils.MessageDictionary.Add(userId, message);

                // this can be removed it just confirms it was saved
                reply.Text = $"Message saved {userId} - {Utils.MessageDictionary[userId]}";
                await connector.Conversations.ReplyToActivityAsync(reply);
            }
            await Conversation.SendAsync(activity, () => new Dialogs.RootDialog());
        }

        else
        {
            HandleSystemMessage(activity);
        }

        var response = Request.CreateResponse(HttpStatusCode.OK);
        return response;
    }

Nest create a class that inherits from IBotToUser that can intercept a message before it goes out to the user. Here we are going to save the message to permanent storage if the text returned tothe user is the text you provided "I’m Sorry, I don’t have an answer for you. Please try and rephrase your question":

public sealed class CustomBotToUser : IBotToUser
{
    private readonly IBotToUser inner;
    private readonly IConnectorClient client;

    public CustomBotToUser(IBotToUser inner, IConnectorClient client)
    {
        SetField.NotNull(out this.inner, nameof(inner), inner);
        SetField.NotNull(out this.client, nameof(client), client);
    }


    public async Task PostAsync(IMessageActivity message,
        CancellationToken cancellationToken = default(CancellationToken))
    {
        if (message.Text == "I’m Sorry, I don’t have an answer for you. Please try and rephrase your question")
        {
            //save to permanant storage here
            //if you would like to use a database
            //I have a very simple database bot example here
            //https://github.com/JasonSowers/DatabaseBotExample
        }

        //user is the recipient
        var userId = message.Recipient.Id;

        //remove entry from dictionary
        Utils.MessageDictionary.Remove(userId);

        //this is just for testing purposes and can be removed
        try
        {
            await inner.PostAsync($"{userId} - {Utils.MessageDictionary[userId]}");
        }
        catch (Exception e)
        {
            await inner.PostAsync($"No entry found for {userId}");
        }
        await inner.PostAsync((Activity) message, cancellationToken);
    }

    public IMessageActivity MakeMessage()
    {
        return inner.MakeMessage();
    }
}

You will also need to register this class in your Global.asax using Autofac making the Aplication_Start() method look something like this:

    protected void Application_Start()
    {
        GlobalConfiguration.Configure(WebApiConfig.Register);

        Conversation.UpdateContainer(
        builder =>
        {
            builder.RegisterModule(new AzureModule(Assembly.GetExecutingAssembly()));

            // Bot Storage: Here we register the state storage for your bot. 
            // Default store: volatile in-memory store - Only for prototyping!
            // We provide adapters for Azure Table, CosmosDb, SQL Azure, or you can implement your own!
            // For samples and documentation, see: [https://github.com/Microsoft/BotBuilder-Azure](https://github.com/Microsoft/BotBuilder-Azure)
            var store = new InMemoryDataStore();

            // Other storage options
            // var store = new TableBotDataStore("...DataStorageConnectionString..."); // requires Microsoft.BotBuilder.Azure Nuget package 
            // var store = new DocumentDbBotDataStore("cosmos db uri", "cosmos db key"); // requires Microsoft.BotBuilder.Azure Nuget package 

            builder.Register(c => store)
                .Keyed<IBotDataStore<BotData>>(AzureModule.Key_DataStore)
                .AsSelf()
                .SingleInstance();

            builder
                .RegisterType<CustomBotToUser>()
                .Keyed<IBotToUser>(typeof(LogBotToUser));
        });
    }

where this is the important part for the Global.asax code I am sharing:

        builder
            .RegisterType<CustomBotToUser>()
            .Keyed<IBotToUser>(typeof(LogBotToUser));