/// <summary>
        /// Get the reply to a question asked by end user.
        /// </summary>
        /// <param name="turnContext">Context object containing information cached for a single turn of conversation with a user.</param>
        /// <param name="message">Text message.</param>
        /// <returns>A task that represents the work queued to execute.</returns>
        public async Task GetReplyToQnAAsync(
            ITurnContext<IMessageActivity> turnContext,
            IMessageActivity message)
        {
            string text = message.Text?.ToLower()?.Trim() ?? string.Empty;

            try
            {
                var queryResult = new QnASearchResultList();

                ResponseCardPayload payload = new ResponseCardPayload();

                if (!string.IsNullOrEmpty(message.ReplyToId) && (message.Value != null))
                {
                    payload = ((JObject)message.Value).ToObject<ResponseCardPayload>();
                }

                queryResult = await this.qnaServiceProvider.GenerateAnswerAsync(question: text, isTestKnowledgeBase: false, payload.PreviousQuestions?.Last().Id.ToString(), payload.PreviousQuestions?.Last().Questions.First()).ConfigureAwait(false);
                bool answerFound = false;

                foreach (QnASearchResult answerData in queryResult.Answers)
                {
                    bool isContextOnly = answerData.Context?.IsContextOnly ?? false;
                    if (answerData.Id != -1 &&
                        ((!isContextOnly && payload.PreviousQuestions == null) ||
                            (isContextOnly && payload.PreviousQuestions != null)))
                    {
                        // This is the expected answer
                        await turnContext.SendActivityAsync(MessageFactory.Attachment(ResponseCard.GetCard(answerData, text, this.appBaseUri, payload))).ConfigureAwait(false);
                        answerFound = true;
                        break;
                    }
                }

                if (!answerFound)
                {
                    await turnContext.SendActivityAsync(MessageFactory.Attachment(UnrecognizedInputCard.GetCard(text))).ConfigureAwait(false);
                }
            }
            catch (Exception ex)
            {
                // Check if knowledge base is empty and has not published yet when end user is asking a question to bot.
                if (((ErrorResponseException)ex).Response.StatusCode == System.Net.HttpStatusCode.BadRequest)
                {
                    var knowledgeBaseId = await this.configurationProvider.GetSavedEntityDetailAsync(Constants.KnowledgeBaseEntityId).ConfigureAwait(false);
                    var hasPublished = await this.qnaServiceProvider.GetInitialPublishedStatusAsync(knowledgeBaseId).ConfigureAwait(false);

                    // Check if knowledge base has not published yet.
                    if (!hasPublished)
                    {
                        this.logger.LogError(ex, "Error while fetching the qna pair: knowledge base may be empty or it has not published yet.");
                        await turnContext.SendActivityAsync(MessageFactory.Attachment(UnrecognizedInputCard.GetCard(text))).ConfigureAwait(false);
                        return;
                    }
                }

                // Throw the error at calling place, if there is any generic exception which is not caught.
                throw;
            }
        }
        /// <summary>
        /// This method will construct the card for ask an expert, when invoked from the response card.
        /// </summary>
        /// <param name="payload">Payload from the response card.</param>
        /// <returns>Ask an expert card.</returns>
        public static Attachment GetCard(ResponseCardPayload payload)
        {
            var data = new AskAnExpertCardPayload
            {
                Title               = payload.UserQuestion, // Pre-populate the description with the user's question
                UserQuestion        = payload.UserQuestion,
                KnowledgeBaseAnswer = payload.KnowledgeBaseAnswer,
            };

            return(GetCard(false, data));
        }
Beispiel #3
0
        /// <summary>
        /// This method will construct the card for share feedback, when invoked from the response card.
        /// </summary>
        /// <param name="payload">Payload from the response card.</param>
        /// <returns>Ask an expert card.</returns>
        public static Attachment GetCard(ResponseCardPayload payload)
        {
            var cardPayload = new ShareFeedbackCardPayload
            {
                Description         = payload.UserQuestion, // Pre-populate the description with the user's question
                UserQuestion        = payload.UserQuestion,
                KnowledgeBaseAnswer = payload?.KnowledgeBaseAnswer,
            };

            return(GetCard(cardPayload, showValidationErrors: false));
        }
        /// <summary>
        /// This method will construct the card for share feedback, when invoked from the response card.
        /// </summary>
        /// <param name="payload">Payload from the response card.</param>
        /// <returns>Ask an expert card.</returns>
        public static Attachment GetCard(ResponseCardPayload payload)
        {
            var cardPayload = new ShareFeedbackCardPayload
            {
                UserQuestion          = payload.UserQuestion,
                KnowledgeBaseAnswer   = payload?.KnowledgeBaseAnswer,
                KnowledgeBaseQuestion = payload?.KnowledgeBaseQuestion,
                TicketId = payload?.TicketId,
            };

            return(GetCard(cardPayload, showValidationErrors: false));
        }
