Esempio n. 1
0
        /// <summary>
        /// Passes a users reply to the dialog for further processing.The bot should keep calling
        /// 'continue()' for future turns until the dialog returns a completion object with
        /// 'isCompleted == true'. To cancel or interrupt the prompt simply delete the `state` object
        /// being persisted.
        /// </summary>
        /// <param name="context">Context for the current turn of the conversation with the user.</param>
        /// <param name="state">A state object that was previously initialized by a call to [begin()](#begin).</param>
        /// <returns>DialogCompletion result</returns>
        public async Task <DialogCompletion> Continue(ITurnContext context, IDictionary <string, object> state)
        {
            BotAssert.ContextNotNull(context);
            if (state == null)
            {
                throw new ArgumentNullException(nameof(state));
            }

            // Create empty dialog set and ourselves to it
            var dialogs = new DialogSet();

            dialogs.Add("dialog", (IDialog)this);

            // Continue the dialog
            IDictionary <string, object> result = null;
            var dc = new DialogContext(dialogs, context, state, (r) => { result = r; });

            if (dc.ActiveDialog != null)
            {
                await dc.Continue();

                return(dc.ActiveDialog != null
                        ?
                       new DialogCompletion {
                    IsActive = true, IsCompleted = false
                }
                        :
                       new DialogCompletion {
                    IsActive = false, IsCompleted = true, Result = result
                });
            }
            else
            {
                return(new DialogCompletion {
                    IsActive = false, IsCompleted = false
                });
            }
        }
