/// <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>
        /// Initializes a new instance of the <see cref="StorageController"/> class.
        /// </summary>
        /// <param name="configurationStorageProvider">Configuration storage provider.</param>
        /// <param name="options">Azure Active Directory Configurations.</param>
        /// <param name="logger">Logger instance.</param>
        public StorageController(ICardConfigurationStorageProvider configurationStorageProvider, IOptionsMonitor <AzureAdSettings> options, ILogger <StorageController> logger)
        {
            this.configurationStorageProvider = configurationStorageProvider;
            var azureAdSettings = options?.CurrentValue;

            this.validUsers = azureAdSettings.Upn.Split(';');
            this.logger     = logger;
        }
コード例 #3
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;
            }
        }
コード例 #4
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="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;
            }
        }
コード例 #5
0
        /// <summary>
        /// Gets edit ticket details adaptive card.
        /// </summary>
        /// <param name="cardConfigurationStorageProvider">Card configuration.</param>
        /// <param name="ticketDetail">Details of the ticket to be edited.</param>
        /// <param name="localizer">The current cultures' string localizer.</param>
        /// <param name="existingTicketDetail">Existing ticket details.</param>
        /// <returns>Returns edit ticket adaptive card.</returns>
        public static TaskModuleResponse GetEditTicketAdaptiveCard(ICardConfigurationStorageProvider cardConfigurationStorageProvider, TicketDetail ticketDetail, IStringLocalizer <Strings> localizer, TicketDetail existingTicketDetail = null)
        {
            var cardTemplate = cardConfigurationStorageProvider?.GetConfigurationsByCardIdAsync(ticketDetail?.CardId).Result;

            return(new TaskModuleResponse
            {
                Task = new TaskModuleContinueResponse
                {
                    Value = new TaskModuleTaskInfo()
                    {
                        Card = EditRequestCard.GetEditRequestCard(ticketDetail, cardTemplate, localizer, existingTicketDetail),
                        Height = TaskModuleHeight,
                        Width = TaskModuleWidth,
                        Title = localizer.GetString("EditRequestTitle"),
                    },
                },
            });
        }
コード例 #6
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;
 }
コード例 #7
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)));
                }
            }
        }
コード例 #8
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;
            }
        }
