Example #1
0
        private async Task <DialogTurnResult> GetFundDocumentURLStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            var documentModel = (FundDocumentModel)stepContext.Values[FundDocumentModelValue];
            var result        = (FoundChoice)stepContext.Result;


            if (result != null)
            {
                documentModel.FundDocument = result.Value.Trim().ToLower();
            }

            var fundDocumentModel = APIService.GetFundDocumentUrl(documentModel);

            //no document was found.
            if (fundDocumentModel == null)
            {
                await stepContext.Context.SendActivityAsync(ChannelFormattingService.FormatSimpleMessage(stepContext.Context, "The requested document is not available."));
            }
            else
            {
                //get the fund document's attributes and send a formatted response.
                var month    = fundDocumentModel.Month == default ? "" : new DateTime(2000, fundDocumentModel.Month, 1).ToString("MMM", CultureInfo.InvariantCulture);
                var year     = fundDocumentModel.Year == default ? "" : $"{fundDocumentModel.Year}";
                var quarter  = fundDocumentModel.Quarter == default ? "" : $"Q{fundDocumentModel.Quarter}";
                var parnFund = fundDocumentModel.FundName == null ? "" : $"{CultureInfo.InvariantCulture.TextInfo.ToTitleCase(fundDocumentModel.FundName)}";
                var url      = fundDocumentModel.HasKenticoURL ? APIService.domain + fundDocumentModel.DocumentURL : fundDocumentModel.DocumentURL;

                var link = ChannelFormattingService.FormatLinkMessageAndSaveToState(stepContext.Context, url, _botService, cancellationToken);

                await stepContext.Context.SendActivityAsync(ChannelFormattingService.FormatSimpleMessage(stepContext.Context, $"You can view the {month} {year} {quarter} {parnFund} {documentModel.FundDocument} " +
                                                                                                         $"by clicking {link}"));
            }

            return(await stepContext.EndDialogAsync(null, cancellationToken));
        }
Example #2
0
        private async Task <DialogTurnResult> GetFundNameStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            var documentModel = (FundDocumentModel)stepContext.Values[FundDocumentModelValue];

            //if no fund is needed
            if (!((bool)stepContext.Result))
            {
                return(await stepContext.NextAsync(null, cancellationToken));
            }

            //generate choices with the list of funds
            var SuggestedActions = SuggestedActionGenerator.GenerateSuggestedActions(Common.PIFundNames);
            var reply            = MessageFactory.Text(ChannelFormattingService.FormatSimpleMessage(stepContext.Context, $"Which fund would you like to see the {documentModel.FundDocument} for?"));

            reply.SuggestedActions = SuggestedActions;

            var retryReply = MessageFactory.Text(ChannelFormattingService.FormatSimpleMessage(stepContext.Context, $"Please select a valid fund to view its {documentModel.FundDocument}"));

            retryReply.SuggestedActions = SuggestedActions;

            //ask for fund name
            return(await stepContext.PromptAsync($"{nameof(FundDocumentsDialog)}.askFundName", new PromptOptions
            {
                Prompt = reply,
                RetryPrompt = retryReply
            }, cancellationToken));
        }
Example #3
0
        private async Task <DialogTurnResult> GetDocumentAssociatedWithFundNameStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            var fundName      = ((string)stepContext.Result);
            var documentModel = (FundDocumentModel)stepContext.Values[FundDocumentModelValue];

            //get response from user, if any, and save it to the model
            if (fundName != null)
            {
                fundName = fundName.Trim().ToLower();

                if (fundName != Common.PIFundNames[Common.PIFundIndexInNamesList].ToLower())
                {
                    fundName = "PI " + fundName;
                }

                documentModel.FundName = fundName;
                stepContext.Values[FundDocumentModelValue] = documentModel;
            }

            if (!string.IsNullOrEmpty(documentModel.FundDocument))
            {
                //wants general PI Fund document   i.e annual report
                return(await stepContext.NextAsync(null, cancellationToken));
            }

            var documentChoices = Common.DocumentsRequireFundName.ToList().ConvertAll(s => CultureInfo.InvariantCulture.TextInfo.ToTitleCase(s));

            //if no document was specified, but a fund was, ask to select which document.
            return(await stepContext.PromptAsync($"{nameof(FundDocumentsDialog)}.askFundDocument", new PromptOptions {
                Prompt = MessageFactory.Text(ChannelFormattingService.FormatSimpleMessage(stepContext.Context, $"Which {CultureInfo.InvariantCulture.TextInfo.ToTitleCase(documentModel.FundName)} document would you like to see?")),
                RetryPrompt = MessageFactory.Text(ChannelFormattingService.FormatSimpleMessage(stepContext.Context, $"Please select a valid document option")),
                Choices = ChoiceFactory.ToChoices(documentChoices)
            }, cancellationToken));
        }
