Botframework V4 webchat
My bot displays a SigninCard to the user, which when clicked user is redirected to an external website where the user will enter login credentials. This external website will perform a service call to a separate endpoint in my bot controller with a token. Now I want to make the bot to display a message to the user, and let the user follow through the rest of the conversation.
For all intents and purposes, it needs to be as same as displaying a proactive message to the user, and jumping to the next waterfall step afterwards.
This is my code so far.
1st approach
Conversation reference information is passed from here.
private async Task<DialogTurnResult> SigninStepStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var convRef = stepContext.Context.Activity.GetConversationReference();
var stateCol = System.Web.HttpUtility.ParseQueryString(string.Empty);
stateCol["userId"] = convRef.User.Id;
stateCol["botId"] = convRef.Bot.Id;
stateCol["conversationId"] = convRef.Conversation.Id;
stateCol["serviceUrl"] = convRef.ServiceUrl;
stateCol["channelId"] = convRef.ChannelId;
SigninCard signinCard = new SigninCard
{
Text = "Please sign-in to continue",
Buttons = new List<CardAction> {
new CardAction(ActionTypes.Signin, "Sign-in",
value: "http://some-external-url?state="+ System.Web.HttpUtility.UrlEncode(stateCol.ToString())) },
};
var reply = stepContext.Context.Activity.CreateReply();
reply.Attachments = new List<Attachment>
{
signinCard.ToAttachment(),
};
return await stepContext.PromptAsync(
"testprompt",
new PromptOptions
{
Prompt = reply,
},
cancellationToken);
}
Conversation reference information is received here. This endpoint is invoked by an external website.
[HttpGet]
public async Task<HttpResponseMessage> Callback(string state)
{
// Resume conversation
var stateCol = System.Web.HttpUtility.ParseQueryString(state);
ConnectorClient connector = new ConnectorClient(new Uri(stateCol["serviceUrl"]));
IMessageActivity newMessage = Activity.CreateMessageActivity();
newMessage.From = new ChannelAccount(stateCol["userId"]);
newMessage.Conversation = new ConversationAccount(id: stateCol["conversationId"]);
newMessage.Recipient = new ChannelAccount(stateCol["botId"]);
newMessage.ChannelId = stateCol["channelId"];
newMessage.Text = "hello user";
newMessage.ReplyToId = stateCol["botId"];
await connector.Conversations.SendToConversationAsync((Activity)newMessage);
//...
}
This piece of code correctly posts "Hello user" message to conversation flow, as a message coming from the bot. I tried connector.Conversations.ReplyTo method, also tried swapping botid and userid values to make it the user replying to the bot. But none of the cases made the waterfall step to jump to the next step in the flow like manually entering a reply text through emulator.
I followed the Idea 2 scenario of this question : https://stackoverflow.com/a/48977832 which is on botfraemwork v3. In botframework v3, it has a resume method as indicated in this answer. But I cannot find this method or anything that makes the conversation to resume, in botframework v4.
Conversation.ResumeAsync(reference, message);
Wondering if there is any special kind of activity that I can send, end the current prompt and make it start the next waterfall step.
2nd approach
On the side, I also tried to follow the ProactiveBot sample code in Botframework 4, to see if that approach will cause the waterfall flow to jump to the next step in the line.
[HttpGet]
public async Task<HttpResponseMessage> Callback(string code, string state, string session_state = null)
{
// Resume conversation
var stateCol = System.Web.HttpUtility.ParseQueryString(state);
MicrosoftAppCredentials.TrustServiceUrl(stateCol["serviceUrl"]);
ConversationReference cr = new ConversationReference();
cr.Bot = new ChannelAccount(stateCol["botId"]);
cr.User = new ChannelAccount(stateCol["userId"]);
cr.ChannelId = stateCol["channelId"];
cr.Conversation = new ConversationAccount(id: stateCol["conversationId"]);
cr.ServiceUrl = stateCol["serviceUrl"];
var msg = cr.GetContinuationActivity();
msg.Text = "login_succeed";
await ((BotAdapter)_adapter).ContinueConversationAsync(_appId, cr, BotCallback, default(CancellationToken));
//..
}
But at ContinueConversationAsync it threw below error, even though I am not using any clientSecret parameter anywhere. Not sure if this is because of using the Signin card to invoke the external web site url in the first code block above.
An unhandled exception occurred while processing the request. ArgumentNullException: Value cannot be null. (Parameter 'clientSecret') Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential..ctor(string clientId, string clientSecret)