Beispiel #5
0
        /// <summary>
        /// This method will construct the card for share feedback, when invoked from the response card.
        /// </summary>
        /// <param name="payload">Payload from the response card.</param>
        /// <param name="appBaseUri">The base URI where the app is hosted.</param>
        /// <returns>Ask an expert card.</returns>
        public static Attachment GetCard(ResponseCardPayload payload, string appBaseUri)
        {
            var cardPayload = new ShareFeedbackCardPayload
            {
                DescriptionHelpful          = payload.UserQuestion,    // Pre-populate the description with the user's question
                DescriptionNeedsImprovement = payload.UserQuestion,    // Pre-populate the description with the user's question
                DescriptionNotHelpful       = payload.UserQuestion,    // Pre-populate the description with the user's question
                UserQuestion        = payload.UserQuestion,
                KnowledgeBaseAnswer = payload?.KnowledgeBaseAnswer,
                Project             = payload.Project,
            };

            return(GetCard(cardPayload, showValidationErrors: false, appBaseUri));
        }
Beispiel #6
0
        /// <summary>
        /// This method builds the body of the response card, and helps to render the follow up prompts if the response contains any.
        /// </summary>
        /// <param name="response">The QnA response model.</param>
        /// <param name="userQuestion">The user question - the actual question asked to the bot.</param>
        /// <param name="answer">The answer string.</param>
        /// <param name="appBaseUri">The base URI where the app is hosted.</param>
        /// <param name="payload">The response card payload.</param>
        /// <returns>A list of adaptive elements which makes up the body of the adaptive card.</returns>
        private static List <AdaptiveElement> BuildResponseCardBody(QnASearchResult response, string userQuestion, string answer, string appBaseUri, ResponseCardPayload payload)
        {
            var cardBodyToConstruct = new List <AdaptiveElement>()
            {
                new AdaptiveTextBlock
                {
                    Text    = answer,
                    Wrap    = true,
                    Spacing = AdaptiveSpacing.Medium,
                },
            };

            // If there follow up prompts, then the follow up prompts will render accordingly.
            if (response?.Context.Prompts.Count > 0)
            {
                List <QnADTO> previousQuestions = BuildListOfPreviousQuestions((int)response.Id, userQuestion, answer, payload);

                foreach (var item in response.Context.Prompts)
                {
                    var container = new AdaptiveContainer
                    {
                        Items = new List <AdaptiveElement>()
                        {
                            new AdaptiveColumnSet
                            {
                                Columns = new List <AdaptiveColumn>()
                                {
                                    new AdaptiveColumn
                                    {
                                        Width = AdaptiveColumnWidth.Stretch,
                                        VerticalContentAlignment = AdaptiveVerticalContentAlignment.Center,
                                        Items = new List <AdaptiveElement>()
                                        {
                                            new AdaptiveTextBlock
                                            {
                                                Wrap = true,
                                                Text = string.Format(Strings.SelectActionItemDisplayTextFormatting, item.DisplayText),
                                                HorizontalAlignment = AdaptiveHorizontalAlignment.Center,
                                            },
                                        },
                                        Spacing         = AdaptiveSpacing.Padding,
                                        BackgroundImage = new AdaptiveBackgroundImage
                                        {
                                            Url = new Uri(appBaseUri + "/content/Followupicon3.3.png"),
                                            HorizontalAlignment = AdaptiveHorizontalAlignment.Left,
                                            VerticalAlignment   = AdaptiveVerticalAlignment.Center,
                                        },
                                    },
                                },
                            },
                        },
                        SelectAction = new AdaptiveSubmitAction
                        {
                            Title = item.DisplayText,
                            Data  = new ResponseCardPayload
                            {
                                MsTeams = new CardAction
                                {
                                    Type        = ActionTypes.MessageBack,
                                    DisplayText = item.DisplayText,
                                    Text        = item.DisplayText,
                                },
                                PreviousQuestions = previousQuestions,
                                IsPrompt          = true,
                                QnAID             = item.QnaId.ToString(),
                            },
                        },
                        Separator = true,
                    };

                    cardBodyToConstruct.Add(container);
                }
            }
            else
            {
                cardBodyToConstruct.Add(new AdaptiveColumnSet
                {
                    Separator = true,
                    Columns   = new List <AdaptiveColumn>
                    {
                        new AdaptiveColumn
                        {
                            Items = new List <AdaptiveElement>
                            {
                                new AdaptiveTextBlock
                                {
                                    Weight = AdaptiveTextWeight.Lighter,
                                    Text   = Strings.ResponseFooterText,
                                    Wrap   = true,
                                },
                            },
                        },
                    },
                });
            }

            return(cardBodyToConstruct);
        }
