/// <summary> /// Saas application Joins the adhoc meeting as trusted entity, won't be seen by other conference participants /// </summary> /// <param name="loggingContext"><see cref="LoggingContext"/> to be used for logging all related events</param> /// <param name="callbackContext">A state/context object which will be provided by SfB in all related events</param> /// <returns><see cref="IOnlineMeetingInvitation"/> which can be used to wait for the meeting join to complete</returns> public async Task <IOnlineMeetingInvitation> JoinAdhocMeeting(LoggingContext loggingContext, string callbackContext) { string href = PlatformResource?.JoinAdhocMeetingLink?.Href; if (string.IsNullOrWhiteSpace(href)) { throw new CapabilityNotAvailableException("Link to join adhoc meeting is not available."); } var callbackUrl = (Parent as Application).GetCustomizedCallbackUrl(); if (callbackUrl != null && callbackContext != null) { // We need to append callbackContext as a query paramter to callbackUrl callbackUrl += callbackUrl.Contains("?") ? "&" : "?"; callbackUrl += "callbackContext=" + callbackContext; // We don't want to pass callbackContext if callbackUrl is being passed callbackContext = null; } var joinMeetingInput = new JoinMeetingInvitationInput() { CallbackContext = callbackContext, OperationContext = Guid.NewGuid().ToString(), CallbackUrl = callbackUrl }; var communication = (Parent as Application).Communication as Communication; IInvitation invite = null; //Adding current invitation to collection for tracking purpose. var tcs = new TaskCompletionSource <IInvitation>(); communication.HandleNewInviteOperationKickedOff(joinMeetingInput.OperationContext, tcs); Logger.Instance.Information("Joining adhoc meeting " + href); var adhocMeetingUri = UriHelper.CreateAbsoluteUri(BaseUri, href); await PostRelatedPlatformResourceAsync(adhocMeetingUri, joinMeetingInput, new ResourceJsonMediaTypeFormatter(), loggingContext).ConfigureAwait(false); Task finishedTask = await Task.WhenAny(Task.Delay(WaitForEvents), tcs.Task).ConfigureAwait(false); if (finishedTask != tcs.Task) { throw new RemotePlatformServiceException("Timeout to get OnlinemeetingInvitation started event from platformservice"); } else { invite = await tcs.Task.ConfigureAwait(false); // Incase need to throw exception } // We know for sure that the invitation is there now. IOnlineMeetingInvitation result = invite as IOnlineMeetingInvitation; if (result == null) { throw new RemotePlatformServiceException("Platformservice did not deliver a OnlinemeetingInvitation resource with operationId " + joinMeetingInput.OperationContext); } return(result); }
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 }