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); }
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); }
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(); }
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(); }
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); }
/// <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."); } }); }
/// <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); } } ); }
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); } }
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"); } }
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 }