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);
        }
Example #3
0
        /// <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;
            }
        }
Example #6
0
        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);
        }
Example #7
0
        // 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)));
        }
Example #8
0
        /// <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);
        }
Example #9
0
        /// <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);
        }
Example #10
0
 /// <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>());
 }
Example #11
0
 /// <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));
 }
Example #12
0
 /// <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>());
 }
Example #13
0
 protected virtual Task ReceiveInvoke(IBotContext context, IInvokeActivity invokeActivity)
 {
     return(Task.CompletedTask);
 }
Example #14
0
 private async Task OnInvoke(IInvokeActivity trigger, HttpResponseMessage response, CancellationToken token)
 {
     response.StatusCode = HttpStatusCode.NotImplemented;
 }
Example #15
0
 /// <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));
 }
Example #16
0
 /// <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>());
 }
Example #17
0
 /// <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));
 }