Example #4
0
        public async Task <DialogTurnResult> GetAttributionsAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            var fundName         = (string)stepContext.Result;
            var attributionModel = (FundAttributionModel)stepContext.Values[FundAttributionModelValue];

            //save given fund name (if any) to the model
            if (fundName != null)
            {
                fundName = fundName.Trim().ToLower();

                //if selected choice was "Mid cap fund" etc, we must add PI to the front. (does not apply to PI Fund)
                if (fundName != Common.PIFundNames[Common.PIFundIndexInNamesList].ToLower())
                {
                    fundName = "PI " + fundName;
                }

                attributionModel.FundName = fundName;
            }

            //get fund contributors or detractors
            var fundAttributions = APIService.GetFundAttributions(attributionModel).Result;

            //format it in a header -> subtitle -> data
            var placeholder = attributionModel.ContributorsOrDetractors == 0 ? "contributors" : "detractors";
            var response    = ChannelFormattingService.FormatHeaderSubtitleAndList(stepContext.Context,
                                                                                   $"{attributionModel.FundTicker} top {placeholder}:", fundAttributions);

            await stepContext.Context.SendActivityAsync(response, null, null, cancellationToken);

            return(await stepContext.EndDialogAsync(null, cancellationToken));
        }
Example #5
0
        private async Task <DialogTurnResult> ProcessQnaIntentAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            //send message to QnA Maker
            var results = await _luisService.QnaRecognizer.GetAnswersAsync(stepContext.Context);

            //if an answer was found, respond with the answer.
            //otherwise, respond with error.
            if (results.Any())
            {
                ConversationData conversationData = await _botService.conversationDataAccessor.GetAsync(stepContext.Context, () => new ConversationData());

                conversationData.FailedAttemptsCount = 0;

                await stepContext.Context.SendActivityAsync(MessageFactory.Text(results.First().Answer), cancellationToken);

                await stepContext.Context.SendActivityAsync(ChannelFormattingService.FormatMessages(stepContext.Context, "If this does not answer your question, please try structuring your question in a " +
                                                                                                    "different way and/or to be more specific", new List <Func <string, string> > {
                    ToHtml.MessageWrapInSpan
                }), cancellationToken : cancellationToken);
            }
            else
            {
                await ProcessRecognitionError(ChannelFormattingService.FormatMessages(stepContext.Context, "Sorry, I was not able to find an answer to your question. Please try structuring your question in a " +
                                                                                      "different way and/or to be more specific", new List <Func <string, string> > {
                    ToHtml.MessageWrapInSpan
                }), stepContext, cancellationToken);
            }

            return(await stepContext.NextAsync(null, cancellationToken));
        }
