in libraries/Microsoft.Bot.Builder.Dialogs.Adaptive/AdaptiveDialog.cs [447:646]
protected virtual async Task<bool> ProcessEventAsync(ActionContext actionContext, DialogEvent dialogEvent, bool preBubble, CancellationToken cancellationToken = default(CancellationToken))
{
// Save into turn
actionContext.State.SetValue(TurnPath.DialogEvent, dialogEvent);
var activity = actionContext.State.GetValue<Activity>(TurnPath.Activity);
// some dialogevents get promoted into turn state for general access outside of the dialogevent.
// This allows events to be fired (in the case of ChooseIntent), or in interruption (Activity)
// Triggers all expressed against turn.recognized or turn.activity, and this mapping maintains that
// any event that is emitted updates those for the rest of rule evaluation.
switch (dialogEvent.Name)
{
case AdaptiveEvents.RecognizedIntent:
{
// we have received a RecognizedIntent event
// get the value and promote to turn.recognized, topintent,topscore and lastintent
var recognizedResult = actionContext.State.GetValue<RecognizerResult>($"{TurnPath.DialogEvent}.value");
// #3572 set these here too (Even though the emitter may have set them) because this event can be emitted by declarative code.
var (name, score) = recognizedResult.GetTopScoringIntent();
actionContext.State.SetValue(TurnPath.Recognized, recognizedResult);
actionContext.State.SetValue(TurnPath.TopIntent, name);
actionContext.State.SetValue(TurnPath.TopScore, score);
actionContext.State.SetValue(DialogPath.LastIntent, name);
// process entities for ambiguity processing (We do this regardless of who handles the event)
ProcessEntities(actionContext, activity);
break;
}
case AdaptiveEvents.ActivityReceived:
{
// We received an ActivityReceived event, promote the activity into turn.activity
actionContext.State.SetValue(TurnPath.Activity, dialogEvent.Value);
activity = ObjectPath.GetPathValue<Activity>(dialogEvent, "Value");
break;
}
}
EnsureDependenciesInstalled();
// Count of events processed
var count = actionContext.State.GetValue<uint>(DialogPath.EventCounter);
actionContext.State.SetValue(DialogPath.EventCounter, ++count);
// Look for triggered evt
var handled = await QueueFirstMatchAsync(actionContext, dialogEvent, cancellationToken).ConfigureAwait(false);
if (handled)
{
return true;
}
// Default processing
if (preBubble)
{
switch (dialogEvent.Name)
{
case AdaptiveEvents.BeginDialog:
if (actionContext.State.GetBoolValue(TurnPath.ActivityProcessed) == false)
{
// Emit leading ActivityReceived event
var activityReceivedEvent = new DialogEvent()
{
Name = AdaptiveEvents.ActivityReceived,
Value = actionContext.Context.Activity,
Bubble = false
};
handled = await ProcessEventAsync(actionContext, dialogEvent: activityReceivedEvent, preBubble: true, cancellationToken: cancellationToken).ConfigureAwait(false);
}
break;
case AdaptiveEvents.ActivityReceived:
if (activity.Type == ActivityTypes.Message)
{
// Recognize utterance (ignore handled)
var recognizeUtteranceEvent = new DialogEvent
{
Name = AdaptiveEvents.RecognizeUtterance,
Value = activity,
Bubble = false
};
await ProcessEventAsync(actionContext, dialogEvent: recognizeUtteranceEvent, preBubble: true, cancellationToken: cancellationToken).ConfigureAwait(false);
// Emit leading RecognizedIntent event
var recognized = actionContext.State.GetValue<RecognizerResult>(TurnPath.Recognized);
var recognizedIntentEvent = new DialogEvent
{
Name = AdaptiveEvents.RecognizedIntent,
Value = recognized,
Bubble = false
};
handled = await ProcessEventAsync(actionContext, dialogEvent: recognizedIntentEvent, preBubble: true, cancellationToken: cancellationToken).ConfigureAwait(false);
}
// Has an interruption occured?
// - Setting this value to true causes any running inputs to re-prompt when they're
// continued. The developer can clear this flag if they want the input to instead
// process the users uterrance when its continued.
if (handled)
{
actionContext.State.SetValue(TurnPath.Interrupted, true);
}
break;
case AdaptiveEvents.RecognizeUtterance:
{
if (activity.Type == ActivityTypes.Message)
{
// Recognize utterance
var recognizedResult = await OnRecognizeAsync(actionContext, activity, cancellationToken).ConfigureAwait(false);
// TODO figure out way to not use turn state to pass this value back to caller.
actionContext.State.SetValue(TurnPath.Recognized, recognizedResult);
// Bug #3572 set these here, because if allowedInterruption is true then event is not emitted, but folks still want the value.
var (name, score) = recognizedResult.GetTopScoringIntent();
actionContext.State.SetValue(TurnPath.TopIntent, name);
actionContext.State.SetValue(TurnPath.TopScore, score);
actionContext.State.SetValue(DialogPath.LastIntent, name);
if (Recognizer != null)
{
await actionContext.DebuggerStepAsync(Recognizer, AdaptiveEvents.RecognizeUtterance, cancellationToken).ConfigureAwait(false);
}
handled = true;
}
}
break;
case AdaptiveEvents.RepromptDialog:
{
// AdaptiveDialogs handle new RepromptDialog as it gives access to the dialogContext.
await this.RepromptDialogAsync(actionContext, actionContext.ActiveDialog, cancellationToken).ConfigureAwait(false);
handled = true;
}
break;
}
}
else
{
switch (dialogEvent.Name)
{
case AdaptiveEvents.BeginDialog:
if (actionContext.State.GetBoolValue(TurnPath.ActivityProcessed) == false)
{
var activityReceivedEvent = new DialogEvent
{
Name = AdaptiveEvents.ActivityReceived,
Value = activity,
Bubble = false
};
handled = await ProcessEventAsync(actionContext, dialogEvent: activityReceivedEvent, preBubble: false, cancellationToken: cancellationToken).ConfigureAwait(false);
}
break;
case AdaptiveEvents.ActivityReceived:
if (activity.Type == ActivityTypes.Message)
{
// Empty sequence?
if (!actionContext.Actions.Any())
{
// Emit trailing unknownIntent event
var unknownIntentEvent = new DialogEvent
{
Name = AdaptiveEvents.UnknownIntent,
Bubble = false
};
handled = await ProcessEventAsync(actionContext, dialogEvent: unknownIntentEvent, preBubble: false, cancellationToken: cancellationToken).ConfigureAwait(false);
}
else
{
handled = false;
}
}
// Has an interruption occured?
// - Setting this value to true causes any running inputs to re-prompt when they're
// continued. The developer can clear this flag if they want the input to instead
// process the users uterrance when its continued.
if (handled)
{
actionContext.State.SetValue(TurnPath.Interrupted, true);
}
break;
}
}
return handled;
}