/// <summary> /// Creates a child <see cref="DialogContext"/> for the given context. /// </summary> /// <param name="dc">The <see cref="DialogContext"/> for the current turn of conversation.</param> /// <returns>The child <see cref="DialogContext"/> or null if no <see cref="AdaptiveDialogState.Actions"/> are found for the given context.</returns> public override DialogContext CreateChildContext(DialogContext dc) { var activeDialogState = dc.ActiveDialog.State as Dictionary <string, object>; AdaptiveDialogState state = null; if (activeDialogState.TryGetValue(AdaptiveKey, out var currentState)) { state = currentState as AdaptiveDialogState; } if (state == null) { state = new AdaptiveDialogState(); activeDialogState[AdaptiveKey] = state; } if (state.Actions != null && state.Actions.Any()) { var childContext = new DialogContext(this.Dialogs, dc, state.Actions.First()); OnSetScopedServices(childContext); return(childContext); } return(null); }
private ActionContext ToActionContext(DialogContext dc) { var activeDialogState = dc.ActiveDialog.State as Dictionary <string, object>; var state = activeDialogState[AdaptiveKey] as AdaptiveDialogState; if (state == null) { state = new AdaptiveDialogState(); activeDialogState[AdaptiveKey] = state; } if (state.Actions == null) { state.Actions = new List <ActionState>(); } var actionContext = new ActionContext(dc.Dialogs, dc, new DialogState { DialogStack = dc.Stack }, state.Actions, changeTurnKey); actionContext.Parent = dc.Parent; return(actionContext); }
public override async Task <DialogTurnResult> BeginDialogAsync(DialogContext dc, object options = null, CancellationToken cancellationToken = default) { if (options is CancellationToken) { throw new ArgumentException($"{nameof(options)} should not ever be a cancellation token"); } EnsureDependenciesInstalled(); await this.CheckForVersionChangeAsync(dc, cancellationToken).ConfigureAwait(false); var cardId = CardId?.GetValue(dc.State); var activeDialogState = dc.ActiveDialog.State as Dictionary <string, object>; activeDialogState[AdaptiveKey] = new AdaptiveDialogState(); var properties = new Dictionary <string, string>() { { "DialogId", Id }, { "Kind", Kind } }; TelemetryClient.TrackEvent("AdaptiveCardDialogStart", properties); // select trigger if (!dc.State.TryGetValue <DialogEvent>("turn.dialogEvent", out DialogEvent dialogEvent)) { dialogEvent = new DialogEvent { Name = AdaptiveEvents.ActivityReceived, Value = dc.Context.Activity, Bubble = false }; // If AdaptiveCardDialog is root dialog there may not be a dialogEvent, and conditions are // looking for this dialogevent as a condition. dc.State.SetValue("turn.dialogEvent", dialogEvent); } var actionContext = ToActionContext(dc); var selection = await this.selector.SelectAsync(actionContext, cancellationToken).ConfigureAwait(false); if (selection.Any()) { var condition = selection[0]; await actionContext.DebuggerStepAsync(condition, dialogEvent, cancellationToken).ConfigureAwait(false); System.Diagnostics.Trace.TraceInformation($"Executing AdaptiveCardDialog: {Id} Rule[{condition.Id}]: {condition.GetType().Name}: {condition.GetExpression()}"); var changes = await condition.ExecuteAsync(actionContext); if (changes != null && changes.Any()) { actionContext.QueueChanges(changes[0]); await actionContext.ApplyChangesAsync(cancellationToken).ConfigureAwait(false); var actionDC = CreateChildContext(actionContext); // execute the sequence, the action should be an actionScope, so we simply start the actionAcope from the selected changelist. // NOTE: We don't do any of the changelist dialog management stuff because we are always single turn response with no dialog stack. var result = await actionDC.BeginDialogAsync(changes[0].Actions[0].DialogId); if (result.Status == DialogTurnStatus.Waiting) { throw new NotSupportedException("You can't wait in an invoke activity"); } } } // --- data bind the template ---- if (this.Template == null) { throw new Exception($"{this.Id}: a template was not provided or is not valid JSON."); } // Get data var data = this.Data?.GetValue(dc.State); if (data == null) { // template library barfs on dialogclass and class memory scopes because it tries to serialize them. data = dc.State.Where(kv => kv.Key != "dialogclass" && kv.Key != "class").ToDictionary(kv => kv.Key, kv2 => kv2.Value); } // bind the card and convert to JObject var cardJson = this.Template.Expand(data); var card = !String.IsNullOrEmpty(cardJson) ? JObject.Parse(cardJson) : null; // stamp dialogId and cardId on all Action.Execute nodes. // set data.cardId = cardId on Action.Execute Nodes foreach (var action in card.SelectTokens("$..[?(@.type=='Action.Execute')]").OfType <JObject>()) { ObjectPath.SetPathValue(action, "data.dialogId", this.Id); ObjectPath.SetPathValue(action, "data.cardId", cardId); } // Send invoke response (new card) var response = new JObject() { { "statusCode", 200 }, { "type", AdaptiveCard.ContentType }, { "value", card } }; var activity = new Activity(type: ActivityTypesEx.InvokeResponse, value: new InvokeResponse() { Status = 200, Body = response }); await dc.Context.SendActivityAsync(activity, cancellationToken).ConfigureAwait(false); return(await dc.EndDialogAsync(null, cancellationToken).ConfigureAwait(false)); }