/// <summary> /// Run a dialog purely by processing an activity and getting the result. /// </summary> /// <remarks> /// NOTE: does not support any activity semantic other then SendActivity. /// </remarks> /// <param name="activity">activity to process.</param> /// <param name="state">state to use.</param> /// <param name="cancellationToken">cancelation token.</param> /// <returns>result of the running the logic against the activity.</returns> public async Task <DialogManagerResult> RunAsync(Activity activity, PersistedState state = null, CancellationToken cancellationToken = default(CancellationToken)) { // Initialize context object var adapter = new DialogManagerAdapter(); var context = new TurnContext(adapter, activity); var result = await this.OnTurnAsync(context, state, cancellationToken).ConfigureAwait(false); result.Activities = adapter.Activities.ToArray(); return(result); }
public static async Task SaveStateAsync(IStorage storage, PersistedStateKeys keys, PersistedState newState, PersistedState oldState = null, string eTag = null) { // Check for state changes var save = false; Dictionary <string, object> changes = new Dictionary <string, object>(); if (oldState != null) { if (JsonConvert.SerializeObject(newState.UserState) != JsonConvert.SerializeObject(oldState.UserState)) { if (eTag != null) { newState.UserState[ETAG] = eTag; } changes[keys.UserState] = newState.UserState; save = true; } if (JsonConvert.SerializeObject(newState.ConversationState) != JsonConvert.SerializeObject(oldState.ConversationState)) { if (eTag != null) { newState.ConversationState[ETAG] = eTag; } changes[keys.ConversationState] = newState.ConversationState; save = true; } } else { if (eTag != null) { newState.UserState[ETAG] = eTag; newState.ConversationState[ETAG] = eTag; } changes[keys.UserState] = newState.UserState; changes[keys.ConversationState] = newState.ConversationState; save = true; } // Save changes if (save) { await storage.WriteAsync(changes).ConfigureAwait(false); } }
/// <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 }); } }