Example #1
0
        private void SaveTranscripts()
        {
            NonBlockingConsole.WriteLine("SaveTranscripts - Entry");

            // TODO: Save transcripts to network share folder or upload to DB

            // Save transcripts in local file
            string filename = "LyncMeetingTranscript.txt";

            using (FileStream fs = new FileStream(filename, FileMode.OpenOrCreate))
            {
                using (BinaryWriter w = new BinaryWriter(fs))
                {
                    foreach (TranscriptRecorderSession t in _activeConversationSessions.Values)
                    {
                        w.Write(t.GetFullTranscript());
                    }
                    foreach (TranscriptRecorderSession t in _activeConferenceSessions.Values)
                    {
                        w.Write(t.GetFullTranscript());
                    }
                }
            }

            NonBlockingConsole.WriteLine("SaveTranscripts - Exit");
        }
Example #2
0
        static void Main(string[] args)
        {
            // Init Transcript Recorder Session Manager
            _appSession = new TranscriptRecorderSessionManager();

            // Cancel token to terminate program early on cntrl + c
            CancellationTokenSource cts = new CancellationTokenSource();

            System.Console.CancelKeyPress += (s, e) =>
            {
                e.Cancel = true;
                cts.Cancel();
            };

            try
            {
                _appSession.RunAsync(cts.Token).Wait();
            }
            catch (Exception e)
            {
                NonBlockingConsole.WriteLine("TranscriptRecorderSessionManager exited with exception: " + e.ToString());
            }
            finally
            {
                Task shutdownTask = _appSession.ShutdownAsync();
                shutdownTask.Wait();
            }
        }
        // Returns the Uri of conference to join.
        public String GetConferenceURI()
        {
            String str = "";

            try
            {
                if (ConfigurationManager.AppSettings[_conferenceURIPrompt] != null)
                {
                    _conferenceURI = ConfigurationManager.AppSettings[_conferenceURIPrompt];
                    NonBlockingConsole.WriteLine("\nUsing {0} as remote user", _conferenceURI);
                    return(_conferenceURI);
                }
                else
                {
                    // Prompt user for conference URI
                    _conferenceURI = UcmaHelper.PromptUser("Enter the URI for the conference to join, in the sip:User@Host format or tel:+1XXXYYYZZZZ format => ", "ConferenceURI");
                    return(str);
                }
            }
            catch (InvalidOperationException iOpEx)
            {
                // Invalid Operation Exception should only be thrown on poorly-entered input.
                NonBlockingConsole.WriteLine("Invalid Operation Exception: " + iOpEx.ToString());
                return(str);
            }
        }
Example #4
0
        internal void RaiseTranscriptRecorderSessionShutdown(TranscriptRecorderSessionShutdownEventArgs args)
        {
            NonBlockingConsole.WriteLine("Raising TranscriptRecorderSessionShutdown event. SessionId: {0}. ConversationId: {1}. ConferenceUri: {2}",
                                         args.SessionId.ToString(), (args.Conversation == null) ? "null" : args.Conversation.Id,
                                         (args.Conference == null) ? "null" : args.Conference.ConferenceUri);

            Message m = new Message("Raising TranscriptRecorderSessionShutdown event. SessionId: " + _sessionId + ".",
                                    MessageType.Info,
                                    (args.Conversation == null) ? "null" : args.Conversation.Id,
                                    (args.Conference == null) ? "null" : args.Conference.ConferenceUri);

            this.OnMessageReceived(m);

            try
            {
                if (this.TranscriptRecorderSessionShutdown != null)
                {
                    this.TranscriptRecorderSessionShutdown.Invoke(this, args);
                }
            }
            catch (Exception e)
            {
                NonBlockingConsole.WriteLine("Error: Exception occured in RaiseTranscriptRecorderSessionShutdown(): {0}.",
                                             e.ToString());
            }
        }