Beispiel #7
0
        /// <summary>
        /// Construct the response card - when user asks a question to the QnA Maker through the bot.
        /// </summary>
        /// <param name="response">The response model.</param>
        /// <param name="userQuestion">Actual question that the user has asked the bot.</param>
        /// <param name="appBaseUri">The base URI where the app is hosted.</param>
        /// <param name="payload">The response card payload.</param>
        /// <returns>The response card to append to a message as an attachment.</returns>
        public static Attachment GetCard(QnASearchResult response, string userQuestion, string appBaseUri, ResponseCardPayload payload)
        {
            List <AdaptiveAction> actions = null;
            string project = (from r in response.Metadata where r.Name.Equals("project") select r).FirstOrDefault()?.Value;

            if (response?.Context.Prompts.Count == 0)
            {
                actions = BuildListOfActions(userQuestion, response.Answer, project, appBaseUri);
            }

            AdaptiveCard responseCard = new AdaptiveCard(new AdaptiveSchemaVersion(1, 2))
            {
                Body    = BuildResponseCardBody(response, userQuestion, response.Answer, appBaseUri, payload),
                Actions = actions,
            };

            return(new Attachment
            {
                ContentType = AdaptiveCard.ContentType,
                Content = responseCard,
            });
        }
Beispiel #8
0
        /// <summary>
        /// This method will build the list of previous questions.
        /// </summary>
        /// <param name="id">The QnA Id of the previous question.</param>
        /// <param name="userQuestion">The question that was asked by the user originally.</param>
        /// <param name="answer">The knowledge base answer.</param>
        /// <param name="payload">The response card payload.</param>
        /// <returns>A list of previous questions.</returns>
        private static List <QnADTO> BuildListOfPreviousQuestions(int id, string userQuestion, string answer, ResponseCardPayload payload)
        {
            var previousQuestions = payload.PreviousQuestions ?? new List <QnADTO>();

            previousQuestions.Add(new QnADTO
            {
                Id        = id,
                Questions = new List <string>()
                {
                    userQuestion,
                },
                Answer = answer,
            });

            return(previousQuestions);
        }
        /// <summary>
        /// This method builds the body of the response card, and helps to render the follow up prompts if the response contains any.
        /// </summary>
        /// <param name="response">The QnA response model.</param>
        /// /// <param name="userQuestion">The user question - the actual question asked to the bot.</param>
        /// <param name="answer">The answer string.</param>
        /// <param name="appBaseUri">The base URI where the app is hosted.</param>
        /// <param name="payload">The response card payload.</param>
        /// <returns>A list of adaptive elements which makes up the body of the adaptive card.</returns>
        private static List <AdaptiveElement> BuildResponseCardBody(QnASearchResult response, string userQuestion, string answer, string appBaseUri, ResponseCardPayload payload)
        {
            var cardBodyToConstruct = new List <AdaptiveElement>()
            {
                new AdaptiveTextBlock
                {
                    Weight = AdaptiveTextWeight.Bolder,
                    Text   = Strings.ResponseHeaderText,
                    Wrap   = true,
                },
                new AdaptiveTextBlock
                {
                    Text    = answer,
                    Wrap    = true,
                    Spacing = AdaptiveSpacing.Medium,
                },
            };

            // If there follow up prompts, then the follow up prompts will render accordingly.
            if (response?.Context.Prompts.Count > 0)
            {
                List <QnADTO> previousQuestions = BuildListOfPreviousQuestions((int)response.Id, userQuestion, answer, payload);

                foreach (var item in response.Context.Prompts)
                {
                    var container = new AdaptiveContainer
                    {
                        Items = new List <AdaptiveElement>()
                        {
                            new AdaptiveColumnSet
                            {
                                Columns = new List <AdaptiveColumn>()
                                {
                                    // This column will be for the icon.
                                    new AdaptiveColumn
                                    {
                                        Width = AdaptiveColumnWidth.Auto,
                                        VerticalContentAlignment = AdaptiveVerticalContentAlignment.Center,
                                        Items = new List <AdaptiveElement>()
                                        {
                                            new AdaptiveImage
                                            {
                                                Url   = new Uri(appBaseUri + "/content/Followupicon.png"),
                                                Size  = AdaptiveImageSize.Stretch,
                                                Style = AdaptiveImageStyle.Default,
                                            },
                                        },
                                        Spacing = AdaptiveSpacing.Small,
                                    },
                                    new AdaptiveColumn
                                    {
                                        Width = AdaptiveColumnWidth.Auto,
                                        VerticalContentAlignment = AdaptiveVerticalContentAlignment.Center,
                                        Items = new List <AdaptiveElement>()
                                        {
                                            new AdaptiveTextBlock
                                            {
                                                Wrap = true,
                                                Text = string.Format(Strings.SelectActionItemDisplayTextFormatting, item.DisplayText),
                                            },
                                        },
                                        Spacing = AdaptiveSpacing.Small,
                                    },
                                },
                            },
                        },
                        SelectAction = new AdaptiveSubmitAction
                        {
                            Title = item.DisplayText,
                            Data  = new ResponseCardPayload
                            {
                                MsTeams = new CardAction
                                {
                                    Type        = ActionTypes.MessageBack,
                                    DisplayText = item.DisplayText,
                                    Text        = item.DisplayText,
                                },
                                PreviousQuestions = previousQuestions,
                                IsPrompt          = true,
                            },
                        },
                        Separator = true,
                    };

                    cardBodyToConstruct.Add(container);
                }
            }

            return(cardBodyToConstruct);
        }
        /// <summary>
        /// Construct the response card - when user asks a question to the QnA Maker through the bot.
        /// </summary>
        /// <param name="response">The response model.</param>
        /// <param name="userQuestion">Actual question that the user has asked the bot.</param>
        /// <param name="appBaseUri">The base URI where the app is hosted.</param>
        /// <param name="payload">The response card payload.</param>
        /// <returns>The response card to append to a message as an attachment.</returns>
        public static Attachment GetCard(QnASearchResult response, string userQuestion, string appBaseUri, ResponseCardPayload payload)
        {
            AdaptiveCard responseCard = new AdaptiveCard(new AdaptiveSchemaVersion(1, 2))
            {
                Body    = BuildResponseCardBody(response, userQuestion, response.Answer, appBaseUri, payload),
                Actions = BuildListOfActions(userQuestion, response.Answer),
            };

            return(new Attachment
            {
                ContentType = AdaptiveCard.ContentType,
                Content = responseCard,
            });
        }
        /// <summary>
        /// Get the reply to a question asked by end user.
        /// </summary>
        /// <param name="turnContext">Context object containing information cached for a single turn of conversation with a user.</param>
        /// <param name="message">Text message.</param>
        /// <returns>A task that represents the work queued to execute.</returns>
        private async Task GetQuestionAnswerReplyAsync(
            ITurnContext turnContext,
            IMessageActivity message)
        {
            string text = message.Text?.ToLower()?.Trim() ?? string.Empty;

            try
            {
                var queryResult = new QnASearchResultList();

                ResponseCardPayload payload = new ResponseCardPayload();

                if (!string.IsNullOrEmpty(message.ReplyToId) && (message.Value != null))
                {
                    payload = ((JObject)message.Value).ToObject <ResponseCardPayload>();
                }

                queryResult = await _qnaServiceProvider.GenerateAnswerAsync(question : text, isTestKnowledgeBase : false, payload.PreviousQuestions?.First().Id.ToString(), payload.PreviousQuestions?.First().Questions.First()).ConfigureAwait(false);

                if (queryResult.Answers.First().Id != -1)
                {
                    var answerData = queryResult.Answers.First();
                    payload.QnaPairId = answerData.Id ?? -1;

                    AnswerModel answerModel = new AnswerModel();

                    if (Validators.IsValidJSON(answerData.Answer))
                    {
                        answerModel = JsonConvert.DeserializeObject <AnswerModel>(answerData.Answer);
                    }

                    if (!string.IsNullOrEmpty(answerModel?.Title) || !string.IsNullOrEmpty(answerModel?.Subtitle) || !string.IsNullOrEmpty(answerModel?.ImageUrl) || !string.IsNullOrEmpty(answerModel?.RedirectionUrl))
                    {
                        await turnContext.SendActivityAsync(MessageFactory.Attachment(MessagingExtensionQnaCard.GetEndUserRichCard(text, answerData, payload.QnaPairId))).ConfigureAwait(false);
                    }
                    else
                    {
                        await turnContext.SendActivityAsync(MessageFactory.Attachment(ResponseCard.GetCard(answerData, text, _appBaseUri, payload))).ConfigureAwait(false);
                    }

                    _telemetryClient.TrackEvent(
                        FaqPlusPlusBot.EVENT_ANSWERED_QUESTION_SINGLE,
                        new Dictionary <string, string>
                    {
                        { "QuestionId", payload.QnaPairId.ToString() },
                        { "QuestionAnswered", queryResult.Answers[0].Questions[0] },
                        { "QuestionAsked", text },
                        { "UserName", turnContext.Activity.From.Name },
                        { "UserAadId", turnContext.Activity.From?.AadObjectId ?? "" },
                        { "Product", _options.ProductName },
                    });
                }
                else
                {
                    await turnContext.SendActivityAsync(MessageFactory.Attachment(UnrecognizedInputCard.GetCard(text))).ConfigureAwait(false);
                }
            }
            catch (Exception ex)
            {
                // Check if knowledge base is empty and has not published yet when end user is asking a question to bot.
                if (((Azure.CognitiveServices.Knowledge.QnAMaker.Models.ErrorResponseException)ex).Response.StatusCode == HttpStatusCode.BadRequest)
                {
                    var knowledgeBaseId = await _configurationProvider.GetSavedEntityDetailAsync(Constants.KnowledgeBaseEntityId).ConfigureAwait(false);

                    var hasPublished = await _qnaServiceProvider.GetInitialPublishedStatusAsync(knowledgeBaseId).ConfigureAwait(false);

                    // Check if knowledge base has not published yet.
                    if (!hasPublished)
                    {
                        this._telemetryClient.TrackException(ex, new Dictionary <string, string> {
                            {
                                "message", "Error while fetching the qna pair: knowledge base may be empty or it has not published yet."
                            },
                        });
                        await turnContext.SendActivityAsync(MessageFactory.Attachment(UnrecognizedInputCard.GetCard(text))).ConfigureAwait(false);

                        return;
                    }
                }

                // Throw the error at calling place, if there is any generic exception which is not caught.
                throw;
            }
        }