Example #6
0
        private async Task <DialogTurnResult> DispatchRecognitionStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            UserProfile profile = await _botService.profileStateAccessor.GetAsync(stepContext.Context, () => new UserProfile());

            ConversationData conversationState = await _botService.conversationDataAccessor.GetAsync(stepContext.Context, () => new ConversationData());

            conversationState.IsCancellable = true;

            //get the intent from LUIS Dispatch. Identifies whether message should be passed onto the main LUIS app or QnA Maker.
            var recognizerResult = await _luisService.Dispatch.RecognizeAsync(stepContext.Context, cancellationToken);

            (string intent, double score) = recognizerResult.GetTopScoringIntent();
            var luisResult = recognizerResult.Properties["luisResult"] as LuisResult;


            switch (intent)
            {
            case /* [redacted] */:
                return(await ProcessQnaIntentAsync(stepContext, cancellationToken));

            case /* [redacted] */:
                return(await ProcessLuisIntentAsync(stepContext, cancellationToken, luisResult));

            default:
                await ProcessRecognitionError(ChannelFormattingService.FormatMessages(stepContext.Context, "Sorry, I do not know what you mean.",
                                                                                      new List <Func <string, string> > {
                    ToHtml.MessageWrapInSpan
                }), stepContext, cancellationToken);

                return(await stepContext.NextAsync(null, cancellationToken));
            }
        }
        private async Task <DialogTurnResult> ParseGivenParametersAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            var attrModel  = new FundAttributesModel();
            var luisResult = (LuisResult)stepContext.Options;

            /*find the attribute entity types that were recognized.
             *      i.e. If I send "I want the PARMX expense ratio", this step will
             *      find that the user requested FundBasicAttributeType and save it within the FundAttributesModel
             */
            FindFundNameAndAttributeEntities(attrModel, luisResult);

            //save the model
            stepContext.Values[FundAttributesModelValue] = attrModel;

            //Prompt for fund name by giving a list of choices. This makes it so the user doesn't get prompted with an error if he/she tries to type the fund themselves
            if (string.IsNullOrEmpty(attrModel.PIFundName))
            {
                var SuggestedActions = SuggestedActionGenerator.GenerateSuggestedActions(Common.PIFundNames);
                var reply            = MessageFactory.Text(ChannelFormattingService.FormatSimpleMessage(stepContext.Context, $"Which fund would you like to see your requested attributes for?"));
                reply.SuggestedActions = SuggestedActions;

                var retryReply = MessageFactory.Text(ChannelFormattingService.FormatSimpleMessage(stepContext.Context, $"Please select a valid fund to view its attributes"));
                retryReply.SuggestedActions = SuggestedActions;

                return(await stepContext.PromptAsync($"{nameof(FundSpecificAttributesDialog)}.askFundName", new PromptOptions
                {
                    Prompt = reply,
                    RetryPrompt = retryReply
                }, cancellationToken));
            }

            return(await stepContext.NextAsync(null, cancellationToken));
        }
Example #8
0
        public AdapterFrameworkHandler(IConfiguration configuration, ILogger <BotFrameworkHttpAdapter> logger, BotStateService botService, ConversationState conversationState = null)
            : base(configuration, logger)
        {
            _logger     = logger;
            _botService = botService;

            //This method is triggered when an exception is thrown somewhere within a Dialog.
            OnTurnError = async(turnContext, exception) =>
            {
                // Log any leaked exception from the application.
                logger.LogError($"Exception caught : {exception.Message} {JsonConvert.SerializeObject(exception)}");

                // Send a catch-all apology to the user.
                await turnContext.SendActivityAsync(ChannelFormattingService.FormatSimpleMessage(turnContext, "Sorry, it looks like something went wrong. Please refresh the page and try again."));

                if (conversationState != null)
                {
                    try
                    {
                        // Delete the conversationState for the current conversation to prevent the
                        // bot from getting stuck in a error-loop caused by being in a bad state.
                        await conversationState.DeleteAsync(turnContext);
                    }
                    catch (Exception e)
                    {
                        logger.LogError($"Exception caught on attempting to Delete ConversationState : {e.Message}");
                    }
                }
            };
        }
Example #9
0
        private async Task <DialogTurnResult> ParseGivenParametersAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            var luisResult    = (LuisResult)stepContext.Options;
            var documentModel = new FundDocumentModel();

            //Get all necessary entities to perform query for fund documents
            var parsedDate = GetDateTimeEntity(luisResult); //try get year

            documentModel.SetDate(parsedDate);
            documentModel.SetQuarter(GetEntityModelResolution(luisResult, QuarterDateTime)); //try get quarter
            documentModel.FundName     = GetEntityModelResolution(luisResult, FundName);     //try get fund name
            documentModel.FundDocument = GetEntityModelResolution(luisResult, DocumentType); //try get fund document

            //if no document and no fund was specified, direct user to fund document page
            if (string.IsNullOrEmpty(documentModel.FundDocument) && string.IsNullOrEmpty(documentModel.FundName))
            {
                var link = ChannelFormattingService.FormatLinkMessageAndSaveToState(stepContext.Context, Common.FundDocumentsURL, _botService, cancellationToken);

                await stepContext.Context.SendActivityAsync(ChannelFormattingService.FormatSimpleMessage(stepContext.Context,
                                                                                                         $"You can view all fund documents by clicking {link}"));

                return(await stepContext.EndDialogAsync(null, cancellationToken));
            }

            var needsFund = false;

            //if no fund was given, but the document requires a fund to be specified
            if (string.IsNullOrEmpty(documentModel.FundName) && Common.DocumentsRequireFundName.Contains(documentModel.FundDocument))
            {
                needsFund = true;
            }

            stepContext.Values[FundDocumentModelValue] = documentModel;
            return(await stepContext.NextAsync(needsFund, cancellationToken));
        }
