コード例 #1
0
ファイル: CafeBot.cs プロジェクト: nim1290/botiumtest
        private async Task <OnTurnProperty> DetectIntentAndEntitiesAsync(ITurnContext turnContext)
        {
            // Handle card input (if any), update state and return.
            if (turnContext.Activity.Value != null)
            {
                var response = JsonConvert.DeserializeObject <Dictionary <string, string> >(turnContext.Activity.Value as string);
                return(OnTurnProperty.FromCardInput(response));
            }

            // Acknowledge attachments from user.
            if (turnContext.Activity.Attachments != null && turnContext.Activity.Attachments.Count > 0)
            {
                await turnContext.SendActivityAsync("Thanks for sending me that attachment. I'm still learning to process attachments.");

                return(null);
            }

            // Nothing to do for this turn if there is no text specified.
            if (string.IsNullOrWhiteSpace(turnContext.Activity.Text) || string.IsNullOrWhiteSpace(turnContext.Activity.Text.Trim()))
            {
                return(null);
            }

            // Call to LUIS recognizer to get intent + entities.
            var luisResults = await _services.LuisServices[LuisConfiguration].RecognizeAsync(turnContext, default(CancellationToken));

            // Return new instance of on turn property from LUIS results.
            // Leverages static fromLUISResults method.
            return(OnTurnProperty.FromLuisResults(luisResults));
        }
コード例 #2
0
        public static OnTurnProperty FromLuisResults(RecognizerResult luisResults)
        {
            var onTurnProperties = new OnTurnProperty();

            onTurnProperties.Intent = luisResults.GetTopScoringIntent().intent;

            // Gather entity values if available. Uses a const list of LUIS entity names.
            foreach (var entity in luisEntities)
            {
                dynamic value  = luisResults.Entities[entity];
                string  strVal = null;
                if (value is JArray)
                {
                    // ConfirmList is nested arrays.
                    value = (from val in (JArray)value
                             select val).FirstOrDefault();
                }

                strVal = (string)value;

                if (strVal == null)
                {
                    // Don't add empty entities.
                    continue;
                }

                onTurnProperties.Entities.Add(new EntityProperty(entity, strVal));
            }

            return(onTurnProperties);
        }
コード例 #3
0
        /// <summary>
        /// Static method to create an on turn property object from card input.
        /// </summary>
        /// <param name="cardValue">context.activity.value from a card interaction</param>
        /// <returns>OnTurnProperty.</returns>
        public static OnTurnProperty FromCardInput(Dictionary <string, string> cardValues)
        {
            // All cards used by this bot are adaptive cards with the card's 'data' property set to useful information.
            var onTurnProperties = new OnTurnProperty();

            foreach (KeyValuePair <string, string> entry in cardValues)
            {
                if (!string.IsNullOrWhiteSpace(entry.Key) && string.Compare(entry.Key.ToLower().Trim(), "intent") == 0)
                {
                    onTurnProperties.Intent = cardValues[entry.Key];
                }
                else
                {
                    onTurnProperties.Entities.Add(new EntityProperty(entry.Key, cardValues[entry.Key]));
                }
            }

            return(onTurnProperties);
        }
コード例 #4
0
        public static OnTurnProperty FromLuisResults(RecognizerResult luisResults)
        {
            var onTurnProperties = new OnTurnProperty();

            onTurnProperties.Intent = luisResults.GetTopScoringIntent().intent;

            // Gather entity values if available. Uses a const list of LUIS entity names.
            foreach (var entity in luisEntities)
            {
                var value = luisResults.Entities.SelectTokens(entity).FirstOrDefault();
                if (value == null)
                {
                    continue;
                }

                onTurnProperties.Entities.Add(new EntityProperty(entity, value));
            }

            return(onTurnProperties);
        }
        public static OnTurnProperty FromLuisResults(RecognizerResult luisResults)
        {
            var onTurnProperties = new OnTurnProperty();

            onTurnProperties.Intent = luisResults.GetTopScoringIntent().intent;

            // Gather entity values if available. Uses a const list of LUIS entity names.
            foreach (var entity in luisEntities)
            {
                var value = luisResults.Entities.SelectTokens(entity).FirstOrDefault();
                if (value == null)
                {
                    continue;
                }

                object property = null;
                var    val      = value.First();
                if (val.Type == JTokenType.Array)
                {
                    var arr = (JArray)val;
                    property = arr[0].ToString(); // Store first value
                }
                else if (val.Type == JTokenType.Object)
                {
                    var obj = (JObject)val;
                    if (obj["type"].ToString() == "datetime")
                    {
                        property = val;  // Store the JToken from LUIS (includes Timex)
                    }
                }
                else if (val.Type == JTokenType.Integer)
                {
                    var num = (JValue)val;
                    property = val.ToString();  // Store string for number of guests
                }

                onTurnProperties.Entities.Add(new EntityProperty(entity, property));
            }

            return(onTurnProperties);
        }
        /// <summary>
        /// Static method to create an on turn property object from card input.
        /// </summary>
        /// <param name="cardValues">context.activity.value from a card interaction</param>
        /// <returns>OnTurnProperty.</returns>
        public static OnTurnProperty FromCardInput(JObject cardValues)
        {
            // All cards used by this bot are adaptive cards with the card's 'data' property set to useful information.
            var onTurnProperties = new OnTurnProperty();

            foreach (var val in cardValues)
            {
                string name  = val.Key;
                JToken value = val.Value;
                if (!string.IsNullOrWhiteSpace(name) && string.Compare(name.ToLower().Trim(), "intent") == 0)
                {
                    onTurnProperties.Intent = value.ToString();
                }
                else
                {
                    onTurnProperties.Entities.Add(new EntityProperty(name, value.ToString()));
                }
            }

            return(onTurnProperties);
        }
