Search code examples
c#botframework

How to fix a Bad Request while trying to display chat history with the BotFramework?


I'm trying to display the user's chat history with my bot so that when they come back they can see their previous conversations. When I'm trying the function "SendConversationHistory(conversationId, transcripts)" I keep getting a Bad Request (400) and the history is not displayed in the chatbot. Does anybody knows how to fix this?

I'm using BotFramework SDK v.3 in C#.

What I do is: 1. Checking if there is any conversation ID stored to be able to reconnect using that conversation ID 2. I send an event to the bot to restore the history 3. I'm able to fetch the previous activities and create the Transcript

After setting my transcript I tried this SendConversationHistory function which is not working.

I can chat with the bot even if this fails using the old conversation ID. I also tried to make the bot do a ReplyToActivity and this works.

I'm following a js sample (v4), this stackoverflow question and this thread about history in github but none of it helped to solve this issue.

MessageController.cs (in HandleSystemMessage)

else if (message.Name.Equals("restoreHistory"))
{
    string convId = message.Conversation.Id;
    List<Activity> activities = JsonConvert.DeserializeObject<List<Activity>>((string)message.Value);
    var incrementId = 0;

    if (message.Id.Contains("|"))
        int.TryParse(message.Id.Split('|')[1], out incrementId);

    foreach(var a in activities)
    {
        incrementId++;
        a.Id = string.Concat(convId, "|", incrementId.ToString().PadLeft(7, '0'));
        a.ChannelData = string.Empty;
    }

    if (activities != null && activities.Count > 0)
    {
        var connector = new ConnectorClient(new System.Uri(message.ServiceUrl));
        var transcripts = new Transcript(activities);
        connector.Conversations.SendConversationHistory(convId, transcripts);
    }
}

default.htm

var botConnection = new BotChat.DirectLine({
    secret: ...,
    conversationId: getPersistedConversationId(),
    token: params['t'],
    domain: params['domain'],
    webSocket: params['webSocket'] && params['webSocket'] === "true"
});
BotChat.App({
    botConnection: botConnection,
    user: { id: ... },
    bot: { id: ... },
    speechOptions: speechOptions
}, document.getElementById("BotChatGoesHere"));

botConnection.connectionStatus$
    .subscribe(function (connectionStatus) {
        switch (connectionStatus) {
            case 2:
                if (botConnection.conversationId === localStorage.getItem('conversationId')) {
                    botConnection.postActivity({
                        from: user,
                        type: 'event',
                        name: 'restoreHistory',
                        text: '',
                        value: localStorage.getItem('conversations')
                    }).subscribe(function (activityId) {
                        console.log('sending chat history.');
                    });
                } else {
                    saveConversationId(botConnection.conversationId);
                }
        }
    });

function saveConversationId(conversationId) {
    localStorage.setItem('conversationId', conversationId);
}
function getPersistedConversationId() {
    return localStorage.getItem('conversationId');
}

The errors I get are those ones:

Exception thrown: 'Microsoft.Rest.TransientFaultHandling.HttpRequestWithStatusException' in Microsoft.Rest.ClientRuntime.dll
Response status code indicates server error: 400 (BadRequest).

Activities' Json

