private async Task <bool> ImageValidatorAsync(PromptValidatorContext <IList <Attachment> > promptContext, CancellationToken cancellationToken)
        {
            var userData = await BotStateAccessor.GetAsync(promptContext.Context, () => new UserData(), cancellationToken);

            bool result    = false;
            var  userInput = promptContext.Recognized.Value;

            if (userInput != null && userInput.Any())
            {
                var remoteFileUrl = userInput[0].ContentUrl;

                // We make the call to the CustomVisionService
                var imageResult = await CustomVisionService.AnalyzeAsync(remoteFileUrl);

                if (imageResult != null)
                {
                    userData.ImageBoundingBox = imageResult.BoundingBox;
                    userData.CroppedImage     = await ImageUtils.GetCroppedImageAsync(remoteFileUrl, imageResult.BoundingBox.Left, imageResult.BoundingBox.Top, imageResult.BoundingBox.Width, imageResult.BoundingBox.Height);

                    result = imageResult.TagName.Contains("watch");
                }
            }

            return(await Task.FromResult(result));
        }
        private async Task <DialogTurnResult> AskforPhotoStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            var userData = await BotStateAccessor.GetAsync(stepContext.Context, () => new UserData(), cancellationToken);

            // Get the text from the activity to use to show the correct card
            var text = stepContext.Context.Activity.Text.ToLowerInvariant();
            var isPositiveFeedback = await TextAnalyticsService.GetTextSentimentAsync(text) > 0.5;

            userData.ProductWasFound = isPositiveFeedback;

            if (isPositiveFeedback)
            {
                // We go directly to the next step as we don't need any input from the user
                return(await stepContext.NextAsync());
            }
            else
            {
                return(await stepContext.PromptAsync(
                           AskForImage,
                           new PromptOptions
                {
                    Prompt = MessageFactory.Text("Do you have a photo of a watch you like? In order to help you we will need an image of a simillar watch to search."),
                    RetryPrompt = MessageFactory.Text("I didn't find any watch on the provided image. Please provide an image with a similar watch you are looking for to continue"),
                },
                           cancellationToken));
            }
        }
        private async Task <DialogTurnResult> SearchWatchesStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            var userData = await BotStateAccessor.GetAsync(stepContext.Context, () => new UserData(), cancellationToken);

            if (userData.ProductWasFound)
            {
                await stepContext.Context.SendActivityAsync("I'm happy that you like them! Please take your time looking at these items. I'm here to help in case of any questions.", cancellationToken : cancellationToken);

                // We end the dialog flow on this step as we don't need any other confirmation at this point.
                return(await stepContext.EndDialogAsync(cancellationToken : cancellationToken));
            }
            else
            {
                var activity = stepContext.Context.Activity;
                var file     = activity.Attachments[0];
                var reply    = activity.CreateReply();

                // Display cropped picture of the watch
                var croppedImageAttachment = await BotUtils.CreateAndUploadAttachmentAsync(reply.ServiceUrl, "image/png", reply.Conversation.Id, userData.CroppedImage.Image, "Cropped Image", _botConfigOptions);

                reply.Attachments = new List <Attachment>()
                {
                    croppedImageAttachment
                };
                reply.Text = "I can see the watch. Let me see what we have in stock.";
                await stepContext.Context.SendActivityAsync(reply, cancellationToken);

                var imageBoxCoordinates = userData.ImageBoundingBox;
                var boundingRectangle   = await ImageUtils.GetBoundingRectangleAsync(imageBoxCoordinates.Top, imageBoxCoordinates.Left, imageBoxCoordinates.Width, imageBoxCoordinates.Height, file.ContentUrl);

                reply.Attachments = await GetSimilarWatchesAsync(file.ContentUrl, boundingRectangle);

                reply.AttachmentLayout = AttachmentLayoutTypes.Carousel;
                reply.Text             = "I found these similar watches. Let me know if you have any questions.";

                // Send the card(s) to the user as an attachment to the activity
                await stepContext.Context.SendActivityAsync(reply, cancellationToken);

                // We end the dialog flow on this step as we don't need any other confirmation at this point.
                return(await stepContext.EndDialogAsync(cancellationToken : cancellationToken));
            }
        }