diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ButtonBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ButtonBuilder.cs
index 0b4b5f3330..06ddbd5e08 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ButtonBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ButtonBuilder.cs
@@ -9,6 +9,7 @@ namespace Discord;
///
public class ButtonBuilder : IInteractableComponentBuilder
{
+ ///
public ComponentType Type => ComponentType.Button;
///
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/LabelBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/LabelBuilder.cs
new file mode 100644
index 0000000000..627b9df922
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/LabelBuilder.cs
@@ -0,0 +1,88 @@
+using System;
+using System.Collections.Immutable;
+using System.Linq;
+
+namespace Discord;
+
+public class LabelBuilder : IMessageComponentBuilder
+{
+ ///
+ public ImmutableArray SupportedComponentTypes { get; } =
+ [
+ ComponentType.SelectMenu,
+ ComponentType.TextInput,
+ ];
+
+ ///
+ /// The maximum length of the label.
+ ///
+ public const int MaxLabelLength = 100;
+
+ ///
+ /// The maximum length of the description.
+ ///
+ public const int MaxDescriptionLength = 69420; // TODO: set to the real limit
+
+ ///
+ public ComponentType Type => ComponentType.Label;
+
+ ///
+ public int? Id { get; set; }
+
+ ///
+ ///
+ ///
+ public string Label { get; set; }
+
+ ///
+ ///
+ ///
+ public string Description { get; set; }
+
+ public IMessageComponentBuilder Component { get; set; }
+
+ ///
+ /// Initializes a new .
+ ///
+ public LabelBuilder() { }
+
+ ///
+ /// Initializes a new with the specified content.
+ ///
+ public LabelBuilder(string label, IMessageComponentBuilder component, string description = null, int? id = null)
+ {
+ Id = id;
+ Label = label;
+ Component = component;
+ Description = description;
+ }
+
+ ///
+ /// Initializes a new from existing component.
+ ///
+ public LabelBuilder(LabelComponent label)
+ {
+ Label = label.Label;
+ Description = label.Description;
+ Id = label.Id;
+ Component = label.Component.ToBuilder();
+ }
+
+ public LabelComponent Build()
+ {
+ Preconditions.NotNullOrWhitespace(Label, nameof(Label));
+ Preconditions.AtMost(Label.Length, MaxLabelLength, nameof(Label));
+
+ Preconditions.AtMost(Description?.Length ?? 0, MaxDescriptionLength, nameof(Description));
+
+ Preconditions.NotNull(Component, nameof(Component));
+
+ if (SupportedComponentTypes.All(x => Component.Type != x))
+ throw new InvalidOperationException($"Component can only be {nameof(SelectMenuBuilder)} or {nameof(TextInputBuilder)}.");
+
+ return new LabelComponent(Id, Label, Description, Component.Build());
+ }
+
+ ///
+ IMessageComponent IMessageComponentBuilder.Build() => Build();
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/MediaGalleryBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/MediaGalleryBuilder.cs
index a6fcb65b55..94e3cf8f73 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/MediaGalleryBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/MediaGalleryBuilder.cs
@@ -18,7 +18,7 @@ public class MediaGalleryBuilder : IMessageComponentBuilder
///
public int? Id { get; set; }
- private List _items = new();
+ private List _items = [];
///
/// Initializes a new instance of the .
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentType.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentType.cs
index 5d5cce5a77..1b57fd8be4 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentType.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentType.cs
@@ -45,18 +45,44 @@ public enum ComponentType
///
ChannelSelect = 8,
+ ///
+ /// A container to display text alongside an accessory component.
+ ///
Section = 9,
+ ///
+ /// A component displaying Markdown text.
+ ///
TextDisplay = 10,
+ ///
+ /// A small image that can be used as an accessory.
+ ///
Thumbnail = 11,
+ ///
+ /// A component displaying images and other media.
+ ///
MediaGallery = 12,
+ ///
+ /// A component displaying an attached file.
+ ///
File = 13,
+ ///
+ /// A component to add vertical padding between other components.
+ ///
Separator = 14,
+ ///
+ /// A container that visually groups a set of components.
+ ///
Container = 17,
+
+ ///
+ ///
+ ///
+ Label = 18,
}
}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/LabelComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/LabelComponent.cs
new file mode 100644
index 0000000000..1f8a8b2d49
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/LabelComponent.cs
@@ -0,0 +1,35 @@
+namespace Discord;
+
+public class LabelComponent : IMessageComponent
+{
+ ///
+ public ComponentType Type => ComponentType.Label;
+
+ ///
+ public int? Id { get; private set; }
+
+ ///
+ ///
+ ///
+ public string Label { get; private set; }
+
+ ///
+ ///
+ ///
+ public string Description { get; private set; }
+
+ ///
+ ///
+ ///
+ public IMessageComponent Component { get; private set; }
+
+ internal LabelComponent(int? id, string label, string description, IMessageComponent component)
+ {
+ Id = id;
+ Label = label;
+ Description = description;
+ Component = component;
+ }
+
+ public IMessageComponentBuilder ToBuilder() => throw new System.NotImplementedException();
+}
diff --git a/src/Discord.Net.Rest/API/Common/LabelComponent.cs b/src/Discord.Net.Rest/API/Common/LabelComponent.cs
new file mode 100644
index 0000000000..f27875e23a
--- /dev/null
+++ b/src/Discord.Net.Rest/API/Common/LabelComponent.cs
@@ -0,0 +1,38 @@
+using Discord.Rest;
+using Newtonsoft.Json;
+
+namespace Discord.API;
+
+internal class LabelComponent : IMessageComponent
+{
+ [JsonProperty("type")]
+ public ComponentType Type { get; set; }
+
+ [JsonProperty("id")]
+ public Optional Id { get; }
+
+ [JsonProperty("label")]
+ public string Label { get; set; }
+
+ [JsonProperty("description")]
+ public string Description { get; set; }
+
+ [JsonProperty("component")]
+ public IMessageComponent Component { get; set; }
+
+ public LabelComponent() {}
+
+ public LabelComponent(Discord.LabelComponent label)
+ {
+ Type = label.Type;
+ Id = label.Id ?? Optional.Unspecified;
+ Label = label.Label;
+ Description = label.Description;
+ Component = label.Component.ToModel();
+ }
+
+ public IMessageComponentBuilder ToBuilder() => null;
+
+ [JsonIgnore]
+ int? IMessageComponent.Id => Id.ToNullable();
+}
diff --git a/src/Discord.Net.Rest/Extensions/MessageComponentExtension.cs b/src/Discord.Net.Rest/Extensions/MessageComponentExtension.cs
index 96893f7e4b..9420f9a363 100644
--- a/src/Discord.Net.Rest/Extensions/MessageComponentExtension.cs
+++ b/src/Discord.Net.Rest/Extensions/MessageComponentExtension.cs
@@ -41,6 +41,9 @@ internal static IMessageComponent ToModel(this IMessageComponent component)
case ContainerComponent container:
return new API.ContainerComponent(container);
+
+ case LabelComponent label:
+ return new API.LabelComponent(label);
}
return null;
@@ -173,6 +176,12 @@ internal static IMessageComponent ToEntity(this IMessageComponent component)
parsed.Id.ToNullable());
}
+ case ComponentType.Label:
+ {
+ var parsed = (API.LabelComponent)component;
+ return new LabelComponent(parsed.Id.ToNullable(), parsed.Label, parsed.Description, parsed.Component.ToEntity());
+ }
+
default:
return null;
}
diff --git a/src/Discord.Net.Rest/Net/Converters/MessageComponentConverter.cs b/src/Discord.Net.Rest/Net/Converters/MessageComponentConverter.cs
index 0f229f33ef..aed0a5ac39 100644
--- a/src/Discord.Net.Rest/Net/Converters/MessageComponentConverter.cs
+++ b/src/Discord.Net.Rest/Net/Converters/MessageComponentConverter.cs
@@ -61,6 +61,9 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
case ComponentType.Container:
messageComponent = new API.ContainerComponent();
break;
+ case ComponentType.Label:
+ messageComponent = new API.LabelComponent();
+ break;
}
serializer.Populate(jsonObject.CreateReader(), messageComponent);
return messageComponent;