Ejemplo n.º 1
0
        private async Task StartAudioVideoIVRFlowAsync(IncomingInviteEventArgs <IAudioVideoInvitation> e)
        {
            Logger.Instance.Information(string.Format("[StartAudioVideoIVRFlowAsync]: LoggingContext: {0}", m_loggingContext));

            m_pstnCallConversation = null;

            // Step1: accept incoming call
            Logger.Instance.Information(string.Format("[StartAudioVideoIVRFlowAsync] Step 1: Accept incoming av call: LoggingContext: {0}", m_loggingContext));
            await e.NewInvite.AcceptAsync(m_loggingContext).ConfigureAwait(false);

            await e.NewInvite.WaitForInviteCompleteAsync().ConfigureAwait(false);

            // if everything is fine, you will be able to get the related conversation
            m_pstnCallConversation = e.NewInvite.RelatedConversation;
            m_pstnCallConversation.HandleResourceRemoved += HandlePSTNCallConversationRemoved;

            // Step 2 : wait AV flow connected and play Promt
            IAudioVideoCall pstnAv   = m_pstnCallConversation.AudioVideoCall;
            IAudioVideoFlow pstnFlow = await pstnAv.WaitForAVFlowConnected().ConfigureAwait(false);

            pstnFlow.ToneReceivedEvent += ToneReceivedEvent;

            // Step 3 : play prompt
            await PlayPromptAsync(pstnFlow, AudioVideoIVRAction.PlayMainPrompt).ConfigureAwait(false);
        }
Ejemplo n.º 2
0
 public AudioVideoIVRJob(IncomingInviteEventArgs <IAudioVideoInvitation> mIncomingInvitation, string callbackUri)
 {
     this.m_incomingInvitation = mIncomingInvitation;
     m_jobId            = Guid.NewGuid().ToString();
     this.m_callbackUri = new Uri(callbackUri);
     m_loggingContext   = new LoggingContext(m_jobId, string.Empty);
 }
Ejemplo n.º 3
0
        private void HandleIncomingAVCall(object sender, IncomingInviteEventArgs <IAudioVideoInvitation> args)
        {
            //Read job settings
            string callbackUri = ConfigurationManager.AppSettings["MyCallbackUri"];

            ApplicationEndpoint ae  = sender as ApplicationEndpoint;
            AudioVideoIVRJob    job = new AudioVideoIVRJob(args, callbackUri);

            job.Start();
        }
Ejemplo n.º 4
0
        private void HandleIncomingAVCall(object sender, IncomingInviteEventArgs <IAudioVideoInvitation> args)
        {
            //Read job settings
            string callbackUri = ConfigurationManager.AppSettings["MyCallbackUri"];
            string agents      = ConfigurationManager.AppSettings["MyAgentList"];


            ApplicationEndpoint ae  = sender as ApplicationEndpoint;
            CallCenterJob       job = new CallCenterJob(args, ae.Application, agents.Split(',', ';'), callbackUri);

            job.Start();
        }
Ejemplo n.º 5
0
 public CallCenterJob(IncomingInviteEventArgs <IAudioVideoInvitation> incomingInvitation, IApplication application, string[] inviteTargets, string callbackUri)
 {
     m_incomingInvitation = incomingInvitation;
     m_jobId            = Guid.NewGuid().ToString();
     m_inviteTargetUris = inviteTargets;
     if (m_inviteTargetUris == null || m_inviteTargetUris.Length == 0)
     {
         throw new ArgumentNullException("Failed to get job input as CallCenterJobInput!");
     }
     m_callbackUri    = new Uri(callbackUri);
     m_application    = application;
     m_loggingContext = new LoggingContext(m_jobId, string.Empty);
 }
