Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,14 @@ public ComponentBuilderV2 WithComponents(IEnumerable<IMessageComponentBuilder> c
/// <inheritdoc cref="IMessageComponentBuilder.Build" />
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -321,4 +321,43 @@ public static BuilderT WithActionRow<BuilderT>(this BuilderT container,
=> container.WithActionRow(new ActionRowBuilder()
.WithComponents(components)
.WithId(id));

/// <summary>
/// Finds the first <see cref="IMessageComponentBuilder"/> in the <see cref="IComponentContainer"/>
/// or any of its child <see cref="IComponentContainer"/>s with matching id.
/// </summary>
/// <returns>
/// The <see cref="IMessageComponentBuilder"/> with matching id, <see langword="null"/> otherwise.
/// </returns>
public static IMessageComponentBuilder FindComponentById(this IComponentContainer container, int id)
=> container.FindComponentById<IMessageComponentBuilder>(id);

/// <summary>
/// Finds the first <c>ComponentT</c> in the <see cref="IComponentContainer"/>
/// or any of its child <see cref="IComponentContainer"/>s with matching id.
/// </summary>
/// <returns>
/// The <c>ComponentT</c> with matching id, <see langword="null"/> otherwise.
/// </returns>
public static ComponentT FindComponentById<ComponentT>(this IComponentContainer container, int id)
where ComponentT : class, IMessageComponentBuilder
=> container.Components
.OfType<ComponentT>()
.FirstOrDefault(x => x.Id == id)
?? container.Components
.OfType<IComponentContainer>()
.Select(x => x.FindComponentById<ComponentT>(id))
.FirstOrDefault(x => x is not null);

/// <summary>
/// Gets a <see cref="IEnumerable{T}">IEnumerable</see> containing ids of <see cref="IMessageComponentBuilder"/>
/// in this <see cref="IComponentContainer"/> and all child <see cref="IComponentContainer"/>s.
/// </summary>
public static IEnumerable<int> GetComponentIds(this IComponentContainer container)
=> container.Components
.Where(x => x.Id is not null)
.Select(x => x.Id.Value)
.Concat(container.Components
.OfType<IComponentContainer>()
.SelectMany(x => x.GetComponentIds()));
}