Example #5
0
        public async Task RunAsync(CancellationToken token)
        {
            NonBlockingConsole.WriteLine("RunAsync - Entry");
            bool        startTask = true;
            List <Task> runTasks  = new List <Task>();

            lock (s_lock)
            {
                if (_state != TranscriptSessionManagerState.Created)
                {
                    NonBlockingConsole.WriteLine("RunAsync - Warn: TranscriptSessionManager is already running.");
                    startTask = false;
                    if (_runTask != null)
                    {
                        runTasks.Add(_runTask);
                    }
                }
                else
                {
                    _state       = TranscriptSessionManagerState.Idle;
                    _cancelToken = token;
                    RegisterEndpointEvents();
                    _runTask = new Task(() =>
                    {
                            #if (CONVERSATION_DIALIN_ENABLED)
                        string conversationUri = _helper.GetRemoteUserURI();
                        if (string.IsNullOrEmpty(conversationUri))
                        {
                            NonBlockingConsole.WriteLine("Error: Valid remote user Uri must be provided for CONVERSATION_DIALIN_ENABLED mode.\n Exiting...\n");
                            return;
                        }
                        StartConversationTranscriptRecorderSession(conversationUri);
                            #endif // (CONVERSATION_DIALIN_ENABLED)

                            #if (CONFERENCE_DIALIN_ENABLED)
                        string conferenceUri = _helper.GetConferenceURI();
                        if (string.IsNullOrEmpty(conferenceUri))
                        {
                            NonBlockingConsole.WriteLine("Error: Valid conference Uri must be provided for CONFERENCE_DIALIN_ENABLED mode.\n Exiting...\n");
                            return;
                        }
                        StartConferenceTranscriptRecorderSession(conferenceUri);
                            #endif // CONFERENCE_DIALIN_ENABLED

                        _waitForTranscriptSessionStarted.WaitOne();
                        _waitForTranscriptSessionTerminated.WaitOne();
                    }, token);
                    runTasks.Add(_runTask);
                }
            } // lock

            if (startTask)
            {
                _runTask.Start();
            }

            await Task.WhenAll(runTasks.ToArray());

            NonBlockingConsole.WriteLine("RunAsync - Exit");
        }
 /// <summary>
 /// Displays <paramref name="textToDisplay"/> and pauses the console to for easier viewing of logs.
 /// </summary>
 /// <param name="textToDisplay">Text to display with whitespace around it.</param>
 public static void PauseBeforeContinuing(string textToDisplay)
 {
     NonBlockingConsole.WriteLine("\n\n********************");
     NonBlockingConsole.WriteLine(textToDisplay);
     NonBlockingConsole.WriteLine("********************\n\n");
     Console.ReadLine();
 }
Example #7
0
 /// <summary>
 /// Delegate that is called when an incoming ConferenceInvite is received.
 /// </summary>
 /// <param name="sender"></param>
 /// <param name="e"></param>
 void UserEndpoint_ConferenceInvitationReceived(object sender, ConferenceInvitationReceivedEventArgs e)
 {
     Task.Factory.StartNew(() =>
     {
         try
         {
             ConferenceInvitation invite = e.Invitation;
             Conversation conversation   = invite.Conversation;
             // TODO: indexing by conv id doesn't work for "public meeting recording" scenario
             if (_activeConversationSessions.ContainsKey(conversation))
             {
                 _activeConversationSessions[conversation].AddIncomingInvitedConference(e);
             }
             else
             {
                 TranscriptRecorderSession t = new TranscriptRecorderSession(e);
                 _activeConversationSessions.Add(conversation, t);
             }
         }
         catch (Exception ex)
         {
             NonBlockingConsole.WriteLine("Error: Exception thrown in UserEndpoint_ConferenceInvitationReceived: " + ex.ToString());
         }
         finally
         {
             _waitForTranscriptSessionStarted.Set();
         }
     });
 }
        private void EndEndpointEstablish(IAsyncResult ar)
        {
            LocalEndpoint currentEndpoint = ar.AsyncState as LocalEndpoint;

            try
            {
                currentEndpoint.EndEstablish(ar);
            }
            catch (AuthenticationException authEx)
            {
                // AuthenticationException will be thrown when the credentials are invalid.
                NonBlockingConsole.WriteLine(authEx.Message);
                throw;
            }
            catch (ConnectionFailureException connFailEx)
            {
                // ConnectionFailureException will be thrown when the endpoint cannot connect to the server, or the credentials are invalid.
                NonBlockingConsole.WriteLine(connFailEx.Message);
                throw;
            }
            catch (InvalidOperationException iOpEx)
            {
                // InvalidOperationException will be thrown when the endpoint is not in a valid state to connect. To connect, the platform must be started and the Endpoint Idle.
                NonBlockingConsole.WriteLine(iOpEx.Message);
                throw;
            }
            finally
            {
                // Again, just for sync. reasons.
                _endpointInitCompletedEvent.Set();
            }
        }
