示例#1
0
        /// <summary>
        /// Raised when the <see cref="INotificationProcessor"/> has received a notification asynchronously.
        /// </summary>
        /// <param name="args">The <see cref="NotificationEventArgs"/> instance containing the event data.</param>
        /// <returns>The <see cref="Task"/>.</returns>
        private async Task NotificationProcessor_OnNotificationReceivedAsync(NotificationEventArgs args)
        {
            this.GraphLogger.CorrelationId = args.ScenarioId;
            var headers = new[]
            {
                new KeyValuePair <string, IEnumerable <string> >(HttpConstants.HeaderNames.ScenarioId, new[] { args.ScenarioId.ToString() }),
                new KeyValuePair <string, IEnumerable <string> >(HttpConstants.HeaderNames.ClientRequestId, new[] { args.RequestId.ToString() }),
                new KeyValuePair <string, IEnumerable <string> >(HttpConstants.HeaderNames.Tenant, new[] { args.TenantId }),
            };

            // Create obfuscation content to match what we
            // would have gotten from the service, then log.
            var notifications = new CommsNotifications {
                Value = new[] { args.Notification }
            };
            var obfuscatedContent = this.GraphLogger.SerializeAndObfuscate(notifications, Formatting.Indented);

            this.GraphLogger.LogHttpMessage(
                TraceLevel.Info,
                TransactionDirection.Incoming,
                HttpTraceType.HttpRequest,
                args.CallbackUri.ToString(),
                HttpMethods.Post,
                obfuscatedContent,
                headers,
                correlationId: args.ScenarioId,
                requestId: args.RequestId);

            if (args.ResourceData is Call call)
            {
                if (call.State == CallState.Established && call.MediaState?.Audio == MediaState.Active)
                {
                    await this.BotPlayPromptAsync(call.Id, args.TenantId, args.ScenarioId).ConfigureAwait(false);
                }
                else if (args.ChangeType == ChangeType.Deleted && call.State == CallState.Terminated)
                {
                    this.GraphLogger.Log(TraceLevel.Info, $"Call State:{call.State}");
                }
            }
            else if (args.ResourceData is PlayPromptOperation playPromptOperation)
            {
                //  checking for the call id sent in ClientContext.
                if (string.IsNullOrWhiteSpace(playPromptOperation.ClientContext))
                {
                    throw new ServiceException(new Error()
                    {
                        Message = "No call id provided in PlayPromptOperation.ClientContext.",
                    });
                }
                else if (playPromptOperation.Status == OperationStatus.Completed)
                {
                    // The operation has been completed, hang up the call
                    await this.BotHangupCallAsync(playPromptOperation.ClientContext, args.TenantId, args.ScenarioId).ConfigureAwait(false);

                    this.GraphLogger.Log(TraceLevel.Info, $"Disconnecting the call.");
                }
            }
        }
