/// <summary> /// Process an incoming message within the conversation. /// </summary> /// <remarks> /// This method: /// 1. instantiates and composes the required components /// 2. deserializes the dialog state (the dialog stack and each dialog's state) from the <see cref="toBot"/> <see cref="Message"/> /// 3. resumes the conversation processes where the dialog suspended to wait for a <see cref="Message"/> /// 4. queues <see cref="Message"/>s to be sent to the user /// 5. serializes the updated dialog state in the messages to be sent to the user. /// /// The <see cref="MakeRoot"/> factory method is invoked for new conversations only, /// because existing conversations have the dialog stack and state serialized in the <see cref="Message"/> data. /// </remarks> /// <param name="toBot">The message sent to the bot.</param> /// <param name="MakeRoot">The factory method to make the root dialog.</param> /// <param name="token">The cancellation token.</param> /// <param name="singletons">An optional list of object instances that should not be serialized.</param> /// <returns>A task that represents the message to send inline back to the user.</returns> public static async Task <Message> SendAsync(Message toBot, Func <IDialog> MakeRoot, CancellationToken token = default(CancellationToken), params object[] singletons) { IWaitFactory waits = new WaitFactory(); IFrameFactory frames = new FrameFactory(waits); IBotData botData = new JObjectBotData(toBot); IConnectorClient client = new ConnectorClient(); var botToUser = new ReactiveBotToUser(toBot, client); var provider = new Serialization.SimpleServiceLocator(singletons) { waits, frames, botData, botToUser }; var formatter = Conversation.MakeBinaryFormatter(provider); IDialogContextStore contextStore = new DialogContextStore(formatter); IDialogContextStore store = new ErrorResilientDialogContextStore(contextStore); IDialogContext context; if (!store.TryLoad(botData.PerUserInConversationData, BlobKey, out context)) { IFiberLoop fiber = new Fiber(frames); context = new Internals.DialogContext(botToUser, botData, fiber); var root = MakeRoot(); var loop = root.Loop(); context.Call <object>(loop, null); await fiber.PollAsync(); } IUserToBot userToBot = (IUserToBot)context; await userToBot.SendAsync(toBot, token); store.Save(context, botData.PerUserInConversationData, BlobKey); return(botToUser.ToUser); }
public static async Task <IBotData> GetBotDataAsync(IAddress userAddress, IBotDataStore <BotData> botDataStore, CancellationToken cancellationToken) { var botData = new JObjectBotData(userAddress, botDataStore); await botData.LoadAsync(cancellationToken); return(botData); }
public static async Task <DialogContext> MakeContextAsync(IDialog root, IBotToUser botToUser) { var data = new JObjectBotData(new Connector.Message()); IFiberLoop fiber = new Fiber(new FrameFactory(new WaitFactory())); var context = new DialogContext(botToUser, data, fiber); var loop = Methods.Void(Methods.Loop(context.ToRest(root.StartAsync), int.MaxValue)); fiber.Call(loop, null); await fiber.PollAsync(); return(context); }
public async Task <HttpResponseMessage> OAuthCallback([FromUri] string userId, [FromUri] string conversationId, [FromUri] string code, [FromUri] string state) { // Check if the bot is running against emulator var connectorType = HttpContext.Current.Request.IsLocal ? ConnectorType.Emulator : ConnectorType.Cloud; // Exchange the Facebook Auth code with Access toekn var token = await FacebookHelpers.ExchangeCodeForAccessToken(userId, conversationId, code, SimpleFacebookAuthDialog.FacebookOauthCallback.ToString()); // Create the message that is send to conversation to resume the login flow var msg = new Message { Text = $"token:{token.AccessToken}", From = new ChannelAccount { Id = userId }, To = new ChannelAccount { Id = botId.Value }, ConversationId = conversationId }; // Resume the conversation to SimpleFacebookAuthDialog var reply = await Conversation.ResumeAsync(botId.Value, userId, conversationId, msg, connectorType : connectorType); // Remove the pending message because login flow is complete IBotData dataBag = new JObjectBotData(reply); PendingMessage pending; if (dataBag.PerUserInConversationData.TryGetValue("pendingMessage", out pending)) { dataBag.PerUserInConversationData.RemoveValue("pendingMessage"); var pendingMessage = pending.GetMessage(); reply.To = pendingMessage.From; reply.From = pendingMessage.To; // Send the login success asynchronously to user var client = Conversation.ResumeContainer.Resolve <IConnectorClient>(TypedParameter.From(connectorType)); await client.Messages.SendMessageAsync(reply); return(Request.CreateResponse("You are now logged in! Continue talking to the bot.")); } else { // Callback is called with no pending message as a result the login flow cannot be resumed. return(Request.CreateErrorResponse(HttpStatusCode.BadRequest, new InvalidOperationException("Cannot resume!"))); } }
public async Task <HttpResponseMessage> OAuthCallback([FromUri] string userId, [FromUri] string conversationId, [FromUri] string channelId, [FromUri] string language, [FromUri] string code, [FromUri] string state) { // Get the resumption cookie var resumptionCookie = new ResumptionCookie(userId, botId.Value, conversationId, channelId, language); // Exchange the Facebook Auth code with Access token var token = await FacebookHelpers.ExchangeCodeForAccessToken(resumptionCookie, code, SimpleFacebookAuthDialog.FacebookOauthCallback.ToString()); // Create the message that is send to conversation to resume the login flow var msg = resumptionCookie.GetMessage(); msg.Text = $"token:{token.AccessToken}"; // Resume the conversation to SimpleFacebookAuthDialog var reply = await Conversation.ResumeAsync(resumptionCookie, msg); // Remove the pending message because login flow is complete IBotData dataBag = new JObjectBotData(reply); ResumptionCookie pending; if (dataBag.PerUserInConversationData.TryGetValue("persistedCookie", out pending)) { dataBag.PerUserInConversationData.RemoveValue("persistedCookie"); using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, reply)) { // make sure that we have the right Channel info for the outgoing message var persistedCookie = pending.GetMessage(); reply.To = persistedCookie.From; reply.From = persistedCookie.To; // Send the login success asynchronously to user var client = scope.Resolve <IConnectorClient>(); await client.Messages.SendMessageAsync(reply); } return(Request.CreateResponse("You are now logged in! Continue talking to the bot.")); } else { // Callback is called with no pending message as a result the login flow cannot be resumed. return(Request.CreateErrorResponse(HttpStatusCode.BadRequest, new InvalidOperationException("Cannot resume!"))); } }