Example #10
0
        /*
         * triggers every time a new conversation is created
         *  i.e when the web app is refreshed, a new conversation is created and this is why you see these messages appear
         *  new members added to the conversation stored in membersAdded
         */
        protected override async Task OnMembersAddedAsync(IList <ChannelAccount> membersAdded, ITurnContext <IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
        {
            foreach (var member in membersAdded)
            {
                //check if the user who joined the conversation is not the Bot
                if (member.Id != turnContext.Activity.Recipient.Id)
                {
                    await turnContext.SendActivityAsync(ChannelFormattingService.FormatSimpleMessage(turnContext, "Hello, I'm Jerry, the PI Investments AI Chatbot!"));

                    await turnContext.SendActivityAsync(ChannelFormattingService.FormatSimpleMessage(turnContext, "How can I help you?"));
                }
            }
        }
        //this method checks for user interruptions. specifically returns a non-null value if the user says cancel or help
        private async Task <DialogTurnResult> InterruptAsync(DialogContext innerDc, CancellationToken cancellationToken)
        {
            var conversationData = await _botService.conversationDataAccessor.GetAsync(innerDc.Context, () => new ConversationData());

            //if the current activity is a message
            if (innerDc.Context.Activity.Type == ActivityTypes.Message)
            {
                var text = innerDc.Context.Activity.Text.ToLowerInvariant();

                //send message to LUIS
                var recognizerResult = await _luisService.Dispatch.RecognizeAsync(innerDc.Context, cancellationToken);

                var topIntent = recognizerResult.GetTopScoringIntent();

                if (topIntent.intent == "luis_intent")
                {
                    var luisResult = recognizerResult.Properties["luisResult"] as LuisResult;

                    switch (luisResult.ConnectedServiceResult.TopScoringIntent.Intent)
                    {
                    case "Utilities.Help":

                        await innerDc.Context.SendActivityAsync(ChannelFormattingService.FormatMessages(innerDc.Context, $"Showing help...",
                                                                                                        new List <Func <string, string> > {
                            ToHtml.MessageWrapInSpan
                        }), cancellationToken : cancellationToken);

                        return(await innerDc.CancelAllDialogsAsync());

                    case "Utilities.Cancel":
                        await innerDc.Context.SendActivityAsync(ChannelFormattingService.FormatMessages(innerDc.Context, $"Alright!",
                                                                                                        new List <Func <string, string> > {
                            ToHtml.MessageWrapInSpan
                        }), cancellationToken : cancellationToken);

                        await _botService.conversationDataAccessor.SetAsync(innerDc.Context, conversationData);

                        await _botService.ConversationState.SaveChangesAsync(innerDc.Context, false, cancellationToken);

                        return(await innerDc.CancelAllDialogsAsync());
                    }
                }
            }

            return(null);
        }
        private async Task <DialogTurnResult> GetSectorWeightingsAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            var fundName    = (string)stepContext.Result;
            var sectorModel = (FundSectorWeightsModel)stepContext.Values[FundSectorModelValue];

            //if a fund was selected from the previous step, save it to the model.
            if (fundName != null)
            {
                fundName = fundName.Trim().ToLower();

                if (fundName != "all funds" && fundName != Common.PIFundNames[Common.PIFundIndexInNamesList].ToLower())
                {
                    fundName = "PI " + fundName;
                }

                sectorModel.AddFund(fundName);
            }

            //if the funds requested contains the fixed income fund, tell the user that no sector weightings exist and remove it.
            if (sectorModel.Funds.Any(s => s == "PRFIX"))
            {
                await stepContext.Context.SendActivityAsync(ChannelFormattingService.FormatSimpleMessage(stepContext.Context, "The PI Fixed Income Fund does not contain sector weightings"));

                sectorModel.Funds.RemoveAll(s => s == "PRFIX");
                stepContext.Values[FundSectorModelValue] = sectorModel;

                //if the fixed income fund was the only fund requested, end the dialog.
                if (sectorModel.Funds.Count == 0)
                {
                    return(await stepContext.EndDialogAsync(null, cancellationToken));
                }
            }

            //get the fund sector weights
            var fundSectorWeights = APIService.GetFundSectorWeights(sectorModel).Result;

            //format the sector weights in header -> subtitles -> list.
            var response = ChannelFormattingService.FormatHeaderSubtitleAndList(stepContext.Context,
                                                                                $"{CultureInfo.InvariantCulture.TextInfo.ToTitleCase(sectorModel.Sector)} sector weights", fundSectorWeights);

            await stepContext.Context.SendActivityAsync(response, null, null, cancellationToken);

            return(await stepContext.EndDialogAsync(null, cancellationToken));
        }
        private async Task <DialogTurnResult> ParseGivenParametersAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            var luisResult = (LuisResult)stepContext.Options;

            var sectorModel = new FundSectorWeightsModel();

            sectorModel.Funds = new List <string>();

            //find fund names and sector
            GetEntityResolutions(luisResult, sectorModel);

            stepContext.Values[FundSectorModelValue] = sectorModel;


            //if no sector was given
            if (string.IsNullOrEmpty(sectorModel.Sector))
            {
                await stepContext.Context.SendActivityAsync(ChannelFormattingService.FormatSimpleMessage(stepContext.Context, "Please select a valid sector to view weighting information"));

                return(await stepContext.EndDialogAsync(null, cancellationToken));
            }
            //if no funds were given, prompt the user to select a fund.
            else if (sectorModel.Funds.Count == 0)
            {
                var choices = Common.PIFundNames.ToList();
                choices.Insert(0, "All funds");
                var suggestedActions = SuggestedActionGenerator.GenerateSuggestedActions(choices);

                var reply = MessageFactory.Text(ChannelFormattingService.FormatSimpleMessage(stepContext.Context, $"Which fund would you like to see " +
                                                                                             $"{CultureInfo.InvariantCulture.TextInfo.ToTitleCase(sectorModel.Sector)} sector weightings for?"));
                var retryReply = MessageFactory.Text(ChannelFormattingService.FormatSimpleMessage(stepContext.Context, "Please select a valid choice"));
                reply.SuggestedActions      = suggestedActions;
                retryReply.SuggestedActions = suggestedActions;

                return(await stepContext.PromptAsync($"{nameof(FundSectorWeightsDialog)}.fundName", new PromptOptions
                {
                    Prompt = reply,
                    RetryPrompt = retryReply
                }, cancellationToken));
            }

            return(await stepContext.NextAsync(null, cancellationToken));
        }
