/// <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;
 }
        /// <summary>
        /// Handle when a message is addressed to the bot in personal scope.
        /// </summary>
        /// <param name="message">Message activity of bot.</param>
        /// <param name="turnContext">The turn context.</param>
        /// <param name="telemetryClient">The Application Insights telemetry client. </param>
        /// <param name="logger">Sends logs to the Application Insights service.</param>
        /// <param name="cardConfigurationStorageProvider">Provider to search card configuration details in Azure Table Storage.</param>
        /// <param name="environment">Hosting environment.</param>
        /// <param name="ticketGenerateStorageProvider">Provider to get ticket id to Azure Table Storage.</param>
        /// <param name="ticketDetailStorageProvider">Provider to store ticket details to Azure Table Storage.</param>
        /// <param name="microsoftAppCredentials">Microsoft Application credentials for Bot/ME.</param>
        /// <param name="appBaseUrl">Represents the Application base Uri.</param>
        /// <param name="localizer">The current cultures' string localizer.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns> A task that represents the work queued to execute for user message activity to bot.</returns>
        internal static async Task OnMessageActivityInPersonalChatAsync(
            IMessageActivity message,
            ITurnContext <IMessageActivity> turnContext,
            TelemetryClient telemetryClient,
            ILogger <RemoteSupportActivityHandler> logger,
            ICardConfigurationStorageProvider cardConfigurationStorageProvider,
            IHostingEnvironment environment,
            ITicketIdGeneratorStorageProvider ticketGenerateStorageProvider,
            ITicketDetailStorageProvider ticketDetailStorageProvider,
            MicrosoftAppCredentials microsoftAppCredentials,
            string appBaseUrl,
            IStringLocalizer <Strings> localizer,
            CancellationToken cancellationToken)
        {
            if (!string.IsNullOrEmpty(message.ReplyToId) && message.Value != null && ((JObject)message.Value).HasValues)
            {
                telemetryClient.TrackTrace("Card submitted in 1:1 chat.");
                await OnAdaptiveCardSubmitInPersonalChatAsync(message: message, turnContext : turnContext, ticketGenerateStorageProvider : ticketGenerateStorageProvider, ticketDetailStorageProvider : ticketDetailStorageProvider, cardConfigurationStorageProvider : cardConfigurationStorageProvider, microsoftAppCredentials : microsoftAppCredentials, logger : logger, appBaseUrl : appBaseUrl, environment : environment, localizer : localizer, cancellationToken : cancellationToken);

                return;
            }

            string text = (turnContext.Activity.Text ?? string.Empty).Trim().ToUpperInvariant();

            switch (text)
            {
            case Constants.NewRequestAction:
                logger.LogInformation("New request action called.");
                CardConfigurationEntity cardTemplateJson = await cardConfigurationStorageProvider.GetConfigurationAsync();

                IMessageActivity newTicketActivity = MessageFactory.Attachment(TicketCard.GetNewTicketCard(cardTemplateJson, localizer));
                await turnContext.SendActivityAsync(newTicketActivity);

                break;

            case Constants.NoCommand:
                return;

            default:
                if (turnContext.Activity.Attachments == null || turnContext.Activity.Attachments.Count == 0)
                {
                    // In case of ME when user clicks on closed or active requests the bot posts adaptive card of request details we don't have to consider this as invalid command.
                    logger.LogInformation("Unrecognized input in End User.");
                    await turnContext.SendActivityAsync(MessageFactory.Attachment(WelcomeCard.GetCard(appBaseUrl, localizer)));
                }

                break;
            }
        }
