/// <summary> /// All subsequent messages are forwarded on to the skill. /// </summary> /// <param name="innerDc">Inner Dialog Context.</param> /// <param name="cancellationToken">Cancellation Token.</param> /// <returns>DialogTurnResult.</returns> protected override async Task <DialogTurnResult> OnContinueDialogAsync(DialogContext innerDc, CancellationToken cancellationToken = default(CancellationToken)) { var activity = innerDc.Context.Activity; if (_authDialog != null && innerDc.ActiveDialog?.Id == _authDialog.Id) { // Handle magic code auth var result = await innerDc.ContinueDialogAsync(cancellationToken); // forward the token response to the skill if (result.Status == DialogTurnStatus.Complete && result.Result is ProviderTokenResponse) { activity.Type = ActivityTypes.Event; activity.Name = TokenEvents.TokenResponseEventName; activity.Value = result.Result as ProviderTokenResponse; } else { return(result); } } var dialogResult = await ForwardToSkillAsync(innerDc, activity); // if there's any response we need to send to the skill queued // forward to skill and start a new turn while (_queuedResponses.Count > 0) { await ForwardToSkillAsync(innerDc, _queuedResponses.Dequeue()); } _skillTransport.Disconnect(); return(dialogResult); }
/// <summary> /// When a SkillDialog is started, a skillBegin event is sent which firstly indicates the Skill is being invoked in Skill mode, also slots are also provided where the information exists in the parent Bot. /// </summary> /// <param name="innerDc">inner dialog context.</param> /// <param name="options">options.</param> /// <param name="cancellationToken">cancellation token.</param> /// <returns>dialog turn result.</returns> protected override async Task <DialogTurnResult> OnBeginDialogAsync(DialogContext innerDc, object options = null, CancellationToken cancellationToken = default(CancellationToken)) { var slots = new Dictionary <string, JObject>(); // Retrieve the SkillContext state object to identify slots (parameters) that can be used to slot-fill when invoking the skill var accessor = _userState.CreateProperty <SkillContext>(nameof(SkillContext)); var skillContext = await accessor.GetAsync(innerDc.Context, () => new SkillContext()); /* In instances where the caller is able to identify/specify the action we process the Action specific slots * In other scenarios (aggregated skill dispatch) we evaluate all possible slots against context and pass across * enabling the Skill to perform it's own action identification. */ var actionName = options != null ? options as string : null; if (actionName != null) { // Find the specified within the selected Skill for slot filling evaluation var action = _skillManifest.Actions.SingleOrDefault(a => a.Id == actionName); if (action != null) { // If the action doesn't define any Slots or SkillContext is empty then we skip slot evaluation if (action.Definition.Slots != null && skillContext.Count > 0) { // Match Slots to Skill Context slots = await MatchSkillContextToSlots(innerDc, action.Definition.Slots, skillContext); } } else { throw new ArgumentException($"Passed Action ({actionName}) could not be found within the {_skillManifest.Id} skill manifest action definition."); } } else { // The caller hasn't got the capability of identifying the action as well as the Skill so we enumerate // actions and slot data to pass what we have // Retrieve a distinct list of all slots, some actions may use the same slot so we use distinct to ensure we only get 1 instance. var skillSlots = _skillManifest.Actions.SelectMany(s => s.Definition.Slots).Distinct(new SlotEqualityComparer()); if (skillSlots != null) { // Match Slots to Skill Context slots = await MatchSkillContextToSlots(innerDc, skillSlots.ToList(), skillContext); } } await innerDc.Context.SendActivityAsync(new Activity(type : ActivityTypes.Trace, text : $"-->Handing off to the {_skillManifest.Name} skill.")); var activity = innerDc.Context.Activity; var entities = new Dictionary <string, Entity>(); foreach (var value in slots) { entities.Add(value.Key, new Entity { Properties = JObject.FromObject(value.Value) }); } var semanticAction = new SemanticAction { Entities = new Dictionary <string, Entity>() }; foreach (var slot in slots) { semanticAction.Entities.Add(slot.Key, new Entity { Properties = slot.Value }); } activity.SemanticAction = semanticAction; // Send skillBegin event to Skill/Bot var dialogResult = await ForwardToSkillAsync(innerDc, activity); // return APIResponse if any if (_apiResponses != null && _apiResponses.Count > 0) { dialogResult.Result = _apiResponses; _skillTransport.Disconnect(); return(await innerDc.EndDialogAsync(_apiResponses)); } // if there's any response we need to send to the skill queued // forward to skill and start a new turn while (_queuedResponses.Count > 0) { await ForwardToSkillAsync(innerDc, _queuedResponses.Dequeue()); } _skillTransport.Disconnect(); return(dialogResult); }