Ejemplo n.º 6
0
 /// <summary>
 /// Method to be invoked on an incoming <see cref="AudioVideoInvitation"/>.
 /// </summary>
 /// <param name="sender"><see cref="ApplicationEndpoint"/> which received the <see cref="AudioVideoInvitation"/>.</param>
 /// <param name="e"><see cref="IncomingInviteEventArgs{T}"/> containing the incoming <see cref="AudioVideoInvitation"/>.</param>
 private void HandleIncomingAudioVideoCall(object sender, IncomingInviteEventArgs <IAudioVideoInvitation> e)
 {
     HandleIncomingAudioVideoCallAsync(e).ContinueWith(t =>
     {
         if (t.IsFaulted)
         {
             Logger.Instance.Error("[AudioVideoIVRJob] Exception while processing incoming audio video call.", t.Exception);
         }
         else
         {
             Logger.Instance.Information("[AudioVideoIVRJob] Incoming audio video call processed.");
         }
     });
 }
Ejemplo n.º 7
0
 /// <summary>
 /// Instance_HandleIncomingAudioVideoCall
 /// </summary>
 /// <param name="sender"></param>
 /// <param name="e"></param>
 private void Instance_HandleIncomingAudioVideoCall(object sender, IncomingInviteEventArgs <IAudioVideoInvitation> e)
 {
     //Start async since we do not want to block the event handler thread
     StartHuntGroupFlowAsync(e).ContinueWith(p =>
     {
         if (p.IsFaulted)
         {
             if (p.Exception != null)
             {
                 Exception baseException = p.Exception.GetBaseException();
                 Logger.Instance.Error(baseException, "StartHuntGroupFlow failed with exception. Job id {0} Instance id {1}", this.JobId, this.InstanceId);
             }
         }
         else
         {
             Logger.Instance.Information("StartHuntGroupFlow completed, Job id {0} Instance id {1}", this.JobId, this.InstanceId);
         }
         this.CleanUpConversations();
     }
                                             );
 }
 /// <summary>
 /// HandleIncomingInstantMessagingCall
 /// </summary>
 /// <param name="sender"></param>
 /// <param name="e"></param>
 private void Instance_HandleIncomingInstantMessagingCall(object sender, IncomingInviteEventArgs <IMessagingInvitation> e)
 {
     //Start async since we do not want to block the event handler thread
     StartInstantMessagingBridgeFlowAsync(e).ContinueWith(p =>
     {
         if (p.IsFaulted)
         {
             //Clean up only in fault case, if success, leave it in established state and rely on client chat to terminate the call
             this.CleanUpConversations();
             if (p.Exception != null)
             {
                 Exception baseException = p.Exception.GetBaseException();
                 Logger.Instance.Error(baseException, "InstantMessagingBridgeFlow failed with exception. Job id {0} Instance id {1}", this.JobId, this.InstanceId);
             }
         }
         else
         {
             Logger.Instance.Information("InstantMessagingBridgeFlow completed, Job id {0} Instance id {1}", this.JobId, this.InstanceId);
         }
     }
                                                          );
 }
Ejemplo n.º 9
0
        private async Task HandleIncomingAudioVideoCallAsync(IncomingInviteEventArgs <IAudioVideoInvitation> e)
        {
            Logger.Instance.Information("[AudioVideoIVRJob] Incoming AudioVideoCall.");

            var input = JobInput as AudioVideoIVRJobInput;
            IAudioVideoInvitation invite = e.NewInvite;

            if (input.Action == AudioVideoIVRActions.TerminateCall)
            {
                Logger.Instance.Information("[AudioVideoIVRJob] Declining incoming call.");
                await invite.DeclineAsync(LoggingContext).ConfigureAwait(false);
            }

            if (input.Action == AudioVideoIVRActions.TransferToUser)
            {
                Logger.Instance.Information("[AudioVideoIVRJob] Forwarding the call to {0}.", input.User);
                await invite.ForwardAsync(LoggingContext, input.User).ConfigureAwait(false);
            }

            if (input.Action == AudioVideoIVRActions.PlayPrompt || input.Action == AudioVideoIVRActions.RepeatPrompt)
            {
                Logger.Instance.Information("[AudioVideoIVRJob] Accepting the call.");
                await invite.AcceptAsync(LoggingContext).ConfigureAwait(false);

                await e.NewInvite.WaitForInviteCompleteAsync().ConfigureAwait(false);

                if (e.NewInvite.RelatedConversation?.AudioVideoCall == null)
                {
                    Logger.Instance.Warning("[AudioVideoIVRJob] AudioVideoModality not found in the conversation.");
                    return;
                }

                Logger.Instance.Information("[AudioVideoIVRJob] Call accepted.");

                var promptHandler = new AudioVideoIVRPromptHandler(input, this.AzureApplication, LoggingContext);
                promptHandler.HandleEstablishedAudioVideo(e.NewInvite.RelatedConversation.AudioVideoCall);
            }
        }