Example #3
0
        /// <summary>
        /// Handle when a message is addressed to the bot in personal scope.
        /// </summary>
        /// <param name="message">Message activity of bot.</param>
        /// <param name="turnContext">The turn context.</param>
        /// <param name="logger">Sends logs to the Application Insights service.</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="ticketDetailStorageProvider">Provider to store ticket details to Azure Table Storage.</param>
        /// <param name="microsoftAppCredentials">Microsoft Application credentials for Bot/ME.</param>
        /// <param name="appBaseUrl">Represents the Application base Uri.</param>
        /// <param name="localizer">The current cultures' string localizer.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns> A task that represents the work queued to execute for user message activity to bot.</returns>
        internal static async Task OnMessageActivityInPersonalChatAsync(
            IMessageActivity message,
            ITurnContext <IMessageActivity> turnContext,
            ILogger logger,
            ICardConfigurationStorageProvider cardConfigurationStorageProvider,
            ITicketIdGeneratorStorageProvider ticketGenerateStorageProvider,
            ITicketDetailStorageProvider ticketDetailStorageProvider,
            MicrosoftAppCredentials microsoftAppCredentials,
            string appBaseUrl,
            IStringLocalizer <Strings> localizer,
            CancellationToken cancellationToken)
        {
            if (!string.IsNullOrEmpty(message.ReplyToId) && message.Value != null && ((JObject)message.Value).HasValues)
            {
                logger.LogInformation("Card submitted in 1:1 chat.");
                await OnAdaptiveCardSubmitInPersonalChatAsync(message, turnContext, ticketGenerateStorageProvider, ticketDetailStorageProvider, cardConfigurationStorageProvider, microsoftAppCredentials, logger, appBaseUrl, localizer, cancellationToken);

                return;
            }

            string text = (turnContext.Activity.Text ?? string.Empty).Trim().ToUpperInvariant();

            if (text.Equals(localizer.GetString("BotCommandNewRequest"), StringComparison.CurrentCultureIgnoreCase))
            {
                logger.LogInformation("New request action called.");
                CardConfigurationEntity cardTemplateJson = await cardConfigurationStorageProvider.GetConfigurationAsync();

                IMessageActivity newTicketActivity = MessageFactory.Attachment(TicketCard.GetNewTicketCard(cardTemplateJson, localizer));
                await turnContext.SendActivityAsync(newTicketActivity);
            }
            else if (text.Equals(localizer.GetString("No").ToString(), StringComparison.CurrentCultureIgnoreCase))
            {
                return;
            }
            else
            {
                if (turnContext.Activity.Attachments == null || turnContext.Activity.Attachments.Count == 0)
                {
                    // In case of ME when user clicks on closed or active requests the bot posts adaptive card of request details we don't have to consider this as invalid command.
                    logger.LogInformation("Unrecognized input in End User.");
                    await turnContext.SendActivityAsync(MessageFactory.Attachment(WelcomeCard.GetCard(appBaseUrl, localizer)));
                }
            }
        }