コード例 #7
0
        private async Task <DialogTurnResult> BeginWhatCanYouDoDialogAsync(DialogContext innerDc, OnTurnProperty onTurnProperty)
        {
            var context = innerDc.Context;

            // Handle case when user interacted with what can you do card.
            // What can you do card sends a custom data property with intent name, text value and possible entities.
            // See ../WhatCanYouDo/Resources/whatCanYouDoCard.json for card definition.
            var queryProperty = (onTurnProperty.Entities ?? new List <EntityProperty>()).Where(item => string.Compare(item.EntityName, QueryProperty) == 0);

            if (queryProperty.Count() > 0)
            {
                Dictionary <string, string> response;
                try
                {
                    response = JsonConvert.DeserializeObject <Dictionary <string, string> >(queryProperty.ElementAtOrDefault(0).Value as string);
                }
                catch
                {
                    await context.SendActivityAsync("Choose a query from the card drop down before you click `Let's talk!`");

                    return(new DialogTurnResult(DialogTurnStatus.Empty, null));
                }

                if (response.TryGetValue("text", out var text))
                {
                    context.Activity.Text = text;
                    await context.SendActivityAsync($"You said: '{context.Activity.Text}'.");
                }

                // Create a set a new onturn property
                await _onTurnAccessor.SetAsync(context, OnTurnProperty.FromCardInput(response));

                return(await BeginDialogAsync(innerDc, response));
            }

            return(await innerDc.BeginDialogAsync(WhatCanYouDo.Name));
        }
コード例 #8
0
        public async override Task <DialogTurnResult> ContinueDialogAsync(DialogContext dc, CancellationToken cancellationToken = default(CancellationToken))
        {
            var turnContext = dc.Context;
            var step        = dc.ActiveDialog.State;

            // Get reservation property.
            var newReservation = await _reservationsAccessor.GetAsync(turnContext, () => new ReservationProperty());

            // Get on turn property. This has any entities that mainDispatcher,
            // or Bot might have captured in its LUIS model.
            var onTurnProperties = await _onTurnAccessor.GetAsync(turnContext, () => new OnTurnProperty("None", new List <EntityProperty>()));

            // If on turn property has entities..
            ReservationResult updateResult = null;

            if (onTurnProperties.Entities.Count > 0)
            {
                // ...update reservation property with on turn property results.
                updateResult = newReservation.UpdateProperties(onTurnProperties);
            }

            // See if updates to reservation resulted in errors, if so, report them to user.
            if (updateResult != null &&
                updateResult.Status == ReservationStatus.Incomplete &&
                updateResult.Outcome != null &&
                updateResult.Outcome.Count > 0)
            {
                await _reservationsAccessor.SetAsync(turnContext, updateResult.NewReservation);

                // Return and do not continue if there is an error.
                await turnContext.SendActivityAsync(updateResult.Outcome[0].Message);

                return(await base.ContinueDialogAsync(dc));
            }

            // Call LUIS and get results.
            var luisResults   = await _botServices.LuisServices[LuisConfiguration].RecognizeAsync(turnContext, cancellationToken);
            var topLuisIntent = luisResults.GetTopScoringIntent();
            var topIntent     = topLuisIntent.intent;

            // If we don't have an intent match from LUIS, go with the intent available via
            // the on turn property (parent's LUIS model).
            if (luisResults.Intents.Count <= 0)
            {
                // Go with intent in onTurnProperty.
                topIntent = string.IsNullOrWhiteSpace(onTurnProperties.Intent) ? "None" : onTurnProperties.Intent;
            }

            // Update object with LUIS result.
            updateResult = newReservation.UpdateProperties(OnTurnProperty.FromLuisResults(luisResults));

            // See if update reservation resulted in errors, if so, report them to user.
            if (updateResult != null &&
                updateResult.Status == ReservationStatus.Incomplete &&
                updateResult.Outcome != null &&
                updateResult.Outcome.Count > 0)
            {
                // Set reservation property.
                await _reservationsAccessor.SetAsync(turnContext, updateResult.NewReservation);

                // Return and do not continue if there is an error.
                await turnContext.SendActivityAsync(updateResult.Outcome[0].Message);

                return(await base.ContinueDialogAsync(dc));
            }

            // Did user ask for help or said cancel or continuing the conversation?
            switch (topIntent)
            {
            case ContinuePromptIntent:
                // User does not want to make any change.
                updateResult.NewReservation.NeedsChange = false;
                break;

            case NoChangeIntent:
                // User does not want to make any change.
                updateResult.NewReservation.NeedsChange = false;
                break;

            case HelpIntent:
                // Come back with contextual help.
                var helpReadOut = updateResult.NewReservation.HelpReadOut();
                await turnContext.SendActivityAsync(helpReadOut);

                break;

            case CancelIntent:
                // Start confirmation prompt.
                var opts = new PromptOptions
                {
                    Prompt = new Activity
                    {
                        Type = ActivityTypes.Message,
                        Text = "Are you sure you want to cancel?",
                    },
                };

                return(await dc.PromptAsync(ConfirmCancelPrompt, opts));

            case InterruptionsIntent:
            default:
                // If we picked up new entity values, do not treat this as an interruption.
                if (onTurnProperties.Entities.Count != 0 || luisResults.Entities.Count > 1)
                {
                    break;
                }

                // Handle interruption.
                var onTurnProperty = await _onTurnAccessor.GetAsync(dc.Context);

                return(await dc.BeginDialogAsync(InterruptionDispatcher, onTurnProperty));
            }

            // Set reservation property based on OnTurn properties.
            await _reservationsAccessor.SetAsync(turnContext, updateResult.NewReservation);

            return(await base.ContinueDialogAsync(dc));
        }
