Exemplo n.º 1
0
        /// <summary>
        /// Forwards an activity to a skill (bot).
        /// </summary>
        /// <remarks>NOTE: Forwarding an activity to a skill will flush UserState and ConversationState changes so that skill has accurate state.</remarks>
        /// <param name="turnContext">turnContext.</param>
        /// <param name="skill">A <see cref="BotFrameworkSkill"/> instance with the skill information.</param>
        /// <param name="skillHostEndpoint">The callback Url for the skill host.</param>
        /// <param name="activity">activity to forward.</param>
        /// <param name="cancellationToken">cancellation Token.</param>
        /// <returns>Async task with optional invokeResponse.</returns>
        public override async Task <InvokeResponse> ForwardActivityAsync(ITurnContext turnContext, BotFrameworkSkill skill, Uri skillHostEndpoint, Activity activity, CancellationToken cancellationToken)
        {
            _logger.LogInformation($"Received request to forward activity to skill id {skill.Id}.");

            // Pull the current claims identity from TurnState (it is stored there on the way in).
            var identity = (ClaimsIdentity)turnContext.TurnState.Get <IIdentity>(BotIdentityKey);

            if (identity.AuthenticationType.Equals("anonymous", StringComparison.InvariantCultureIgnoreCase))
            {
                throw new NotSupportedException("Anonymous calls are not supported for skills, please ensure your bot is configured with a MicrosoftAppId and Password).");
            }

            // Get current Bot ID from the identity audience claim
            var botAppId = identity.Claims?.SingleOrDefault(claim => claim.Type == AuthenticationConstants.AudienceClaim)?.Value;

            if (string.IsNullOrWhiteSpace(botAppId))
            {
                throw new InvalidOperationException("Unable to get the audience from the current request identity");
            }

            var appCredentials = await GetAppCredentialsAsync(botAppId, skill.AppId).ConfigureAwait(false);

            if (appCredentials == null)
            {
                throw new InvalidOperationException("Unable to get appCredentials to connect to the skill");
            }

            // Get token for the skill call
            var token = await appCredentials.GetTokenAsync().ConfigureAwait(false);

            // POST to skill
            using (var client = new HttpClient())
            {
                // Create a deep clone of the activity so we can update it without impacting the original activity.
                var activityClone = JObject.FromObject(activity).ToObject <Activity>();

                // TODO use SkillConversation class here instead of hard coded encoding...
                // Encode original bot service URL and ConversationId in the new conversation ID so we can unpack it later.
                // var skillConversation = new SkillConversation() { ServiceUrl = activity.ServiceUrl, ConversationId = activity.Conversation.Id };
                // activity.Conversation.Id = skillConversation.GetSkillConversationId()
                activityClone.Conversation.Id = Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new[]
                {
                    activityClone.Conversation.Id,
                    activityClone.ServiceUrl
                })));
                activityClone.ServiceUrl = skillHostEndpoint.ToString();
                activityClone.Recipient.Properties["skillId"] = skill.Id;
                using (var jsonContent = new StringContent(JsonConvert.SerializeObject(activityClone, new JsonSerializerSettings {
                    NullValueHandling = NullValueHandling.Ignore
                }), Encoding.UTF8, "application/json"))
                {
                    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
                    var response = await client.PostAsync($"{skill.SkillEndpoint}", jsonContent, cancellationToken).ConfigureAwait(false);

                    var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

                    if (content.Length > 0)
                    {
                        return(new InvokeResponse()
                        {
                            Status = (int)response.StatusCode,
                            Body = JsonConvert.DeserializeObject(content)
                        });
                    }
                }
            }

            return(null);
        }