Esempio n. 2
0
        public async Task <DialogResult> Begin(TurnContext context, object state, IDialogOptions options)
        {
            BotAssert.ContextNotNull(context);
            if (state == null)
            {
                throw new ArgumentNullException(nameof(state));
            }
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            // Create empty dialog set and ourselves to it
            var dialogs = new DialogSet();

            dialogs.Add("control", (IDialog)this);

            // Start the control
            var cdc = dialogs.CreateContext(context, state);
            await cdc.Begin("control", options.ApplyDefaults(_defaultOptions));

            return(cdc.DialogResult);
        }
        /// <summary>
        /// Creates a dialog stack and starts a dialog, pushing it onto the stack.
        /// </summary>
        /// <param name="dialog">The dialog to start.</param>
        /// <param name="turnContext">The context for the current turn of the conversation.</param>
        /// <param name="accessor">The <see cref="IStatePropertyAccessor{DialogState}"/> accessor
        /// with which to manage the state of the dialog stack.</param>
        /// <param name="cancellationToken">A cancellation token that can be used by other objects
        /// or threads to receive notice of cancellation.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        public static async Task RunAsync(this Dialog dialog, ITurnContext turnContext, IStatePropertyAccessor <DialogState> accessor, CancellationToken cancellationToken)
        {
            var dialogSet = new DialogSet(accessor)
            {
                TelemetryClient = dialog.TelemetryClient
            };

            dialogSet.Add(dialog);

            var dialogContext = await dialogSet.CreateContextAsync(turnContext, cancellationToken).ConfigureAwait(false);

            if (turnContext.TurnState.Get <IIdentity>(BotAdapter.BotIdentityKey) is ClaimsIdentity claimIdentity && SkillValidation.IsSkillClaim(claimIdentity.Claims))
            {
                // The bot is running as a skill.
                if (turnContext.Activity.Type == ActivityTypes.EndOfConversation && dialogContext.Stack.Any())
                {
                    // Handle remote cancellation request if we have something in the stack.
                    var activeDialogContext = GetActiveDialogContext(dialogContext);

                    var remoteCancelText = "Skill was canceled by a request from the host.";
                    await turnContext.TraceActivityAsync($"{typeof(Dialog).Name}.RunAsync()", label : $"{remoteCancelText}", cancellationToken : cancellationToken).ConfigureAwait(false);

                    // Send cancellation message to the top dialog in the stack to ensure all the parents are canceled in the right order.
                    await activeDialogContext.CancelAllDialogsAsync(true, cancellationToken : cancellationToken).ConfigureAwait(false);
                }
                else
                {
                    // Process a reprompt event sent from the parent.
                    if (turnContext.Activity.Type == ActivityTypes.Event && turnContext.Activity.Name == DialogEvents.RepromptDialog && dialogContext.Stack.Any())
                    {
                        await dialogContext.RepromptDialogAsync(cancellationToken).ConfigureAwait(false);

                        return;
                    }

                    // Run the Dialog with the new message Activity and capture the results so we can send end of conversation if needed.
                    var result = await dialogContext.ContinueDialogAsync(cancellationToken).ConfigureAwait(false);

                    if (result.Status == DialogTurnStatus.Empty)
                    {
                        var startMessageText = $"Starting {dialog.Id}.";
                        await turnContext.TraceActivityAsync($"{typeof(Dialog).Name}.RunAsync()", label : $"{startMessageText}", cancellationToken : cancellationToken).ConfigureAwait(false);

                        result = await dialogContext.BeginDialogAsync(dialog.Id, null, cancellationToken).ConfigureAwait(false);
                    }

                    // Send end of conversation if it is completed or cancelled.
                    if (result.Status == DialogTurnStatus.Complete || result.Status == DialogTurnStatus.Cancelled)
                    {
                        var endMessageText = $"Dialog {dialog.Id} has **completed**. Sending EndOfConversation.";
                        await turnContext.TraceActivityAsync($"{typeof(Dialog).Name}.RunAsync()", label : $"{endMessageText}", value : result.Result, cancellationToken : cancellationToken).ConfigureAwait(false);

                        // Send End of conversation at the end.
                        var activity = new Activity(ActivityTypes.EndOfConversation)
                        {
                            Value = result.Result
                        };
                        await turnContext.SendActivityAsync(activity, cancellationToken).ConfigureAwait(false);
                    }
                }
            }
        /// <summary>
        /// Creates a dialog stack and starts a dialog, pushing it onto the stack.
        /// </summary>
        /// <param name="dialog">The dialog to start.</param>
        /// <param name="turnContext">The context for the current turn of the conversation.</param>
        /// <param name="accessor">The <see cref="IStatePropertyAccessor{DialogState}"/> accessor
        /// with which to manage the state of the dialog stack.</param>
        /// <param name="cancellationToken">A cancellation token that can be used by other objects
        /// or threads to receive notice of cancellation.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        public static async Task RunAsync(this Dialog dialog, ITurnContext turnContext, IStatePropertyAccessor <DialogState> accessor, CancellationToken cancellationToken)
        {
            var dialogSet = new DialogSet(accessor)
            {
                TelemetryClient = dialog.TelemetryClient
            };

            dialogSet.Add(dialog);

            var dialogContext = await dialogSet.CreateContextAsync(turnContext, cancellationToken).ConfigureAwait(false);

            // Handle EoC and Reprompt event from a parent bot (can be root bot to skill or skill to skill)
            if (IsFromParentToSkill(turnContext))
            {
                // Handle remote cancellation request from parent.
                if (turnContext.Activity.Type == ActivityTypes.EndOfConversation)
                {
                    if (!dialogContext.Stack.Any())
                    {
                        // No dialogs to cancel, just return.
                        return;
                    }

                    var activeDialogContext = GetActiveDialogContext(dialogContext);

                    // Send cancellation message to the top dialog in the stack to ensure all the parents are canceled in the right order.
                    await activeDialogContext.CancelAllDialogsAsync(true, cancellationToken : cancellationToken).ConfigureAwait(false);

                    return;
                }

                // Handle a reprompt event sent from the parent.
                if (turnContext.Activity.Type == ActivityTypes.Event && turnContext.Activity.Name == DialogEvents.RepromptDialog)
                {
                    if (!dialogContext.Stack.Any())
                    {
                        // No dialogs to reprompt, just return.
                        return;
                    }

                    await dialogContext.RepromptDialogAsync(cancellationToken).ConfigureAwait(false);

                    return;
                }
            }

            // Continue or start the dialog.
            var result = await dialogContext.ContinueDialogAsync(cancellationToken).ConfigureAwait(false);

            if (result.Status == DialogTurnStatus.Empty)
            {
                result = await dialogContext.BeginDialogAsync(dialog.Id, null, cancellationToken).ConfigureAwait(false);
            }

            // Skills should send EoC when the dialog completes.
            if (result.Status == DialogTurnStatus.Complete || result.Status == DialogTurnStatus.Cancelled)
            {
                if (SendEoCToParent(turnContext))
                {
                    // Send End of conversation at the end.
                    var code     = result.Status == DialogTurnStatus.Complete ? EndOfConversationCodes.CompletedSuccessfully : EndOfConversationCodes.UserCancelled;
                    var activity = new Activity(ActivityTypes.EndOfConversation)
                    {
                        Value = result.Result, Locale = turnContext.Activity.Locale, Code = code
                    };
                    await turnContext.SendActivityAsync(activity, cancellationToken).ConfigureAwait(false);
                }
            }
        }