Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Components V2 #3065

Open
wants to merge 17 commits into
base: dev
Choose a base branch
from
Open
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
53 changes: 33 additions & 20 deletions src/Discord.Net.Core/Entities/Interactions/IDiscordInteraction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ public interface IDiscordInteraction : ISnowflakeEntity
/// A task that represents an asynchronous send operation for delivering the message.
/// </returns>
Task RespondAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null,
MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null);
MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None);

/// <summary>
/// Responds to this interaction with a file attachment.
Expand All @@ -149,7 +149,8 @@ Task RespondAsync(string text = null, Embed[] embeds = null, bool isTTS = false,
/// </returns>
#if NETCOREAPP3_0_OR_GREATER
async Task RespondWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null,
MessageFlags flags = MessageFlags.None)
{
using (var file = new FileAttachment(fileStream, fileName))
{
Expand All @@ -158,7 +159,8 @@ async Task RespondWithFileAsync(Stream fileStream, string fileName, string text
}
#else
Task RespondWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null);
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null
, MessageFlags flags = MessageFlags.None);
#endif
/// <summary>
/// Responds to this interaction with a file attachment.
Expand All @@ -180,7 +182,8 @@ Task RespondWithFileAsync(Stream fileStream, string fileName, string text = null
/// </returns>
#if NETCOREAPP3_0_OR_GREATER
async Task RespondWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null,
MessageFlags flags = MessageFlags.None)
{
using (var file = new FileAttachment(filePath, fileName))
{
Expand All @@ -189,7 +192,8 @@ async Task RespondWithFileAsync(string filePath, string fileName = null, string
}
#else
Task RespondWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null);
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null,
MessageFlags flags = MessageFlags.None);
#endif
/// <summary>
/// Responds to this interaction with a file attachment.
Expand All @@ -210,11 +214,13 @@ Task RespondWithFileAsync(string filePath, string fileName = null, string text =
/// </returns>
#if NETCOREAPP3_0_OR_GREATER
Task RespondWithFileAsync(FileAttachment attachment, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
=> RespondWithFilesAsync(new FileAttachment[] { attachment }, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll);
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null,
MessageFlags flags = MessageFlags.None)
=> RespondWithFilesAsync([attachment], text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags);
#else
Task RespondWithFileAsync(FileAttachment attachment, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null);
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null,
MessageFlags flags = MessageFlags.None);
#endif
/// <summary>
/// Responds to this interaction with a collection of file attachments.
Expand All @@ -234,7 +240,9 @@ Task RespondWithFileAsync(FileAttachment attachment, string text = null, Embed[]
/// contains the sent message.
/// </returns>
Task RespondWithFilesAsync(IEnumerable<FileAttachment> attachments, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null);
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null,
MessageFlags flags = MessageFlags.None);

/// <summary>
/// Sends a followup message for this interaction.
/// </summary>
Expand All @@ -252,7 +260,9 @@ Task RespondWithFilesAsync(IEnumerable<FileAttachment> attachments, string text
/// contains the sent message.
/// </returns>
Task<IUserMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null);
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null,
MessageFlags flags = MessageFlags.None);

/// <summary>
/// Sends a followup message for this interaction.
/// </summary>
Expand All @@ -273,17 +283,19 @@ Task<IUserMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool
/// </returns>
#if NETCOREAPP3_0_OR_GREATER
async Task<IUserMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None)
{
using (var file = new FileAttachment(fileStream, fileName))
{
return await FollowupWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll).ConfigureAwait(false);
return await FollowupWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags).ConfigureAwait(false);
}
}
#else
Task<IUserMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null);
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null,
MessageFlags flags = MessageFlags.None);
#endif

/// <summary>
/// Sends a followup message for this interaction.
/// </summary>
Expand All @@ -304,16 +316,17 @@ Task<IUserMessage> FollowupWithFileAsync(Stream fileStream, string fileName, str
/// </returns>
#if NETCOREAPP3_0_OR_GREATER
async Task<IUserMessage> FollowupWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null,
MessageFlags flags = MessageFlags.None)
{
using (var file = new FileAttachment(filePath, fileName))
{
return await FollowupWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll).ConfigureAwait(false);
return await FollowupWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags).ConfigureAwait(false);
}
}
#else
Task<IUserMessage> FollowupWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null);
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None);
#endif
/// <summary>
/// Sends a followup message for this interaction.
Expand All @@ -334,11 +347,11 @@ Task<IUserMessage> FollowupWithFileAsync(string filePath, string fileName = null
/// </returns>
#if NETCOREAPP3_0_OR_GREATER
Task<IUserMessage> FollowupWithFileAsync(FileAttachment attachment, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
=> FollowupWithFilesAsync(new FileAttachment[] { attachment }, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll);
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None)
=> FollowupWithFilesAsync(new FileAttachment[] { attachment }, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags);
#else
Task<IUserMessage> FollowupWithFileAsync(FileAttachment attachment, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null);
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None);
#endif
/// <summary>
/// Sends a followup message for this interaction.
Expand All @@ -358,7 +371,7 @@ Task<IUserMessage> FollowupWithFileAsync(FileAttachment attachment, string text
/// contains the sent message.
/// </returns>
Task<IUserMessage> FollowupWithFilesAsync(IEnumerable<FileAttachment> attachments, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null);
AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None);
/// <summary>
/// Gets the original response for this interaction.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
using System.Collections.Generic;

namespace Discord
namespace Discord;