Example #14
0
        private async Task <DialogTurnResult> GetHoldingsStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            var fundName     = ((string)stepContext.Result);
            var holdingModel = (FundHoldingsModel)stepContext.Values[FundHoldingModelValue];

            //if a fund name was selected from the previous step, save it to the model
            if (fundName != null)
            {
                fundName = fundName.Trim().ToLower();

                if (fundName == "all funds")
                {
                    holdingModel.AllFunds = true;
                }
                else if (fundName != Common.PIFundNames[Common.PIFundIndexInNamesList].ToLower())
                {
                    fundName = "PI " + fundName;
                }

                holdingModel.FundName.Add(fundName);
                stepContext.Values[FundHoldingModelValue] = holdingModel;
            }

            //get all holdings
            var allHoldings = await APIService.GetFundHoldingsAttributes(holdingModel);

            //if none were found,the company is not a holding in the requested funds.
            if (allHoldings == null)
            {
                await stepContext.Context.SendActivityAsync(ChannelFormattingService.FormatSimpleMessage(stepContext.Context, $"{CultureInfo.InvariantCulture.TextInfo.ToTitleCase(holdingModel.Company)} is not a holding in the requested fund"));

                return(await stepContext.EndDialogAsync(null, cancellationToken));
            }

            //format the holdings in a header -> subititle -> list
            var response = ChannelFormattingService.FormatHeaderSubtitleAndList(stepContext.Context,
                                                                                $"{CultureInfo.InvariantCulture.TextInfo.ToTitleCase(holdingModel.Company)}" + " holding information:", allHoldings);

            await stepContext.Context.SendActivityAsync(response, null, null, cancellationToken);

            return(await stepContext.EndDialogAsync(null, cancellationToken));
        }