示例#2
0
        /// <summary>
        /// Raised when the <see cref="INotificationProcessor"/> has received a notification asynchronously.
        /// </summary>
        /// <param name="args">The <see cref="NotificationEventArgs"/> instance containing the event data.</param>
        /// <returns>The <see cref="Task"/>.</returns>
        private async Task NotificationProcessor_OnNotificationReceivedAsync(NotificationEventArgs args)
        {
            this.GraphLogger.CorrelationId = args.ScenarioId;
            var headers = new[]
            {
                new KeyValuePair <string, IEnumerable <string> >(HttpConstants.HeaderNames.ScenarioId, new[] { args.ScenarioId.ToString() }),
                new KeyValuePair <string, IEnumerable <string> >(HttpConstants.HeaderNames.ClientRequestId, new[] { args.RequestId.ToString() }),
                new KeyValuePair <string, IEnumerable <string> >(HttpConstants.HeaderNames.Tenant, new[] { args.TenantId }),
            };

            // Create obfuscation content to match what we
            // would have gotten from the service, then log.
            var notifications = new CommsNotifications {
                Value = new[] { args.Notification }
            };
            var obfuscatedContent = this.GraphLogger.SerializeAndObfuscate(notifications, Formatting.Indented);

            this.GraphLogger.LogHttpMessage(
                TraceLevel.Info,
                TransactionDirection.Incoming,
                HttpTraceType.HttpRequest,
                args.CallbackUri.ToString(),
                HttpMethods.Post,
                obfuscatedContent,
                headers,
                correlationId: args.ScenarioId,
                requestId: args.RequestId);

            if (args.ResourceData is Call call)
            {
                if (call.State == CallState.Established && call.MediaState?.Audio == MediaState.Active)
                {
                    this.GraphLogger.Log(TraceLevel.Info, $"Call State:{call.State}");
                }
                else if (args.ChangeType == ChangeType.Deleted && call.State == CallState.Terminated)
                {
                    this.GraphLogger.Log(TraceLevel.Info, $"Call State:{call.State}");
                }
            }
            else if (args.Notification.ResourceUrl.EndsWith("/participants") && args.ResourceData is List <object> participantObjects)
            {
                this.GraphLogger.Log(TraceLevel.Info, "Total count of participants found in this roster is " + participantObjects.Count());
                foreach (var participantObject in participantObjects)
                {
                    var participant = participantObject as Participant;
                    this.GraphLogger.Log(TraceLevel.Info, "Id: " + participant?.Info?.Identity?.User?.Id.ToString(), "DisplayName: " + participant?.Info?.Identity?.User?.DisplayName.ToString());
                }
            }
        }
        /// <summary>
        /// Processes the notifications and raises the required callbacks.
        /// This function should be called in order for the SDK to raise
        /// any required events and process state changes.
        /// </summary>
        /// <param name="client">The stateful client.</param>
        /// <param name="request">The http request that is incoming from service.</param>
        /// <returns>Http Response Message after processed by the SDK. This has to
        /// be returned to the server.</returns>
        private static async Task <HttpResponseMessage> ProcessNotificationAsync(ICommunicationsClient client, HttpRequestMessage request)
        {
            client.NotNull(nameof(client));
            request.NotNull(nameof(request));
            var stopwatch = Stopwatch.StartNew();

            var scenarioId = client.GraphLogger.ParseScenarioId(request);
            var requestId  = client.GraphLogger.ParseRequestId(request);

            CommsNotifications notifications = null;

            try
            {
                // Parse out the notification content.
                var content = await request.Content.ReadAsStringAsync().ConfigureAwait(false);

                var serializer = client.Serializer;
                notifications = NotificationProcessor.ExtractNotifications(content, serializer);
            }
            catch (ServiceException ex)
            {
                var statusCode = (int)ex.StatusCode >= 200
                    ? ex.StatusCode
                    : HttpStatusCode.BadRequest;
                return(client.LogAndCreateResponse(request, requestId, scenarioId, notifications, statusCode, stopwatch, ex));
            }
            catch (Exception ex)
            {
                var statusCode = HttpStatusCode.BadRequest;
                return(client.LogAndCreateResponse(request, requestId, scenarioId, notifications, statusCode, stopwatch, ex));
            }

            RequestValidationResult result;

            try
            {
                // Autenticate the incoming request.
                result = await client.AuthenticationProvider
                         .ValidateInboundRequestAsync(request)
                         .ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                var clientEx = new ClientException(
                    new Error
                {
                    Code    = ErrorConstants.Codes.ClientCallbackError,
                    Message = ErrorConstants.Messages.ClientErrorAuthenticatingRequest,
                },
                    ex);

                throw clientEx;
            }

            if (!result.IsValid)
            {
                var statusCode = HttpStatusCode.Unauthorized;
                return(client.LogAndCreateResponse(request, requestId, scenarioId, notifications, statusCode, stopwatch));
            }

            // The request is valid. Let's evaluate any policies on the
            // incoming call before sending it off to the SDK for processing.
            var call     = notifications?.Value?.FirstOrDefault()?.GetResourceData() as Call;
            var response = await EvaluateAndHandleIncomingCallPoliciesAsync(call).ConfigureAwait(false);

            if (response != null)
            {
                var level = client.GraphLogger.LogHttpRequest(request, response.StatusCode, notifications);
                client.GraphLogger.LogHttpResponse(level, request, response, stopwatch.ElapsedMilliseconds);
                stopwatch.Stop();
                return(response);
            }

            try
            {
                var additionalData = request.GetHttpAndContentHeaders().ToDictionary(
                    pair => pair.Key,
                    pair => (object)string.Join(",", pair.Value),
                    StringComparer.OrdinalIgnoreCase);
                client.ProcessNotifications(request.RequestUri, notifications, result.TenantId, requestId, scenarioId, additionalData);
            }
            catch (ServiceException ex)
            {
                var statusCode = (int)ex.StatusCode >= 200
                    ? ex.StatusCode
                    : HttpStatusCode.InternalServerError;
                return(client.LogAndCreateResponse(request, requestId, scenarioId, notifications, statusCode, stopwatch, ex));
            }
            catch (Exception ex)
            {
                var statusCode = HttpStatusCode.InternalServerError;
                return(client.LogAndCreateResponse(request, requestId, scenarioId, notifications, statusCode, stopwatch, ex));
            }

            return(client.LogAndCreateResponse(request, requestId, scenarioId, notifications, HttpStatusCode.Accepted, stopwatch));
        }
        /// <summary>
        /// Raised when the <see cref="INotificationProcessor"/> has received a notification asynchronously.
        /// </summary>
        /// <param name="args">The <see cref="NotificationEventArgs"/> instance containing the event data.</param>
        /// <returns>The <see cref="Task"/>.</returns>
        private async Task NotificationProcessor_OnNotificationReceivedAsync(NotificationEventArgs args)
        {
            this.GraphLogger.CorrelationId = args.ScenarioId;
            var headers = new[]
            {
                new KeyValuePair <string, IEnumerable <string> >(HttpConstants.HeaderNames.ScenarioId, new[] { args.ScenarioId.ToString() }),
                new KeyValuePair <string, IEnumerable <string> >(HttpConstants.HeaderNames.ClientRequestId, new[] { args.RequestId.ToString() }),
                new KeyValuePair <string, IEnumerable <string> >(HttpConstants.HeaderNames.Tenant, new[] { args.TenantId }),
            };

            // Create obfuscation content to match what we
            // would have gotten from the service, then log.
            var notifications = new CommsNotifications {
                Value = new[] { args.Notification }
            };
            var obfuscatedContent = this.GraphLogger.SerializeAndObfuscate(notifications, Formatting.Indented);

            this.GraphLogger.LogHttpMessage(
                TraceLevel.Info,
                TransactionDirection.Incoming,
                HttpTraceType.HttpRequest,
                args.CallbackUri.ToString(),
                HttpMethods.Post,
                obfuscatedContent,
                headers,
                correlationId: args.ScenarioId,
                requestId: args.RequestId);

            if (args.ResourceData is Call call)
            {
                if (args.ChangeType == ChangeType.Created && call.State == CallState.Incoming)
                {
                    await this.BotAnswerIncomingCallAsync(call.Id, args.TenantId, args.ScenarioId).ConfigureAwait(false);
                }
                else if (args.ChangeType == ChangeType.Updated && call.State == CallState.Established)
                {
                    this.GraphLogger.Log(TraceLevel.Info, "In Established");

                    if (call.ToneInfo == null && call.MediaState.Audio == MediaState.Active)
                    {
                        await this.BotPlayNotificationPromptAsync(call.Id, args.TenantId, args.ScenarioId).ConfigureAwait(false);
                    }
                    else if (call.ToneInfo?.SequenceId == 1)
                    {
                        InvitationParticipantInfo transferTarget = null;

                        if (call.ToneInfo.Tone == Tone.Tone1)
                        {
                            transferTarget = new InvitationParticipantInfo
                            {
                                Identity = new IdentitySet
                                {
                                    User = new Identity
                                    {
                                        Id = ConfigurationManager.AppSetting["ObjectIds:First"],
                                    },
                                },
                            };
                        }
                        else if (call.ToneInfo.Tone == Tone.Tone2)
                        {
                            transferTarget = new InvitationParticipantInfo
                            {
                                Identity = new IdentitySet
                                {
                                    User = new Identity
                                    {
                                        Id = ConfigurationManager.AppSetting["ObjectIds:Second"],
                                    },
                                },
                            };
                        }
                        else
                        {
                            transferTarget = new InvitationParticipantInfo
                            {
                                Identity = new IdentitySet
                                {
                                    User = new Identity
                                    {
                                        Id = ConfigurationManager.AppSetting["ObjectIds:Third"],
                                    },
                                },
                            };
                        }

                        await this.BotTransferCallAsync(transferTarget, call.Id, args.TenantId, args.ScenarioId).ConfigureAwait(false);
                    }

                    return;
                }
                else if (args.ChangeType == ChangeType.Deleted && call.State == CallState.Terminated)
                {
                    this.GraphLogger.Log(TraceLevel.Info, "Call is Terminated now");
                }

                return;
            }
            else if (args.ResourceData is PlayPromptOperation playPromptOperation)
            {
                //  checking for the call id sent in ClientContext.
                if (string.IsNullOrWhiteSpace(playPromptOperation.ClientContext))
                {
                    throw new ServiceException(new Error()
                    {
                        Message = "No call id provided in PlayPromptOperation.ClientContext.",
                    });
                }
                else if (playPromptOperation.CompletionReason == PlayPromptCompletionReason.CompletedSuccessfully && playPromptOperation.Status == OperationStatus.Completed)
                {
                    await this.BotSubscribesToToneAsync(playPromptOperation.ClientContext, args.TenantId, args.ScenarioId).ConfigureAwait(false);
                }
            }
        }
        /// <summary>
        /// Raised when the <see cref="INotificationProcessor"/> has received a notification asynchronously.
        /// </summary>
        /// <param name="args">The <see cref="NotificationEventArgs"/> instance containing the event data.</param>
        /// <returns>The <see cref="Task"/>.</returns>
        private async Task NotificationProcessor_OnNotificationReceivedAsync(NotificationEventArgs args)
        {
            this.GraphLogger.CorrelationId = args.ScenarioId;
            var headers = new[]
            {
                new KeyValuePair <string, IEnumerable <string> >(HttpConstants.HeaderNames.ScenarioId, new[] { args.ScenarioId.ToString() }),
                new KeyValuePair <string, IEnumerable <string> >(HttpConstants.HeaderNames.ClientRequestId, new[] { args.RequestId.ToString() }),
                new KeyValuePair <string, IEnumerable <string> >(HttpConstants.HeaderNames.Tenant, new[] { args.TenantId }),
            };

            // Create obfuscation content to match what we
            // would have gotten from the service, then log.
            var notifications = new CommsNotifications {
                Value = new[] { args.Notification }
            };
            var obfuscatedContent = this.GraphLogger.SerializeAndObfuscate(notifications, Formatting.Indented);

            this.GraphLogger.LogHttpMessage(
                TraceLevel.Info,
                TransactionDirection.Incoming,
                HttpTraceType.HttpRequest,
                args.CallbackUri.ToString(),
                HttpMethods.Post,
                obfuscatedContent,
                headers,
                correlationId: args.ScenarioId,
                requestId: args.RequestId);

            if (args.ResourceData is Call call)
            {
                if (args.ChangeType == ChangeType.Created && call.State == CallState.Incoming)
                {
                    await this.BotAnswerIncomingCallAsync(call.Id, args.TenantId, args.ScenarioId).ConfigureAwait(false);
                }
                else if (args.ChangeType == ChangeType.Updated && call.State == CallState.Established && call.MediaState?.Audio == MediaState.Active)
                {
                    // there can potentially be multiple established notifications for the same call,
                    // but since this is just sample code, it is not handled.
                    // your production code must handle such a case and not call record multiple times for the same call
                    await this.BotRecordsIncomingCallAsync(call.Id, args.TenantId, args.ScenarioId).ConfigureAwait(false);
                }
                else if (args.ChangeType == ChangeType.Deleted && call.State == CallState.Terminated)
                {
                    this.CleanupCall(call.Id);
                }
            }
            // Receiving updates for the play prompt operation.
            else if (args.ResourceData is PlayPromptOperation playPromptOperation)
            {
                //  checking for the call id sent in ClientContext.
                if (string.IsNullOrWhiteSpace(playPromptOperation.ClientContext))
                {
                    throw new ServiceException(new Error()
                    {
                        Message = "No call id provided in PlayPromptOperation.ClientContext.",
                    });
                }
                else if (playPromptOperation.Status == OperationStatus.Completed)
                {
                    // The operation has been completed, hang up the call
                    await this.BotHangupCallAsync(playPromptOperation.ClientContext, args.TenantId, args.ScenarioId).ConfigureAwait(false);

                    this.GraphLogger.Log(TraceLevel.Info, $"Disconnecting the call.");
                }
            }
            else if (args.ResourceData is RecordOperation recordOperation)
            {
                if (recordOperation.Status == OperationStatus.Completed && recordOperation.ResultInfo.Code == 200)
                {
                    var recordingFileName = $"audio/recording-{recordOperation.ClientContext}.wav";

                    await this.DownloadRecording(recordingFileName, recordOperation).ConfigureAwait(false);

                    var prompts = new Prompt[] {
                        new MediaPrompt
                        {
                            MediaInfo = new MediaInfo()
                            {
                                Uri = new Uri(this.botBaseUri, recordingFileName).ToString()
                            },
                        },
                    };

                    await this.BotPlayPromptAsync(prompts, recordOperation.ClientContext, args.TenantId, args.ScenarioId).ConfigureAwait(false);
                }
            }
        }