/// <summary> /// Converts JSON property to adaptive card TextBlock element. /// </summary> /// <param name="cardElementTemplate">TextBlock item element json property.</param> /// <param name="showDateValidation">true if need to show validation message else false.</param> /// <returns>Returns adaptive card TextBlock item element.</returns> public static AdaptiveTextBlock ConvertToAdaptiveTextBlock(string cardElementTemplate, bool showDateValidation = false) { var result = JsonConvert.DeserializeObject <Dictionary <string, string> >(cardElementTemplate); bool isVisible = true; if (!string.IsNullOrEmpty(CardHelper.TryParseTicketDetailsKeyValuePair(result, "isVisible"))) { bool status = bool.TryParse(CardHelper.TryParseTicketDetailsKeyValuePair(result, "isVisible"), out isVisible); } string color = CardHelper.TryParseTicketDetailsKeyValuePair(result, "color"); return(new AdaptiveTextBlock() { Id = CardHelper.TryParseTicketDetailsKeyValuePair(result, "id"), Text = CardHelper.TryParseTicketDetailsKeyValuePair(result, "text"), IsVisible = isVisible, Color = string.IsNullOrEmpty(color) ? AdaptiveTextColor.Default : (AdaptiveTextColor)Enum.Parse(typeof(AdaptiveTextColor), color), }); }
/// <summary> /// Handle members added conversationUpdate event in team. /// </summary> /// <param name="membersAdded">Channel account information needed to route a message.</param> /// <param name="turnContext">Context object containing information cached for a single turn of conversation with a user.</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 represents the work queued to execute.</returns> internal static async Task OnMembersAddedToTeamAsync( IList <ChannelAccount> membersAdded, ITurnContext <IConversationUpdateActivity> turnContext, MicrosoftAppCredentials microsoftAppCredentials, ILogger logger, string appBaseUrl, IStringLocalizer <Strings> localizer, CancellationToken cancellationToken) { turnContext = turnContext ?? throw new ArgumentNullException(nameof(turnContext)); var activity = turnContext.Activity; if (membersAdded.Any(channelAccount => channelAccount.Id == activity.Recipient.Id)) { // Bot was added to a team logger.LogInformation($"Bot added to team {activity.Conversation.Id}"); var teamDetails = ((JObject)turnContext.Activity.ChannelData).ToObject <TeamsChannelData>(); var teamWelcomeCardAttachment = WelcomeTeamCard.GetCard(appBaseUrl, localizer); await CardHelper.SendCardToTeamAsync(turnContext, teamWelcomeCardAttachment, teamDetails.Team.Id, microsoftAppCredentials, cancellationToken); } }
/// <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> /// Get result for messaging extension tab. /// </summary> /// <param name="searchServiceResults">List of tickets from Azure search service.</param> /// <param name="localizer">The current cultures' string localizer.</param> /// <param name="commandId">Command id to determine which tab in message extension has been invoked.</param> /// <param name="onCallSMEUsers">OncallSMEUsers to give support from group-chat or on-call.</param> /// <returns><see cref="Task"/> Returns MessagingExtensionResult which will be shown in messaging extension tab.</returns> public static MessagingExtensionResult GetMessagingExtensionResult( IList <TicketDetail> searchServiceResults, IStringLocalizer <Strings> localizer, string commandId = "", string onCallSMEUsers = "") { MessagingExtensionResult composeExtensionResult = new MessagingExtensionResult { Type = "result", AttachmentLayout = AttachmentLayoutTypes.List, Attachments = new List <MessagingExtensionAttachment>(), }; if (searchServiceResults != null) { foreach (var ticket in searchServiceResults) { var dynamicElements = new List <AdaptiveElement> { CardHelper.GetAdaptiveCardColumnSet(localizer.GetString("RequestNumberText"), $"#{ticket.TicketId}"), CardHelper.GetAdaptiveCardColumnSet(localizer.GetString("TitleDisplayText"), ticket.Title), CardHelper.GetAdaptiveCardColumnSet(localizer.GetString("DescriptionText"), ticket.Description), CardHelper.GetAdaptiveCardColumnSet(localizer.GetString("CreatedOnText"), ticket.CreatedOn.ToString(CultureInfo.InvariantCulture)), }; AdaptiveCard commandIdCard = new AdaptiveCard(Constants.AdaptiveCardVersion) { Body = dynamicElements, Actions = new List <AdaptiveAction>(), }; if (commandId == Constants.ActiveCommandId && !string.IsNullOrEmpty(onCallSMEUsers)) { commandIdCard.Actions.Add( new AdaptiveOpenUrlAction { Title = localizer.GetString("EscalateButtonText"), Url = new Uri(CreateGroupChat(onCallSMEUsers, ticket.TicketId, ticket.RequesterName, localizer)), }); } else if ((commandId == Constants.UrgentCommandId || commandId == Constants.AssignedCommandId || commandId == Constants.UnassignedCommandId) && ticket.SmeConversationId != null) { commandIdCard.Actions.Add( new AdaptiveOpenUrlAction { Title = localizer.GetString("GoToOriginalThreadButtonText"), Url = new Uri(CreateDeepLinkToThread(ticket.SmeConversationId)), }); } ThumbnailCard previewCard = new ThumbnailCard { Title = $"<b>{HttpUtility.HtmlEncode(ticket.Title)} | {HttpUtility.HtmlEncode(ticket.Severity == (int)TicketSeverity.Acil ? localizer.GetString("UrgentText") : localizer.GetString("NormalText"))}</b>", Subtitle = ticket.Description.Length <= TruncateDescriptionLength?HttpUtility.HtmlEncode(ticket.Description) : HttpUtility.HtmlEncode(ticket.Description.Substring(0, 45)) + Ellipsis, Text = ticket.RequesterName, }; composeExtensionResult.Attachments.Add(new Attachment { ContentType = AdaptiveCard.ContentType, Content = commandIdCard, }.ToMessagingExtensionAttachment(previewCard.ToAttachment())); } } return(composeExtensionResult); }
/// <summary> /// Converts JSON property to adaptive card DateInput element. /// </summary> /// <param name="cardElementTemplate">DateInput item element json property.</param> /// <returns>Returns adaptive card DateInput item element.</returns> public static AdaptiveDateInput ConvertToAdaptiveDateInput(string cardElementTemplate) { var result = JsonConvert.DeserializeObject <Dictionary <string, string> >(cardElementTemplate); return(new AdaptiveDateInput() { Id = CardHelper.TryParseTicketDetailsKeyValuePair(result, "id"), Placeholder = CardHelper.TryParseTicketDetailsKeyValuePair(result, "placeholder"), Value = string.IsNullOrEmpty(CardHelper.TryParseTicketDetailsKeyValuePair(result, "value")) ? DateTime.Now.ToString(CultureInfo.InvariantCulture) : CardHelper.TryParseTicketDetailsKeyValuePair(result, "value"), Max = CardHelper.TryParseTicketDetailsKeyValuePair(result, "max"), Min = CardHelper.TryParseTicketDetailsKeyValuePair(result, "min"), }); }