Ejemplo n.º 10
0
        private async Task StartHuntGroupFlowAsync(IncomingInviteEventArgs <IAudioVideoInvitation> e)
        {
            Logger.Instance.Information(string.Format("[StartHuntGroupFlow] StartHuntGroupFlow: LoggingContext: {0}", LoggingContext));

            m_pstnCallConversation    = null;
            m_outboundAVConversations = new List <IConversation>();

            // Step1: accept incoming call
            Logger.Instance.Information(string.Format("[StartHuntGroupFlow] Step 1: Accept incoming av call: LoggingContext: {0}", LoggingContext));
            await e.NewInvite.AcceptAsync(LoggingContext).ConfigureAwait(false);

            await e.NewInvite.WaitForInviteCompleteAsync().ConfigureAwait(false);

            // if everything is fine, you will be able to get the related conversation
            m_pstnCallConversation = e.NewInvite.RelatedConversation;
            m_pstnCallConversation.HandleResourceRemoved += HandlePSTNCallConversationRemoved;

            // Step 2 : wait AV flow connected and play Promt
            IAudioVideoCall pstnAv   = m_pstnCallConversation.AudioVideoCall;
            IAudioVideoFlow pstnFlow = await pstnAv.WaitForAVFlowConnected().ConfigureAwait(false);

            pstnFlow.ToneReceivedEvent += HandleToneReceived;

            // Step 3 : play prompt

            // We support two modes in this job.
            //  A : InviteTargetUris are provided in the configuration; we will send invites to all of them and transfer the incoming call
            //      to whoever accepts it.
            //  B : InviteTargetUris not provided in the configuration; we will provide the user with a list of agents and let the user
            //      pick the target transfer using DTMF tones.
            bool skipDTMF = m_jobInput.InviteTargetUris != null && m_jobInput.InviteTargetUris.Length > 0;

            string wavFile     = skipDTMF ? "HuntGroupA.wav" : "HuntGroupB.wav";
            var    resourceUri = new Uri(string.Format(this.AzureApplication.ResourceUriFormat, wavFile));

            try
            {
                await pstnFlow.PlayPromptAsync(resourceUri, LoggingContext).ConfigureAwait(false);
            }
            catch (CapabilityNotAvailableException ex)
            {
                Logger.Instance.Error("[HuntGroupJob] PlayPrompt api is not available!", ex);
            }
            catch (RemotePlatformServiceException ex)
            {
                ErrorInformation error = ex.ErrorInformation;
                if (error != null && error.Code == ErrorCode.Informational && error.Subcode == ErrorSubcode.CallTerminated)
                {
                    Logger.Instance.Information("[HuntGroupJob] Call terminated while playing prompt.");
                }
                else
                {
                    throw;
                }
            }

            string callContext = pstnAv.CallContext;

            if (string.IsNullOrEmpty(callContext))
            {
                throw new PlatformserviceApplicationException("No valid callcontext in audioVideo resource ");
            }

            //Step 4 : Make out bound call to agents and do transfer
            ICommunication communication = m_pstnCallConversation.Parent as ICommunication;

            bool transferFlowSuccess = false;

            if (skipDTMF)
            {
                List <Task> TasksWithAgent = new List <Task>();
                foreach (string to in m_jobInput.InviteTargetUris)
                {
                    Task a = this.StartAgentCallAndTransferFlowAsync(communication, to, callContext).ContinueWith
                             (
                        pTask =>
                    {
                        if (pTask.IsFaulted)
                        {
                            Logger.Instance.Warning("[HuntGroupJob] Transfer flow failed." + pTask.Exception);
                        }
                        else
                        {
                            Logger.Instance.Information("[HuntGroupJob] Transfer flow complete.");
                            transferFlowSuccess = true;
                        }
                    }
                             );
                    TasksWithAgent.Add(a);
                }

                await Task.WhenAll(TasksWithAgent.ToArray()).ConfigureAwait(false);
            }
            else //Upgraded version, with DTMF recognize
            {
                //wait tone
                string target = await m_toneReceived.Task.ConfigureAwait(false);

                try
                {
                    await this.StartAgentCallAndTransferFlowAsync(communication, target, callContext).ConfigureAwait(false);

                    transferFlowSuccess = true;
                }
                catch (CapabilityNotAvailableException ex)
                {
                    Logger.Instance.Warning("[HuntGroupJob] Transfer flow failed.", ex);
                }
                catch (RemotePlatformServiceException ex)
                {
                    Logger.Instance.Warning("[HuntGroupJob] Transfer flow failed.", ex);
                }
            }

            m_outboundCallTransferLock = 0;

            if (transferFlowSuccess)
            {
                Logger.Instance.Information("TransferFlow success");
            }
            else
            {
                Logger.Instance.Error("TransferFlow Failed, see above trace for error info");
            }
        }