[  
    {  
        "type":"message",
        "id":"6Tklwkie30H7oCI1eyWx9i-h|0000001",
        "timestamp":"2019-06-19T10:37:40.1175096Z",
        "channelId":"directline",
        "from":{  
            "id":"MyBotName",
            "name":"MyBotName"
        },
        "conversation":{  
            "id":"6Tklwkie30H7oCI1eyWx9i-h"
        },
        "text":"Hello, I'm MyBotName. How can I help you? To get started ask me a question.",
        "attachments":[  

        ],
        "entities":[  

        ],
        "replyToId":"7E6AtBm9iXL"
    },
    {  
        "type":"message",
        "id":"6Tklwkie30H7oCI1eyWx9i-h|0000003",
        "timestamp":"2019-06-19T10:37:43.4034223Z",
        "localTimestamp":"2019-06-19T10:37:43.2484146+00:00",
        "channelId":"directline",
        "from":{  
            "id":"MyBotName",
            "name":"MyBotName"
        },
        "conversation":{  
            "id":"6Tklwkie30H7oCI1eyWx9i-h"
        },
        "attachmentLayout":"carousel",
        "locale":"en-US",
        "text":"",
        "attachments":[  
            {  
                "contentType":"application/vnd.microsoft.card.hero",
                "content":{  
                    "title":"Title1",
                    "subtitle":"",
                    "text":"",
                    "images":[  
                        {  
                            "url":"http://..."
                        }
                    ],
                    "buttons":[  
                        {  
                            "type":"openUrl",
                            "title":"Button1",
                            "value":"https://..."
                        },
                        {  
                            "type":"openUrl",
                            "title":"Button2",
                            "value":"https://..."
                        }
                    ]
                }
            },
            {  
                "contentType":"application/vnd.microsoft.card.hero",
                "content":{  
                    "title":"Title2",
                    "subtitle":"",
                    "text":"",
                    "images":[  
                        {  
                            "url":"http://..."
                        }
                    ],
                    "buttons":[  
                        {  
                            "type":"openUrl",
                            "title":"Button1",
                            "value":"https://..."
                        },
                        {  
                            "type":"openUrl",
                            "title":"Button2",
                            "value":"https://..."
                        }
                    ]
                }
            },
            {  
                "contentType":"application/vnd.microsoft.card.hero",
                "content":{  
                    "title":"Title3",
                    "subtitle":"",
                    "text":"",
                    "images":[  
                        {  
                            "url":"http://..."
                        }
                    ],
                    "buttons":[  
                        {  
                            "type":"openUrl",
                            "title":"Button1",
                            "value":"https://..."
                        },
                        {  
                            "type":"openUrl",
                            "title":"Button2",
                            "value":"https://..."
                        }
                    ]
                }
            }
        ],
        "entities":[  

        ],
        "replyToId":"6Tklwkie30H7oCI1eyWx9i-h|0000000"
    },
    {  
        "type":"message",
        "id":"6Tklwkie30H7oCI1eyWx9i-h|0000000",
        "timestamp":"2019-06-19T10:37:33.8526334Z",
        "serviceUrl":"https://directline.botframework.com/",
        "channelId":"directline",
        "from":{  
            "id":"anonymous",
            "name":"Anonymous"
        },
        "conversation":{  
            "id":"6Tklwkie30H7oCI1eyWx9i-h"
        },
        "recipient":{  
            "id":"MyBotName@WI6mwV4z0jY",
            "name":"MyBotName"
        },
        "textFormat":"plain",
        "locale":"en-US",
        "text":"i'm looking for this",
        "entities":[  
            {  
                "type":"ClientCapabilities",
                "requiresBotState":true,
                "supportsTts":true,
                "supportsListening":true
            }
        ],
        "channelData":{  
            "clientActivityId":"1560940638882.6146258363791712.0"
        }
    }
]

Solution

  • If you're debugging in Visual Studio, you can click on "View Details" to see the whole exception object.

    View Details

    For an ErrorResponseException, you can see the actual error message that was returned in the HTTP response. In this case it says "Invalid activities" which makes it clear that something is wrong with the activities you're trying to send.

    Invalid activities

    It turns out you're not specifying the recipient of your activities. If you specify a recipient then you should get past your "BadRequest" exception.

    However: There are a few more gotchas you might run into. Even if you don't get an exception, there are some things that can make SendConversationHistory fail without error while providing no feedback for why your activities aren't showing up in the chat.

    Your strategy for populating the ID property is risky because your bot might send a message to the conversation before calling SendConversationHistory. If your bot had sent a message to the user during that same turn, it will have the same activity ID that you're trying to assign to the activity in the conversation history. This will cause a conflict and you won't see the activity show up. I recommend formatting the ID in some unique way that doesn't resemble the ID values that Direct Line automatically generates.

    I was able to send your activities successfully by modifying the code like this:

    foreach (var a in activities)
    {
        incrementId++;
        // Use your own ID format
        a.Id = string.Concat("history|", incrementId.ToString().PadLeft(7, '0'));
        a.ChannelData = null;
        a.Conversation = new ConversationAccount(id: convId);
    
        if (a.From.Name == message.Recipient.Name)
        {
            // If the activity was from the bot, assign the user as the recipient
            a.Recipient = message.From;
            a.From = message.Recipient;
        }
        else
        {
            // If the activity was from the user, assign the bot as the recipient
            a.Recipient = message.Recipient;
            a.From = message.From;
        }
    }