Beispiel #12
0
        /// <summary>
        /// This method builds the body of the response card, and helps to render the follow up prompts if the response contains any.
        /// </summary>
        /// <param name="response">The QnA response model.</param>
        /// /// <param name="userQuestion">The user question - the actual question asked to the bot.</param>
        /// <param name="answer">The answer string.</param>
        /// <param name="appBaseUri">The base URI where the app is hosted.</param>
        /// <param name="payload">The response card payload.</param>
        /// <param name="isRichCard">Boolean value where true represent it is a rich card while false represent it is a normal card.</param>
        /// <returns>A list of adaptive elements which makes up the body of the adaptive card.</returns>
        private static List <AdaptiveElement> BuildResponseCardBody(QnASearchResult response, string userQuestion, string answer, string appBaseUri, ResponseCardPayload payload, bool isRichCard)
        {
            var textAlignment = CultureInfo.CurrentCulture.TextInfo.IsRightToLeft ? AdaptiveHorizontalAlignment.Right : AdaptiveHorizontalAlignment.Left;
            var answerModel   = isRichCard ? JsonConvert.DeserializeObject <AnswerModel>(response?.Answer) : new AnswerModel();

            var cardBodyToConstruct = new List <AdaptiveElement>()
            {
                new AdaptiveTextBlock
                {
                    Weight = AdaptiveTextWeight.Bolder,
                    Text   = Strings.ResponseHeaderText,
                    Wrap   = true,
                    HorizontalAlignment = textAlignment,
                },
                new AdaptiveTextBlock
                {
                    Size                = AdaptiveTextSize.Default,
                    Wrap                = true,
                    Text                = response?.Questions[0],
                    IsVisible           = isRichCard,
                    HorizontalAlignment = textAlignment,
                },
                new AdaptiveTextBlock
                {
                    Wrap   = true,
                    Text   = answerModel.Title ?? string.Empty,
                    Size   = AdaptiveTextSize.Large,
                    Weight = AdaptiveTextWeight.Bolder,
                    HorizontalAlignment = textAlignment,
                },
                new AdaptiveTextBlock
                {
                    Text = answerModel.Subtitle ?? string.Empty,
                    Size = AdaptiveTextSize.Medium,
                    HorizontalAlignment = textAlignment,
                },
            };

            if (!string.IsNullOrWhiteSpace(answerModel?.ImageUrl))
            {
                cardBodyToConstruct.Add(new AdaptiveImage
                {
                    Url       = new Uri(answerModel.ImageUrl.Trim()),
                    Size      = AdaptiveImageSize.Auto,
                    Style     = AdaptiveImageStyle.Default,
                    AltText   = answerModel.Title,
                    IsVisible = isRichCard,
                });
            }

            cardBodyToConstruct.Add(new AdaptiveTextBlock
            {
                Text                = answer,
                Wrap                = true,
                Size                = isRichCard ? AdaptiveTextSize.Small : AdaptiveTextSize.Default,
                Spacing             = AdaptiveSpacing.Medium,
                HorizontalAlignment = textAlignment,
            });

            // If there follow up prompts, then the follow up prompts will render accordingly.
            if (response?.Context.Prompts.Count > 0)
            {
                List <QnADTO> previousQuestions = BuildListOfPreviousQuestions((int)response.Id, userQuestion, answer, payload);

                foreach (var item in response.Context.Prompts)
                {
                    var container = new AdaptiveContainer
                    {
                        Items = new List <AdaptiveElement>()
                        {
                            new AdaptiveColumnSet
                            {
                                Columns = new List <AdaptiveColumn>()
                                {
                                    // This column will be for the icon.
                                    new AdaptiveColumn
                                    {
                                        Width = AdaptiveColumnWidth.Auto,
                                        VerticalContentAlignment = AdaptiveVerticalContentAlignment.Center,
                                        Items = new List <AdaptiveElement>()
                                        {
                                            new AdaptiveImage
                                            {
                                                Url         = new Uri(appBaseUri + "/content/Followupicon.png"),
                                                PixelWidth  = IconWidth,
                                                PixelHeight = IconHeight,
                                            },
                                        },
                                        Spacing = AdaptiveSpacing.Small,
                                    },
                                    new AdaptiveColumn
                                    {
                                        Width = AdaptiveColumnWidth.Stretch,
                                        VerticalContentAlignment = AdaptiveVerticalContentAlignment.Center,
                                        Items = new List <AdaptiveElement>()
                                        {
                                            new AdaptiveTextBlock
                                            {
                                                Wrap = true,
                                                Text = string.Format(Strings.SelectActionItemDisplayTextFormatting, item.DisplayText?.Trim()),
                                                HorizontalAlignment = textAlignment,
                                            },
                                        },
                                        Spacing = AdaptiveSpacing.Small,
                                    },
                                },
                            },
                        },
                        SelectAction = new AdaptiveSubmitAction
                        {
                            Title = item.DisplayText,
                            Data  = new ResponseCardPayload
                            {
                                MsTeams = new CardAction
                                {
                                    Type        = ActionTypes.MessageBack,
                                    DisplayText = item.DisplayText,
                                    Text        = item.DisplayText,
                                },
                                PreviousQuestions = previousQuestions,
                                IsPrompt          = true,
                            },
                        },
                        Separator = true,
                    };

                    cardBodyToConstruct.Add(container);
                }
            }

            return(cardBodyToConstruct);
        }
