/// <summary>
 /// Initializes a new instance of the <see cref="RemoteSupportActivityHandler"/> class.
 /// </summary>
 /// <param name="microsoftAppCredentials">Microsoft Application credentials for Bot/ME.</param>
 /// <param name="logger">Sends logs to the Application Insights service.</param>
 /// <param name="localizer">The current cultures' string localizer.</param>
 /// <param name="telemetryClient">The Application Insights telemetry client. </param>
 /// <param name="options">A set of key/value application configuration properties.</param>
 /// <param name="ticketDetailStorageProvider">Provider to store ticket details to Azure Table Storage.</param>
 /// <param name="onCallSupportDetailSearchService">Provider to search on call support details in Azure Table Storage.</param>
 /// <param name="ticketSearchService">Provider to search ticket details in Azure Table Storage.</param>
 /// <param name="tokenHelper">Generating custom JWT token and retrieving access token for user.</param>
 /// <param name="cardConfigurationStorageProvider">Provider to search card configuration details in Azure Table Storage.</param>
 /// <param name="ticketGenerateStorageProvider">Provider to get ticket id to Azure Table Storage.</param>
 /// <param name="onCallSupportDetailStorageProvider"> Provider for fetching and storing information about on call support in storage table.</param>
 public RemoteSupportActivityHandler(
     MicrosoftAppCredentials microsoftAppCredentials,
     ILogger <RemoteSupportActivityHandler> logger,
     IStringLocalizer <Strings> localizer,
     TelemetryClient telemetryClient,
     IOptions <RemoteSupportActivityHandlerOptions> options,
     ITicketDetailStorageProvider ticketDetailStorageProvider,
     IOnCallSupportDetailSearchService onCallSupportDetailSearchService,
     ITicketSearchService ticketSearchService,
     ICardConfigurationStorageProvider cardConfigurationStorageProvider,
     ITokenHelper tokenHelper,
     ITicketIdGeneratorStorageProvider ticketGenerateStorageProvider,
     IOnCallSupportDetailStorageProvider onCallSupportDetailStorageProvider)
 {
     this.microsoftAppCredentials = microsoftAppCredentials;
     this.logger                             = logger;
     this.localizer                          = localizer;
     this.telemetryClient                    = telemetryClient;
     this.options                            = options ?? throw new ArgumentNullException(nameof(options));
     this.teamId                             = this.options.Value.TeamId;
     this.ticketDetailStorageProvider        = ticketDetailStorageProvider;
     this.ticketSearchService                = ticketSearchService;
     this.onCallSupportDetailSearchService   = onCallSupportDetailSearchService;
     this.appBaseUrl                         = this.options.Value.AppBaseUri;
     this.tokenHelper                        = tokenHelper;
     this.cardConfigurationStorageProvider   = cardConfigurationStorageProvider;
     this.ticketGenerateStorageProvider      = ticketGenerateStorageProvider;
     this.onCallSupportDetailStorageProvider = onCallSupportDetailStorageProvider;
 }