Example #4
0
        /// <summary>
        /// Method Handle adaptive card submit in 1:1 chat and Send new ticket details to SME team.
        /// </summary>
        /// <param name="message">Message activity of bot.</param>
        /// <param name="turnContext">Context object containing information cached for a single turn of conversation with a user.</param>
        /// <param name="ticketGenerateStorageProvider">Provider to get ticket id to 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="microsoftAppCredentials">Microsoft Application credentials for Bot/ME.</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 handles submit action in 1:1 chat.</returns>
        internal static async Task OnAdaptiveCardSubmitInPersonalChatAsync(
            IMessageActivity message,
            ITurnContext <IMessageActivity> turnContext,
            ITicketIdGeneratorStorageProvider ticketGenerateStorageProvider,
            ITicketDetailStorageProvider ticketDetailStorageProvider,
            ICardConfigurationStorageProvider cardConfigurationStorageProvider,
            MicrosoftAppCredentials microsoftAppCredentials,
            ILogger logger,
            string appBaseUrl,
            IStringLocalizer <Strings> localizer,
            CancellationToken cancellationToken)
        {
            IMessageActivity endUserUpdateCard;

            switch (message.Text.ToUpperInvariant())
            {
            case Constants.SendRequestAction:
                TicketDetail newTicketDetail = JsonConvert.DeserializeObject <TicketDetail>(message.Value?.ToString());
                if (TicketHelper.ValidateRequestDetail(newTicketDetail))
                {
                    AdaptiveCardAction cardDetail = ((JObject)message.Value).ToObject <AdaptiveCardAction>();
                    logger.LogInformation("Adding new request with additional details.");
                    var ticketTd = await ticketGenerateStorageProvider.GetTicketIdAsync();

                    // Update new request with additional details.
                    var userDetails = await GetUserDetailsInPersonalChatAsync(turnContext, cancellationToken);

                    newTicketDetail.TicketId = ticketTd.ToString(CultureInfo.InvariantCulture);
                    newTicketDetail          = TicketHelper.GetNewTicketDetails(turnContext: turnContext, ticketDetail: newTicketDetail, ticketAdditionalDetails: message.Value?.ToString(), cardId: cardDetail.CardId, member: userDetails);
                    bool result = await ticketDetailStorageProvider.UpsertTicketAsync(newTicketDetail);

                    if (!result)
                    {
                        logger.LogError("Error in storing new ticket details in table storage.");
                        await turnContext.SendActivityAsync(localizer.GetString("AzureStorageErrorText"));

                        return;
                    }

                    logger.LogInformation("New request created with ticket Id:" + newTicketDetail.TicketId);

                    // Get card item element mappings
                    var carditemElementMapping = await cardConfigurationStorageProvider.GetCardItemElementMappingAsync(cardDetail?.CardId);

                    endUserUpdateCard = MessageFactory.Attachment(TicketCard.GetTicketDetailsForPersonalChatCard(carditemElementMapping, newTicketDetail, localizer, false));
                    await CardHelper.SendRequestCardToSMEChannelAsync(turnContext : turnContext, ticketDetail : newTicketDetail, logger : logger, ticketDetailStorageProvider : ticketDetailStorageProvider, applicationBasePath : appBaseUrl, cardElementMapping : carditemElementMapping, localizer, teamId : cardDetail?.TeamId, microsoftAppCredentials : microsoftAppCredentials, cancellationToken : cancellationToken);

                    await CardHelper.UpdateRequestCardForEndUserAsync(turnContext, endUserUpdateCard);

                    await turnContext.SendActivityAsync(MessageFactory.Text(localizer.GetString("EndUserNotificationText", newTicketDetail.TicketId)));
                }
                else
                {
                    // Update card with validation message.
                    newTicketDetail.AdditionalProperties = CardHelper.ValidateAdditionalTicketDetails(message.Value?.ToString(), timeSpan: turnContext.Activity.LocalTimestamp.Value.Offset);
                    CardConfigurationEntity cardTemplateJson = await cardConfigurationStorageProvider.GetConfigurationAsync();

                    endUserUpdateCard = MessageFactory.Attachment(TicketCard.GetNewTicketCard(cardConfiguration: cardTemplateJson, localizer: localizer, showValidationMessage: true, ticketDetail: newTicketDetail));
                    await CardHelper.UpdateRequestCardForEndUserAsync(turnContext, endUserUpdateCard);
                }

                break;

            case Constants.WithdrawRequestAction:
                var payload = ((JObject)message.Value).ToObject <AdaptiveCardAction>();
                endUserUpdateCard = MessageFactory.Attachment(WithdrawCard.GetCard(payload.PostedValues, localizer));

                // Get the ticket from the data store.
                TicketDetail ticketDetail = await ticketDetailStorageProvider.GetTicketAsync(payload.PostedValues);

                if (ticketDetail.TicketStatus == (int)TicketState.Kapatılmış)
                {
                    await turnContext.SendActivityAsync(localizer.GetString("WithdrawErrorMessage"));

                    return;
                }

                ticketDetail.LastModifiedByName     = message.From.Name;
                ticketDetail.LastModifiedByObjectId = message.From.AadObjectId;
                ticketDetail.TicketStatus           = (int)TicketState.Vazgeçilmiş;
                bool success = await ticketDetailStorageProvider.UpsertTicketAsync(ticketDetail);

                if (!success)
                {
                    logger.LogError("Error in updating ticket details in table storage.");
                    await turnContext.SendActivityAsync(localizer.GetString("AzureStorageErrorText"));

                    return;
                }

                logger.LogInformation("Withdrawn the ticket:" + ticketDetail.TicketId);
                IMessageActivity smeWithdrawNotification = MessageFactory.Text(localizer.GetString("SmeWithdrawNotificationText", ticketDetail.RequesterName));
                var itemElementMapping = await cardConfigurationStorageProvider.GetCardItemElementMappingAsync(ticketDetail?.CardId);

                await CardHelper.UpdateSMECardAsync(turnContext, ticketDetail, smeWithdrawNotification, appBaseUrl, itemElementMapping, localizer, logger, cancellationToken);

                await CardHelper.UpdateRequestCardForEndUserAsync(turnContext, endUserUpdateCard);

                break;
            }
        }