Example #9
0
 /// <summary>
 /// Delegate that is called when an incoming InstantMessagingCall arrives.
 /// </summary>
 /// <param name="sender"></param>
 /// <param name="e"></param>
 void InstantMessagingCall_Received(object sender, CallReceivedEventArgs <InstantMessagingCall> e)
 {
     Task.Factory.StartNew(() =>
     {
         try
         {
             if (_activeConversationSessions.ContainsKey(e.Call.Conversation))
             {
                 _activeConversationSessions[e.Call.Conversation].AddIMIncomingCall(e);
             }
             else if (e.IsNewConversation)
             {
                 Conversation c = e.Call.Conversation;
                 TranscriptRecorderSession t          = new TranscriptRecorderSession(e);
                 t.TranscriptRecorderSessionChanged  += this.TranscriptRecorder_OnTranscriptRecorderSessionChanged;
                 t.TranscriptRecorderSessionShutdown += this.TranscriptRecorder_OnTranscriptRecorderSessionShutdown;
                 _activeConversationSessions.Add(c, t);
             }
             else if (e.IsConferenceDialOut)
             {
                 // TODO: Join Conference then accept IM call
                 throw new NotImplementedException("InstantMessagingCall_Received with ConferenceDialOut AudioVideoCall is not yet supported.");
             }
         }
         catch (Exception ex)
         {
             NonBlockingConsole.WriteLine("Error: Exception thrown in InstantMessagingCall_Received: " + ex.ToString());
         }
         finally
         {
             _waitForTranscriptSessionStarted.Set();
         }
     });
 }
Example #10
0
        internal void OnMediaTranscriptRecorderError(Message m)
        {
            NonBlockingConsole.WriteLine("Error message loged: " + m.ToString());

            _messages.Add(m);

            // TODO: Logic to handle errors that can happen in indiviual recorders/modality calls
        }
Example #11
0
        public void Shutdown()
        {
            if (_state == TranscriptRecorderState.Terminated)
            {
                return;
            }
            _state = TranscriptRecorderState.Terminated;

            TranscriptRecorderSessionShutdownEventArgs shutdownArgs = null;

            lock (_transcriptRecorders)
            {
                foreach (MediaTranscriptRecorder m in _transcriptRecorders)
                {
                    m.Shutdown();
                }
                _transcriptRecorders.Clear();
            }

            try
            {
                Message shutdownMessage = new Message("TranscriptRecorderSession is shutting down. SessionId: ", _sessionId.ToString());
                this.OnMessageReceived(shutdownMessage);

                shutdownArgs = new TranscriptRecorderSessionShutdownEventArgs(this);

                if (_conversation != null &&
                    ((_conversation.State != ConversationState.Terminating) || (_conversation.State != ConversationState.Terminated)))
                {
                    Message m = new Message("Conversation shutting down.", _conversation.LocalParticipant.DisplayName,
                                            _conversation.LocalParticipant.UserAtHost, _conversation.LocalParticipant.Uri, DateTime.Now,
                                            _conversation.Id, (this.Conference == null) ? "null" : this.Conference.ConferenceUri,
                                            MessageType.ConversationInfo, MessageDirection.Outgoing);
                    this.OnMessageReceived(m);

                    _conversation.BeginTerminate(EndTerminateConversation, _conversation);
                    //_waitForConversationTerminated.WaitOne();
                    _conversation = null;
                }
                else
                {
                    _waitForConversationTerminated.Set();
                }

                ShutdownConversationContext();
            }
            catch (Exception e)
            {
                NonBlockingConsole.WriteLine("TranscriptRecorderSession.Shutdown(). Exception occured during conversation shutdown: {0}",
                                             e.ToString());
            }
            finally
            {
                // Raise Shutdown event to MeetingTranscriptSessionManager
                this.RaiseTranscriptRecorderSessionShutdown(shutdownArgs);
            }
        }