Пример #2
0
        /// <summary>
        /// Handle message activity in channel.
        /// </summary>
        /// <param name="message">A message in a conversation.</param>
        /// <param name="turnContext">Context object containing information cached for a single turn of conversation with a user.</param>
        /// <param name="onCallSupportDetailSearchService">Provider to search on call support details in Azure Table Storage.</param>
        /// <param name="ticketDetailStorageProvider">Provider to store ticket details to Azure Table Storage.</param>
        /// <param name="cardConfigurationStorageProvider">Provider to search card configuration details in Azure Table Storage.</param>
        /// <param name="logger">Sends logs to the Application Insights service.</param>
        /// <param name="appBaseUrl">Represents the Application base Uri.</param>
        /// <param name="localizer">The current cultures' string localizer.</param>
        /// <param name="cancellationToken">Propagates notification that operations should be canceled.</param>
        /// <returns>A task that represents the work queued to execute.</returns>
        internal static async Task OnMessageActivityInChannelAsync(
            IMessageActivity message,
            ITurnContext <IMessageActivity> turnContext,
            IOnCallSupportDetailSearchService onCallSupportDetailSearchService,
            ITicketDetailStorageProvider ticketDetailStorageProvider,
            ICardConfigurationStorageProvider cardConfigurationStorageProvider,
            ILogger logger,
            string appBaseUrl,
            IStringLocalizer <Strings> localizer,
            CancellationToken cancellationToken)
        {
            turnContext = turnContext ?? throw new ArgumentNullException(nameof(turnContext));
            if (!string.IsNullOrEmpty(message.ReplyToId) && message.Value != null && ((JObject)message.Value).HasValues)
            {
                logger.LogInformation($"Card submit in channel {message.Value?.ToString()}");
                await OnAdaptiveCardSubmitInChannelAsync(message : message, turnContext : turnContext, ticketDetailStorageProvider : ticketDetailStorageProvider, cardConfigurationStorageProvider : cardConfigurationStorageProvider, logger : logger, appBaseUrl : appBaseUrl, localizer : localizer, cancellationToken : cancellationToken);

                return;
            }

            turnContext.Activity.RemoveRecipientMention();
            string text = turnContext.Activity.Text.Trim();

            switch (text.ToUpperInvariant())
            {
            case Constants.ManageExpertsAction:
                // Get on call support data from storage
                var onCallSupportDetails = await onCallSupportDetailSearchService.SearchOnCallSupportTeamAsync(searchQuery : string.Empty, count : 10);

                var onCallSMEDetailActivity = MessageFactory.Attachment(OnCallSMEDetailCard.GetOnCallSMEDetailCard(onCallSupportDetails, localizer));
                var result = await turnContext.SendActivityAsync(onCallSMEDetailActivity);

                // Add activityId in the data which will be posted to task module in future after clicking on Manage button.
                AdaptiveCard       adaptiveCard = (AdaptiveCard)onCallSMEDetailActivity.Attachments?[0].Content;
                AdaptiveCardAction cardAction   = (AdaptiveCardAction)((AdaptiveSubmitAction)adaptiveCard?.Actions?[0]).Data;
                cardAction.ActivityId = result.Id;

                // Refresh manage experts card with activity Id bound to manage button.
                onCallSMEDetailActivity.Id        = result.Id;
                onCallSMEDetailActivity.ReplyToId = result.Id;
                await turnContext.UpdateActivityAsync(onCallSMEDetailActivity);

                break;

            default:
                logger.LogInformation("Unrecognized input in channel.");
                await turnContext.SendActivityAsync(MessageFactory.Attachment(WelcomeTeamCard.GetCard(appBaseUrl, localizer)));

                break;
            }
        }
Пример #3
0
 /// <summary>
 /// Initializes a new instance of the <see cref="RemoteSupportController"/> class.
 /// </summary>
 /// <param name="logger">Instance to send logs to the Application Insights service.</param>
 /// <param name="botAdapter">Remote support bot adapter.</param>
 /// <param name="microsoftAppCredentials">Microsoft Application credentials for Bot/ME.</param>
 /// <param name="options">A set of key/value application configuration properties.</param>
 /// <param name="onCallSupportDetailSearchService">Provider to search on call support details in Azure Table Storage.</param>
 /// <param name="onCallSupportDetailStorageProvider">Provider to store on call support details in Azure Table Storage.</param>
 /// <param name="cardConfigurationStorageProvider">Provider to store card configuration details in Azure Table Storage.</param>
 public RemoteSupportController(
     ILogger <RemoteSupportController> logger,
     BotFrameworkAdapter botAdapter,
     MicrosoftAppCredentials microsoftAppCredentials,
     IOptions <RemoteSupportActivityHandlerOptions> options,
     IOnCallSupportDetailSearchService onCallSupportDetailSearchService,
     IOnCallSupportDetailStorageProvider onCallSupportDetailStorageProvider,
     ICardConfigurationStorageProvider cardConfigurationStorageProvider)
     : base()
 {
     this.options    = options ?? throw new ArgumentNullException(nameof(options));
     this.logger     = logger;
     this.botAdapter = botAdapter;
     this.appId      = microsoftAppCredentials != null ? microsoftAppCredentials.MicrosoftAppId : throw new ArgumentNullException(nameof(microsoftAppCredentials));
     this.onCallSupportDetailSearchService   = onCallSupportDetailSearchService;
     this.onCallSupportDetailStorageProvider = onCallSupportDetailStorageProvider;
     this.cardConfigurationStorageProvider   = cardConfigurationStorageProvider;
 }
