private async Task OnInvoke(IInvokeActivity invoke, IConnectorClient connectorClient, IStateClient stateClient, HttpResponseMessage response, CancellationToken token) { MicrosoftAppCredentials.TrustServiceUrl(invoke.RelatesTo.ServiceUrl); var jobject = invoke.Value as JObject; if (jobject == null) { throw new ArgumentException("Request payload must be a valid json object."); } // This is a temporary workaround for the issue that the channelId for "webchat" is mapped to "directline" in the incoming RelatesTo object invoke.RelatesTo.ChannelId = (invoke.RelatesTo.ChannelId == "directline") ? "webchat" : invoke.RelatesTo.ChannelId; if (invoke.RelatesTo.User == null) { // Bot keeps the userId in context.ConversationData[cartId] var conversationData = await stateClient.BotState.GetConversationDataAsync(invoke.RelatesTo.ChannelId, invoke.RelatesTo.Conversation.Id, token); var cartId = conversationData.GetProperty <string>(RootDialog.CARTKEY); if (!string.IsNullOrEmpty(cartId)) { invoke.RelatesTo.User = new ChannelAccount { Id = conversationData.GetProperty <string>(cartId) }; } } var updateResponse = default(object); switch (invoke.Name) { case PaymentOperations.UpdateShippingAddressOperationName: updateResponse = await this.ProcessShippingUpdate(jobject.ToObject <PaymentRequestUpdate>(), ShippingUpdateKind.Address, token); break; case PaymentOperations.UpdateShippingOptionOperationName: updateResponse = await this.ProcessShippingUpdate(jobject.ToObject <PaymentRequestUpdate>(), ShippingUpdateKind.Options, token); break; case PaymentOperations.PaymentCompleteOperationName: updateResponse = await this.ProcessPaymentComplete(invoke, jobject.ToObject <PaymentRequestComplete>(), token); break; default: throw new ArgumentException("Invoke activity name is not a supported request type."); } response.Content = new ObjectContent <object>( updateResponse, this.Configuration.Formatters.JsonFormatter, JsonMediaTypeFormatter.DefaultMediaType); response.StatusCode = HttpStatusCode.OK; }
private async Task <PaymentRequestCompleteResult> ProcessPaymentComplete(IInvokeActivity invoke, PaymentRequestComplete paymentRequestComplete, CancellationToken token = default(CancellationToken)) { var paymentRequest = paymentRequestComplete.PaymentRequest; var paymentResponse = paymentRequestComplete.PaymentResponse; paymentRequest.Details = (await this.ProcessShippingUpdate( new PaymentRequestUpdate() { Id = paymentRequest.Id, Details = paymentRequest.Details, ShippingAddress = paymentResponse.ShippingAddress, ShippingOption = paymentResponse.ShippingOption }, ShippingUpdateKind.Both, token)).Details; PaymentRecord paymentRecord = null; PaymentRequestCompleteResult result = null; Exception paymentProcessingException = null; try { paymentRecord = await this.paymentService.ProcessPaymentAsync(paymentRequest, paymentResponse); result = new PaymentRequestCompleteResult("success"); } catch (Exception ex) { paymentProcessingException = ex; // TODO: If payment is captured but not charged this would be considered "unknown" (charge the captured amount after shipping scenario). result = new PaymentRequestCompleteResult("failure"); } try { var message = invoke.RelatesTo.GetPostToBotMessage(); if (result.Result == "success") { // Resume the conversation with the receipt to user message.Text = paymentRequestComplete.Id; message.Value = paymentRecord; } else { // Resume the conversation with error message message.Text = $"Failed to process payment with error: {paymentProcessingException?.Message}"; } await Conversation.ResumeAsync(invoke.RelatesTo, message, token); } catch (Exception ex) { Trace.TraceError($"Failed to resume the conversation using ConversationReference: {JsonConvert.SerializeObject(invoke.RelatesTo)} and exception: {ex.Message}"); } return(result); }
/// <summary> /// Invoked when the user opens the messaging extension or searching any content in it. /// </summary> /// <param name="turnContext">Context object containing information cached for a single turn of conversation with a user.</param> /// <param name="query">Contains messaging extension query keywords.</param> /// <param name="cancellationToken">Propagates notification that operations should be canceled.</param> /// <returns>Messaging extension response object to fill compose extension section.</returns> /// <remarks> /// https://docs.microsoft.com/en-us/dotnet/api/microsoft.bot.builder.teams.teamsactivityhandler.onteamsmessagingextensionqueryasync?view=botbuilder-dotnet-stable. /// </remarks> protected override async Task <MessagingExtensionResponse> OnTeamsMessagingExtensionQueryAsync( ITurnContext <IInvokeActivity> turnContext, MessagingExtensionQuery query, CancellationToken cancellationToken) { IInvokeActivity turnContextActivity = turnContext?.Activity; try { if (!await this.CheckTeamsValidaionAsync(turnContext)) { return(new MessagingExtensionResponse { ComposeExtension = new MessagingExtensionResult { Text = this.localizer.GetString("InvalidTeamText"), Type = "message", }, }); } MessagingExtensionQuery messageExtensionQuery = JsonConvert.DeserializeObject <MessagingExtensionQuery>(turnContextActivity.Value.ToString()); string searchQuery = SearchHelper.GetSearchQueryString(messageExtensionQuery); turnContextActivity.TryGetChannelData <TeamsChannelData>(out var teamsChannelData); var cycleStatus = await this.rewardCycleStorageProvider.GetActiveRewardCycleAsync(teamsChannelData.Channel.Id); if (cycleStatus != null) { return(new MessagingExtensionResponse { ComposeExtension = await SearchHelper.GetSearchResultAsync(this.appBaseUrl, searchQuery, cycleStatus.CycleId, teamsChannelData.Channel.Id, messageExtensionQuery.QueryOptions.Count, messageExtensionQuery.QueryOptions.Skip, this.nominateDetailSearchService, this.localizer), }); } return(new MessagingExtensionResponse { ComposeExtension = new MessagingExtensionResult { Text = this.localizer.GetString("CycleValidationMessage"), Type = "message", }, }); } catch (Exception ex) { this.logger.LogError(ex, $"Failed to handle the messaging extension command {turnContextActivity.Name}: {ex.Message}", SeverityLevel.Error); throw; } }
public static T GetTaskModuleMetadata <T>(this IInvokeActivity activity) { // get task module metadata from activity if (activity.Value is JObject activityValueObject && activityValueObject.TryGetValue("data", StringComparison.InvariantCultureIgnoreCase, out JToken dataValue) && dataValue is JObject dataObject) { var adaptiveCardValue = dataObject.ToObject <AdaptiveCardValue <T> >(); if (adaptiveCardValue != null) { return(adaptiveCardValue.Data); } } throw new InvalidOperationException($"Task module metadata is not defined. Activity {JsonConvert.SerializeObject(activity)}"); }
/// <summary> /// Invoked when the user opens the messaging extension or searching any content in it. /// </summary> /// <param name="turnContext">Context object containing information cached for a single turn of conversation with a user.</param> /// <param name="query">Contains messaging extension query keywords.</param> /// <param name="cancellationToken">Propagates notification that operations should be canceled.</param> /// <returns>Messaging extension response object to fill compose extension section.</returns> /// <remarks> /// https://docs.microsoft.com/en-us/dotnet/api/microsoft.bot.builder.teams.teamsactivityhandler.onteamsmessagingextensionqueryasync?view=botbuilder-dotnet-stable. /// </remarks> protected override async Task <MessagingExtensionResponse> OnTeamsMessagingExtensionQueryAsync( ITurnContext <IInvokeActivity> turnContext, MessagingExtensionQuery query, CancellationToken cancellationToken) { IInvokeActivity turnContextActivity = turnContext?.Activity; try { MessagingExtensionQuery messageExtensionQuery = JsonConvert.DeserializeObject <MessagingExtensionQuery>(turnContextActivity.Value.ToString()); string searchQuery = SearchHelper.GetSearchQueryString(messageExtensionQuery); string onCallSMEUsers = string.Empty; turnContextActivity.TryGetChannelData <TeamsChannelData>(out var teamsChannelData); if (messageExtensionQuery.CommandId == Constants.ActiveCommandId || messageExtensionQuery.CommandId == Constants.ClosedCommandId) { onCallSMEUsers = await CardHelper.GetOnCallSMEuserListAsync(turnContext, this.onCallSupportDetailSearchService, this.teamId, this.logger); return(new MessagingExtensionResponse { ComposeExtension = await SearchHelper.GetSearchResultAsync(searchQuery, messageExtensionQuery.CommandId, messageExtensionQuery.QueryOptions.Count, messageExtensionQuery.QueryOptions.Skip, this.ticketSearchService, this.localizer, turnContext.Activity.From.AadObjectId, onCallSMEUsers), }); } if (turnContext != null && teamsChannelData.Team != null && teamsChannelData.Team.Id == this.teamId && (messageExtensionQuery.CommandId == Constants.UrgentCommandId || messageExtensionQuery.CommandId == Constants.AssignedCommandId || messageExtensionQuery.CommandId == Constants.UnassignedCommandId)) { return(new MessagingExtensionResponse { ComposeExtension = await SearchHelper.GetSearchResultAsync(searchQuery, messageExtensionQuery.CommandId, messageExtensionQuery.QueryOptions.Count, messageExtensionQuery.QueryOptions.Skip, this.ticketSearchService, this.localizer), }); } return(new MessagingExtensionResponse { ComposeExtension = new MessagingExtensionResult { Text = this.localizer.GetString("InvalidTeamText"), Type = "message", }, }); } catch (Exception ex) { this.logger.LogError(ex, $"Failed to handle the messaging extension command {turnContextActivity.Name}: {ex.Message}", SeverityLevel.Error); throw; } }
private AdaptiveCardInvokeValue GetAdaptiveCardInvokeValue(IInvokeActivity activity) { if (activity.Value == null) { var response = CreateAdaptiveCardInvokeErrorResponse(HttpStatusCode.BadRequest, "BadRequest", "Missing value property"); throw new InvokeResponseException(HttpStatusCode.BadRequest, response); } var obj = activity.Value as JObject; if (obj == null) { var response = CreateAdaptiveCardInvokeErrorResponse(HttpStatusCode.BadRequest, "BadRequest", "Value property is not properly formed"); throw new InvokeResponseException(HttpStatusCode.BadRequest, response); } AdaptiveCardInvokeValue invokeValue = null; try { invokeValue = obj.ToObject <AdaptiveCardInvokeValue>(); } #pragma warning disable CA1031 // Do not catch general exception types catch #pragma warning restore CA1031 // Do not catch general exception types { var response = CreateAdaptiveCardInvokeErrorResponse(HttpStatusCode.BadRequest, "BadRequest", "Value property is not properly formed"); throw new InvokeResponseException(HttpStatusCode.BadRequest, response); } if (invokeValue.Action == null) { var response = CreateAdaptiveCardInvokeErrorResponse(HttpStatusCode.BadRequest, "BadRequest", "Missing action property"); throw new InvokeResponseException(HttpStatusCode.BadRequest, response); } if (invokeValue.Action.Type != "Action.Execute") { var response = CreateAdaptiveCardInvokeErrorResponse(HttpStatusCode.BadRequest, "NotSupported", $"The action '{invokeValue.Action.Type}'is not supported."); throw new InvokeResponseException(HttpStatusCode.BadRequest, response); } return(invokeValue); }
// Fetches skillId from CardAction data if present public static string GetSkillId(this IInvokeActivity activity, ILogger logger) { if (activity == null) { logger.Log(LogLevel.Error, "activity is null from TaskModule"); throw new ArgumentNullException(nameof(activity)); } if (activity.Value == null) { logger.Log(LogLevel.Error, "activity.Value is null from TaskModule"); throw new ArgumentException("activity.Value is null.", nameof(activity)); } // GetSkillId from Activity Value var data = JObject.Parse(activity.Value.ToString()).SelectToken("data.data")?.ToObject <SkillCardActionData>(); return(data.SkillId ?? throw new ArgumentException("SkillId in TaskModule is null", nameof(SkillCardActionData))); }
/// <summary> /// Update user request details to Microsoft Azure Table storage. /// </summary> /// <param name="activity">Represents activity for current turn of bot.</param> /// <param name="userRequestDetails">User new request detail.</param> /// <returns>Represent a task queued for operation.</returns> public async Task <bool> UpdateUserRequestDetailsAsync(IInvokeActivity activity, AddUserResponseRequestDetail userRequestDetails) { if (userRequestDetails != null && activity != null) { var userResponse = new UserResponseEntity() { QuestionLabel = userRequestDetails.Label, QuestionText = userRequestDetails.Question, ResponseText = userRequestDetails.Response, UserId = activity.From.AadObjectId, LastUpdatedDate = DateTime.UtcNow, ResponseId = userRequestDetails.ResponseId, }; return(await this.userResponseStorageProvider.UpsertUserResponseAsync(userResponse)); } return(false); }
/// <summary> /// Store user suggestion to Microsoft Azure Table storage. /// </summary> /// <param name="activity">Represents activity for current turn of bot.</param> /// <param name="userSuggestionDetails">New suggestion detail.</param> /// <returns>Represent a task queued for operation.</returns> public async Task <CompanyResponseEntity> AddNewSuggestionAsync(IInvokeActivity activity, AddUserResponseRequestDetail userSuggestionDetails) { userSuggestionDetails = userSuggestionDetails ?? throw new ArgumentNullException(nameof(userSuggestionDetails)); activity = activity ?? throw new ArgumentNullException(nameof(activity)); var userResponse = new CompanyResponseEntity() { QuestionLabel = userSuggestionDetails.Label, QuestionText = userSuggestionDetails.Question, ResponseText = userSuggestionDetails.Response, ResponseId = Guid.NewGuid().ToString(), UserId = activity.From.AadObjectId, LastUpdatedDate = DateTime.UtcNow, CreatedDate = DateTime.UtcNow, ApprovalStatus = PendingRequestStatus, CreatedBy = activity.From.Name, ApprovedOrRejectedDate = DateTime.UtcNow, UserPrincipalName = userSuggestionDetails.UPN, }; await this.companyResponseStorageProvider.UpsertConverationStateAsync(userResponse); return(userResponse); }
/// <summary> /// Gets O365 connector card action query data. /// </summary> /// <param name="activity">The activity.</param> /// <returns>O365 connector card action query data.</returns> public static O365ConnectorCardActionQuery GetO365ConnectorCardActionQueryData(this IInvokeActivity activity) { return(JObject.FromObject(activity.Value).ToObject <O365ConnectorCardActionQuery>()); }
/// <summary> /// Checks if the activity is a O365 connector card action query. /// </summary> /// <param name="activity">Incoming activity.</param> /// <returns>True is activity is a actionable card query, false otherwise.</returns> public static bool IsO365ConnectorCardActionQuery(this IInvokeActivity activity) { return(activity.Type == ActivityTypes.Invoke && !string.IsNullOrEmpty(activity.Name) && activity.Name.StartsWith("actionableMessage/executeAction", StringComparison.OrdinalIgnoreCase)); }
/// <summary> /// Gets signin state verification query data. /// </summary> /// <param name="activity">The activity.</param> /// <returns>Signin state verification query data.</returns> public static SigninStateVerificationQuery GetSigninStateVerificationQueryData(this IInvokeActivity activity) { return(JObject.FromObject(activity.Value).ToObject <SigninStateVerificationQuery>()); }
protected virtual Task ReceiveInvoke(IBotContext context, IInvokeActivity invokeActivity) { return(Task.CompletedTask); }
private async Task OnInvoke(IInvokeActivity trigger, HttpResponseMessage response, CancellationToken token) { response.StatusCode = HttpStatusCode.NotImplemented; }
/// <summary> /// Checks if the activity is a compose extension query. /// </summary> /// <param name="activity">Incoming activity.</param> /// <returns>True is activity is a compose extension query, false otherwise.</returns> public static bool IsComposeExtensionQuery(this IInvokeActivity activity) { return(activity.Type == ActivityTypes.Invoke && !string.IsNullOrEmpty(activity.Name) && activity.Name.StartsWith("composeExtension", StringComparison.OrdinalIgnoreCase)); }
/// <summary> /// Gets the compose extension query data. /// </summary> /// <param name="activity">The activity.</param> /// <returns>Compose extension query data.</returns> public static ComposeExtensionQuery GetComposeExtensionQueryData(this IInvokeActivity activity) { return(JObject.FromObject(activity.Value).ToObject <ComposeExtensionQuery>()); }
/// <summary> /// Checks if the activity is a signin state verification query. /// </summary> /// <param name="activity">Incoming activity.</param> /// <returns>True is activity is a signin state verification query, false otherwise.</returns> public static bool IsSigninStateVerificationQuery(this IInvokeActivity activity) { return(activity.Type == ActivityTypes.Invoke && !string.IsNullOrEmpty(activity.Name) && activity.Name.StartsWith("signin/verifyState", StringComparison.OrdinalIgnoreCase)); }