/// <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; } }
/// <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); }
/// <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; } }
/// <summary> /// Validates endorsement status. /// </summary> /// <param name="turnContext">Context object containing information cached for a single turn of conversation with a user.</param> /// <param name="valuesforTaskModule">Get the binded values from the card.</param> /// <returns>Returns the true, if endorsement is successful, else false.</returns> private async Task <bool> CheckEndorseStatusAsync(ITurnContext <IInvokeActivity> turnContext, AdaptiveCardAction valuesforTaskModule) { var teamsDetails = turnContext.Activity.TeamsGetTeamInfo(); var teamsChannelAccounts = await TeamsInfo.GetTeamMembersAsync(turnContext, teamsDetails.Id, CancellationToken.None); var userDetails = teamsChannelAccounts.Where(member => member.AadObjectId == turnContext.Activity.From.AadObjectId).FirstOrDefault(); var endorseEntity = await this.endorseDetailStorageProvider.GetEndorseDetailAsync(teamsDetails.Id, valuesforTaskModule.RewardCycleId, valuesforTaskModule.NominatedToPrincipalName); var result = endorseEntity.Where(row => row.EndorseForAwardId == valuesforTaskModule.AwardId && row.EndorsedByObjectId == userDetails.AadObjectId).FirstOrDefault(); if (result == null) { var endorsedetails = new EndorseEntity { TeamId = teamsDetails.Id, EndorsedByObjectId = userDetails.AadObjectId, EndorsedByPrincipalName = userDetails.Email, EndorseForAward = valuesforTaskModule.AwardName, EndorsedToPrincipalName = valuesforTaskModule.NominatedToPrincipalName, EndorsedToObjectId = valuesforTaskModule.NominatedToObjectId, EndorsedOn = DateTime.UtcNow, EndorseForAwardId = valuesforTaskModule.AwardId, AwardCycle = valuesforTaskModule.RewardCycleId, }; return(await this.endorseDetailStorageProvider.StoreOrUpdateEndorseDetailAsync(endorsedetails)); } return(false); }