Example #15
0
        private async Task <DialogTurnResult> ParseGivenParametersAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            var luisResult = (LuisResult)stepContext.Options;

            var holdingModel = new FundHoldingsModel();

            holdingModel.HoldingAttributes = new List <string>();
            holdingModel.FundName          = new List <string>();

            //get the fund names, company, and (optional) holding attributes requested
            GetEntityModelResolution(luisResult, holdingModel);

            stepContext.Values[FundHoldingModelValue] = holdingModel;

            // no company was recognized
            if (string.IsNullOrEmpty(holdingModel.Company))
            {
                await stepContext.Context.SendActivityAsync(ChannelFormattingService.FormatSimpleMessage(stepContext.Context, "Please specify a company to see holding information."));

                return(await stepContext.EndDialogAsync(null, cancellationToken));
            }
            //if no funds were selected, prompt user to select a fund or select all funds.
            else if (holdingModel.FundName.Count == 0)
            {
                var choices = Common.PIFundNames.ToList();
                choices.Insert(0, "All funds");
                var suggestedActions = SuggestedActionGenerator.GenerateSuggestedActions(choices);

                var reply      = MessageFactory.Text(ChannelFormattingService.FormatSimpleMessage(stepContext.Context, "Which fund would you like to see holding information for?"));
                var retryReply = MessageFactory.Text(ChannelFormattingService.FormatSimpleMessage(stepContext.Context, "Please select a valid choice"));
                reply.SuggestedActions      = suggestedActions;
                retryReply.SuggestedActions = suggestedActions;

                return(await stepContext.PromptAsync($"{nameof(FundHoldingsDialog)}.fundName", new PromptOptions {
                    Prompt = reply,
                    RetryPrompt = retryReply
                }, cancellationToken));
            }


            return(await stepContext.NextAsync(null, cancellationToken));
        }
Example #16
0
        private async Task ProcessRecognitionError(string message, WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            ConversationData conversationData = await _botService.conversationDataAccessor.GetAsync(stepContext.Context, () => new ConversationData());

            //incremene the failed counter
            conversationData.FailedAttemptsCount += 1;
            //if the user has failed more than 3 times
            if (conversationData.FailedAttemptsCount >= 3)
            {
                await stepContext.Context.SendActivityAsync(ChannelFormattingService.FormatMessages(stepContext.Context, "Sorry, I am still unable to understand your question. Please call Shareholder Services at " +
                                                                                                    "(800) 999-3505 for further asssistance.", new List <Func <string, string> > {
                    ToHtml.MessageWrapInSpan
                }), null, null, cancellationToken);
            }
            //otherwise respond with "sorry, I do not know what you mean." (this message may be variated so it is passed as a paramter)
            else
            {
                await stepContext.Context.SendActivityAsync(message, cancellationToken : cancellationToken);
            }
        }
Example #17
0
        private async Task <DialogTurnResult> ParseGivenParametersAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            var luisResult       = (LuisResult)stepContext.Options;
            var attributionModel = new FundAttributionModel();

            //find all entities found by LUIS and save it to the model
            GetEntityResolutions(luisResult, attributionModel);

            //if the user requested "any fund"
            if (attributionModel.FundName == "any fund")
            {
                attributionModel.FundName = null;
                await stepContext.Context.SendActivityAsync(ChannelFormattingService.FormatSimpleMessage(stepContext.Context, "You must select a fund to view top contributors/detractors"));

                return(await stepContext.EndDialogAsync(null, cancellationToken));
            }

            stepContext.Values[FundAttributionModelValue] = attributionModel;

            //if no fund name is given, we need to ask the user
            if (string.IsNullOrEmpty(attributionModel.FundName))
            {
                var choices          = Common.PIFundNames.ToList();
                var suggestedActions = SuggestedActionGenerator.GenerateSuggestedActions(choices);

                var placeholder = attributionModel.ContributorsOrDetractors == 0 ? "top contributors" : "top detractors";

                var reply      = MessageFactory.Text(ChannelFormattingService.FormatSimpleMessage(stepContext.Context, $"Which fund would you like to see {placeholder} for?"));
                var retryReply = MessageFactory.Text(ChannelFormattingService.FormatSimpleMessage(stepContext.Context, "Please select a valid choice"));
                reply.SuggestedActions      = suggestedActions;
                retryReply.SuggestedActions = suggestedActions;

                return(await stepContext.PromptAsync($"{nameof(FundAttributionDialog)}.fundName", new PromptOptions
                {
                    Prompt = reply,
                    RetryPrompt = retryReply
                }, cancellationToken));
            }

            return(await stepContext.NextAsync(null, cancellationToken));
        }
        private async Task <DialogTurnResult> ParseGivenParametersAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            //get luisResult containing all entities and the attribute model from the options sent from the parent dialog
            LuisResult          luisResult = (LuisResult)((List <object>)stepContext.Options)[0];
            FundAttributesModel attrModel  = (FundAttributesModel)((List <object>)stepContext.Options)[1];

            attrModel.FundBasicAttributes = new List <string>();

            // store recognized entities into the Attribute model
            ParseEntities(attrModel, luisResult);

            //get fund basic attributes requested
            Dictionary <string, Dictionary <string, string> > basicAttributes = await APIService.GetFundBasicsAttributes(attrModel);

            //send response in a subtitle > list format
            var message = ChannelFormattingService.FormatSubtitleAndList(stepContext.Context, basicAttributes);

            await stepContext.Context.SendActivityAsync(message, null, null, cancellationToken);

            return(await stepContext.EndDialogAsync(true, cancellationToken));
        }