コード例 #9
0
        /// <summary>
        /// Handle adaptive card submit in channel.
        /// Updates the ticket status based on the user submission.
        /// </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="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 OnAdaptiveCardSubmitInChannelAsync(
            IMessageActivity message,
            ITurnContext <IMessageActivity> turnContext,
            ITicketDetailStorageProvider ticketDetailStorageProvider,
            ICardConfigurationStorageProvider cardConfigurationStorageProvider,
            ILogger logger,
            string appBaseUrl,
            IStringLocalizer <Strings> localizer,
            CancellationToken cancellationToken)
        {
            string             smeNotification;
            IMessageActivity   userNotification;
            ChangeTicketStatus payload = ((JObject)message.Value).ToObject <ChangeTicketStatus>();

            payload.Action = payload.RequestType == null ? payload.Action : RequestTypeText;
            logger.LogInformation($"Received submit:  action={payload.Action} ticketId={payload.TicketId}");

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

            if (ticketData == null)
            {
                await turnContext.SendActivityAsync($"Ticket {payload.TicketId} was not found in the data store");

                logger.LogInformation($"Ticket {payload.TicketId} was not found in the data store");
                return;
            }

            // Update the ticket based on the payload.
            switch (payload.Action)
            {
            case ChangeTicketStatus.ReopenAction:
                ticketData.TicketStatus       = (int)TicketState.Atanmamış;
                ticketData.AssignedToName     = null;
                ticketData.AssignedToObjectId = null;
                ticketData.ClosedOn           = null;
                smeNotification  = localizer.GetString("SmeUnassignedStatus", message.From.Name);
                userNotification = MessageFactory.Text(localizer.GetString("ReopenedTicketUserNotification", ticketData.TicketId));
                break;

            case ChangeTicketStatus.CloseAction:
                ticketData.TicketStatus = (int)TicketState.Kapatılmış;
                ticketData.ClosedByName = message.From.Name;
                ticketData.ClosedOn     = message.From.AadObjectId;
                smeNotification         = localizer.GetString("SmeClosedStatus", message.From.Name);
                userNotification        = MessageFactory.Text(localizer.GetString("ClosedTicketUserNotification", ticketData.TicketId));
                break;

            case ChangeTicketStatus.AssignToSelfAction:
                ticketData.TicketStatus       = (int)TicketState.Atanmış;
                ticketData.AssignedToName     = message.From.Name;
                ticketData.AssignedToObjectId = message.From.AadObjectId;
                ticketData.ClosedOn           = null;
                smeNotification  = localizer.GetString("SmeAssignedStatus", message.From.Name);
                userNotification = MessageFactory.Text(localizer.GetString("AssignedTicketUserNotification", ticketData.TicketId));
                break;

            case ChangeTicketStatus.RequestTypeAction:
                ticketData.Severity    = (int)(TicketSeverity)Enum.Parse(typeof(TicketSeverity), payload.RequestType);
                ticketData.RequestType = payload.RequestType;
                logger.LogInformation($"Received submit:  action={payload.RequestType} ticketId={payload.TicketId}");
                smeNotification  = localizer.GetString("SmeSeverityStatus", ticketData.RequestType, message.From.Name);
                userNotification = MessageFactory.Text(localizer.GetString("RequestActionTicketUserNotification", ticketData.TicketId));
                break;

            default:
                logger.LogInformation($"Unknown status command {payload.Action}", SeverityLevel.Warning);
                return;
            }

            ticketData.LastModifiedByName     = message.From.Name;
            ticketData.LastModifiedByObjectId = message.From.AadObjectId;
            ticketData.LastModifiedOn         = DateTime.UtcNow;

            await ticketDetailStorageProvider.UpsertTicketAsync(ticketData);

            logger.LogInformation($"Ticket {ticketData.TicketId} updated to status ({ticketData.TicketStatus}, {ticketData.AssignedToObjectId}) in store");

            // Get card item element mappings
            var cardElementMapping = await cardConfigurationStorageProvider.GetCardItemElementMappingAsync(ticketData.CardId);

            // Update the card in the SME team.
            Activity updateCardActivity = new Activity(ActivityTypes.Message)
            {
                Id           = ticketData.SmeTicketActivityId,
                Conversation = new ConversationAccount {
                    Id = ticketData.SmeConversationId
                },
                Attachments = new List <Attachment> {
                    new SmeTicketCard(ticketData).GetTicketDetailsForSMEChatCard(cardElementMapping, ticketData, appBaseUrl, localizer)
                },
            };
            ResourceResponse updateResponse = await turnContext.UpdateActivityAsync(updateCardActivity, cancellationToken);

            logger.LogInformation($"Card for ticket {ticketData.TicketId} updated to status ({ticketData.TicketStatus}, {ticketData.AssignedToObjectId}), activityId = {updateResponse.Id}");

            // Post update to user and SME team thread.
            if (!string.IsNullOrEmpty(smeNotification))
            {
                ResourceResponse smeResponse = await turnContext.SendActivityAsync(smeNotification);

                logger.LogInformation($"SME team notified of update to ticket {ticketData.TicketId}, activityId = {smeResponse.Id}");
            }

            if (userNotification != null)
            {
                userNotification.Conversation = new ConversationAccount {
                    Id = ticketData.RequesterConversationId
                };
                ResourceResponse[] userResponse = await turnContext.Adapter.SendActivitiesAsync(turnContext, new Activity[] { (Activity)userNotification }, cancellationToken);

                logger.LogInformation($"User notified of update to ticket {ticketData.TicketId}, activityId = {userResponse.FirstOrDefault()?.Id}");
            }
        }