/// <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> /// 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; } }