Example #19
0
        private async Task <DialogTurnResult> DispatchIraAccountDocumentAsync(WaterfallStepContext stepContext, AccountDocumentModel documentModel, CancellationToken cancellationToken)
        {
            //if no document was specified for the account, lead the user to the general documents page
            if (string.IsNullOrEmpty(documentModel.AccountDocument))
            {
                var link = ChannelFormattingService.FormatLinkMessageAndSaveToState(stepContext.Context, Common.PIIraDocumentUrl, _botService, cancellationToken);

                await stepContext.Context.SendActivityAsync(ChannelFormattingService.FormatSimpleMessage(stepContext.Context, $"You can view all Ira-Related Account documents by clicking {link}. " +
                                                                                                         $"You can also ask for a specific document in the same fashion and I can direct you right to it."));
            }
            //if document was originally specified
            else
            {
                //get the URL and respond.
                var link = ChannelFormattingService.FormatLinkMessageAndSaveToState(stepContext.Context, APIService.GetAccountDocumentURL(documentModel), _botService, cancellationToken);

                await stepContext.Context.SendActivityAsync(ChannelFormattingService.FormatSimpleMessage(stepContext.Context, $"You can view the IRA {documentModel.AccountDocument} document by clicking {link}"));
            }

            return(await stepContext.NextAsync(null, cancellationToken));
        }
Example #20
0
        private async Task <DialogTurnResult> ProcessLuisIntentAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken, LuisResult luisResult)
        {
            UserProfile profile = await _botService.profileStateAccessor.GetAsync(stepContext.Context, () => new UserProfile());

            ConversationData conversationData = await _botService.conversationDataAccessor.GetAsync(stepContext.Context, () => new ConversationData());

            //get the connected service result from the LUIS Main App. This will contain all the specific entities and intents.
            var intent = luisResult.ConnectedServiceResult.TopScoringIntent.Intent.Trim();

            //if user is sending a greeting, greet back
            if (intent == /* [redacted] */)
            {
                await stepContext.Context.SendActivityAsync(ChannelFormattingService.FormatMessages(stepContext.Context, $"Hello, how can I help you today?",
                                                                                                    new List <Func <string, string> > {
                    ToHtml.MessageWrapInSpan
                }));

                conversationData.FailedAttemptsCount = 0;
                return(await stepContext.NextAsync(null, cancellationToken));

                //if the user has a specific intent, find the dialog ID associated with the intent in the Dictionary and begin
            }
            else if (IntentToDialogId.ContainsKey(intent))
            {
                conversationData.FailedAttemptsCount = 0;
                return(await stepContext.BeginDialogAsync(IntentToDialogId[intent], luisResult, cancellationToken));

                //otherwise...
            }
            else
            {
                await ProcessRecognitionError(ChannelFormattingService.FormatMessages(stepContext.Context, "Sorry, I do not know what you mean.",
                                                                                      new List <Func <string, string> > {
                    ToHtml.MessageWrapInSpan
                }), stepContext, cancellationToken);

                return(await stepContext.NextAsync(null, cancellationToken));
            }
        }