Beispiel #13
0
        /// <summary>
        /// Construct the response card - when user asks a question to the QnA Maker through the bot.
        /// </summary>
        /// <param name="response">The response model.</param>
        /// <param name="userQuestion">Actual question that the user has asked the bot.</param>
        /// <param name="appBaseUri">The base URI where the app is hosted.</param>
        /// <param name="payload">The response card payload.</param>
        /// <returns>The response card to append to a message as an attachment.</returns>
        public static Attachment GetCard(QnASearchResult response, string userQuestion, string appBaseUri, ResponseCardPayload payload)
        {
            bool isRichCard = false;
            AdaptiveSubmitActionData answerModel = new AdaptiveSubmitActionData();

            if (Validators.IsValidJSON(response.Answer))
            {
                answerModel = JsonConvert.DeserializeObject <AdaptiveSubmitActionData>(response.Answer);
                isRichCard  = Validators.IsRichCard(answerModel);
            }

            string       answer       = isRichCard ? answerModel.Description : response.Answer;
            AdaptiveCard responseCard = new AdaptiveCard(new AdaptiveSchemaVersion(1, 2))
            {
                Body    = BuildResponseCardBody(response, userQuestion, answer, appBaseUri, payload, isRichCard),
                Actions = BuildListOfActions(userQuestion, answer),
            };

            if (!string.IsNullOrEmpty(answerModel.RedirectionUrl))
            {
                responseCard.Actions.Add(
                    new AdaptiveOpenUrlAction
                {
                    Title = Strings.OpenArticle,
                    Url   = new Uri(answerModel.RedirectionUrl),
                });
            }

            return(new Attachment
            {
                ContentType = AdaptiveCard.ContentType,
                Content = responseCard,
            });
        }
