diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderV2.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderV2.cs index 71bb127e..8689d708 100644 --- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderV2.cs +++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderV2.cs @@ -53,9 +53,14 @@ public class ComponentBuilderV2 : IStaticComponentContainer /// public MessageComponent Build() { - Preconditions.AtLeast(Components?.Count ?? 0, 1, nameof(Components.Count), "At least 1 component must be added to this container."); + Preconditions.NotNull(Components, nameof(Components)); + Preconditions.AtLeast(Components.Count, 1, nameof(Components.Count), "At least 1 component must be added to this container."); Preconditions.AtMost(this.ComponentCount(), MaxComponents, nameof(Components.Count), $"A message must contain {MaxComponents} components or less."); + var ids = this.GetComponentIds().ToList(); + if (ids.Count != ids.Distinct().Count()) + throw new InvalidOperationException("Components must have unique ids."); + if (_components.Any(x => x is not ActionRowBuilder and not SectionBuilder diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentContainerExtensions.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentContainerExtensions.cs index 4e31e161..318367a5 100644 --- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentContainerExtensions.cs +++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentContainerExtensions.cs @@ -321,4 +321,43 @@ public static class ComponentContainerExtensions => container.WithActionRow(new ActionRowBuilder() .WithComponents(components) .WithId(id)); + + /// + /// Finds the first in the + /// or any of its child s with matching id. + /// + /// + /// The with matching id, otherwise. + /// + public static IMessageComponentBuilder FindComponentById(this IComponentContainer container, int id) + => container.FindComponentById(id); + + /// + /// Finds the first ComponentT in the + /// or any of its child s with matching id. + /// + /// + /// The ComponentT with matching id, otherwise. + /// + public static ComponentT FindComponentById(this IComponentContainer container, int id) + where ComponentT : class, IMessageComponentBuilder + => container.Components + .OfType() + .FirstOrDefault(x => x.Id == id) + ?? container.Components + .OfType() + .Select(x => x.FindComponentById(id)) + .FirstOrDefault(x => x is not null); + + /// + /// Gets a IEnumerable containing ids of + /// in this and all child s. + /// + public static IEnumerable GetComponentIds(this IComponentContainer container) + => container.Components + .Where(x => x.Id is not null) + .Select(x => x.Id.Value) + .Concat(container.Components + .OfType() + .SelectMany(x => x.GetComponentIds())); }