Beispiel #1
0
        // ----------------
        // UPDATING METHODS
        // ----------------

        public async Task SaveActivitiesAsync(ITurnContext turnContext, IEnumerable <Activity> activities, CancellationToken cancellationToken = default)
        {
            BotAssert.ContextNotNull(turnContext);

            if (activities is null)
            {
                throw new ArgumentNullException(nameof(activities));
            }

            var state = await GetStateAsync(turnContext, cancellationToken).ConfigureAwait(false);

            foreach (var activity in activities)
            {
                if (activity.Id != null)
                {
                    // Remove any activities with a matching ID so that a duplicate isn't saved when updating
                    await UnsaveActivityAsync(turnContext, activity.Id, cancellationToken).ConfigureAwait(false);
                }

                if (CardTree.GetIds(activity, TreeNodeType.Activity).Any())
                {
                    state.SavedActivities.Add(activity);
                }
            }

            await StateAccessor.SetAsync(turnContext, state, cancellationToken).ConfigureAwait(false);
        }
Beispiel #2
0
        private async Task UpdateActivityAsync(ITurnContext turnContext, Activity activity, CancellationToken cancellationToken)
        {
            var ignoreUpdate = turnContext.TurnState.Get <CardManagerTurnState>()?.MiddlewareIgnoreUpdate;

            ignoreUpdate?.Add(activity);

            try
            {
                await turnContext.UpdateActivityAsync(activity, cancellationToken).ConfigureAwait(false);
            }
            catch
            {
                // TODO: Find out what exceptions I need to handle
                throw;
            }
            finally
            {
                ignoreUpdate?.Remove(activity);
            }

            if (!CardTree.GetIds(activity, TreeNodeType.Activity).Any())
            {
                await RemoveActivityAsync(turnContext, activity, cancellationToken).ConfigureAwait(false);
            }
        }
        public static void AdaptOutgoingCardActions(this IEnumerable <IMessageActivity> activities, string channelId = null)
        {
            if (activities is null)
            {
                throw new ArgumentNullException(nameof(activities));
            }

            foreach (var activity in activities)
            {
                var activityChannelId = channelId ?? activity.ChannelId;

                CardTree.Recurse(
                    activity,
                    (CardAction action) =>
                {
                    var text  = action.Text;
                    var value = action.Value;

                    void EnsureText()
                    {
                        if (text == null)
                        {
                            action.Text = value.SerializeIfNeeded();
                        }
                    }

                    void EnsureValue()
                    {
                        if (value == null)
                        {
                            action.Value = text;
                        }
                    }

                    void EnsureStringValue()
                    {
                        if (!(value is string))
                        {
                            if (value == null && text != null)
                            {
                                action.Value = text;
                            }
                            else
                            {
                                action.Value = value.SerializeIfNeeded();
                            }
                        }
                    }

                    void EnsureObjectValue()
                    {
                        // Check if value is null or otherwise primitive
                        if (value.ToJObject() is null)
                        {
                            if (value is string stringValue && stringValue.TryParseJObject() is JObject parsedValue)
                            {
                                action.Value = parsedValue;
                            }
        public static ISet <DataId> GetIdsFromBatch(this IEnumerable <IMessageActivity> activities)
        {
            if (activities is null)
            {
                throw new ArgumentNullException(nameof(activities));
            }

            return(CardTree.GetIds(activities, TreeNodeType.Batch));
        }
Beispiel #5
0
    private CardDecision getCardDecision(RpsAgent otherPlayer)
    {
        List <Card> otherMoves = otherPlayer.GetMoveSequence();
        CardTree    cardTree   = getCardTree(otherMoves);

        // Look up the most likely move based on N-gram tree
        // Tries to do N-gram first, if not enough trials, decreases N and tries again (until N == 1)
        for (int nGramN = nGramAmount; nGramAmount >= 1; nGramAmount--)
        {
            Queue <Card> lastNMoves = new Queue <Card>();
            for (int i = Mathf.Max(otherMoves.Count - nGramN, 0); i < otherMoves.Count; i++)
            {
                lastNMoves.Enqueue(otherMoves[i]);
            }

            CardTree cardTreeNode = cardTree;
            while (lastNMoves.Count > 0)
            {
                cardTreeNode = cardTreeNode.Traverse(lastNMoves.Dequeue());
            }

            if (cardTreeNode.totalCount < minNGramSamplesNeeded)
            {
                continue;
            }

            Card likelyCard = cardTreeNode.MostLikelyCard();
            Dictionary <Card, int> opponentCardCounts = otherPlayer.getCardCounts();

            if (likelyCard == Card.Rock)
            {
                if (opponentCardCounts[Card.Paper] == 0 && opponentCardCounts[Card.Scissors] == 0)
                {
                    return(CardDecision.DefiniteRock);
                }
                return(CardDecision.ProbableRock);
            }
            else if (likelyCard == Card.Paper)
            {
                if (opponentCardCounts[Card.Scissors] == 0 && opponentCardCounts[Card.Rock] == 0)
                {
                    return(CardDecision.DefinitePaper);
                }
                return(CardDecision.ProbablePaper);
            }
            else if (likelyCard == Card.Scissors)
            {
                if (opponentCardCounts[Card.Rock] == 0 && opponentCardCounts[Card.Paper] == 0)
                {
                    return(CardDecision.DefiniteScissors);
                }
                return(CardDecision.ProbableScissors);
            }
        }
        return(CardDecision.Random);
    }
        private static T Set <T>(
            T entryValue,
            string behaviorName,
            object behaviorValue,
            TreeNodeType entryType)
            where T : class
        {
            if (behaviorName is null)
            {
                throw new ArgumentNullException(nameof(behaviorName));
            }

            var jToken = behaviorValue is null ? null : JToken.FromObject(behaviorValue);

            return(CardTree.SetLibraryData(entryValue, new JObject {
                { behaviorName, jToken }
            }, entryType, true));
        }
        /// <summary>
        /// This will convert Adaptive Cards to JObject instances to work around this issue:
        /// https://github.com/microsoft/AdaptiveCards/issues/2148.
        /// </summary>
        /// <param name="activities">A batch of activities.</param>
        public static void ConvertAdaptiveCards(this IEnumerable <IMessageActivity> activities)
        {
            if (activities is null)
            {
                throw new ArgumentNullException(nameof(activities));
            }

            CardTree.Recurse(
                activities,
                (Attachment attachment) =>
            {
                if (attachment.ContentType == ContentTypes.AdaptiveCard)
                {
                    attachment.Content = attachment.Content.ToJObject();
                }
            },
                TreeNodeType.Batch,
                TreeNodeType.Attachment);
        }
Beispiel #8
0
        public void Add(Card c)
        {
            switch (c)
            {
            case Card.Rock:
                if (rockTree == null)
                {
                    rockTree = new CardTree(1);
                }
                else
                {
                    rockTree.count++;
                }
                break;

            case Card.Paper:
                if (paperTree == null)
                {
                    paperTree = new CardTree(1);
                }
                else
                {
                    paperTree.count++;
                }
                break;

            case Card.Scissors:
                if (scissorsTree == null)
                {
                    scissorsTree = new CardTree(1);
                }
                else
                {
                    scissorsTree.count++;
                }
                break;
            }
        }
Beispiel #9
0
    private CardTree getCardTree(List <Card> otherMoves)
    {
        /* Constructs a prefix tree for looking up N-grams, since the number of possible
         * N-move sequences is 3^N
         *
         * The prefix tree actually stores all the information needed for 1 to N grams
         */

        CardTree cardTreeHead = new CardTree(-1);
        CardTree cardTreeNode;

        for (int i = 0; i < otherMoves.Count; i++)
        {
            cardTreeNode = cardTreeHead;

            for (int j = i; j < Mathf.Min(otherMoves.Count, i + nGramAmount + 1); j++)
            {
                cardTreeNode.Add(otherMoves[j]);
                cardTreeNode = cardTreeNode.Traverse(otherMoves[j]);
            }
        }

        return(cardTreeHead);
    }
Beispiel #10
0
 public static void SetInAdaptiveCard(ref object card, DataIdOptions options = null) => card = CardTree.ApplyIds(card, options, TreeNodeType.AdaptiveCard);
Beispiel #11
0
 public static void SetInActionData(ref object data, DataIdOptions options = null) => data = CardTree.ApplyIds(data, options, TreeNodeType.ActionData);
Beispiel #12
0
 public static void SetInCardAction(CardAction action, DataIdOptions options = null) => CardTree.ApplyIds(action, options, TreeNodeType.CardAction);
Beispiel #13
0
 public static void SetInSubmitAction(ref object action, DataIdOptions options = null) => action = CardTree.ApplyIds(action, options, TreeNodeType.SubmitAction);
Beispiel #14
0
 public static void SetInVideoCard(VideoCard card, DataIdOptions options = null) => CardTree.ApplyIds(card, options, TreeNodeType.VideoCard);
Beispiel #15
0
 public static void SetInThumbnailCard(ThumbnailCard card, DataIdOptions options = null) => CardTree.ApplyIds(card, options, TreeNodeType.ThumbnailCard);
Beispiel #16
0
 public static void SetInSigninCard(SigninCard card, DataIdOptions options = null) => CardTree.ApplyIds(card, options, TreeNodeType.SigninCard);
Beispiel #17
0
 public static void SetInAttachment(Attachment attachment, DataIdOptions options = null) => CardTree.ApplyIds(attachment, options, TreeNodeType.Attachment);
Beispiel #18
0
 public static void SetInCarousel(IEnumerable <Attachment> carousel, DataIdOptions options = null) => CardTree.ApplyIds(carousel, options, TreeNodeType.Carousel);
Beispiel #19
0
 public static void SetInActivity(IMessageActivity activity, DataIdOptions options = null) => CardTree.ApplyIds(activity, options, TreeNodeType.Activity);
Beispiel #20
0
 public static void SetInBatch(IEnumerable <IMessageActivity> batch, DataIdOptions options = null) => CardTree.ApplyIds(batch, options, TreeNodeType.Batch);
Beispiel #21
0
        public async Task DeleteActionSourceAsync(ITurnContext turnContext, string dataIdScope, CancellationToken cancellationToken = default)
        {
            // TODO: Provide a way to delete elements by specifying an ID that's not in the incoming action data

            BotAssert.ContextNotNull(turnContext);

            if (string.IsNullOrEmpty(dataIdScope))
            {
                throw new ArgumentNullException(nameof(dataIdScope));
            }

            var state = await GetStateAsync(turnContext, cancellationToken).ConfigureAwait(false);

            if (dataIdScope == DataIdScopes.Batch)
            {
                if (turnContext.GetIncomingActionData().ToJObject().GetIdFromActionData(DataIdScopes.Batch) is string batchId)
                {
                    var toDelete = new DataId(DataIdScopes.Batch, batchId);

                    // Iterate over a copy of the set so the original can be modified
                    foreach (var activity in state.SavedActivities.ToList())
                    {
                        // Delete any activity that contains the specified batch ID (data items are compared by value)
                        if (CardTree.GetIds(activity, TreeNodeType.Activity).Any(toDelete.Equals))
                        {
                            await DeleteActivityAsync(turnContext, activity, cancellationToken).ConfigureAwait(false);
                        }
                    }
                }
            }
            else
            {
                var matchResult = await GetDataMatchAsync(turnContext, cancellationToken).ConfigureAwait(false);

                var matchedActivity      = matchResult.SavedActivity;
                var matchedAttachment    = matchResult.SavedAttachment;
                var matchedAction        = matchResult.SavedAction;
                var shouldUpdateActivity = false;

                // TODO: Provide options for how to determine emptiness when cascading deletion
                // (e.g. when a card has no more actions rather than only when the card is completely empty)

                if (dataIdScope == DataIdScopes.Action && matchedActivity != null && matchedAttachment != null && matchedAction != null)
                {
                    if (matchedAttachment.ContentType.EqualsCI(ContentTypes.AdaptiveCard))
                    {
                        // For Adaptive Cards
                        if (matchedAction is JObject savedSubmitAction)
                        {
                            var adaptiveCard = (JObject)savedSubmitAction.Root;

                            // Remove the submit action from the Adaptive Card.
                            // SafeRemove will work whether the action is in an array
                            // or is the value of a select action property.
                            savedSubmitAction.SafeRemove();

                            matchedAttachment.Content = matchedAttachment.Content.FromJObject(adaptiveCard);
                            shouldUpdateActivity      = true;

                            // Check if the Adaptive Card is now empty
                            if (adaptiveCard.GetValue(AdaptiveProperties.Body).IsNullishOrEmpty() &&
                                adaptiveCard.GetValue(AdaptiveProperties.Actions).IsNullishOrEmpty())
                            {
                                // If the card is now empty, execute the next if block to delete it
                                dataIdScope = DataIdScopes.Card;
                            }
                        }
                    }
                    else
                    {
                        // For Bot Framework rich cards
                        if (matchedAction is CardAction cardAction)
                        {
                            // Remove the card action from the card
                            CardTree.Recurse(
                                matchedAttachment,
                                (IList <CardAction> actions) =>
                            {
                                actions.Remove(cardAction);
                            },
                                TreeNodeType.Attachment,
                                TreeNodeType.CardActionList);

                            shouldUpdateActivity = true;

                            // Check if the card is now empty.
                            // We are assuming that if a developer wants to make a rich card
                            // with only postBack/messageBack buttons then they will use a hero card
                            // and any other card would have more content than just postBack/messageBack buttons
                            // so only a hero card should potentially be empty at this point.
                            // We aren't checking if Buttons is null because it can't be at this point.
                            if (matchedAttachment.Content is HeroCard heroCard &&
                                !heroCard.Buttons.Any() &&
                                heroCard.Images?.Any() != true &&
                                string.IsNullOrWhiteSpace(heroCard.Title) &&
                                string.IsNullOrWhiteSpace(heroCard.Subtitle) &&
                                string.IsNullOrWhiteSpace(heroCard.Text))
                            {
                                // If the card is now empty, execute the next if block to delete it
                                dataIdScope = DataIdScopes.Card;
                            }
                        }
                    }
                }

                if (dataIdScope == DataIdScopes.Card && matchedActivity != null && matchedAttachment != null)
                {
                    matchedActivity.Attachments.Remove(matchedAttachment);

                    shouldUpdateActivity = true;

                    // Check if the activity is now empty
                    if (string.IsNullOrWhiteSpace(matchedActivity.Text) && !matchedActivity.Attachments.Any())
                    {
                        // If the activity is now empty, execute the next if block to delete it
                        dataIdScope = DataIdScopes.Carousel;
                    }
                }

                if (dataIdScope == DataIdScopes.Carousel && matchedActivity != null)
                {
                    await DeleteActivityAsync(turnContext, matchedActivity, cancellationToken).ConfigureAwait(false);
                }
                else if (shouldUpdateActivity)
                {
                    await UpdateActivityAsync(turnContext, matchedActivity, cancellationToken).ConfigureAwait(false);
                }
            }

            await StateAccessor.SetAsync(turnContext, state, cancellationToken).ConfigureAwait(false);
        }
Beispiel #22
0
 public static void SetInOAuthCard(OAuthCard card, DataIdOptions options = null) => CardTree.ApplyIds(card, options, TreeNodeType.OAuthCard);
Beispiel #23
0
        private async Task <DataMatchResult> GetDataMatchAsync(
            ITurnContext turnContext,
            CancellationToken cancellationToken = default)
        {
            var result = new DataMatchResult();

            if (!(turnContext.GetIncomingActionData() is JObject incomingData))
            {
                return(result);
            }

            var state = await GetStateAsync(turnContext, cancellationToken).ConfigureAwait(false);

            var couldBeFromAdaptiveCard = turnContext.Activity.Value.ToJObject() is JObject;

            // Iterate over all saved activities that contain any of the action data ID's from the incoming action data
            foreach (var savedActivity in state.SavedActivities
                     .Where(activity => activity?.Attachments?.Any() == true))
            {
                foreach (var savedAttachment in savedActivity.Attachments.WhereNotNull())
                {
                    if (savedAttachment.ContentType.EqualsCI(ContentTypes.AdaptiveCard))
                    {
                        if (couldBeFromAdaptiveCard && savedAttachment.Content.ToJObject() is JObject savedAdaptiveCard)
                        {
                            // For Adaptive Cards we need to check the inputs
                            var inputsMatch            = true;
                            var dataWithoutInputValues = incomingData.DeepClone() as JObject;

                            // First, determine what matching submit action data is expected to look like
                            // by taking the incoming data and removing the values associated with
                            // the inputs found in the Adaptive Card
                            foreach (var inputId in AdaptiveCardUtil.GetAdaptiveInputs(savedAdaptiveCard)
                                     .Select(AdaptiveCardUtil.GetAdaptiveInputId))
                            {
                                // If the Adaptive Card is poorly designed,
                                // the same input ID might show up multiple times.
                                // Therefore we're checking if the original incoming data
                                // contained the ID, because the inputs might still
                                // match even if this input was already removed.
                                if (incomingData.ContainsKey(inputId))
                                {
                                    // Removing a property that doesn't exist
                                    // will not throw an exception
                                    dataWithoutInputValues.Remove(inputId);
                                }
                                else
                                {
                                    inputsMatch = false;

                                    break;
                                }
                            }

                            // Second, if all the input ID's found in the card were present in the incoming data
                            // then check each submit action in the card to see if its data matches the incoming data
                            if (inputsMatch)
                            {
                                CardTree.Recurse(
                                    savedAdaptiveCard,
                                    (JObject savedSubmitAction) =>
                                {
                                    var submitActionData = savedSubmitAction.GetValue(
                                        AdaptiveProperties.Data) ?? new JObject();

                                    if (JToken.DeepEquals(submitActionData, dataWithoutInputValues))
                                    {
                                        result.Add(savedActivity, savedAttachment, savedSubmitAction);
                                    }
                                },
                                    TreeNodeType.AdaptiveCard,
                                    TreeNodeType.SubmitAction);
                            }
                        }
                    }
                    else
                    {
                        // For Bot Framework cards that are not Adaptive Cards
                        CardTree.Recurse(
                            savedAttachment,
                            (CardAction savedCardAction) =>
                        {
                            var savedData = savedCardAction.Value.ToJObject(true);

                            // This will not throw an exception if the saved action data is null
                            if (JToken.DeepEquals(savedData, incomingData))
                            {
                                result.Add(savedActivity, savedAttachment, savedCardAction);
                            }
                        },
                            TreeNodeType.Attachment,
                            TreeNodeType.CardAction);
                    }
                }
            }

            return(result);
        }
Beispiel #24
0
 public static void SetInReceiptCard(ReceiptCard card, DataIdOptions options = null) => CardTree.ApplyIds(card, options, TreeNodeType.ReceiptCard);