Example #12
0
        // Callback for BeginContextSendData on the context channel.
        void BeginContextSendDataCB(IAsyncResult res)
        {
            ConversationContextChannel channel = res as ConversationContextChannel;

            try
            {
                channel.EndSendData(res);
            }
            catch (Exception e)
            {
                NonBlockingConsole.WriteLine("Error: Context channel SendData failed with exception: " + e.ToString());
            }
        }
Example #13
0
        void TranscriptRecorder_OnTranscriptRecorderSessionShutdown(object sender, TranscriptRecorderSessionShutdownEventArgs e)
        {
            NonBlockingConsole.WriteLine("TranscriptRecorder_OnTranscriptRecorderSessionShutdown event. SessionId: {0}. ConversationId: {1}. ConferenceId: {2}",
                                         e.SessionId.ToString(),
                                         (e.Conversation == null) ? "null" : e.Conversation.Id,
                                         (e.Conference == null) ? "null" : e.Conference.ConferenceUri);

            Task task = StopTranscriptRecorderSessionAsync(e.SessionId, false);

            if (task != null)
            {
                task.Wait();
            }
        }
        private void EndPlatformShutdown(IAsyncResult ar)
        {
            CollaborationPlatform collabPlatform = ar.AsyncState as CollaborationPlatform;

            try
            {
                //Shutdown actions will not throw.
                collabPlatform.EndShutdown(ar);
                NonBlockingConsole.WriteLine("The platform is now shut down.");
            }
            finally
            {
                _platformShutdownCompletedEvent.Set();
            }
        }
Example #15
0
        internal void OnMessageReceived(Message m, string appId = null)
        {
            NonBlockingConsole.WriteLine("Message logged: " + m.ToString());

            _messages.Add(m);
            if (_localConvContextChannel != null)
            {
                ContentType contentType = new ContentType();
                _localConvContextChannel.BeginSendData(contentType, Constants.GetBytes(MessageToContextualData(m)), this.BeginContextSendDataCB, _localConvContextChannel);
            }
            if (_convContextChannel != null)
            {
                ContentType contentType = new ContentType();
                _convContextChannel.BeginSendData(contentType, Constants.GetBytes(MessageToContextualData(m)), this.BeginContextSendDataCB, _convContextChannel);
            }
        }
Example #16
0
        public string GetFullTranscript(bool print = false)
        {
            String transcript = "";

            foreach (Message m in _messages)
            {
                transcript += m.ToString() + "\n";
            }

            if (print)
            {
                NonBlockingConsole.WriteLine("Meeting Transcript:\n" + transcript);
            }

            return(transcript);
        }
Example #17
0
        private void SaveTranscript(TranscriptRecorderSession trs)
        {
            NonBlockingConsole.WriteLine("SaveTranscript - Entry");

            string filename = "LyncMeetingTranscript.txt";

            using (FileStream fs = new FileStream(filename, FileMode.OpenOrCreate))
            {
                using (BinaryWriter w = new BinaryWriter(fs))
                {
                    w.Write(trs.GetFullTranscript());
                }
            }

            NonBlockingConsole.WriteLine("SaveTranscript - Exit");
        }
Example #18
0
        // Callback for BeginContextEstablish on the context channel.
        void BeginContextEstablishCB(IAsyncResult res)
        {
            ConversationContextChannel channel = res as ConversationContextChannel;

            try
            {
                if (channel.State == ConversationContextChannelState.Establishing)
                {
                    NonBlockingConsole.WriteLine("Context channel is in the Establishing state");
                    channel.EndEstablish(res);
                }
            }
            catch (Exception e)
            {
                NonBlockingConsole.WriteLine("Error: Context channel establish failed with exception: " + e.ToString());
            }
        }
