Beispiel #1
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);
            }
        }
Beispiel #2
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);
        }
        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 #4
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);
        }