Пример #4
0
        /// <summary>
        /// Method updates experts card in team after modifying on call experts list.
        /// </summary>
        /// <param name="turnContext">Provides context for a turn of a bot.</param>
        /// <param name="onCallExpertsDetail">Details of on call support experts updated.</param>
        /// <param name="onCallSupportDetailSearchService">Provider to search on call support details in Azure Table Storage.</param>
        /// <param name="onCallSupportDetailStorageProvider"> Provider for fetching and storing information about on call support in storage table.</param>
        /// <param name="localizer">The current cultures' string localizer.</param>
        /// <returns>A task that sends notification in newly created channel and mention its members.</returns>
        public static async Task UpdateManageExpertsCardInTeamAsync(ITurnContext <IInvokeActivity> turnContext, OnCallExpertsDetail onCallExpertsDetail, IOnCallSupportDetailSearchService onCallSupportDetailSearchService, IOnCallSupportDetailStorageProvider onCallSupportDetailStorageProvider, IStringLocalizer <Strings> localizer)
        {
            // Get last 10 updated on call support data from storage.
            // This is required because search service refresh interval is 10 minutes. So we need to get latest entry stored in storage from storage provider and append previous 9 updated records to it in order to show on screen.
            var previousOnCallSupportDetails = await onCallSupportDetailSearchService?.SearchOnCallSupportTeamAsync(string.Empty, 9);

            var currentOnCallSupportDetails = await onCallSupportDetailStorageProvider?.GetOnCallSupportDetailAsync(onCallExpertsDetail?.OnCallSupportId);

            List <OnCallSupportDetail> onCallSupportDetails = new List <OnCallSupportDetail>
            {
                currentOnCallSupportDetails,
            };

            onCallSupportDetails.AddRange(previousOnCallSupportDetails);

            // Replace message id in conversation id with card activity id to be refreshed.
            var conversationId = turnContext?.Activity.Conversation.Id;

            conversationId = conversationId?.Replace(turnContext.Activity.Conversation.Id.Split(';')[1].Split("=")[1], onCallExpertsDetail?.OnCallSupportCardActivityId, StringComparison.OrdinalIgnoreCase);
            var onCallSMEDetailCardAttachment = OnCallSMEDetailCard.GetOnCallSMEDetailCard(onCallSupportDetails, localizer);

            // Add activityId in the data which will be posted to task module in future after clicking on Manage button.
            AdaptiveCard       adaptiveCard = (AdaptiveCard)onCallSMEDetailCardAttachment.Content;
            AdaptiveCardAction cardAction   = (AdaptiveCardAction)((AdaptiveSubmitAction)adaptiveCard?.Actions?[0]).Data;

            cardAction.ActivityId = onCallExpertsDetail?.OnCallSupportCardActivityId;

            // Update the card in the SME team with updated on call experts list.
            var updateExpertsCardActivity = new Activity(ActivityTypes.Message)
            {
                Id           = onCallExpertsDetail?.OnCallSupportCardActivityId,
                ReplyToId    = onCallExpertsDetail?.OnCallSupportCardActivityId,
                Conversation = new ConversationAccount {
                    Id = conversationId
                },
                Attachments = new List <Attachment> {
                    onCallSMEDetailCardAttachment
                },
            };
            await turnContext.UpdateActivityAsync(updateExpertsCardActivity);
        }