Example #21
0
        private async Task <DialogTurnResult> DispatchNonIraAccountDocumentAsync(WaterfallStepContext stepContext, AccountDocumentModel documentModel, CancellationToken cancellationToken)
        {
            var sendGeneralUrl = false;

            //if the user requested a Standard acct transfer form.
            if (Common.IraOnlyDocumentTypes.Contains(documentModel.AccountDocument))
            {
                await stepContext.Context.SendActivityAsync(ChannelFormattingService.FormatSimpleMessage(stepContext.Context,
                                                                                                         $"Sorry, there is currently not a {documentModel.AccountDocument} document/form for Standard accounts."));

                sendGeneralUrl = true;
            }
            //the user requested a specific document, find the URL and respond.
            else if (!string.IsNullOrEmpty(documentModel.AccountDocument))
            {
                var link = ChannelFormattingService.FormatLinkMessageAndSaveToState(stepContext.Context, APIService.GetAccountDocumentURL(documentModel), _botService, cancellationToken);

                await stepContext.Context.SendActivityAsync(ChannelFormattingService.FormatSimpleMessage(stepContext.Context, $"You can view the Standard {documentModel.AccountDocument} " +
                                                                                                         $"document by clicking {link}"));
            }
            else
            {
                sendGeneralUrl = true;
            }

            //if the user did not specify a document, or the user requested a standard acct. transfer form, lead them to the general document page.
            if (sendGeneralUrl)
            {
                var link = ChannelFormattingService.FormatLinkMessageAndSaveToState(stepContext.Context, Common.PINonIraDocumentUrl, _botService, cancellationToken);

                await stepContext.Context.SendActivityAsync(ChannelFormattingService.FormatSimpleMessage(stepContext.Context, $"You can view all Standard Account related documents by clicking {link}. " +
                                                                                                         $"You can also ask for a specific document in the same fashion and I can direct you right to it."));
            }

            return(await stepContext.NextAsync(null, cancellationToken));
        }
Example #22
0
        private async Task <DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            var conversationData = await _botService.conversationDataAccessor.GetAsync(stepContext.Context, () => new ConversationData());

            conversationData.IsCancellable = true;

            //this condition is satisfied when the user sends "cancel" or "help"
            if (conversationData.DidCancel)
            {
                await stepContext.Context.SendActivityAsync(ChannelFormattingService.FormatMessages(stepContext.Context, "Please enter something I can help you with!",
                                                                                                    new List <Func <string, string> > {
                    ToHtml.MessageWrapInSpan
                }));

                conversationData.DidCancel = false;

                await _botService.conversationDataAccessor.SetAsync(stepContext.Context, conversationData);

                await _botService.ConversationState.SaveChangesAsync(stepContext.Context, false, cancellationToken);
            }


            return(await stepContext.EndDialogAsync(null, cancellationToken));
        }
Example #23
0
        private async Task <DialogTurnResult> ParseAllGivenInformationStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            var luisResult = (LuisResult)stepContext.Options;

            var documentModel = new AccountDocumentModel
            {
                //get ira or standard account type
                AccountType = GetEntityModelResolution(luisResult, AccountTypeEntity),
                //get account document
                AccountDocument = GetEntityModelResolution(luisResult, DocumentTypeEntity)
            };


            documentModel.SetIsIra(); //determines if account is an IRA acct or Standard

            //if any account type was specified.
            if (documentModel.IsIra.HasValue)
            {
                stepContext.Values[DocumentModelValue] = documentModel;

                //the account requested is an IRA
                if (documentModel.IsIra == 1)
                {
                    return(await DispatchIraAccountDocumentAsync(stepContext, documentModel, cancellationToken));
                }

                //account is standard
                return(await DispatchNonIraAccountDocumentAsync(stepContext, documentModel, cancellationToken));
            }
            else
            {
                //no account has been supplied

                //if the document specified is an IRA transfer form, but the user did not specify IRA
                if (Common.IraOnlyDocumentTypes.Contains(documentModel.AccountDocument))
                {
                    documentModel.AccountType = "ira";
                    stepContext.Values[DocumentModelValue] = documentModel;
                    return(await DispatchIraAccountDocumentAsync(stepContext, documentModel, cancellationToken));
                }

                //otherwise, prompt for which account type.
                var msg = "Which type of account would you like to see ";
                if (string.IsNullOrEmpty(documentModel.AccountDocument))
                {
                    msg += " documents for?";
                }
                else
                {
                    msg += $" the {documentModel.AccountDocument} application/form for?";
                }

                msg = ChannelFormattingService.FormatSimpleMessage(stepContext.Context, msg);

                stepContext.Values[DocumentModelValue] = documentModel;

                return(await stepContext.PromptAsync($"{nameof(AccountDocumentsDialog)}.accountType", new PromptOptions
                {
                    Prompt = MessageFactory.Text(msg),
                    RetryPrompt = MessageFactory.Text(ChannelFormattingService.FormatSimpleMessage(stepContext.Context, "Please select a valid account type")),
                    Choices = ChoiceFactory.ToChoices(AccountTypeChoices)
                }, cancellationToken));
            }
        }