コード例 #9
0
        protected async Task <DialogTurnResult> BeginChildDialogAsync(DialogContext dc, OnTurnProperty onTurnProperty)
        {
            switch (onTurnProperty.Intent)
            {
            // Help, ChitChat and QnA share the same QnA Maker model. So just call the QnA Dialog.
            case QnADialog.Name:
            case ChitChatDialog.Name:
            case "Help":
                return(await dc.BeginDialogAsync(QnADialog.Name));

            case BookTableDialog.Name:
                return(await dc.BeginDialogAsync(BookTableDialog.Name));

            case WhoAreYouDialog.Name:
                return(await dc.BeginDialogAsync(WhoAreYouDialog.Name));

            case WhatCanYouDo.Name:
                return(await dc.BeginDialogAsync(WhatCanYouDo.Name));

            case "None":
            default:
                await dc.Context.SendActivityAsync("I'm still learning.. Sorry, I do not know how to help you with that.");

                await dc.Context.SendActivityAsync($"Follow[this link](https://www.bing.com/search?q={dc.Context.Activity.Text}) to search the web!");

                return(new DialogTurnResult(DialogTurnStatus.Empty));
            }
        }
        public ReservationResult UpdateProperties(OnTurnProperty onTurnProperty)
        {
            var returnResult = new ReservationResult(this);

            return(Validate(onTurnProperty, returnResult));
        }
        public static ReservationResult Validate(OnTurnProperty onTurnProperty, ReservationResult returnResult)
        {
            if (onTurnProperty == null || onTurnProperty.Entities.Count == 0)
            {
                return(returnResult);
            }

            // We only will pull number -> party size, datetimeV2 -> date and time, cafeLocation -> location.
            var numberEntity       = onTurnProperty.Entities.Find(item => item.EntityName.Equals(PartySizeEntity));
            var dateTimeEntity     = onTurnProperty.Entities.Find(item => item.EntityName.Equals(DateTimeEntity));
            var locationEntity     = onTurnProperty.Entities.Find(item => item.EntityName.Equals(LocationEntity));
            var confirmationEntity = onTurnProperty.Entities.Find(item => item.EntityName.Equals(ConfirmationEntity));

            if (numberEntity != null)
            {
                // We only accept MaxPartySize in a reservation.
                var partySize = int.Parse(numberEntity.Value as string);
                if (partySize > MaxPartySize)
                {
                    returnResult.Outcome.Add(new ReservationOutcome($"Sorry. {int.Parse(numberEntity.Value as string)} does not work. I can only accept up to 10 guests in a reservation.", PartySizeEntity));
                    returnResult.Status = ReservationStatus.Incomplete;
                }
                else
                {
                    returnResult.NewReservation.PartySize = partySize;
                }
            }

            if (dateTimeEntity != null)
            {
                // Get parsed date time from TIMEX
                // LUIS returns a timex expression and so get and un-wrap that.
                // Take the first date time since book table scenario does not have to deal with multiple date times or date time ranges.
                var timexProp = ((JToken)dateTimeEntity.Value)?["timex"]?[0]?.ToString();
                if (timexProp != null)
                {
                    var today       = DateTime.Now;
                    var parsedTimex = new TimexProperty(timexProp);

                    // Validate the date (and check constraints (later))
                    if (parsedTimex.DayOfMonth != null && parsedTimex.Year != null && parsedTimex.Month != null)
                    {
                        var date = DateTimeOffset.Parse($"{parsedTimex.Year}-{parsedTimex.Month}-{parsedTimex.DayOfMonth}");
                        returnResult.NewReservation.Date         = date.UtcDateTime.ToString("o").Split("T")[0];
                        returnResult.NewReservation.DateLGString = new TimexProperty(returnResult.NewReservation.Date).ToNaturalLanguage(today);
                    }

                    // See if the time meets our constraints.
                    if (parsedTimex.Hour != null &&
                        parsedTimex.Minute != null &&
                        parsedTimex.Second != null)
                    {
                        var timexOptions = ((JToken)dateTimeEntity.Value)?["timex"]?.ToObject <List <string> >();

                        var validtime = TimexRangeResolver.Evaluate(timexOptions, reservationTimeConstraints);

                        returnResult.NewReservation.Time  = ((int)parsedTimex.Hour).ToString("D2");
                        returnResult.NewReservation.Time += ":";
                        returnResult.NewReservation.Time += ((int)parsedTimex.Minute).ToString("D2");
                        returnResult.NewReservation.Time += ":";
                        returnResult.NewReservation.Time += ((int)parsedTimex.Second).ToString("D2");

                        if (validtime == null || (validtime.Count == 0))
                        {
                            // Validation failed!
                            returnResult.Outcome.Add(new ReservationOutcome("Sorry, that time does not work. I can only make reservations that are in the daytime (6AM - 6PM)", DateTimeEntity));
                            returnResult.NewReservation.Time = string.Empty;
                            returnResult.Status = ReservationStatus.Incomplete;
                        }
                    }

                    // Get date time LG string if we have both date and time.
                    if (!string.IsNullOrWhiteSpace(returnResult.NewReservation.Date) && !string.IsNullOrWhiteSpace(returnResult.NewReservation.Time))
                    {
                        returnResult.NewReservation.DateTimeLGString = new TimexProperty(returnResult.NewReservation.Date + "T" + returnResult.NewReservation.Time).ToNaturalLanguage(today);
                    }
                }
            }

            // Take the first found value.
            if (locationEntity != null)
            {
                var cafeLocation = locationEntity.Value;

                // Capitalize cafe location.
                returnResult.NewReservation.Location = char.ToUpper(((string)cafeLocation)[0]) + ((string)cafeLocation).Substring(1);
            }

            // Accept confirmation entity if available only if we have a complete reservation
            if (confirmationEntity != null)
            {
                var value = confirmationEntity.Value as string;
                if (value != null && value == "yes")
                {
                    returnResult.NewReservation.ReservationConfirmed = true;
                    returnResult.NewReservation.NeedsChange          = false;
                }
                else
                {
                    returnResult.NewReservation.NeedsChange          = true;
                    returnResult.NewReservation.ReservationConfirmed = false;
                }
            }

            return(returnResult);
        }
        public static ReservationResult FromOnTurnProperty(OnTurnProperty onTurnProperty)
        {
            var returnResult = new ReservationResult(new ReservationProperty());

            return(Validate(onTurnProperty, returnResult));
        }
コード例 #13
0
        protected async Task <DialogTurnResult> BeginChildDialogAsync(DialogContext dc, OnTurnProperty onTurnProperty)
        {
            switch (onTurnProperty.Intent)
            {
            // Help, ChitChat and QnA share the same QnA Maker model. So just call the QnA Dialog.
            case QnADialog.Name:
            case ChitChatDialog.Name:
            case "Help":
                return(await dc.BeginDialogAsync(QnADialog.Name));

            case BookTableDialog.Name:
                return(await dc.BeginDialogAsync(BookTableDialog.Name));

            case WhoAreYouDialog.Name:
                return(await dc.BeginDialogAsync(WhoAreYouDialog.Name));

            case WhatCanYouDo.Name:
                return(await BeginWhatCanYouDoDialogAsync(dc, onTurnProperty));

            case "None":
            default:
                await dc.Context.SendActivityAsync("I'm still learning.. Sorry, I do not know how to help you with that.");

                await dc.Context.SendActivityAsync($"https://giphy.com/gifs/cat-funny-cute-11IYKJ5sN73twk");

                return(new DialogTurnResult(DialogTurnStatus.Empty));
            }
        }