Beispiel #14
0
        /// <summary>
        /// Get the reply to a question asked by end user.
        /// </summary>
        /// <param name="turnContext">Context object containing information cached for a single turn of conversation with a user.</param>
        /// <param name="message">Text message.</param>
        /// <returns>A task that represents the work queued to execute.</returns>
        private async Task GetQuestionAnswerReplyAsync(
            ITurnContext <IMessageActivity> turnContext,
            IMessageActivity message)
        {
            string text = message.Text?.ToLower()?.Trim() ?? string.Empty;

            try
            {
                var queryResult = new QnASearchResultList();

                ResponseCardPayload payload = new ResponseCardPayload();

                if (!string.IsNullOrEmpty(message.ReplyToId) && (message.Value != null))
                {
                    payload = ((JObject)message.Value).ToObject <ResponseCardPayload>();
                }

                queryResult = await this.qnaServiceProvider.GenerateAnswerAsync(question : text, isTestKnowledgeBase : false, payload.PreviousQuestions?.First().Id.ToString(), payload.PreviousQuestions?.First().Questions.First()).ConfigureAwait(false);

                if (queryResult.Answers.First().Id != -1)
                {
                    var         answerData  = queryResult.Answers.First();
                    AnswerModel answerModel = new AnswerModel();

                    if (Validators.IsValidJSON(answerData.Answer))
                    {
                        answerModel = JsonConvert.DeserializeObject <AnswerModel>(answerData.Answer);
                    }

                    if (!string.IsNullOrEmpty(answerModel?.Title) || !string.IsNullOrEmpty(answerModel?.Subtitle) || !string.IsNullOrEmpty(answerModel?.ImageUrl) || !string.IsNullOrEmpty(answerModel?.RedirectionUrl))
                    {
                        await turnContext.SendActivityAsync(MessageFactory.Attachment(MessagingExtensionQnaCard.GetEndUserRichCard(text, answerData))).ConfigureAwait(false);
                    }
                    else
                    {
                        await turnContext.SendActivityAsync(MessageFactory.Attachment(ResponseCard.GetCard(answerData, text, this.appBaseUri, payload))).ConfigureAwait(false);
                    }
                }
                else
                {
                    await turnContext.SendActivityAsync(MessageFactory.Attachment(UnrecognizedInputCard.GetCard(text))).ConfigureAwait(false);
                }
            }
            catch (Exception ex)
            {
                // Check if knowledge base is empty and has not published yet when end user is asking a question to bot.
                if (((ErrorResponseException)ex).Response.StatusCode == HttpStatusCode.BadRequest)
                {
                    var knowledgeBaseId = await this.configurationProvider.GetSavedEntityDetailAsync(Constants.KnowledgeBaseEntityId).ConfigureAwait(false);

                    var hasPublished = await this.qnaServiceProvider.GetInitialPublishedStatusAsync(knowledgeBaseId).ConfigureAwait(false);

                    // Check if knowledge base has not published yet.
                    if (!hasPublished)
                    {
                        this.logger.LogError(ex, "Error while fetching the qna pair: knowledge base may be empty or it has not published yet.");
                        await turnContext.SendActivityAsync(MessageFactory.Attachment(UnrecognizedInputCard.GetCard(text))).ConfigureAwait(false);

                        return;
                    }
                }

                // Throw the error at calling place, if there is any generic exception which is not caught.
                throw;
            }
        }