Ejemplo n.º 11
0
        private async Task StartCallCenterFlowAsync(IncomingInviteEventArgs <IAudioVideoInvitation> e)
        {
            Logger.Instance.Information(string.Format("[StartCallCenterFlowAsync] StartCallCenterFlowAsync: LoggingContext: {0}", m_loggingContext));

            m_pstnCallConversation    = null;
            m_outboundAVConversations = new List <IConversation>();

            // Step1: accept incoming call
            Logger.Instance.Information(string.Format("[StartCallCenterFlowAsync] Step 1: Accept incoming av call: LoggingContext: {0}", m_loggingContext));
            await e.NewInvite.AcceptAsync(m_loggingContext).ConfigureAwait(false);

            await e.NewInvite.WaitForInviteCompleteAsync().ConfigureAwait(false);

            // if everything is fine, you will be able to get the related conversation
            m_pstnCallConversation = e.NewInvite.RelatedConversation;
            m_pstnCallConversation.HandleResourceRemoved += HandlePSTNCallConversationRemoved;

            // Step 2 : wait AV flow connected and play Promt
            IAudioVideoCall pstnAv   = m_pstnCallConversation.AudioVideoCall;
            IAudioVideoFlow pstnFlow = await pstnAv.WaitForAVFlowConnected().ConfigureAwait(false);

            // Step 3 : play prompt


            // InviteTargetUris are provided in the configuration; we will send invites to all of them and transfer the incoming call
            //      to whoever accepts it.

            string wavFile     = "CallTransferSample.wav";
            var    resourceUri = new Uri(string.Format("{0}://{1}/resources/{2}", m_callbackUri.Scheme, m_callbackUri.Host, wavFile));

            try
            {
                await pstnFlow.PlayPromptAsync(resourceUri, m_loggingContext).ConfigureAwait(false);
            }
            catch (CapabilityNotAvailableException ex)
            {
                Logger.Instance.Error("[CallCenterJob] PlayPrompt api is not available!", ex);
            }
            catch (RemotePlatformServiceException ex)
            {
                ErrorInformation error = ex.ErrorInformation;
                if (error != null && error.Code == ErrorCode.Informational && error.Subcode == ErrorSubcode.CallTerminated)
                {
                    Logger.Instance.Information("[CallCenterJob] Call terminated while playing prompt.");
                }
                else
                {
                    throw;
                }
            }

            string callContext = pstnAv.CallContext;

            if (string.IsNullOrEmpty(callContext))
            {
                throw new Exception("No valid callcontext in audioVideo resource ");
            }

            //Step 4 : Make out bound call to agents and do transfer
            ICommunication communication = m_pstnCallConversation.Parent as ICommunication;

            bool transferFlowSuccess = false;

            List <Task> TasksWithAgent = new List <Task>();

            foreach (string to in m_inviteTargetUris)
            {
                Task a = this.StartAgentCallAndTransferFlowAsync(communication, to, callContext).ContinueWith
                         (
                    pTask =>
                {
                    if (pTask.IsFaulted)
                    {
                        Logger.Instance.Warning("[CallCenterJob] Transfer flow failed." + pTask.Exception);
                    }
                    else
                    {
                        Logger.Instance.Information("[CallCenterJob] Transfer flow complete.");
                        transferFlowSuccess = true;
                    }
                }
                         );
                TasksWithAgent.Add(a);
            }

            await Task.WhenAll(TasksWithAgent.ToArray()).ConfigureAwait(false);

            m_outboundCallTransferLock = 0;

            if (transferFlowSuccess)
            {
                Logger.Instance.Information("TransferFlow success");
            }
            else
            {
                Logger.Instance.Error("TransferFlow Failed, see above trace for error info");
            }
        }
        private async Task StartInstantMessagingBridgeFlowAsync(IncomingInviteEventArgs <IMessagingInvitation> e)
        {
            Logger.Instance.Information(string.Format("[InstantMessagingBridgeFlow] StartInstantMessagingBridgeFlow: LoggingContext: {0}", LoggingContext));

            CallbackContext callbackcontext = new CallbackContext {
                InstanceId = this.InstanceId, JobId = this.JobId
            };
            string callbackContextJsonString = JsonConvert.SerializeObject(callbackcontext);
            string CallbackUrl = string.Format(CultureInfo.InvariantCulture, AzureApplication.CallbackUriFormat, HttpUtility.UrlEncode(callbackContextJsonString));

            string meetingUrl = string.Empty;

            //There will be two conversation legs for the saas app
            m_p2pConversation  = null;
            m_confConversation = null;

            #region Step 1 Start adhoc meeting
            //Step1:
            Logger.Instance.Information(string.Format("[InstantMessagingBridgeFlow] Step 1: Start adhoc meeting: LoggingContext: {0}", LoggingContext));

            IOnlineMeetingInvitation onlineMeetingInvite = await e.NewInvite.StartAdhocMeetingAsync(m_handleIncomingMessageInput.Subject, CallbackUrl, LoggingContext).ConfigureAwait(false);

            if (string.IsNullOrEmpty(onlineMeetingInvite.MeetingUrl))
            {
                throw new PlatformserviceApplicationException("Do not get valid MeetingUrl on onlineMeetingInvitation resource after startAdhocMeeting!");
            }
            meetingUrl = onlineMeetingInvite.MeetingUrl;

            Logger.Instance.Information(string.Format("[InstantMessagingBridgeFlow] Get meeting uri: {0} LoggingContext: {1}", onlineMeetingInvite.MeetingUrl, LoggingContext));

            //wait on embedded onlinemeetingInvitation to complete, so that we can have valid related conversation
            await onlineMeetingInvite.WaitForInviteCompleteAsync().ConfigureAwait(false);

            //this is conference conversation leg
            m_confConversation = onlineMeetingInvite.RelatedConversation;
            if (m_confConversation == null)
            {
                throw new PlatformserviceApplicationException("onlineMeetingInvite.RelatedConversation is null? this is propably app code bug!");
            }
            #endregion

            #region Step 2 add Messaging modality on conference conversation
            //Step2:
            Logger.Instance.Information(string.Format("[InstantMessagingBridgeFlow] Step2: add Messaging modality on conference conversation: LoggingContext: {0}", LoggingContext));
            IMessagingCall confMessaging = m_confConversation.MessagingCall;
            if (confMessaging == null)
            {
                throw new PlatformserviceApplicationException("[InstantMessagingBridgeFlow] No valid Messaging resource on conference conversation");
            }
            //Hook up the event handler on "MessagingModality" of the conference leg and make sure what ever message anon user , or agent input , the app can all know and note down
            confMessaging.IncomingMessageReceived      += OnIncomingMessageReceived;
            m_confConversation.HandleParticipantChange += this.OnParticipantChange;
            IMessagingInvitation messageInvitation = await confMessaging.EstablishAsync(LoggingContext).ConfigureAwait(false);

            await messageInvitation.WaitForInviteCompleteAsync().ConfigureAwait(false);//messageInvitation cannot be null here

            #endregion

            #region Step 3 Start AcceptAndBridge
            //Step3:
            Logger.Instance.Information(string.Format("[InstantMessagingBridgeFlow] Step3:  Start AcceptAndBridge: LoggingContext: {0}", LoggingContext));
            await e.NewInvite.AcceptAndBridgeAsync(LoggingContext, meetingUrl, m_handleIncomingMessageInput.Subject).ConfigureAwait(false);

            await e.NewInvite.WaitForInviteCompleteAsync().ConfigureAwait(false);

            m_p2pConversation = e.NewInvite.RelatedConversation;

            //This is to clean the conf conversation leg when the p2p conversation is removed
            m_p2pConversation.HandleResourceRemoved += (o, args) =>
            {
                m_p2pConversation.HandleResourceRemoved = null;
                this.OnClientChatDisconnected();
            };

            IMessagingCall p2pMessaging = m_p2pConversation.MessagingCall;
            if (p2pMessaging == null || p2pMessaging.State != Rtc.Internal.Platform.ResourceContract.CallState.Connected)
            {
                Logger.Instance.Error(string.Format("[InstantMessagingBridgeFlow] p2pMessaging is null or not in connected state: LoggingContext: {0}", LoggingContext));
                throw new PlatformserviceApplicationException("[InstantMessagingBridgeFlow] p2pMessaging is null or not in connected state");
            }
            #endregion

            #region Step 4 Send welcome message
            //Step4:
            Logger.Instance.Information(string.Format("[InstantMessagingBridgeFlow] Step4:  Send welcome message: LoggingContext: {0}", LoggingContext));
            await p2pMessaging.SendMessageAsync(m_handleIncomingMessageInput.WelcomeMessage, LoggingContext).ConfigureAwait(false);

            #endregion

            #region Step 5 Add bridged participant to enable agent send message to client chat
            //Step5:
            Logger.Instance.Information(string.Format("[InstantMessagingBridgeFlow] Step5: Add bridged participant to enable agent send message to client chat. LoggingContext: {0}", LoggingContext));
            IConversationBridge conversationBridge = m_p2pConversation.ConversationBridge;
            if (conversationBridge == null)
            {
                Logger.Instance.Error(string.Format("[InstantMessagingBridgeFlow] conversationBridge == null after accept and bridge. LoggingContext: {0}", LoggingContext));
                throw new PlatformserviceApplicationException("[InstantMessagingBridgeFlow]  conversationBridge == null after accept and bridge");
            }
            await conversationBridge.AddBridgedParticipantAsync(LoggingContext, m_handleIncomingMessageInput.InvitedTargetDisplayName, m_handleIncomingMessageInput.InviteTargetUri, false).ConfigureAwait(false);

            #endregion

            #region Step 6 Start addParticipant to conference
            //Step 6:
            Logger.Instance.Information(string.Format("[HandleIncomingMessageJob] Step5: Start addParticipant to conference: LoggingContext: {0}", LoggingContext));
            IParticipantInvitation participantInvitation = await m_confConversation.AddParticipantAsync(m_handleIncomingMessageInput.InviteTargetUri, LoggingContext).ConfigureAwait(false);

            await participantInvitation.WaitForInviteCompleteAsync().ConfigureAwait(false);

            #endregion
        }