Пример #5
0
        /// <summary>
        /// Gets the email id's of the SME uses who are available for oncallSupport.
        /// </summary>
        /// <param name="turnContext">Context object containing information cached for a single turn of conversation with a user.</param>
        /// <param name="onCallSupportDetailSearchService">Provider to search on call support details in Azure Table Storage.</param>
        /// <param name="teamId">Team id to which the message is being sent.</param>
        /// <param name="logger">Sends logs to the Application Insights service.</param>
        /// <returns>string with appended email id's.</returns>
        public static async Task <string> GetOnCallSMEuserListAsync(ITurnContext <IInvokeActivity> turnContext, IOnCallSupportDetailSearchService onCallSupportDetailSearchService, string teamId, ILogger <RemoteSupportActivityHandler> logger)
        {
            try
            {
                var teamsChannelAccounts = await TeamsInfo.GetTeamMembersAsync(turnContext, teamId, CancellationToken.None);

                var onCallSupportDetails = await onCallSupportDetailSearchService?.SearchOnCallSupportTeamAsync(string.Empty, 1);

                string onCallSMEUsers = string.Empty;
                if (onCallSupportDetails != null && onCallSupportDetails.Any())
                {
                    var onCallSMEDetail = JsonConvert.DeserializeObject <List <OnCallSMEDetail> >(onCallSupportDetails.First().OnCallSMEs);
                    if (onCallSMEDetail != null)
                    {
                        foreach (var onCallSME in onCallSMEDetail)
                        {
                            onCallSMEUsers += string.IsNullOrEmpty(onCallSMEUsers) ? teamsChannelAccounts.FirstOrDefault(teamsChannelAccount => teamsChannelAccount.AadObjectId == onCallSME.ObjectId)?.Email : "," + teamsChannelAccounts.FirstOrDefault(teamsChannelAccount => teamsChannelAccount.AadObjectId == onCallSME.ObjectId)?.Email;
                        }
                    }
                }

                return(onCallSMEUsers);
            }
#pragma warning disable CA1031 // Do not catch general exception types
            catch (Exception ex)
#pragma warning restore CA1031 // Do not catch general exception types
            {
                logger.LogError(ex, "Error in getting the oncallSMEUsers list.");
            }

            return(null);
        }
        /// <summary>
        /// Gets the email id's of the SME uses who are available for oncallSupport.
        /// </summary>
        /// <param name="turnContext">Context object containing information cached for a single turn of conversation with a user.</param>
        /// <param name="onCallSupportDetailSearchService">Provider to search on call support details in Azure Table Storage.</param>
        /// <param name="teamId">Team id to which the message is being sent.</param>
        /// <param name="memoryCache">MemoryCache instance for caching oncallexpert details.</param>
        /// <param name="logger">Sends logs to the Application Insights service.</param>
        /// <returns>string with appended email id's.</returns>
        public static async Task <string> GetOnCallSMEUserListAsync(ITurnContext <IInvokeActivity> turnContext, IOnCallSupportDetailSearchService onCallSupportDetailSearchService, string teamId, IMemoryCache memoryCache, ILogger <RemoteSupportActivityHandler> logger)
        {
            try
            {
                string onCallSMEUsers = string.Empty;

                var onCallSupportDetails = await onCallSupportDetailSearchService?.SearchOnCallSupportTeamAsync(searchQuery : string.Empty, count : 1);

                if (onCallSupportDetails != null && onCallSupportDetails.Any())
                {
                    var onCallSMEDetails = JsonConvert.DeserializeObject <List <OnCallSMEDetail> >(onCallSupportDetails.First().OnCallSMEs);
                    var expertEmailList  = new List <string>();
                    foreach (var onCallSMEDetail in onCallSMEDetails)
                    {
                        var expertDetails = await TeamMemberCacheHelper.GetMemberInfoAsync(memoryCache, turnContext, onCallSMEDetail.ObjectId, teamId, CancellationToken.None);

                        expertEmailList.Add(expertDetails.Email);
                    }

                    onCallSMEUsers = string.Join(", ", expertEmailList);
                }

                return(onCallSMEUsers);
            }
#pragma warning disable CA1031 // Do not catch general exception types
            catch (Exception ex)
#pragma warning restore CA1031 // Do not catch general exception types
            {
                logger.LogError(ex, "Error in getting the oncallSMEUsers list.");
            }

            return(null);
        }