Example #19
0
        private void RegisterEndpointEvents()
        {
            NonBlockingConsole.WriteLine("RegisterEndpointEvents - Entry");
            if (_userEndpoint != null)
            {
                #if (CONVERSATION_DIALOUT_ENABLED || CONFERENCE_DIALOUT_ENABLED)
                _userEndpoint.RegisterForIncomingCall <AudioVideoCall>(AudioVideoCall_Received);
                _userEndpoint.RegisterForIncomingCall <InstantMessagingCall>(InstantMessagingCall_Received);
                #endif // (CONVERSATION_DIALOUT_ENABLED || CONFERENCE_DIALOUT_ENABLED)

                #if (CONFERENCE_DIALOUT_ENABLED)
                _userEndpoint.ConferenceInvitationReceived += new EventHandler <ConferenceInvitationReceivedEventArgs>(UserEndpoint_ConferenceInvitationReceived);
                #endif // (CONFERENCE_DIALOUT_ENABLED)
            }

            NonBlockingConsole.WriteLine("RegisterEndpointEvents - Exit");
        }
Example #20
0
        public void AddIncomingInvitedConference(ConferenceInvitationReceivedEventArgs e, CancellationTokenSource cts = null)
        {
            if (_state != TranscriptRecorderState.Active)
            {
                NonBlockingConsole.WriteLine("Warn: AddIncomingInvitedConferece in unexpected TranscriptRecorderSession state: " + _state.ToString());
            }

            // Log Conference Invitation Recv
            ConversationParticipant caller = e.RemoteParticipant;

            Message m = new Message("Conference Started Invite Accept Started.", caller.DisplayName, caller.UserAtHost, caller.Uri, DateTime.Now,
                                    "null", e.Invitation.ConferenceUri,
                                    MessageType.ConferenceInfo, MessageDirection.Outgoing);

            this.OnMessageReceived(m);

            e.Invitation.BeginAccept(ConferenceInvitation_AcceptCompleted, e.Invitation);
        }
        /// <summary>
        /// If the 'key' is not found in app config, prompts the user using prompt text.
        /// </summary>
        /// <param name="promptText">If key is not found in app.Config, the user will be prompted for input using this parameter.</param>
        /// <param name="key">Searches for this key in app.Config and returns if found. Pass null to always prompt.</param>
        /// <returns>String value either from App.Config or user input.</returns>
        public static string PromptUser(string promptText, string key)
        {
            String value;

            if (String.IsNullOrEmpty(key) || ConfigurationManager.AppSettings[key] == null)
            {
                NonBlockingConsole.WriteLine(string.Empty);
                Console.Write(promptText);
                value = Console.ReadLine();
            }
            else
            {
                value = ConfigurationManager.AppSettings[key];
                NonBlockingConsole.WriteLine("Using keypair {0} - {1} from AppSettings...", key, value);
            }

            return(value);
        }
        // Method to establish an already created UserEndpoint.
        // This method returns an established UserEndpoint object. If you do not want to monitor LocalOwnerPresence, you may
        // want to call the CreateEstablishedUserEndpoint method directly. Otherwise, you may call ReadUserSettings
        // followed by CreateUserEndpoint, followed by EstablishUserEndpoint methods.
        public bool EstablishUserEndpoint(UserEndpoint userEndpoint)
        {
            // Startup the platform, if not already
            if (_isPlatformStarted == false)
            {
                userEndpoint.Platform.BeginStartup(EndPlatformStartup, userEndpoint.Platform);

                // Sync; wait for the platform startup to complete.
                _platformStartupCompleted.WaitOne();
                NonBlockingConsole.WriteLine("Platform started...");
                _isPlatformStarted = true;
            }
            // Establish the user endpoint
            userEndpoint.BeginEstablish(EndEndpointEstablish, userEndpoint);

            // Sync; wait for the registration to complete.
            _endpointInitCompletedEvent.WaitOne();
            NonBlockingConsole.WriteLine("Endpoint established...");
            return(true);
        }
