From 531b5eb3b7fe319f2295b8be5fadb09370e873e0 Mon Sep 17 00:00:00 2001
From: Mihail Gribkov <61027276+Misha-133@users.noreply.github.com>
Date: Fri, 14 Jun 2024 00:43:11 +0300
Subject: [PATCH] [Feature] premium buttonz (#2933)
---
.../Builders/ActionRowBuilder.cs | 198 ++
.../Builders/ButtonBuilder.cs | 320 ++++
.../Builders/ComponentBuilder.cs | 332 ++++
.../Builders/SelectMenuBuilder.cs | 406 ++++
.../Builders/SelectMenuOptionBuilder.cs | 207 +++
.../Builders/TextInputBuilder.cs | 262 +++
.../MessageComponents/ButtonComponent.cs | 118 +-
.../MessageComponents/ButtonStyle.cs | 54 +-
.../MessageComponents/ComponentBuilder.cs | 1636 -----------------
.../API/Common/ButtonComponent.cs | 4 +
.../Entities/Messages/RestMessage.cs | 3 +-
.../Entities/Messages/SocketMessage.cs | 3 +-
12 files changed, 1825 insertions(+), 1718 deletions(-)
create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ActionRowBuilder.cs
create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ButtonBuilder.cs
create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilder.cs
create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SelectMenuBuilder.cs
create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SelectMenuOptionBuilder.cs
create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextInputBuilder.cs
delete mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentBuilder.cs
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ActionRowBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ActionRowBuilder.cs
new file mode 100644
index 00000000..dbbe4e99
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ActionRowBuilder.cs
@@ -0,0 +1,198 @@
+using Discord.Utils;
+using System.Collections.Generic;
+using System.Linq;
+using System;
+
+namespace Discord;
+
+///
+/// Represents a class used to build Action rows.
+///
+public class ActionRowBuilder
+{
+ ///
+ /// The max amount of child components this row can hold.
+ ///
+ public const int MaxChildCount = 5;
+
+ ///
+ /// Gets or sets the components inside this row.
+ ///
+ /// cannot be null.
+ /// count exceeds .
+ public List Components
+ {
+ get => _components;
+ set
+ {
+ if (value == null)
+ throw new ArgumentNullException(nameof(value), $"{nameof(Components)} cannot be null.");
+
+ _components = value.Count switch
+ {
+ 0 => throw new ArgumentOutOfRangeException(nameof(value), "There must be at least 1 component in a row."),
+ > MaxChildCount => throw new ArgumentOutOfRangeException(nameof(value), $"Action row can only contain {MaxChildCount} child components!"),
+ _ => value
+ };
+ }
+ }
+
+ private List _components = new List();
+
+ ///
+ /// Adds a list of components to the current row.
+ ///
+ /// The list of components to add.
+ ///
+ /// The current builder.
+ public ActionRowBuilder WithComponents(List components)
+ {
+ Components = components;
+ return this;
+ }
+
+ ///
+ /// Adds a component at the end of the current row.
+ ///
+ /// The component to add.
+ /// Components count reached
+ /// The current builder.
+ public ActionRowBuilder AddComponent(IMessageComponent component)
+ {
+ if (Components.Count >= MaxChildCount)
+ throw new InvalidOperationException($"Components count reached {MaxChildCount}");
+
+ Components.Add(component);
+ return this;
+ }
+
+ ///
+ /// Adds a to the .
+ ///
+ /// The custom id of the menu.
+ /// The options of the menu.
+ /// The placeholder of the menu.
+ /// The min values of the placeholder.
+ /// The max values of the placeholder.
+ /// Whether or not the menu is disabled.
+ /// The type of the select menu.
+ /// Menus valid channel types (only for )
+ /// The current builder.
+ public ActionRowBuilder WithSelectMenu(string customId, List options = null,
+ string placeholder = null, int minValues = 1, int maxValues = 1, bool disabled = false,
+ ComponentType type = ComponentType.SelectMenu, ChannelType[] channelTypes = null)
+ {
+ return WithSelectMenu(new SelectMenuBuilder()
+ .WithCustomId(customId)
+ .WithOptions(options)
+ .WithPlaceholder(placeholder)
+ .WithMaxValues(maxValues)
+ .WithMinValues(minValues)
+ .WithDisabled(disabled)
+ .WithType(type)
+ .WithChannelTypes(channelTypes));
+ }
+
+ ///
+ /// Adds a to the .
+ ///
+ /// The menu to add.
+ /// A Select Menu cannot exist in a pre-occupied ActionRow.
+ /// The current builder.
+ 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);
+
+ return this;
+ }
+
+ ///
+ /// Adds a with specified parameters to the .
+ ///
+ /// The label text for the newly added button.
+ /// The style of this newly added button.
+ /// A to be used with this button.
+ /// The custom id of the newly added button.
+ /// A URL to be used only if the is a Link.
+ /// Whether or not the newly created button is disabled.
+ /// The current builder.
+ public ActionRowBuilder WithButton(
+ string label = null,
+ string customId = null,
+ ButtonStyle style = ButtonStyle.Primary,
+ IEmote emote = null,
+ string url = null,
+ bool disabled = false)
+ {
+ var button = new ButtonBuilder()
+ .WithLabel(label)
+ .WithStyle(style)
+ .WithEmote(emote)
+ .WithCustomId(customId)
+ .WithUrl(url)
+ .WithDisabled(disabled);
+
+ return WithButton(button);
+ }
+
+ ///
+ /// Adds a to the .
+ ///
+ /// The button to add.
+ /// Components count reached .
+ /// A button cannot be added to a row with a SelectMenu.
+ /// The current builder.
+ 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);
+
+ return this;
+ }
+
+ ///
+ /// Builds the current builder to a that can be used within a
+ ///
+ /// A that can be used within a
+ public ActionRowComponent Build()
+ {
+ return new ActionRowComponent(_components);
+ }
+
+ internal bool CanTakeComponent(IMessageComponent component)
+ {
+ switch (component.Type)
+ {
+ case ComponentType.ActionRow:
+ return false;
+ case ComponentType.Button:
+ if (Components.Any(x => x.Type.IsSelectType()))
+ return false;
+ else
+ return Components.Count < 5;
+ case ComponentType.SelectMenu:
+ case ComponentType.ChannelSelect:
+ case ComponentType.MentionableSelect:
+ case ComponentType.RoleSelect:
+ case ComponentType.UserSelect:
+ return Components.Count == 0;
+ default:
+ return false;
+ }
+ }
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ButtonBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ButtonBuilder.cs
new file mode 100644
index 00000000..f1f44a3c
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ButtonBuilder.cs
@@ -0,0 +1,320 @@
+using Discord.Utils;
+
+using System;
+
+namespace Discord;
+
+///
+/// Represents a class used to build 's.
+///
+public class ButtonBuilder
+{
+ ///
+ /// The max length of a .
+ ///
+ public const int MaxButtonLabelLength = 80;
+
+ ///
+ /// Gets or sets the label of the current button.
+ ///
+ /// length exceeds .
+ /// length exceeds .
+ public string Label
+ {
+ get => _label;
+ set => _label = value?.Length switch
+ {
+ > MaxButtonLabelLength => throw new ArgumentOutOfRangeException(nameof(value), $"Label length must be less or equal to {MaxButtonLabelLength}."),
+ 0 => throw new ArgumentOutOfRangeException(nameof(value), "Label length must be at least 1."),
+ _ => value
+ };
+ }
+
+ ///
+ /// Gets or sets the custom id of the current button.
+ ///
+ /// length exceeds
+ /// length subceeds 1.
+ public string CustomId
+ {
+ get => _customId;
+ set => _customId = value?.Length switch
+ {
+ > ComponentBuilder.MaxCustomIdLength => throw new ArgumentOutOfRangeException(nameof(value), $"Custom Id length must be less or equal to {ComponentBuilder.MaxCustomIdLength}."),
+ 0 => throw new ArgumentOutOfRangeException(nameof(value), "Custom Id length must be at least 1."),
+ _ => value
+ };
+ }
+
+ ///
+ /// Gets or sets the of the current button.
+ ///
+ public ButtonStyle Style { get; set; }
+
+ ///
+ /// Gets or sets the of the current button.
+ ///
+ public IEmote Emote { get; set; }
+
+ ///
+ /// Gets or sets the url of the current button.
+ ///
+ public string Url { get; set; }
+
+ ///
+ /// Gets or sets whether the current button is disabled.
+ ///
+ public bool IsDisabled { get; set; }
+
+ ///
+ /// Gets or sets the id of the sku associated with the current button.
+ ///
+ ///
+ /// if the button is not of type .
+ ///
+ public ulong? SkuId { get; set; }
+
+ private string _label;
+ private string _customId;
+
+ ///
+ /// Creates a new instance of a .
+ ///
+ public ButtonBuilder() { }
+
+ ///
+ /// Creates a new instance of a .
+ ///
+ /// The label to use on the newly created link button.
+ /// The url of this button.
+ /// The custom ID of this button.
+ /// The custom ID of this button.
+ /// The emote of this button.
+ /// Disabled this button or not.
+ /// The sku id of this button.
+ public ButtonBuilder(string label = null, string customId = null, ButtonStyle style = ButtonStyle.Primary, string url = null, IEmote emote = null, bool isDisabled = false, ulong? skuId = null)
+ {
+ CustomId = customId;
+ Style = style;
+ Url = url;
+ Label = label;
+ IsDisabled = isDisabled;
+ Emote = emote;
+ SkuId = skuId;
+ }
+
+ ///
+ /// Creates a new instance of a from instance of a .
+ ///
+ public ButtonBuilder(ButtonComponent button)
+ {
+ CustomId = button.CustomId;
+ Style = button.Style;
+ Url = button.Url;
+ Label = button.Label;
+ IsDisabled = button.IsDisabled;
+ Emote = button.Emote;
+ SkuId = button.SkuId;
+ }
+
+ ///
+ /// Creates a button with the style.
+ ///
+ /// The label for this link button.
+ /// The url for this link button to go to.
+ /// The emote for this link button.
+ /// A builder with the newly created button.
+ public static ButtonBuilder CreateLinkButton(string label, string url, IEmote emote = null)
+ => new (label, null, ButtonStyle.Link, url, emote: emote);
+
+ ///
+ /// Creates a button with the style.
+ ///
+ /// The label for this danger button.
+ /// The custom id for this danger button.
+ /// The emote for this danger button.
+ /// A builder with the newly created button.
+ public static ButtonBuilder CreateDangerButton(string label, string customId, IEmote emote = null)
+ => new (label, customId, ButtonStyle.Danger, emote: emote);
+
+ ///
+ /// Creates a button with the style.
+ ///
+ /// The label for this primary button.
+ /// The custom id for this primary button.
+ /// The emote for this primary button.
+ /// A builder with the newly created button.
+ public static ButtonBuilder CreatePrimaryButton(string label, string customId, IEmote emote = null)
+ => new (label, customId, emote: emote);
+
+ ///
+ /// Creates a button with the style.
+ ///
+ /// The label for this secondary button.
+ /// The custom id for this secondary button.
+ /// The emote for this secondary button.
+ /// A builder with the newly created button.
+ public static ButtonBuilder CreateSecondaryButton(string label, string customId, IEmote emote = null)
+ => new (label, customId, ButtonStyle.Secondary, emote: emote);
+
+ ///
+ /// Creates a button with the style.
+ ///
+ /// The label for this success button.
+ /// The custom id for this success button.
+ /// The emote for this success button.
+ /// A builder with the newly created button.
+ public static ButtonBuilder CreateSuccessButton(string label, string customId, IEmote emote = null)
+ => new (label, customId, ButtonStyle.Success, emote: emote);
+
+ ///
+ /// Creates a button with the style.
+ ///
+ /// The label for this premium button.
+ /// The sku id for this premium button.
+ /// The emote for this premium button.
+ /// A builder with the newly created button.
+ public static ButtonBuilder CreatePremiumButton(string label, ulong skuId, IEmote emote = null)
+ => new (label, style: ButtonStyle.Success, emote: emote, skuId: skuId);
+
+ ///
+ /// Sets the current buttons label to the specified text.
+ ///
+ /// The text for the label.
+ ///
+ /// The current builder.
+ public ButtonBuilder WithLabel(string label)
+ {
+ Label = label;
+ return this;
+ }
+
+ ///
+ /// Sets the current buttons style.
+ ///
+ /// The style for this builders button.
+ /// The current builder.
+ public ButtonBuilder WithStyle(ButtonStyle style)
+ {
+ Style = style;
+ return this;
+ }
+
+ ///
+ /// Sets the current buttons emote.
+ ///
+ /// The emote to use for the current button.
+ /// The current builder.
+ public ButtonBuilder WithEmote(IEmote emote)
+ {
+ Emote = emote;
+ return this;
+ }
+
+ ///
+ /// Sets the current buttons url.
+ ///
+ /// The url to use for the current button.
+ /// The current builder.
+ public ButtonBuilder WithUrl(string url)
+ {
+ Url = url;
+ return this;
+ }
+
+ ///
+ /// Sets the custom id of the current button.
+ ///
+ /// The id to use for the current button.
+ ///
+ /// The current builder.
+ public ButtonBuilder WithCustomId(string id)
+ {
+ CustomId = id;
+ return this;
+ }
+
+ ///
+ /// Sets whether the current button is disabled.
+ ///
+ /// Whether the current button is disabled or not.
+ /// The current builder.
+ public ButtonBuilder WithDisabled(bool isDisabled)
+ {
+ IsDisabled = isDisabled;
+ return this;
+ }
+
+ ///
+ /// Sets the sku id of the current button.
+ ///
+ /// The id of the sku
+ /// The current builder.
+ public ButtonBuilder WithSkuId(ulong? skuId)
+ {
+ SkuId = skuId;
+ return this;
+ }
+
+ ///
+ /// Builds this builder into a to be used in a .
+ ///
+ /// A to be used in a .
+ /// A button must contain either a or a , but not both.
+ /// A button must have an or a .
+ /// A link button must contain a URL.
+ /// A URL must include a protocol (http or https).
+ /// A non-link button must contain a custom id
+ public ButtonComponent Build()
+ {
+ if (string.IsNullOrWhiteSpace(Label) && Emote is null)
+ throw new InvalidOperationException("A button must have an Emote or a label!");
+
+ var a = 0;
+ if (!string.IsNullOrWhiteSpace(Url))
+ a++;
+ if (!string.IsNullOrWhiteSpace(CustomId))
+ a++;
+ if (SkuId is not null)
+ a++;
+
+ if (a is 0 or > 1)
+ throw new InvalidOperationException("A button must contain either a URL, CustomId or SkuId, but not multiple of them!");
+
+ switch (Style)
+ {
+ case 0:
+ {
+ throw new ArgumentException("A button must have a style.", nameof(Style));
+ }
+
+ case ButtonStyle.Primary:
+ case ButtonStyle.Secondary:
+ case ButtonStyle.Success:
+ case ButtonStyle.Danger:
+ {
+ if (string.IsNullOrWhiteSpace(CustomId))
+ throw new InvalidOperationException("Non-link and non-premium buttons must have a custom id associated with them");
+
+ }
+ break;
+
+ case ButtonStyle.Link:
+ {
+ if (string.IsNullOrWhiteSpace(Url))
+ throw new InvalidOperationException("Link buttons must have a link associated with them");
+ UrlValidation.ValidateButton(Url);
+ }
+ break;
+
+ case ButtonStyle.Premium:
+ {
+ if (SkuId is null)
+ throw new InvalidOperationException("Premium buttons must have a sku id associated with them");
+ }
+ break;
+ }
+
+ return new ButtonComponent(Style, Label, Emote, CustomId, Url, IsDisabled, SkuId);
+ }
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilder.cs
new file mode 100644
index 00000000..eeb7db85
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilder.cs
@@ -0,0 +1,332 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Discord;
+
+///
+/// Represents a builder for creating a .
+///
+public class ComponentBuilder
+{
+ ///
+ /// The max length of a .
+ ///
+ public const int MaxCustomIdLength = 100;
+
+ ///
+ /// The max amount of rows a message can have.
+ ///
+ public const int MaxActionRowCount = 5;
+
+ ///
+ /// Gets or sets the Action Rows for this Component Builder.
+ ///
+ /// cannot be null.
+ /// count exceeds .
+ public List ActionRows
+ {
+ get => _actionRows;
+ set
+ {
+ if (value == null)
+ throw new ArgumentNullException(nameof(value), $"{nameof(ActionRows)} cannot be null.");
+ if (value.Count > MaxActionRowCount)
+ throw new ArgumentOutOfRangeException(nameof(value), $"Action row count must be less than or equal to {MaxActionRowCount}.");
+ _actionRows = value;
+ }
+ }
+
+ private List _actionRows;
+
+ ///
+ /// Creates a new builder from a message.
+ ///
+ /// The message to create the builder from.
+ /// The newly created builder.
+ public static ComponentBuilder FromMessage(IMessage message)
+ => FromComponents(message.Components);
+
+ ///
+ /// Creates a new builder from the provided list of components.
+ ///
+ /// The components to create the builder from.
+ /// The newly created builder.
+ public static ComponentBuilder FromComponents(IReadOnlyCollection components)
+ {
+ var builder = new ComponentBuilder();
+ for (int i = 0; i != components.Count; i++)
+ {
+ var component = components.ElementAt(i);
+ builder.AddComponent(component, i);
+ }
+ return builder;
+ }
+
+ internal void AddComponent(IMessageComponent component, int row)
+ {
+ switch (component)
+ {
+ case ButtonComponent button:
+ WithButton(button.Label, button.CustomId, button.Style, button.Emote, button.Url, button.IsDisabled, row);
+ break;
+ case ActionRowComponent actionRow:
+ foreach (var cmp in actionRow.Components)
+ AddComponent(cmp, row);
+ break;
+ case SelectMenuComponent menu:
+ WithSelectMenu(menu.CustomId, menu.Options?.Select(x => new SelectMenuOptionBuilder(x.Label, x.Value, x.Description, x.Emote, x.IsDefault)).ToList(), menu.Placeholder, menu.MinValues, menu.MaxValues, menu.IsDisabled, row);
+ break;
+ }
+ }
+
+ ///
+ /// Removes all components of the given type from the .
+ ///
+ /// The to remove.
+ /// The current builder.
+ public ComponentBuilder RemoveComponentsOfType(ComponentType t)
+ {
+ this.ActionRows.ForEach(ar => ar.Components.RemoveAll(c => c.Type == t));
+ return this;
+ }
+
+ ///
+ /// Removes a component from the .
+ ///
+ /// The custom id of the component.
+ /// The current builder.
+ public ComponentBuilder RemoveComponent(string customId)
+ {
+ this.ActionRows.ForEach(ar => ar.Components.RemoveAll(c => c.CustomId == customId));
+ return this;
+ }
+
+ ///
+ /// Removes a Link Button from the based on its URL.
+ ///
+ /// The URL of the Link Button.
+ /// The current builder.
+ public ComponentBuilder RemoveButtonByURL(string url)
+ {
+ this.ActionRows.ForEach(ar => ar.Components.RemoveAll(c => c is ButtonComponent b && b.Url == url));
+ return this;
+ }
+
+ ///
+ /// Adds a to the at the specific row.
+ /// If the row cannot accept the component then it will add it to a row that can.
+ ///
+ /// The custom id of the menu.
+ /// The options of the menu.
+ /// The placeholder of the menu.
+ /// The min values of the placeholder.
+ /// The max values of the placeholder.
+ /// Whether or not the menu is disabled.
+ /// The row to add the menu to.
+ /// The type of the select menu.
+ /// Menus valid channel types (only for )
+ ///
+ public ComponentBuilder WithSelectMenu(string customId, List options = null,
+ string placeholder = null, int minValues = 1, int maxValues = 1, bool disabled = false, int row = 0, ComponentType type = ComponentType.SelectMenu,
+ ChannelType[] channelTypes = null, SelectMenuDefaultValue[] defaultValues = null)
+ {
+ return WithSelectMenu(new SelectMenuBuilder()
+ .WithCustomId(customId)
+ .WithOptions(options)
+ .WithPlaceholder(placeholder)
+ .WithMaxValues(maxValues)
+ .WithMinValues(minValues)
+ .WithDisabled(disabled)
+ .WithType(type)
+ .WithChannelTypes(channelTypes)
+ .WithDefaultValues(defaultValues),
+ row);
+ }
+
+ ///
+ /// Adds a to the at the specific row.
+ /// If the row cannot accept the component then it will add it to a row that can.
+ ///
+ /// The menu to add.
+ /// The row to attempt to add this component on.
+ /// There is no more row to add a menu.
+ /// must be less than .
+ /// The current builder.
+ public ComponentBuilder WithSelectMenu(SelectMenuBuilder menu, int row = 0)
+ {
+ Preconditions.LessThan(row, MaxActionRowCount, nameof(row));
+ 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 (_actionRows == null)
+ {
+ _actionRows = new List
+ {
+ new ActionRowBuilder().AddComponent(builtMenu)
+ };
+ }
+ else
+ {
+ if (_actionRows.Count == row)
+ _actionRows.Add(new ActionRowBuilder().AddComponent(builtMenu));
+ else
+ {
+ ActionRowBuilder actionRow;
+ if (_actionRows.Count > row)
+ actionRow = _actionRows.ElementAt(row);
+ else
+ {
+ actionRow = new ActionRowBuilder();
+ _actionRows.Add(actionRow);
+ }
+
+ if (actionRow.CanTakeComponent(builtMenu))
+ actionRow.AddComponent(builtMenu);
+ else if (row < MaxActionRowCount)
+ WithSelectMenu(menu, row + 1);
+ else
+ throw new InvalidOperationException($"There is no more row to add a {nameof(builtMenu)}");
+ }
+ }
+
+ return this;
+ }
+
+ ///
+ /// Adds a with specified parameters to the at the specific row.
+ /// If the row cannot accept the component then it will add it to a row that can.
+ ///
+ /// The label text for the newly added button.
+ /// The style of this newly added button.
+ /// A to be used with this button.
+ /// The custom id of the newly added button.
+ /// A URL to be used only if the is a Link.
+ /// Whether or not the newly created button is disabled.
+ /// The row the button should be placed on.
+ /// The id of the sku associated with the current button.
+ /// The current builder.
+ public ComponentBuilder WithButton(
+ string label = null,
+ string customId = null,
+ ButtonStyle style = ButtonStyle.Primary,
+ IEmote emote = null,
+ string url = null,
+ bool disabled = false,
+ int row = 0,
+ ulong? skuId = null)
+ {
+ var button = new ButtonBuilder()
+ .WithLabel(label)
+ .WithStyle(style)
+ .WithEmote(emote)
+ .WithCustomId(customId)
+ .WithUrl(url)
+ .WithDisabled(disabled)
+ .WithSkuId(skuId);
+
+ return WithButton(button, row);
+ }
+
+ ///
+ /// Adds a to the at the specific row.
+ /// If the row cannot accept the component then it will add it to a row that can.
+ ///
+ /// The button to add.
+ /// The row to add the button.
+ /// There is no more row to add a button.
+ /// must be less than .
+ /// The current builder.
+ public ComponentBuilder WithButton(ButtonBuilder button, int row = 0)
+ {
+ Preconditions.LessThan(row, MaxActionRowCount, nameof(row));
+
+ var builtButton = button.Build();
+
+ if (_actionRows == null)
+ {
+ _actionRows = new List
+ {
+ new ActionRowBuilder().AddComponent(builtButton)
+ };
+ }
+ else
+ {
+ if (_actionRows.Count == row)
+ _actionRows.Add(new ActionRowBuilder().AddComponent(builtButton));
+ else
+ {
+ ActionRowBuilder actionRow;
+ if (_actionRows.Count > row)
+ actionRow = _actionRows.ElementAt(row);
+ else
+ {
+ actionRow = new ActionRowBuilder();
+ _actionRows.Add(actionRow);
+ }
+
+ if (actionRow.CanTakeComponent(builtButton))
+ actionRow.AddComponent(builtButton);
+ else if (row < MaxActionRowCount)
+ WithButton(button, row + 1);
+ else
+ throw new InvalidOperationException($"There is no more row to add a {nameof(button)}");
+ }
+ }
+
+ return this;
+ }
+
+ ///
+ /// Adds a row to this component builder.
+ ///
+ /// The row to add.
+ /// The component builder contains the max amount of rows defined as .
+ /// The current builder.
+ public ComponentBuilder AddRow(ActionRowBuilder row)
+ {
+ _actionRows ??= new();
+
+ if (_actionRows.Count >= MaxActionRowCount)
+ throw new IndexOutOfRangeException("The max amount of rows has been reached");
+
+ ActionRows.Add(row);
+ return this;
+ }
+
+ ///
+ /// Sets the rows of this component builder to a specified collection.
+ ///
+ /// The rows to set.
+ /// The collection contains more rows then is allowed by discord.
+ /// The current builder.
+ public ComponentBuilder WithRows(IEnumerable rows)
+ {
+ if (rows.Count() > MaxActionRowCount)
+ throw new IndexOutOfRangeException($"Cannot have more than {MaxActionRowCount} rows");
+
+ _actionRows = new List(rows);
+ return this;
+ }
+
+ ///
+ /// Builds this builder into a used to send your components.
+ ///
+ /// A that can be sent with .
+ public MessageComponent Build()
+ {
+ if (_actionRows?.SelectMany(x => x.Components)?.Any(x => x.Type == ComponentType.TextInput) ?? false)
+ throw new ArgumentException("TextInputComponents are not allowed in messages.", nameof(ActionRows));
+
+ if (_actionRows?.Count > 0)
+ for (int i = 0; i < _actionRows?.Count; i++)
+ if (_actionRows[i]?.Components?.Count == 0)
+ _actionRows.RemoveAt(i);
+
+ return _actionRows != null
+ ? new MessageComponent(_actionRows.Select(x => x.Build()).ToList())
+ : MessageComponent.Empty;
+ }
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SelectMenuBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SelectMenuBuilder.cs
new file mode 100644
index 00000000..000a1c62
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SelectMenuBuilder.cs
@@ -0,0 +1,406 @@
+using Discord.Utils;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Discord;
+
+///
+/// Represents a class used to build 's.
+///
+public class SelectMenuBuilder
+{
+ ///
+ /// The max length of a .
+ ///
+ public const int MaxPlaceholderLength = 100;
+
+ ///
+ /// The maximum number of values for the and properties.
+ ///
+ public const int MaxValuesCount = 25;
+
+ ///
+ /// The maximum number of options a can have.
+ ///
+ public const int MaxOptionCount = 25;
+
+ ///
+ /// Gets or sets the custom id of the current select menu.
+ ///
+ /// length exceeds
+ /// length subceeds 1.
+ public string CustomId
+ {
+ get => _customId;
+ set => _customId = value?.Length switch
+ {
+ > ComponentBuilder.MaxCustomIdLength => throw new ArgumentOutOfRangeException(nameof(value), $"Custom Id length must be less or equal to {ComponentBuilder.MaxCustomIdLength}."),
+ 0 => throw new ArgumentOutOfRangeException(nameof(value), "Custom Id length must be at least 1."),
+ _ => value
+ };
+ }
+
+ ///
+ /// Gets or sets the type of the current select menu.
+ ///
+ /// Type must be a select menu type.
+ public ComponentType Type
+ {
+ get => _type;
+ set => _type = value.IsSelectType()
+ ? value
+ : throw new ArgumentException("Type must be a select menu type.", nameof(value));
+ }
+
+ ///
+ /// Gets or sets the placeholder text of the current select menu.
+ ///
+ /// length exceeds .
+ /// length subceeds 1.
+ public string Placeholder
+ {
+ get => _placeholder;
+ set => _placeholder = value?.Length switch
+ {
+ > MaxPlaceholderLength => throw new ArgumentOutOfRangeException(nameof(value), $"Placeholder length must be less or equal to {MaxPlaceholderLength}."),
+ 0 => throw new ArgumentOutOfRangeException(nameof(value), "Placeholder length must be at least 1."),
+ _ => value
+ };
+ }
+
+ ///
+ /// Gets or sets the minimum values of the current select menu.
+ ///
+ /// exceeds .
+ public int MinValues
+ {
+ get => _minValues;
+ set
+ {
+ Preconditions.AtMost(value, MaxValuesCount, nameof(MinValues));
+ _minValues = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the maximum values of the current select menu.
+ ///
+ /// exceeds .
+ public int MaxValues
+ {
+ get => _maxValues;
+ set
+ {
+ Preconditions.AtMost(value, MaxValuesCount, nameof(MaxValues));
+ _maxValues = value;
+ }
+ }
+
+ ///
+ /// Gets or sets a collection of for this current select menu.
+ ///
+ /// count exceeds .
+ /// is null.
+ public List Options
+ {
+ get => _options;
+ set
+ {
+ if (value != null)
+ Preconditions.AtMost(value.Count, MaxOptionCount, nameof(Options));
+
+ _options = value;
+ }
+ }
+
+ ///
+ /// Gets or sets whether the current menu is disabled.
+ ///
+ public bool IsDisabled { get; set; }
+
+ ///
+ /// Gets or sets the menu's channel types (only valid on s).
+ ///
+ public List ChannelTypes { get; set; }
+
+ public List DefaultValues
+ {
+ get => _defaultValues;
+ set
+ {
+ if (value != null)
+ Preconditions.AtMost(value.Count, MaxOptionCount, nameof(DefaultValues));
+
+ _defaultValues = value;
+ }
+ }
+
+ private List _options = new List();
+ private int _minValues = 1;
+ private int _maxValues = 1;
+ private string _placeholder;
+ private string _customId;
+ private ComponentType _type = ComponentType.SelectMenu;
+ private List _defaultValues = new();
+
+ ///
+ /// Creates a new instance of a .
+ ///
+ public SelectMenuBuilder() { }
+
+ ///
+ /// Creates a new instance of a from instance of .
+ ///
+ public SelectMenuBuilder(SelectMenuComponent selectMenu)
+ {
+ Placeholder = selectMenu.Placeholder;
+ CustomId = selectMenu.CustomId;
+ MaxValues = selectMenu.MaxValues;
+ MinValues = selectMenu.MinValues;
+ IsDisabled = selectMenu.IsDisabled;
+ Options = selectMenu.Options?
+ .Select(x => new SelectMenuOptionBuilder(x.Label, x.Value, x.Description, x.Emote, x.IsDefault))
+ .ToList();
+ DefaultValues = selectMenu.DefaultValues?.ToList();
+ }
+
+ ///
+ /// Creates a new instance of a .
+ ///
+ /// The custom id of this select menu.
+ /// The options for this select menu.
+ /// The placeholder of this select menu.
+ /// The max values of this select menu.
+ /// The min values of this select menu.
+ /// Disabled this select menu or not.
+ /// The of this select menu.
+ /// The types of channels this menu can select (only valid on s)
+ public SelectMenuBuilder(string customId, List options = null, string placeholder = null, int maxValues = 1, int minValues = 1,
+ bool isDisabled = false, ComponentType type = ComponentType.SelectMenu, List channelTypes = null, List defaultValues = null)
+ {
+ CustomId = customId;
+ Options = options;
+ Placeholder = placeholder;
+ IsDisabled = isDisabled;
+ MaxValues = maxValues;
+ MinValues = minValues;
+ Type = type;
+ ChannelTypes = channelTypes ?? new();
+ DefaultValues = defaultValues ?? new();
+ }
+
+ ///
+ /// Sets the field CustomId.
+ ///
+ /// The value to set the field CustomId to.
+ ///
+ ///
+ /// The current builder.
+ ///
+ public SelectMenuBuilder WithCustomId(string customId)
+ {
+ CustomId = customId;
+ return this;
+ }
+
+ ///
+ /// Sets the field placeholder.
+ ///
+ /// The value to set the field placeholder to.
+ ///
+ ///
+ /// The current builder.
+ ///
+ public SelectMenuBuilder WithPlaceholder(string placeholder)
+ {
+ Placeholder = placeholder;
+ return this;
+ }
+
+ ///
+ /// Sets the field minValues.
+ ///
+ /// The value to set the field minValues to.
+ ///
+ ///
+ /// The current builder.
+ ///
+ public SelectMenuBuilder WithMinValues(int minValues)
+ {
+ MinValues = minValues;
+ return this;
+ }
+
+ ///
+ /// Sets the field maxValues.
+ ///
+ /// The value to set the field maxValues to.
+ ///
+ ///
+ /// The current builder.
+ ///
+ public SelectMenuBuilder WithMaxValues(int maxValues)
+ {
+ MaxValues = maxValues;
+ return this;
+ }
+
+ ///
+ /// Sets the field options.
+ ///
+ /// The value to set the field options to.
+ ///
+ ///
+ /// The current builder.
+ ///
+ public SelectMenuBuilder WithOptions(List options)
+ {
+ Options = options;
+ return this;
+ }
+
+ ///
+ /// Add one option to menu options.
+ ///
+ /// The option builder class containing the option properties.
+ /// Options count reached .
+ ///
+ /// The current builder.
+ ///
+ public SelectMenuBuilder AddOption(SelectMenuOptionBuilder option)
+ {
+ Options ??= new();
+
+ if (Options.Count >= MaxOptionCount)
+ throw new InvalidOperationException($"Options count reached {MaxOptionCount}.");
+
+ Options.Add(option);
+ return this;
+ }
+
+ ///
+ /// Add one option to menu options.
+ ///
+ /// The label for this option.
+ /// The value of this option.
+ /// The description of this option.
+ /// The emote of this option.
+ /// Render this option as selected by default or not.
+ /// Options count reached .
+ ///
+ /// The current builder.
+ ///
+ public SelectMenuBuilder AddOption(string label, string value, string description = null, IEmote emote = null, bool? isDefault = null)
+ {
+ AddOption(new SelectMenuOptionBuilder(label, value, description, emote, isDefault));
+ return this;
+ }
+
+ ///
+ /// Add one default value to menu options.
+ ///
+ /// The id of an entity to add.
+ /// The type of an entity to add.
+ /// Default values count reached .
+ ///
+ /// The current builder.
+ ///
+ public SelectMenuBuilder AddDefaultValue(ulong id, SelectDefaultValueType type)
+ => AddDefaultValue(new(id, type));
+
+ ///
+ /// Add one default value to menu options.
+ ///
+ /// The default value to add.
+ /// Default values count reached .
+ ///
+ /// The current builder.
+ ///
+ public SelectMenuBuilder AddDefaultValue(SelectMenuDefaultValue value)
+ {
+ if (DefaultValues.Count >= MaxOptionCount)
+ throw new InvalidOperationException($"Options count reached {MaxOptionCount}.");
+
+ DefaultValues.Add(value);
+ return this;
+ }
+
+ ///
+ /// Sets the field default values.
+ ///
+ /// The value to set the field default values to.
+ ///
+ /// The current builder.
+ ///
+ public SelectMenuBuilder WithDefaultValues(params SelectMenuDefaultValue[] defaultValues)
+ {
+ DefaultValues = defaultValues?.ToList();
+ return this;
+ }
+
+ ///
+ /// Sets whether the current menu is disabled.
+ ///
+ /// Whether the current menu is disabled or not.
+ ///
+ /// The current builder.
+ ///
+ public SelectMenuBuilder WithDisabled(bool isDisabled)
+ {
+ IsDisabled = isDisabled;
+ return this;
+ }
+
+ ///
+ /// Sets the menu's current type.
+ ///
+ /// The type of the menu.
+ ///
+ /// The current builder.
+ ///
+ public SelectMenuBuilder WithType(ComponentType type)
+ {
+ Type = type;
+ return this;
+ }
+
+ ///
+ /// Sets the menus valid channel types (only for s).
+ ///
+ /// The valid channel types of the menu.
+ ///
+ /// The current builder.
+ ///
+ public SelectMenuBuilder WithChannelTypes(List channelTypes)
+ {
+ ChannelTypes = channelTypes;
+ return this;
+ }
+
+ ///
+ /// Sets the menus valid channel types (only for s).
+ ///
+ /// The valid channel types of the menu.
+ ///
+ /// The current builder.
+ ///
+ public SelectMenuBuilder WithChannelTypes(params ChannelType[] channelTypes)
+ {
+ ChannelTypes = channelTypes is null
+ ? ChannelTypeUtils.AllChannelTypes()
+ : channelTypes.ToList();
+ return this;
+ }
+
+ ///
+ /// Builds a
+ ///
+ /// The newly built
+ public SelectMenuComponent Build()
+ {
+ var options = Options?.Select(x => x.Build()).ToList();
+
+ return new SelectMenuComponent(CustomId, options, Placeholder, MinValues, MaxValues, IsDisabled, Type, ChannelTypes, DefaultValues);
+ }
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SelectMenuOptionBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SelectMenuOptionBuilder.cs
new file mode 100644
index 00000000..6e5b87bf
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SelectMenuOptionBuilder.cs
@@ -0,0 +1,207 @@
+using System;
+
+namespace Discord;
+
+///
+/// Represents a class used to build 's.
+///
+public class SelectMenuOptionBuilder
+{
+ ///
+ /// The maximum length of a .
+ ///
+ public const int MaxSelectLabelLength = 100;
+
+ ///
+ /// The maximum length of a .
+ ///
+ public const int MaxDescriptionLength = 100;
+
+ ///
+ /// The maximum length of a .
+ ///
+ public const int MaxSelectValueLength = 100;
+
+ ///
+ /// Gets or sets the label of the current select menu.
+ ///
+ /// length exceeds
+ /// length subceeds 1.
+ public string Label
+ {
+ get => _label;
+ set => _label = value?.Length switch
+ {
+ > MaxSelectLabelLength => throw new ArgumentOutOfRangeException(nameof(value), $"Label length must be less or equal to {MaxSelectLabelLength}."),
+ 0 => throw new ArgumentOutOfRangeException(nameof(value), "Label length must be at least 1."),
+ _ => value
+ };
+ }
+
+ ///
+ /// Gets or sets the value of the current select menu.
+ ///
+ /// length exceeds .
+ /// length subceeds 1.
+ public string Value
+ {
+ get => _value;
+ set => _value = value?.Length switch
+ {
+ > MaxSelectValueLength => throw new ArgumentOutOfRangeException(nameof(value), $"Value length must be less or equal to {MaxSelectValueLength}."),
+ 0 => throw new ArgumentOutOfRangeException(nameof(value), "Value length must be at least 1."),
+ _ => value
+ };
+ }
+
+ ///
+ /// Gets or sets this menu options description.
+ ///
+ /// length exceeds .
+ /// length subceeds 1.
+ public string Description
+ {
+ get => _description;
+ set => _description = value?.Length switch
+ {
+ > MaxDescriptionLength => throw new ArgumentOutOfRangeException(nameof(value), $"Description length must be less or equal to {MaxDescriptionLength}."),
+ 0 => throw new ArgumentOutOfRangeException(nameof(value), "Description length must be at least 1."),
+ _ => value
+ };
+ }
+
+ ///
+ /// Gets or sets the emote of this option.
+ ///
+ public IEmote Emote { get; set; }
+
+ ///
+ /// Gets or sets the whether or not this option will render selected by default.
+ ///
+ public bool? IsDefault { get; set; }
+
+ private string _label;
+ private string _value;
+ private string _description;
+
+ ///
+ /// Creates a new instance of a .
+ ///
+ public SelectMenuOptionBuilder() { }
+
+ ///
+ /// Creates a new instance of a .
+ ///
+ /// The label for this option.
+ /// The value of this option.
+ /// The description of this option.
+ /// The emote of this option.
+ /// Render this option as selected by default or not.
+ public SelectMenuOptionBuilder(string label, string value, string description = null, IEmote emote = null, bool? isDefault = null)
+ {
+ Label = label;
+ Value = value;
+ Description = description;
+ Emote = emote;
+ IsDefault = isDefault;
+ }
+
+ ///
+ /// Creates a new instance of a from instance of a .
+ ///
+ public SelectMenuOptionBuilder(SelectMenuOption option)
+ {
+ Label = option.Label;
+ Value = option.Value;
+ Description = option.Description;
+ Emote = option.Emote;
+ IsDefault = option.IsDefault;
+ }
+
+ ///
+ /// Sets the field label.
+ ///
+ /// The value to set the field label to.
+ ///
+ ///
+ /// The current builder.
+ ///
+ public SelectMenuOptionBuilder WithLabel(string label)
+ {
+ Label = label;
+ return this;
+ }
+
+ ///
+ /// Sets the field value.
+ ///
+ /// The value to set the field value to.
+ ///
+ ///
+ /// The current builder.
+ ///
+ public SelectMenuOptionBuilder WithValue(string value)
+ {
+ Value = value;
+ return this;
+ }
+
+ ///
+ /// Sets the field description.
+ ///
+ /// The value to set the field description to.
+ ///
+ ///
+ /// The current builder.
+ ///
+ public SelectMenuOptionBuilder WithDescription(string description)
+ {
+ Description = description;
+ return this;
+ }
+
+ ///
+ /// Sets the field emote.
+ ///
+ /// The value to set the field emote to.
+ ///
+ /// The current builder.
+ ///
+ public SelectMenuOptionBuilder WithEmote(IEmote emote)
+ {
+ Emote = emote;
+ return this;
+ }
+
+ ///
+ /// Sets the field default.
+ ///
+ /// The value to set the field default to.
+ ///
+ /// The current builder.
+ ///
+ public SelectMenuOptionBuilder WithDefault(bool isDefault)
+ {
+ IsDefault = isDefault;
+ return this;
+ }
+
+ ///
+ /// Builds a .
+ ///
+ /// The newly built .
+ public SelectMenuOption Build()
+ {
+ if (string.IsNullOrWhiteSpace(Label))
+ throw new ArgumentNullException(nameof(Label), "Option must have a label.");
+
+ Preconditions.AtMost(Label.Length, MaxSelectLabelLength, nameof(Label), $"Label length must be less or equal to {MaxSelectLabelLength}.");
+
+ if (string.IsNullOrWhiteSpace(Value))
+ throw new ArgumentNullException(nameof(Value), "Option must have a value.");
+
+ Preconditions.AtMost(Value.Length, MaxSelectValueLength, nameof(Value), $"Value length must be less or equal to {MaxSelectValueLength}.");
+
+ return new SelectMenuOption(Label, Value, Description, Emote, IsDefault);
+ }
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextInputBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextInputBuilder.cs
new file mode 100644
index 00000000..b675980b
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextInputBuilder.cs
@@ -0,0 +1,262 @@
+using System;
+using System.Linq;
+
+namespace Discord;
+
+///
+/// Represents a builder for creating a .
+///
+public class TextInputBuilder
+{
+ ///
+ /// The max length of a .
+ ///
+ public const int MaxPlaceholderLength = 100;
+ public const int LargestMaxLength = 4000;
+
+ ///
+ /// Gets or sets the custom id of the current text input.
+ ///
+ /// length exceeds
+ /// length subceeds 1.
+ public string CustomId
+ {
+ get => _customId;
+ set => _customId = value?.Length switch
+ {
+ > ComponentBuilder.MaxCustomIdLength => throw new ArgumentOutOfRangeException(nameof(value), $"Custom Id length must be less or equal to {ComponentBuilder.MaxCustomIdLength}."),
+ 0 => throw new ArgumentOutOfRangeException(nameof(value), "Custom Id length must be at least 1."),
+ _ => value
+ };
+ }
+
+ ///
+ /// Gets or sets the style of the current text input.
+ ///
+ public TextInputStyle Style { get; set; } = TextInputStyle.Short;
+
+ ///
+ /// Gets or sets the label of the current text input.
+ ///
+ public string Label { get; set; }
+
+ ///
+ /// Gets or sets the placeholder of the current text input.
+ ///
+ /// is longer than characters
+ public string Placeholder
+ {
+ get => _placeholder;
+ set => _placeholder = (value?.Length ?? 0) <= MaxPlaceholderLength
+ ? value
+ : throw new ArgumentException($"Placeholder cannot have more than {MaxPlaceholderLength} characters.");
+ }
+
+ ///
+ /// Gets or sets the minimum length of the current text input.
+ ///
+ /// is less than 0.
+ /// is greater than .
+ /// is greater than .
+ public int? MinLength
+ {
+ get => _minLength;
+ set
+ {
+ if (value < 0)
+ throw new ArgumentOutOfRangeException(nameof(value), $"MinLength must not be less than 0");
+ if (value > LargestMaxLength)
+ throw new ArgumentOutOfRangeException(nameof(value), $"MinLength must not be greater than {LargestMaxLength}");
+ if (value > (MaxLength ?? LargestMaxLength))
+ throw new ArgumentOutOfRangeException(nameof(value), $"MinLength must be less than MaxLength");
+ _minLength = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the maximum length of the current text input.
+ ///
+ /// is less than 0.
+ /// is greater than .
+ /// is less than .
+ public int? MaxLength
+ {
+ get => _maxLength;
+ set
+ {
+ if (value < 0)
+ throw new ArgumentOutOfRangeException(nameof(value), $"MaxLength must not be less than 0");
+ if (value > LargestMaxLength)
+ throw new ArgumentOutOfRangeException(nameof(value), $"MaxLength most not be greater than {LargestMaxLength}");
+ if (value < (MinLength ?? -1))
+ throw new ArgumentOutOfRangeException(nameof(value), $"MaxLength must be greater than MinLength ({MinLength})");
+ _maxLength = value;
+ }
+ }
+
+ ///
+ /// Gets or sets whether the user is required to input text.
+ ///
+ public bool? Required { get; set; }
+
+ ///
+ /// Gets or sets the default value of the text input.
+ ///
+ /// .Length is less than 0.
+ ///
+ /// .Length is greater than or .
+ ///
+ ///
+ /// is and contains a new line character.
+ ///
+ public string Value
+ {
+ get => _value;
+ set
+ {
+ if (value?.Length > (MaxLength ?? LargestMaxLength))
+ throw new ArgumentOutOfRangeException(nameof(value), $"Value must not be longer than {MaxLength ?? LargestMaxLength}.");
+ if (value?.Length < (MinLength ?? 0))
+ throw new ArgumentOutOfRangeException(nameof(value), $"Value must not be shorter than {MinLength}");
+
+ _value = value;
+ }
+ }
+
+ private string _customId;
+ private int? _maxLength;
+ private int? _minLength;
+ private string _placeholder;
+ private string _value;
+
+ ///
+ /// Creates a new instance of a .
+ ///
+ /// The text input's label.
+ /// The text input's style.
+ /// The text input's custom id.
+ /// The text input's placeholder.
+ /// The text input's minimum length.
+ /// The text input's maximum length.
+ /// The text input's required value.
+ public TextInputBuilder(string label, string customId, TextInputStyle style = TextInputStyle.Short, string placeholder = null,
+ int? minLength = null, int? maxLength = null, bool? required = null, string value = null)
+ {
+ Label = label;
+ Style = style;
+ CustomId = customId;
+ Placeholder = placeholder;
+ MinLength = minLength;
+ MaxLength = maxLength;
+ Required = required;
+ Value = value;
+ }
+
+ ///
+ /// Creates a new instance of a .
+ ///
+ public TextInputBuilder()
+ {
+
+ }
+
+ ///
+ /// Sets the label of the current builder.
+ ///
+ /// The value to set.
+ /// The current builder.
+ public TextInputBuilder WithLabel(string label)
+ {
+ Label = label;
+ return this;
+ }
+
+ ///
+ /// Sets the style of the current builder.
+ ///
+ /// The value to set.
+ /// The current builder.
+ public TextInputBuilder WithStyle(TextInputStyle style)
+ {
+ Style = style;
+ return this;
+ }
+
+ ///
+ /// Sets the custom id of the current builder.
+ ///
+ /// The value to set.
+ /// The current builder.
+ public TextInputBuilder WithCustomId(string customId)
+ {
+ CustomId = customId;
+ return this;
+ }
+
+ ///
+ /// Sets the placeholder of the current builder.
+ ///
+ /// The value to set.
+ /// The current builder.
+ public TextInputBuilder WithPlaceholder(string placeholder)
+ {
+ Placeholder = placeholder;
+ return this;
+ }
+
+ ///
+ /// Sets the value of the current builder.
+ ///
+ /// The value to set
+ /// The current builder.
+ public TextInputBuilder WithValue(string value)
+ {
+ Value = value;
+ return this;
+ }
+
+ ///
+ /// Sets the minimum length of the current builder.
+ ///
+ /// The value to set.
+ /// The current builder.
+ public TextInputBuilder WithMinLength(int minLength)
+ {
+ MinLength = minLength;
+ return this;
+ }
+
+ ///
+ /// Sets the maximum length of the current builder.
+ ///
+ /// The value to set.
+ /// The current builder.
+ public TextInputBuilder WithMaxLength(int maxLength)
+ {
+ MaxLength = maxLength;
+ return this;
+ }
+
+ ///
+ /// Sets the required value of the current builder.
+ ///
+ /// The value to set.
+ /// The current builder.
+ public TextInputBuilder WithRequired(bool required)
+ {
+ Required = required;
+ return this;
+ }
+
+ public TextInputComponent Build()
+ {
+ if (string.IsNullOrEmpty(CustomId))
+ throw new ArgumentException("TextInputComponents must have a custom id.", nameof(CustomId));
+ if (string.IsNullOrWhiteSpace(Label))
+ throw new ArgumentException("TextInputComponents must have a label.", nameof(Label));
+ if (Style is TextInputStyle.Short && Value?.Any(x => x == '\n') is true)
+ throw new ArgumentException($"Value must not contain new line characters when style is {TextInputStyle.Short}.", nameof(Value));
+
+ return new TextInputComponent(CustomId, Label, Placeholder, MinLength, MaxLength, Style, Required, Value);
+ }
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ButtonComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ButtonComponent.cs
index 4b9fa275..c387f9ad 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ButtonComponent.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ButtonComponent.cs
@@ -1,61 +1,69 @@
-namespace Discord
+namespace Discord;
+
+///
+/// Represents a Button.
+///
+public class ButtonComponent : IMessageComponent
{
+ ///
+ public ComponentType Type => ComponentType.Button;
+
///
- /// Represents a Button.
+ /// Gets the of this button, example buttons with each style can be found Here.
///
- public class ButtonComponent : IMessageComponent
+ public ButtonStyle Style { get; }
+
+ ///
+ /// Gets the label of the button, this is the text that is shown.
+ ///
+ public string Label { get; }
+
+ ///
+ /// Gets the displayed with this button.
+ ///
+ public IEmote Emote { get; }
+
+ ///
+ public string CustomId { get; }
+
+ ///
+ /// Gets the URL for a button.
+ ///
+ ///
+ /// You cannot have a button with a URL and a CustomId.
+ ///
+ public string Url { get; }
+
+ ///
+ /// Gets whether this button is disabled or not.
+ ///
+ public bool IsDisabled { get; }
+
+ ///
+ /// Gets the id of the sku associated with the current button.
+ ///
+ ///
+ /// if the button is not of type .
+ ///
+ public ulong? SkuId { get; }
+
+ ///
+ /// Turns this button into a button builder.
+ ///
+ ///
+ /// A newly created button builder with the same properties as this button.
+ ///
+ public ButtonBuilder ToBuilder()
+ => new (Label, CustomId, Style, Url, Emote, IsDisabled);
+
+ internal ButtonComponent(ButtonStyle style, string label, IEmote emote, string customId, string url, bool isDisabled, ulong? skuId)
{
- ///
- public ComponentType Type => ComponentType.Button;
-
- ///
- /// Gets the of this button, example buttons with each style can be found Here.
- ///
- public ButtonStyle Style { get; }
-
- ///
- /// Gets the label of the button, this is the text that is shown.
- ///
- public string Label { get; }
-
- ///
- /// Gets the displayed with this button.
- ///
- public IEmote Emote { get; }
-
- ///
- public string CustomId { get; }
-
- ///
- /// Gets the URL for a button.
- ///
- ///
- /// You cannot have a button with a URL and a CustomId.
- ///
- public string Url { get; }
-
- ///
- /// Gets whether this button is disabled or not.
- ///
- public bool IsDisabled { get; }
-
- ///
- /// Turns this button into a button builder.
- ///
- ///
- /// A newly created button builder with the same properties as this button.
- ///
- public ButtonBuilder ToBuilder()
- => new ButtonBuilder(Label, CustomId, Style, Url, Emote, IsDisabled);
-
- internal ButtonComponent(ButtonStyle style, string label, IEmote emote, string customId, string url, bool isDisabled)
- {
- Style = style;
- Label = label;
- Emote = emote;
- CustomId = customId;
- Url = url;
- IsDisabled = isDisabled;
- }
+ Style = style;
+ Label = label;
+ Emote = emote;
+ CustomId = customId;
+ Url = url;
+ IsDisabled = isDisabled;
+ SkuId = skuId;
}
}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ButtonStyle.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ButtonStyle.cs
index 92d48ab4..3b7a3158 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ButtonStyle.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ButtonStyle.cs
@@ -1,33 +1,37 @@
-namespace Discord
+namespace Discord;
+
+///
+/// Represents different styles to use with buttons. You can see an example of the different styles at
+///
+public enum ButtonStyle
{
///
- /// Represents different styles to use with buttons. You can see an example of the different styles at
+ /// A Blurple button.
///
- public enum ButtonStyle
- {
- ///
- /// A Blurple button
- ///
- Primary = 1,
+ Primary = 1,
- ///
- /// A Grey (or gray) button
- ///
- Secondary = 2,
+ ///
+ /// A Grey (or gray) button.
+ ///
+ Secondary = 2,
- ///
- /// A Green button
- ///
- Success = 3,
+ ///
+ /// A Green button.
+ ///
+ Success = 3,
- ///
- /// A Red button
- ///
- Danger = 4,
+ ///
+ /// A Red button.
+ ///
+ Danger = 4,
- ///
- /// A button with a little popup box indicating that this button is a link.
- ///
- Link = 5
- }
+ ///
+ /// A button with a little popup box indicating that this button is a link.
+ ///
+ Link = 5,
+
+ ///
+ /// A gradient button, opens a product's details modal.
+ ///
+ Premium = 6,
}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentBuilder.cs
deleted file mode 100644
index e3c1623c..00000000
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentBuilder.cs
+++ /dev/null
@@ -1,1636 +0,0 @@
-using Discord.Utils;
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace Discord
-{
- ///
- /// Represents a builder for creating a .
- ///
- public class ComponentBuilder
- {
- ///
- /// The max length of a .
- ///
- public const int MaxCustomIdLength = 100;
-
- ///
- /// The max amount of rows a message can have.
- ///
- public const int MaxActionRowCount = 5;
-
- ///
- /// Gets or sets the Action Rows for this Component Builder.
- ///
- /// cannot be null.
- /// count exceeds .
- public List ActionRows
- {
- get => _actionRows;
- set
- {
- if (value == null)
- throw new ArgumentNullException(nameof(value), $"{nameof(ActionRows)} cannot be null.");
- if (value.Count > MaxActionRowCount)
- throw new ArgumentOutOfRangeException(nameof(value), $"Action row count must be less than or equal to {MaxActionRowCount}.");
- _actionRows = value;
- }
- }
-
- private List _actionRows;
-
- ///
- /// Creates a new builder from a message.
- ///
- /// The message to create the builder from.
- /// The newly created builder.
- public static ComponentBuilder FromMessage(IMessage message)
- => FromComponents(message.Components);
-
- ///
- /// Creates a new builder from the provided list of components.
- ///
- /// The components to create the builder from.
- /// The newly created builder.
- public static ComponentBuilder FromComponents(IReadOnlyCollection components)
- {
- var builder = new ComponentBuilder();
- for (int i = 0; i != components.Count; i++)
- {
- var component = components.ElementAt(i);
- builder.AddComponent(component, i);
- }
- return builder;
- }
-
- internal void AddComponent(IMessageComponent component, int row)
- {
- switch (component)
- {
- case ButtonComponent button:
- WithButton(button.Label, button.CustomId, button.Style, button.Emote, button.Url, button.IsDisabled, row);
- break;
- case ActionRowComponent actionRow:
- foreach (var cmp in actionRow.Components)
- AddComponent(cmp, row);
- break;
- case SelectMenuComponent menu:
- WithSelectMenu(menu.CustomId, menu.Options?.Select(x => new SelectMenuOptionBuilder(x.Label, x.Value, x.Description, x.Emote, x.IsDefault)).ToList(), menu.Placeholder, menu.MinValues, menu.MaxValues, menu.IsDisabled, row);
- break;
- }
- }
-
- ///
- /// Removes all components of the given type from the .
- ///
- /// The to remove.
- /// The current builder.
- public ComponentBuilder RemoveComponentsOfType(ComponentType t)
- {
- this.ActionRows.ForEach(ar => ar.Components.RemoveAll(c => c.Type == t));
- return this;
- }
-
- ///
- /// Removes a component from the .
- ///
- /// The custom id of the component.
- /// The current builder.
- public ComponentBuilder RemoveComponent(string customId)
- {
- this.ActionRows.ForEach(ar => ar.Components.RemoveAll(c => c.CustomId == customId));
- return this;
- }
-
- ///
- /// Removes a Link Button from the based on its URL.
- ///
- /// The URL of the Link Button.
- /// The current builder.
- public ComponentBuilder RemoveButtonByURL(string url)
- {
- this.ActionRows.ForEach(ar => ar.Components.RemoveAll(c => c is ButtonComponent b && b.Url == url));
- return this;
- }
-
- ///
- /// Adds a to the at the specific row.
- /// If the row cannot accept the component then it will add it to a row that can.
- ///
- /// The custom id of the menu.
- /// The options of the menu.
- /// The placeholder of the menu.
- /// The min values of the placeholder.
- /// The max values of the placeholder.
- /// Whether or not the menu is disabled.
- /// The row to add the menu to.
- /// The type of the select menu.
- /// Menus valid channel types (only for )
- ///
- public ComponentBuilder WithSelectMenu(string customId, List options = null,
- string placeholder = null, int minValues = 1, int maxValues = 1, bool disabled = false, int row = 0, ComponentType type = ComponentType.SelectMenu,
- ChannelType[] channelTypes = null, SelectMenuDefaultValue[] defaultValues = null)
- {
- return WithSelectMenu(new SelectMenuBuilder()
- .WithCustomId(customId)
- .WithOptions(options)
- .WithPlaceholder(placeholder)
- .WithMaxValues(maxValues)
- .WithMinValues(minValues)
- .WithDisabled(disabled)
- .WithType(type)
- .WithChannelTypes(channelTypes)
- .WithDefaultValues(defaultValues),
- row);
- }
-
- ///
- /// Adds a to the at the specific row.
- /// If the row cannot accept the component then it will add it to a row that can.
- ///
- /// The menu to add.
- /// The row to attempt to add this component on.
- /// There is no more row to add a menu.
- /// must be less than .
- /// The current builder.
- public ComponentBuilder WithSelectMenu(SelectMenuBuilder menu, int row = 0)
- {
- Preconditions.LessThan(row, MaxActionRowCount, nameof(row));
- 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 (_actionRows == null)
- {
- _actionRows = new List
- {
- new ActionRowBuilder().AddComponent(builtMenu)
- };
- }
- else
- {
- if (_actionRows.Count == row)
- _actionRows.Add(new ActionRowBuilder().AddComponent(builtMenu));
- else
- {
- ActionRowBuilder actionRow;
- if (_actionRows.Count > row)
- actionRow = _actionRows.ElementAt(row);
- else
- {
- actionRow = new ActionRowBuilder();
- _actionRows.Add(actionRow);
- }
-
- if (actionRow.CanTakeComponent(builtMenu))
- actionRow.AddComponent(builtMenu);
- else if (row < MaxActionRowCount)
- WithSelectMenu(menu, row + 1);
- else
- throw new InvalidOperationException($"There is no more row to add a {nameof(builtMenu)}");
- }
- }
-
- return this;
- }
-
- ///
- /// Adds a with specified parameters to the at the specific row.
- /// If the row cannot accept the component then it will add it to a row that can.
- ///
- /// The label text for the newly added button.
- /// The style of this newly added button.
- /// A to be used with this button.
- /// The custom id of the newly added button.
- /// A URL to be used only if the is a Link.
- /// Whether or not the newly created button is disabled.
- /// The row the button should be placed on.
- /// The current builder.
- public ComponentBuilder WithButton(
- string label = null,
- string customId = null,
- ButtonStyle style = ButtonStyle.Primary,
- IEmote emote = null,
- string url = null,
- bool disabled = false,
- int row = 0)
- {
- var button = new ButtonBuilder()
- .WithLabel(label)
- .WithStyle(style)
- .WithEmote(emote)
- .WithCustomId(customId)
- .WithUrl(url)
- .WithDisabled(disabled);
-
- return WithButton(button, row);
- }
-
- ///
- /// Adds a to the at the specific row.
- /// If the row cannot accept the component then it will add it to a row that can.
- ///
- /// The button to add.
- /// The row to add the button.
- /// There is no more row to add a button.
- /// must be less than .
- /// The current builder.
- public ComponentBuilder WithButton(ButtonBuilder button, int row = 0)
- {
- Preconditions.LessThan(row, MaxActionRowCount, nameof(row));
-
- var builtButton = button.Build();
-
- if (_actionRows == null)
- {
- _actionRows = new List
- {
- new ActionRowBuilder().AddComponent(builtButton)
- };
- }
- else
- {
- if (_actionRows.Count == row)
- _actionRows.Add(new ActionRowBuilder().AddComponent(builtButton));
- else
- {
- ActionRowBuilder actionRow;
- if (_actionRows.Count > row)
- actionRow = _actionRows.ElementAt(row);
- else
- {
- actionRow = new ActionRowBuilder();
- _actionRows.Add(actionRow);
- }
-
- if (actionRow.CanTakeComponent(builtButton))
- actionRow.AddComponent(builtButton);
- else if (row < MaxActionRowCount)
- WithButton(button, row + 1);
- else
- throw new InvalidOperationException($"There is no more row to add a {nameof(button)}");
- }
- }
-
- return this;
- }
-
- ///
- /// Adds a row to this component builder.
- ///
- /// The row to add.
- /// The component builder contains the max amount of rows defined as .
- /// The current builder.
- public ComponentBuilder AddRow(ActionRowBuilder row)
- {
- _actionRows ??= new();
-
- if (_actionRows.Count >= MaxActionRowCount)
- throw new IndexOutOfRangeException("The max amount of rows has been reached");
-
- ActionRows.Add(row);
- return this;
- }
-
- ///
- /// Sets the rows of this component builder to a specified collection.
- ///
- /// The rows to set.
- /// The collection contains more rows then is allowed by discord.
- /// The current builder.
- public ComponentBuilder WithRows(IEnumerable rows)
- {
- if (rows.Count() > MaxActionRowCount)
- throw new IndexOutOfRangeException($"Cannot have more than {MaxActionRowCount} rows");
-
- _actionRows = new List(rows);
- return this;
- }
-
- ///
- /// Builds this builder into a used to send your components.
- ///
- /// A that can be sent with .
- public MessageComponent Build()
- {
- if (_actionRows?.SelectMany(x => x.Components)?.Any(x => x.Type == ComponentType.TextInput) ?? false)
- throw new ArgumentException("TextInputComponents are not allowed in messages.", nameof(ActionRows));
-
- if (_actionRows?.Count > 0)
- for (int i = 0; i < _actionRows?.Count; i++)
- if (_actionRows[i]?.Components?.Count == 0)
- _actionRows.RemoveAt(i);
-
- return _actionRows != null
- ? new MessageComponent(_actionRows.Select(x => x.Build()).ToList())
- : MessageComponent.Empty;
- }
- }
-
- ///
- /// Represents a class used to build Action rows.
- ///
- public class ActionRowBuilder
- {
- ///
- /// The max amount of child components this row can hold.
- ///
- public const int MaxChildCount = 5;
-
- ///
- /// Gets or sets the components inside this row.
- ///
- /// cannot be null.
- /// count exceeds .
- public List Components
- {
- get => _components;
- set
- {
- if (value == null)
- throw new ArgumentNullException(nameof(value), $"{nameof(Components)} cannot be null.");
-
- _components = value.Count switch
- {
- 0 => throw new ArgumentOutOfRangeException(nameof(value), "There must be at least 1 component in a row."),
- > MaxChildCount => throw new ArgumentOutOfRangeException(nameof(value), $"Action row can only contain {MaxChildCount} child components!"),
- _ => value
- };
- }
- }
-
- private List _components = new List();
-
- ///
- /// Adds a list of components to the current row.
- ///
- /// The list of components to add.
- ///
- /// The current builder.
- public ActionRowBuilder WithComponents(List components)
- {
- Components = components;
- return this;
- }
-
- ///
- /// Adds a component at the end of the current row.
- ///
- /// The component to add.
- /// Components count reached
- /// The current builder.
- public ActionRowBuilder AddComponent(IMessageComponent component)
- {
- if (Components.Count >= MaxChildCount)
- throw new InvalidOperationException($"Components count reached {MaxChildCount}");
-
- Components.Add(component);
- return this;
- }
-
- ///
- /// Adds a to the .
- ///
- /// The custom id of the menu.
- /// The options of the menu.
- /// The placeholder of the menu.
- /// The min values of the placeholder.
- /// The max values of the placeholder.
- /// Whether or not the menu is disabled.
- /// The type of the select menu.
- /// Menus valid channel types (only for )
- /// The current builder.
- public ActionRowBuilder WithSelectMenu(string customId, List options = null,
- string placeholder = null, int minValues = 1, int maxValues = 1, bool disabled = false,
- ComponentType type = ComponentType.SelectMenu, ChannelType[] channelTypes = null)
- {
- return WithSelectMenu(new SelectMenuBuilder()
- .WithCustomId(customId)
- .WithOptions(options)
- .WithPlaceholder(placeholder)
- .WithMaxValues(maxValues)
- .WithMinValues(minValues)
- .WithDisabled(disabled)
- .WithType(type)
- .WithChannelTypes(channelTypes));
- }
-
- ///
- /// Adds a to the .
- ///
- /// The menu to add.
- /// A Select Menu cannot exist in a pre-occupied ActionRow.
- /// The current builder.
- 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);
-
- return this;
- }
-
- ///
- /// Adds a with specified parameters to the .
- ///
- /// The label text for the newly added button.
- /// The style of this newly added button.
- /// A to be used with this button.
- /// The custom id of the newly added button.
- /// A URL to be used only if the is a Link.
- /// Whether or not the newly created button is disabled.
- /// The current builder.
- public ActionRowBuilder WithButton(
- string label = null,
- string customId = null,
- ButtonStyle style = ButtonStyle.Primary,
- IEmote emote = null,
- string url = null,
- bool disabled = false)
- {
- var button = new ButtonBuilder()
- .WithLabel(label)
- .WithStyle(style)
- .WithEmote(emote)
- .WithCustomId(customId)
- .WithUrl(url)
- .WithDisabled(disabled);
-
- return WithButton(button);
- }
-
- ///
- /// Adds a to the .
- ///
- /// The button to add.
- /// Components count reached .
- /// A button cannot be added to a row with a SelectMenu.
- /// The current builder.
- 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);
-
- return this;
- }
-
- ///
- /// Builds the current builder to a that can be used within a
- ///
- /// A that can be used within a
- public ActionRowComponent Build()
- {
- return new ActionRowComponent(_components);
- }
-
- internal bool CanTakeComponent(IMessageComponent component)
- {
- switch (component.Type)
- {
- case ComponentType.ActionRow:
- return false;
- case ComponentType.Button:
- if (Components.Any(x => x.Type.IsSelectType()))
- return false;
- else
- return Components.Count < 5;
- case ComponentType.SelectMenu:
- case ComponentType.ChannelSelect:
- case ComponentType.MentionableSelect:
- case ComponentType.RoleSelect:
- case ComponentType.UserSelect:
- return Components.Count == 0;
- default:
- return false;
- }
- }
- }
-
- ///
- /// Represents a class used to build 's.
- ///
- public class ButtonBuilder
- {
- ///
- /// The max length of a .
- ///
- public const int MaxButtonLabelLength = 80;
-
- ///
- /// Gets or sets the label of the current button.
- ///
- /// length exceeds .
- /// length exceeds .
- public string Label
- {
- get => _label;
- set => _label = value?.Length switch
- {
- > MaxButtonLabelLength => throw new ArgumentOutOfRangeException(nameof(value), $"Label length must be less or equal to {MaxButtonLabelLength}."),
- 0 => throw new ArgumentOutOfRangeException(nameof(value), "Label length must be at least 1."),
- _ => value
- };
- }
-
- ///
- /// Gets or sets the custom id of the current button.
- ///
- /// length exceeds
- /// length subceeds 1.
- public string CustomId
- {
- get => _customId;
- set => _customId = value?.Length switch
- {
- > ComponentBuilder.MaxCustomIdLength => throw new ArgumentOutOfRangeException(nameof(value), $"Custom Id length must be less or equal to {ComponentBuilder.MaxCustomIdLength}."),
- 0 => throw new ArgumentOutOfRangeException(nameof(value), "Custom Id length must be at least 1."),
- _ => value
- };
- }
-
- ///
- /// Gets or sets the of the current button.
- ///
- public ButtonStyle Style { get; set; }
-
- ///
- /// Gets or sets the of the current button.
- ///
- public IEmote Emote { get; set; }
-
- ///
- /// Gets or sets the url of the current button.
- ///
- public string Url { get; set; }
-
- ///
- /// Gets or sets whether the current button is disabled.
- ///
- public bool IsDisabled { get; set; }
-
- private string _label;
- private string _customId;
-
- ///
- /// Creates a new instance of a .
- ///
- public ButtonBuilder() { }
-
- ///
- /// Creates a new instance of a .
- ///
- /// The label to use on the newly created link button.
- /// The url of this button.
- /// The custom ID of this button.
- /// The custom ID of this button.
- /// The emote of this button.
- /// Disabled this button or not.
- public ButtonBuilder(string label = null, string customId = null, ButtonStyle style = ButtonStyle.Primary, string url = null, IEmote emote = null, bool isDisabled = false)
- {
- CustomId = customId;
- Style = style;
- Url = url;
- Label = label;
- IsDisabled = isDisabled;
- Emote = emote;
- }
-
- ///
- /// Creates a new instance of a from instance of a .
- ///
- public ButtonBuilder(ButtonComponent button)
- {
- CustomId = button.CustomId;
- Style = button.Style;
- Url = button.Url;
- Label = button.Label;
- IsDisabled = button.IsDisabled;
- Emote = button.Emote;
- }
-
- ///
- /// Creates a button with the style.
- ///
- /// The label for this link button.
- /// The url for this link button to go to.
- /// The emote for this link button.
- /// A builder with the newly created button.
- public static ButtonBuilder CreateLinkButton(string label, string url, IEmote emote = null)
- => new ButtonBuilder(label, null, ButtonStyle.Link, url, emote: emote);
-
- ///
- /// Creates a button with the style.
- ///
- /// The label for this danger button.
- /// The custom id for this danger button.
- /// The emote for this danger button.
- /// A builder with the newly created button.
- public static ButtonBuilder CreateDangerButton(string label, string customId, IEmote emote = null)
- => new ButtonBuilder(label, customId, ButtonStyle.Danger, emote: emote);
-
- ///
- /// Creates a button with the style.
- ///
- /// The label for this primary button.
- /// The custom id for this primary button.
- /// The emote for this primary button.
- /// A builder with the newly created button.
- public static ButtonBuilder CreatePrimaryButton(string label, string customId, IEmote emote = null)
- => new ButtonBuilder(label, customId, emote: emote);
-
- ///
- /// Creates a button with the style.
- ///
- /// The label for this secondary button.
- /// The custom id for this secondary button.
- /// The emote for this secondary button.
- /// A builder with the newly created button.
- public static ButtonBuilder CreateSecondaryButton(string label, string customId, IEmote emote = null)
- => new ButtonBuilder(label, customId, ButtonStyle.Secondary, emote: emote);
-
- ///
- /// Creates a button with the style.
- ///
- /// The label for this success button.
- /// The custom id for this success button.
- /// The emote for this success button.
- /// A builder with the newly created button.
- public static ButtonBuilder CreateSuccessButton(string label, string customId, IEmote emote = null)
- => new ButtonBuilder(label, customId, ButtonStyle.Success, emote: emote);
-
- ///
- /// Sets the current buttons label to the specified text.
- ///
- /// The text for the label.
- ///
- /// The current builder.
- public ButtonBuilder WithLabel(string label)
- {
- Label = label;
- return this;
- }
-
- ///
- /// Sets the current buttons style.
- ///
- /// The style for this builders button.
- /// The current builder.
- public ButtonBuilder WithStyle(ButtonStyle style)
- {
- Style = style;
- return this;
- }
-
- ///
- /// Sets the current buttons emote.
- ///
- /// The emote to use for the current button.
- /// The current builder.
- public ButtonBuilder WithEmote(IEmote emote)
- {
- Emote = emote;
- return this;
- }
-
- ///
- /// Sets the current buttons url.
- ///
- /// The url to use for the current button.
- /// The current builder.
- public ButtonBuilder WithUrl(string url)
- {
- Url = url;
- return this;
- }
-
- ///
- /// Sets the custom id of the current button.
- ///
- /// The id to use for the current button.
- ///
- /// The current builder.
- public ButtonBuilder WithCustomId(string id)
- {
- CustomId = id;
- return this;
- }
-
- ///
- /// Sets whether the current button is disabled.
- ///
- /// Whether the current button is disabled or not.
- /// The current builder.
- public ButtonBuilder WithDisabled(bool isDisabled)
- {
- IsDisabled = isDisabled;
- return this;
- }
-
- ///
- /// Builds this builder into a to be used in a .
- ///
- /// A to be used in a .
- /// A button must contain either a or a , but not both.
- /// A button must have an or a .
- /// A link button must contain a URL.
- /// A URL must include a protocol (http or https).
- /// A non-link button must contain a custom id
- public ButtonComponent Build()
- {
- if (string.IsNullOrWhiteSpace(Label) && Emote == null)
- throw new InvalidOperationException("A button must have an Emote or a label!");
-
- if (!(string.IsNullOrWhiteSpace(Url) ^ string.IsNullOrWhiteSpace(CustomId)))
- throw new InvalidOperationException("A button must contain either a URL or a CustomId, but not both!");
-
- if (Style == 0)
- throw new ArgumentException("A button must have a style.", nameof(Style));
-
- if (Style == ButtonStyle.Link)
- {
- if (string.IsNullOrWhiteSpace(Url))
- throw new InvalidOperationException("Link buttons must have a link associated with them");
- UrlValidation.ValidateButton(Url);
- }
- else if (string.IsNullOrWhiteSpace(CustomId))
- throw new InvalidOperationException("Non-link buttons must have a custom id associated with them");
-
- return new ButtonComponent(Style, Label, Emote, CustomId, Url, IsDisabled);
- }
- }
-
- ///
- /// Represents a class used to build 's.
- ///
- public class SelectMenuBuilder
- {
- ///
- /// The max length of a .
- ///
- public const int MaxPlaceholderLength = 100;
-
- ///
- /// The maximum number of values for the and properties.
- ///
- public const int MaxValuesCount = 25;
-
- ///
- /// The maximum number of options a can have.
- ///
- public const int MaxOptionCount = 25;
-
- ///
- /// Gets or sets the custom id of the current select menu.
- ///
- /// length exceeds
- /// length subceeds 1.
- public string CustomId
- {
- get => _customId;
- set => _customId = value?.Length switch
- {
- > ComponentBuilder.MaxCustomIdLength => throw new ArgumentOutOfRangeException(nameof(value), $"Custom Id length must be less or equal to {ComponentBuilder.MaxCustomIdLength}."),
- 0 => throw new ArgumentOutOfRangeException(nameof(value), "Custom Id length must be at least 1."),
- _ => value
- };
- }
-
- ///
- /// Gets or sets the type of the current select menu.
- ///
- /// Type must be a select menu type.
- public ComponentType Type
- {
- get => _type;
- set => _type = value.IsSelectType()
- ? value
- : throw new ArgumentException("Type must be a select menu type.", nameof(value));
- }
-
- ///
- /// Gets or sets the placeholder text of the current select menu.
- ///
- /// length exceeds .
- /// length subceeds 1.
- public string Placeholder
- {
- get => _placeholder;
- set => _placeholder = value?.Length switch
- {
- > MaxPlaceholderLength => throw new ArgumentOutOfRangeException(nameof(value), $"Placeholder length must be less or equal to {MaxPlaceholderLength}."),
- 0 => throw new ArgumentOutOfRangeException(nameof(value), "Placeholder length must be at least 1."),
- _ => value
- };
- }
-
- ///
- /// Gets or sets the minimum values of the current select menu.
- ///
- /// exceeds .
- public int MinValues
- {
- get => _minValues;
- set
- {
- Preconditions.AtMost(value, MaxValuesCount, nameof(MinValues));
- _minValues = value;
- }
- }
-
- ///
- /// Gets or sets the maximum values of the current select menu.
- ///
- /// exceeds .
- public int MaxValues
- {
- get => _maxValues;
- set
- {
- Preconditions.AtMost(value, MaxValuesCount, nameof(MaxValues));
- _maxValues = value;
- }
- }
-
- ///
- /// Gets or sets a collection of for this current select menu.
- ///
- /// count exceeds .
- /// is null.
- public List Options
- {
- get => _options;
- set
- {
- if (value != null)
- Preconditions.AtMost(value.Count, MaxOptionCount, nameof(Options));
-
- _options = value;
- }
- }
-
- ///
- /// Gets or sets whether the current menu is disabled.
- ///
- public bool IsDisabled { get; set; }
-
- ///
- /// Gets or sets the menu's channel types (only valid on s).
- ///
- public List ChannelTypes { get; set; }
-
- public List DefaultValues
- {
- get => _defaultValues;
- set
- {
- if (value != null)
- Preconditions.AtMost(value.Count, MaxOptionCount, nameof(DefaultValues));
-
- _defaultValues = value;
- }
- }
-
- private List _options = new List();
- private int _minValues = 1;
- private int _maxValues = 1;
- private string _placeholder;
- private string _customId;
- private ComponentType _type = ComponentType.SelectMenu;
- private List _defaultValues = new();
-
- ///
- /// Creates a new instance of a .
- ///
- public SelectMenuBuilder() { }
-
- ///
- /// Creates a new instance of a from instance of .
- ///
- public SelectMenuBuilder(SelectMenuComponent selectMenu)
- {
- Placeholder = selectMenu.Placeholder;
- CustomId = selectMenu.CustomId;
- MaxValues = selectMenu.MaxValues;
- MinValues = selectMenu.MinValues;
- IsDisabled = selectMenu.IsDisabled;
- Options = selectMenu.Options?
- .Select(x => new SelectMenuOptionBuilder(x.Label, x.Value, x.Description, x.Emote, x.IsDefault))
- .ToList();
- DefaultValues = selectMenu.DefaultValues?.ToList();
- }
-
- ///
- /// Creates a new instance of a .
- ///
- /// The custom id of this select menu.
- /// The options for this select menu.
- /// The placeholder of this select menu.
- /// The max values of this select menu.
- /// The min values of this select menu.
- /// Disabled this select menu or not.
- /// The of this select menu.
- /// The types of channels this menu can select (only valid on s)
- public SelectMenuBuilder(string customId, List options = null, string placeholder = null, int maxValues = 1, int minValues = 1,
- bool isDisabled = false, ComponentType type = ComponentType.SelectMenu, List channelTypes = null, List defaultValues = null)
- {
- CustomId = customId;
- Options = options;
- Placeholder = placeholder;
- IsDisabled = isDisabled;
- MaxValues = maxValues;
- MinValues = minValues;
- Type = type;
- ChannelTypes = channelTypes ?? new();
- DefaultValues = defaultValues ?? new();
- }
-
- ///
- /// Sets the field CustomId.
- ///
- /// The value to set the field CustomId to.
- ///
- ///
- /// The current builder.
- ///
- public SelectMenuBuilder WithCustomId(string customId)
- {
- CustomId = customId;
- return this;
- }
-
- ///
- /// Sets the field placeholder.
- ///
- /// The value to set the field placeholder to.
- ///
- ///
- /// The current builder.
- ///
- public SelectMenuBuilder WithPlaceholder(string placeholder)
- {
- Placeholder = placeholder;
- return this;
- }
-
- ///
- /// Sets the field minValues.
- ///
- /// The value to set the field minValues to.
- ///
- ///
- /// The current builder.
- ///
- public SelectMenuBuilder WithMinValues(int minValues)
- {
- MinValues = minValues;
- return this;
- }
-
- ///
- /// Sets the field maxValues.
- ///
- /// The value to set the field maxValues to.
- ///
- ///
- /// The current builder.
- ///
- public SelectMenuBuilder WithMaxValues(int maxValues)
- {
- MaxValues = maxValues;
- return this;
- }
-
- ///
- /// Sets the field options.
- ///
- /// The value to set the field options to.
- ///
- ///
- /// The current builder.
- ///
- public SelectMenuBuilder WithOptions(List options)
- {
- Options = options;
- return this;
- }
-
- ///
- /// Add one option to menu options.
- ///
- /// The option builder class containing the option properties.
- /// Options count reached .
- ///
- /// The current builder.
- ///
- public SelectMenuBuilder AddOption(SelectMenuOptionBuilder option)
- {
- Options ??= new();
-
- if (Options.Count >= MaxOptionCount)
- throw new InvalidOperationException($"Options count reached {MaxOptionCount}.");
-
- Options.Add(option);
- return this;
- }
-
- ///
- /// Add one option to menu options.
- ///
- /// The label for this option.
- /// The value of this option.
- /// The description of this option.
- /// The emote of this option.
- /// Render this option as selected by default or not.
- /// Options count reached .
- ///
- /// The current builder.
- ///
- public SelectMenuBuilder AddOption(string label, string value, string description = null, IEmote emote = null, bool? isDefault = null)
- {
- AddOption(new SelectMenuOptionBuilder(label, value, description, emote, isDefault));
- return this;
- }
-
- ///
- /// Add one default value to menu options.
- ///
- /// The id of an entity to add.
- /// The type of an entity to add.
- /// Default values count reached .
- ///
- /// The current builder.
- ///
- public SelectMenuBuilder AddDefaultValue(ulong id, SelectDefaultValueType type)
- => AddDefaultValue(new(id, type));
-
- ///
- /// Add one default value to menu options.
- ///
- /// The default value to add.
- /// Default values count reached .
- ///
- /// The current builder.
- ///
- public SelectMenuBuilder AddDefaultValue(SelectMenuDefaultValue value)
- {
- if (DefaultValues.Count >= MaxOptionCount)
- throw new InvalidOperationException($"Options count reached {MaxOptionCount}.");
-
- DefaultValues.Add(value);
- return this;
- }
-
- ///
- /// Sets the field default values.
- ///
- /// The value to set the field default values to.
- ///
- /// The current builder.
- ///
- public SelectMenuBuilder WithDefaultValues(params SelectMenuDefaultValue[] defaultValues)
- {
- DefaultValues = defaultValues?.ToList();
- return this;
- }
-
- ///
- /// Sets whether the current menu is disabled.
- ///
- /// Whether the current menu is disabled or not.
- ///
- /// The current builder.
- ///
- public SelectMenuBuilder WithDisabled(bool isDisabled)
- {
- IsDisabled = isDisabled;
- return this;
- }
-
- ///
- /// Sets the menu's current type.
- ///
- /// The type of the menu.
- ///
- /// The current builder.
- ///
- public SelectMenuBuilder WithType(ComponentType type)
- {
- Type = type;
- return this;
- }
-
- ///
- /// Sets the menus valid channel types (only for s).
- ///
- /// The valid channel types of the menu.
- ///
- /// The current builder.
- ///
- public SelectMenuBuilder WithChannelTypes(List channelTypes)
- {
- ChannelTypes = channelTypes;
- return this;
- }
-
- ///
- /// Sets the menus valid channel types (only for s).
- ///
- /// The valid channel types of the menu.
- ///
- /// The current builder.
- ///
- public SelectMenuBuilder WithChannelTypes(params ChannelType[] channelTypes)
- {
- ChannelTypes = channelTypes is null
- ? ChannelTypeUtils.AllChannelTypes()
- : channelTypes.ToList();
- return this;
- }
-
- ///
- /// Builds a
- ///
- /// The newly built
- public SelectMenuComponent Build()
- {
- var options = Options?.Select(x => x.Build()).ToList();
-
- return new SelectMenuComponent(CustomId, options, Placeholder, MinValues, MaxValues, IsDisabled, Type, ChannelTypes, DefaultValues);
- }
- }
-
- ///
- /// Represents a class used to build 's.
- ///
- public class SelectMenuOptionBuilder
- {
- ///
- /// The maximum length of a .
- ///
- public const int MaxSelectLabelLength = 100;
-
- ///
- /// The maximum length of a .
- ///
- public const int MaxDescriptionLength = 100;
-
- ///
- /// The maximum length of a .
- ///
- public const int MaxSelectValueLength = 100;
-
- ///
- /// Gets or sets the label of the current select menu.
- ///
- /// length exceeds
- /// length subceeds 1.
- public string Label
- {
- get => _label;
- set => _label = value?.Length switch
- {
- > MaxSelectLabelLength => throw new ArgumentOutOfRangeException(nameof(value), $"Label length must be less or equal to {MaxSelectLabelLength}."),
- 0 => throw new ArgumentOutOfRangeException(nameof(value), "Label length must be at least 1."),
- _ => value
- };
- }
-
- ///
- /// Gets or sets the value of the current select menu.
- ///
- /// length exceeds .
- /// length subceeds 1.
- public string Value
- {
- get => _value;
- set => _value = value?.Length switch
- {
- > MaxSelectValueLength => throw new ArgumentOutOfRangeException(nameof(value), $"Value length must be less or equal to {MaxSelectValueLength}."),
- 0 => throw new ArgumentOutOfRangeException(nameof(value), "Value length must be at least 1."),
- _ => value
- };
- }
-
- ///
- /// Gets or sets this menu options description.
- ///
- /// length exceeds .
- /// length subceeds 1.
- public string Description
- {
- get => _description;
- set => _description = value?.Length switch
- {
- > MaxDescriptionLength => throw new ArgumentOutOfRangeException(nameof(value), $"Description length must be less or equal to {MaxDescriptionLength}."),
- 0 => throw new ArgumentOutOfRangeException(nameof(value), "Description length must be at least 1."),
- _ => value
- };
- }
-
- ///
- /// Gets or sets the emote of this option.
- ///
- public IEmote Emote { get; set; }
-
- ///
- /// Gets or sets the whether or not this option will render selected by default.
- ///
- public bool? IsDefault { get; set; }
-
- private string _label;
- private string _value;
- private string _description;
-
- ///
- /// Creates a new instance of a .
- ///
- public SelectMenuOptionBuilder() { }
-
- ///
- /// Creates a new instance of a .
- ///
- /// The label for this option.
- /// The value of this option.
- /// The description of this option.
- /// The emote of this option.
- /// Render this option as selected by default or not.
- public SelectMenuOptionBuilder(string label, string value, string description = null, IEmote emote = null, bool? isDefault = null)
- {
- Label = label;
- Value = value;
- Description = description;
- Emote = emote;
- IsDefault = isDefault;
- }
-
- ///
- /// Creates a new instance of a from instance of a .
- ///
- public SelectMenuOptionBuilder(SelectMenuOption option)
- {
- Label = option.Label;
- Value = option.Value;
- Description = option.Description;
- Emote = option.Emote;
- IsDefault = option.IsDefault;
- }
-
- ///
- /// Sets the field label.
- ///
- /// The value to set the field label to.
- ///
- ///
- /// The current builder.
- ///
- public SelectMenuOptionBuilder WithLabel(string label)
- {
- Label = label;
- return this;
- }
-
- ///
- /// Sets the field value.
- ///
- /// The value to set the field value to.
- ///
- ///
- /// The current builder.
- ///
- public SelectMenuOptionBuilder WithValue(string value)
- {
- Value = value;
- return this;
- }
-
- ///
- /// Sets the field description.
- ///
- /// The value to set the field description to.
- ///
- ///
- /// The current builder.
- ///
- public SelectMenuOptionBuilder WithDescription(string description)
- {
- Description = description;
- return this;
- }
-
- ///
- /// Sets the field emote.
- ///
- /// The value to set the field emote to.
- ///
- /// The current builder.
- ///
- public SelectMenuOptionBuilder WithEmote(IEmote emote)
- {
- Emote = emote;
- return this;
- }
-
- ///
- /// Sets the field default.
- ///
- /// The value to set the field default to.
- ///
- /// The current builder.
- ///
- public SelectMenuOptionBuilder WithDefault(bool isDefault)
- {
- IsDefault = isDefault;
- return this;
- }
-
- ///
- /// Builds a .
- ///
- /// The newly built .
- public SelectMenuOption Build()
- {
- if (string.IsNullOrWhiteSpace(Label))
- throw new ArgumentNullException(nameof(Label), "Option must have a label.");
-
- Preconditions.AtMost(Label.Length, MaxSelectLabelLength, nameof(Label), $"Label length must be less or equal to {MaxSelectLabelLength}.");
-
- if (string.IsNullOrWhiteSpace(Value))
- throw new ArgumentNullException(nameof(Value), "Option must have a value.");
-
- Preconditions.AtMost(Value.Length, MaxSelectValueLength, nameof(Value), $"Value length must be less or equal to {MaxSelectValueLength}.");
-
- return new SelectMenuOption(Label, Value, Description, Emote, IsDefault);
- }
- }
-
- public class TextInputBuilder
- {
- ///
- /// The max length of a .
- ///
- public const int MaxPlaceholderLength = 100;
- public const int LargestMaxLength = 4000;
-
- ///
- /// Gets or sets the custom id of the current text input.
- ///
- /// length exceeds
- /// length subceeds 1.
- public string CustomId
- {
- get => _customId;
- set => _customId = value?.Length switch
- {
- > ComponentBuilder.MaxCustomIdLength => throw new ArgumentOutOfRangeException(nameof(value), $"Custom Id length must be less or equal to {ComponentBuilder.MaxCustomIdLength}."),
- 0 => throw new ArgumentOutOfRangeException(nameof(value), "Custom Id length must be at least 1."),
- _ => value
- };
- }
-
- ///
- /// Gets or sets the style of the current text input.
- ///
- public TextInputStyle Style { get; set; } = TextInputStyle.Short;
-
- ///
- /// Gets or sets the label of the current text input.
- ///
- public string Label { get; set; }
-
- ///
- /// Gets or sets the placeholder of the current text input.
- ///
- /// is longer than characters
- public string Placeholder
- {
- get => _placeholder;
- set => _placeholder = (value?.Length ?? 0) <= MaxPlaceholderLength
- ? value
- : throw new ArgumentException($"Placeholder cannot have more than {MaxPlaceholderLength} characters.");
- }
-
- ///
- /// Gets or sets the minimum length of the current text input.
- ///
- /// is less than 0.
- /// is greater than .
- /// is greater than .
- public int? MinLength
- {
- get => _minLength;
- set
- {
- if (value < 0)
- throw new ArgumentOutOfRangeException(nameof(value), $"MinLength must not be less than 0");
- if (value > LargestMaxLength)
- throw new ArgumentOutOfRangeException(nameof(value), $"MinLength must not be greater than {LargestMaxLength}");
- if (value > (MaxLength ?? LargestMaxLength))
- throw new ArgumentOutOfRangeException(nameof(value), $"MinLength must be less than MaxLength");
- _minLength = value;
- }
- }
-
- ///
- /// Gets or sets the maximum length of the current text input.
- ///
- /// is less than 0.
- /// is greater than .
- /// is less than .
- public int? MaxLength
- {
- get => _maxLength;
- set
- {
- if (value < 0)
- throw new ArgumentOutOfRangeException(nameof(value), $"MaxLength must not be less than 0");
- if (value > LargestMaxLength)
- throw new ArgumentOutOfRangeException(nameof(value), $"MaxLength most not be greater than {LargestMaxLength}");
- if (value < (MinLength ?? -1))
- throw new ArgumentOutOfRangeException(nameof(value), $"MaxLength must be greater than MinLength ({MinLength})");
- _maxLength = value;
- }
- }
-
- ///
- /// Gets or sets whether the user is required to input text.
- ///
- public bool? Required { get; set; }
-
- ///
- /// Gets or sets the default value of the text input.
- ///
- /// .Length is less than 0.
- ///
- /// .Length is greater than or .
- ///
- ///
- /// is and contains a new line character.
- ///
- public string Value
- {
- get => _value;
- set
- {
- if (value?.Length > (MaxLength ?? LargestMaxLength))
- throw new ArgumentOutOfRangeException(nameof(value), $"Value must not be longer than {MaxLength ?? LargestMaxLength}.");
- if (value?.Length < (MinLength ?? 0))
- throw new ArgumentOutOfRangeException(nameof(value), $"Value must not be shorter than {MinLength}");
-
- _value = value;
- }
- }
-
- private string _customId;
- private int? _maxLength;
- private int? _minLength;
- private string _placeholder;
- private string _value;
-
- ///
- /// Creates a new instance of a .
- ///
- /// The text input's label.
- /// The text input's style.
- /// The text input's custom id.
- /// The text input's placeholder.
- /// The text input's minimum length.
- /// The text input's maximum length.
- /// The text input's required value.
- public TextInputBuilder(string label, string customId, TextInputStyle style = TextInputStyle.Short, string placeholder = null,
- int? minLength = null, int? maxLength = null, bool? required = null, string value = null)
- {
- Label = label;
- Style = style;
- CustomId = customId;
- Placeholder = placeholder;
- MinLength = minLength;
- MaxLength = maxLength;
- Required = required;
- Value = value;
- }
-
- ///
- /// Creates a new instance of a .
- ///
- public TextInputBuilder()
- {
-
- }
-
- ///
- /// Sets the label of the current builder.
- ///
- /// The value to set.
- /// The current builder.
- public TextInputBuilder WithLabel(string label)
- {
- Label = label;
- return this;
- }
-
- ///
- /// Sets the style of the current builder.
- ///
- /// The value to set.
- /// The current builder.
- public TextInputBuilder WithStyle(TextInputStyle style)
- {
- Style = style;
- return this;
- }
-
- ///
- /// Sets the custom id of the current builder.
- ///
- /// The value to set.
- /// The current builder.
- public TextInputBuilder WithCustomId(string customId)
- {
- CustomId = customId;
- return this;
- }
-
- ///
- /// Sets the placeholder of the current builder.
- ///
- /// The value to set.
- /// The current builder.
- public TextInputBuilder WithPlaceholder(string placeholder)
- {
- Placeholder = placeholder;
- return this;
- }
-
- ///
- /// Sets the value of the current builder.
- ///
- /// The value to set
- /// The current builder.
- public TextInputBuilder WithValue(string value)
- {
- Value = value;
- return this;
- }
-
- ///
- /// Sets the minimum length of the current builder.
- ///
- /// The value to set.
- /// The current builder.
- public TextInputBuilder WithMinLength(int minLength)
- {
- MinLength = minLength;
- return this;
- }
-
- ///
- /// Sets the maximum length of the current builder.
- ///
- /// The value to set.
- /// The current builder.
- public TextInputBuilder WithMaxLength(int maxLength)
- {
- MaxLength = maxLength;
- return this;
- }
-
- ///
- /// Sets the required value of the current builder.
- ///
- /// The value to set.
- /// The current builder.
- public TextInputBuilder WithRequired(bool required)
- {
- Required = required;
- return this;
- }
-
- public TextInputComponent Build()
- {
- if (string.IsNullOrEmpty(CustomId))
- throw new ArgumentException("TextInputComponents must have a custom id.", nameof(CustomId));
- if (string.IsNullOrWhiteSpace(Label))
- throw new ArgumentException("TextInputComponents must have a label.", nameof(Label));
- if (Style == TextInputStyle.Short && Value?.Contains('\n') == true)
- throw new ArgumentException($"Value must not contain new line characters when style is {TextInputStyle.Short}.", nameof(Value));
-
- return new TextInputComponent(CustomId, Label, Placeholder, MinLength, MaxLength, Style, Required, Value);
- }
- }
-}
diff --git a/src/Discord.Net.Rest/API/Common/ButtonComponent.cs b/src/Discord.Net.Rest/API/Common/ButtonComponent.cs
index 7f737d7a..f820fc02 100644
--- a/src/Discord.Net.Rest/API/Common/ButtonComponent.cs
+++ b/src/Discord.Net.Rest/API/Common/ButtonComponent.cs
@@ -25,6 +25,9 @@ namespace Discord.API
[JsonProperty("disabled")]
public Optional Disabled { get; set; }
+ [JsonProperty("sku_id")]
+ public Optional SkuId { get; set; }
+
public ButtonComponent() { }
public ButtonComponent(Discord.ButtonComponent c)
@@ -35,6 +38,7 @@ namespace Discord.API
CustomId = c.CustomId;
Url = c.Url;
Disabled = c.IsDisabled;
+ SkuId = c.SkuId ?? Optional.Unspecified;
if (c.Emote != null)
{
diff --git a/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs
index fd9aa07c..c0da0228 100644
--- a/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs
+++ b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs
@@ -181,7 +181,8 @@ namespace Discord.Rest
: null,
parsed.CustomId.GetValueOrDefault(),
parsed.Url.GetValueOrDefault(),
- parsed.Disabled.GetValueOrDefault());
+ parsed.Disabled.GetValueOrDefault(),
+ parsed.SkuId.ToNullable());
}
case ComponentType.SelectMenu or ComponentType.ChannelSelect or ComponentType.RoleSelect or ComponentType.MentionableSelect or ComponentType.UserSelect:
{
diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs
index 930c6ca1..a9ada8de 100644
--- a/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs
+++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs
@@ -215,7 +215,8 @@ namespace Discord.WebSocket
: null,
parsed.CustomId.GetValueOrDefault(),
parsed.Url.GetValueOrDefault(),
- parsed.Disabled.GetValueOrDefault());
+ parsed.Disabled.GetValueOrDefault(),
+ parsed.SkuId.ToNullable());
}
case ComponentType.SelectMenu:
{