/// <summary> /// Called when the skill dialog is started and pushed onto the dialog stack. /// </summary> /// <param name="dc">The <see cref="DialogContext"/> for the current turn of conversation.</param> /// <param name="options">Optional, initial information to pass to the dialog.</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> /// <remarks>If the task is successful, the result indicates whether the dialog is still /// active after the turn has been processed by the dialog.</remarks> public override async Task <DialogTurnResult> BeginDialogAsync(DialogContext dc, object options = null, CancellationToken cancellationToken = default) { var dialogArgs = ValidateBeginDialogArgs(options); // Create deep clone of the original activity to avoid altering it before forwarding it. var skillActivity = ObjectPath.Clone(dialogArgs.Activity); // Apply conversation reference and common properties from incoming activity before sending. skillActivity.ApplyConversationReference(dc.Context.Activity.GetConversationReference(), true); // Store delivery mode and connection name in dialog state for later use. dc.ActiveDialog.State[DeliverModeStateKey] = dialogArgs.Activity.DeliveryMode; // Create the conversationId and store it in the dialog context state so we can use it later var skillConversationId = await CreateSkillConversationIdAsync(dc.Context, dc.Context.Activity, cancellationToken).ConfigureAwait(false); dc.ActiveDialog.State[SkillConversationIdStateKey] = skillConversationId; // Send the activity to the skill. var eocActivity = await SendToSkillAsync(dc.Context, skillActivity, skillConversationId, cancellationToken).ConfigureAwait(false); if (eocActivity != null) { return(await dc.EndDialogAsync(eocActivity.Value, cancellationToken).ConfigureAwait(false)); } return(EndOfTurn); }
/// <summary> /// Called when the skill dialog is _continued_, where it is the active dialog and the /// user replies with a new activity. /// </summary> /// <param name="dc">The <see cref="DialogContext"/> for the current turn of conversation.</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> /// <remarks>If the task is successful, the result indicates whether the dialog is still /// active after the turn has been processed by the dialog. The result may also contain a /// return value.</remarks> public override async Task <DialogTurnResult> ContinueDialogAsync(DialogContext dc, CancellationToken cancellationToken = default) { if (!OnValidateActivity(dc.Context.Activity)) { return(EndOfTurn); } // Handle EndOfConversation from the skill (this will be sent to the this dialog by the SkillHandler if received from the Skill) if (dc.Context.Activity.Type == ActivityTypes.EndOfConversation) { return(await dc.EndDialogAsync(dc.Context.Activity.Value, cancellationToken).ConfigureAwait(false)); } // Create deep clone of the original activity to avoid altering it before forwarding it. var skillActivity = ObjectPath.Clone(dc.Context.Activity); skillActivity.DeliveryMode = dc.ActiveDialog.State[DeliverModeStateKey] as string; var skillConversationId = (string)dc.ActiveDialog.State[SkillConversationIdStateKey]; // Just forward to the remote skill var eocActivity = await SendToSkillAsync(dc.Context, skillActivity, skillConversationId, cancellationToken).ConfigureAwait(false); if (eocActivity != null) { return(await dc.EndDialogAsync(eocActivity.Value, cancellationToken).ConfigureAwait(false)); } return(EndOfTurn); }
public override async Task <DialogTurnResult> ContinueDialogAsync(DialogContext dc, CancellationToken cancellationToken = default) { await dc.Context.TraceActivityAsync($"{GetType().Name}.ContinueDialogAsync()", label : $"ActivityType: {dc.Context.Activity.Type}", cancellationToken : cancellationToken).ConfigureAwait(false); // Handle EndOfConversation from the skill (this will be sent to the this dialog by the SkillHandler if received from the Skill) if (dc.Context.Activity.Type == ActivityTypes.EndOfConversation) { await dc.Context.TraceActivityAsync($"{GetType().Name}.ContinueDialogAsync()", label : $"Got {ActivityTypes.EndOfConversation}", cancellationToken : cancellationToken).ConfigureAwait(false); return(await dc.EndDialogAsync(dc.Context.Activity.Value, cancellationToken).ConfigureAwait(false)); } // Forward only Message and Event activities to the skill if (dc.Context.Activity.Type == ActivityTypes.Message || dc.Context.Activity.Type == ActivityTypes.Event) { // Create deep clone of the original activity to avoid altering it before forwarding it. var skillActivity = ObjectPath.Clone(dc.Context.Activity); skillActivity.DeliveryMode = dc.ActiveDialog.State[DeliverModeStateKey] as string; // Just forward to the remote skill var eocActivity = await SendToSkillAsync(dc.Context, skillActivity, cancellationToken).ConfigureAwait(false); if (eocActivity != null) { return(await dc.EndDialogAsync(eocActivity.Value, cancellationToken).ConfigureAwait(false)); } } return(EndOfTurn); }
public override async Task <DialogTurnResult> BeginDialogAsync(DialogContext dc, object options = null, CancellationToken cancellationToken = default) { var dialogArgs = ValidateBeginDialogArgs(options); await dc.Context.TraceActivityAsync($"{GetType().Name}.BeginDialogAsync()", label : $"Using activity of type: {dialogArgs.Activity.Type}", cancellationToken : cancellationToken).ConfigureAwait(false); // Create deep clone of the original activity to avoid altering it before forwarding it. var skillActivity = ObjectPath.Clone(dialogArgs.Activity); // Apply conversation reference and common properties from incoming activity before sending. skillActivity.ApplyConversationReference(dc.Context.Activity.GetConversationReference(), true); // Store delivery mode and connection name in dialog state for later use. dc.ActiveDialog.State[DeliverModeStateKey] = dialogArgs.Activity.DeliveryMode; // Send the activity to the skill. var eocActivity = await SendToSkillAsync(dc.Context, skillActivity, cancellationToken).ConfigureAwait(false); if (eocActivity != null) { return(await dc.EndDialogAsync(eocActivity.Value, cancellationToken).ConfigureAwait(false)); } return(EndOfTurn); }
public override async Task <DialogTurnResult> BeginDialogAsync(DialogContext dc, object options = null, CancellationToken cancellationToken = default) { var dialogArgs = ValidateBeginDialogOptions(options); await dc.Context.TraceActivityAsync($"{GetType().Name}.BeginDialogAsync()", label : $"Using activity of type: {dialogArgs.Activity.Type}", cancellationToken : cancellationToken).ConfigureAwait(false); // Store Skill information for this dialog instance await _activeSkillProperty.SetAsync(dc.Context, dialogArgs.Skill, cancellationToken).ConfigureAwait(false); // Create deep clone of the original activity to avoid altering it before forwarding it. var skillActivity = ObjectPath.Clone(dialogArgs.Activity); // Apply conversation reference and common properties from incoming activity before sending. skillActivity.ApplyConversationReference(dc.Context.Activity.GetConversationReference(), true); // Send the activity to the skill. await SendToSkillAsync(dc, skillActivity, dialogArgs.Skill, cancellationToken).ConfigureAwait(false); return(EndOfTurn); }
/// <summary> /// Called when the skill dialog is _continued_, where it is the active dialog and the /// user replies with a new activity. /// </summary> /// <param name="dc">The <see cref="DialogContext"/> for the current turn of conversation.</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> /// <remarks>If the task is successful, the result indicates whether the dialog is still /// active after the turn has been processed by the dialog. The result may also contain a /// return value.</remarks> public override async Task <DialogTurnResult> ContinueDialogAsync(DialogContext dc, CancellationToken cancellationToken = default) { // with adaptive dialogs, ResumeDialog is not called directly. Instead the Interrupted flag is set, which // acts as the signal to the SkillDialog to resume the skill. if (dc.State.TryGetValue <bool>(TurnPath.Interrupted, out bool interrupted) && interrupted) { // resume dialog execution dc.State.SetValue(TurnPath.Interrupted, false); return(await this.ResumeDialogAsync(dc, DialogReason.EndCalled, cancellationToken : cancellationToken).ConfigureAwait(false)); } if (!OnValidateActivity(dc.Context.Activity)) { return(EndOfTurn); } // Handle EndOfConversation from the skill (this will be sent to the this dialog by the SkillHandler if received from the Skill) if (dc.Context.Activity.Type == ActivityTypes.EndOfConversation) { return(await dc.EndDialogAsync(dc.Context.Activity.Value, cancellationToken).ConfigureAwait(false)); } // Create deep clone of the original activity to avoid altering it before forwarding it. var skillActivity = ObjectPath.Clone(dc.Context.Activity); skillActivity.DeliveryMode = dc.ActiveDialog.State[DeliverModeStateKey] as string; var skillConversationId = (string)dc.ActiveDialog.State[SkillConversationIdStateKey]; // Just forward to the remote skill var eocActivity = await SendToSkillAsync(dc.Context, skillActivity, skillConversationId, cancellationToken).ConfigureAwait(false); if (eocActivity != null) { return(await dc.EndDialogAsync(eocActivity.Value, cancellationToken).ConfigureAwait(false)); } return(EndOfTurn); }
/// <summary> /// Runs dialog system in the context of an ITurnContext. /// </summary> /// <param name="context">turn context.</param> /// <param name="state">stored state.</param> /// <param name="cancellationToken">cancelation token.</param> /// <returns>result of the running the logic against the activity.</returns> public async Task <DialogManagerResult> OnTurnAsync(ITurnContext context, PersistedState state = null, CancellationToken cancellationToken = default(CancellationToken)) { var saveState = false; var keys = GetKeys(context); var storage = context.TurnState.Get <IStorage>(); if (state == null) { if (storage == null) { throw new Exception("DialogManager: unable to load the bots state.Bot.storage not assigned."); } state = await LoadStateAsync(storage, keys).ConfigureAwait(false); saveState = true; } // Clone state to preserve original state var newState = ObjectPath.Clone(state); // Check for expired conversation var now = DateTime.UtcNow; if (this.ExpireAfter.HasValue && newState.ConversationState.ContainsKey(LASTACCESS)) { var lastAccess = DateTime.Parse(newState.ConversationState[LASTACCESS] as string); if ((DateTime.UtcNow - lastAccess) >= TimeSpan.FromMilliseconds((double)this.ExpireAfter)) { // Clear conversation state state.ConversationState = new Dictionary <string, object>(); state.ConversationState[ETAG] = newState.ConversationState[ETAG]; } } newState.ConversationState[LASTACCESS] = DateTime.UtcNow.ToString("u"); // Ensure dialog stack populated DialogState dialogState; if (!newState.ConversationState.ContainsKey(DIALOGS)) { dialogState = new DialogState(); newState.ConversationState[DIALOGS] = dialogState; } else { dialogState = (DialogState)newState.ConversationState[DIALOGS]; } var namedScopes = MemoryScope.GetScopesMemory(context); namedScopes[ScopePath.USER] = newState.UserState; namedScopes[ScopePath.CONVERSATION] = newState.ConversationState; namedScopes[ScopePath.TURN] = new Dictionary <string, object>(StringComparer.InvariantCultureIgnoreCase); // Create DialogContext var dc = new DialogContext( this.dialogSet, context, dialogState); DialogTurnResult turnResult = null; if (dc.ActiveDialog == null) { // start root dialog turnResult = await dc.BeginDialogAsync(this.rootDialogId, cancellationToken : cancellationToken).ConfigureAwait(false); } else { // Continue execution // - This will apply any queued up interruptions and execute the current/next step(s). turnResult = await dc.ContinueDialogAsync(cancellationToken : cancellationToken).ConfigureAwait(false); if (turnResult.Status == DialogTurnStatus.Empty) { // restart root dialog turnResult = await dc.BeginDialogAsync(this.rootDialogId, cancellationToken : cancellationToken).ConfigureAwait(false); } } // send trace of memory await dc.Context.SendActivityAsync((Activity)Activity.CreateTraceActivity("BotState", "https://www.botframework.com/schemas/botState", dc.State.GetMemorySnapshot(), "Bot State")).ConfigureAwait(false); // Save state if loaded from storage if (saveState) { await DialogManager.SaveStateAsync(storage, keys : keys, newState : newState, oldState : state, eTag : "*").ConfigureAwait(false); return(new DialogManagerResult() { TurnResult = turnResult }); } else { return(new DialogManagerResult() { TurnResult = turnResult, NewState = newState }); } }