Example #23
0
        internal void RaiseTranscriptRecorderSessionChanged(ConferenceSession conference)
        {
            NonBlockingConsole.WriteLine("Raising TranscriptRecorderSessionShutdown event. SessionId: {0}. ConferenceUri: {1}",
                                         this.SessionId.ToString(), (conference == null) ? "null" : conference.ConferenceUri);

            try
            {
                if (this.TranscriptRecorderSessionChanged != null)
                {
                    TranscriptRecorderSessionChangedEventArgs args = new TranscriptRecorderSessionChangedEventArgs(this);
                    this.TranscriptRecorderSessionChanged.Invoke(this, args);
                }
            }
            catch (Exception e)
            {
                NonBlockingConsole.WriteLine("Error: Exception occured in TranscriptRecorderSessionChanged(). Conference: {0}. {1}.",
                                             conference.ConferenceUri,
                                             e.ToString());
            }
        }
Example #24
0
        private void ConferenceInvitation_AcceptCompleted(IAsyncResult result)
        {
            try
            {
                ConferenceInvitation invite = result.AsyncState as ConferenceInvitation;
                invite.EndAccept(result);

                if (_conversation == null)
                {
                    _conversation = invite.Conversation;

                    _conversationTranscriptRecorder = new ConversationTranscriptRecorder(this, _conversation);
                    _transcriptRecorders.Add(_conversationTranscriptRecorder);
                    _conversationToCallTranscriptMapping.Add(_conversationTranscriptRecorder, new List <MediaTranscriptRecorder>());

                    // TODO: Handle case where we're already joined into a different meeting for this conv?

                    ConferenceTranscriptRecorder conferenceTranscriptRecorder = new ConferenceTranscriptRecorder(this, _conversation);
                    _transcriptRecorders.Add(conferenceTranscriptRecorder);
                    _conversationToCallTranscriptMapping[_conversationTranscriptRecorder].Add(conferenceTranscriptRecorder);

                    conferenceTranscriptRecorder.ConferenceInviteAccepted(result);
                }
                else
                {
                    NonBlockingConsole.WriteLine("Warn: Already have a Conference/active conversation");
                    // Treat this as a sub conversation?

                    /*
                     * subConvRecorder = new ConversationTranscriptRecorder(this, subConversation, true);
                     * _transcriptRecorders.Add(subConvRecorder);
                     * _conversationToCallTranscriptMapping.Add(subConvRecorder, new List<MediaTranscriptRecorder>());
                     * _conversationToCallTranscriptMapping[subConvRecorder].Add(addingTranscriptRecorder);
                     */
                }
            }
            catch (Exception e)
            {
                NonBlockingConsole.WriteLine("Error: Exception occurred during conference invite acceptance: " + e.ToString());
            }
        }
Example #25
0
        public void AddIMIncomingCall(CallReceivedEventArgs <InstantMessagingCall> e, CancellationTokenSource cts = null)
        {
            if (_state != TranscriptRecorderState.Active)
            {
                NonBlockingConsole.WriteLine("Warn: AddIMIncomingCall in unexpected TranscriptRecorderSession state: " + _state.ToString());
            }

            IMTranscriptRecorder    i      = new IMTranscriptRecorder(this);
            ConversationParticipant caller = e.RemoteParticipant;

            Message m = new Message("InstantMessaging Conversation Participant Added.", caller.DisplayName,
                                    caller.UserAtHost, caller.Uri, DateTime.Now,
                                    _conversation.Id, _conversation.ConferenceSession.ConferenceUri,
                                    MessageType.ConversationInfo, MessageDirection.Outgoing);

            this.OnMessageReceived(m);

            _transcriptRecorders.Add(i);
            _conversationToCallTranscriptMapping[_conversationTranscriptRecorder].Add(i);

            i.InstantMessagingCall_Received(e);
        }
        private void EndPlatformStartup(IAsyncResult ar)
        {
            CollaborationPlatform collabPlatform = ar.AsyncState as CollaborationPlatform;

            try
            {
                // The platform should now be started.
                collabPlatform.EndStartup(ar);
                // It should be noted that all the re-thrown exceptions will crash the application. This is intentional.
                // Ideal exception handling would report the error and shut down nicely. In production code, consider using
                // an IAsyncResult implementation to report the error instead of throwing or put the implementation
                // in this try block.
            }
            catch (OperationFailureException opFailEx)
            {
                // OperationFailureException will be thrown when the platform cannot establish, here, usually due to invalid data.
                NonBlockingConsole.WriteLine(opFailEx.Message);
                throw;
            }
            catch (ConnectionFailureException connFailEx)
            {
                // ConnectionFailureException will be thrown when the platform cannot connect.
                // ClientPlatforms will not throw this exception on startup.
                NonBlockingConsole.WriteLine(connFailEx.Message);
                throw;
            }
            catch (RealTimeException realTimeEx)
            {
                // RealTimeException may be thrown as a result of any UCMA operation.
                NonBlockingConsole.WriteLine(realTimeEx.Message);
                throw;
            }
            finally
            {
                // Again, just for sync. reasons.
                _platformStartupCompleted.Set();
            }
        }