/// <summary>
/// Represents a <see cref="IMessageComponent"/> Row for child components to live in.
/// </summary>
public class ActionRowComponent : IMessageComponent
{
/// <summary>
/// Represents a <see cref="IMessageComponent"/> Row for child components to live in.
/// </summary>
public class ActionRowComponent : IMessageComponent
{
/// <inheritdoc/>
public ComponentType Type => ComponentType.ActionRow;
/// <inheritdoc/>
public ComponentType Type => ComponentType.ActionRow;

/// <summary>
/// Gets the child components in this row.
/// </summary>
public IReadOnlyCollection<IMessageComponent> Components { get; internal set; }
/// <inheritdoc/>
public int? Id { get; internal set; }

internal ActionRowComponent() { }
/// <summary>
/// Gets the child components in this row.
/// </summary>
public IReadOnlyCollection<IMessageComponent> Components { get; internal set; }

internal ActionRowComponent(List<IMessageComponent> components)
{
Components = components;
}
internal ActionRowComponent() { }

string IMessageComponent.CustomId => null;
internal ActionRowComponent(IReadOnlyCollection<IMessageComponent> components)
{
Components = components;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ namespace Discord;
/// <summary>
/// Represents a class used to build Action rows.
/// </summary>
public class ActionRowBuilder
public class ActionRowBuilder : IMessageComponentBuilder, IInteractableComponentContainer
{
public ComponentType Type => ComponentType.ActionRow;

public int? Id { get; set; }

/// <summary>
/// The max amount of child components this row can hold.
/// </summary>
Expand All @@ -20,7 +24,7 @@ public class ActionRowBuilder
/// </summary>
/// <exception cref="ArgumentNullException" accessor="set"><see cref="Components"/> cannot be null.</exception>
/// <exception cref="ArgumentException" accessor="set"><see cref="Components"/> count exceeds <see cref="MaxChildCount"/>.</exception>
public List<IMessageComponent> Components
public List<IMessageComponentBuilder> Components
{
get => _components;
set
Expand All @@ -37,15 +41,29 @@ public List<IMessageComponent> Components
}
}

private List<IMessageComponent> _components = new List<IMessageComponent>();

public ActionRowBuilder AddComponents(params IMessageComponentBuilder[] components)
{
foreach (var component in components)
AddComponent(component);
return this;
}

public ActionRowBuilder WithComponents(IEnumerable<IMessageComponentBuilder> components)
{
Components = components.ToList();
return this;
}

private List<IMessageComponentBuilder> _components = new ();

/// <summary>
/// Adds a list of components to the current row.
/// </summary>
/// <param name="components">The list of components to add.</param>
/// <inheritdoc cref="Components"/>
/// <returns>The current builder.</returns>
public ActionRowBuilder WithComponents(List<IMessageComponent> components)
public ActionRowBuilder WithComponents(List<IMessageComponentBuilder> components)
{
Components = components;
return this;
Expand All @@ -57,7 +75,7 @@ public ActionRowBuilder WithComponents(List<IMessageComponent> components)
/// <param name="component">The component to add.</param>
/// <exception cref="InvalidOperationException">Components count reached <see cref="MaxChildCount"/></exception>
/// <returns>The current builder.</returns>
public ActionRowBuilder AddComponent(IMessageComponent component)
public ActionRowBuilder AddComponent(IMessageComponentBuilder component)
{
if (Components.Count >= MaxChildCount)
throw new InvalidOperationException($"Components count reached {MaxChildCount}");
Expand Down Expand Up @@ -103,13 +121,11 @@ public ActionRowBuilder WithSelectMenu(SelectMenuBuilder menu)
{
if (menu.Options is not null && menu.Options.Distinct().Count() != menu.Options.Count)
throw new InvalidOperationException("Please make sure that there is no duplicates values.");

var builtMenu = menu.Build();


if (Components.Count != 0)
throw new InvalidOperationException($"A Select Menu cannot exist in a pre-occupied ActionRow.");

AddComponent(builtMenu);
AddComponent(menu);

return this;
}
Expand Down Expand Up @@ -152,15 +168,13 @@ public ActionRowBuilder WithButton(
/// <returns>The current builder.</returns>
public ActionRowBuilder WithButton(ButtonBuilder button)
{
var builtButton = button.Build();

if (Components.Count >= 5)
throw new InvalidOperationException($"Components count reached {MaxChildCount}");

if (Components.Any(x => x.Type.IsSelectType()))
throw new InvalidOperationException($"A button cannot be added to a row with a SelectMenu");

AddComponent(builtButton);
AddComponent(button);

return this;
}
Expand All @@ -171,10 +185,11 @@ public ActionRowBuilder WithButton(ButtonBuilder button)
/// <returns>A <see cref="ActionRowComponent"/> that can be used within a <see cref="ComponentBuilder"/></returns>
public ActionRowComponent Build()
{
return new ActionRowComponent(_components);
return new ActionRowComponent(_components.Select(x => x.Build()).ToList());
}
IMessageComponent IMessageComponentBuilder.Build() => Build();

internal bool CanTakeComponent(IMessageComponent component)
internal bool CanTakeComponent(IMessageComponentBuilder component)
{
switch (component.Type)
{
Expand All @@ -195,4 +210,11 @@ internal bool CanTakeComponent(IMessageComponent component)
return false;
}
}


IComponentContainer IComponentContainer.AddComponent(IMessageComponentBuilder component) => AddComponent(component);

IComponentContainer IComponentContainer.AddComponents(params IMessageComponentBuilder[] components) => AddComponents(components);

IComponentContainer IComponentContainer.WithComponents(IEnumerable<IMessageComponentBuilder> components) => WithComponents(components);
}
Loading