/// <summary> /// Logs an incoming message to the Bot. Core channel information is stored along with the original message and sentiment. /// Logs an event called "BotMessageReceived". /// Logs the following properties: conversation id, from user id, conversation name, channel id, client info. /// Other info logged based on configuration: from user name, original message, sentiment. /// </summary> /// <param name="activity">The incoming Activity from the Bot Framework.</param> /// <param name="additionalProperties">Additional properties to log.</param> public void LogIncomingMessage(ITurnContext context, Dictionary <string, string> additionalProperties = null) { // Simple guard statements EnsureNotNull(context, "Context activity cannot be null"); // Track the event with an exception wrapper TrackEventWithExceptionWrapper((TelemetryClient tc) => { var activity = context.Activity.AsMessageActivity(); Dictionary <string, string> telemetryProperties = new Dictionary <string, string>(); // Set the Telemetry context as we don't have a BF Telemetry Initializer at this time... if (!string.IsNullOrEmpty(activity.Conversation.Id)) { tc.Context.Session.Id = activity.Conversation.Id; } if (!string.IsNullOrEmpty(activity.From.Id)) { tc.Context.User.Id = activity.From.Id; } // General message metadata, defensive to cover for discrepancies across channels if (!string.IsNullOrEmpty(activity.ChannelId)) { telemetryProperties.Add(TelemetryHelper.ChannelProperty, activity.ChannelId); } if (!string.IsNullOrEmpty(activity.From.Id)) { telemetryProperties.Add(TelemetryHelper.FromIdProperty, activity.From.Id); } if (!string.IsNullOrEmpty(activity.Conversation.Id)) { telemetryProperties.Add(TelemetryHelper.ConversationIdProperty, activity.Conversation.Id); } if (!string.IsNullOrEmpty(activity.Conversation.Name)) { telemetryProperties.Add(TelemetryHelper.ConversationNameProperty, activity.Conversation.Name); } if (additionalProperties != null) { foreach (KeyValuePair <string, string> valuePair in additionalProperties) { if (telemetryProperties.ContainsKey(valuePair.Key)) { telemetryProperties[valuePair.Key] = valuePair.Value; } else { telemetryProperties.Add(valuePair.Key, valuePair.Value); } } } // Now check for specific entities such as client info... if (activity.Entities != null) { // Do we have any client info? e.g. language, locale, device type var clientEntity = activity.Entities.FirstOrDefault(e => e.Type == TelemetryHelper.ClientInfoIdentifier); if (clientEntity != null && clientEntity.Properties != null) { string prop = clientEntity.Properties.ToString(); telemetryProperties.Add(TelemetryHelper.ClientInfoProperty, prop); } } // For some customers, logging user name within Application Insights might be an issue so have provided a config setting to disable this feature if (logUserName && !string.IsNullOrEmpty(activity.From.Name)) { telemetryProperties.Add(TelemetryHelper.FromNameProperty, activity.From.Name); } // For some customers, logging the utterances within Application Insights might be an so have provided a config setting to disable this feature if (logOriginalMessages && !string.IsNullOrEmpty(activity.Text)) { telemetryProperties.Add(TelemetryHelper.TextProperty, activity.Text); } // Only perform Text Analytics operations if we have a valid key and log sentiment is set to true if (!string.IsNullOrEmpty(textAnalyticsKey) && logSentimentAndKeyPhrases && !string.IsNullOrEmpty(activity.Text)) { try { // Crude but gets a good sense of the utterance length. // NOTE: before splitting, replace multiple instances of spaces with a single space // and trim either end, so that we do not skew the amount of words in the trimmed list. string modifiedText = Regex.Replace(activity.Text, @"\s+", " ").Trim(); string[] words = activity.Text.Split(' '); // Sentiment and Key Phrase extraction is not effective on short utterances so we skip if less than the provided threshold if (words.Length >= sentimentWordThreshold) { // For each utterance we identify the language in order to then extract key-phrases and sentiment which is addded to the Telemetry // LUIS now performs Key Phrase Extraction and Sentiment for you but not exposed as part of the v4SDK, once fixed this can be removed. var(identifiedLanguage, keyPhrases, sentiment) = TextAnalyticsHelper.EvaluateUtterance(textAnalyticsClient, activity.Text); if (!string.IsNullOrEmpty(identifiedLanguage)) { telemetryProperties.Add(TelemetryHelper.LanguageProperty, identifiedLanguage); } if (!string.IsNullOrEmpty(keyPhrases)) { telemetryProperties.Add(TelemetryHelper.KeyPhrasesProperty, keyPhrases); } if (sentiment != int.MinValue) { string sentimentScore = sentiment.ToString("N2"); telemetryProperties.Add(TelemetryHelper.SentimentProperty, sentimentScore); } } else { tc.TrackTrace($"TelemetryHelper::LogIncomingMessage::No sentiment calculated for a utterance with {words.Length} word(s)."); } } catch (Exception e) { tc.TrackException(e); tc.TrackTrace($"TelemetryHelper::Exception ocurred whilst calculating sentiment - skipping but still logging without it. {e.Message}"); } } // Log the event tc.TrackEvent(TelemetryHelper.BotMessageReceivedEvent, telemetryProperties); }); }