/// <summary> /// Add a new suggestion in company response entity in Microsoft Azure Table storage. /// </summary> /// <param name="turnContext">Context object containing information cached for a single turn of conversation with a user.</param> /// <param name="userRequestDetails">User response new request details object used to send new request data.</param> /// <param name="customAPIAuthenticationToken">Generate JWT token used by client application to authenticate HTTP calls with API.</param> /// <param name="cancellationToken">Propagates notification that operations should be canceled.</param> /// <returns>A task that represents the work queued to execute.</returns> private async Task <MessagingExtensionActionResponse> AddNewSuggestionResultAsync( ITurnContext <IInvokeActivity> turnContext, AddUserResponseRequestDetail userRequestDetails, string customAPIAuthenticationToken, CancellationToken cancellationToken) { var activity = turnContext.Activity; var companyResponseEntity = await this.companyStorageHelper.AddNewSuggestionAsync(activity, userRequestDetails); // Parse team channel deep link URL and get team id. var teamId = AdaptiveCardHelper.ParseTeamIdFromDeepLink(this.botSetting.Value.TeamIdDeepLink); if (string.IsNullOrEmpty(teamId)) { throw new NullReferenceException("Provided team details seems to incorrect, please reach out to the Admin."); } var isAddSuggestionSuccess = await this.companyResponseStorageProvider.UpsertConverationStateAsync(companyResponseEntity); if (isAddSuggestionSuccess) { // Tracking for company response suggested request. this.RecordEvent(RecordSuggestCompanyResponse, turnContext); var attachment = AdminCard.GetNewResponseRequestCard(companyResponseEntity, localizer: this.localizer); var resourceResponse = await this.SendCardToTeamAsync(turnContext, attachment, teamId, cancellationToken); companyResponseEntity.ActivityId = resourceResponse.ActivityId; await this.companyResponseStorageProvider.UpsertConverationStateAsync(companyResponseEntity); return(new MessagingExtensionActionResponse { Task = new TaskModuleContinueResponse { Value = new TaskModuleTaskInfo { Url = $"{this.options.Value.AppBaseUri}/response-message?token={customAPIAuthenticationToken}&status=addSuccess&isCompanyResponse=true&message={this.localizer.GetString("AddNewSuggestionSuccessMessage")}&telemetry=${this.telemetrySettings.Value.InstrumentationKey}&theme=" + "{theme}&locale=" + "{locale}", Height = TaskModuleHeight, Width = TaskModuleWidth, Title = this.localizer.GetString("ManageYourResponsesTitleText"), }, }, }); } else { return(new MessagingExtensionActionResponse { Task = new TaskModuleContinueResponse { Value = new TaskModuleTaskInfo { Url = $"{this.options.Value.AppBaseUri}/response-message?token={customAPIAuthenticationToken}&status=editFailed&isCompanyResponse=true&message={this.localizer.GetString("AddNewSuggestionFailedMessage")}&theme=" + "{theme}&locale=" + "{locale}", Height = TaskModuleHeight, Width = TaskModuleWidth, Title = this.localizer.GetString("ManageYourResponsesTitleText"), }, }, }); } }
/// <summary> /// When OnTurn method receives a submit invoke activity on bot turn, it calls this method. /// </summary> /// <param name="turnContext">Context object containing information cached for a single turn of conversation with a user.</param> /// <param name="taskModuleRequest">Task module invoke request value payload.</param> /// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param> /// <returns>A task that represents a task module response.</returns> protected override async Task <TaskModuleResponse> OnTeamsTaskModuleSubmitAsync(ITurnContext <IInvokeActivity> turnContext, TaskModuleRequest taskModuleRequest, CancellationToken cancellationToken) { try { turnContext = turnContext ?? throw new ArgumentNullException(nameof(turnContext)); var activity = (Activity)turnContext.Activity; this.RecordEvent(nameof(this.OnTeamsTaskModuleFetchAsync), turnContext); IMessageActivity notificationCard; Activity mentionActivity; var valuesfromTaskModule = JsonConvert.DeserializeObject <TaskModuleResponseDetails>(((JObject)activity.Value).GetValue("data", StringComparison.OrdinalIgnoreCase)?.ToString()); switch (valuesfromTaskModule.Command.ToUpperInvariant()) { case Constants.SaveAdminDetailsAction: mentionActivity = await CardHelper.GetMentionActivityAsync(valuesfromTaskModule.AdminPrincipalName.Split(",").ToList(), turnContext.Activity.From.AadObjectId, valuesfromTaskModule.TeamId, turnContext, this.localizer, this.logger, MentionActivityType.SetAdmin, cancellationToken); var cardDetail = (Activity)MessageFactory.Attachment(AdminCard.GetAdminCard(this.localizer, valuesfromTaskModule, this.options.Value.ManifestId)); await this.SendMentionedCardAsync(turnContext, cardDetail, mentionActivity); this.logger.LogInformation("R&R admin has been configured"); break; case Constants.CancelCommand: break; case Constants.UpdateAdminDetailCommand: mentionActivity = await CardHelper.GetMentionActivityAsync(valuesfromTaskModule.AdminPrincipalName.Split(",").ToList(), turnContext.Activity.From.AadObjectId, valuesfromTaskModule.TeamId, turnContext, this.localizer, this.logger, MentionActivityType.SetAdmin, cancellationToken); notificationCard = MessageFactory.Attachment(AdminCard.GetAdminCard(this.localizer, valuesfromTaskModule, this.options.Value.ManifestId)); notificationCard.Id = turnContext.Activity.Conversation.Id.Split(';')[1].Split("=")[1]; notificationCard.Conversation = turnContext.Activity.Conversation; await turnContext.UpdateActivityAsync(notificationCard); await turnContext.SendActivityAsync(mentionActivity); this.logger.LogInformation("Card is updated."); break; case Constants.NominateAction: var awardsList = await this.awardsStorageProvider.GetAwardsAsync(valuesfromTaskModule.TeamId); await turnContext.SendActivityAsync(MessageFactory.Carousel(NominateCarouselCard.GetAwardsCard(this.appBaseUrl, awardsList, this.localizer, valuesfromTaskModule))); break; case Constants.SaveNominatedDetailsAction: turnContext.Activity.Conversation.Id = valuesfromTaskModule.TeamId; var result = (Activity)MessageFactory.Attachment(EndorseCard.GetEndorseCard(this.appBaseUrl, this.localizer, valuesfromTaskModule)); mentionActivity = await CardHelper.GetMentionActivityAsync(valuesfromTaskModule.NominatedToPrincipalName.Split(",").Select(row => row.Trim()).ToList(), turnContext.Activity.From.AadObjectId, valuesfromTaskModule.TeamId, turnContext, this.localizer, this.logger, MentionActivityType.Nomination, cancellationToken); await this.SendMentionedCardAsync(turnContext, result, mentionActivity); this.logger.LogInformation("Nominated an award"); break; case Constants.OkCommand: return(null); default: this.logger.LogInformation($"Invalid command for task module submit activity.Command is : {valuesfromTaskModule.Command} "); await turnContext.SendActivityAsync(this.localizer.GetString("ErrorMessage")); break; } return(null); } catch (Exception ex) { this.logger.LogError(ex, $"Error at OnTeamsTaskModuleSubmitAsync(): {ex.Message}", SeverityLevel.Error); await turnContext.SendActivityAsync(this.localizer.GetString("ErrorMessage")); throw; } }
/// <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="cancellationToken">Propagates notification that operations should be canceled.</param> /// <returns>A task that represents the work queued to execute.</returns> private async Task OnMessageActivityInChannelAsync( IMessageActivity message, ITurnContext <IMessageActivity> turnContext, CancellationToken cancellationToken) { try { if (message.Value == null) { await turnContext.SendActivityAsync(this.localizer.GetString("ErrorWhenMessageInChannel")); return; } IMessageActivity userNotification = null; CompanyResponseEntity companyResponseEntity = null; var cardPostedData = ((JObject)message.Value).ToObject <AdaptiveSubmitActionData>(); var text = cardPostedData.AdaptiveCardActions.Text; var activity = turnContext.Activity; switch (text) { case Constants.ApproveCommand: if (string.IsNullOrEmpty(cardPostedData.UpdatedQuestionCategory) || string.IsNullOrEmpty(cardPostedData.UpdatedQuestionText) || string.IsNullOrEmpty(cardPostedData.UpdatedResponseText)) { companyResponseEntity = this.companyResponseStorageProvider.GetCompanyResponseEntityAsync(cardPostedData.ResponseId).GetAwaiter().GetResult(); var attachment = AdminCard.GetNewResponseRequestCard(companyResponseEntity, localizer: this.localizer, emptyApproveField: true); await AdaptiveCardHelper.RefreshCardAsync(turnContext, companyResponseEntity.ActivityId, attachment); return; } companyResponseEntity = this.companyStorageHelper.AddApprovedData(cardPostedData, activity.From.Name, activity.From.AadObjectId); var approveRequestResult = this.companyResponseStorageProvider.UpsertConverationStateAsync(companyResponseEntity).GetAwaiter().GetResult(); if (approveRequestResult) { // Refresh the approved card in channel. var attachment = AdminCard.GetRefreshedCardForApprovedRequest(companyResponseEntity, activity.From.Name, localizer: this.localizer); await AdaptiveCardHelper.RefreshCardAsync(turnContext, companyResponseEntity.ActivityId, attachment); // Get user notification attachment and send it to user for approved request. userNotification = MessageFactory.Attachment(UserCard.GetNotificationCardForApprovedRequest(companyResponseEntity, localizer: this.localizer)); var result = await this.conversationStorageProvider.GetConversationEntityAsync(companyResponseEntity.UserId); if (result != null) { await AdaptiveCardHelper.SendNotificationCardAsync(turnContext, userNotification, result.ConversationId, cancellationToken); // Tracking for number of requests approved. this.RecordEvent(ApprovedRequestEventName, turnContext); } else { this.logger.LogInformation("Unable to send notification card for approved request because conversation id is null."); } } else { this.logger.LogInformation("Unable to approve the request."); } break; case Constants.RejectCommand: companyResponseEntity = this.companyStorageHelper.AddRejectedData(cardPostedData, activity.From.Name, activity.From.AadObjectId); var rejectRequestResult = this.companyResponseStorageProvider.UpsertConverationStateAsync(companyResponseEntity).GetAwaiter().GetResult(); if (rejectRequestResult) { // Get user notification rejected card attachment. var attachment = AdminCard.GetRefreshedCardForRejectedRequest(companyResponseEntity, activity.From.Name, localizer: this.localizer); await AdaptiveCardHelper.RefreshCardAsync(turnContext, companyResponseEntity.ActivityId, attachment); // Send end user notification for approved request. userNotification = MessageFactory.Attachment(UserCard.GetNotificationCardForRejectedRequest(companyResponseEntity, localizer: this.localizer)); var result = await this.conversationStorageProvider.GetConversationEntityAsync(companyResponseEntity.UserId); if (result != null) { await AdaptiveCardHelper.SendNotificationCardAsync(turnContext, userNotification, result.ConversationId, cancellationToken); // Tracking for number of requests rejected. this.RecordEvent(RejectedRequestEventName, turnContext); } else { this.logger.LogInformation("Unable to send notification card for rejected request because conversation id is null."); } } else { this.logger.LogInformation("Unable to reject the request."); } return; default: this.logger.LogInformation("Unrecognized input in channel"); break; } } catch (Exception ex) { this.logger.LogError(ex, $"Error processing message: {ex.Message}", SeverityLevel.Error); throw; } }
/// <summary> /// When OnTurn method receives a submit invoke activity on bot turn, it calls this method. /// </summary> /// <param name="turnContext">Context object containing information cached for a single turn of conversation with a user.</param> /// <param name="taskModuleRequest">Task module invoke request value payload.</param> /// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param> /// <returns>A task that represents a task module response.</returns> protected override async Task <TaskModuleResponse> OnTeamsTaskModuleSubmitAsync(ITurnContext <IInvokeActivity> turnContext, TaskModuleRequest taskModuleRequest, CancellationToken cancellationToken) { try { turnContext = turnContext ?? throw new ArgumentNullException(nameof(turnContext)); var activity = (Activity)turnContext.Activity; this.RecordEvent(nameof(this.OnTeamsTaskModuleFetchAsync), turnContext); Activity mentionActivity; var valuesFromTaskModule = JsonConvert.DeserializeObject <TaskModuleResponseDetails>(((JObject)activity.Value).GetValue("data", StringComparison.OrdinalIgnoreCase)?.ToString()); switch (valuesFromTaskModule.Command.ToUpperInvariant()) { // Command to send award admin card on save admin action. case Constants.SaveAdminDetailsAction: mentionActivity = await CardHelper.GetMentionActivityAsync( valuesFromTaskModule.AdminUserPrincipalName.Split(",").ToList(), turnContext.Activity.From.AadObjectId, valuesFromTaskModule.TeamId, turnContext, this.localizer, this.logger, MentionActivityType.SetAdmin, cancellationToken); var cardDetail = AdminCard.GetAdminCard(this.localizer, valuesFromTaskModule); await this.SendCardAndMentionsAsync(turnContext, cardDetail, mentionActivity); this.logger.LogInformation("Admin has been configured successfully."); break; // Command to update award admin card case Constants.UpdateAdminDetailCommand: mentionActivity = await CardHelper.GetMentionActivityAsync( valuesFromTaskModule.AdminUserPrincipalName.Split(",").ToList(), turnContext.Activity.From.AadObjectId, valuesFromTaskModule.TeamId, turnContext, this.localizer, this.logger, MentionActivityType.SetAdmin, cancellationToken); var notificationCard = (Activity)MessageFactory.Attachment(AdminCard.GetAdminCard(this.localizer, valuesFromTaskModule)); // Split here extracts the activity id from turn context conversation notificationCard.Id = turnContext.Activity.Conversation.Id.Split(';')[1].Split("=")[1]; notificationCard.Conversation = turnContext.Activity.Conversation; await turnContext.UpdateActivityAsync(notificationCard); await turnContext.SendActivityAsync(mentionActivity); this.logger.LogInformation("Admin card is updated successfully."); break; // Command to show list of awards ready for nomination case Constants.NominateAction: var awardsList = await this.awardsStorageProvider.GetAwardsAsync(valuesFromTaskModule.TeamId); await turnContext.SendActivityAsync(MessageFactory.Carousel(NominateCarouselCard.GetAwardNominationCards(this.options.Value.AppBaseUri, awardsList, this.localizer, valuesFromTaskModule))); this.logger.LogInformation("Nomination carousel card is sent successfully."); break; // Command to save nominated user details case Constants.SaveNominatedDetailsAction: turnContext.Activity.Conversation.Id = valuesFromTaskModule.TeamId; var endorsementCard = EndorseCard.GetEndorseCard(this.options.Value.AppBaseUri, this.localizer, valuesFromTaskModule); mentionActivity = await CardHelper.GetMentionActivityAsync( valuesFromTaskModule.NomineeUserPrincipalNames.Split(",").Select(row => row.Trim()).ToList(), turnContext.Activity.From.AadObjectId, valuesFromTaskModule.TeamId, turnContext, this.localizer, this.logger, MentionActivityType.Nomination, cancellationToken); await this.SendCardAndMentionsAsync(turnContext, endorsementCard, mentionActivity); this.logger.LogInformation("Award nomination for user sent successfully"); break; // Commands to close task modules case Constants.OkCommand: case Constants.CancelCommand: this.logger.LogInformation($"{valuesFromTaskModule.Command.ToUpperInvariant()} is called. [note] - no actions are performed."); break; default: this.logger.LogInformation($"Invalid command for task module submit activity.Command is : {valuesFromTaskModule.Command} "); await turnContext.SendActivityAsync(this.localizer.GetString("ErrorMessage")); break; } return(null); } catch (Exception ex) { this.logger.LogError(ex, $"Error at OnTeamsTaskModuleSubmitAsync(): {ex.Message}"); throw; } }