Example #27
0
        void TranscriptRecorder_OnTranscriptRecorderSessionChanged(object sender, TranscriptRecorderSessionChangedEventArgs e)
        {
            NonBlockingConsole.WriteLine("TranscriptRecorder_OnTranscriptRecorderSessionChanged event. SessionId: {0}. ConversationId: {1}. ConferenceId: {2}",
                                         e.SessionId.ToString(),
                                         (e.Conversation == null) ? "null" : e.Conversation.Id,
                                         (e.Conference == null) ? "null" : e.Conference.ConferenceUri);

            TranscriptRecorderSession session = null;

            if ((e.Conversation != null) && (e.Conference != null) &&
                _activeConversationSessions.TryGetValue(e.Conversation, out session))
            {
                // Add TranscriptRecorderSession to conference table (if no entry for this Conference already exists)
                lock (s_lock)
                {
                    if (!_activeConferenceSessions.ContainsKey(e.Conference))
                    {
                        NonBlockingConsole.WriteLine("TranscriptRecorder_OnTranscriptRecorderSessionChanged: Adding TranscriptRecorderSession for Conference entry: {0}.",
                                                     e.Conference.ConferenceUri);
                        _activeConferenceSessions.Add(e.Conference, session);

                        // If successfully added TranscriptRecorderSession to conference table, remove from conversation table
                        if (_activeConversationSessions.ContainsKey(e.Conversation))
                        {
                            NonBlockingConsole.WriteLine("TranscriptRecorder_OnTranscriptRecorderSessionChanged: Removing TranscriptRecorderSession for Conversation entry: {0}.",
                                                         e.Conversation.Id);

                            _activeConversationSessions.Remove(e.Conversation);
                        }
                    }
                } // lock
            }
            else
            {
                NonBlockingConsole.WriteLine("[Warn] TranscriptRecorder_OnTranscriptRecorderSessionChanged called on invalid Conversation or Conference. Ignoring event.");
            }
        }
        // Method to create an established UserEndpoint.
        // This method returns an established UserEndpoint object. If you do not want to monitor LocalOwnerPresence, you may
        // want to call this CreateEstablishedUserEndpoint method directly. Otherwise, you may call ReadUserSettings
        // followed by CreateUserEndpoint, followed by EstablishUserEndpoint methods.
        public UserEndpoint CreateEstablishedUserEndpoint(string endpointFriendlyName)
        {
            UserEndpointSettings userEndpointSettings;
            UserEndpoint         userEndpoint = null;

            try
            {
                // Read user settings
                userEndpointSettings = ReadUserSettings(endpointFriendlyName);

                // Create User Endpoint
                userEndpoint = CreateUserEndpoint(userEndpointSettings);

                // Establish the user endpoint
                EstablishUserEndpoint(userEndpoint);
            }
            catch (InvalidOperationException iOpEx)
            {
                // Invalid Operation Exception should only be thrown on poorly-entered input.
                NonBlockingConsole.WriteLine("Invalid Operation Exception: " + iOpEx.ToString());
            }

            return(userEndpoint);
        }
        // Method to read user settings from app.config file or from the console prompts
        // This method returns a UserEndpointSettings object. If you do not want to monitor LocalOwnerPresence, you may
        // want to call the CreateEstablishedUserEndpoint method directly. Otherwise, you may call ReadUserSettings
        // followed by CreateUserEndpoint, followed by EstablishUserEndpoint methods.
        public UserEndpointSettings ReadUserSettings(string userFriendlyName)
        {
            UserEndpointSettings userEndpointSettings = null;
            string prompt = string.Empty;

            if (string.IsNullOrEmpty(userFriendlyName))
            {
                userFriendlyName = "Default User";
            }

            try
            {
                NonBlockingConsole.WriteLine(string.Empty);
                NonBlockingConsole.WriteLine("Creating User Endpoint for {0}...", userFriendlyName);
                NonBlockingConsole.WriteLine("");

                if (ConfigurationManager.AppSettings[_serverFQDNPrompt + _userCount] != null)
                {
                    _serverFqdn = ConfigurationManager.AppSettings[_serverFQDNPrompt + _userCount];
                    NonBlockingConsole.WriteLine("Using {0} as Microsoft Lync Server", _serverFqdn);
                }
                else
                {
                    // Prompt user for server FQDN. If server FQDN was entered before, then let the user use the saved value.
                    string        localServer;
                    StringBuilder promptBuilder = new StringBuilder();
                    if (!string.IsNullOrEmpty(_serverFqdn))
                    {
                        promptBuilder.Append("Current Microsoft Lync Server = ");
                        promptBuilder.Append(_serverFqdn);
                        promptBuilder.AppendLine(". Please hit ENTER to retain this setting - OR - ");
                    }

                    promptBuilder.Append("Please enter the FQDN of the Microsoft Lync Server that the ");
                    promptBuilder.Append(userFriendlyName);
                    promptBuilder.Append(" endpoint is homed on => ");
                    localServer = PromptUser(promptBuilder.ToString(), null);

                    if (!String.IsNullOrEmpty(localServer))
                    {
                        _serverFqdn = localServer;
                    }
                }

                // Prompt user for user name
                prompt = String.Concat("Please enter the User Name for ",
                                       userFriendlyName,
                                       " (or hit the ENTER key to use current credentials)\r\n" +
                                       "Please enter the User Name => ");
                _userName = PromptUser(prompt, _userNamePrompt + _userCount);

                // If user name is empty, use current credentials
                if (string.IsNullOrEmpty(_userName))
                {
                    NonBlockingConsole.WriteLine("Username was empty - using current credentials...");
                    _useSuppliedCredentials = true;
                }
                else
                {
                    // Prompt for password
                    prompt        = String.Concat("Enter the User Password for ", userFriendlyName, " => ");
                    _userPassword = PromptUser(prompt, null);

                    prompt      = String.Concat("Please enter the User Domain for ", userFriendlyName, " => ");
                    _userDomain = PromptUser(prompt, _userDomainPrompt + _userCount);
                }

                // Prompt user for user URI
                prompt   = String.Concat("Please enter the User URI for ", userFriendlyName, " in the User@Host format => ");
                _userURI = PromptUser(prompt, _userURIPrompt + _userCount);
                if (!(_userURI.ToLower().StartsWith("sip:") || _userURI.ToLower().StartsWith("tel:")))
                {
                    _userURI = "sip:" + _userURI;
                }

                // Increment the last user number
                _userCount++;

                // Initalize and register the endpoint, using the credentials of the user the application will be acting as.
                // NOTE: the _userURI should always be of the form "sip:user@host"
                userEndpointSettings = new UserEndpointSettings(_userURI, _serverFqdn);

                if (!_useSuppliedCredentials)
                {
                    _credential = new System.Net.NetworkCredential(_userName, _userPassword, _userDomain);
                    userEndpointSettings.Credential = _credential;
                }
                else
                {
                    userEndpointSettings.Credential = System.Net.CredentialCache.DefaultNetworkCredentials;
                }
            }
            catch (InvalidOperationException iOpEx)
            {
                // Invalid Operation Exception should only be thrown on poorly-entered input.
                NonBlockingConsole.WriteLine("Invalid Operation Exception: " + iOpEx.ToString());
            }

            return(userEndpointSettings);
        }
 /// <summary>
 /// Prompts the user to press a key, unblocking any waiting calls to the
 /// <code>WaitForSampleFinish</code> method
 /// </summary>
 public static void FinishSample()
 {
     NonBlockingConsole.WriteLine("Please hit any key to end the sample.");
     Console.ReadKey();
     _sampleFinished.Set();
 }