From 3c9be4a7aeba36c88d51b9c209d4728ac4b0fc8a Mon Sep 17 00:00:00 2001
From: Misha133 <mihagribkov133@gmail.com>
Date: Sun, 15 Dec 2024 19:19:36 +0300
Subject: [PATCH 01/15] oh well. at least it runs

---
 .../MessageComponents/ActionRowComponent.cs   | 36 +++++++++----------
 .../Builders/ButtonBuilder.cs                 |  8 +++--
 .../Builders/ComponentBuilder.cs              |  2 +-
 .../Builders/SelectMenuBuilder.cs             |  8 +++--
 .../Builders/TextInputBuilder.cs              |  7 ++--
 .../MessageComponents/ButtonComponent.cs      |  8 +++--
 .../MessageComponents/ComponentType.cs        |  8 +++++
 .../IInteractableComponent.cs                 |  9 +++++
 .../MessageComponents/IMessageComponent.cs    | 22 +++++-------
 .../MessageComponents/SelectMenuComponent.cs  | 12 ++++---
 .../MessageComponents/TextInputComponent.cs   | 10 ++++--
 .../Entities/Interactions/Modals/Modal.cs     | 11 +-----
 .../Interactions/Modals/ModalBuilder.cs       |  8 ++---
 .../API/Common/ActionRowComponent.cs          |  6 +++-
 .../API/Common/ButtonComponent.cs             | 11 ++++--
 .../API/Common/SelectMenuComponent.cs         |  8 ++++-
 .../API/Common/TextInputComponent.cs          |  9 ++++-
 .../API/Common/UnfurledMediaItem.cs           |  9 +++++
 .../RestMessageComponentData.cs               |  2 +-
 .../Interactions/Modals/RestModalData.cs      |  5 +--
 .../Entities/Messages/RestMessage.cs          | 13 ++++---
 .../SocketMessageComponentData.cs             |  4 +--
 .../Interaction/Modals/SocketModalData.cs     |  5 +--
 .../Entities/Messages/SocketMessage.cs        |  4 ++-
 24 files changed, 143 insertions(+), 82 deletions(-)
 create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/IInteractableComponent.cs
 create mode 100644 src/Discord.Net.Rest/API/Common/UnfurledMediaItem.cs

diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ActionRowComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ActionRowComponent.cs
index 202a5687ff..bca11c4824 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ActionRowComponent.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ActionRowComponent.cs
@@ -1,27 +1,27 @@
 using System.Collections.Generic;
 
-namespace Discord
+namespace Discord;
+
+/// <summary>
+///     Represents a <see cref="IMessageComponent"/> Row for child components to live in.
+/// </summary>
+public class ActionRowComponent : IMessageComponent
 {
-    /// <summary>
-    ///     Represents a <see cref="IMessageComponent"/> Row for child components to live in.
-    /// </summary>
-    public class ActionRowComponent : IMessageComponent
-    {
-        /// <inheritdoc/>
-        public ComponentType Type => ComponentType.ActionRow;
+    /// <inheritdoc/>
+    public ComponentType Type => ComponentType.ActionRow;
 
-        /// <summary>
-        ///     Gets the child components in this row.
-        /// </summary>
-        public IReadOnlyCollection<IMessageComponent> Components { get; internal set; }
+    /// <inheritdoc/>
+    public int? Id { get; internal set; }
 
-        internal ActionRowComponent() { }
+    /// <summary>
+    ///     Gets the child components in this row.
+    /// </summary>
+    public IReadOnlyCollection<IMessageComponent> Components { get; internal set; }
 
-        internal ActionRowComponent(List<IMessageComponent> components)
-        {
-            Components = components;
-        }
+    internal ActionRowComponent() { }
 
-        string IMessageComponent.CustomId => null;
+    internal ActionRowComponent(List<IMessageComponent> components)
+    {
+        Components = components;
     }
 }
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 4522eb0629..058f4e473c 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ButtonBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ButtonBuilder.cs
@@ -74,6 +74,8 @@ public string CustomId
     /// </remarks>
     public ulong? SkuId { get; set; }
 
+    public int? Id { get; set; }
+
     private string _label;
     private string _customId;
 
@@ -92,7 +94,7 @@ public ButtonBuilder() { }
     /// <param name="emote">The emote of this button.</param>
     /// <param name="isDisabled">Disabled this button or not.</param>
     /// <param name="skuId">The sku id of this button.</param>
-    public ButtonBuilder(string label = null, string customId = null, ButtonStyle style = ButtonStyle.Primary, string url = null, IEmote emote = null, bool isDisabled = false, ulong? skuId = null)
+    public ButtonBuilder(string label = null, string customId = null, ButtonStyle style = ButtonStyle.Primary, string url = null, IEmote emote = null, bool isDisabled = false, ulong? skuId = null, int? id = null)
     {
         CustomId = customId;
         Style = style;
@@ -101,6 +103,7 @@ public ButtonBuilder(string label = null, string customId = null, ButtonStyle st
         IsDisabled = isDisabled;
         Emote = emote;
         SkuId = skuId;
+        Id = id;
     }
 
     /// <summary>
@@ -115,6 +118,7 @@ public ButtonBuilder(ButtonComponent button)
         IsDisabled = button.IsDisabled;
         Emote = button.Emote;
         SkuId = button.SkuId;
+        Id = button.Id;
     }
 
     /// <summary>
@@ -316,6 +320,6 @@ public ButtonComponent Build()
             break;
         }
 
-        return new ButtonComponent(Style, Label, Emote, CustomId, Url, IsDisabled, SkuId);
+        return new ButtonComponent(Style, Label, Emote, CustomId, Url, IsDisabled, SkuId, Id);
     }
 }
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilder.cs
index eeb7db8596..b4c60ba9f3 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilder.cs
@@ -98,7 +98,7 @@ public ComponentBuilder RemoveComponentsOfType(ComponentType t)
     /// <returns>The current builder.</returns>
     public ComponentBuilder RemoveComponent(string customId)
     {
-        this.ActionRows.ForEach(ar => ar.Components.RemoveAll(c => c.CustomId == customId));
+        this.ActionRows.ForEach(ar => ar.Components.RemoveAll(c => c is IInteractableComponent ic && ic.CustomId == customId));
         return this;
     }
 
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SelectMenuBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SelectMenuBuilder.cs
index 000a1c6203..41c49e93f0 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SelectMenuBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SelectMenuBuilder.cs
@@ -136,6 +136,8 @@ public List<SelectMenuDefaultValue> DefaultValues
         }
     }
 
+    public int? Id { get; set; }
+
     private List<SelectMenuOptionBuilder> _options = new List<SelectMenuOptionBuilder>();
     private int _minValues = 1;
     private int _maxValues = 1;
@@ -163,6 +165,7 @@ public SelectMenuBuilder(SelectMenuComponent selectMenu)
            .Select(x => new SelectMenuOptionBuilder(x.Label, x.Value, x.Description, x.Emote, x.IsDefault))
            .ToList();
         DefaultValues = selectMenu.DefaultValues?.ToList();
+        Id = selectMenu.Id;
     }
 
     /// <summary>
@@ -177,7 +180,7 @@ public SelectMenuBuilder(SelectMenuComponent selectMenu)
     /// <param name="type">The <see cref="ComponentType"/> of this select menu.</param>
     /// <param name="channelTypes">The types of channels this menu can select (only valid on <see cref="ComponentType.ChannelSelect"/>s)</param>
     public SelectMenuBuilder(string customId, List<SelectMenuOptionBuilder> options = null, string placeholder = null, int maxValues = 1, int minValues = 1,
-        bool isDisabled = false, ComponentType type = ComponentType.SelectMenu, List<ChannelType> channelTypes = null, List<SelectMenuDefaultValue> defaultValues = null)
+        bool isDisabled = false, ComponentType type = ComponentType.SelectMenu, List<ChannelType> channelTypes = null, List<SelectMenuDefaultValue> defaultValues = null, int? id = null)
     {
         CustomId = customId;
         Options = options;
@@ -188,6 +191,7 @@ public SelectMenuBuilder(string customId, List<SelectMenuOptionBuilder> options
         Type = type;
         ChannelTypes = channelTypes ?? new();
         DefaultValues = defaultValues ?? new();
+        Id = id;
     }
 
     /// <summary>
@@ -401,6 +405,6 @@ public SelectMenuComponent Build()
     {
         var options = Options?.Select(x => x.Build()).ToList();
 
-        return new SelectMenuComponent(CustomId, options, Placeholder, MinValues, MaxValues, IsDisabled, Type, ChannelTypes, DefaultValues);
+        return new SelectMenuComponent(CustomId, options, Placeholder, MinValues, MaxValues, IsDisabled, Type, Id, ChannelTypes, DefaultValues);
     }
 }
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextInputBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextInputBuilder.cs
index b675980bf6..2efe92b63d 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextInputBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextInputBuilder.cs
@@ -99,6 +99,8 @@ public int? MaxLength
     /// </summary>
     public bool? Required { get; set; }
 
+    public int? Id { get; set; }
+
     /// <summary>
     ///     Gets or sets the default value of the text input.
     /// </summary>
@@ -140,7 +142,7 @@ public string Value
     /// <param name="maxLength">The text input's maximum length.</param>
     /// <param name="required">The text input's required value.</param>
     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)
+        int? minLength = null, int? maxLength = null, bool? required = null, string value = null, int? id = null)
     {
         Label = label;
         Style = style;
@@ -150,6 +152,7 @@ public TextInputBuilder(string label, string customId, TextInputStyle style = Te
         MaxLength = maxLength;
         Required = required;
         Value = value;
+        Id = id;
     }
 
     /// <summary>
@@ -257,6 +260,6 @@ public TextInputComponent Build()
         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);
+        return new TextInputComponent(CustomId, Label, Placeholder, MinLength, MaxLength, Style, Required, Value, Id);
     }
 }
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ButtonComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ButtonComponent.cs
index c387f9ad53..5a850e2015 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ButtonComponent.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ButtonComponent.cs
@@ -3,11 +3,14 @@ namespace Discord;
 /// <summary>
 ///     Represents a <see cref="IMessageComponent"/> Button.
 /// </summary>
-public class ButtonComponent : IMessageComponent
+public class ButtonComponent : IInteractableComponent
 {
     /// <inheritdoc/>
     public ComponentType Type => ComponentType.Button;
 
+    /// <inheritdoc/>
+    public int? Id { get; }
+
     /// <summary>
     ///     Gets the <see cref="ButtonStyle"/> of this button, example buttons with each style can be found <see href="https://discord.com/assets/7bb017ce52cfd6575e21c058feb3883b.png">Here</see>.
     /// </summary>
@@ -56,9 +59,10 @@ public class ButtonComponent : IMessageComponent
     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)
+    internal ButtonComponent(ButtonStyle style, string label, IEmote emote, string customId, string url, bool isDisabled, ulong? skuId, int? id)
     {
         Style = style;
+        Id = id;
         Label = label;
         Emote = emote;
         CustomId = customId;
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentType.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentType.cs
index 0ad3f741a1..3e80deada9 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentType.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentType.cs
@@ -44,5 +44,13 @@ public enum ComponentType
         ///     A select menu for picking from channels.
         /// </summary>
         ChannelSelect = 8,
+
+        TextDisplay = 10,
+
+        MediaGallery = 12,
+
+        File = 13,
+
+        Separator = 14
     }
 }
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/IInteractableComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/IInteractableComponent.cs
new file mode 100644
index 0000000000..61c2265889
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/IInteractableComponent.cs
@@ -0,0 +1,9 @@
+namespace Discord;
+
+public interface IInteractableComponent : IMessageComponent
+{
+    /// <summary>
+    ///     Gets the custom id of the component if possible; otherwise <see langword="null"/>.
+    /// </summary>
+    string CustomId { get; }
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/IMessageComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/IMessageComponent.cs
index 9366a44d69..6bc1c07d0d 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/IMessageComponent.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/IMessageComponent.cs
@@ -1,18 +1,14 @@
-namespace Discord
+namespace Discord;
+
+/// <summary>
+///     Represents a message component on a message.
+/// </summary>
+public interface IMessageComponent
 {
     /// <summary>
-    ///     Represents a message component on a message.
+    ///     Gets the <see cref="ComponentType"/> of this Message Component.
     /// </summary>
-    public interface IMessageComponent
-    {
-        /// <summary>
-        ///     Gets the <see cref="ComponentType"/> of this Message Component.
-        /// </summary>
-        ComponentType Type { get; }
+    ComponentType Type { get; }
 
-        /// <summary>
-        ///     Gets the custom id of the component if possible; otherwise <see langword="null"/>.
-        /// </summary>
-        string CustomId { get; }
-    }
+    int? Id { get; }
 }
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SelectMenuComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SelectMenuComponent.cs
index 39631deea5..7d1a2558e3 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SelectMenuComponent.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SelectMenuComponent.cs
@@ -7,11 +7,14 @@ namespace Discord
     /// <summary>
     ///     Represents a select menu component defined at <see href="https://discord.com/developers/docs/interactions/message-components#select-menu-object"/>
     /// </summary>
-    public class SelectMenuComponent : IMessageComponent
+    public class SelectMenuComponent : IInteractableComponent
     {
         /// <inheritdoc/>
         public ComponentType Type { get; }
 
+        /// <inheritdoc/>
+        public int? Id { get; }
+
         /// <inheritdoc/>
         public string CustomId { get; }
 
@@ -67,7 +70,7 @@ public SelectMenuBuilder ToBuilder()
                 DefaultValues.ToList());
 
         internal SelectMenuComponent(string customId, List<SelectMenuOption> options, string placeholder, int minValues, int maxValues,
-            bool disabled, ComponentType type, IEnumerable<ChannelType> channelTypes = null, IEnumerable<SelectMenuDefaultValue> defaultValues = null)
+            bool disabled, ComponentType type, int? id, IEnumerable<ChannelType> channelTypes = null, IEnumerable<SelectMenuDefaultValue> defaultValues = null)
         {
             CustomId = customId;
             Options = options;
@@ -76,8 +79,9 @@ internal SelectMenuComponent(string customId, List<SelectMenuOption> options, st
             MaxValues = maxValues;
             IsDisabled = disabled;
             Type = type;
-            ChannelTypes = channelTypes?.ToArray() ?? Array.Empty<ChannelType>();
-            DefaultValues = defaultValues?.ToArray() ?? Array.Empty<SelectMenuDefaultValue>();
+            Id = id;
+            ChannelTypes = channelTypes?.ToArray() ?? [];
+            DefaultValues = defaultValues?.ToArray() ?? [];
         }
     }
 }
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/TextInputComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/TextInputComponent.cs
index e2da1132c8..c462d0c45c 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/TextInputComponent.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/TextInputComponent.cs
@@ -1,9 +1,11 @@
+using Newtonsoft.Json;
+
 namespace Discord
 {
     /// <summary>
     ///     Represents a <see cref="IMessageComponent"/> text input.
     /// </summary>
-    public class TextInputComponent : IMessageComponent
+    public class TextInputComponent : IInteractableComponent
     {
         /// <inheritdoc/>
         public ComponentType Type => ComponentType.TextInput;
@@ -11,6 +13,9 @@ public class TextInputComponent : IMessageComponent
         /// <inheritdoc/>
         public string CustomId { get; }
 
+        /// <inheritdoc/>
+        public int? Id { get; }
+
         /// <summary>
         ///     Gets the label of the component; this is the text shown above it.
         /// </summary>
@@ -47,7 +52,7 @@ public class TextInputComponent : IMessageComponent
         public string Value { get; }
 
         internal TextInputComponent(string customId, string label, string placeholder, int? minLength, int? maxLength,
-            TextInputStyle style, bool? required, string value)
+            TextInputStyle style, bool? required, string value, int? id)
         {
             CustomId = customId;
             Label = label;
@@ -57,6 +62,7 @@ internal TextInputComponent(string customId, string label, string placeholder, i
             Style = style;
             Required = required;
             Value = value;
+            Id = id;
         }
     }
 }
diff --git a/src/Discord.Net.Core/Entities/Interactions/Modals/Modal.cs b/src/Discord.Net.Core/Entities/Interactions/Modals/Modal.cs
index a435d33ef1..6062db0cc0 100644
--- a/src/Discord.Net.Core/Entities/Interactions/Modals/Modal.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/Modals/Modal.cs
@@ -1,19 +1,10 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
 namespace Discord
 {
     /// <summary>
     ///     Represents a modal interaction.
     /// </summary>
-    public class Modal : IMessageComponent
+    public class Modal
     {
-        /// <inheritdoc/>
-        public ComponentType Type => throw new NotSupportedException("Modals do not have a component type.");
-
         /// <summary>
         ///     Gets the title of the modal.
         /// </summary>
diff --git a/src/Discord.Net.Core/Entities/Interactions/Modals/ModalBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/Modals/ModalBuilder.cs
index c534e9f385..98a37e2a8e 100644
--- a/src/Discord.Net.Core/Entities/Interactions/Modals/ModalBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/Modals/ModalBuilder.cs
@@ -114,18 +114,18 @@ public ModalBuilder AddComponents(List<IMessageComponent> components, int row)
         ///     Gets a <typeparamref name="TMessageComponent"/> by the specified <paramref name="customId"/>.
         /// </summary>
         /// <typeparam name="TMessageComponent">The type of the component to get.</typeparam>
-        /// <param name="customId">The <see cref="IMessageComponent.CustomId"/> of the component to get.</param>
+        /// <param name="customId">The <see cref="IInteractableComponent.CustomId"/> of the component to get.</param>
         /// <returns>
         ///     The component of type <typeparamref name="TMessageComponent"/> that was found, <see langword="null"/> otherwise.
         /// </returns>
         public TMessageComponent GetComponent<TMessageComponent>(string customId)
-            where TMessageComponent : class, IMessageComponent
+            where TMessageComponent : class, IInteractableComponent
         {
             Preconditions.NotNull(customId, nameof(customId));
 
             return Components.ActionRows
                 ?.SelectMany(r => r.Components.OfType<TMessageComponent>())
-                .FirstOrDefault(c => c?.CustomId == customId);
+                .FirstOrDefault(c => c is IInteractableComponent ic && ic?.CustomId == customId);
         }
 
         /// <summary>
@@ -185,7 +185,7 @@ public ModalBuilder RemoveComponent(string customId)
         {
             Preconditions.NotNull(customId, nameof(customId));
 
-            Components.ActionRows?.ForEach(r => r.Components.RemoveAll(c => c.CustomId == customId));
+            Components.ActionRows?.ForEach(r => r.Components.RemoveAll(c => c is IInteractableComponent ic && ic.CustomId == customId));
             return this;
         }
 
diff --git a/src/Discord.Net.Rest/API/Common/ActionRowComponent.cs b/src/Discord.Net.Rest/API/Common/ActionRowComponent.cs
index e97ca71d65..4ee6311ee3 100644
--- a/src/Discord.Net.Rest/API/Common/ActionRowComponent.cs
+++ b/src/Discord.Net.Rest/API/Common/ActionRowComponent.cs
@@ -8,6 +8,9 @@ internal class ActionRowComponent : IMessageComponent
         [JsonProperty("type")]
         public ComponentType Type { get; set; }
 
+        [JsonProperty("id")]
+        public Optional<int> Id { get; set; }
+
         [JsonProperty("components")]
         public IMessageComponent[] Components { get; set; }
 
@@ -29,9 +32,10 @@ internal ActionRowComponent(Discord.ActionRowComponent c)
                     _ => null
                 };
             }).ToArray();
+            Id = c.Id ?? Optional<int>.Unspecified;
         }
 
         [JsonIgnore]
-        string IMessageComponent.CustomId => null;
+        int? IMessageComponent.Id => Id.ToNullable();
     }
 }
diff --git a/src/Discord.Net.Rest/API/Common/ButtonComponent.cs b/src/Discord.Net.Rest/API/Common/ButtonComponent.cs
index f820fc026d..84f6e0c825 100644
--- a/src/Discord.Net.Rest/API/Common/ButtonComponent.cs
+++ b/src/Discord.Net.Rest/API/Common/ButtonComponent.cs
@@ -2,11 +2,14 @@
 
 namespace Discord.API
 {
-    internal class ButtonComponent : IMessageComponent
+    internal class ButtonComponent : IInteractableComponent
     {
         [JsonProperty("type")]
         public ComponentType Type { get; set; }
 
+        [JsonProperty("id")]
+        public Optional<int> Id { get; set; }
+
         [JsonProperty("style")]
         public ButtonStyle Style { get; set; }
 
@@ -39,6 +42,7 @@ public ButtonComponent(Discord.ButtonComponent c)
             Url = c.Url;
             Disabled = c.IsDisabled;
             SkuId = c.SkuId ?? Optional<ulong>.Unspecified;
+            Id = c.Id ?? Optional<int>.Unspecified;
 
             if (c.Emote != null)
             {
@@ -62,6 +66,9 @@ public ButtonComponent(Discord.ButtonComponent c)
         }
 
         [JsonIgnore]
-        string IMessageComponent.CustomId => CustomId.GetValueOrDefault();
+        string IInteractableComponent.CustomId => CustomId.GetValueOrDefault();
+
+        [JsonIgnore]
+        int? IMessageComponent.Id => Id.ToNullable();
     }
 }
diff --git a/src/Discord.Net.Rest/API/Common/SelectMenuComponent.cs b/src/Discord.Net.Rest/API/Common/SelectMenuComponent.cs
index c7a69568cd..f605d75b84 100644
--- a/src/Discord.Net.Rest/API/Common/SelectMenuComponent.cs
+++ b/src/Discord.Net.Rest/API/Common/SelectMenuComponent.cs
@@ -3,11 +3,14 @@
 
 namespace Discord.API
 {
-    internal class SelectMenuComponent : IMessageComponent
+    internal class SelectMenuComponent : IInteractableComponent
     {
         [JsonProperty("type")]
         public ComponentType Type { get; set; }
 
+        [JsonProperty("id")]
+        public Optional<int> Id { get; set; }
+
         [JsonProperty("custom_id")]
         public string CustomId { get; set; }
 
@@ -52,5 +55,8 @@ public SelectMenuComponent(Discord.SelectMenuComponent component)
             ChannelTypes = component.ChannelTypes.ToArray();
             DefaultValues = component.DefaultValues.Select(x => new SelectMenuDefaultValue {Id = x.Id, Type = x.Type}).ToArray();
         }
+
+        [JsonIgnore]
+        int? IMessageComponent.Id => Id.ToNullable();
     }
 }
diff --git a/src/Discord.Net.Rest/API/Common/TextInputComponent.cs b/src/Discord.Net.Rest/API/Common/TextInputComponent.cs
index a475345fcf..458cdaca56 100644
--- a/src/Discord.Net.Rest/API/Common/TextInputComponent.cs
+++ b/src/Discord.Net.Rest/API/Common/TextInputComponent.cs
@@ -2,11 +2,14 @@
 
 namespace Discord.API
 {
-    internal class TextInputComponent : IMessageComponent
+    internal class TextInputComponent : IInteractableComponent
     {
         [JsonProperty("type")]
         public ComponentType Type { get; set; }
 
+        [JsonProperty("id")]
+        public Optional<int> Id { get; set; }
+
         [JsonProperty("style")]
         public TextInputStyle Style { get; set; }
 
@@ -44,6 +47,10 @@ public TextInputComponent(Discord.TextInputComponent component)
             MaxLength = component.MaxLength ?? Optional<int>.Unspecified;
             Required = component.Required ?? Optional<bool>.Unspecified;
             Value = component.Value ?? Optional<string>.Unspecified;
+            Id = component.Id ?? Optional<int>.Unspecified;
         }
+
+        [JsonIgnore]
+        int? IMessageComponent.Id => Id.ToNullable();
     }
 }
diff --git a/src/Discord.Net.Rest/API/Common/UnfurledMediaItem.cs b/src/Discord.Net.Rest/API/Common/UnfurledMediaItem.cs
new file mode 100644
index 0000000000..60635c41a7
--- /dev/null
+++ b/src/Discord.Net.Rest/API/Common/UnfurledMediaItem.cs
@@ -0,0 +1,9 @@
+using Newtonsoft.Json;
+
+namespace Discord.API;
+
+public class UnfurledMediaItem
+{
+    [JsonProperty("url")]
+    public string Url { get; set; }
+}
diff --git a/src/Discord.Net.Rest/Entities/Interactions/MessageComponents/RestMessageComponentData.cs b/src/Discord.Net.Rest/Entities/Interactions/MessageComponents/RestMessageComponentData.cs
index bc44d0df55..f72eceecb4 100644
--- a/src/Discord.Net.Rest/Entities/Interactions/MessageComponents/RestMessageComponentData.cs
+++ b/src/Discord.Net.Rest/Entities/Interactions/MessageComponents/RestMessageComponentData.cs
@@ -93,7 +93,7 @@ internal RestMessageComponentData(Model model, BaseDiscordClient discord, IGuild
             }
         }
 
-        internal RestMessageComponentData(IMessageComponent component, BaseDiscordClient discord, IGuild guild)
+        internal RestMessageComponentData(IInteractableComponent component, BaseDiscordClient discord, IGuild guild)
         {
             CustomId = component.CustomId;
             Type = component.Type;
diff --git a/src/Discord.Net.Rest/Entities/Interactions/Modals/RestModalData.cs b/src/Discord.Net.Rest/Entities/Interactions/Modals/RestModalData.cs
index c1f329e7d3..1831d4b51a 100644
--- a/src/Discord.Net.Rest/Entities/Interactions/Modals/RestModalData.cs
+++ b/src/Discord.Net.Rest/Entities/Interactions/Modals/RestModalData.cs
@@ -1,8 +1,5 @@
-using System;
 using System.Collections.Generic;
 using System.Linq;
-using DataModel = Discord.API.MessageComponentInteractionData;
-using InterationModel = Discord.API.Interaction;
 using Model = Discord.API.ModalInteractionData;
 
 namespace Discord.Rest
@@ -26,7 +23,7 @@ internal RestModalData(Model model, BaseDiscordClient discord, IGuild guild)
         {
             CustomId = model.CustomId;
             Components = model.Components
-                .SelectMany(x => x.Components)
+                .SelectMany(x => x.Components.OfType<IInteractableComponent>())
                 .Select(x => new RestMessageComponentData(x, discord, guild))
                 .ToArray();
         }
diff --git a/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs
index 132f1c97d7..d4fddd038e 100644
--- a/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs
+++ b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs
@@ -191,7 +191,8 @@ internal virtual void Update(Model model)
                                     parsed.CustomId.GetValueOrDefault(),
                                     parsed.Url.GetValueOrDefault(),
                                     parsed.Disabled.GetValueOrDefault(),
-                                    parsed.SkuId.ToNullable());
+                                    parsed.SkuId.ToNullable(),
+                                    parsed.Id.ToNullable());
                             }
                         case ComponentType.SelectMenu or ComponentType.ChannelSelect or ComponentType.RoleSelect or ComponentType.MentionableSelect or ComponentType.UserSelect:
                             {
@@ -213,6 +214,7 @@ internal virtual void Update(Model model)
                                     parsed.MaxValues,
                                     parsed.Disabled,
                                     parsed.Type,
+                                    parsed.Id.ToNullable(),
                                     parsed.ChannelTypes.GetValueOrDefault(),
                                     parsed.DefaultValues.IsSpecified
                                         ? parsed.DefaultValues.Value.Select(x => new SelectMenuDefaultValue(x.Id, x.Type))
@@ -236,15 +238,16 @@ internal virtual void Update(Model model)
                 if (value.Length > 0)
                 {
                     var reactions = ImmutableArray.CreateBuilder<RestReaction>(value.Length);
-                    for (int i = 0; i < value.Length; i++)
-                        reactions.Add(RestReaction.Create(value[i]));
+                    foreach (var t in value)
+                        reactions.Add(RestReaction.Create(t));
+
                     _reactions = reactions.ToImmutable();
                 }
                 else
-                    _reactions = ImmutableArray.Create<RestReaction>();
+                    _reactions = [];
             }
             else
-                _reactions = ImmutableArray.Create<RestReaction>();
+                _reactions = [];
 
             if (model.Interaction.IsSpecified)
             {
diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/MessageComponents/SocketMessageComponentData.cs b/src/Discord.Net.WebSocket/Entities/Interaction/MessageComponents/SocketMessageComponentData.cs
index b193b77f38..718cc3b130 100644
--- a/src/Discord.Net.WebSocket/Entities/Interaction/MessageComponents/SocketMessageComponentData.cs
+++ b/src/Discord.Net.WebSocket/Entities/Interaction/MessageComponents/SocketMessageComponentData.cs
@@ -90,13 +90,13 @@ internal SocketMessageComponentData(Model model, DiscordSocketClient discord, Cl
             }
         }
 
-        internal SocketMessageComponentData(IMessageComponent component, DiscordSocketClient discord, ClientState state, SocketGuild guild, API.User dmUser)
+        internal SocketMessageComponentData(IInteractableComponent component, DiscordSocketClient discord, ClientState state, SocketGuild guild, API.User dmUser)
         {
             CustomId = component.CustomId;
             Type = component.Type;
 
             Value = component.Type == ComponentType.TextInput
-                ? (component as API.TextInputComponent).Value.Value
+                ? ((TextInputComponent)component).Value
                 : null;
 
             if (component is API.SelectMenuComponent select)
diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/Modals/SocketModalData.cs b/src/Discord.Net.WebSocket/Entities/Interaction/Modals/SocketModalData.cs
index 52c7615b2d..c20562ecc7 100644
--- a/src/Discord.Net.WebSocket/Entities/Interaction/Modals/SocketModalData.cs
+++ b/src/Discord.Net.WebSocket/Entities/Interaction/Modals/SocketModalData.cs
@@ -1,8 +1,5 @@
-using System;
 using System.Collections.Generic;
 using System.Linq;
-using DataModel = Discord.API.MessageComponentInteractionData;
-using InterationModel = Discord.API.Interaction;
 using Model = Discord.API.ModalInteractionData;
 
 namespace Discord.WebSocket
@@ -26,7 +23,7 @@ internal SocketModalData(Model model, DiscordSocketClient discord, ClientState s
         {
             CustomId = model.CustomId;
             Components = model.Components
-                .SelectMany(x => x.Components)
+                .SelectMany(x => x.Components.OfType<IInteractableComponent>())
                 .Select(x => new SocketMessageComponentData(x, discord, state, guild, dmUser))
                 .ToArray();
         }
diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs
index c778137e01..878940f7cf 100644
--- a/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs
+++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs
@@ -224,7 +224,8 @@ internal virtual void Update(ClientState state, Model model)
                                     parsed.CustomId.GetValueOrDefault(),
                                     parsed.Url.GetValueOrDefault(),
                                     parsed.Disabled.GetValueOrDefault(),
-                                    parsed.SkuId.ToNullable());
+                                    parsed.SkuId.ToNullable(),
+                                    parsed.Id.ToNullable());
                             }
                         case ComponentType.SelectMenu:
                             {
@@ -246,6 +247,7 @@ internal virtual void Update(ClientState state, Model model)
                                     parsed.MaxValues,
                                     parsed.Disabled,
                                     parsed.Type,
+                                    parsed.Id.ToNullable(),
                                     parsed.ChannelTypes.GetValueOrDefault(),
                                     parsed.DefaultValues.IsSpecified
                                         ? parsed.DefaultValues.Value.Select(x => new SelectMenuDefaultValue(x.Id, x.Type))

From 2f501adb33b2c41d0ce94b04445da03fe502bdd3 Mon Sep 17 00:00:00 2001
From: Misha133 <mihagribkov133@gmail.com>
Date: Sun, 15 Dec 2024 21:11:45 +0300
Subject: [PATCH 02/15] well that's some breakings

---
 .../Interactions/IDiscordInteraction.cs       | 53 ++++++----
 .../Builders/ComponentBuilder.cs              | 10 +-
 .../Builders/TextDisplayComponentBuilder.cs   | 18 ++++
 .../MessageComponents/MessageComponent.cs     | 35 ++++---
 .../MessageComponents/SeparatorSpacingSize.cs |  8 ++
 .../MessageComponents/TextDisplayComponent.cs | 16 +++
 .../Entities/Messages/MessageFlags.cs         |  2 +
 .../InteractionModuleBase.cs                  | 60 ++++++------
 .../RestInteractionModuleBase.cs              |  4 +-
 .../API/Common/ForumThreadMessage.cs          |  2 +-
 .../API/Common/InteractionCallbackData.cs     |  2 +-
 .../API/Common/TextDisplayComponent.cs        | 26 +++++
 .../API/Rest/CreateMessageParams.cs           |  2 +-
 .../API/Rest/CreateMultipartPostAsync.cs      |  2 +-
 .../API/Rest/CreatePostParams.cs              | 34 +++----
 .../API/Rest/CreateWebhookMessageParams.cs    |  2 +-
 .../Rest/ModifyInteractionResponseParams.cs   |  2 +-
 .../API/Rest/ModifyMessageParams.cs           | 32 +++---
 .../API/Rest/ModifyWebhookMessageParams.cs    | 27 ++---
 .../API/Rest/UploadFileParams.cs              |  2 +-
 .../API/Rest/UploadInteractionFileParams.cs   |  6 +-
 .../API/Rest/UploadWebhookFileParams.cs       |  2 +-
 .../Entities/Channels/ChannelHelper.cs        |  4 +-
 .../Entities/Channels/ThreadHelper.cs         |  4 +-
 .../CommandBase/RestCommandBase.cs            | 84 +++++++++-------
 .../Interactions/InteractionHelper.cs         | 10 +-
 .../MessageComponents/RestMessageComponent.cs | 74 +++++++-------
 .../Entities/Interactions/Modals/RestModal.cs | 96 ++++++++++--------
 .../Entities/Interactions/RestInteraction.cs  | 45 +++++----
 .../Interactions/RestPingInteraction.cs       | 12 +--
 .../RestAutocompleteInteraction.cs            | 12 +--
 .../Entities/Messages/MessageHelper.cs        | 12 +--
 .../Entities/Messages/RestMessage.cs          | 71 ++------------
 .../Entities/Messages/RestUserMessage.cs      | 20 ++--
 .../Extensions/MessageComponentExtension.cs   | 98 +++++++++++++++++++
 .../Converters/MessageComponentConverter.cs   |  3 +
 .../SocketMessageComponent.cs                 | 85 +++++++++-------
 .../Interaction/Modals/SocketModal.cs         | 78 +++++++++------
 .../SocketAutocompleteInteraction.cs          |  8 +-
 .../SocketBaseCommand/SocketCommandBase.cs    | 61 +++++++-----
 .../Entities/Interaction/SocketInteraction.cs | 56 +++++------
 .../Entities/Messages/SocketMessage.cs        | 68 ++-----------
 .../WebhookClientHelper.cs                    | 10 +-
 43 files changed, 705 insertions(+), 553 deletions(-)
 create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextDisplayComponentBuilder.cs
 create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/SeparatorSpacingSize.cs
 create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/TextDisplayComponent.cs
 create mode 100644 src/Discord.Net.Rest/API/Common/TextDisplayComponent.cs
 create mode 100644 src/Discord.Net.Rest/Extensions/MessageComponentExtension.cs

diff --git a/src/Discord.Net.Core/Entities/Interactions/IDiscordInteraction.cs b/src/Discord.Net.Core/Entities/Interactions/IDiscordInteraction.cs
index 5b04de590b..d7a530bba8 100644
--- a/src/Discord.Net.Core/Entities/Interactions/IDiscordInteraction.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/IDiscordInteraction.cs
@@ -127,7 +127,7 @@ public interface IDiscordInteraction : ISnowflakeEntity
         ///     A task that represents an asynchronous send operation for delivering the message.
         /// </returns>
         Task RespondAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null,
-            MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null);
+            MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None);
 
         /// <summary>
         ///     Responds to this interaction with a file attachment.
@@ -149,7 +149,8 @@ Task RespondAsync(string text = null, Embed[] embeds = null, bool isTTS = false,
         /// </returns>
 #if NETCOREAPP3_0_OR_GREATER
         async Task RespondWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
             using (var file = new FileAttachment(fileStream, fileName))
             {
@@ -158,7 +159,8 @@ async Task RespondWithFileAsync(Stream fileStream, string fileName, string text
         }
 #else
         Task RespondWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null);
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null
+            , MessageFlags flags = MessageFlags.None);
 #endif
         /// <summary>
         ///     Responds to this interaction with a file attachment.
@@ -180,7 +182,8 @@ Task RespondWithFileAsync(Stream fileStream, string fileName, string text = null
         /// </returns>
 #if NETCOREAPP3_0_OR_GREATER
         async Task RespondWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
             using (var file = new FileAttachment(filePath, fileName))
             {
@@ -189,7 +192,8 @@ async Task RespondWithFileAsync(string filePath, string fileName = null, string
         }
 #else
         Task RespondWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null);
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None);
 #endif
         /// <summary>
         ///     Responds to this interaction with a file attachment.
@@ -210,11 +214,13 @@ Task RespondWithFileAsync(string filePath, string fileName = null, string text =
         /// </returns>
 #if NETCOREAPP3_0_OR_GREATER
         Task RespondWithFileAsync(FileAttachment attachment, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
-            => RespondWithFilesAsync(new FileAttachment[] { attachment }, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll);
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
+            => RespondWithFilesAsync([attachment], text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags);
 #else
         Task RespondWithFileAsync(FileAttachment attachment, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null);
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None);
 #endif
         /// <summary>
         ///     Responds to this interaction with a collection of file attachments.
@@ -234,7 +240,9 @@ Task RespondWithFileAsync(FileAttachment attachment, string text = null, Embed[]
         ///     contains the sent message.
         /// </returns>
         Task RespondWithFilesAsync(IEnumerable<FileAttachment> attachments, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null);
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None);
+
         /// <summary>
         ///     Sends a followup message for this interaction.
         /// </summary>
@@ -252,7 +260,9 @@ Task RespondWithFilesAsync(IEnumerable<FileAttachment> attachments, string text
         ///     contains the sent message.
         /// </returns>
         Task<IUserMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-             AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null);
+             AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null,
+             MessageFlags flags = MessageFlags.None);
+
         /// <summary>
         ///     Sends a followup message for this interaction.
         /// </summary>
@@ -273,17 +283,19 @@ Task<IUserMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool
         /// </returns>
 #if NETCOREAPP3_0_OR_GREATER
         async Task<IUserMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None)
         {
             using (var file = new FileAttachment(fileStream, fileName))
             {
-                return await FollowupWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll).ConfigureAwait(false);
+                return await FollowupWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags).ConfigureAwait(false);
             }
         }
 #else
         Task<IUserMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null);
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None);
 #endif
+
         /// <summary>
         ///     Sends a followup message for this interaction.
         /// </summary>
@@ -304,16 +316,17 @@ Task<IUserMessage> FollowupWithFileAsync(Stream fileStream, string fileName, str
         /// </returns>
 #if NETCOREAPP3_0_OR_GREATER
         async Task<IUserMessage> FollowupWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
             using (var file = new FileAttachment(filePath, fileName))
             {
-                return await FollowupWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll).ConfigureAwait(false);
+                return await FollowupWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags).ConfigureAwait(false);
             }
         }
 #else
         Task<IUserMessage> FollowupWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null);
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None);
 #endif
         /// <summary>
         ///     Sends a followup message for this interaction.
@@ -334,11 +347,11 @@ Task<IUserMessage> FollowupWithFileAsync(string filePath, string fileName = null
         /// </returns>
 #if NETCOREAPP3_0_OR_GREATER
         Task<IUserMessage> FollowupWithFileAsync(FileAttachment attachment, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
-            => FollowupWithFilesAsync(new FileAttachment[] { attachment }, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll);
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None)
+            => FollowupWithFilesAsync(new FileAttachment[] { attachment }, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags);
 #else
         Task<IUserMessage> FollowupWithFileAsync(FileAttachment attachment, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null);
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None);
 #endif
         /// <summary>
         ///     Sends a followup message for this interaction.
@@ -358,7 +371,7 @@ Task<IUserMessage> FollowupWithFileAsync(FileAttachment attachment, string text
         ///     contains the sent message.
         /// </returns>
         Task<IUserMessage> FollowupWithFilesAsync(IEnumerable<FileAttachment> attachments, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null);
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None);
         /// <summary>
         ///     Gets the original response for this interaction.
         /// </summary>
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilder.cs
index b4c60ba9f3..ce11bdee9d 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilder.cs
@@ -67,13 +67,18 @@ internal void AddComponent(IMessageComponent component, int row)
     {
         switch (component)
         {
+            case TextDisplayComponent textDisplay:
+                break;
+
             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;
@@ -247,10 +252,7 @@ public ComponentBuilder WithButton(ButtonBuilder button, int row = 0)
 
         if (_actionRows == null)
         {
-            _actionRows = new List<ActionRowBuilder>
-            {
-                new ActionRowBuilder().AddComponent(builtButton)
-            };
+            _actionRows = [new ActionRowBuilder().AddComponent(builtButton)];
         }
         else
         {
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextDisplayComponentBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextDisplayComponentBuilder.cs
new file mode 100644
index 0000000000..28d6fe41bf
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextDisplayComponentBuilder.cs
@@ -0,0 +1,18 @@
+namespace Discord;
+
+public class TextDisplayComponentBuilder
+{
+    public int? Id { get; set; }
+
+    private string _content;
+    public string Content
+    {
+        get => _content;
+        set => _content = value;
+    }
+
+    public TextDisplayComponent Build()
+    {
+        return new(Id, _content);
+    }
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/MessageComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/MessageComponent.cs
index 7205886816..18946a7380 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/MessageComponent.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/MessageComponent.cs
@@ -1,26 +1,25 @@
 using System.Collections.Generic;
 
-namespace Discord
+namespace Discord;
+
+/// <summary>
+///     Represents a component object used to send components with messages.
+/// </summary>
+public class MessageComponent
 {
     /// <summary>
-    ///     Represents a component object used to send components with messages.
+    ///     Gets the components to be used in a message.
     /// </summary>
-    public class MessageComponent
-    {
-        /// <summary>
-        ///     Gets the components to be used in a message.
-        /// </summary>
-        public IReadOnlyCollection<ActionRowComponent> Components { get; }
+    public IReadOnlyCollection<IMessageComponent> Components { get; }
 
-        internal MessageComponent(List<ActionRowComponent> components)
-        {
-            Components = components;
-        }
-
-        /// <summary>
-        ///     Returns a empty <see cref="MessageComponent"/>.
-        /// </summary>
-        internal static MessageComponent Empty
-            => new MessageComponent(new List<ActionRowComponent>());
+    internal MessageComponent(List<IMessageComponent> components)
+    {
+        Components = components;
     }
+
+    /// <summary>
+    ///     Returns a empty <see cref="MessageComponent"/>.
+    /// </summary>
+    internal static MessageComponent Empty
+        => new([]);
 }
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SeparatorSpacingSize.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SeparatorSpacingSize.cs
new file mode 100644
index 0000000000..405b08c58d
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SeparatorSpacingSize.cs
@@ -0,0 +1,8 @@
+namespace Discord;
+
+public enum SeparatorSpacingSize
+{
+    Small = 1,
+
+    Large = 2
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/TextDisplayComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/TextDisplayComponent.cs
new file mode 100644
index 0000000000..1829804f59
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/TextDisplayComponent.cs
@@ -0,0 +1,16 @@
+namespace Discord;
+
+public class TextDisplayComponent : IMessageComponent
+{
+    public ComponentType Type => ComponentType.TextDisplay;
+
+    public int? Id { get; }
+
+    public string Content { get; }
+
+    internal TextDisplayComponent(int? id, string content)
+    {
+        Id = id;
+        Content = content;
+    }
+}
diff --git a/src/Discord.Net.Core/Entities/Messages/MessageFlags.cs b/src/Discord.Net.Core/Entities/Messages/MessageFlags.cs
index 10f1aeba16..51f7d89fc9 100644
--- a/src/Discord.Net.Core/Entities/Messages/MessageFlags.cs
+++ b/src/Discord.Net.Core/Entities/Messages/MessageFlags.cs
@@ -57,5 +57,7 @@ public enum MessageFlags
         ///     This message is a voice message.
         /// </summary>
         VoiceMessage = 1 << 13,
+
+        UiKitComponents = 1 << 15,
     }
 }
diff --git a/src/Discord.Net.Interactions/InteractionModuleBase.cs b/src/Discord.Net.Interactions/InteractionModuleBase.cs
index 62e5c3ab03..5b9b90f35a 100644
--- a/src/Discord.Net.Interactions/InteractionModuleBase.cs
+++ b/src/Discord.Net.Interactions/InteractionModuleBase.cs
@@ -44,55 +44,55 @@ internal void SetContext(IInteractionContext context)
         protected virtual Task DeferAsync(bool ephemeral = false, RequestOptions options = null)
             => Context.Interaction.DeferAsync(ephemeral, options);
 
-        /// <inheritdoc cref="IDiscordInteraction.RespondAsync(string, Embed[], bool, bool, AllowedMentions, MessageComponent, Embed, RequestOptions, PollProperties)"/>
+        /// <inheritdoc cref="IDiscordInteraction.RespondAsync(string, Embed[], bool, bool, AllowedMentions, MessageComponent, Embed, RequestOptions, PollProperties, MessageFlags)"/>
         protected virtual Task RespondAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent components = null, Embed embed = null, PollProperties poll = null)
-            => Context.Interaction.RespondAsync(text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll);
+            AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent components = null, Embed embed = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None)
+            => Context.Interaction.RespondAsync(text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags);
 
-        /// <inheritdoc cref="IDiscordInteraction.RespondWithFileAsync(Stream, string, string, Embed[], bool, bool, AllowedMentions, MessageComponent, Embed, RequestOptions, PollProperties)"/>
+        /// <inheritdoc cref="IDiscordInteraction.RespondWithFileAsync(Stream, string, string, Embed[], bool, bool, AllowedMentions, MessageComponent, Embed, RequestOptions, PollProperties, MessageFlags)"/>
         protected virtual Task RespondWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
-            => Context.Interaction.RespondWithFileAsync(fileStream, fileName, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll);
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None)
+            => Context.Interaction.RespondWithFileAsync(fileStream, fileName, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags);
 
-        /// <inheritdoc cref="IDiscordInteraction.RespondWithFileAsync(string, string, string, Embed[], bool, bool, AllowedMentions, MessageComponent, Embed, RequestOptions, PollProperties)"/>
+        /// <inheritdoc cref="IDiscordInteraction.RespondWithFileAsync(string, string, string, Embed[], bool, bool, AllowedMentions, MessageComponent, Embed, RequestOptions, PollProperties, MessageFlags)"/>
         protected virtual Task RespondWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
-            => Context.Interaction.RespondWithFileAsync(filePath, fileName, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll);
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None)
+            => Context.Interaction.RespondWithFileAsync(filePath, fileName, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags);
 
-        /// <inheritdoc cref="IDiscordInteraction.RespondWithFileAsync(FileAttachment, string, Embed[], bool, bool, AllowedMentions, MessageComponent, Embed, RequestOptions, PollProperties)"/>
+        /// <inheritdoc cref="IDiscordInteraction.RespondWithFileAsync(FileAttachment, string, Embed[], bool, bool, AllowedMentions, MessageComponent, Embed, RequestOptions, PollProperties, MessageFlags)"/>
         protected virtual Task RespondWithFileAsync(FileAttachment attachment, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
-            => Context.Interaction.RespondWithFileAsync(attachment, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll);
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None)
+            => Context.Interaction.RespondWithFileAsync(attachment, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags);
 
-        /// <inheritdoc cref="IDiscordInteraction.RespondWithFilesAsync(IEnumerable{FileAttachment}, string, Embed[], bool, bool, AllowedMentions, MessageComponent, Embed, RequestOptions, PollProperties)"/>
+        /// <inheritdoc cref="IDiscordInteraction.RespondWithFilesAsync(IEnumerable{FileAttachment}, string, Embed[], bool, bool, AllowedMentions, MessageComponent, Embed, RequestOptions, PollProperties, MessageFlags)"/>
         protected virtual Task RespondWithFilesAsync(IEnumerable<FileAttachment> attachments, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
-            => Context.Interaction.RespondWithFilesAsync(attachments, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll);
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None)
+            => Context.Interaction.RespondWithFilesAsync(attachments, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags);
 
-        /// <inheritdoc cref="IDiscordInteraction.FollowupAsync(string, Embed[], bool, bool, AllowedMentions, MessageComponent, Embed, RequestOptions, PollProperties)"/>
+        /// <inheritdoc cref="IDiscordInteraction.FollowupAsync(string, Embed[], bool, bool, AllowedMentions, MessageComponent, Embed, RequestOptions, PollProperties, MessageFlags)"/>
         protected virtual Task<IUserMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent components = null, Embed embed = null, PollProperties poll = null)
-            => Context.Interaction.FollowupAsync(text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll);
+            AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent components = null, Embed embed = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None)
+            => Context.Interaction.FollowupAsync(text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags);
 
-        /// <inheritdoc cref="IDiscordInteraction.FollowupWithFileAsync(Stream, string, string, Embed[], bool, bool, AllowedMentions, MessageComponent, Embed, RequestOptions, PollProperties)"/>
+        /// <inheritdoc cref="IDiscordInteraction.FollowupWithFileAsync(Stream, string, string, Embed[], bool, bool, AllowedMentions, MessageComponent, Embed, RequestOptions, PollProperties, MessageFlags)"/>
         protected virtual Task<IUserMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
-            => Context.Interaction.FollowupWithFileAsync(fileStream, fileName, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll);
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None)
+            => Context.Interaction.FollowupWithFileAsync(fileStream, fileName, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags);
 
-        /// <inheritdoc cref="IDiscordInteraction.FollowupWithFileAsync(string, string, string, Embed[], bool, bool, AllowedMentions, MessageComponent, Embed, RequestOptions, PollProperties)"/>
+        /// <inheritdoc cref="IDiscordInteraction.FollowupWithFileAsync(string, string, string, Embed[], bool, bool, AllowedMentions, MessageComponent, Embed, RequestOptions, PollProperties, MessageFlags)"/>
         protected virtual Task<IUserMessage> FollowupWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
-            => Context.Interaction.FollowupWithFileAsync(filePath, fileName, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll);
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None)
+            => Context.Interaction.FollowupWithFileAsync(filePath, fileName, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags);
 
-        /// <inheritdoc cref="IDiscordInteraction.FollowupWithFileAsync(FileAttachment, string, Embed[], bool, bool, AllowedMentions, MessageComponent, Embed, RequestOptions, PollProperties)"/>
+        /// <inheritdoc cref="IDiscordInteraction.FollowupWithFileAsync(FileAttachment, string, Embed[], bool, bool, AllowedMentions, MessageComponent, Embed, RequestOptions, PollProperties, MessageFlags)"/>
         protected virtual Task<IUserMessage> FollowupWithFileAsync(FileAttachment attachment, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
-            => Context.Interaction.FollowupWithFileAsync(attachment, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll);
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None)
+            => Context.Interaction.FollowupWithFileAsync(attachment, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags);
 
-        /// <inheritdoc cref="IDiscordInteraction.FollowupWithFilesAsync(IEnumerable{FileAttachment}, string, Embed[], bool, bool, AllowedMentions, MessageComponent, Embed, RequestOptions, PollProperties)"/>
+        /// <inheritdoc cref="IDiscordInteraction.FollowupWithFilesAsync(IEnumerable{FileAttachment}, string, Embed[], bool, bool, AllowedMentions, MessageComponent, Embed, RequestOptions, PollProperties, MessageFlags)"/>
         protected virtual Task<IUserMessage> FollowupWithFilesAsync(IEnumerable<FileAttachment> attachments, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
-            => Context.Interaction.FollowupWithFilesAsync(attachments, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll);
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None)
+            => Context.Interaction.FollowupWithFilesAsync(attachments, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags);
 
         /// <inheritdoc cref="IMessageChannel.SendMessageAsync(string, bool, Embed, RequestOptions, AllowedMentions, MessageReference, MessageComponent, ISticker[], Embed[], MessageFlags, PollProperties)"/>
         protected virtual Task<IUserMessage> ReplyAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null,
diff --git a/src/Discord.Net.Interactions/RestInteractionModuleBase.cs b/src/Discord.Net.Interactions/RestInteractionModuleBase.cs
index 1778b7efec..cdcc4c89d6 100644
--- a/src/Discord.Net.Interactions/RestInteractionModuleBase.cs
+++ b/src/Discord.Net.Interactions/RestInteractionModuleBase.cs
@@ -43,8 +43,8 @@ protected override Task DeferAsync(bool ephemeral = false, RequestOptions option
         ///     A Task representing the operation of creating the interaction response.
         /// </returns>
         /// <exception cref="InvalidOperationException">Thrown if the interaction isn't a type of <see cref="RestInteraction"/>.</exception>
-        protected override Task RespondAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent components = null, Embed embed = null, PollProperties poll = null)
-            => HandleInteractionAsync(x => x.Respond(text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll));
+        protected override Task RespondAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, RequestOptions options = null, MessageComponent components = null, Embed embed = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None)
+            => HandleInteractionAsync(x => x.Respond(text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags));
 
         /// <summary>
         ///     Responds to the interaction with a modal.
diff --git a/src/Discord.Net.Rest/API/Common/ForumThreadMessage.cs b/src/Discord.Net.Rest/API/Common/ForumThreadMessage.cs
index c64920e33a..8874324677 100644
--- a/src/Discord.Net.Rest/API/Common/ForumThreadMessage.cs
+++ b/src/Discord.Net.Rest/API/Common/ForumThreadMessage.cs
@@ -17,7 +17,7 @@ internal class ForumThreadMessage
     public Optional<AllowedMentions> AllowedMentions { get; set; }
 
     [JsonProperty("components")]
-    public Optional<API.ActionRowComponent[]> Components { get; set; }
+    public Optional<IMessageComponent[]> Components { get; set; }
 
     [JsonProperty("sticker_ids")]
     public Optional<ulong[]> Stickers { get; set; }
diff --git a/src/Discord.Net.Rest/API/Common/InteractionCallbackData.cs b/src/Discord.Net.Rest/API/Common/InteractionCallbackData.cs
index 0b3302dde0..2596c490da 100644
--- a/src/Discord.Net.Rest/API/Common/InteractionCallbackData.cs
+++ b/src/Discord.Net.Rest/API/Common/InteractionCallbackData.cs
@@ -21,7 +21,7 @@ internal class InteractionCallbackData
         public Optional<MessageFlags> Flags { get; set; }
 
         [JsonProperty("components")]
-        public Optional<ActionRowComponent[]> Components { get; set; }
+        public Optional<IMessageComponent[]> Components { get; set; }
 
         [JsonProperty("choices")]
         public Optional<ApplicationCommandOptionChoice[]> Choices { get; set; }
diff --git a/src/Discord.Net.Rest/API/Common/TextDisplayComponent.cs b/src/Discord.Net.Rest/API/Common/TextDisplayComponent.cs
new file mode 100644
index 0000000000..04d66e06c9
--- /dev/null
+++ b/src/Discord.Net.Rest/API/Common/TextDisplayComponent.cs
@@ -0,0 +1,26 @@
+using Newtonsoft.Json;
+
+namespace Discord.API;
+
+internal class TextDisplayComponent : IMessageComponent
+{
+    [JsonProperty("type")]
+    public ComponentType Type { get; set; }
+
+    [JsonProperty("id")]
+    public Optional<int> Id { get; set; }
+
+    [JsonProperty("content")]
+    public string Content { get; set; }
+
+    public TextDisplayComponent() { }
+
+    public TextDisplayComponent(Discord.TextDisplayComponent component)
+    {
+        Type = component.Type;
+        Id = component.Id ?? Optional<int>.Unspecified;
+        Content = component.Content;
+    }
+
+    int? IMessageComponent.Id => Id.ToNullable();
+}
diff --git a/src/Discord.Net.Rest/API/Rest/CreateMessageParams.cs b/src/Discord.Net.Rest/API/Rest/CreateMessageParams.cs
index e22f8fbd47..42174ab944 100644
--- a/src/Discord.Net.Rest/API/Rest/CreateMessageParams.cs
+++ b/src/Discord.Net.Rest/API/Rest/CreateMessageParams.cs
@@ -24,7 +24,7 @@ internal class CreateMessageParams
         public Optional<MessageReference> MessageReference { get; set; }
 
         [JsonProperty("components")]
-        public Optional<API.ActionRowComponent[]> Components { get; set; }
+        public Optional<IMessageComponent[]> Components { get; set; }
 
         [JsonProperty("sticker_ids")]
         public Optional<ulong[]> Stickers { get; set; }
diff --git a/src/Discord.Net.Rest/API/Rest/CreateMultipartPostAsync.cs b/src/Discord.Net.Rest/API/Rest/CreateMultipartPostAsync.cs
index 85cffe13f4..710e14ac42 100644
--- a/src/Discord.Net.Rest/API/Rest/CreateMultipartPostAsync.cs
+++ b/src/Discord.Net.Rest/API/Rest/CreateMultipartPostAsync.cs
@@ -24,7 +24,7 @@ internal class CreateMultipartPostAsync
         public Optional<string> Content { get; set; }
         public Optional<Embed[]> Embeds { get; set; }
         public Optional<AllowedMentions> AllowedMentions { get; set; }
-        public Optional<ActionRowComponent[]> MessageComponent { get; set; }
+        public Optional<IMessageComponent[]> MessageComponent { get; set; }
         public Optional<MessageFlags?> Flags { get; set; }
         public Optional<ulong[]> Stickers { get; set; }
         public Optional<ulong[]> TagIds { get; set; }
diff --git a/src/Discord.Net.Rest/API/Rest/CreatePostParams.cs b/src/Discord.Net.Rest/API/Rest/CreatePostParams.cs
index d74678f631..f568cb9dde 100644
--- a/src/Discord.Net.Rest/API/Rest/CreatePostParams.cs
+++ b/src/Discord.Net.Rest/API/Rest/CreatePostParams.cs
@@ -1,28 +1,22 @@
 using Newtonsoft.Json;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
-namespace Discord.API.Rest
+namespace Discord.API.Rest;
+
+internal class CreatePostParams
 {
-    internal class CreatePostParams
-    {
-        // thread
-        [JsonProperty("name")]
-        public string Title { get; set; }
+    // thread
+    [JsonProperty("name")]
+    public string Title { get; set; }
 
-        [JsonProperty("auto_archive_duration")]
-        public ThreadArchiveDuration ArchiveDuration { get; set; }
+    [JsonProperty("auto_archive_duration")]
+    public ThreadArchiveDuration ArchiveDuration { get; set; }
 
-        [JsonProperty("rate_limit_per_user")]
-        public Optional<int?> Slowmode { get; set; }
+    [JsonProperty("rate_limit_per_user")]
+    public Optional<int?> Slowmode { get; set; }
 
-        [JsonProperty("message")]
-        public ForumThreadMessage Message { get; set; }
+    [JsonProperty("message")]
+    public ForumThreadMessage Message { get; set; }
 
-        [JsonProperty("applied_tags")]
-        public Optional<ulong[]> Tags { get; set; }
-    }
+    [JsonProperty("applied_tags")]
+    public Optional<ulong[]> Tags { get; set; }
 }
diff --git a/src/Discord.Net.Rest/API/Rest/CreateWebhookMessageParams.cs b/src/Discord.Net.Rest/API/Rest/CreateWebhookMessageParams.cs
index 52dd1806dc..0258425c7b 100644
--- a/src/Discord.Net.Rest/API/Rest/CreateWebhookMessageParams.cs
+++ b/src/Discord.Net.Rest/API/Rest/CreateWebhookMessageParams.cs
@@ -37,7 +37,7 @@ internal class CreateWebhookMessageParams
         public Optional<MessageFlags> Flags { get; set; }
 
         [JsonProperty("components")]
-        public Optional<API.ActionRowComponent[]> Components { get; set; }
+        public Optional<IMessageComponent[]> Components { get; set; }
 
         [JsonProperty("file")]
         public Optional<MultipartFile> File { get; set; }
diff --git a/src/Discord.Net.Rest/API/Rest/ModifyInteractionResponseParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyInteractionResponseParams.cs
index a2c7cbee6b..2afc464132 100644
--- a/src/Discord.Net.Rest/API/Rest/ModifyInteractionResponseParams.cs
+++ b/src/Discord.Net.Rest/API/Rest/ModifyInteractionResponseParams.cs
@@ -14,7 +14,7 @@ internal class ModifyInteractionResponseParams
         public Optional<AllowedMentions> AllowedMentions { get; set; }
 
         [JsonProperty("components")]
-        public Optional<ActionRowComponent[]> Components { get; set; }
+        public Optional<IMessageComponent[]> Components { get; set; }
 
         [JsonProperty("flags")]
         public Optional<MessageFlags?> Flags { get; set; }
diff --git a/src/Discord.Net.Rest/API/Rest/ModifyMessageParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyMessageParams.cs
index 3dba45a5b7..3bee06750d 100644
--- a/src/Discord.Net.Rest/API/Rest/ModifyMessageParams.cs
+++ b/src/Discord.Net.Rest/API/Rest/ModifyMessageParams.cs
@@ -1,19 +1,21 @@
 using Newtonsoft.Json;
 
-namespace Discord.API.Rest
+namespace Discord.API.Rest;
+
+internal class ModifyMessageParams
 {
-    [JsonObject(MemberSerialization = MemberSerialization.OptIn)]
-    internal class ModifyMessageParams
-    {
-        [JsonProperty("content")]
-        public Optional<string> Content { get; set; }
-        [JsonProperty("embeds")]
-        public Optional<API.Embed[]> Embeds { get; set; }
-        [JsonProperty("components")]
-        public Optional<API.ActionRowComponent[]> Components { get; set; }
-        [JsonProperty("flags")]
-        public Optional<MessageFlags?> Flags { get; set; }
-        [JsonProperty("allowed_mentions")]
-        public Optional<AllowedMentions> AllowedMentions { get; set; }
-    }
+    [JsonProperty("content")]
+    public Optional<string> Content { get; set; }
+
+    [JsonProperty("embeds")]
+    public Optional<Embed[]> Embeds { get; set; }
+
+    [JsonProperty("components")]
+    public Optional<IMessageComponent[]> Components { get; set; }
+
+    [JsonProperty("flags")]
+    public Optional<MessageFlags?> Flags { get; set; }
+
+    [JsonProperty("allowed_mentions")]
+    public Optional<AllowedMentions> AllowedMentions { get; set; }
 }
diff --git a/src/Discord.Net.Rest/API/Rest/ModifyWebhookMessageParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyWebhookMessageParams.cs
index e73efaf36a..db80eba75a 100644
--- a/src/Discord.Net.Rest/API/Rest/ModifyWebhookMessageParams.cs
+++ b/src/Discord.Net.Rest/API/Rest/ModifyWebhookMessageParams.cs
@@ -1,17 +1,18 @@
 using Newtonsoft.Json;
 
-namespace Discord.API.Rest
+namespace Discord.API.Rest;
+
+internal class ModifyWebhookMessageParams
 {
-    [JsonObject(MemberSerialization = MemberSerialization.OptIn)]
-    internal class ModifyWebhookMessageParams
-    {
-        [JsonProperty("content")]
-        public Optional<string> Content { get; set; }
-        [JsonProperty("embeds")]
-        public Optional<Embed[]> Embeds { get; set; }
-        [JsonProperty("allowed_mentions")]
-        public Optional<AllowedMentions> AllowedMentions { get; set; }
-        [JsonProperty("components")]
-        public Optional<API.ActionRowComponent[]> Components { get; set; }
-    }
+    [JsonProperty("content")]
+    public Optional<string> Content { get; set; }
+
+    [JsonProperty("embeds")]
+    public Optional<Embed[]> Embeds { get; set; }
+
+    [JsonProperty("allowed_mentions")]
+    public Optional<AllowedMentions> AllowedMentions { get; set; }
+
+    [JsonProperty("components")]
+    public Optional<IMessageComponent[]> Components { get; set; }
 }
diff --git a/src/Discord.Net.Rest/API/Rest/UploadFileParams.cs b/src/Discord.Net.Rest/API/Rest/UploadFileParams.cs
index c1e4309eed..5a10353428 100644
--- a/src/Discord.Net.Rest/API/Rest/UploadFileParams.cs
+++ b/src/Discord.Net.Rest/API/Rest/UploadFileParams.cs
@@ -21,7 +21,7 @@ internal class UploadFileParams
         public Optional<Embed[]> Embeds { get; set; }
         public Optional<AllowedMentions> AllowedMentions { get; set; }
         public Optional<MessageReference> MessageReference { get; set; }
-        public Optional<ActionRowComponent[]> MessageComponent { get; set; }
+        public Optional<IMessageComponent[]> MessageComponent { get; set; }
         public Optional<MessageFlags?> Flags { get; set; }
         public Optional<ulong[]> Stickers { get; set; }
         public Optional<CreatePollParams> Poll { get; set; }
diff --git a/src/Discord.Net.Rest/API/Rest/UploadInteractionFileParams.cs b/src/Discord.Net.Rest/API/Rest/UploadInteractionFileParams.cs
index dcdc7e65a8..26697ecbe9 100644
--- a/src/Discord.Net.Rest/API/Rest/UploadInteractionFileParams.cs
+++ b/src/Discord.Net.Rest/API/Rest/UploadInteractionFileParams.cs
@@ -21,7 +21,7 @@ internal class UploadInteractionFileParams
         public Optional<bool> IsTTS { get; set; }
         public Optional<Embed[]> Embeds { get; set; }
         public Optional<AllowedMentions> AllowedMentions { get; set; }
-        public Optional<ActionRowComponent[]> MessageComponents { get; set; }
+        public Optional<IMessageComponent[]> MessageComponents { get; set; }
         public Optional<MessageFlags> Flags { get; set; }
         public Optional<CreatePollParams> Poll { get; set; }
 
@@ -66,9 +66,9 @@ public IReadOnlyDictionary<string, object> ToDictionary()
             if (Poll.IsSpecified)
                 data["poll"] = Poll.Value;
 
-            List<object> attachments = new();
+            List<object> attachments = [];
 
-            for (int n = 0; n != Files.Length; n++)
+            for (var n = 0; n != Files.Length; n++)
             {
                 var attachment = Files[n];
 
diff --git a/src/Discord.Net.Rest/API/Rest/UploadWebhookFileParams.cs b/src/Discord.Net.Rest/API/Rest/UploadWebhookFileParams.cs
index f33eecfb9d..6c404475cc 100644
--- a/src/Discord.Net.Rest/API/Rest/UploadWebhookFileParams.cs
+++ b/src/Discord.Net.Rest/API/Rest/UploadWebhookFileParams.cs
@@ -22,7 +22,7 @@ internal class UploadWebhookFileParams
         public Optional<string> AvatarUrl { get; set; }
         public Optional<Embed[]> Embeds { get; set; }
         public Optional<AllowedMentions> AllowedMentions { get; set; }
-        public Optional<ActionRowComponent[]> MessageComponents { get; set; }
+        public Optional<IMessageComponent[]> MessageComponents { get; set; }
         public Optional<MessageFlags> Flags { get; set; }
         public Optional<string> ThreadName { get; set; }
         public Optional<ulong[]> AppliedTags { get; set; }
diff --git a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
index 3fd65d02d6..0c8e2395bb 100644
--- a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
+++ b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
@@ -320,7 +320,7 @@ public static async Task<RestUserMessage> SendMessageAsync(IMessageChannel chann
                 Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified,
                 AllowedMentions = allowedMentions?.ToModel(),
                 MessageReference = messageReference?.ToModel(),
-                Components = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
+                Components = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
                 Stickers = stickers?.Any() ?? false ? stickers.Select(x => x.Id).ToArray() : Optional<ulong[]>.Unspecified,
                 Flags = flags,
                 Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
@@ -449,7 +449,7 @@ public static async Task<RestUserMessage> SendFilesAsync(IMessageChannel channel
                 Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified,
                 AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified,
                 MessageReference = messageReference?.ToModel() ?? Optional<API.MessageReference>.Unspecified,
-                MessageComponent = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
+                MessageComponent = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
                 Stickers = stickers?.Any() ?? false ? stickers.Select(x => x.Id).ToArray() : Optional<ulong[]>.Unspecified,
                 Flags = flags,
                 Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
diff --git a/src/Discord.Net.Rest/Entities/Channels/ThreadHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ThreadHelper.cs
index a133211d8e..29be16cb47 100644
--- a/src/Discord.Net.Rest/Entities/Channels/ThreadHelper.cs
+++ b/src/Discord.Net.Rest/Entities/Channels/ThreadHelper.cs
@@ -163,7 +163,7 @@ public static async Task<RestThreadChannel> CreatePostAsync(IForumChannel channe
                     Content = text,
                     Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified,
                     Flags = flags,
-                    Components = components?.Components?.Any() ?? false ? components.Components.Select(x => new API.ActionRowComponent(x)).ToArray() : Optional<API.ActionRowComponent[]>.Unspecified,
+                    Components = components?.Components?.Any() ?? false ? components.Components.Select(x => x.ToModel()).ToArray() : Optional<IMessageComponent[]>.Unspecified,
                     Stickers = stickers?.Any() ?? false ? stickers.Select(x => x.Id).ToArray() : Optional<ulong[]>.Unspecified,
                 },
                 Tags = tagIds
@@ -224,7 +224,7 @@ public static async Task<RestThreadChannel> CreatePostAsync(IForumChannel channe
                 Content = text,
                 Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified,
                 Flags = flags,
-                MessageComponent = components?.Components?.Any() ?? false ? components.Components.Select(x => new API.ActionRowComponent(x)).ToArray() : Optional<API.ActionRowComponent[]>.Unspecified,
+                MessageComponent = components?.Components?.Any() ?? false ? components.Components.Select(x => x.ToModel()).ToArray() : Optional<IMessageComponent[]>.Unspecified,
                 Slowmode = slowmode,
                 Stickers = stickers?.Any() ?? false ? stickers.Select(x => x.Id).ToArray() : Optional<ulong[]>.Unspecified,
                 Title = title,
diff --git a/src/Discord.Net.Rest/Entities/Interactions/CommandBase/RestCommandBase.cs b/src/Discord.Net.Rest/Entities/Interactions/CommandBase/RestCommandBase.cs
index dae9b890d7..ef16c0f55c 100644
--- a/src/Discord.Net.Rest/Entities/Interactions/CommandBase/RestCommandBase.cs
+++ b/src/Discord.Net.Rest/Entities/Interactions/CommandBase/RestCommandBase.cs
@@ -1,12 +1,11 @@
 using Discord.API.Rest;
-using Discord.Net.Rest;
+
 using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
-using System.Text;
 using System.Threading.Tasks;
-using DataModel = Discord.API.ApplicationCommandInteractionData;
+
 using Model = Discord.API.Interaction;
 
 namespace Discord.Rest
@@ -81,7 +80,8 @@ public override string Respond(
             MessageComponent components = null,
             Embed embed = null,
             RequestOptions options = null,
-            PollProperties poll = null)
+            PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
             if (!IsValidToken)
                 throw new InvalidOperationException("Interaction token is no longer valid");
@@ -89,7 +89,7 @@ public override string Respond(
             if (!InteractionHelper.CanSendResponse(this) && Discord.ResponseInternalTimeCheck)
                 throw new TimeoutException($"Cannot respond to an interaction after {InteractionHelper.ResponseTimeLimit} seconds!");
 
-            embeds ??= Array.Empty<Embed>();
+            embeds ??= [];
             if (embed != null)
                 embeds = new[] { embed }.Concat(embeds).ToArray();
 
@@ -123,8 +123,12 @@ public override string Respond(
                     AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified,
                     Embeds = embeds.Select(x => x.ToModel()).ToArray(),
                     TTS = isTTS,
-                    Components = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
-                    Flags = ephemeral ? MessageFlags.Ephemeral : Optional<MessageFlags>.Unspecified,
+                    Components = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
+                    Flags = ephemeral
+                        ? flags | MessageFlags.Ephemeral
+                        : flags == MessageFlags.None
+                            ? Optional<MessageFlags>.Unspecified
+                            : flags,
                     Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
                 }
             };
@@ -152,12 +156,13 @@ public override Task<RestFollowupMessage> FollowupAsync(
             MessageComponent components = null,
             Embed embed = null,
             RequestOptions options = null,
-            PollProperties poll = null)
+            PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
             if (!IsValidToken)
                 throw new InvalidOperationException("Interaction token is no longer valid");
 
-            embeds ??= Array.Empty<Embed>();
+            embeds ??= [];
             if (embed != null)
                 embeds = new[] { embed }.Concat(embeds).ToArray();
 
@@ -172,13 +177,15 @@ public override Task<RestFollowupMessage> FollowupAsync(
                 AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified,
                 IsTTS = isTTS,
                 Embeds = embeds.Select(x => x.ToModel()).ToArray(),
-                Components = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
-                Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
+                Components = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
+                Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified,
+                Flags = ephemeral
+                    ? flags | MessageFlags.Ephemeral
+                    : flags == MessageFlags.None
+                        ? Optional<MessageFlags>.Unspecified
+                        : flags,
             };
 
-            if (ephemeral)
-                args.Flags = MessageFlags.Ephemeral;
-
             return InteractionHelper.SendFollowupAsync(Discord, args, Token, Channel, options);
         }
 
@@ -194,7 +201,8 @@ public override async Task<RestFollowupMessage> FollowupWithFileAsync(
             MessageComponent components = null,
             Embed embed = null,
             RequestOptions options = null,
-            PollProperties poll = null)
+            PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
             if (!IsValidToken)
                 throw new InvalidOperationException("Interaction token is no longer valid");
@@ -203,7 +211,7 @@ public override async Task<RestFollowupMessage> FollowupWithFileAsync(
             Preconditions.NotNullOrEmpty(fileName, nameof(fileName), "File Name must not be empty or null");
 
             using (var file = new FileAttachment(fileStream, fileName))
-                return await FollowupWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll).ConfigureAwait(false);
+                return await FollowupWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags).ConfigureAwait(false);
         }
 
         /// <inheritdoc/>
@@ -218,7 +226,8 @@ public override async Task<RestFollowupMessage> FollowupWithFileAsync(
             MessageComponent components = null,
             Embed embed = null,
             RequestOptions options = null,
-            PollProperties poll = null)
+            PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
             Preconditions.NotNullOrEmpty(filePath, nameof(filePath), "Path must exist");
 
@@ -226,7 +235,7 @@ public override async Task<RestFollowupMessage> FollowupWithFileAsync(
             Preconditions.NotNullOrEmpty(fileName, nameof(fileName), "File Name must not be empty or null");
 
             using (var file = new FileAttachment(File.OpenRead(filePath), fileName))
-                return await FollowupWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll).ConfigureAwait(false);
+                return await FollowupWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags).ConfigureAwait(false);
         }
 
         /// <inheritdoc/>
@@ -240,9 +249,10 @@ public override Task<RestFollowupMessage> FollowupWithFileAsync(
             MessageComponent components = null,
             Embed embed = null,
             RequestOptions options = null,
-            PollProperties poll = null)
+            PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
-            return FollowupWithFilesAsync(new FileAttachment[] { attachment }, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll);
+            return FollowupWithFilesAsync([attachment], text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags);
         }
 
         /// <inheritdoc/>
@@ -256,12 +266,13 @@ public override Task<RestFollowupMessage> FollowupWithFilesAsync(
             MessageComponent components = null,
             Embed embed = null,
             RequestOptions options = null,
-            PollProperties poll = null)
+            PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
             if (!IsValidToken)
                 throw new InvalidOperationException("Interaction token is no longer valid");
 
-            embeds ??= Array.Empty<Embed>();
+            embeds ??= [];
             if (embed != null)
                 embeds = new[] { embed }.Concat(embeds).ToArray();
 
@@ -289,23 +300,22 @@ public override Task<RestFollowupMessage> FollowupWithFilesAsync(
                 {
                     throw new ArgumentException("The Roles flag is mutually exclusive with the list of Role Ids.", nameof(allowedMentions));
                 }
-            }
-
-            var flags = MessageFlags.None;
-
-            if (ephemeral)
-                flags |= MessageFlags.Ephemeral;
+            };
 
             var args = new API.Rest.UploadWebhookFileParams(attachments.ToArray())
-                {
-                    Flags = flags,
-                    Content = text,
-                    IsTTS = isTTS,
-                    Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified,
-                    AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified,
-                    MessageComponents = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
-                    Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
-                };
+            {
+                Flags = ephemeral
+                    ? flags | MessageFlags.Ephemeral
+                    : flags == MessageFlags.None
+                        ? Optional<MessageFlags>.Unspecified
+                        : flags,
+                Content = text,
+                IsTTS = isTTS,
+                Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified,
+                AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified,
+                MessageComponents = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
+                Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
+            };
             return InteractionHelper.SendFollowupAsync(Discord, args, Token, Channel, options);
         }
 
diff --git a/src/Discord.Net.Rest/Entities/Interactions/InteractionHelper.cs b/src/Discord.Net.Rest/Entities/Interactions/InteractionHelper.cs
index 6afbf74e98..ce07a2940b 100644
--- a/src/Discord.Net.Rest/Entities/Interactions/InteractionHelper.cs
+++ b/src/Discord.Net.Rest/Entities/Interactions/InteractionHelper.cs
@@ -430,8 +430,8 @@ public static Task DeleteUnknownApplicationCommandAsync(BaseDiscordClient client
                 Embeds = apiEmbeds?.ToArray() ?? Optional<API.Embed[]>.Unspecified,
                 AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value.ToModel() : Optional<API.AllowedMentions>.Unspecified,
                 Components = args.Components.IsSpecified
-                        ? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Array.Empty<API.ActionRowComponent>()
-                        : Optional<API.ActionRowComponent[]>.Unspecified,
+                        ? args.Components.Value?.Components.Select(x => x.ToModel()).ToArray() ?? []
+                        : Optional<IMessageComponent[]>.Unspecified,
             };
 
             return client.ApiClient.ModifyInteractionFollowupMessageAsync(apiArgs, message.Id, message.Token, options);
@@ -478,8 +478,7 @@ public static Task DeleteFollowupMessageAsync(BaseDiscordClient client, RestFoll
                     Embeds = apiEmbeds?.ToArray() ?? Optional<API.Embed[]>.Unspecified,
                     AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value?.ToModel() : Optional<API.AllowedMentions>.Unspecified,
                     Components = args.Components.IsSpecified
-                        ? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Array.Empty<API.ActionRowComponent>()
-                        : Optional<API.ActionRowComponent[]>.Unspecified,
+                        ? args.Components.Value?.Components.Select(x => x.ToModel()).ToArray() ?? [] : Optional<IMessageComponent[]>.Unspecified,
                     Flags = args.Flags
                 };
 
@@ -495,8 +494,7 @@ public static Task DeleteFollowupMessageAsync(BaseDiscordClient client, RestFoll
                     Embeds = apiEmbeds?.ToArray() ?? Optional<API.Embed[]>.Unspecified,
                     AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value?.ToModel() : Optional<API.AllowedMentions>.Unspecified,
                     MessageComponents = args.Components.IsSpecified
-                        ? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Array.Empty<API.ActionRowComponent>()
-                        : Optional<API.ActionRowComponent[]>.Unspecified
+                        ? args.Components.Value?.Components.Select(x => x.ToModel()).ToArray() ?? [] : Optional<IMessageComponent[]>.Unspecified
                 };
 
                 return client.ApiClient.ModifyInteractionResponseAsync(apiArgs, token, options);
diff --git a/src/Discord.Net.Rest/Entities/Interactions/MessageComponents/RestMessageComponent.cs b/src/Discord.Net.Rest/Entities/Interactions/MessageComponents/RestMessageComponent.cs
index 811b45605e..8748ef4a37 100644
--- a/src/Discord.Net.Rest/Entities/Interactions/MessageComponents/RestMessageComponent.cs
+++ b/src/Discord.Net.Rest/Entities/Interactions/MessageComponents/RestMessageComponent.cs
@@ -77,7 +77,8 @@ public override string Respond(
             MessageComponent components = null,
             Embed embed = null,
             RequestOptions options = null,
-            PollProperties poll = null)
+            PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
             if (!IsValidToken)
                 throw new InvalidOperationException("Interaction token is no longer valid");
@@ -85,7 +86,7 @@ public override string Respond(
             if (!InteractionHelper.CanSendResponse(this) && Discord.ResponseInternalTimeCheck)
                 throw new TimeoutException($"Cannot respond to an interaction after {InteractionHelper.ResponseTimeLimit} seconds!");
 
-            embeds ??= Array.Empty<Embed>();
+            embeds ??= [];
             if (embed != null)
                 embeds = new[] { embed }.Concat(embeds).ToArray();
 
@@ -119,14 +120,16 @@ public override string Respond(
                     AllowedMentions = allowedMentions?.ToModel(),
                     Embeds = embeds.Select(x => x.ToModel()).ToArray(),
                     TTS = isTTS,
-                    Components = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
-                    Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
+                    Components = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
+                    Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified,
+                    Flags = ephemeral
+                        ? flags | MessageFlags.Ephemeral
+                        : flags == MessageFlags.None
+                            ? Optional<MessageFlags>.Unspecified
+                            : flags,
                 }
             };
 
-            if (ephemeral)
-                response.Data.Value.Flags = MessageFlags.Ephemeral;
-
             lock (_lock)
             {
                 if (HasResponded)
@@ -208,8 +211,8 @@ public async Task UpdateAsync(Action<MessageProperties> func, RequestOptions opt
                         AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value?.ToModel() : Optional<API.AllowedMentions>.Unspecified,
                         Embeds = apiEmbeds?.ToArray() ?? Optional<API.Embed[]>.Unspecified,
                         Components = args.Components.IsSpecified
-                            ? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Array.Empty<API.ActionRowComponent>()
-                            : Optional<API.ActionRowComponent[]>.Unspecified,
+                            ? args.Components.Value?.Components.Select(x => x.ToModel()).ToArray() ?? []
+                            : Optional<IMessageComponent[]>.Unspecified,
                         Flags = args.Flags.IsSpecified ? args.Flags.Value ?? Optional<MessageFlags>.Unspecified : Optional<MessageFlags>.Unspecified
                     }
                 };
@@ -227,8 +230,8 @@ public async Task UpdateAsync(Action<MessageProperties> func, RequestOptions opt
                     AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value?.ToModel() : Optional<API.AllowedMentions>.Unspecified,
                     Embeds = apiEmbeds?.ToArray() ?? Optional<API.Embed[]>.Unspecified,
                     MessageComponents = args.Components.IsSpecified
-                        ? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Array.Empty<API.ActionRowComponent>()
-                        : Optional<API.ActionRowComponent[]>.Unspecified,
+                        ? args.Components.Value?.Components.Select(x => x.ToModel()).ToArray() ?? []
+                        : Optional<IMessageComponent[]>.Unspecified,
                     Flags = args.Flags.IsSpecified ? args.Flags.Value ?? Optional<MessageFlags>.Unspecified : Optional<MessageFlags>.Unspecified
                 };
 
@@ -256,12 +259,13 @@ public override Task<RestFollowupMessage> FollowupAsync(
             MessageComponent components = null,
             Embed embed = null,
             RequestOptions options = null,
-            PollProperties poll = null)
+            PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
             if (!IsValidToken)
                 throw new InvalidOperationException("Interaction token is no longer valid");
 
-            embeds ??= Array.Empty<Embed>();
+            embeds ??= [];
             if (embed != null)
                 embeds = new[] { embed }.Concat(embeds).ToArray();
 
@@ -276,14 +280,15 @@ public override Task<RestFollowupMessage> FollowupAsync(
                 AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified,
                 IsTTS = isTTS,
                 Embeds = embeds.Select(x => x.ToModel()).ToArray(),
-                Components = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
-                Flags = ephemeral ? MessageFlags.Ephemeral : MessageFlags.None,
+                Components = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
+                Flags = ephemeral
+                    ? flags | MessageFlags.Ephemeral
+                    : flags == MessageFlags.None
+                        ? Optional<MessageFlags>.Unspecified
+                        : flags,
                 Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
             };
 
-            if (ephemeral)
-                args.Flags = MessageFlags.Ephemeral;
-
             return InteractionHelper.SendFollowupAsync(Discord, args, Token, Channel, options);
         }
 
@@ -299,7 +304,8 @@ public override async Task<RestFollowupMessage> FollowupWithFileAsync(
             MessageComponent components = null,
             Embed embed = null,
             RequestOptions options = null,
-            PollProperties poll = null)
+            PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
             if (!IsValidToken)
                 throw new InvalidOperationException("Interaction token is no longer valid");
@@ -308,7 +314,7 @@ public override async Task<RestFollowupMessage> FollowupWithFileAsync(
             Preconditions.NotNullOrEmpty(fileName, nameof(fileName), "File Name must not be empty or null");
 
             using (var file = new FileAttachment(fileStream, fileName))
-                return await FollowupWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll).ConfigureAwait(false);
+                return await FollowupWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags).ConfigureAwait(false);
         }
 
         /// <inheritdoc/>
@@ -323,7 +329,8 @@ public override async Task<RestFollowupMessage> FollowupWithFileAsync(
             MessageComponent components = null,
             Embed embed = null,
             RequestOptions options = null,
-            PollProperties poll = null)
+            PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
             Preconditions.NotNullOrEmpty(filePath, nameof(filePath), "Path must exist");
 
@@ -331,7 +338,7 @@ public override async Task<RestFollowupMessage> FollowupWithFileAsync(
             Preconditions.NotNullOrEmpty(fileName, nameof(fileName), "File Name must not be empty or null");
 
             using (var file = new FileAttachment(File.OpenRead(filePath), fileName))
-                return await FollowupWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll).ConfigureAwait(false);
+                return await FollowupWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags).ConfigureAwait(false);
         }
 
         /// <inheritdoc/>
@@ -345,9 +352,10 @@ public override Task<RestFollowupMessage> FollowupWithFileAsync(
             MessageComponent components = null,
             Embed embed = null,
             RequestOptions options = null,
-            PollProperties poll = null)
+            PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
-            return FollowupWithFilesAsync(new FileAttachment[] { attachment }, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll);
+            return FollowupWithFilesAsync([attachment], text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags);
         }
 
         /// <inheritdoc/>
@@ -361,12 +369,13 @@ public override Task<RestFollowupMessage> FollowupWithFilesAsync(
             MessageComponent components = null,
             Embed embed = null,
             RequestOptions options = null,
-            PollProperties poll = null)
+            PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
             if (!IsValidToken)
                 throw new InvalidOperationException("Interaction token is no longer valid");
 
-            embeds ??= Array.Empty<Embed>();
+            embeds ??= [];
             if (embed != null)
                 embeds = new[] { embed }.Concat(embeds).ToArray();
 
@@ -396,19 +405,18 @@ public override Task<RestFollowupMessage> FollowupWithFilesAsync(
                 }
             }
 
-            var flags = MessageFlags.None;
-
-            if (ephemeral)
-                flags |= MessageFlags.Ephemeral;
-
             var args = new API.Rest.UploadWebhookFileParams(attachments.ToArray())
             {
-                Flags = flags,
+                Flags = ephemeral
+                    ? flags | MessageFlags.Ephemeral
+                    : flags == MessageFlags.None
+                        ? Optional<MessageFlags>.Unspecified
+                        : flags,
                 Content = text,
                 IsTTS = isTTS,
                 Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified,
                 AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified,
-                MessageComponents = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
+                MessageComponents = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
                 Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
             };
             return InteractionHelper.SendFollowupAsync(Discord, args, Token, Channel, options);
diff --git a/src/Discord.Net.Rest/Entities/Interactions/Modals/RestModal.cs b/src/Discord.Net.Rest/Entities/Interactions/Modals/RestModal.cs
index edeeea0b90..e7d08e4192 100644
--- a/src/Discord.Net.Rest/Entities/Interactions/Modals/RestModal.cs
+++ b/src/Discord.Net.Rest/Entities/Interactions/Modals/RestModal.cs
@@ -3,6 +3,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.ComponentModel;
 using System.IO;
 using System.Linq;
 using System.Reflection;
@@ -137,12 +138,13 @@ public override Task<RestFollowupMessage> FollowupAsync(
             MessageComponent component = null,
             Embed embed = null,
             RequestOptions options = null,
-            PollProperties poll = null)
+            PollProperties poll = null, 
+            MessageFlags flags = MessageFlags.None)
         {
             if (!IsValidToken)
                 throw new InvalidOperationException("Interaction token is no longer valid");
 
-            embeds ??= Array.Empty<Embed>();
+            embeds ??= [];
             if (embed != null)
                 embeds = new[] { embed }.Concat(embeds).ToArray();
 
@@ -157,13 +159,15 @@ public override Task<RestFollowupMessage> FollowupAsync(
                 AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified,
                 IsTTS = isTTS,
                 Embeds = embeds.Select(x => x.ToModel()).ToArray(),
-                Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ??Optional<API.ActionRowComponent[]>.Unspecified,
-                Poll = poll?.ToModel() ?? Optional<API.Rest.CreatePollParams>.Unspecified
+                Components = component?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
+                Poll = poll?.ToModel() ?? Optional<API.Rest.CreatePollParams>.Unspecified,
+                Flags = ephemeral
+                    ? flags | MessageFlags.Ephemeral
+                    : flags == MessageFlags.None
+                        ? Optional<MessageFlags>.Unspecified
+                        : flags,
             };
 
-            if (ephemeral)
-                args.Flags = MessageFlags.Ephemeral;
-
             return InteractionHelper.SendFollowupAsync(Discord, args, Token, Channel, options);
         }
 
@@ -194,12 +198,13 @@ public override Task<RestFollowupMessage> FollowupWithFileAsync(
             MessageComponent component = null,
             Embed embed = null,
             RequestOptions options = null,
-            PollProperties poll = null)
+            PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
             if (!IsValidToken)
                 throw new InvalidOperationException("Interaction token is no longer valid");
 
-            embeds ??= Array.Empty<Embed>();
+            embeds ??= [];
             if (embed != null)
                 embeds = new[] { embed }.Concat(embeds).ToArray();
 
@@ -216,14 +221,16 @@ public override Task<RestFollowupMessage> FollowupWithFileAsync(
                 AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified,
                 IsTTS = isTTS,
                 Embeds = embeds.Select(x => x.ToModel()).ToArray(),
-                Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
+                Components = component?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
                 File = fileStream is not null ? new MultipartFile(fileStream, fileName) : Optional<MultipartFile>.Unspecified,
                 Poll = poll?.ToModel() ?? Optional<API.Rest.CreatePollParams>.Unspecified,
+                Flags = ephemeral
+                    ? flags | MessageFlags.Ephemeral
+                    : flags == MessageFlags.None
+                        ? Optional<MessageFlags>.Unspecified
+                        : flags,
             };
 
-            if (ephemeral)
-                args.Flags = MessageFlags.Ephemeral;
-
             return InteractionHelper.SendFollowupAsync(Discord, args, Token, Channel, options);
         }
 
@@ -254,12 +261,13 @@ public override async Task<RestFollowupMessage> FollowupWithFileAsync(
             MessageComponent component = null,
             Embed embed = null,
             RequestOptions options = null,
-            PollProperties poll = null)
+            PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
             if (!IsValidToken)
                 throw new InvalidOperationException("Interaction token is no longer valid");
 
-            embeds ??= Array.Empty<Embed>();
+            embeds ??= [];
             if (embed != null)
                 embeds = new[] { embed }.Concat(embeds).ToArray();
 
@@ -279,14 +287,16 @@ public override async Task<RestFollowupMessage> FollowupWithFileAsync(
                 AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified,
                 IsTTS = isTTS,
                 Embeds = embeds.Select(x => x.ToModel()).ToArray(),
-                Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
+                Components = component?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
                 File = fileStream != null ? new MultipartFile(fileStream, fileName) : Optional<MultipartFile>.Unspecified,
-                Poll = poll?.ToModel() ?? Optional<API.Rest.CreatePollParams>.Unspecified
+                Poll = poll?.ToModel() ?? Optional<API.Rest.CreatePollParams>.Unspecified,
+                Flags = ephemeral
+                    ? flags | MessageFlags.Ephemeral
+                    : flags == MessageFlags.None
+                        ? Optional<MessageFlags>.Unspecified
+                        : flags,
             };
 
-            if (ephemeral)
-                args.Flags = MessageFlags.Ephemeral;
-
             return await InteractionHelper.SendFollowupAsync(Discord, args, Token, Channel, options);
         }
 
@@ -315,7 +325,8 @@ public override string Respond(
             MessageComponent component = null,
             Embed embed = null,
             RequestOptions options = null,
-            PollProperties poll = null)
+            PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
             if (!IsValidToken)
                 throw new InvalidOperationException("Interaction token is no longer valid");
@@ -323,7 +334,7 @@ public override string Respond(
             if (!InteractionHelper.CanSendResponse(this) && Discord.ResponseInternalTimeCheck)
                 throw new TimeoutException($"Cannot respond to an interaction after {InteractionHelper.ResponseTimeLimit} seconds!");
 
-            embeds ??= Array.Empty<Embed>();
+            embeds ??= [];
             if (embed != null)
                 embeds = new[] { embed }.Concat(embeds).ToArray();
 
@@ -357,8 +368,12 @@ public override string Respond(
                     AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified,
                     Embeds = embeds.Select(x => x.ToModel()).ToArray(),
                     TTS = isTTS,
-                    Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
-                    Flags = ephemeral ? MessageFlags.Ephemeral : Optional<MessageFlags>.Unspecified,
+                    Components = component?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
+                    Flags = ephemeral
+                        ? flags | MessageFlags.Ephemeral
+                        : flags == MessageFlags.None
+                            ? Optional<MessageFlags>.Unspecified
+                            : flags,
                     Poll = poll?.ToModel() ?? Optional<API.Rest.CreatePollParams>.Unspecified
                 }
             };
@@ -390,12 +405,13 @@ public override Task<RestFollowupMessage> FollowupWithFilesAsync(
             MessageComponent components = null,
             Embed embed = null,
             RequestOptions options = null,
-            PollProperties poll = null)
+            PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
             if (!IsValidToken)
                 throw new InvalidOperationException("Interaction token is no longer valid");
 
-            embeds ??= Array.Empty<Embed>();
+            embeds ??= [];
             if (embed != null)
                 embeds = new[] { embed }.Concat(embeds).ToArray();
 
@@ -425,19 +441,18 @@ public override Task<RestFollowupMessage> FollowupWithFilesAsync(
                 }
             }
 
-            var flags = MessageFlags.None;
-
-            if (ephemeral)
-                flags |= MessageFlags.Ephemeral;
-
             var args = new API.Rest.UploadWebhookFileParams(attachments.ToArray())
             {
-                Flags = flags,
+                Flags = ephemeral
+                    ? flags | MessageFlags.Ephemeral
+                    : flags == MessageFlags.None
+                        ? Optional<MessageFlags>.Unspecified
+                        : flags,
                 Content = text,
                 IsTTS = isTTS,
                 Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified,
                 AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified,
-                MessageComponents = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
+                MessageComponents = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
                 Poll = poll?.ToModel() ?? Optional<API.Rest.CreatePollParams>.Unspecified
             };
             return InteractionHelper.SendFollowupAsync(Discord, args, Token, Channel, options);
@@ -454,9 +469,10 @@ public override Task<RestFollowupMessage> FollowupWithFileAsync(
             MessageComponent components = null,
             Embed embed = null,
             RequestOptions options = null,
-            PollProperties poll = null)
+            PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
-            return FollowupWithFilesAsync(new FileAttachment[] { attachment }, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll);
+            return FollowupWithFilesAsync([attachment], text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags);
         }
 
         /// <inheritdoc/>
@@ -541,8 +557,8 @@ public async Task UpdateAsync(Action<MessageProperties> func, RequestOptions opt
                         AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value?.ToModel() : Optional<API.AllowedMentions>.Unspecified,
                         Embeds = apiEmbeds?.ToArray() ?? Optional<API.Embed[]>.Unspecified,
                         Components = args.Components.IsSpecified
-                            ? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Array.Empty<API.ActionRowComponent>()
-                            : Optional<API.ActionRowComponent[]>.Unspecified,
+                            ? args.Components.Value?.Components.Select(x => x.ToModel()).ToArray() ?? []
+                            : Optional<IMessageComponent[]>.Unspecified,
                         Flags = args.Flags.IsSpecified ? args.Flags.Value ?? Optional<MessageFlags>.Unspecified : Optional<MessageFlags>.Unspecified
                     }
                 };
@@ -551,7 +567,7 @@ public async Task UpdateAsync(Action<MessageProperties> func, RequestOptions opt
             }
             else
             {
-                var attachments = args.Attachments.Value?.ToArray() ?? Array.Empty<FileAttachment>();
+                var attachments = args.Attachments.Value?.ToArray() ?? [];
 
                 var response = new API.Rest.UploadInteractionFileParams(attachments)
                 {
@@ -560,8 +576,8 @@ public async Task UpdateAsync(Action<MessageProperties> func, RequestOptions opt
                     AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value?.ToModel() : Optional<API.AllowedMentions>.Unspecified,
                     Embeds = apiEmbeds?.ToArray() ?? Optional<API.Embed[]>.Unspecified,
                     MessageComponents = args.Components.IsSpecified
-                        ? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Array.Empty<API.ActionRowComponent>()
-                        : Optional<API.ActionRowComponent[]>.Unspecified,
+                        ? args.Components.Value?.Components.Select(x => x.ToModel()).ToArray() ?? []
+                        : Optional<IMessageComponent[]>.Unspecified,
                     Flags = args.Flags.IsSpecified ? args.Flags.Value ?? Optional<MessageFlags>.Unspecified : Optional<MessageFlags>.Unspecified
                 };
 
diff --git a/src/Discord.Net.Rest/Entities/Interactions/RestInteraction.cs b/src/Discord.Net.Rest/Entities/Interactions/RestInteraction.cs
index 551eb9abb8..d8713ccccb 100644
--- a/src/Discord.Net.Rest/Entities/Interactions/RestInteraction.cs
+++ b/src/Discord.Net.Rest/Entities/Interactions/RestInteraction.cs
@@ -337,7 +337,7 @@ public async Task<RestInteractionMessage> ModifyOriginalResponseAsync(Action<Mes
 
         /// <inheritdoc/>
         public abstract string Respond(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null);
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None);
 
         /// <summary>
         ///     Sends a followup message for this interaction.
@@ -356,7 +356,7 @@ public abstract string Respond(string text = null, Embed[] embeds = null, bool i
         ///     contains the sent message.
         /// </returns>
         public abstract Task<RestFollowupMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-             AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null);
+             AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None);
 
         /// <summary>
         ///     Sends a followup message for this interaction.
@@ -377,7 +377,7 @@ public abstract Task<RestFollowupMessage> FollowupAsync(string text = null, Embe
         ///     contains the sent message.
         /// </returns>
         public abstract Task<RestFollowupMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null);
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None);
 
         /// <summary>
         ///     Sends a followup message for this interaction.
@@ -398,7 +398,7 @@ public abstract Task<RestFollowupMessage> FollowupWithFileAsync(Stream fileStrea
         ///     contains the sent message.
         /// </returns>
         public abstract Task<RestFollowupMessage> FollowupWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null);
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None);
 
         /// <summary>
         ///     Sends a followup message for this interaction.
@@ -418,7 +418,7 @@ public abstract Task<RestFollowupMessage> FollowupWithFileAsync(string filePath,
         ///     contains the sent message.
         /// </returns>
         public abstract Task<RestFollowupMessage> FollowupWithFileAsync(FileAttachment attachment, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null);
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None);
 
         /// <summary>
         ///     Sends a followup message for this interaction.
@@ -438,7 +438,7 @@ public abstract Task<RestFollowupMessage> FollowupWithFileAsync(FileAttachment a
         ///     contains the sent message.
         /// </returns>
         public abstract Task<RestFollowupMessage> FollowupWithFilesAsync(IEnumerable<FileAttachment> attachments, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null);
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None);
 
         /// <inheritdoc/>
         public Task DeleteOriginalResponseAsync(RequestOptions options = null)
@@ -457,7 +457,7 @@ public Task RespondWithPremiumRequiredAsync(RequestOptions options = null)
 
         /// <inheritdoc/>
         Task IDiscordInteraction.RespondAsync(string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions,
-            MessageComponent components, Embed embed, RequestOptions options, PollProperties poll)
+            MessageComponent components, Embed embed, RequestOptions options, PollProperties poll, MessageFlags flags)
             => Task.FromResult(Respond(text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll));
         /// <inheritdoc/>
         Task IDiscordInteraction.DeferAsync(bool ephemeral, RequestOptions options)
@@ -465,45 +465,48 @@ Task IDiscordInteraction.DeferAsync(bool ephemeral, RequestOptions options)
         /// <inheritdoc/>
         Task IDiscordInteraction.RespondWithModalAsync(Modal modal, RequestOptions options)
             => Task.FromResult(RespondWithModal(modal, options));
+
         /// <inheritdoc/>
         async Task<IUserMessage> IDiscordInteraction.FollowupAsync(string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions,
-            MessageComponent components, Embed embed, RequestOptions options, PollProperties poll)
-            => await FollowupAsync(text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll).ConfigureAwait(false);
+            MessageComponent components, Embed embed, RequestOptions options, PollProperties poll, MessageFlags flags)
+            => await FollowupAsync(text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags).ConfigureAwait(false);
         /// <inheritdoc/>
         async Task<IUserMessage> IDiscordInteraction.GetOriginalResponseAsync(RequestOptions options)
             => await GetOriginalResponseAsync(options).ConfigureAwait(false);
         /// <inheritdoc/>
         async Task<IUserMessage> IDiscordInteraction.ModifyOriginalResponseAsync(Action<MessageProperties> func, RequestOptions options)
             => await ModifyOriginalResponseAsync(func, options).ConfigureAwait(false);
+
         /// <inheritdoc/>
         async Task<IUserMessage> IDiscordInteraction.FollowupWithFileAsync(Stream fileStream, string fileName, string text, Embed[] embeds, bool isTTS, bool ephemeral,
-            AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options, PollProperties poll)
-            => await FollowupWithFileAsync(fileStream, fileName, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll).ConfigureAwait(false);
+            AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options, PollProperties poll, MessageFlags flags)
+            => await FollowupWithFileAsync(fileStream, fileName, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags).ConfigureAwait(false);
         /// <inheritdoc/>
         async Task<IUserMessage> IDiscordInteraction.FollowupWithFileAsync(string filePath, string fileName, string text, Embed[] embeds, bool isTTS, bool ephemeral,
-            AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options, PollProperties poll)
-            => await FollowupWithFileAsync(filePath, text, fileName, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll).ConfigureAwait(false);
+            AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options, PollProperties poll, MessageFlags flags)
+            => await FollowupWithFileAsync(filePath, text, fileName, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags).ConfigureAwait(false);
         /// <inheritdoc/>
         async Task<IUserMessage> IDiscordInteraction.FollowupWithFileAsync(FileAttachment attachment, string text, Embed[] embeds, bool isTTS,
-            bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options, PollProperties poll)
-            => await FollowupWithFileAsync(attachment, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll).ConfigureAwait(false);
+            bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options, PollProperties poll, MessageFlags flags)
+            => await FollowupWithFileAsync(attachment, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags).ConfigureAwait(false);
         /// <inheritdoc/>
         async Task<IUserMessage> IDiscordInteraction.FollowupWithFilesAsync(IEnumerable<FileAttachment> attachments, string text, Embed[] embeds, bool isTTS,
-            bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options, PollProperties poll)
-            => await FollowupWithFilesAsync(attachments, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll).ConfigureAwait(false);
+            bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options, PollProperties poll, MessageFlags flags)
+            => await FollowupWithFilesAsync(attachments, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags).ConfigureAwait(false);
+
         /// <inheritdoc/>
         Task IDiscordInteraction.RespondWithFilesAsync(IEnumerable<FileAttachment> attachments, string text, Embed[] embeds, bool isTTS, bool ephemeral,
-            AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options, PollProperties poll) => throw new NotSupportedException("REST-Based interactions don't support files.");
+            AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options, PollProperties poll, MessageFlags flags) => throw new NotSupportedException("REST-Based interactions don't support files.");
 #if NETCOREAPP3_0_OR_GREATER != true
         /// <inheritdoc/>
         Task IDiscordInteraction.RespondWithFileAsync(Stream fileStream, string fileName, string text, Embed[] embeds, bool isTTS, bool ephemeral,
-            AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options, PollProperties poll) => throw new NotSupportedException("REST-Based interactions don't support files.");
+            AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options, PollProperties poll, MessageFlags flags) => throw new NotSupportedException("REST-Based interactions don't support files.");
         /// <inheritdoc/>
         Task IDiscordInteraction.RespondWithFileAsync(string filePath, string fileName, string text, Embed[] embeds, bool isTTS,
-            bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options, PollProperties poll) => throw new NotSupportedException("REST-Based interactions don't support files.");
+            bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options, PollProperties poll, MessageFlags flags) => throw new NotSupportedException("REST-Based interactions don't support files.");
         /// <inheritdoc/>
         Task IDiscordInteraction.RespondWithFileAsync(FileAttachment attachment, string text, Embed[] embeds, bool isTTS, bool ephemeral,
-            AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options, PollProperties poll) => throw new NotSupportedException("REST-Based interactions don't support files.");
+            AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options, PollProperties poll, MessageFlags flags) => throw new NotSupportedException("REST-Based interactions don't support files.");
 #endif
         #endregion
     }
diff --git a/src/Discord.Net.Rest/Entities/Interactions/RestPingInteraction.cs b/src/Discord.Net.Rest/Entities/Interactions/RestPingInteraction.cs
index 82b44c25ba..5b93127503 100644
--- a/src/Discord.Net.Rest/Entities/Interactions/RestPingInteraction.cs
+++ b/src/Discord.Net.Rest/Entities/Interactions/RestPingInteraction.cs
@@ -37,11 +37,11 @@ public string AcknowledgePing()
 
         public override string Defer(bool ephemeral = false, RequestOptions options = null) => throw new NotSupportedException();
         public override string RespondWithModal(Modal modal, RequestOptions options = null) => throw new NotSupportedException();
-        public override string Respond(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null) => throw new NotSupportedException();
-        public override Task<RestFollowupMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null) => throw new NotSupportedException();
-        public override Task<RestFollowupMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null) => throw new NotSupportedException();
-        public override Task<RestFollowupMessage> FollowupWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null) => throw new NotSupportedException();
-        public override Task<RestFollowupMessage> FollowupWithFileAsync(FileAttachment attachment, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null) => throw new NotSupportedException();
-        public override Task<RestFollowupMessage> FollowupWithFilesAsync(IEnumerable<FileAttachment> attachments, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null) => throw new NotSupportedException();
+        public override string Respond(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None) => throw new NotSupportedException();
+        public override Task<RestFollowupMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None) => throw new NotSupportedException();
+        public override Task<RestFollowupMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None) => throw new NotSupportedException();
+        public override Task<RestFollowupMessage> FollowupWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None) => throw new NotSupportedException();
+        public override Task<RestFollowupMessage> FollowupWithFileAsync(FileAttachment attachment, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None) => throw new NotSupportedException();
+        public override Task<RestFollowupMessage> FollowupWithFilesAsync(IEnumerable<FileAttachment> attachments, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None) => throw new NotSupportedException();
     }
 }
diff --git a/src/Discord.Net.Rest/Entities/Interactions/SlashCommands/RestAutocompleteInteraction.cs b/src/Discord.Net.Rest/Entities/Interactions/SlashCommands/RestAutocompleteInteraction.cs
index dcad1dbd43..6c60990b70 100644
--- a/src/Discord.Net.Rest/Entities/Interactions/SlashCommands/RestAutocompleteInteraction.cs
+++ b/src/Discord.Net.Rest/Entities/Interactions/SlashCommands/RestAutocompleteInteraction.cs
@@ -100,17 +100,17 @@ public string Respond(RequestOptions options = null, params AutocompleteResult[]
             => Respond(result, options);
         public override string Defer(bool ephemeral = false, RequestOptions options = null)
             => throw new NotSupportedException("Autocomplete interactions don't support this method!");
-        public override string Respond(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
+        public override string Respond(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None)
             => throw new NotSupportedException("Autocomplete interactions don't support this method!");
-        public override Task<RestFollowupMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
+        public override Task<RestFollowupMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None)
             => throw new NotSupportedException("Autocomplete interactions don't support this method!");
-        public override Task<RestFollowupMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
+        public override Task<RestFollowupMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None)
             => throw new NotSupportedException("Autocomplete interactions don't support this method!");
-        public override Task<RestFollowupMessage> FollowupWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
+        public override Task<RestFollowupMessage> FollowupWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None)
             => throw new NotSupportedException("Autocomplete interactions don't support this method!");
-        public override Task<RestFollowupMessage> FollowupWithFileAsync(FileAttachment attachment, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
+        public override Task<RestFollowupMessage> FollowupWithFileAsync(FileAttachment attachment, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None)
             => throw new NotSupportedException("Autocomplete interactions don't support this method!");
-        public override Task<RestFollowupMessage> FollowupWithFilesAsync(IEnumerable<FileAttachment> attachments, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
+        public override Task<RestFollowupMessage> FollowupWithFilesAsync(IEnumerable<FileAttachment> attachments, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None)
             => throw new NotSupportedException("Autocomplete interactions don't support this method!");
         public override string RespondWithModal(Modal modal, RequestOptions options = null)
             => throw new NotSupportedException("Autocomplete interactions don't support this method!");
diff --git a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs
index ac1eeb1132..f7b26d0923 100644
--- a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs
+++ b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs
@@ -55,16 +55,16 @@ public static Task<Model> ModifyAsync(ulong channelId, ulong msgId, BaseDiscordC
                 Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions.UserIds), "A max of 100 user Ids are allowed.");
 
                 // check that user flag and user Id list are exclusive, same with role flag and role Id list
-                if (allowedMentions != null && allowedMentions.AllowedTypes.HasValue)
+                if (allowedMentions is { AllowedTypes: not null })
                 {
                     if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Users) &&
-                        allowedMentions.UserIds != null && allowedMentions.UserIds.Count > 0)
+                        allowedMentions.UserIds is { Count: > 0 })
                     {
                         throw new ArgumentException("The Users flag is mutually exclusive with the list of User Ids.", nameof(allowedMentions));
                     }
 
                     if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Roles) &&
-                        allowedMentions.RoleIds != null && allowedMentions.RoleIds.Count > 0)
+                        allowedMentions.RoleIds is { Count: > 0 })
                     {
                         throw new ArgumentException("The Roles flag is mutually exclusive with the list of Role Ids.", nameof(allowedMentions));
                     }
@@ -93,13 +93,13 @@ public static Task<Model> ModifyAsync(ulong channelId, ulong msgId, BaseDiscordC
                     Embeds = apiEmbeds?.ToArray() ?? Optional<API.Embed[]>.Unspecified,
                     Flags = args.Flags.IsSpecified ? args.Flags.Value : Optional.Create<MessageFlags?>(),
                     AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value.ToModel() : Optional.Create<API.AllowedMentions>(),
-                    Components = args.Components.IsSpecified ? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Array.Empty<API.ActionRowComponent>() : Optional<API.ActionRowComponent[]>.Unspecified,
+                    Components = args.Components.IsSpecified ? args.Components.Value?.Components.Select(x => x.ToModel()).ToArray() ?? [] : Optional<IMessageComponent[]>.Unspecified,
                 };
                 return client.ApiClient.ModifyMessageAsync(channelId, msgId, apiArgs, options);
             }
             else
             {
-                var attachments = args.Attachments.Value?.ToArray() ?? Array.Empty<FileAttachment>();
+                var attachments = args.Attachments.Value?.ToArray() ?? [];
 
                 var apiArgs = new UploadFileParams(attachments)
                 {
@@ -107,7 +107,7 @@ public static Task<Model> ModifyAsync(ulong channelId, ulong msgId, BaseDiscordC
                     Embeds = apiEmbeds?.ToArray() ?? Optional<API.Embed[]>.Unspecified,
                     Flags = args.Flags.IsSpecified ? args.Flags.Value : Optional.Create<MessageFlags?>(),
                     AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value.ToModel() : Optional.Create<API.AllowedMentions>(),
-                    MessageComponent = args.Components.IsSpecified ? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Array.Empty<API.ActionRowComponent>() : Optional<API.ActionRowComponent[]>.Unspecified
+                    MessageComponent = args.Components.IsSpecified ? args.Components.Value?.Components.Select(x => x.ToModel()).ToArray() ?? [] : Optional<IMessageComponent[]>.Unspecified
                 };
 
                 return client.ApiClient.ModifyMessageAsync(channelId, msgId, apiArgs, options);
diff --git a/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs
index d4fddd038e..c418c4855f 100644
--- a/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs
+++ b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs
@@ -95,12 +95,12 @@ public abstract class RestMessage : RestEntity<ulong>, IMessage, IUpdateable
 
         /// <inheritdoc />
         public PurchaseNotification PurchaseNotification { get; private set; }
-        
+
         /// <inheritdoc />
         public MessageCallData? CallData { get; private set; }
 
         /// <inheritdoc cref="IMessage.Components"/>
-        public IReadOnlyCollection<ActionRowComponent> Components { get; private set; }
+        public IReadOnlyCollection<IMessageComponent> Components { get; private set; }
         /// <summary>
         ///     Gets a collection of the mentioned users in the message.
         /// </summary>
@@ -150,7 +150,7 @@ internal virtual void Update(Model model)
             if (model.Activity.IsSpecified)
             {
                 // create a new Activity from the API model
-                Activity = new MessageActivity()
+                Activity = new MessageActivity
                 {
                     Type = model.Activity.Value.Type.Value,
                     PartyId = model.Activity.Value.PartyId.GetValueOrDefault()
@@ -170,64 +170,9 @@ internal virtual void Update(Model model)
                 };
             }
 
-            if (model.Components.IsSpecified)
-            {
-                Components = model.Components.Value.Where(x => x.Type is ComponentType.ActionRow)
-                    .Select(x => new ActionRowComponent(((API.ActionRowComponent)x).Components.Select<IMessageComponent, IMessageComponent>(y =>
-                {
-                    switch (y.Type)
-                    {
-                        case ComponentType.Button:
-                            {
-                                var parsed = (API.ButtonComponent)y;
-                                return new Discord.ButtonComponent(
-                                    parsed.Style,
-                                    parsed.Label.GetValueOrDefault(),
-                                    parsed.Emote.IsSpecified
-                                        ? parsed.Emote.Value.Id.HasValue
-                                            ? new Emote(parsed.Emote.Value.Id.Value, parsed.Emote.Value.Name, parsed.Emote.Value.Animated.GetValueOrDefault())
-                                            : new Emoji(parsed.Emote.Value.Name)
-                                        : null,
-                                    parsed.CustomId.GetValueOrDefault(),
-                                    parsed.Url.GetValueOrDefault(),
-                                    parsed.Disabled.GetValueOrDefault(),
-                                    parsed.SkuId.ToNullable(),
-                                    parsed.Id.ToNullable());
-                            }
-                        case ComponentType.SelectMenu or ComponentType.ChannelSelect or ComponentType.RoleSelect or ComponentType.MentionableSelect or ComponentType.UserSelect:
-                            {
-                                var parsed = (API.SelectMenuComponent)y;
-                                return new SelectMenuComponent(
-                                    parsed.CustomId,
-                                    parsed.Options?.Select(z => new SelectMenuOption(
-                                        z.Label,
-                                        z.Value,
-                                        z.Description.GetValueOrDefault(),
-                                        z.Emoji.IsSpecified
-                                            ? z.Emoji.Value.Id.HasValue
-                                                ? new Emote(z.Emoji.Value.Id.Value, z.Emoji.Value.Name, z.Emoji.Value.Animated.GetValueOrDefault())
-                                                : new Emoji(z.Emoji.Value.Name)
-                                            : null,
-                                        z.Default.ToNullable())).ToList(),
-                                    parsed.Placeholder.GetValueOrDefault(),
-                                    parsed.MinValues,
-                                    parsed.MaxValues,
-                                    parsed.Disabled,
-                                    parsed.Type,
-                                    parsed.Id.ToNullable(),
-                                    parsed.ChannelTypes.GetValueOrDefault(),
-                                    parsed.DefaultValues.IsSpecified
-                                        ? parsed.DefaultValues.Value.Select(x => new SelectMenuDefaultValue(x.Id, x.Type))
-                                        : Array.Empty<SelectMenuDefaultValue>()
-                                );
-                            }
-                        default:
-                            return null;
-                    }
-                }).ToList())).ToImmutableArray();
-            }
-            else
-                Components = new List<ActionRowComponent>();
+            Components = model.Components.IsSpecified
+                ? model.Components.Value.Select(x => x.ToEntity()).ToImmutableArray()
+                : [];
 
             if (model.Flags.IsSpecified)
                 Flags = model.Flags.Value;
@@ -292,11 +237,11 @@ internal virtual void Update(Model model)
                         ? new GuildProductPurchase(model.PurchaseNotification.Value.ProductPurchase.Value.ListingId, model.PurchaseNotification.Value.ProductPurchase.Value.ProductName)
                         : null);
             }
-            
+
             if (model.Call.IsSpecified)
                 CallData = new MessageCallData(model.Call.Value.Participants, model.Call.Value.EndedTimestamp.ToNullable());
         }
-        
+
         /// <inheritdoc />
         public async Task UpdateAsync(RequestOptions options = null)
         {
diff --git a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs
index 4f80fa4385..fb5a8985d7 100644
--- a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs
+++ b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs
@@ -84,7 +84,7 @@ internal override void Update(Model model)
             if (model.MentionEveryone.IsSpecified)
                 _isMentioningEveryone = model.MentionEveryone.Value;
             if (model.RoleMentions.IsSpecified)
-                _roleMentionIds = model.RoleMentions.Value.ToImmutableArray();
+                _roleMentionIds = [..model.RoleMentions.Value];
 
             if (model.Attachments.IsSpecified)
             {
@@ -92,12 +92,13 @@ internal override void Update(Model model)
                 if (value.Length > 0)
                 {
                     var attachments = ImmutableArray.CreateBuilder<Attachment>(value.Length);
-                    for (int i = 0; i < value.Length; i++)
-                        attachments.Add(Attachment.Create(value[i], Discord));
+                    foreach (var t in value)
+                        attachments.Add(Attachment.Create(t, Discord));
+
                     _attachments = attachments.ToImmutable();
                 }
                 else
-                    _attachments = ImmutableArray.Create<Attachment>();
+                    _attachments = [];
             }
 
             if (model.Embeds.IsSpecified)
@@ -106,12 +107,13 @@ internal override void Update(Model model)
                 if (value.Length > 0)
                 {
                     var embeds = ImmutableArray.CreateBuilder<Embed>(value.Length);
-                    for (int i = 0; i < value.Length; i++)
-                        embeds.Add(value[i].ToEntity());
+                    foreach (var t in value)
+                        embeds.Add(t.ToEntity());
+
                     _embeds = embeds.ToImmutable();
                 }
                 else
-                    _embeds = ImmutableArray.Create<Embed>();
+                    _embeds = [];
             }
 
             var guildId = (Channel as IGuildChannel)?.GuildId;
@@ -123,7 +125,7 @@ internal override void Update(Model model)
                 model.Content = text;
             }
 
-            if (model.ReferencedMessage.IsSpecified && model.ReferencedMessage.Value != null)
+            if (model.ReferencedMessage is { IsSpecified: true, Value: not null })
             {
                 var refMsg = model.ReferencedMessage.Value;
                 IUser refMsgAuthor = MessageHelper.GetAuthor(Discord, guild, refMsg.Author.Value, refMsg.WebhookId.ToNullable());
@@ -141,7 +143,7 @@ internal override void Update(Model model)
                     _stickers = stickers.ToImmutable();
                 }
                 else
-                    _stickers = ImmutableArray.Create<StickerItem>();
+                    _stickers = [];
             }
 
             if (model.Resolved.IsSpecified)
diff --git a/src/Discord.Net.Rest/Extensions/MessageComponentExtension.cs b/src/Discord.Net.Rest/Extensions/MessageComponentExtension.cs
new file mode 100644
index 0000000000..a36ba6e20c
--- /dev/null
+++ b/src/Discord.Net.Rest/Extensions/MessageComponentExtension.cs
@@ -0,0 +1,98 @@
+using System.Collections.Immutable;
+using System.Linq;
+
+namespace Discord.Rest;
+
+internal static class MessageComponentExtension
+{
+    internal static IMessageComponent ToModel(this IMessageComponent component)
+    {
+        switch (component)
+        {
+            case ButtonComponent btn:
+                return new API.ButtonComponent(btn);
+
+            case SelectMenuComponent select:
+                return new API.SelectMenuComponent(select);
+
+            case TextInputComponent textInput:
+                return new API.TextInputComponent(textInput);
+
+            case TextDisplayComponent textDisplay:
+                return new API.TextDisplayComponent(textDisplay);
+        }
+
+        return null;
+    }
+
+    internal static IMessageComponent ToEntity(this IMessageComponent component)
+    {
+        switch (component.Type)
+        {
+            case ComponentType.ActionRow:
+            {
+                var parsed = (API.ActionRowComponent)component;
+                return new ActionRowComponent()
+                {
+                    Id = component.Id,
+                    Components = parsed.Components.Select(x => x.ToEntity()).ToImmutableArray()
+                };
+            }
+
+            case ComponentType.Button:
+            {
+                var parsed = (API.ButtonComponent)component;
+                return new ButtonComponent(
+                    parsed.Style,
+                    parsed.Label.GetValueOrDefault(),
+                    parsed.Emote.IsSpecified
+                        ? parsed.Emote.Value.Id.HasValue
+                            ? new Emote(parsed.Emote.Value.Id.Value, parsed.Emote.Value.Name, parsed.Emote.Value.Animated.GetValueOrDefault())
+                            : new Emoji(parsed.Emote.Value.Name)
+                        : null,
+                    parsed.CustomId.GetValueOrDefault(),
+                    parsed.Url.GetValueOrDefault(),
+                    parsed.Disabled.GetValueOrDefault(),
+                    parsed.SkuId.ToNullable(),
+                    parsed.Id.ToNullable());
+            }
+
+            case ComponentType.SelectMenu or ComponentType.ChannelSelect or ComponentType.RoleSelect or ComponentType.MentionableSelect or ComponentType.UserSelect:
+            {
+                var parsed = (API.SelectMenuComponent)component;
+                return new SelectMenuComponent(
+                    parsed.CustomId,
+                    parsed.Options?.Select(z => new SelectMenuOption(
+                        z.Label,
+                        z.Value,
+                        z.Description.GetValueOrDefault(),
+                        z.Emoji.IsSpecified
+                            ? z.Emoji.Value.Id.HasValue
+                                ? new Emote(z.Emoji.Value.Id.Value, z.Emoji.Value.Name, z.Emoji.Value.Animated.GetValueOrDefault())
+                                : new Emoji(z.Emoji.Value.Name)
+                            : null,
+                        z.Default.ToNullable())).ToList(),
+                    parsed.Placeholder.GetValueOrDefault(),
+                    parsed.MinValues,
+                    parsed.MaxValues,
+                    parsed.Disabled,
+                    parsed.Type,
+                    parsed.Id.ToNullable(),
+                    parsed.ChannelTypes.GetValueOrDefault(),
+                    parsed.DefaultValues.IsSpecified
+                        ? parsed.DefaultValues.Value.Select(x => new SelectMenuDefaultValue(x.Id, x.Type))
+                        : []
+                );
+            }
+
+            case ComponentType.TextDisplay:
+            {
+                var parsed = (API.TextDisplayComponent)component;
+                return new TextDisplayComponent(parsed.Id.ToNullable(), parsed.Content);
+            }
+
+            default:
+                return null;
+        }
+    }
+}
diff --git a/src/Discord.Net.Rest/Net/Converters/MessageComponentConverter.cs b/src/Discord.Net.Rest/Net/Converters/MessageComponentConverter.cs
index 7888219bcb..d88c5a7ad5 100644
--- a/src/Discord.Net.Rest/Net/Converters/MessageComponentConverter.cs
+++ b/src/Discord.Net.Rest/Net/Converters/MessageComponentConverter.cs
@@ -39,6 +39,9 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
                 case ComponentType.TextInput:
                     messageComponent = new API.TextInputComponent();
                     break;
+                case ComponentType.TextDisplay:
+                    messageComponent = new API.TextDisplayComponent();
+                    break;
             }
             serializer.Populate(jsonObject.CreateReader(), messageComponent);
             return messageComponent;
diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/MessageComponents/SocketMessageComponent.cs b/src/Discord.Net.WebSocket/Entities/Interaction/MessageComponents/SocketMessageComponent.cs
index f24a4be6fd..16bc37722f 100644
--- a/src/Discord.Net.WebSocket/Entities/Interaction/MessageComponents/SocketMessageComponent.cs
+++ b/src/Discord.Net.WebSocket/Entities/Interaction/MessageComponents/SocketMessageComponent.cs
@@ -82,7 +82,8 @@ public override async Task RespondWithFilesAsync(
             MessageComponent components = null,
             Embed embed = null,
             RequestOptions options = null,
-            PollProperties poll = null)
+            PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
             if (!IsValidToken)
                 throw new InvalidOperationException("Interaction token is no longer valid");
@@ -90,7 +91,7 @@ public override async Task RespondWithFilesAsync(
             if (!InteractionHelper.CanSendResponse(this) && Discord.ResponseInternalTimeCheck)
                 throw new TimeoutException($"Cannot respond to an interaction after {InteractionHelper.ResponseTimeLimit} seconds!");
 
-            embeds ??= Array.Empty<Embed>();
+            embeds ??= [];
             if (embed != null)
                 embeds = new[] { embed }.Concat(embeds).ToArray();
 
@@ -122,8 +123,12 @@ public override async Task RespondWithFilesAsync(
                 AllowedMentions = allowedMentions != null ? allowedMentions?.ToModel() : Optional<API.AllowedMentions>.Unspecified,
                 Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified,
                 IsTTS = isTTS,
-                MessageComponents = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
-                Flags = ephemeral ? MessageFlags.Ephemeral : Optional<MessageFlags>.Unspecified,
+                MessageComponents = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
+                Flags = ephemeral
+                    ? flags | MessageFlags.Ephemeral
+                    : flags == MessageFlags.None
+                        ? Optional<MessageFlags>.Unspecified
+                        : flags,
                 Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
             };
 
@@ -149,7 +154,8 @@ public override async Task RespondAsync(
             MessageComponent components = null,
             Embed embed = null,
             RequestOptions options = null,
-            PollProperties poll = null)
+            PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
             if (!IsValidToken)
                 throw new InvalidOperationException("Interaction token is no longer valid");
@@ -157,7 +163,7 @@ public override async Task RespondAsync(
             if (!InteractionHelper.CanSendResponse(this) && Discord.ResponseInternalTimeCheck)
                 throw new TimeoutException($"Cannot respond to an interaction after {InteractionHelper.ResponseTimeLimit} seconds!");
 
-            embeds ??= Array.Empty<Embed>();
+            embeds ??= [];
             if (embed != null)
                 embeds = new[] { embed }.Concat(embeds).ToArray();
 
@@ -167,16 +173,16 @@ public override async Task RespondAsync(
             Preconditions.ValidatePoll(poll);
 
             // check that user flag and user Id list are exclusive, same with role flag and role Id list
-            if (allowedMentions != null && allowedMentions.AllowedTypes.HasValue)
+            if (allowedMentions is { AllowedTypes: not null })
             {
                 if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Users) &&
-                    allowedMentions.UserIds != null && allowedMentions.UserIds.Count > 0)
+                    allowedMentions.UserIds is { Count: > 0 })
                 {
                     throw new ArgumentException("The Users flag is mutually exclusive with the list of User Ids.", nameof(allowedMentions));
                 }
 
                 if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Roles) &&
-                    allowedMentions.RoleIds != null && allowedMentions.RoleIds.Count > 0)
+                    allowedMentions.RoleIds is { Count: > 0 })
                 {
                     throw new ArgumentException("The Roles flag is mutually exclusive with the list of Role Ids.", nameof(allowedMentions));
                 }
@@ -191,8 +197,12 @@ public override async Task RespondAsync(
                     AllowedMentions = allowedMentions?.ToModel(),
                     Embeds = embeds.Select(x => x.ToModel()).ToArray(),
                     TTS = isTTS,
-                    Flags = ephemeral ? MessageFlags.Ephemeral : Optional<MessageFlags>.Unspecified,
-                    Components = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
+                    Flags = ephemeral
+                        ? flags | MessageFlags.Ephemeral
+                        : flags == MessageFlags.None
+                            ? Optional<MessageFlags>.Unspecified
+                            : flags,
+                    Components = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
                     Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
                 }
             };
@@ -250,13 +260,13 @@ public async Task UpdateAsync(Action<MessageProperties> func, RequestOptions opt
             {
                 var allowedMentions = args.AllowedMentions.Value;
                 if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Users)
-                && allowedMentions.UserIds != null && allowedMentions.UserIds.Count > 0)
+                    && allowedMentions.UserIds is { Count: > 0 })
                 {
                     throw new ArgumentException("The Users flag is mutually exclusive with the list of User Ids.", nameof(args.AllowedMentions));
                 }
 
                 if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Roles)
-                && allowedMentions.RoleIds != null && allowedMentions.RoleIds.Count > 0)
+                    && allowedMentions.RoleIds is { Count: > 0 })
                 {
                     throw new ArgumentException("The Roles flag is mutually exclusive with the list of Role Ids.", nameof(args.AllowedMentions));
                 }
@@ -273,8 +283,8 @@ public async Task UpdateAsync(Action<MessageProperties> func, RequestOptions opt
                         AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value?.ToModel() : Optional<API.AllowedMentions>.Unspecified,
                         Embeds = apiEmbeds?.ToArray() ?? Optional<API.Embed[]>.Unspecified,
                         Components = args.Components.IsSpecified
-                            ? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Array.Empty<API.ActionRowComponent>()
-                            : Optional<API.ActionRowComponent[]>.Unspecified,
+                            ? args.Components.Value?.Components.Select(x => x.ToModel()).ToArray() ?? []
+                            : Optional<IMessageComponent[]>.Unspecified,
                         Flags = args.Flags.IsSpecified ? args.Flags.Value ?? Optional<MessageFlags>.Unspecified : Optional<MessageFlags>.Unspecified
                     }
                 };
@@ -283,7 +293,7 @@ public async Task UpdateAsync(Action<MessageProperties> func, RequestOptions opt
             }
             else
             {
-                var attachments = args.Attachments.Value?.ToArray() ?? Array.Empty<FileAttachment>();
+                var attachments = args.Attachments.Value?.ToArray() ?? [];
 
                 var response = new API.Rest.UploadInteractionFileParams(attachments)
                 {
@@ -292,8 +302,8 @@ public async Task UpdateAsync(Action<MessageProperties> func, RequestOptions opt
                     AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value?.ToModel() : Optional<API.AllowedMentions>.Unspecified,
                     Embeds = apiEmbeds?.ToArray() ?? Optional<API.Embed[]>.Unspecified,
                     MessageComponents = args.Components.IsSpecified
-                        ? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Array.Empty<API.ActionRowComponent>()
-                        : Optional<API.ActionRowComponent[]>.Unspecified,
+                        ? args.Components.Value?.Components.Select(x => x.ToModel()).ToArray() ?? []
+                        : Optional<IMessageComponent[]>.Unspecified,
                     Flags = args.Flags.IsSpecified ? args.Flags.Value ?? Optional<MessageFlags>.Unspecified : Optional<MessageFlags>.Unspecified
                 };
 
@@ -321,12 +331,13 @@ public override Task<RestFollowupMessage> FollowupAsync(
             MessageComponent components = null,
             Embed embed = null,
             RequestOptions options = null,
-            PollProperties poll = null)
+            PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
             if (!IsValidToken)
                 throw new InvalidOperationException("Interaction token is no longer valid");
 
-            embeds ??= Array.Empty<Embed>();
+            embeds ??= [];
             if (embed != null)
                 embeds = new[] { embed }.Concat(embeds).ToArray();
 
@@ -341,13 +352,15 @@ public override Task<RestFollowupMessage> FollowupAsync(
                 AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified,
                 IsTTS = isTTS,
                 Embeds = embeds.Select(x => x.ToModel()).ToArray(),
-                Components = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
-                Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
+                Components = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
+                Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified,
+                Flags = ephemeral
+                    ? flags | MessageFlags.Ephemeral
+                    : flags == MessageFlags.None
+                        ? Optional<MessageFlags>.Unspecified
+                        : flags,
             };
 
-            if (ephemeral)
-                args.Flags = MessageFlags.Ephemeral;
-
             return InteractionHelper.SendFollowupAsync(Discord.Rest, args, Token, Channel, options);
         }
 
@@ -362,12 +375,13 @@ public override Task<RestFollowupMessage> FollowupWithFilesAsync(
             MessageComponent components = null,
             Embed embed = null,
             RequestOptions options = null,
-            PollProperties poll = null)
+            PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
             if (!IsValidToken)
                 throw new InvalidOperationException("Interaction token is no longer valid");
 
-            embeds ??= Array.Empty<Embed>();
+            embeds ??= [];
             if (embed != null)
                 embeds = new[] { embed }.Concat(embeds).ToArray();
 
@@ -385,31 +399,30 @@ public override Task<RestFollowupMessage> FollowupWithFilesAsync(
             if (allowedMentions != null && allowedMentions.AllowedTypes.HasValue)
             {
                 if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Users) &&
-                    allowedMentions.UserIds != null && allowedMentions.UserIds.Count > 0)
+                    allowedMentions.UserIds is { Count: > 0 })
                 {
                     throw new ArgumentException("The Users flag is mutually exclusive with the list of User Ids.", nameof(allowedMentions));
                 }
 
                 if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Roles) &&
-                    allowedMentions.RoleIds != null && allowedMentions.RoleIds.Count > 0)
+                    allowedMentions.RoleIds is { Count: > 0 })
                 {
                     throw new ArgumentException("The Roles flag is mutually exclusive with the list of Role Ids.", nameof(allowedMentions));
                 }
             }
 
-            var flags = MessageFlags.None;
-
-            if (ephemeral)
-                flags |= MessageFlags.Ephemeral;
-
             var args = new API.Rest.UploadWebhookFileParams(attachments.ToArray())
             {
-                Flags = flags,
+                Flags = ephemeral
+                    ? flags | MessageFlags.Ephemeral
+                    : flags == MessageFlags.None
+                        ? Optional<MessageFlags>.Unspecified
+                        : flags,
                 Content = text,
                 IsTTS = isTTS,
                 Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified,
                 AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified,
-                MessageComponents = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
+                MessageComponents = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
                 Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
             };
             return InteractionHelper.SendFollowupAsync(Discord, args, Token, Channel, options);
diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/Modals/SocketModal.cs b/src/Discord.Net.WebSocket/Entities/Interaction/Modals/SocketModal.cs
index 7b1b0a6502..90c85a260e 100644
--- a/src/Discord.Net.WebSocket/Entities/Interaction/Modals/SocketModal.cs
+++ b/src/Discord.Net.WebSocket/Entities/Interaction/Modals/SocketModal.cs
@@ -79,7 +79,8 @@ public override async Task RespondWithFilesAsync(
             MessageComponent components = null,
             Embed embed = null,
             RequestOptions options = null,
-            PollProperties poll = null)
+            PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
             if (!IsValidToken)
                 throw new InvalidOperationException("Interaction token is no longer valid");
@@ -87,7 +88,7 @@ public override async Task RespondWithFilesAsync(
             if (!InteractionHelper.CanSendResponse(this) && Discord.ResponseInternalTimeCheck)
                 throw new TimeoutException($"Cannot respond to an interaction after {InteractionHelper.ResponseTimeLimit} seconds!");
 
-            embeds ??= Array.Empty<Embed>();
+            embeds ??= [];
             if (embed != null)
                 embeds = new[] { embed }.Concat(embeds).ToArray();
 
@@ -119,8 +120,12 @@ public override async Task RespondWithFilesAsync(
                 AllowedMentions = allowedMentions != null ? allowedMentions?.ToModel() : Optional<API.AllowedMentions>.Unspecified,
                 Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified,
                 IsTTS = isTTS,
-                MessageComponents = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
-                Flags = ephemeral ? MessageFlags.Ephemeral : Optional<MessageFlags>.Unspecified,
+                MessageComponents = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
+                Flags = ephemeral
+                    ? flags | MessageFlags.Ephemeral
+                    : flags == MessageFlags.None
+                        ? Optional<MessageFlags>.Unspecified
+                        : flags,
                 Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
             };
 
@@ -146,7 +151,8 @@ public override async Task RespondAsync(
             MessageComponent components = null,
             Embed embed = null,
             RequestOptions options = null,
-            PollProperties poll = null)
+            PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
             if (!IsValidToken)
                 throw new InvalidOperationException("Interaction token is no longer valid");
@@ -154,7 +160,7 @@ public override async Task RespondAsync(
             if (!InteractionHelper.CanSendResponse(this) && Discord.ResponseInternalTimeCheck)
                 throw new TimeoutException($"Cannot respond to an interaction after {InteractionHelper.ResponseTimeLimit} seconds!");
 
-            embeds ??= Array.Empty<Embed>();
+            embeds ??= [];
             if (embed != null)
                 embeds = new[] { embed }.Concat(embeds).ToArray();
 
@@ -164,16 +170,16 @@ public override async Task RespondAsync(
             Preconditions.ValidatePoll(poll);
 
             // check that user flag and user Id list are exclusive, same with role flag and role Id list
-            if (allowedMentions != null && allowedMentions.AllowedTypes.HasValue)
+            if (allowedMentions is { AllowedTypes: not null })
             {
                 if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Users) &&
-                    allowedMentions.UserIds != null && allowedMentions.UserIds.Count > 0)
+                    allowedMentions.UserIds is { Count: > 0 })
                 {
                     throw new ArgumentException("The Users flag is mutually exclusive with the list of User Ids.", nameof(allowedMentions));
                 }
 
                 if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Roles) &&
-                    allowedMentions.RoleIds != null && allowedMentions.RoleIds.Count > 0)
+                    allowedMentions.RoleIds is { Count: > 0 })
                 {
                     throw new ArgumentException("The Roles flag is mutually exclusive with the list of Role Ids.", nameof(allowedMentions));
                 }
@@ -188,8 +194,12 @@ public override async Task RespondAsync(
                     AllowedMentions = allowedMentions?.ToModel(),
                     Embeds = embeds.Select(x => x.ToModel()).ToArray(),
                     TTS = isTTS,
-                    Flags = ephemeral ? MessageFlags.Ephemeral : Optional<MessageFlags>.Unspecified,
-                    Components = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
+                    Flags = ephemeral
+                        ? flags | MessageFlags.Ephemeral
+                        : flags == MessageFlags.None
+                            ? Optional<MessageFlags>.Unspecified
+                            : flags,
+                    Components = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
                     Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
                 }
             };
@@ -270,9 +280,11 @@ public async Task UpdateAsync(Action<MessageProperties> func, RequestOptions opt
                         AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value?.ToModel() : Optional<API.AllowedMentions>.Unspecified,
                         Embeds = apiEmbeds?.ToArray() ?? Optional<API.Embed[]>.Unspecified,
                         Components = args.Components.IsSpecified
-                            ? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Array.Empty<API.ActionRowComponent>()
-                            : Optional<API.ActionRowComponent[]>.Unspecified,
-                        Flags = args.Flags.IsSpecified ? args.Flags.Value ?? Optional<MessageFlags>.Unspecified : Optional<MessageFlags>.Unspecified
+                            ? args.Components.Value?.Components.Select(x => x.ToModel()).ToArray() ?? []
+                            : Optional<IMessageComponent[]>.Unspecified,
+                        Flags = args.Flags.IsSpecified
+                            ? args.Flags.Value ?? Optional<MessageFlags>.Unspecified
+                            : Optional<MessageFlags>.Unspecified
                     }
                 };
 
@@ -289,8 +301,8 @@ public async Task UpdateAsync(Action<MessageProperties> func, RequestOptions opt
                     AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value?.ToModel() : Optional<API.AllowedMentions>.Unspecified,
                     Embeds = apiEmbeds?.ToArray() ?? Optional<API.Embed[]>.Unspecified,
                     MessageComponents = args.Components.IsSpecified
-                        ? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Array.Empty<API.ActionRowComponent>()
-                        : Optional<API.ActionRowComponent[]>.Unspecified,
+                        ? args.Components.Value?.Components.Select(x => x.ToModel()).ToArray() ?? []
+                        : Optional<IMessageComponent[]>.Unspecified,
                     Flags = args.Flags.IsSpecified ? args.Flags.Value ?? Optional<MessageFlags>.Unspecified : Optional<MessageFlags>.Unspecified
                 };
 
@@ -318,12 +330,13 @@ public override Task<RestFollowupMessage> FollowupAsync(
             MessageComponent components = null,
             Embed embed = null,
             RequestOptions options = null,
-            PollProperties poll = null)
+            PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
             if (!IsValidToken)
                 throw new InvalidOperationException("Interaction token is no longer valid");
 
-            embeds ??= Array.Empty<Embed>();
+            embeds ??= [];
             if (embed != null)
                 embeds = new[] { embed }.Concat(embeds).ToArray();
 
@@ -338,14 +351,15 @@ public override Task<RestFollowupMessage> FollowupAsync(
                 AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified,
                 IsTTS = isTTS,
                 Embeds = embeds.Select(x => x.ToModel()).ToArray(),
-                Components = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
-                Flags = ephemeral ? MessageFlags.Ephemeral : Optional<MessageFlags>.Unspecified,
+                Components = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
+                Flags = ephemeral
+                    ? flags | MessageFlags.Ephemeral
+                    : flags == MessageFlags.None
+                        ? Optional<MessageFlags>.Unspecified
+                        : flags,
                 Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
             };
 
-            if (ephemeral)
-                args.Flags = MessageFlags.Ephemeral;
-
             return InteractionHelper.SendFollowupAsync(Discord.Rest, args, Token, Channel, options);
         }
 
@@ -360,12 +374,13 @@ public override Task<RestFollowupMessage> FollowupWithFilesAsync(
             MessageComponent components = null,
             Embed embed = null,
             RequestOptions options = null,
-            PollProperties poll = null)
+            PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
             if (!IsValidToken)
                 throw new InvalidOperationException("Interaction token is no longer valid");
 
-            embeds ??= Array.Empty<Embed>();
+            embeds ??= [];
             if (embed != null)
                 embeds = new[] { embed }.Concat(embeds).ToArray();
 
@@ -395,19 +410,18 @@ public override Task<RestFollowupMessage> FollowupWithFilesAsync(
                 }
             }
 
-            var flags = MessageFlags.None;
-
-            if (ephemeral)
-                flags |= MessageFlags.Ephemeral;
-
             var args = new API.Rest.UploadWebhookFileParams(attachments.ToArray())
             {
-                Flags = flags,
+                Flags = ephemeral
+                    ? flags | MessageFlags.Ephemeral
+                    : flags == MessageFlags.None
+                        ? Optional<MessageFlags>.Unspecified
+                        : flags,
                 Content = text,
                 IsTTS = isTTS,
                 Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified,
                 AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified,
-                MessageComponents = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
+                MessageComponents = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
                 Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
             };
             return InteractionHelper.SendFollowupAsync(Discord, args, Token, Channel, options);
diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/SlashCommands/SocketAutocompleteInteraction.cs b/src/Discord.Net.WebSocket/Entities/Interaction/SlashCommands/SocketAutocompleteInteraction.cs
index 7e6b484ab9..c4521513ff 100644
--- a/src/Discord.Net.WebSocket/Entities/Interaction/SlashCommands/SocketAutocompleteInteraction.cs
+++ b/src/Discord.Net.WebSocket/Entities/Interaction/SlashCommands/SocketAutocompleteInteraction.cs
@@ -91,15 +91,15 @@ public async Task RespondAsync(IEnumerable<AutocompleteResult> result, RequestOp
         /// </returns>
         public Task RespondAsync(RequestOptions options = null, params AutocompleteResult[] result)
             => RespondAsync(result, options);
-        public override Task RespondAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
+        public override Task RespondAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None)
             => throw new NotSupportedException("Autocomplete interactions don't support this method!");
-        public override Task<RestFollowupMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
+        public override Task<RestFollowupMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None)
             => throw new NotSupportedException("Autocomplete interactions don't support this method!");
-        public override Task<RestFollowupMessage> FollowupWithFilesAsync(IEnumerable<FileAttachment> attachments, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
+        public override Task<RestFollowupMessage> FollowupWithFilesAsync(IEnumerable<FileAttachment> attachments, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None)
             => throw new NotSupportedException("Autocomplete interactions don't support this method!");
         public override Task DeferAsync(bool ephemeral = false, RequestOptions options = null)
             => throw new NotSupportedException("Autocomplete interactions don't support this method!");
-        public override Task RespondWithFilesAsync(IEnumerable<FileAttachment> attachments, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
+        public override Task RespondWithFilesAsync(IEnumerable<FileAttachment> attachments, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None)
             => throw new NotSupportedException("Autocomplete interactions don't support this method!");
 
         /// <inheritdoc/>
diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketCommandBase.cs b/src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketCommandBase.cs
index 3b6da47742..2d8dcf0509 100644
--- a/src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketCommandBase.cs
+++ b/src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketCommandBase.cs
@@ -77,7 +77,8 @@ public override async Task RespondAsync(
             MessageComponent components = null,
             Embed embed = null,
             RequestOptions options = null,
-            PollProperties poll = null)
+            PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
             if (!IsValidToken)
                 throw new InvalidOperationException("Interaction token is no longer valid");
@@ -85,7 +86,7 @@ public override async Task RespondAsync(
             if (!InteractionHelper.CanSendResponse(this) && Discord.ResponseInternalTimeCheck)
                 throw new TimeoutException($"Cannot respond to an interaction after {InteractionHelper.ResponseTimeLimit} seconds!");
 
-            embeds ??= Array.Empty<Embed>();
+            embeds ??= [];
             if (embed != null)
                 embeds = new[] { embed }.Concat(embeds).ToArray();
 
@@ -119,8 +120,12 @@ public override async Task RespondAsync(
                     AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified,
                     Embeds = embeds.Select(x => x.ToModel()).ToArray(),
                     TTS = isTTS,
-                    Components = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
-                    Flags = ephemeral ? MessageFlags.Ephemeral : Optional<MessageFlags>.Unspecified,
+                    Components = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
+                    Flags = ephemeral
+                        ? flags | MessageFlags.Ephemeral
+                        : flags == MessageFlags.None
+                            ? Optional<MessageFlags>.Unspecified
+                            : flags,
                     Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
                 }
             };
@@ -183,7 +188,8 @@ public override async Task RespondWithFilesAsync(
             MessageComponent components = null,
             Embed embed = null,
             RequestOptions options = null,
-            PollProperties poll = null)
+            PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
             if (!IsValidToken)
                 throw new InvalidOperationException("Interaction token is no longer valid");
@@ -191,7 +197,7 @@ public override async Task RespondWithFilesAsync(
             if (!InteractionHelper.CanSendResponse(this) && Discord.ResponseInternalTimeCheck)
                 throw new TimeoutException($"Cannot respond to an interaction after {InteractionHelper.ResponseTimeLimit} seconds!");
 
-            embeds ??= Array.Empty<Embed>();
+            embeds ??= [];
             if (embed != null)
                 embeds = new[] { embed }.Concat(embeds).ToArray();
 
@@ -223,8 +229,12 @@ public override async Task RespondWithFilesAsync(
                 AllowedMentions = allowedMentions != null ? allowedMentions?.ToModel() : Optional<API.AllowedMentions>.Unspecified,
                 Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified,
                 IsTTS = isTTS,
-                Flags = ephemeral ? MessageFlags.Ephemeral : Optional<MessageFlags>.Unspecified,
-                MessageComponents = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
+                Flags = ephemeral
+                    ? flags | MessageFlags.Ephemeral
+                    : flags == MessageFlags.None
+                        ? Optional<MessageFlags>.Unspecified
+                        : flags,
+                MessageComponents = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
                 Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
             };
 
@@ -250,12 +260,13 @@ public override Task<RestFollowupMessage> FollowupAsync(
             MessageComponent components = null,
             Embed embed = null,
             RequestOptions options = null,
-            PollProperties poll = null)
+            PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
             if (!IsValidToken)
                 throw new InvalidOperationException("Interaction token is no longer valid");
 
-            embeds ??= Array.Empty<Embed>();
+            embeds ??= [];
             if (embed != null)
                 embeds = new[] { embed }.Concat(embeds).ToArray();
 
@@ -270,13 +281,15 @@ public override Task<RestFollowupMessage> FollowupAsync(
                 AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified,
                 IsTTS = isTTS,
                 Embeds = embeds.Select(x => x.ToModel()).ToArray(),
-                Components = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
-                Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
+                Components = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
+                Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified,
+                Flags = ephemeral
+                    ? flags | MessageFlags.Ephemeral
+                    : flags == MessageFlags.None
+                        ? Optional<MessageFlags>.Unspecified
+                        : flags,
             };
 
-            if (ephemeral)
-                args.Flags = MessageFlags.Ephemeral;
-
             return InteractionHelper.SendFollowupAsync(Discord.Rest, args, Token, Channel, options);
         }
 
@@ -291,12 +304,13 @@ public override Task<RestFollowupMessage> FollowupWithFilesAsync(
             MessageComponent components = null,
             Embed embed = null,
             RequestOptions options = null,
-            PollProperties poll = null)
+            PollProperties poll = null,
+            MessageFlags flags = MessageFlags.None)
         {
             if (!IsValidToken)
                 throw new InvalidOperationException("Interaction token is no longer valid");
 
-            embeds ??= Array.Empty<Embed>();
+            embeds ??= [];
             if (embed != null)
                 embeds = new[] { embed }.Concat(embeds).ToArray();
 
@@ -326,19 +340,18 @@ public override Task<RestFollowupMessage> FollowupWithFilesAsync(
                 }
             }
 
-            var flags = MessageFlags.None;
-
-            if (ephemeral)
-                flags |= MessageFlags.Ephemeral;
-
             var args = new API.Rest.UploadWebhookFileParams(attachments.ToArray())
             {
-                Flags = flags,
+                Flags = ephemeral
+                    ? flags | MessageFlags.Ephemeral
+                    : flags == MessageFlags.None
+                        ? Optional<MessageFlags>.Unspecified
+                        : flags,
                 Content = text,
                 IsTTS = isTTS,
                 Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified,
                 AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified,
-                MessageComponents = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
+                MessageComponents = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
                 Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
             };
             return InteractionHelper.SendFollowupAsync(Discord, args, Token, Channel, options);
diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/SocketInteraction.cs b/src/Discord.Net.WebSocket/Entities/Interaction/SocketInteraction.cs
index 24aecb47f1..5b3c9bc054 100644
--- a/src/Discord.Net.WebSocket/Entities/Interaction/SocketInteraction.cs
+++ b/src/Discord.Net.WebSocket/Entities/Interaction/SocketInteraction.cs
@@ -211,7 +211,7 @@ ChannelType.Media or
         /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
         /// <exception cref="InvalidOperationException">The parameters provided were invalid or the token was invalid.</exception>
         public abstract Task RespondAsync(string text = null, Embed[] embeds = null, bool isTTS = false,
-            bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null);
+            bool ephemeral = false, AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None);
 
         /// <summary>
         ///     Responds to this interaction with a file attachment.
@@ -232,11 +232,11 @@ public abstract Task RespondAsync(string text = null, Embed[] embeds = null, boo
         ///     contains the sent message.
         /// </returns>
         public async Task RespondWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None)
         {
             using (var file = new FileAttachment(fileStream, fileName))
             {
-                await RespondWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll).ConfigureAwait(false);
+                await RespondWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags).ConfigureAwait(false);
             }
         }
 
@@ -259,11 +259,11 @@ public async Task RespondWithFileAsync(Stream fileStream, string fileName, strin
         ///     contains the sent message.
         /// </returns>
         public async Task RespondWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None)
         {
             using (var file = new FileAttachment(filePath, fileName))
             {
-                await RespondWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll).ConfigureAwait(false);
+                await RespondWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags).ConfigureAwait(false);
             }
         }
 
@@ -285,8 +285,8 @@ public async Task RespondWithFileAsync(string filePath, string fileName = null,
         ///     contains the sent message.
         /// </returns>
         public Task RespondWithFileAsync(FileAttachment attachment, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
-            => RespondWithFilesAsync(new FileAttachment[] { attachment }, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll);
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None)
+            => RespondWithFilesAsync([attachment], text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags);
 
         /// <summary>
         ///     Responds to this interaction with a collection of file attachments.
@@ -306,7 +306,7 @@ public Task RespondWithFileAsync(FileAttachment attachment, string text = null,
         ///     contains the sent message.
         /// </returns>
         public abstract Task RespondWithFilesAsync(IEnumerable<FileAttachment> attachments, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null);
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None);
 
         /// <summary>
         ///     Sends a followup message for this interaction.
@@ -324,7 +324,7 @@ public abstract Task RespondWithFilesAsync(IEnumerable<FileAttachment> attachmen
         ///     The sent message.
         /// </returns>
         public abstract Task<RestFollowupMessage> FollowupAsync(string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-             AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null);
+             AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None);
 
         /// <summary>
         ///     Sends a followup message for this interaction.
@@ -344,11 +344,11 @@ public abstract Task<RestFollowupMessage> FollowupAsync(string text = null, Embe
         ///     The sent message.
         /// </returns>
         public async Task<RestFollowupMessage> FollowupWithFileAsync(Stream fileStream, string fileName, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None)
         {
             using (var file = new FileAttachment(fileStream, fileName))
             {
-                return await FollowupWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll).ConfigureAwait(false);
+                return await FollowupWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags).ConfigureAwait(false);
             }
         }
 
@@ -370,11 +370,11 @@ public async Task<RestFollowupMessage> FollowupWithFileAsync(Stream fileStream,
         ///     The sent message.
         /// </returns>
         public async Task<RestFollowupMessage> FollowupWithFileAsync(string filePath, string fileName = null, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None)
         {
             using (var file = new FileAttachment(filePath, fileName))
             {
-                return await FollowupWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll).ConfigureAwait(false);
+                return await FollowupWithFileAsync(file, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags).ConfigureAwait(false);
             }
         }
 
@@ -396,8 +396,8 @@ public async Task<RestFollowupMessage> FollowupWithFileAsync(string filePath, st
         ///     contains the sent message.
         /// </returns>
         public Task<RestFollowupMessage> FollowupWithFileAsync(FileAttachment attachment, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null)
-            => FollowupWithFilesAsync(new FileAttachment[] { attachment }, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll);
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None)
+            => FollowupWithFilesAsync(new FileAttachment[] { attachment }, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags);
 
         /// <summary>
         ///     Sends a followup message for this interaction.
@@ -417,7 +417,7 @@ public Task<RestFollowupMessage> FollowupWithFileAsync(FileAttachment attachment
         ///     contains the sent message.
         /// </returns>
         public abstract Task<RestFollowupMessage> FollowupWithFilesAsync(IEnumerable<FileAttachment> attachments, string text = null, Embed[] embeds = null, bool isTTS = false, bool ephemeral = false,
-            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null);
+            AllowedMentions allowedMentions = null, MessageComponent components = null, Embed embed = null, RequestOptions options = null, PollProperties poll = null, MessageFlags flags = MessageFlags.None);
 
         /// <summary>
         ///     Gets the original response for this interaction.
@@ -504,26 +504,26 @@ async Task<IUserMessage> IDiscordInteraction.ModifyOriginalResponseAsync(Action<
             => await ModifyOriginalResponseAsync(func, options).ConfigureAwait(false);
         /// <inheritdoc/>
         async Task IDiscordInteraction.RespondAsync(string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components,
-            Embed embed, RequestOptions options, PollProperties poll)
-            => await RespondAsync(text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll).ConfigureAwait(false);
+            Embed embed, RequestOptions options, PollProperties poll, MessageFlags flags)
+            => await RespondAsync(text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags).ConfigureAwait(false);
         /// <inheritdoc/>
         async Task<IUserMessage> IDiscordInteraction.FollowupAsync(string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions,
-            MessageComponent components, Embed embed, RequestOptions options, PollProperties poll)
-            => await FollowupAsync(text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll).ConfigureAwait(false);
+            MessageComponent components, Embed embed, RequestOptions options, PollProperties poll, MessageFlags flags)
+            => await FollowupAsync(text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags).ConfigureAwait(false);
         /// <inheritdoc/>
         async Task<IUserMessage> IDiscordInteraction.FollowupWithFilesAsync(IEnumerable<FileAttachment> attachments, string text, Embed[] embeds, bool isTTS,
-            bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options, PollProperties poll)
-            => await FollowupWithFilesAsync(attachments, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll).ConfigureAwait(false);
+            bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options, PollProperties poll, MessageFlags flags)
+            => await FollowupWithFilesAsync(attachments, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags).ConfigureAwait(false);
 #if NETCOREAPP3_0_OR_GREATER != true
         /// <inheritdoc/>
-        async Task<IUserMessage> IDiscordInteraction.FollowupWithFileAsync(Stream fileStream, string fileName, string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options, PollProperties poll)
-            => await FollowupWithFileAsync(fileStream, fileName, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll).ConfigureAwait(false);
+        async Task<IUserMessage> IDiscordInteraction.FollowupWithFileAsync(Stream fileStream, string fileName, string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options, PollProperties poll, MessageFlags flags)
+            => await FollowupWithFileAsync(fileStream, fileName, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags).ConfigureAwait(false);
         /// <inheritdoc/>
-        async Task<IUserMessage> IDiscordInteraction.FollowupWithFileAsync(string filePath, string fileName, string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options, PollProperties poll)
-            => await FollowupWithFileAsync(filePath, fileName, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll).ConfigureAwait(false);
+        async Task<IUserMessage> IDiscordInteraction.FollowupWithFileAsync(string filePath, string fileName, string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options, PollProperties poll, MessageFlags flags)
+            => await FollowupWithFileAsync(filePath, fileName, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags).ConfigureAwait(false);
         /// <inheritdoc/>
-        async Task<IUserMessage> IDiscordInteraction.FollowupWithFileAsync(FileAttachment attachment, string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options, PollProperties poll)
-            => await FollowupWithFileAsync(attachment, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll).ConfigureAwait(false);
+        async Task<IUserMessage> IDiscordInteraction.FollowupWithFileAsync(FileAttachment attachment, string text, Embed[] embeds, bool isTTS, bool ephemeral, AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options, PollProperties poll, MessageFlags flags)
+            => await FollowupWithFileAsync(attachment, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options, poll, flags).ConfigureAwait(false);
 #endif
         #endregion
     }
diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs
index 878940f7cf..552baf5aff 100644
--- a/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs
+++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs
@@ -1,10 +1,13 @@
 using Discord.Rest;
+
 using Newtonsoft.Json.Linq;
+
 using System;
 using System.Collections.Generic;
 using System.Collections.Immutable;
 using System.Linq;
 using System.Threading.Tasks;
+
 using Model = Discord.API.Message;
 
 namespace Discord.WebSocket
@@ -65,7 +68,7 @@ public abstract class SocketMessage : SocketEntity<ulong>, IMessage
         public MessageReference Reference { get; private set; }
 
         /// <inheritdoc/>
-        public IReadOnlyCollection<ActionRowComponent> Components { get; private set; }
+        public IReadOnlyCollection<IMessageComponent> Components { get; private set; }
 
         /// <summary>
         ///     Gets the interaction this message is a response to.
@@ -203,64 +206,9 @@ internal virtual void Update(ClientState state, Model model)
                 };
             }
 
-            if (model.Components.IsSpecified)
-            {
-                Components = model.Components.Value.Where(x => x.Type is ComponentType.ActionRow)
-                    .Select(x => new ActionRowComponent(((API.ActionRowComponent)x).Components.Select<IMessageComponent, IMessageComponent>(y =>
-                {
-                    switch (y.Type)
-                    {
-                        case ComponentType.Button:
-                            {
-                                var parsed = (API.ButtonComponent)y;
-                                return new Discord.ButtonComponent(
-                                    parsed.Style,
-                                    parsed.Label.GetValueOrDefault(),
-                                    parsed.Emote.IsSpecified
-                                        ? parsed.Emote.Value.Id.HasValue
-                                            ? new Emote(parsed.Emote.Value.Id.Value, parsed.Emote.Value.Name, parsed.Emote.Value.Animated.GetValueOrDefault())
-                                            : new Emoji(parsed.Emote.Value.Name)
-                                        : null,
-                                    parsed.CustomId.GetValueOrDefault(),
-                                    parsed.Url.GetValueOrDefault(),
-                                    parsed.Disabled.GetValueOrDefault(),
-                                    parsed.SkuId.ToNullable(),
-                                    parsed.Id.ToNullable());
-                            }
-                        case ComponentType.SelectMenu:
-                            {
-                                var parsed = (API.SelectMenuComponent)y;
-                                return new SelectMenuComponent(
-                                    parsed.CustomId,
-                                    parsed.Options.Select(z => new SelectMenuOption(
-                                        z.Label,
-                                        z.Value,
-                                        z.Description.GetValueOrDefault(),
-                                        z.Emoji.IsSpecified
-                                        ? z.Emoji.Value.Id.HasValue
-                                            ? new Emote(z.Emoji.Value.Id.Value, z.Emoji.Value.Name, z.Emoji.Value.Animated.GetValueOrDefault())
-                                            : new Emoji(z.Emoji.Value.Name)
-                                        : null,
-                                        z.Default.ToNullable())).ToList(),
-                                    parsed.Placeholder.GetValueOrDefault(),
-                                    parsed.MinValues,
-                                    parsed.MaxValues,
-                                    parsed.Disabled,
-                                    parsed.Type,
-                                    parsed.Id.ToNullable(),
-                                    parsed.ChannelTypes.GetValueOrDefault(),
-                                    parsed.DefaultValues.IsSpecified
-                                        ? parsed.DefaultValues.Value.Select(x => new SelectMenuDefaultValue(x.Id, x.Type))
-                                        : Array.Empty<SelectMenuDefaultValue>()
-                                    );
-                            }
-                        default:
-                            return null;
-                    }
-                }).ToList())).ToImmutableArray();
-            }
-            else
-                Components = new List<ActionRowComponent>();
+            Components = model.Components.IsSpecified
+                ? model.Components.Value.Select(x => x.ToEntity()).ToImmutableArray()
+                : [];
 
             if (model.UserMentions.IsSpecified)
             {
@@ -317,7 +265,7 @@ internal virtual void Update(ClientState state, Model model)
                         ? new GuildProductPurchase(model.PurchaseNotification.Value.ProductPurchase.Value.ListingId, model.PurchaseNotification.Value.ProductPurchase.Value.ProductName)
                         : null);
             }
-            
+
             if (model.Call.IsSpecified)
                 CallData = new MessageCallData(model.Call.Value.Participants, model.Call.Value.EndedTimestamp.ToNullable());
         }
diff --git a/src/Discord.Net.Webhook/WebhookClientHelper.cs b/src/Discord.Net.Webhook/WebhookClientHelper.cs
index 47db9a21bb..d6b62f8f2b 100644
--- a/src/Discord.Net.Webhook/WebhookClientHelper.cs
+++ b/src/Discord.Net.Webhook/WebhookClientHelper.cs
@@ -48,7 +48,7 @@ public static async Task<ulong> SendMessageAsync(DiscordWebhookClient client,
             if (allowedMentions != null)
                 args.AllowedMentions = allowedMentions.ToModel();
             if (components != null)
-                args.Components = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray();
+                args.Components = components?.Components.Select(x => x.ToModel()).ToArray();
             if (threadName is not null)
                 args.ThreadName = threadName;
             if (appliedTags != null)
@@ -108,14 +108,14 @@ public static Task ModifyMessageAsync(DiscordWebhookClient client, ulong message
                     AllowedMentions = args.AllowedMentions.IsSpecified
                         ? args.AllowedMentions.Value.ToModel()
                         : Optional.Create<API.AllowedMentions>(),
-                    Components = args.Components.IsSpecified ? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() : Optional<API.ActionRowComponent[]>.Unspecified,
+                    Components = args.Components.IsSpecified ? args.Components.Value?.Components.Select(x => x.ToModel()).ToArray() : Optional<IMessageComponent[]>.Unspecified,
                 };
 
                 return client.ApiClient.ModifyWebhookMessageAsync(client.Webhook.Id, messageId, apiArgs, options, threadId);
             }
             else
             {
-                var attachments = args.Attachments.Value?.ToArray() ?? Array.Empty<FileAttachment>();
+                var attachments = args.Attachments.Value?.ToArray() ?? [];
 
                 var apiArgs = new UploadWebhookFileParams(attachments)
                 {
@@ -127,7 +127,7 @@ public static Task ModifyMessageAsync(DiscordWebhookClient client, ulong message
                     AllowedMentions = args.AllowedMentions.IsSpecified
                         ? args.AllowedMentions.Value.ToModel()
                         : Optional.Create<API.AllowedMentions>(),
-                    MessageComponents = args.Components.IsSpecified ? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() : Optional<API.ActionRowComponent[]>.Unspecified,
+                    MessageComponents = args.Components.IsSpecified ? args.Components.Value?.Components.Select(x => x.ToModel()).ToArray() : Optional<IMessageComponent[]>.Unspecified,
                 };
 
                 return client.ApiClient.ModifyWebhookMessageAsync(client.Webhook.Id, messageId, apiArgs, options, threadId);
@@ -204,7 +204,7 @@ public static async Task<ulong> SendFilesAsync(DiscordWebhookClient client,
                 IsTTS = isTTS,
                 Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified,
                 AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified,
-                MessageComponents = components?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified,
+                MessageComponents = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
                 Flags = flags,
                 ThreadName = threadName,
                 AppliedTags = appliedTags,

From 5af8c5f101b6cd42d0965e45da70224837735c9a Mon Sep 17 00:00:00 2001
From: Misha133 <mihagribkov133@gmail.com>
Date: Sat, 1 Feb 2025 20:05:53 +0300
Subject: [PATCH 03/15] all the models (i think)

---
 .../MessageComponents/ComponentType.cs        |  6 ++++-
 .../Entities/Messages/MessageFlags.cs         |  5 +++-
 .../API/Common/ContainerComponent.cs          | 25 +++++++++++++++++++
 .../API/Common/FileComponent.cs               | 21 ++++++++++++++++
 .../API/Common/MediaGalleryComponent.cs       | 19 ++++++++++++++
 .../API/Common/MediaGalleryItem.cs            | 15 +++++++++++
 .../API/Common/SectionComponent.cs            | 22 ++++++++++++++++
 .../API/Common/SeparatorComponent.cs          | 21 ++++++++++++++++
 .../API/Common/ThumbnailComponent.cs          | 22 ++++++++++++++++
 9 files changed, 154 insertions(+), 2 deletions(-)
 create mode 100644 src/Discord.Net.Rest/API/Common/ContainerComponent.cs
 create mode 100644 src/Discord.Net.Rest/API/Common/FileComponent.cs
 create mode 100644 src/Discord.Net.Rest/API/Common/MediaGalleryComponent.cs
 create mode 100644 src/Discord.Net.Rest/API/Common/MediaGalleryItem.cs
 create mode 100644 src/Discord.Net.Rest/API/Common/SectionComponent.cs
 create mode 100644 src/Discord.Net.Rest/API/Common/SeparatorComponent.cs
 create mode 100644 src/Discord.Net.Rest/API/Common/ThumbnailComponent.cs

diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentType.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentType.cs
index 3e80deada9..c0d5e196b9 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentType.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentType.cs
@@ -45,12 +45,16 @@ public enum ComponentType
         /// </summary>
         ChannelSelect = 8,
 
+        Section = 9,
+
         TextDisplay = 10,
 
         MediaGallery = 12,
 
         File = 13,
 
-        Separator = 14
+        Separator = 14,
+
+        Container = 17,
     }
 }
diff --git a/src/Discord.Net.Core/Entities/Messages/MessageFlags.cs b/src/Discord.Net.Core/Entities/Messages/MessageFlags.cs
index 51f7d89fc9..6219e8fc9c 100644
--- a/src/Discord.Net.Core/Entities/Messages/MessageFlags.cs
+++ b/src/Discord.Net.Core/Entities/Messages/MessageFlags.cs
@@ -58,6 +58,9 @@ public enum MessageFlags
         /// </summary>
         VoiceMessage = 1 << 13,
 
-        UiKitComponents = 1 << 15,
+        /// <summary>
+        ///     This message is using v2 components.
+        /// </summary>
+        ComponentsV2 = 1 << 15,
     }
 }
diff --git a/src/Discord.Net.Rest/API/Common/ContainerComponent.cs b/src/Discord.Net.Rest/API/Common/ContainerComponent.cs
new file mode 100644
index 0000000000..645f03f1cf
--- /dev/null
+++ b/src/Discord.Net.Rest/API/Common/ContainerComponent.cs
@@ -0,0 +1,25 @@
+using Newtonsoft.Json;
+
+namespace Discord.API;
+
+internal class ContainerComponent : IMessageComponent
+{
+    [JsonProperty("type")]
+    public ComponentType Type { get; set; }
+
+    [JsonProperty("id")]
+    public Optional<int> Id { get; set; }
+
+    [JsonProperty("accent_color")]
+    public Optional<int> AccentColor { get; set; }
+
+    [JsonProperty("spoiler")]
+    public Optional<bool> IsSpoiler { get; set; }
+
+    [JsonProperty("components")]
+    public IMessageComponent[] Components { get; set; }
+
+    public ContainerComponent() { }
+
+    int? IMessageComponent.Id => Id.ToNullable();
+}
diff --git a/src/Discord.Net.Rest/API/Common/FileComponent.cs b/src/Discord.Net.Rest/API/Common/FileComponent.cs
new file mode 100644
index 0000000000..8f95dba4b4
--- /dev/null
+++ b/src/Discord.Net.Rest/API/Common/FileComponent.cs
@@ -0,0 +1,21 @@
+using Newtonsoft.Json;
+
+namespace Discord.API;
+
+internal class FileComponent : IMessageComponent
+{
+    [JsonProperty("type")]
+    public ComponentType Type { get; set; }
+    [JsonProperty("id")]
+    public Optional<int> Id { get; set; }
+
+    [JsonProperty("file")]
+    public UnfurledMediaItem File { get; set; }
+
+    [JsonProperty("spoiler")]
+    public Optional<bool> IsSpoiler { get; set; }
+
+    public FileComponent() { }
+
+    int? IMessageComponent.Id => Id.ToNullable();
+}
diff --git a/src/Discord.Net.Rest/API/Common/MediaGalleryComponent.cs b/src/Discord.Net.Rest/API/Common/MediaGalleryComponent.cs
new file mode 100644
index 0000000000..f8a0546361
--- /dev/null
+++ b/src/Discord.Net.Rest/API/Common/MediaGalleryComponent.cs
@@ -0,0 +1,19 @@
+using Newtonsoft.Json;
+
+namespace Discord.API;
+
+internal class MediaGalleryComponent : IMessageComponent
+{
+    [JsonProperty("type")]
+    public ComponentType Type { get; set; }
+
+    [JsonProperty("id")]
+    public Optional<int> Id { get; set; }
+
+    [JsonProperty("items")]
+    public MediaGalleryItem[] Items { get; set; }
+
+    public MediaGalleryComponent() { }
+
+    int? IMessageComponent.Id => Id.ToNullable();
+}
diff --git a/src/Discord.Net.Rest/API/Common/MediaGalleryItem.cs b/src/Discord.Net.Rest/API/Common/MediaGalleryItem.cs
new file mode 100644
index 0000000000..dda02dc5b7
--- /dev/null
+++ b/src/Discord.Net.Rest/API/Common/MediaGalleryItem.cs
@@ -0,0 +1,15 @@
+using Newtonsoft.Json;
+
+namespace Discord.API;
+
+internal class MediaGalleryItem
+{
+    [JsonProperty("media")]
+    public UnfurledMediaItem Media { get; set; }
+
+    [JsonProperty("description")]
+    public Optional<string> Description { get; set; }
+
+    [JsonProperty("spoiler")]
+    public Optional<bool> IsSpoiler { get; set; }
+}
diff --git a/src/Discord.Net.Rest/API/Common/SectionComponent.cs b/src/Discord.Net.Rest/API/Common/SectionComponent.cs
new file mode 100644
index 0000000000..0674bd17eb
--- /dev/null
+++ b/src/Discord.Net.Rest/API/Common/SectionComponent.cs
@@ -0,0 +1,22 @@
+using Newtonsoft.Json;
+
+namespace Discord.API;
+
+internal class SectionComponent : IMessageComponent
+{
+    [JsonProperty("type")]
+    public ComponentType Type { get; set; }
+
+    [JsonProperty("id")]
+    public Optional<int> Id { get; set; }
+
+    [JsonProperty("components")]
+    public IMessageComponent[] Components { get; set; }
+
+    [JsonProperty("accessory")]
+    public IMessageComponent Accessory { get; set; }
+
+    public SectionComponent() { }
+
+    int? IMessageComponent.Id => Id.ToNullable();
+}
diff --git a/src/Discord.Net.Rest/API/Common/SeparatorComponent.cs b/src/Discord.Net.Rest/API/Common/SeparatorComponent.cs
new file mode 100644
index 0000000000..d83f123eb6
--- /dev/null
+++ b/src/Discord.Net.Rest/API/Common/SeparatorComponent.cs
@@ -0,0 +1,21 @@
+using Newtonsoft.Json;
+
+namespace Discord.API;
+
+internal class SeparatorComponent : IMessageComponent
+{
+    [JsonProperty("type")]
+    public ComponentType Type { get; set; }
+
+    [JsonProperty("id")]
+    public Optional<int> Id { get; set; }
+
+    [JsonProperty("divider")]
+    public Optional<bool> IsDivider { get; set; }
+
+    [JsonProperty("spacing")]
+    public Optional<SeparatorSpacingSize> Spacing { get; set; }
+
+    public SeparatorComponent() { }
+    int? IMessageComponent.Id => Id.ToNullable();
+}
diff --git a/src/Discord.Net.Rest/API/Common/ThumbnailComponent.cs b/src/Discord.Net.Rest/API/Common/ThumbnailComponent.cs
new file mode 100644
index 0000000000..854e302be9
--- /dev/null
+++ b/src/Discord.Net.Rest/API/Common/ThumbnailComponent.cs
@@ -0,0 +1,22 @@
+using Newtonsoft.Json;
+
+namespace Discord.API;
+
+internal class ThumbnailComponent : IMessageComponent
+{
+    [JsonProperty("type")]
+    public ComponentType Type { get; set; }
+
+    [JsonProperty("id")]
+    public Optional<int> Id { get; set; }
+
+    [JsonProperty("description")]
+    public Optional<string> Description { get; set; }
+
+    [JsonProperty("spoiler")]
+    public Optional<bool> IsSpoiler { get; set; }
+
+    public ThumbnailComponent() { }
+
+    int? IMessageComponent.Id => Id.ToNullable();
+}

From ba9dcdf0a117e14f271b16b17dda347dc97952f6 Mon Sep 17 00:00:00 2001
From: Misha133 <mihagribkov133@gmail.com>
Date: Sun, 2 Feb 2025 18:39:03 +0300
Subject: [PATCH 04/15] barebones builder impl

---
 .../MessageComponents/ActionRowComponent.cs   |  2 +-
 .../Builders/ActionRowBuilder.cs              | 29 +++---
 .../Builders/ButtonBuilder.cs                 |  6 +-
 .../Builders/ComponentBuilder.cs              | 39 ++++----
 .../Builders/ComponentBuilderV2.cs            | 85 ++++++++++++++++++
 .../Builders/ContainerComponentBuilder.cs     | 61 +++++++++++++
 .../Builders/FileComponentBuilder.cs          | 37 ++++++++
 .../Builders/IInteractableComponentBuilder.cs |  6 ++
 .../Builders/IMessageComponentBuilder.cs      | 10 +++
 .../Builders/MediaGalleryBuilder.cs           | 39 ++++++++
 .../Builders/MediaGalleryItemProperties.cs    | 19 ++++
 .../Builders/SectionBuilder.cs                | 53 +++++++++++
 .../Builders/SelectMenuBuilder.cs             |  4 +-
 .../Builders/SeparatorBuilder.cs              | 37 ++++++++
 .../Builders/TextDisplayBuilder.cs            | 33 +++++++
 .../Builders/TextDisplayComponentBuilder.cs   | 18 ----
 .../Builders/TextInputBuilder.cs              |  6 +-
 .../Builders/ThumbnailBuilder.cs              | 45 ++++++++++
 .../Builders/UnfurledMediaItemProperties.cs   | 12 +++
 .../MessageComponents/ComponentType.cs        |  2 +
 .../MessageComponents/ContainerComponent.cs   | 24 +++++
 .../MessageComponents/FileComponent.cs        | 19 ++++
 .../MediaGalleryComponent.cs                  | 18 ++++
 .../MessageComponents/MediaGalleryItem.cs     | 17 ++++
 .../MessageComponents/SectionComponent.cs     | 21 +++++
 .../MessageComponents/SeparatorComponent.cs   | 19 ++++
 .../MessageComponents/TextDisplayComponent.cs |  2 +-
 .../MessageComponents/ThumbnailComponent.cs   | 22 +++++
 .../MessageComponents/UnfurledMediaItem.cs    | 11 +++
 .../Interactions/Modals/ModalBuilder.cs       | 31 +++----
 .../API/Common/ContainerComponent.cs          | 11 +++
 .../API/Common/FileComponent.cs               |  9 ++
 .../API/Common/MediaGalleryComponent.cs       | 14 +++
 .../API/Common/SectionComponent.cs            | 10 +++
 .../API/Common/SeparatorComponent.cs          |  9 ++
 .../API/Common/ThumbnailComponent.cs          | 12 +++
 .../Extensions/MessageComponentExtension.cs   | 88 ++++++++++++++++++-
 .../Converters/MessageComponentConverter.cs   | 21 ++++-
 38 files changed, 821 insertions(+), 80 deletions(-)
 create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderV2.cs
 create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ContainerComponentBuilder.cs
 create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/FileComponentBuilder.cs
 create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IInteractableComponentBuilder.cs
 create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IMessageComponentBuilder.cs
 create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/MediaGalleryBuilder.cs
 create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/MediaGalleryItemProperties.cs
 create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SectionBuilder.cs
 create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SeparatorBuilder.cs
 create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextDisplayBuilder.cs
 delete mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextDisplayComponentBuilder.cs
 create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ThumbnailBuilder.cs
 create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/UnfurledMediaItemProperties.cs
 create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/ContainerComponent.cs
 create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/FileComponent.cs
 create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/MediaGalleryComponent.cs
 create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/MediaGalleryItem.cs
 create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/SectionComponent.cs
 create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/SeparatorComponent.cs
 create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/ThumbnailComponent.cs
 create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/UnfurledMediaItem.cs

diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ActionRowComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ActionRowComponent.cs
index bca11c4824..855b61ee29 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ActionRowComponent.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ActionRowComponent.cs
@@ -20,7 +20,7 @@ public class ActionRowComponent : IMessageComponent
 
     internal ActionRowComponent() { }
 
-    internal ActionRowComponent(List<IMessageComponent> components)
+    internal ActionRowComponent(IReadOnlyCollection<IMessageComponent> components)
     {
         Components = components;
     }
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ActionRowBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ActionRowBuilder.cs
index dbbe4e9956..3004c8e825 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ActionRowBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ActionRowBuilder.cs
@@ -8,8 +8,12 @@ namespace Discord;
 /// <summary>
 ///     Represents a class used to build Action rows.
 /// </summary>
-public class ActionRowBuilder
+public class ActionRowBuilder : IMessageComponentBuilder
 {
+    public ComponentType Type => ComponentType.ActionRow;
+
+    public int? Id { get; set; }
+
     /// <summary>
     ///     The max amount of child components this row can hold.
     /// </summary>
@@ -20,7 +24,7 @@ public class ActionRowBuilder
     /// </summary>
     /// <exception cref="ArgumentNullException" accessor="set"><see cref="Components"/> cannot be null.</exception>
     /// <exception cref="ArgumentException" accessor="set"><see cref="Components"/> count exceeds <see cref="MaxChildCount"/>.</exception>
-    public List<IMessageComponent> Components
+    public List<IMessageComponentBuilder> Components
     {
         get => _components;
         set
@@ -37,7 +41,7 @@ public List<IMessageComponent> Components
         }
     }
 
-    private List<IMessageComponent> _components = new List<IMessageComponent>();
+    private List<IMessageComponentBuilder> _components = new ();
 
     /// <summary>
     ///     Adds a list of components to the current row.
@@ -45,7 +49,7 @@ public List<IMessageComponent> Components
     /// <param name="components">The list of components to add.</param>
     /// <inheritdoc cref="Components"/>
     /// <returns>The current builder.</returns>
-    public ActionRowBuilder WithComponents(List<IMessageComponent> components)
+    public ActionRowBuilder WithComponents(List<IMessageComponentBuilder> components)
     {
         Components = components;
         return this;
@@ -57,7 +61,7 @@ public ActionRowBuilder WithComponents(List<IMessageComponent> components)
     /// <param name="component">The component to add.</param>
     /// <exception cref="InvalidOperationException">Components count reached <see cref="MaxChildCount"/></exception>
     /// <returns>The current builder.</returns>
-    public ActionRowBuilder AddComponent(IMessageComponent component)
+    public ActionRowBuilder AddComponent(IMessageComponentBuilder component)
     {
         if (Components.Count >= MaxChildCount)
             throw new InvalidOperationException($"Components count reached {MaxChildCount}");
@@ -103,13 +107,11 @@ public ActionRowBuilder WithSelectMenu(SelectMenuBuilder menu)
     {
         if (menu.Options is not null && menu.Options.Distinct().Count() != menu.Options.Count)
             throw new InvalidOperationException("Please make sure that there is no duplicates values.");
-
-        var builtMenu = menu.Build();
-
+        
         if (Components.Count != 0)
             throw new InvalidOperationException($"A Select Menu cannot exist in a pre-occupied ActionRow.");
 
-        AddComponent(builtMenu);
+        AddComponent(menu);
 
         return this;
     }
@@ -152,15 +154,13 @@ public ActionRowBuilder WithButton(
     /// <returns>The current builder.</returns>
     public ActionRowBuilder WithButton(ButtonBuilder button)
     {
-        var builtButton = button.Build();
-
         if (Components.Count >= 5)
             throw new InvalidOperationException($"Components count reached {MaxChildCount}");
 
         if (Components.Any(x => x.Type.IsSelectType()))
             throw new InvalidOperationException($"A button cannot be added to a row with a SelectMenu");
 
-        AddComponent(builtButton);
+        AddComponent(button);
 
         return this;
     }
@@ -171,10 +171,11 @@ public ActionRowBuilder WithButton(ButtonBuilder button)
     /// <returns>A <see cref="ActionRowComponent"/> that can be used within a <see cref="ComponentBuilder"/></returns>
     public ActionRowComponent Build()
     {
-        return new ActionRowComponent(_components);
+        return new ActionRowComponent(_components.Select(x => x.Build()).ToList());
     }
+    IMessageComponent IMessageComponentBuilder.Build() => Build();
 
-    internal bool CanTakeComponent(IMessageComponent component)
+    internal bool CanTakeComponent(IMessageComponentBuilder component)
     {
         switch (component.Type)
         {
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 058f4e473c..e6c56afe99 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ButtonBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ButtonBuilder.cs
@@ -7,8 +7,10 @@ namespace Discord;
 /// <summary>
 ///     Represents a class used to build <see cref="ButtonComponent"/>'s.
 /// </summary>
-public class ButtonBuilder
+public class ButtonBuilder : IInteractableComponentBuilder
 {
+    public ComponentType Type => ComponentType.Button;
+
     /// <summary>
     ///     The max length of a <see cref="ButtonComponent.Label"/>.
     /// </summary>
@@ -322,4 +324,6 @@ public ButtonComponent Build()
 
         return new ButtonComponent(Style, Label, Emote, CustomId, Url, IsDisabled, SkuId, Id);
     }
+
+    IMessageComponent IMessageComponentBuilder.Build() => Build();
 }
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilder.cs
index ce11bdee9d..5193c45852 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilder.cs
@@ -67,18 +67,13 @@ internal void AddComponent(IMessageComponent component, int row)
     {
         switch (component)
         {
-            case TextDisplayComponent textDisplay:
-                break;
-
             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;
@@ -103,7 +98,7 @@ public ComponentBuilder RemoveComponentsOfType(ComponentType t)
     /// <returns>The current builder.</returns>
     public ComponentBuilder RemoveComponent(string customId)
     {
-        this.ActionRows.ForEach(ar => ar.Components.RemoveAll(c => c is IInteractableComponent ic && ic.CustomId == customId));
+        this.ActionRows.ForEach(ar => ar.Components.RemoveAll(c => c is IInteractableComponent i && i.CustomId == customId));
         return this;
     }
 
@@ -163,20 +158,15 @@ 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<ActionRowBuilder>
-            {
-                new ActionRowBuilder().AddComponent(builtMenu)
-            };
+            _actionRows = [new ActionRowBuilder().AddComponent(menu)];
         }
         else
         {
             if (_actionRows.Count == row)
-                _actionRows.Add(new ActionRowBuilder().AddComponent(builtMenu));
+                _actionRows.Add(new ActionRowBuilder().AddComponent(menu));
             else
             {
                 ActionRowBuilder actionRow;
@@ -188,12 +178,12 @@ public ComponentBuilder WithSelectMenu(SelectMenuBuilder menu, int row = 0)
                     _actionRows.Add(actionRow);
                 }
 
-                if (actionRow.CanTakeComponent(builtMenu))
-                    actionRow.AddComponent(builtMenu);
+                if (actionRow.CanTakeComponent(menu))
+                    actionRow.AddComponent(menu);
                 else if (row < MaxActionRowCount)
                     WithSelectMenu(menu, row + 1);
                 else
-                    throw new InvalidOperationException($"There is no more row to add a {nameof(builtMenu)}");
+                    throw new InvalidOperationException($"There is no more row to add a {nameof(menu)}");
             }
         }
 
@@ -248,16 +238,17 @@ public ComponentBuilder WithButton(ButtonBuilder button, int row = 0)
     {
         Preconditions.LessThan(row, MaxActionRowCount, nameof(row));
 
-        var builtButton = button.Build();
-
         if (_actionRows == null)
         {
-            _actionRows = [new ActionRowBuilder().AddComponent(builtButton)];
+            _actionRows = new List<ActionRowBuilder>
+            {
+                new ActionRowBuilder().AddComponent(button)
+            };
         }
         else
         {
             if (_actionRows.Count == row)
-                _actionRows.Add(new ActionRowBuilder().AddComponent(builtButton));
+                _actionRows.Add(new ActionRowBuilder().AddComponent(button));
             else
             {
                 ActionRowBuilder actionRow;
@@ -269,8 +260,8 @@ public ComponentBuilder WithButton(ButtonBuilder button, int row = 0)
                     _actionRows.Add(actionRow);
                 }
 
-                if (actionRow.CanTakeComponent(builtButton))
-                    actionRow.AddComponent(builtButton);
+                if (actionRow.CanTakeComponent(button))
+                    actionRow.AddComponent(button);
                 else if (row < MaxActionRowCount)
                     WithButton(button, row + 1);
                 else
@@ -328,7 +319,7 @@ public MessageComponent Build()
                     _actionRows.RemoveAt(i);
 
         return _actionRows != null
-            ? new MessageComponent(_actionRows.Select(x => x.Build()).ToList())
+            ? new MessageComponent(_actionRows.Select(x => x.Build()).OfType<IMessageComponent>().ToList())
             : MessageComponent.Empty;
     }
 }
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderV2.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderV2.cs
new file mode 100644
index 0000000000..18ec131d83
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderV2.cs
@@ -0,0 +1,85 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Discord;
+
+public class ComponentBuilderV2
+{
+    public ComponentBuilderV2() {}
+
+    private List<IMessageComponentBuilder> _components = new();
+
+    public List<IMessageComponentBuilder> Components
+    {
+        get => _components;
+        set
+        {
+            _components = value ?? throw new ArgumentNullException(nameof(value), $"{nameof(Components)} cannot be null.");
+        }
+    }
+
+    public ComponentBuilderV2 AddComponent(IMessageComponentBuilder component)
+    {
+        Components.Add(component);
+        return this;
+    }
+
+    public ComponentBuilderV2 AddComponent(IMessageComponent component)
+    {
+        return this;
+    }
+
+    public ComponentBuilderV2 WithComponents(List<IMessageComponentBuilder> components)
+    {
+        Components = components;
+        return this;
+    }
+
+    public ComponentBuilderV2 WithActionRow(ActionRowBuilder actionRow)
+    {
+        Components.Add(actionRow);
+        return this;
+    }
+
+    public ComponentBuilderV2 WithTextDisplay(TextDisplayBuilder textDisplayComponent)
+    {
+        Components.Add(textDisplayComponent);
+        return this;
+    }
+
+    public ComponentBuilderV2 WithSection(SectionBuilder sectionComponent)
+    {
+        Components.Add(sectionComponent);
+        return this;
+    }
+
+    public ComponentBuilderV2 WithMediaGallery(MediaGalleryBuilder mediaGallery)
+    {
+        Components.Add(mediaGallery);
+        return this;
+    }
+
+    public ComponentBuilderV2 WithSeparator(SeparatorBuilder separator)
+    {
+        Components.Add(separator);
+        return this;
+    }
+
+    public ComponentBuilderV2 WithFile(FileComponentBuilder file)
+    {
+        Components.Add(file);
+        return this;
+    }
+
+    public ComponentBuilderV2 WithContainer(ContainerComponentBuilder container)
+    {
+        Components.Add(container);
+        return this;
+    }
+
+    public MessageComponent Build()
+    {
+        return new MessageComponent(Components.Select(x => x.Build()).ToList());
+    }
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ContainerComponentBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ContainerComponentBuilder.cs
new file mode 100644
index 0000000000..5eebad2c63
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ContainerComponentBuilder.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+
+namespace Discord;
+
+public class ContainerComponentBuilder : IMessageComponentBuilder
+{
+    public ComponentType Type => ComponentType.Container;
+
+    public int? Id { get; set; }
+
+    private List<IMessageComponentBuilder> _components = new();
+
+    public List<IMessageComponentBuilder> Components
+    {
+        get => _components;
+        set => _components = value ?? throw new ArgumentNullException(nameof(value), $"{nameof(Components)} cannot be null.");
+    }
+
+    public int? AccentColor { get; set; }
+
+    public bool? IsSpoiler { get; set; }
+
+    public ContainerComponentBuilder WithId(int id)
+    {
+        Id = id;
+        return this;
+    }
+
+    public ContainerComponentBuilder WithAccentColor(int accentColor)
+    {
+        AccentColor = accentColor;
+        return this;
+    }
+
+    public ContainerComponentBuilder WithSpoiler(bool isSpoiler)
+    {
+        IsSpoiler = isSpoiler;
+        return this;
+    }
+
+    public ContainerComponentBuilder AddComponent(IMessageComponentBuilder component)
+    {
+        Components.Add(component);
+        return this;
+    }
+
+    public ContainerComponentBuilder WithComponents(List<IMessageComponentBuilder> components)
+    {
+        Components = components;
+        return this;
+    }
+
+    public ContainerComponent Build()
+    {
+        return new(Components.ConvertAll(x => x.Build()).ToImmutableArray(), AccentColor, IsSpoiler, Id);
+    }
+
+    IMessageComponent IMessageComponentBuilder.Build() => Build();
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/FileComponentBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/FileComponentBuilder.cs
new file mode 100644
index 0000000000..a40b7e4888
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/FileComponentBuilder.cs
@@ -0,0 +1,37 @@
+namespace Discord;
+
+public class FileComponentBuilder : IMessageComponentBuilder
+{
+    public ComponentType Type => ComponentType.File;
+
+    public int? Id { get; set; }
+
+    public UnfurledMediaItemProperties File { get; set; }
+
+    public bool? IsSpoiler { get; set; }
+
+    public FileComponentBuilder WithFile(UnfurledMediaItemProperties file)
+    {
+        File = file;
+        return this;
+    }
+
+    public FileComponentBuilder WithIsSpoiler(bool? isSpoiler)
+    {
+        IsSpoiler = isSpoiler;
+        return this;
+    }
+
+    public FileComponentBuilder WithId(int id)
+    {
+        Id = id;
+        return this;
+    }
+
+    public FileComponent Build()
+    {
+        return new(new UnfurledMediaItem(File.Url), IsSpoiler, Id);
+    }
+
+    IMessageComponent IMessageComponentBuilder.Build() => Build();
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IInteractableComponentBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IInteractableComponentBuilder.cs
new file mode 100644
index 0000000000..703d9950b7
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IInteractableComponentBuilder.cs
@@ -0,0 +1,6 @@
+namespace Discord;
+
+public interface IInteractableComponentBuilder : IMessageComponentBuilder
+{
+    string CustomId { get; set; }
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IMessageComponentBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IMessageComponentBuilder.cs
new file mode 100644
index 0000000000..cb5ab6e511
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IMessageComponentBuilder.cs
@@ -0,0 +1,10 @@
+namespace Discord;
+
+public interface IMessageComponentBuilder
+{
+    ComponentType Type { get; }
+
+    int? Id { get; set;  }
+
+    IMessageComponent 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
new file mode 100644
index 0000000000..563d5396e9
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/MediaGalleryBuilder.cs
@@ -0,0 +1,39 @@
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+
+namespace Discord;
+
+public class MediaGalleryBuilder : IMessageComponentBuilder
+{
+    public ComponentType Type => ComponentType.MediaGallery;
+
+    public int? Id { get; set; }
+
+    private List<MediaGalleryItemProperties> _items = new();
+
+    public List<MediaGalleryItemProperties> Items
+    {
+        get => _items;
+        set => _items = value;
+    }
+
+    public MediaGalleryBuilder AddItem(MediaGalleryItemProperties item)
+    {
+        _items.Add(item);
+        return this;
+    }
+
+    public MediaGalleryBuilder WithId(int id)
+    {
+        Id = id;
+        return this;
+    }
+
+    public MediaGalleryComponent Build()
+    {
+        return new(_items.Select(x => new MediaGalleryItem(new UnfurledMediaItem(x.Media.Url), x.Description, x.IsSpoiler)).ToImmutableArray(), Id);
+    }
+
+    IMessageComponent IMessageComponentBuilder.Build() => Build();
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/MediaGalleryItemProperties.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/MediaGalleryItemProperties.cs
new file mode 100644
index 0000000000..9bbc8e02c4
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/MediaGalleryItemProperties.cs
@@ -0,0 +1,19 @@
+namespace Discord;
+
+public struct MediaGalleryItemProperties
+{
+    public UnfurledMediaItemProperties Media { get; set; }
+
+    public string Description { get; set; }
+
+    public bool IsSpoiler { get; set; }
+
+    public MediaGalleryItemProperties() { }
+
+    public MediaGalleryItemProperties(UnfurledMediaItemProperties media, string description = null, bool isSpoiler = false)
+    {
+        Media = media;
+        Description = description;
+        IsSpoiler = isSpoiler;
+    }
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SectionBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SectionBuilder.cs
new file mode 100644
index 0000000000..dcc08ffd02
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SectionBuilder.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+
+namespace Discord;
+
+public class SectionBuilder : IMessageComponentBuilder
+{
+    public ComponentType Type => ComponentType.Section;
+
+    public int? Id { get; set; }
+
+    private List<IMessageComponentBuilder> _components = new();
+    public List<IMessageComponentBuilder> Components
+    {
+        get => _components;
+        set => _components = value ?? throw new ArgumentNullException(nameof(value), $"{nameof(Components)} cannot be null.");
+    }
+
+    public IMessageComponentBuilder Accessory { get; set; }
+
+    public SectionBuilder WithId(int id)
+    {
+        Id = id;
+        return this;
+    }
+
+    public SectionBuilder WithAccessory(IMessageComponentBuilder accessory)
+    {
+        Accessory = accessory;
+        return this;
+    }
+
+    public SectionBuilder AddComponent(IMessageComponentBuilder component)
+    {
+        Components.Add(component);
+        return this;
+    }
+
+    public SectionBuilder WithComponents(List<IMessageComponentBuilder> components)
+    {
+        Components = components;
+        return this;
+    }
+
+    public SectionComponent Build()
+    {
+        return new(Id, Components.Select(x => x.Build()).ToImmutableArray(), Accessory?.Build());
+    }
+
+    IMessageComponent IMessageComponentBuilder.Build() => Build();
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SelectMenuBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SelectMenuBuilder.cs
index 41c49e93f0..7c1c2b2702 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SelectMenuBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SelectMenuBuilder.cs
@@ -8,7 +8,7 @@ namespace Discord;
 /// <summary>
 ///     Represents a class used to build <see cref="SelectMenuComponent"/>'s.
 /// </summary>
-public class SelectMenuBuilder
+public class SelectMenuBuilder : IInteractableComponentBuilder
 {
     /// <summary>
     ///     The max length of a <see cref="SelectMenuComponent.Placeholder"/>.
@@ -407,4 +407,6 @@ public SelectMenuComponent Build()
 
         return new SelectMenuComponent(CustomId, options, Placeholder, MinValues, MaxValues, IsDisabled, Type, Id, ChannelTypes, DefaultValues);
     }
+
+    IMessageComponent IMessageComponentBuilder.Build() => Build();
 }
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SeparatorBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SeparatorBuilder.cs
new file mode 100644
index 0000000000..ba62fe2a0a
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SeparatorBuilder.cs
@@ -0,0 +1,37 @@
+namespace Discord;
+
+public class SeparatorBuilder : IMessageComponentBuilder
+{
+    public ComponentType Type => ComponentType.Separator;
+
+    public bool? IsDivider { get; set; }
+
+    public SeparatorSpacingSize? Spacing { get; set; }
+
+    public int? Id { get; set; }
+
+    public SeparatorBuilder WithIsDivider(bool? isDivider)
+    {
+        IsDivider = isDivider;
+        return this;
+    }
+
+    public SeparatorBuilder WithSpacing(SeparatorSpacingSize? spacing)
+    {
+        Spacing = spacing;
+        return this;
+    }
+
+    public SeparatorBuilder WithId(int id)
+    {
+        Id = id;
+        return this;
+    }
+
+    public SeparatorComponent Build()
+    {
+        return new(IsDivider, Spacing, Id);
+    }
+
+    IMessageComponent IMessageComponentBuilder.Build() => Build();
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextDisplayBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextDisplayBuilder.cs
new file mode 100644
index 0000000000..42e331ceb9
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextDisplayBuilder.cs
@@ -0,0 +1,33 @@
+namespace Discord;
+
+public class TextDisplayBuilder : IMessageComponentBuilder
+{
+    public ComponentType Type => ComponentType.ActionRow;
+
+    public int? Id { get; set; }
+
+    private string _content;
+    public string Content
+    {
+        get => _content;
+        set => _content = value;
+    }
+    public TextDisplayBuilder WithContent(string content)
+    {
+        Content = content;
+        return this;
+    }
+
+    public TextDisplayBuilder WithId(int id)
+    {
+        Id = id;
+        return this;
+    }
+
+    public TextDisplayComponent Build()
+    {
+        return new(_content, Id);
+    }
+
+    IMessageComponent IMessageComponentBuilder.Build() => Build();
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextDisplayComponentBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextDisplayComponentBuilder.cs
deleted file mode 100644
index 28d6fe41bf..0000000000
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextDisplayComponentBuilder.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-namespace Discord;
-
-public class TextDisplayComponentBuilder
-{
-    public int? Id { get; set; }
-
-    private string _content;
-    public string Content
-    {
-        get => _content;
-        set => _content = value;
-    }
-
-    public TextDisplayComponent Build()
-    {
-        return new(Id, _content);
-    }
-}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextInputBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextInputBuilder.cs
index 2efe92b63d..dcdfc2cdda 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextInputBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextInputBuilder.cs
@@ -6,8 +6,10 @@ namespace Discord;
 /// <summary>
 ///     Represents a builder for creating a <see cref="TextInputComponent"/>.
 /// </summary>
-public class TextInputBuilder
+public class TextInputBuilder : IInteractableComponentBuilder
 {
+    public ComponentType Type => ComponentType.TextInput;
+
     /// <summary>
     ///     The max length of a <see cref="TextInputComponent.Placeholder"/>.
     /// </summary>
@@ -262,4 +264,6 @@ public TextInputComponent Build()
 
         return new TextInputComponent(CustomId, Label, Placeholder, MinLength, MaxLength, Style, Required, Value, Id);
     }
+
+    IMessageComponent IMessageComponentBuilder.Build() => Build();
 }
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ThumbnailBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ThumbnailBuilder.cs
new file mode 100644
index 0000000000..3d6b3d127d
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ThumbnailBuilder.cs
@@ -0,0 +1,45 @@
+namespace Discord;
+
+public class ThumbnailComponentBuilder : IMessageComponentBuilder
+{
+    public ComponentType Type => ComponentType.Thumbnail;
+
+    public int? Id { get; set; }
+
+    public UnfurledMediaItemProperties Media { get; set; }
+
+    public string Description { get; set; }
+
+    public bool IsSpoiler { get; set; } = false;
+
+    public ThumbnailComponentBuilder WithMedia(UnfurledMediaItemProperties media)
+    {
+        Media = media;
+        return this;
+    }
+
+    public ThumbnailComponentBuilder WithDescription(string description)
+    {
+        Description = description;
+        return this;
+    }
+
+    public ThumbnailComponentBuilder WithId(int id)
+    {
+        Id = id;
+        return this;
+    }
+
+    public ThumbnailComponentBuilder WithSpoiler(bool isSpoiler)
+    {
+        IsSpoiler = isSpoiler;
+        return this;
+    }
+
+    public ThumbnailComponent Build()
+    {
+        return new(Id, new UnfurledMediaItem(Media.Url), Description, IsSpoiler);
+    }
+
+    IMessageComponent IMessageComponentBuilder.Build() => Build();
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/UnfurledMediaItemProperties.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/UnfurledMediaItemProperties.cs
new file mode 100644
index 0000000000..e8babb78da
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/UnfurledMediaItemProperties.cs
@@ -0,0 +1,12 @@
+namespace Discord;
+
+public struct UnfurledMediaItemProperties
+{
+    public string Url { get; set; }
+
+    public UnfurledMediaItemProperties() {}
+    public UnfurledMediaItemProperties(string url)
+    {
+        Url = url;
+    }
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentType.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentType.cs
index c0d5e196b9..5d5cce5a77 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentType.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ComponentType.cs
@@ -49,6 +49,8 @@ public enum ComponentType
 
         TextDisplay = 10,
 
+        Thumbnail = 11,
+
         MediaGallery = 12,
 
         File = 13,
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ContainerComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ContainerComponent.cs
new file mode 100644
index 0000000000..30fa37d39f
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ContainerComponent.cs
@@ -0,0 +1,24 @@
+using System.Collections.Generic;
+
+namespace Discord;
+
+public class ContainerComponent : IMessageComponent
+{
+    public ComponentType Type => ComponentType.Container;
+
+    public int? Id { get; }
+
+    public IReadOnlyCollection<IMessageComponent> Components { get; }
+
+    public int? AccentColor { get; }
+
+    public bool? IsSpoiler { get; }
+
+    internal ContainerComponent(IReadOnlyCollection<IMessageComponent> components, int? accentColor, bool? isSpoiler, int? id = null)
+    {
+        Components = components;
+        AccentColor = accentColor;
+        IsSpoiler = isSpoiler;
+        Id = id;
+    }
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/FileComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/FileComponent.cs
new file mode 100644
index 0000000000..40b89ba6e7
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/FileComponent.cs
@@ -0,0 +1,19 @@
+namespace Discord;
+
+public class FileComponent : IMessageComponent
+{
+    public ComponentType Type => ComponentType.File;
+
+    public UnfurledMediaItem File { get; }
+
+    public int? Id { get; }
+
+    public bool? IsSpoiler { get; }
+
+    internal FileComponent(UnfurledMediaItem file, bool? isSpoiler, int? id = null)
+    {
+        File = file;
+        IsSpoiler = isSpoiler;
+        Id = id;
+    }
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/MediaGalleryComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/MediaGalleryComponent.cs
new file mode 100644
index 0000000000..d08ecaa545
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/MediaGalleryComponent.cs
@@ -0,0 +1,18 @@
+using System.Collections.Generic;
+
+namespace Discord;
+
+public class MediaGalleryComponent : IMessageComponent
+{
+    public ComponentType Type => ComponentType.MediaGallery;
+
+    public int? Id { get; }
+
+    public IReadOnlyCollection<MediaGalleryItem> Items { get; }
+
+    internal MediaGalleryComponent(IReadOnlyCollection<MediaGalleryItem> items, int? id)
+    {
+        Items = items;
+        Id = id;
+    }
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/MediaGalleryItem.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/MediaGalleryItem.cs
new file mode 100644
index 0000000000..db1b4c0d17
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/MediaGalleryItem.cs
@@ -0,0 +1,17 @@
+namespace Discord;
+
+public readonly struct MediaGalleryItem
+{
+    public UnfurledMediaItem Media { get; }
+
+    public string Description { get; }
+
+    public bool IsSpoiler { get; }
+
+    internal MediaGalleryItem(UnfurledMediaItem media, string description, bool? isSpoiler)
+    {
+        Media = media;
+        Description = description;
+        IsSpoiler = isSpoiler ?? false;
+    }
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SectionComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SectionComponent.cs
new file mode 100644
index 0000000000..90a7e12df7
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SectionComponent.cs
@@ -0,0 +1,21 @@
+using System.Collections.Generic;
+
+namespace Discord;
+
+public class SectionComponent : IMessageComponent
+{
+    public ComponentType Type => ComponentType.Section;
+
+    public int? Id { get; }
+
+    public IReadOnlyCollection<IMessageComponent> Components { get; }
+
+    public IMessageComponent Accessory { get; }
+
+    internal SectionComponent(int? id, IReadOnlyCollection<IMessageComponent> components, IMessageComponent accessory)
+    {
+        Id = id;
+        Components = components;
+        Accessory = accessory;
+    }
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SeparatorComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SeparatorComponent.cs
new file mode 100644
index 0000000000..b722683a3a
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SeparatorComponent.cs
@@ -0,0 +1,19 @@
+namespace Discord;
+
+public class SeparatorComponent : IMessageComponent
+{
+    public ComponentType Type => ComponentType.Separator;
+
+    public int? Id { get; }
+
+    public bool? IsDivider { get; }
+
+    public SeparatorSpacingSize? Spacing { get; }
+
+    internal SeparatorComponent(bool? isDivider, SeparatorSpacingSize? spacing, int? id = null)
+    {
+        IsDivider = isDivider;
+        Spacing = spacing;
+        Id = id;
+    }
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/TextDisplayComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/TextDisplayComponent.cs
index 1829804f59..e96bac84e9 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/TextDisplayComponent.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/TextDisplayComponent.cs
@@ -8,7 +8,7 @@ public class TextDisplayComponent : IMessageComponent
 
     public string Content { get; }
 
-    internal TextDisplayComponent(int? id, string content)
+    internal TextDisplayComponent(string content, int? id = null)
     {
         Id = id;
         Content = content;
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ThumbnailComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ThumbnailComponent.cs
new file mode 100644
index 0000000000..fbbaf34ecd
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ThumbnailComponent.cs
@@ -0,0 +1,22 @@
+namespace Discord;
+
+public class ThumbnailComponent : IMessageComponent
+{
+    public ComponentType Type => ComponentType.Thumbnail;
+
+    public int? Id { get; }
+
+    public UnfurledMediaItem Media { get; }
+
+    public string Description { get; }
+
+    public bool IsSpoiler { get; }
+
+    internal ThumbnailComponent(int? id, UnfurledMediaItem media, string description, bool? isSpoiler)
+    {
+        Id = id;
+        Media = media;
+        Description = description;
+        IsSpoiler = isSpoiler ?? false;
+    }
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/UnfurledMediaItem.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/UnfurledMediaItem.cs
new file mode 100644
index 0000000000..d9cb7b9592
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/UnfurledMediaItem.cs
@@ -0,0 +1,11 @@
+namespace Discord;
+
+public readonly struct UnfurledMediaItem
+{
+    public string Url { get; }
+
+    internal UnfurledMediaItem(string url)
+    {
+        Url = url;
+    }
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/Modals/ModalBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/Modals/ModalBuilder.cs
index 98a37e2a8e..6e04e95d9e 100644
--- a/src/Discord.Net.Core/Entities/Interactions/Modals/ModalBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/Modals/ModalBuilder.cs
@@ -111,21 +111,20 @@ public ModalBuilder AddComponents(List<IMessageComponent> components, int row)
         }
 
         /// <summary>
-        ///     Gets a <typeparamref name="TMessageComponent"/> by the specified <paramref name="customId"/>.
+        ///     Gets a <typeparamref name="TMessageComponentBuilder"/> by the specified <paramref name="customId"/>.
         /// </summary>
-        /// <typeparam name="TMessageComponent">The type of the component to get.</typeparam>
-        /// <param name="customId">The <see cref="IInteractableComponent.CustomId"/> of the component to get.</param>
+        /// <typeparam name="TMessageComponentBuilder">The type of the component to get.</typeparam>
+        /// <param name="customId">The <see cref="IInteractableComponentBuilder.CustomId"/> of the component to get.</param>
         /// <returns>
-        ///     The component of type <typeparamref name="TMessageComponent"/> that was found, <see langword="null"/> otherwise.
+        ///     The component of type <typeparamref name="TMessageComponentBuilder"/> that was found, <see langword="null"/> otherwise.
         /// </returns>
-        public TMessageComponent GetComponent<TMessageComponent>(string customId)
-            where TMessageComponent : class, IInteractableComponent
+        public TMessageComponentBuilder GetComponent<TMessageComponentBuilder>(string customId)
+            where TMessageComponentBuilder : class, IInteractableComponentBuilder
         {
             Preconditions.NotNull(customId, nameof(customId));
 
-            return Components.ActionRows
-                ?.SelectMany(r => r.Components.OfType<TMessageComponent>())
-                .FirstOrDefault(c => c is IInteractableComponent ic && ic?.CustomId == customId);
+            return Components.ActionRows?.SelectMany(r => r.Components.OfType<TMessageComponentBuilder>())
+                .FirstOrDefault(c => c.CustomId == customId);
         }
 
         /// <summary>
@@ -141,7 +140,7 @@ public ModalBuilder UpdateTextInput(string customId, Action<TextInputBuilder> up
         {
             Preconditions.NotNull(customId, nameof(customId));
 
-            var component = GetComponent<TextInputComponent>(customId) ?? throw new ArgumentException($"There is no component of type {nameof(TextInputComponent)} with the specified custom ID in this modal builder.", nameof(customId));
+            var component = GetComponent<TextInputBuilder>(customId) ?? throw new ArgumentException($"There is no component of type {nameof(TextInputComponent)} with the specified custom ID in this modal builder.", nameof(customId));
             var row = Components.ActionRows.First(r => r.Components.Contains(component));
 
             var builder = new TextInputBuilder
@@ -159,7 +158,7 @@ public ModalBuilder UpdateTextInput(string customId, Action<TextInputBuilder> up
             updateTextInput(builder);
 
             row.Components.Remove(component);
-            row.AddComponent(builder.Build());
+            row.AddComponent(builder);
 
             return this;
         }
@@ -314,19 +313,17 @@ public ModalComponentBuilder WithTextInput(TextInputBuilder text, int row = 0)
         {
             Preconditions.LessThan(row, MaxActionRowCount, nameof(row));
 
-            var builtButton = text.Build();
-
             if (_actionRows == null)
             {
                 _actionRows = new List<ActionRowBuilder>
                 {
-                    new ActionRowBuilder().AddComponent(builtButton)
+                    new ActionRowBuilder().AddComponent(text)
                 };
             }
             else
             {
                 if (_actionRows.Count == row)
-                    _actionRows.Add(new ActionRowBuilder().AddComponent(builtButton));
+                    _actionRows.Add(new ActionRowBuilder().AddComponent(text));
                 else
                 {
                     ActionRowBuilder actionRow;
@@ -338,8 +335,8 @@ public ModalComponentBuilder WithTextInput(TextInputBuilder text, int row = 0)
                         _actionRows.Add(actionRow);
                     }
 
-                    if (actionRow.CanTakeComponent(builtButton))
-                        actionRow.AddComponent(builtButton);
+                    if (actionRow.CanTakeComponent(text))
+                        actionRow.AddComponent(text);
                     else if (row < MaxActionRowCount)
                         WithTextInput(text, row + 1);
                     else
diff --git a/src/Discord.Net.Rest/API/Common/ContainerComponent.cs b/src/Discord.Net.Rest/API/Common/ContainerComponent.cs
index 645f03f1cf..6681c82b33 100644
--- a/src/Discord.Net.Rest/API/Common/ContainerComponent.cs
+++ b/src/Discord.Net.Rest/API/Common/ContainerComponent.cs
@@ -1,4 +1,6 @@
+using Discord.Rest;
 using Newtonsoft.Json;
+using System.Linq;
 
 namespace Discord.API;
 
@@ -21,5 +23,14 @@ internal class ContainerComponent : IMessageComponent
 
     public ContainerComponent() { }
 
+    public ContainerComponent(Discord.ContainerComponent component)
+    {
+        Type = component.Type;
+        Id = component.Id ?? Optional<int>.Unspecified;
+        AccentColor = component.AccentColor ?? Optional<int>.Unspecified;
+        IsSpoiler = component.IsSpoiler ?? Optional<bool>.Unspecified;
+        Components = component.Components.Select(x => x.ToModel()).ToArray();
+    }
+
     int? IMessageComponent.Id => Id.ToNullable();
 }
diff --git a/src/Discord.Net.Rest/API/Common/FileComponent.cs b/src/Discord.Net.Rest/API/Common/FileComponent.cs
index 8f95dba4b4..81f189ae6b 100644
--- a/src/Discord.Net.Rest/API/Common/FileComponent.cs
+++ b/src/Discord.Net.Rest/API/Common/FileComponent.cs
@@ -1,3 +1,4 @@
+using Discord.Rest;
 using Newtonsoft.Json;
 
 namespace Discord.API;
@@ -17,5 +18,13 @@ internal class FileComponent : IMessageComponent
 
     public FileComponent() { }
 
+    public FileComponent(Discord.FileComponent component)
+    {
+        Type = component.Type;
+        Id = component.Id ?? Optional<int>.Unspecified;
+        File = component.File.ToModel();
+        IsSpoiler = component.IsSpoiler ?? Optional<bool>.Unspecified;
+    }
+
     int? IMessageComponent.Id => Id.ToNullable();
 }
diff --git a/src/Discord.Net.Rest/API/Common/MediaGalleryComponent.cs b/src/Discord.Net.Rest/API/Common/MediaGalleryComponent.cs
index f8a0546361..cc26cbd155 100644
--- a/src/Discord.Net.Rest/API/Common/MediaGalleryComponent.cs
+++ b/src/Discord.Net.Rest/API/Common/MediaGalleryComponent.cs
@@ -1,4 +1,6 @@
+using Discord.Rest;
 using Newtonsoft.Json;
+using System.Linq;
 
 namespace Discord.API;
 
@@ -15,5 +17,17 @@ internal class MediaGalleryComponent : IMessageComponent
 
     public MediaGalleryComponent() { }
 
+    public MediaGalleryComponent(Discord.MediaGalleryComponent component)
+    {
+        Type = component.Type;
+        Id = component.Id ?? Optional<int>.Unspecified;
+        Items = component.Items.Select(x => new MediaGalleryItem
+        {
+            Description = x.Description,
+            IsSpoiler = x.IsSpoiler,
+            Media = x.Media.ToModel()
+        }).ToArray();
+    }
+
     int? IMessageComponent.Id => Id.ToNullable();
 }
diff --git a/src/Discord.Net.Rest/API/Common/SectionComponent.cs b/src/Discord.Net.Rest/API/Common/SectionComponent.cs
index 0674bd17eb..76ea893d8c 100644
--- a/src/Discord.Net.Rest/API/Common/SectionComponent.cs
+++ b/src/Discord.Net.Rest/API/Common/SectionComponent.cs
@@ -1,4 +1,6 @@
+using Discord.Rest;
 using Newtonsoft.Json;
+using System.Linq;
 
 namespace Discord.API;
 
@@ -18,5 +20,13 @@ internal class SectionComponent : IMessageComponent
 
     public SectionComponent() { }
 
+    public SectionComponent(Discord.SectionComponent component)
+    {
+        Type = component.Type;
+        Id = component.Id ?? Optional<int>.Unspecified;
+        Components = component.Components.Select(x => x.ToModel()).ToArray();
+        Accessory = component.Accessory.ToModel();
+    }
+
     int? IMessageComponent.Id => Id.ToNullable();
 }
diff --git a/src/Discord.Net.Rest/API/Common/SeparatorComponent.cs b/src/Discord.Net.Rest/API/Common/SeparatorComponent.cs
index d83f123eb6..f01affac2f 100644
--- a/src/Discord.Net.Rest/API/Common/SeparatorComponent.cs
+++ b/src/Discord.Net.Rest/API/Common/SeparatorComponent.cs
@@ -17,5 +17,14 @@ internal class SeparatorComponent : IMessageComponent
     public Optional<SeparatorSpacingSize> Spacing { get; set; }
 
     public SeparatorComponent() { }
+
+    public SeparatorComponent(Discord.SeparatorComponent component)
+    {
+        Type = component.Type;
+        Id = component.Id ?? Optional<int>.Unspecified;
+        IsDivider = component.IsDivider ?? Optional<bool>.Unspecified;
+        Spacing = component.Spacing ?? Optional<SeparatorSpacingSize>.Unspecified;
+    }
+
     int? IMessageComponent.Id => Id.ToNullable();
 }
diff --git a/src/Discord.Net.Rest/API/Common/ThumbnailComponent.cs b/src/Discord.Net.Rest/API/Common/ThumbnailComponent.cs
index 854e302be9..8ee7314fcb 100644
--- a/src/Discord.Net.Rest/API/Common/ThumbnailComponent.cs
+++ b/src/Discord.Net.Rest/API/Common/ThumbnailComponent.cs
@@ -10,6 +10,9 @@ internal class ThumbnailComponent : IMessageComponent
     [JsonProperty("id")]
     public Optional<int> Id { get; set; }
 
+    [JsonProperty("media")]
+    public UnfurledMediaItem Media { get; set; }
+
     [JsonProperty("description")]
     public Optional<string> Description { get; set; }
 
@@ -18,5 +21,14 @@ internal class ThumbnailComponent : IMessageComponent
 
     public ThumbnailComponent() { }
 
+    public ThumbnailComponent(Discord.ThumbnailComponent component)
+    {
+        Type = component.Type;
+        Id = component.Id ?? Optional<int>.Unspecified;
+        Media = new UnfurledMediaItem { Url = component.Media.Url };
+        Description = component.Description;
+        IsSpoiler = component.IsSpoiler;
+    }
+
     int? IMessageComponent.Id => Id.ToNullable();
 }
diff --git a/src/Discord.Net.Rest/Extensions/MessageComponentExtension.cs b/src/Discord.Net.Rest/Extensions/MessageComponentExtension.cs
index a36ba6e20c..19d986eaa5 100644
--- a/src/Discord.Net.Rest/Extensions/MessageComponentExtension.cs
+++ b/src/Discord.Net.Rest/Extensions/MessageComponentExtension.cs
@@ -20,6 +20,24 @@ internal static IMessageComponent ToModel(this IMessageComponent component)
 
             case TextDisplayComponent textDisplay:
                 return new API.TextDisplayComponent(textDisplay);
+
+            case SectionComponent section:
+                return new API.SectionComponent(section);
+
+            case ThumbnailComponent thumbnail:
+                return new API.ThumbnailComponent(thumbnail);
+
+            case MediaGalleryComponent mediaGallery:
+                return new API.MediaGalleryComponent(mediaGallery);
+
+            case SeparatorComponent separator:
+                return new API.SeparatorComponent(separator);
+
+            case FileComponent file:
+                return new API.FileComponent(file);
+
+            case ContainerComponent container:
+                return new API.ContainerComponent(container);
         }
 
         return null;
@@ -85,14 +103,82 @@ internal static IMessageComponent ToEntity(this IMessageComponent component)
                 );
             }
 
+            case ComponentType.TextInput:
+            {
+                var parsed = (API.TextInputComponent)component;
+                return new TextInputComponent(parsed.CustomId,
+                    parsed.Label,
+                    parsed.Placeholder.GetValueOrDefault(null),
+                    parsed.MinLength.ToNullable(),
+                    parsed.MaxLength.ToNullable(),
+                    parsed.Style,
+                    parsed.Required.ToNullable(),
+                    parsed.Value.GetValueOrDefault(null),
+                    parsed.Id.ToNullable());
+            }
+
             case ComponentType.TextDisplay:
             {
                 var parsed = (API.TextDisplayComponent)component;
-                return new TextDisplayComponent(parsed.Id.ToNullable(), parsed.Content);
+                return new TextDisplayComponent(parsed.Content, parsed.Id.ToNullable());
+            }
+
+            case ComponentType.Section:
+            {
+                var parsed = (API.SectionComponent)component;
+                return new SectionComponent(parsed.Id.ToNullable(), parsed.Components.Select(x => x.ToEntity()).ToImmutableArray(), parsed.Accessory.ToModel());
+            }
+
+            case ComponentType.Thumbnail:
+            {
+                var parsed = (API.ThumbnailComponent)component;
+                return new ThumbnailComponent(parsed.Id.ToNullable(), parsed.Media.ToEntity(), parsed.Description.GetValueOrDefault(null), parsed.IsSpoiler.ToNullable());
+            }
+
+            case ComponentType.MediaGallery:
+            {
+                var parsed = (API.MediaGalleryComponent)component;
+
+                return new MediaGalleryComponent(
+                    parsed.Items.Select(x => new MediaGalleryItem(x.Media.ToEntity(), x.Description.GetValueOrDefault(null), x.IsSpoiler.GetValueOrDefault(false))).ToList(),
+                    parsed.Id.ToNullable());
+            }
+
+            case ComponentType.Separator:
+            {
+                var parsed = (API.SeparatorComponent)component;
+                return new SeparatorComponent(parsed.IsDivider.ToNullable(), parsed.Spacing.ToNullable(), parsed.Id.ToNullable());
+            }
+
+            case ComponentType.File:
+            {
+                var parsed = (API.FileComponent)component;
+                return new FileComponent(parsed.File.ToEntity(), parsed.IsSpoiler.ToNullable(), parsed.Id.ToNullable());
+            }
+
+            case ComponentType.Container:
+            {
+                var parsed = (API.ContainerComponent)component;
+                return new ContainerComponent(parsed.Components.Select(x => x.ToEntity()).ToImmutableArray(),
+                    parsed.AccentColor.ToNullable(),
+                    parsed.IsSpoiler.ToNullable(),
+                    parsed.Id.ToNullable());
             }
 
             default:
                 return null;
         }
     }
+
+    internal static UnfurledMediaItem ToEntity(this API.UnfurledMediaItem mediaItem)
+    {
+        return new UnfurledMediaItem(mediaItem.Url);
+    }
+    internal static API.UnfurledMediaItem ToModel(this UnfurledMediaItem mediaItem)
+    {
+        return new API.UnfurledMediaItem
+        {
+            Url = mediaItem.Url
+        };
+    }
 }
diff --git a/src/Discord.Net.Rest/Net/Converters/MessageComponentConverter.cs b/src/Discord.Net.Rest/Net/Converters/MessageComponentConverter.cs
index d88c5a7ad5..0f229f33ef 100644
--- a/src/Discord.Net.Rest/Net/Converters/MessageComponentConverter.cs
+++ b/src/Discord.Net.Rest/Net/Converters/MessageComponentConverter.cs
@@ -1,12 +1,13 @@
 using Newtonsoft.Json;
 using Newtonsoft.Json.Linq;
 using System;
+using System.ComponentModel;
 
 namespace Discord.Net.Converters
 {
     internal class MessageComponentConverter : JsonConverter
     {
-        public static MessageComponentConverter Instance => new MessageComponentConverter();
+        public static MessageComponentConverter Instance => new ();
 
         public override bool CanRead => true;
         public override bool CanWrite => false;
@@ -42,6 +43,24 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
                 case ComponentType.TextDisplay:
                     messageComponent = new API.TextDisplayComponent();
                     break;
+                case ComponentType.Thumbnail:
+                    messageComponent = new API.ThumbnailComponent();
+                    break;
+                case ComponentType.Section:
+                    messageComponent = new API.SectionComponent();
+                    break;
+                case ComponentType.MediaGallery:
+                    messageComponent = new API.MediaGalleryComponent();
+                    break;
+                case ComponentType.Separator:
+                    messageComponent = new API.SeparatorComponent();
+                    break;
+                case ComponentType.File:
+                    messageComponent = new API.FileComponent();
+                    break;
+                case ComponentType.Container:
+                    messageComponent = new API.ContainerComponent();
+                    break;
             }
             serializer.Populate(jsonObject.CreateReader(), messageComponent);
             return messageComponent;

From 93b8f6d16c37ec14d68d4d1e4022586fb9e6390d Mon Sep 17 00:00:00 2001
From: Misha133 <mihagribkov133@gmail.com>
Date: Tue, 4 Feb 2025 21:38:19 +0300
Subject: [PATCH 05/15] commit

---
 .../MessageComponents/Builders/ComponentBuilderV2.cs  | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderV2.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderV2.cs
index 18ec131d83..3c6dfc9c88 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderV2.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderV2.cs
@@ -78,6 +78,17 @@ public ComponentBuilderV2 WithContainer(ContainerComponentBuilder container)
         return this;
     }
 
+    public ComponentBuilderV2 WithButton(ButtonBuilder button)
+    {
+        Components.Add(button);
+        return this;
+    }
+
+    public ComponentBuilderV2 WithSelectMenu(SelectMenuBuilder selectMenu)
+    {
+        Components.Add(selectMenu);
+        return this;
+    }
     public MessageComponent Build()
     {
         return new MessageComponent(Components.Select(x => x.Build()).ToList());

From f92349698298ce3b63fd48ab5253f7789b6391b7 Mon Sep 17 00:00:00 2001
From: Misha133 <mihagribkov133@gmail.com>
Date: Tue, 4 Feb 2025 23:23:22 +0300
Subject: [PATCH 06/15] initial take on `WithX` component builder design

---
 .../Builders/ActionRowBuilder.cs              |  29 ++-
 .../Builders/ButtonBuilder.cs                 |   6 +
 .../Builders/ComponentBuilderV2.cs            |  67 ++-----
 .../Builders/ComponentContainerExtensions.cs  | 170 ++++++++++++++++++
 .../Builders/ContainerComponentBuilder.cs     |  30 +++-
 .../Builders/FileComponentBuilder.cs          |   2 +-
 .../Builders/IComponentContainer.cs           |  14 ++
 .../IInteractableComponentContainer.cs        |   6 +
 .../Builders/IStaticComponentContainer.cs     |   6 +
 .../Builders/MediaGalleryBuilder.cs           |  21 ++-
 .../Builders/SectionBuilder.cs                |  30 ++--
 .../Builders/SelectMenuBuilder.cs             |   6 +
 .../Builders/SeparatorBuilder.cs              |   2 +-
 .../Builders/TextDisplayBuilder.cs            |   2 +-
 .../MessageComponents/ContainerComponent.cs   |   4 +-
 .../API/Common/ContainerComponent.cs          |   4 +-
 16 files changed, 319 insertions(+), 80 deletions(-)
 create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentContainerExtensions.cs
 create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IComponentContainer.cs
 create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IInteractableComponentContainer.cs
 create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IStaticComponentContainer.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
index 3004c8e825..f1780f9bd9 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ActionRowBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ActionRowBuilder.cs
@@ -8,7 +8,7 @@ namespace Discord;
 /// <summary>
 ///     Represents a class used to build Action rows.
 /// </summary>
-public class ActionRowBuilder : IMessageComponentBuilder
+public class ActionRowBuilder : IMessageComponentBuilder, IInteractableComponentContainer
 {
     public ComponentType Type => ComponentType.ActionRow;
 
@@ -41,6 +41,20 @@ public List<IMessageComponentBuilder> Components
         }
     }
 
+
+    public ActionRowBuilder AddComponents(params IEnumerable<IMessageComponentBuilder> components)
+    {
+        foreach (var component in components)
+            AddComponent(component);
+        return this;
+    }
+
+    public ActionRowBuilder WithComponents(IEnumerable<IMessageComponentBuilder> components)
+    {
+        Components = components.ToList();
+        return this;
+    }
+
     private List<IMessageComponentBuilder> _components = new ();
 
     /// <summary>
@@ -165,6 +179,12 @@ public ActionRowBuilder WithButton(ButtonBuilder button)
         return this;
     }
 
+    public ActionRowBuilder WithId(int? id)
+    {
+        Id = id;
+        return this;
+    }
+
     /// <summary>
     ///     Builds the current builder to a <see cref="ActionRowComponent"/> that can be used within a <see cref="ComponentBuilder"/>
     /// </summary>
@@ -196,4 +216,11 @@ internal bool CanTakeComponent(IMessageComponentBuilder component)
                 return false;
         }
     }
+
+
+    IComponentContainer IComponentContainer.AddComponent(IMessageComponentBuilder component) => AddComponent(component);
+
+    IComponentContainer IComponentContainer.AddComponents(params IEnumerable<IMessageComponentBuilder> components) => AddComponents(components);
+
+    IComponentContainer IComponentContainer.WithComponents(IEnumerable<IMessageComponentBuilder> components) => WithComponents(components);
 }
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 e6c56afe99..e16a2258e6 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ButtonBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ButtonBuilder.cs
@@ -262,6 +262,12 @@ public ButtonBuilder WithSkuId(ulong? skuId)
         return this;
     }
 
+    public ButtonBuilder WithId(int? id)
+    {
+        Id = id;
+        return this;
+    }
+
     /// <summary>
     ///     Builds this builder into a <see cref="ButtonComponent"/> to be used in a <see cref="ComponentBuilder"/>.
     /// </summary>
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderV2.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderV2.cs
index 3c6dfc9c88..9bc2905cca 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderV2.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderV2.cs
@@ -4,7 +4,7 @@
 
 namespace Discord;
 
-public class ComponentBuilderV2
+public class ComponentBuilderV2 : IStaticComponentContainer
 {
     public ComponentBuilderV2() {}
 
@@ -25,72 +25,25 @@ public ComponentBuilderV2 AddComponent(IMessageComponentBuilder component)
         return this;
     }
 
-    public ComponentBuilderV2 AddComponent(IMessageComponent component)
+    public ComponentBuilderV2 AddComponents(params IEnumerable<IMessageComponentBuilder> components)
     {
+        foreach (var component in components)
+            Components.Add(component);
         return this;
     }
 
-    public ComponentBuilderV2 WithComponents(List<IMessageComponentBuilder> components)
+    public ComponentBuilderV2 WithComponents(IEnumerable<IMessageComponentBuilder> components)
     {
-        Components = components;
+        Components = components.ToList();
         return this;
     }
 
-    public ComponentBuilderV2 WithActionRow(ActionRowBuilder actionRow)
-    {
-        Components.Add(actionRow);
-        return this;
-    }
-
-    public ComponentBuilderV2 WithTextDisplay(TextDisplayBuilder textDisplayComponent)
-    {
-        Components.Add(textDisplayComponent);
-        return this;
-    }
-
-    public ComponentBuilderV2 WithSection(SectionBuilder sectionComponent)
-    {
-        Components.Add(sectionComponent);
-        return this;
-    }
-
-    public ComponentBuilderV2 WithMediaGallery(MediaGalleryBuilder mediaGallery)
-    {
-        Components.Add(mediaGallery);
-        return this;
-    }
-
-    public ComponentBuilderV2 WithSeparator(SeparatorBuilder separator)
-    {
-        Components.Add(separator);
-        return this;
-    }
-
-    public ComponentBuilderV2 WithFile(FileComponentBuilder file)
-    {
-        Components.Add(file);
-        return this;
-    }
-
-    public ComponentBuilderV2 WithContainer(ContainerComponentBuilder container)
-    {
-        Components.Add(container);
-        return this;
-    }
-
-    public ComponentBuilderV2 WithButton(ButtonBuilder button)
-    {
-        Components.Add(button);
-        return this;
-    }
-
-    public ComponentBuilderV2 WithSelectMenu(SelectMenuBuilder selectMenu)
-    {
-        Components.Add(selectMenu);
-        return this;
-    }
     public MessageComponent Build()
     {
         return new MessageComponent(Components.Select(x => x.Build()).ToList());
     }
+
+    IComponentContainer IComponentContainer.AddComponent(IMessageComponentBuilder component) => AddComponent(component);
+    IComponentContainer IComponentContainer.AddComponents(params IEnumerable<IMessageComponentBuilder> components) => AddComponents(components);
+    IComponentContainer IComponentContainer.WithComponents(IEnumerable<IMessageComponentBuilder> components) => WithComponents(components);
 }
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentContainerExtensions.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentContainerExtensions.cs
new file mode 100644
index 0000000000..44216b7de3
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentContainerExtensions.cs
@@ -0,0 +1,170 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Discord;
+
+public static class ComponentContainerExtensions
+{
+    public static IStaticComponentContainer WithTextDisplay(this IStaticComponentContainer container, TextDisplayBuilder textDisplay)
+    {
+        container.AddComponent(textDisplay);
+        return container;
+    }
+    public static IStaticComponentContainer WithTextDisplay(this IStaticComponentContainer container,
+        string content,
+        int? id = null)
+        => container.WithTextDisplay(new TextDisplayBuilder()
+            .WithContent(content)
+            .WithId(id));
+
+    public static IStaticComponentContainer WithSection(this IStaticComponentContainer container, SectionBuilder section)
+    {
+        container.AddComponent(section);
+        return container;
+    }
+
+    public static IStaticComponentContainer WithSection(this IStaticComponentContainer container,
+        IEnumerable<TextDisplayBuilder> components,
+        IMessageComponentBuilder accessory,
+        bool isSpoiler = false,
+        int? id = null)
+        => container.WithSection(new SectionBuilder()
+            .WithComponents(components)
+            .WithAccessory(accessory)
+            .WithId(id));
+
+    public static IStaticComponentContainer WithMediaGallery(this IStaticComponentContainer container, MediaGalleryBuilder mediaGallery)
+    {
+        container.AddComponent(mediaGallery);
+        return container;
+    }
+
+    public static IStaticComponentContainer WithMediaGallery(this IStaticComponentContainer container,
+        IEnumerable<MediaGalleryItemProperties> items,
+        int? id = null)
+        => container.WithMediaGallery(new MediaGalleryBuilder()
+            .WithItems(items)
+            .WithId(id));
+
+    public static IStaticComponentContainer WithMediaGallery(this IStaticComponentContainer container,
+        IEnumerable<string> urls,
+        int? id = null)
+        => container.WithMediaGallery(new MediaGalleryBuilder()
+            .WithItems(urls.Select(x => new MediaGalleryItemProperties(new UnfurledMediaItemProperties(x))))
+            .WithId(id));
+
+    public static IStaticComponentContainer WithSeparator(this IStaticComponentContainer container, SeparatorBuilder separator)
+    {
+        container.AddComponent(separator);
+        return container;
+    }
+
+    public static IStaticComponentContainer WithSeparator(this IStaticComponentContainer container,
+        SeparatorSpacingSize spacing = SeparatorSpacingSize.Small,
+        bool isDivider = true,
+        int? id = null)
+        => container.WithSeparator(new SeparatorBuilder()
+            .WithSpacing(spacing)
+            .WithIsDivider(isDivider)
+            .WithId(id));
+
+    public static IStaticComponentContainer WithFile(this IStaticComponentContainer container, FileComponentBuilder file)
+    {
+        container.AddComponent(file);
+        return container;
+    }
+
+    public static IStaticComponentContainer WithFile(this IStaticComponentContainer container,
+        string url,
+        bool isSpoiler = false,
+        int? id = null)
+        => container.WithFile(new FileComponentBuilder()
+            .WithFile(new UnfurledMediaItemProperties(url))
+            .WithIsSpoiler(isSpoiler)
+            .WithId(id));
+
+    public static IStaticComponentContainer WithContainer(this IStaticComponentContainer container, ContainerComponentBuilder containerComponent)
+    {
+        container.AddComponent(containerComponent);
+        return container;
+    }
+
+    public static IStaticComponentContainer WithContainer(this IStaticComponentContainer container,
+        IEnumerable<IMessageComponentBuilder> components,
+        Color? accentColor = null,
+        bool isSpoiler = false,
+        int? id = null)
+        => container.WithContainer(new ContainerComponentBuilder()
+            .WithComponents(components)
+            .WithAccentColor(accentColor)
+            .WithSpoiler(isSpoiler)
+            .WithId(id));
+
+    public static IInteractableComponentContainer WithButton(this IInteractableComponentContainer container, ButtonBuilder button)
+    {
+        container.AddComponent(button);
+        return container;
+    }
+
+    public static IInteractableComponentContainer WithButton(this IInteractableComponentContainer container,
+        string label = null,
+        string customId = null,
+        ButtonStyle style = ButtonStyle.Primary,
+        IEmote emote = null,
+        string url = null,
+        bool disabled = false,
+        ulong? skuId = null,
+        int? id = null)
+    => container.WithButton(new ButtonBuilder()
+            .WithLabel(label)
+            .WithStyle(style)
+            .WithEmote(emote)
+            .WithCustomId(customId)
+            .WithUrl(url)
+            .WithDisabled(disabled)
+            .WithSkuId(skuId)
+            .WithId(id));
+
+    public static IInteractableComponentContainer WithSelectMenu(this IInteractableComponentContainer container, SelectMenuBuilder selectMenu)
+    {
+        container.AddComponent(selectMenu);
+        return container;
+    }
+
+    public static IInteractableComponentContainer WithSelectMenu(this IInteractableComponentContainer container,
+        string customId,
+        List<SelectMenuOptionBuilder> 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,
+        int? id = null)
+        => container.WithSelectMenu(new SelectMenuBuilder()
+                .WithCustomId(customId)
+                .WithOptions(options)
+                .WithPlaceholder(placeholder)
+                .WithMaxValues(maxValues)
+                .WithMinValues(minValues)
+                .WithDisabled(disabled)
+                .WithType(type)
+                .WithChannelTypes(channelTypes)
+                .WithDefaultValues(defaultValues)
+                .WithId(id));
+
+    public static IStaticComponentContainer WithActionRow(this IStaticComponentContainer container, ActionRowBuilder actionRow)
+    {
+        container.AddComponent(actionRow);
+        return container;
+    }
+
+    public static IStaticComponentContainer WithActionRow(this IStaticComponentContainer container,
+        IEnumerable<IMessageComponentBuilder> components,
+        int? id = null)
+        => container.WithActionRow(new ActionRowBuilder()
+            .WithComponents(components)
+            .WithId(id));
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ContainerComponentBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ContainerComponentBuilder.cs
index 5eebad2c63..44c18ac6b1 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ContainerComponentBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ContainerComponentBuilder.cs
@@ -1,10 +1,11 @@
 using System;
 using System.Collections.Generic;
 using System.Collections.Immutable;
+using System.Linq;
 
 namespace Discord;
 
-public class ContainerComponentBuilder : IMessageComponentBuilder
+public class ContainerComponentBuilder : IMessageComponentBuilder, IComponentContainer 
 {
     public ComponentType Type => ComponentType.Container;
 
@@ -18,21 +19,26 @@ public List<IMessageComponentBuilder> Components
         set => _components = value ?? throw new ArgumentNullException(nameof(value), $"{nameof(Components)} cannot be null.");
     }
 
-    public int? AccentColor { get; set; }
+    public uint? AccentColor { get; set; }
 
     public bool? IsSpoiler { get; set; }
 
-    public ContainerComponentBuilder WithId(int id)
+    public ContainerComponentBuilder WithId(int? id)
     {
         Id = id;
         return this;
     }
 
-    public ContainerComponentBuilder WithAccentColor(int accentColor)
+    public ContainerComponentBuilder WithAccentColor(uint? accentColor)
     {
         AccentColor = accentColor;
         return this;
     }
+    public ContainerComponentBuilder WithAccentColor(Color? color)
+    {
+        AccentColor = color?.RawValue;
+        return this;
+    }
 
     public ContainerComponentBuilder WithSpoiler(bool isSpoiler)
     {
@@ -46,6 +52,19 @@ public ContainerComponentBuilder AddComponent(IMessageComponentBuilder component
         return this;
     }
 
+    public ContainerComponentBuilder AddComponents(params IEnumerable<IMessageComponentBuilder> components)
+    {
+        foreach (var component in components)
+            Components.Add(component);
+        return this;
+    }
+
+    public ContainerComponentBuilder WithComponents(IEnumerable<IMessageComponentBuilder> components)
+    {
+        Components = components.ToList();
+        return this;
+    }
+
     public ContainerComponentBuilder WithComponents(List<IMessageComponentBuilder> components)
     {
         Components = components;
@@ -58,4 +77,7 @@ public ContainerComponent Build()
     }
 
     IMessageComponent IMessageComponentBuilder.Build() => Build();
+    IComponentContainer IComponentContainer.AddComponent(IMessageComponentBuilder component) => AddComponent(component);
+    IComponentContainer IComponentContainer.AddComponents(params IEnumerable<IMessageComponentBuilder> components) => AddComponents(components);
+    IComponentContainer IComponentContainer.WithComponents(IEnumerable<IMessageComponentBuilder> components) => WithComponents(components);
 }
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/FileComponentBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/FileComponentBuilder.cs
index a40b7e4888..6e7f825ecd 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/FileComponentBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/FileComponentBuilder.cs
@@ -22,7 +22,7 @@ public FileComponentBuilder WithIsSpoiler(bool? isSpoiler)
         return this;
     }
 
-    public FileComponentBuilder WithId(int id)
+    public FileComponentBuilder WithId(int? id)
     {
         Id = id;
         return this;
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IComponentContainer.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IComponentContainer.cs
new file mode 100644
index 0000000000..c8f28045b0
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IComponentContainer.cs
@@ -0,0 +1,14 @@
+using System.Collections.Generic;
+
+namespace Discord;
+
+public interface IComponentContainer
+{
+    List<IMessageComponentBuilder> Components { get; }
+
+    IComponentContainer AddComponent(IMessageComponentBuilder component);
+
+    IComponentContainer AddComponents(params IEnumerable<IMessageComponentBuilder> components);
+
+    IComponentContainer WithComponents(IEnumerable<IMessageComponentBuilder> components);
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IInteractableComponentContainer.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IInteractableComponentContainer.cs
new file mode 100644
index 0000000000..69c310a120
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IInteractableComponentContainer.cs
@@ -0,0 +1,6 @@
+namespace Discord;
+
+public interface IInteractableComponentContainer : IComponentContainer
+{
+    
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IStaticComponentContainer.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IStaticComponentContainer.cs
new file mode 100644
index 0000000000..aed2af1b54
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IStaticComponentContainer.cs
@@ -0,0 +1,6 @@
+namespace Discord;
+
+public interface IStaticComponentContainer : IComponentContainer
+{
+    
+}
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 563d5396e9..5e5d96f272 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/MediaGalleryBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/MediaGalleryBuilder.cs
@@ -24,7 +24,26 @@ public MediaGalleryBuilder AddItem(MediaGalleryItemProperties item)
         return this;
     }
 
-    public MediaGalleryBuilder WithId(int id)
+    public MediaGalleryBuilder AddItem(string url, string description = null, bool isSpoiler = false)
+    {
+        _items.Add(new MediaGalleryItemProperties(new UnfurledMediaItemProperties(url), description, isSpoiler));
+        return this;
+    }
+
+    public MediaGalleryBuilder AddItems(params IEnumerable<MediaGalleryItemProperties> items)
+    {
+        foreach (var item in items)
+            _items.Add(item);
+        return this;
+    }
+
+    public MediaGalleryBuilder WithItems(IEnumerable<MediaGalleryItemProperties> items)
+    {
+        _items = items.ToList();
+        return this;
+    }
+
+    public MediaGalleryBuilder WithId(int? id)
     {
         Id = id;
         return this;
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SectionBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SectionBuilder.cs
index dcc08ffd02..43251fbfda 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SectionBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SectionBuilder.cs
@@ -5,7 +5,7 @@
 
 namespace Discord;
 
-public class SectionBuilder : IMessageComponentBuilder
+public class SectionBuilder : IMessageComponentBuilder, IStaticComponentContainer
 {
     public ComponentType Type => ComponentType.Section;
 
@@ -18,29 +18,36 @@ public List<IMessageComponentBuilder> Components
         set => _components = value ?? throw new ArgumentNullException(nameof(value), $"{nameof(Components)} cannot be null.");
     }
 
-    public IMessageComponentBuilder Accessory { get; set; }
+    public SectionBuilder AddComponent(IMessageComponentBuilder component)
+    {
+        Components.Add(component);
+        return this;
+    }
 
-    public SectionBuilder WithId(int id)
+    public SectionBuilder AddComponents(params IEnumerable<IMessageComponentBuilder> components)
     {
-        Id = id;
+        foreach (var component in components)
+            AddComponent(component);
         return this;
     }
 
-    public SectionBuilder WithAccessory(IMessageComponentBuilder accessory)
+    public SectionBuilder WithComponents(IEnumerable<IMessageComponentBuilder> components)
     {
-        Accessory = accessory;
+        Components = components.ToList();
         return this;
     }
 
-    public SectionBuilder AddComponent(IMessageComponentBuilder component)
+    public IMessageComponentBuilder Accessory { get; set; }
+
+    public SectionBuilder WithId(int? id)
     {
-        Components.Add(component);
+        Id = id;
         return this;
     }
 
-    public SectionBuilder WithComponents(List<IMessageComponentBuilder> components)
+    public SectionBuilder WithAccessory(IMessageComponentBuilder accessory)
     {
-        Components = components;
+        Accessory = accessory;
         return this;
     }
 
@@ -50,4 +57,7 @@ public SectionComponent Build()
     }
 
     IMessageComponent IMessageComponentBuilder.Build() => Build();
+    IComponentContainer IComponentContainer.AddComponent(IMessageComponentBuilder component) => AddComponent(component);
+    IComponentContainer IComponentContainer.AddComponents(params IEnumerable<IMessageComponentBuilder> components) => AddComponents(components);
+    IComponentContainer IComponentContainer.WithComponents(IEnumerable<IMessageComponentBuilder> components) => WithComponents(components.ToList());
 }
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SelectMenuBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SelectMenuBuilder.cs
index 7c1c2b2702..c68e8623a9 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SelectMenuBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SelectMenuBuilder.cs
@@ -397,6 +397,12 @@ public SelectMenuBuilder WithChannelTypes(params ChannelType[] channelTypes)
         return this;
     }
 
+    public SelectMenuBuilder WithId(int? id)
+    {
+        Id = id;
+        return this;
+    }
+
     /// <summary>
     ///     Builds a <see cref="SelectMenuComponent"/>
     /// </summary>
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SeparatorBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SeparatorBuilder.cs
index ba62fe2a0a..085d18a960 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SeparatorBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SeparatorBuilder.cs
@@ -22,7 +22,7 @@ public SeparatorBuilder WithSpacing(SeparatorSpacingSize? spacing)
         return this;
     }
 
-    public SeparatorBuilder WithId(int id)
+    public SeparatorBuilder WithId(int? id)
     {
         Id = id;
         return this;
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextDisplayBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextDisplayBuilder.cs
index 42e331ceb9..1ad3612995 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextDisplayBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextDisplayBuilder.cs
@@ -18,7 +18,7 @@ public TextDisplayBuilder WithContent(string content)
         return this;
     }
 
-    public TextDisplayBuilder WithId(int id)
+    public TextDisplayBuilder WithId(int? id)
     {
         Id = id;
         return this;
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ContainerComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ContainerComponent.cs
index 30fa37d39f..f434a0e021 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ContainerComponent.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ContainerComponent.cs
@@ -10,11 +10,11 @@ public class ContainerComponent : IMessageComponent
 
     public IReadOnlyCollection<IMessageComponent> Components { get; }
 
-    public int? AccentColor { get; }
+    public uint? AccentColor { get; }
 
     public bool? IsSpoiler { get; }
 
-    internal ContainerComponent(IReadOnlyCollection<IMessageComponent> components, int? accentColor, bool? isSpoiler, int? id = null)
+    internal ContainerComponent(IReadOnlyCollection<IMessageComponent> components, uint? accentColor, bool? isSpoiler, int? id = null)
     {
         Components = components;
         AccentColor = accentColor;
diff --git a/src/Discord.Net.Rest/API/Common/ContainerComponent.cs b/src/Discord.Net.Rest/API/Common/ContainerComponent.cs
index 6681c82b33..50bd8b08d2 100644
--- a/src/Discord.Net.Rest/API/Common/ContainerComponent.cs
+++ b/src/Discord.Net.Rest/API/Common/ContainerComponent.cs
@@ -13,7 +13,7 @@ internal class ContainerComponent : IMessageComponent
     public Optional<int> Id { get; set; }
 
     [JsonProperty("accent_color")]
-    public Optional<int> AccentColor { get; set; }
+    public Optional<uint> AccentColor { get; set; }
 
     [JsonProperty("spoiler")]
     public Optional<bool> IsSpoiler { get; set; }
@@ -27,7 +27,7 @@ public ContainerComponent(Discord.ContainerComponent component)
     {
         Type = component.Type;
         Id = component.Id ?? Optional<int>.Unspecified;
-        AccentColor = component.AccentColor ?? Optional<int>.Unspecified;
+        AccentColor = component.AccentColor ?? Optional<uint>.Unspecified;
         IsSpoiler = component.IsSpoiler ?? Optional<bool>.Unspecified;
         Components = component.Components.Select(x => x.ToModel()).ToArray();
     }

From e7eda0698cd165307847fb2b3393466b7cf0b000 Mon Sep 17 00:00:00 2001
From: Misha133 <mihagribkov133@gmail.com>
Date: Tue, 4 Feb 2025 23:36:29 +0300
Subject: [PATCH 07/15] actually usable stuff now

---
 .../Builders/ComponentContainerExtensions.cs  | 61 ++++++++++++-------
 ...omponentBuilder.cs => ContainerBuilder.cs} | 18 +++---
 2 files changed, 49 insertions(+), 30 deletions(-)
 rename src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/{ContainerComponentBuilder.cs => ContainerBuilder.cs} (71%)

diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentContainerExtensions.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentContainerExtensions.cs
index 44216b7de3..cebe2471b6 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentContainerExtensions.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentContainerExtensions.cs
@@ -5,108 +5,122 @@ namespace Discord;
 
 public static class ComponentContainerExtensions
 {
-    public static IStaticComponentContainer WithTextDisplay(this IStaticComponentContainer container, TextDisplayBuilder textDisplay)
+    public static BuilderT WithTextDisplay<BuilderT>(this BuilderT container, TextDisplayBuilder textDisplay)
+        where BuilderT : class, IStaticComponentContainer
     {
         container.AddComponent(textDisplay);
         return container;
     }
-    public static IStaticComponentContainer WithTextDisplay(this IStaticComponentContainer container,
+
+    public static BuilderT WithTextDisplay<BuilderT>(this BuilderT container,
         string content,
         int? id = null)
+        where BuilderT : class, IStaticComponentContainer
         => container.WithTextDisplay(new TextDisplayBuilder()
             .WithContent(content)
             .WithId(id));
 
-    public static IStaticComponentContainer WithSection(this IStaticComponentContainer container, SectionBuilder section)
+    public static BuilderT WithSection<BuilderT>(this BuilderT container, SectionBuilder section)
+        where BuilderT : class, IStaticComponentContainer
     {
         container.AddComponent(section);
         return container;
     }
 
-    public static IStaticComponentContainer WithSection(this IStaticComponentContainer container,
+    public static BuilderT WithSection<BuilderT>(this BuilderT container,
         IEnumerable<TextDisplayBuilder> components,
         IMessageComponentBuilder accessory,
         bool isSpoiler = false,
         int? id = null)
+        where BuilderT : class, IStaticComponentContainer
         => container.WithSection(new SectionBuilder()
             .WithComponents(components)
             .WithAccessory(accessory)
             .WithId(id));
 
-    public static IStaticComponentContainer WithMediaGallery(this IStaticComponentContainer container, MediaGalleryBuilder mediaGallery)
+    public static BuilderT WithMediaGallery<BuilderT>(this BuilderT container, MediaGalleryBuilder mediaGallery)
+        where BuilderT : class, IStaticComponentContainer
     {
         container.AddComponent(mediaGallery);
         return container;
     }
 
-    public static IStaticComponentContainer WithMediaGallery(this IStaticComponentContainer container,
+    public static BuilderT WithMediaGallery<BuilderT>(this BuilderT container,
         IEnumerable<MediaGalleryItemProperties> items,
-        int? id = null)
+        int? id = null) where BuilderT : class, IStaticComponentContainer
         => container.WithMediaGallery(new MediaGalleryBuilder()
             .WithItems(items)
             .WithId(id));
 
-    public static IStaticComponentContainer WithMediaGallery(this IStaticComponentContainer container,
+    public static BuilderT WithMediaGallery<BuilderT>(this BuilderT container,
         IEnumerable<string> urls,
         int? id = null)
+        where BuilderT : class, IStaticComponentContainer
         => container.WithMediaGallery(new MediaGalleryBuilder()
             .WithItems(urls.Select(x => new MediaGalleryItemProperties(new UnfurledMediaItemProperties(x))))
             .WithId(id));
 
-    public static IStaticComponentContainer WithSeparator(this IStaticComponentContainer container, SeparatorBuilder separator)
+    public static BuilderT WithSeparator<BuilderT>(this BuilderT container, SeparatorBuilder separator)
+        where BuilderT : class, IStaticComponentContainer
     {
         container.AddComponent(separator);
         return container;
     }
 
-    public static IStaticComponentContainer WithSeparator(this IStaticComponentContainer container,
+    public static BuilderT WithSeparator<BuilderT>(this BuilderT container,
         SeparatorSpacingSize spacing = SeparatorSpacingSize.Small,
         bool isDivider = true,
         int? id = null)
+        where BuilderT : class, IStaticComponentContainer
         => container.WithSeparator(new SeparatorBuilder()
             .WithSpacing(spacing)
             .WithIsDivider(isDivider)
             .WithId(id));
 
-    public static IStaticComponentContainer WithFile(this IStaticComponentContainer container, FileComponentBuilder file)
+    public static BuilderT WithFile<BuilderT>(this BuilderT container, FileComponentBuilder file)
+        where BuilderT : class, IStaticComponentContainer
     {
         container.AddComponent(file);
         return container;
     }
 
-    public static IStaticComponentContainer WithFile(this IStaticComponentContainer container,
+    public static BuilderT WithFile<BuilderT>(this BuilderT container,
         string url,
         bool isSpoiler = false,
         int? id = null)
+        where BuilderT : class, IStaticComponentContainer
         => container.WithFile(new FileComponentBuilder()
             .WithFile(new UnfurledMediaItemProperties(url))
             .WithIsSpoiler(isSpoiler)
             .WithId(id));
 
-    public static IStaticComponentContainer WithContainer(this IStaticComponentContainer container, ContainerComponentBuilder containerComponent)
+    public static BuilderT WithContainer<BuilderT>(this BuilderT container, ContainerBuilder containerComponent)
+        where BuilderT : class, IStaticComponentContainer
     {
         container.AddComponent(containerComponent);
         return container;
     }
 
-    public static IStaticComponentContainer WithContainer(this IStaticComponentContainer container,
+    public static BuilderT WithContainer<BuilderT>(this BuilderT container,
         IEnumerable<IMessageComponentBuilder> components,
         Color? accentColor = null,
         bool isSpoiler = false,
         int? id = null)
-        => container.WithContainer(new ContainerComponentBuilder()
+        where BuilderT : class, IStaticComponentContainer
+        => container.WithContainer(new ContainerBuilder()
             .WithComponents(components)
             .WithAccentColor(accentColor)
             .WithSpoiler(isSpoiler)
             .WithId(id));
 
-    public static IInteractableComponentContainer WithButton(this IInteractableComponentContainer container, ButtonBuilder button)
+    public static BuilderT WithButton<BuilderT>(this BuilderT container, ButtonBuilder button)
+        where BuilderT : class, IInteractableComponentContainer
     {
         container.AddComponent(button);
         return container;
     }
 
-    public static IInteractableComponentContainer WithButton(this IInteractableComponentContainer container,
+    public static BuilderT WithButton<BuilderT>(this BuilderT container,
         string label = null,
         string customId = null,
         ButtonStyle style = ButtonStyle.Primary,
@@ -115,6 +129,7 @@ public static IInteractableComponentContainer WithButton(this IInteractableCompo
         bool disabled = false,
         ulong? skuId = null,
         int? id = null)
+        where BuilderT : class, IInteractableComponentContainer
     => container.WithButton(new ButtonBuilder()
             .WithLabel(label)
             .WithStyle(style)
@@ -125,13 +140,14 @@ public static IInteractableComponentContainer WithButton(this IInteractableCompo
             .WithSkuId(skuId)
             .WithId(id));
 
-    public static IInteractableComponentContainer WithSelectMenu(this IInteractableComponentContainer container, SelectMenuBuilder selectMenu)
+    public static BuilderT WithSelectMenu<BuilderT>(this BuilderT container, SelectMenuBuilder selectMenu)
+        where BuilderT : class, IInteractableComponentContainer
     {
         container.AddComponent(selectMenu);
         return container;
     }
 
-    public static IInteractableComponentContainer WithSelectMenu(this IInteractableComponentContainer container,
+    public static BuilderT WithSelectMenu<BuilderT>(this BuilderT container,
         string customId,
         List<SelectMenuOptionBuilder> options = null,
         string placeholder = null,
@@ -143,6 +159,7 @@ public static IInteractableComponentContainer WithSelectMenu(this IInteractableC
         ChannelType[] channelTypes = null,
         SelectMenuDefaultValue[] defaultValues = null,
         int? id = null)
+        where BuilderT : class, IInteractableComponentContainer
         => container.WithSelectMenu(new SelectMenuBuilder()
                 .WithCustomId(customId)
                 .WithOptions(options)
@@ -155,15 +172,17 @@ public static IInteractableComponentContainer WithSelectMenu(this IInteractableC
                 .WithDefaultValues(defaultValues)
                 .WithId(id));
 
-    public static IStaticComponentContainer WithActionRow(this IStaticComponentContainer container, ActionRowBuilder actionRow)
+    public static BuilderT WithActionRow<BuilderT>(this BuilderT container, ActionRowBuilder actionRow)
+        where BuilderT : class, IStaticComponentContainer
     {
         container.AddComponent(actionRow);
         return container;
     }
 
-    public static IStaticComponentContainer WithActionRow(this IStaticComponentContainer container,
+    public static BuilderT WithActionRow<BuilderT>(this BuilderT container,
         IEnumerable<IMessageComponentBuilder> components,
         int? id = null)
+        where BuilderT : class, IStaticComponentContainer
         => container.WithActionRow(new ActionRowBuilder()
             .WithComponents(components)
             .WithId(id));
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ContainerComponentBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ContainerBuilder.cs
similarity index 71%
rename from src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ContainerComponentBuilder.cs
rename to src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ContainerBuilder.cs
index 44c18ac6b1..75d0d2ea5f 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ContainerComponentBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ContainerBuilder.cs
@@ -5,7 +5,7 @@
 
 namespace Discord;
 
-public class ContainerComponentBuilder : IMessageComponentBuilder, IComponentContainer 
+public class ContainerBuilder : IMessageComponentBuilder, IStaticComponentContainer 
 {
     public ComponentType Type => ComponentType.Container;
 
@@ -23,49 +23,49 @@ public List<IMessageComponentBuilder> Components
 
     public bool? IsSpoiler { get; set; }
 
-    public ContainerComponentBuilder WithId(int? id)
+    public ContainerBuilder WithId(int? id)
     {
         Id = id;
         return this;
     }
 
-    public ContainerComponentBuilder WithAccentColor(uint? accentColor)
+    public ContainerBuilder WithAccentColor(uint? accentColor)
     {
         AccentColor = accentColor;
         return this;
     }
-    public ContainerComponentBuilder WithAccentColor(Color? color)
+    public ContainerBuilder WithAccentColor(Color? color)
     {
         AccentColor = color?.RawValue;
         return this;
     }
 
-    public ContainerComponentBuilder WithSpoiler(bool isSpoiler)
+    public ContainerBuilder WithSpoiler(bool isSpoiler)
     {
         IsSpoiler = isSpoiler;
         return this;
     }
 
-    public ContainerComponentBuilder AddComponent(IMessageComponentBuilder component)
+    public ContainerBuilder AddComponent(IMessageComponentBuilder component)
     {
         Components.Add(component);
         return this;
     }
 
-    public ContainerComponentBuilder AddComponents(params IEnumerable<IMessageComponentBuilder> components)
+    public ContainerBuilder AddComponents(params IEnumerable<IMessageComponentBuilder> components)
     {
         foreach (var component in components)
             Components.Add(component);
         return this;
     }
 
-    public ContainerComponentBuilder WithComponents(IEnumerable<IMessageComponentBuilder> components)
+    public ContainerBuilder WithComponents(IEnumerable<IMessageComponentBuilder> components)
     {
         Components = components.ToList();
         return this;
     }
 
-    public ContainerComponentBuilder WithComponents(List<IMessageComponentBuilder> components)
+    public ContainerBuilder WithComponents(List<IMessageComponentBuilder> components)
     {
         Components = components;
         return this;

From 4d61ba70551f27663a97d722522e891adeae2c8a Mon Sep 17 00:00:00 2001
From: Misha133 <mihagribkov133@gmail.com>
Date: Tue, 4 Feb 2025 23:54:24 +0300
Subject: [PATCH 08/15] few more changes

---
 .../Builders/ContainerBuilder.cs                 |  5 -----
 .../Builders/ThumbnailBuilder.cs                 | 16 +++++++++++-----
 2 files changed, 11 insertions(+), 10 deletions(-)

diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ContainerBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ContainerBuilder.cs
index 75d0d2ea5f..a7bc9efa8b 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ContainerBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ContainerBuilder.cs
@@ -29,11 +29,6 @@ public ContainerBuilder WithId(int? id)
         return this;
     }
 
-    public ContainerBuilder WithAccentColor(uint? accentColor)
-    {
-        AccentColor = accentColor;
-        return this;
-    }
     public ContainerBuilder WithAccentColor(Color? color)
     {
         AccentColor = color?.RawValue;
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ThumbnailBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ThumbnailBuilder.cs
index 3d6b3d127d..df98bd07b8 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ThumbnailBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ThumbnailBuilder.cs
@@ -1,6 +1,6 @@
 namespace Discord;
 
-public class ThumbnailComponentBuilder : IMessageComponentBuilder
+public class ThumbnailBuilder : IMessageComponentBuilder
 {
     public ComponentType Type => ComponentType.Thumbnail;
 
@@ -12,25 +12,31 @@ public class ThumbnailComponentBuilder : IMessageComponentBuilder
 
     public bool IsSpoiler { get; set; } = false;
 
-    public ThumbnailComponentBuilder WithMedia(UnfurledMediaItemProperties media)
+    public ThumbnailBuilder WithMedia(UnfurledMediaItemProperties media)
     {
         Media = media;
         return this;
     }
 
-    public ThumbnailComponentBuilder WithDescription(string description)
+    public ThumbnailBuilder WithMedia(string url)
+    {
+        Media = new UnfurledMediaItemProperties(url);
+        return this;
+    }
+
+    public ThumbnailBuilder WithDescription(string description)
     {
         Description = description;
         return this;
     }
 
-    public ThumbnailComponentBuilder WithId(int id)
+    public ThumbnailBuilder WithId(int id)
     {
         Id = id;
         return this;
     }
 
-    public ThumbnailComponentBuilder WithSpoiler(bool isSpoiler)
+    public ThumbnailBuilder WithSpoiler(bool isSpoiler)
     {
         IsSpoiler = isSpoiler;
         return this;

From 4433ab2b0c794af2b2125def7ef315eb973b7847 Mon Sep 17 00:00:00 2001
From: Misha133 <mihagribkov133@gmail.com>
Date: Wed, 5 Feb 2025 00:28:20 +0300
Subject: [PATCH 09/15] fixes, changes, syntax sugar

---
 .../Builders/TextDisplayBuilder.cs                 | 14 +++++++++-----
 .../MessageComponents/Builders/ThumbnailBuilder.cs |  9 +++++++++
 .../Builders/UnfurledMediaItemProperties.cs        |  2 ++
 .../Extensions/MessageComponentExtension.cs        |  5 ++++-
 4 files changed, 24 insertions(+), 6 deletions(-)

diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextDisplayBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextDisplayBuilder.cs
index 1ad3612995..484e85d94b 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextDisplayBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextDisplayBuilder.cs
@@ -6,12 +6,16 @@ public class TextDisplayBuilder : IMessageComponentBuilder
 
     public int? Id { get; set; }
 
-    private string _content;
-    public string Content
+    public string Content { get; set; }
+
+    public TextDisplayBuilder() { }
+
+    public TextDisplayBuilder(string content, int? id = null)
     {
-        get => _content;
-        set => _content = value;
+        Content = content;
+        Id = id;
     }
+
     public TextDisplayBuilder WithContent(string content)
     {
         Content = content;
@@ -26,7 +30,7 @@ public TextDisplayBuilder WithId(int? id)
 
     public TextDisplayComponent Build()
     {
-        return new(_content, Id);
+        return new(Content, Id);
     }
 
     IMessageComponent IMessageComponentBuilder.Build() => Build();
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ThumbnailBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ThumbnailBuilder.cs
index df98bd07b8..d6e411411d 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ThumbnailBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ThumbnailBuilder.cs
@@ -12,6 +12,15 @@ public class ThumbnailBuilder : IMessageComponentBuilder
 
     public bool IsSpoiler { get; set; } = false;
 
+    public ThumbnailBuilder() { }
+
+    public ThumbnailBuilder(UnfurledMediaItemProperties media, string description = null, bool isSpoiler = false)
+    {
+        Media = media;
+        Description = description;
+        IsSpoiler = isSpoiler;
+    }
+
     public ThumbnailBuilder WithMedia(UnfurledMediaItemProperties media)
     {
         Media = media;
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/UnfurledMediaItemProperties.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/UnfurledMediaItemProperties.cs
index e8babb78da..c9d2b85d30 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/UnfurledMediaItemProperties.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/UnfurledMediaItemProperties.cs
@@ -9,4 +9,6 @@ public UnfurledMediaItemProperties(string url)
     {
         Url = url;
     }
+
+    public static implicit operator UnfurledMediaItemProperties(string url) => new(url);
 }
diff --git a/src/Discord.Net.Rest/Extensions/MessageComponentExtension.cs b/src/Discord.Net.Rest/Extensions/MessageComponentExtension.cs
index 19d986eaa5..ad409bf64f 100644
--- a/src/Discord.Net.Rest/Extensions/MessageComponentExtension.cs
+++ b/src/Discord.Net.Rest/Extensions/MessageComponentExtension.cs
@@ -9,6 +9,9 @@ internal static IMessageComponent ToModel(this IMessageComponent component)
     {
         switch (component)
         {
+            case ActionRowComponent actionRow:
+                return new API.ActionRowComponent(actionRow);
+
             case ButtonComponent btn:
                 return new API.ButtonComponent(btn);
 
@@ -50,7 +53,7 @@ internal static IMessageComponent ToEntity(this IMessageComponent component)
             case ComponentType.ActionRow:
             {
                 var parsed = (API.ActionRowComponent)component;
-                return new ActionRowComponent()
+                return new ActionRowComponent
                 {
                     Id = component.Id,
                     Components = parsed.Components.Select(x => x.ToEntity()).ToImmutableArray()

From de10572374227116b88adf0917a15df6c6faa7a2 Mon Sep 17 00:00:00 2001
From: Misha133 <mihagribkov133@gmail.com>
Date: Wed, 5 Feb 2025 17:43:03 +0300
Subject: [PATCH 10/15] rework message flags + auto add `ComponentsV2`

---
 .../Builders/ComponentContainerExtensions.cs  |  4 +-
 .../Builders/FileComponentBuilder.cs          |  9 ++++
 .../Builders/MediaGalleryBuilder.cs           |  8 +++
 .../Builders/ThumbnailBuilder.cs              |  2 +-
 src/Discord.Net.Core/Utils/Preconditions.cs   |  6 +++
 .../API/Rest/CreateWebhookMessageParams.cs    | 13 ++++-
 .../API/Rest/UploadInteractionFileParams.cs   | 18 +++++--
 .../API/Rest/UploadWebhookFileParams.cs       | 18 +++++--
 .../Entities/Channels/ChannelHelper.cs        | 12 +++--
 .../SocketMessageComponent.cs                 | 52 +++++++++++-------
 .../Interaction/Modals/SocketModal.cs         | 53 +++++++++++--------
 .../SocketBaseCommand/SocketCommandBase.cs    | 52 +++++++++++-------
 12 files changed, 166 insertions(+), 81 deletions(-)

diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentContainerExtensions.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentContainerExtensions.cs
index cebe2471b6..797d4b4ece 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentContainerExtensions.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentContainerExtensions.cs
@@ -85,12 +85,12 @@ public static BuilderT WithFile<BuilderT>(this BuilderT container, FileComponent
     }
 
     public static BuilderT WithFile<BuilderT>(this BuilderT container,
-        string url,
+        UnfurledMediaItemProperties file,
         bool isSpoiler = false,
         int? id = null)
         where BuilderT : class, IStaticComponentContainer
         => container.WithFile(new FileComponentBuilder()
-            .WithFile(new UnfurledMediaItemProperties(url))
+            .WithFile(file)
             .WithIsSpoiler(isSpoiler)
             .WithId(id));
 
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/FileComponentBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/FileComponentBuilder.cs
index 6e7f825ecd..0322423ad2 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/FileComponentBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/FileComponentBuilder.cs
@@ -10,6 +10,15 @@ public class FileComponentBuilder : IMessageComponentBuilder
 
     public bool? IsSpoiler { get; set; }
 
+    public FileComponentBuilder() {}
+
+    public FileComponentBuilder(UnfurledMediaItemProperties media, bool isSpoiler = false, int? id = null)
+    {
+        File = media;
+        Id = id;
+        IsSpoiler = isSpoiler;
+    }
+
     public FileComponentBuilder WithFile(UnfurledMediaItemProperties file)
     {
         File = file;
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 5e5d96f272..509cbe1387 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/MediaGalleryBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/MediaGalleryBuilder.cs
@@ -12,6 +12,14 @@ public class MediaGalleryBuilder : IMessageComponentBuilder
 
     private List<MediaGalleryItemProperties> _items = new();
 
+    public MediaGalleryBuilder() { }
+
+    public MediaGalleryBuilder(IEnumerable<MediaGalleryItemProperties> items, int? id = null)
+    {
+        Items = items.ToList();
+        Id = id;
+    }
+
     public List<MediaGalleryItemProperties> Items
     {
         get => _items;
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ThumbnailBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ThumbnailBuilder.cs
index d6e411411d..b4da4bcfc4 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ThumbnailBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ThumbnailBuilder.cs
@@ -10,7 +10,7 @@ public class ThumbnailBuilder : IMessageComponentBuilder
 
     public string Description { get; set; }
 
-    public bool IsSpoiler { get; set; } = false;
+    public bool IsSpoiler { get; set; }
 
     public ThumbnailBuilder() { }
 
diff --git a/src/Discord.Net.Core/Utils/Preconditions.cs b/src/Discord.Net.Core/Utils/Preconditions.cs
index 83da3a66ad..372238bf77 100644
--- a/src/Discord.Net.Core/Utils/Preconditions.cs
+++ b/src/Discord.Net.Core/Utils/Preconditions.cs
@@ -401,5 +401,11 @@ public static void Options(string name, string description)
         }
 
         #endregion
+
+        public static void ValidateMessageFlags(MessageFlags flags)
+        {
+            if (flags is not MessageFlags.None and not MessageFlags.SuppressEmbeds and not MessageFlags.SuppressNotification and not MessageFlags.ComponentsV2 and not MessageFlags.Ephemeral)
+                throw new ArgumentException("The only valid MessageFlags are Ephemeral, SuppressEmbeds, SuppressNotification, ComponentsV2 and None.", nameof(flags));
+        }
     }
 }
diff --git a/src/Discord.Net.Rest/API/Rest/CreateWebhookMessageParams.cs b/src/Discord.Net.Rest/API/Rest/CreateWebhookMessageParams.cs
index 0258425c7b..e3b10738bf 100644
--- a/src/Discord.Net.Rest/API/Rest/CreateWebhookMessageParams.cs
+++ b/src/Discord.Net.Rest/API/Rest/CreateWebhookMessageParams.cs
@@ -3,6 +3,7 @@
 using Newtonsoft.Json;
 using System.Collections.Generic;
 using System.IO;
+using System.Linq;
 using System.Text;
 
 namespace Discord.API.Rest
@@ -55,6 +56,7 @@ public IReadOnlyDictionary<string, object> ToDictionary()
         {
             var d = new Dictionary<string, object>();
 
+            var extraFlags = MessageFlags.None;
             if (File.IsSpecified)
             {
                 d["file"] = File.Value;
@@ -77,14 +79,21 @@ public IReadOnlyDictionary<string, object> ToDictionary()
                 payload["embeds"] = Embeds.Value;
             if (AllowedMentions.IsSpecified)
                 payload["allowed_mentions"] = AllowedMentions.Value;
+
+
             if (Components.IsSpecified)
+            {
                 payload["components"] = Components.Value;
+                if (Components.Value.Any(x => x.Type is not ComponentType.ActionRow))
+                    extraFlags |= MessageFlags.ComponentsV2;
+            }
+
+            payload["flags"] = Flags.GetValueOrDefault(MessageFlags.None) | extraFlags;
+
             if (ThreadName.IsSpecified)
                 payload["thread_name"] = ThreadName.Value;
             if (AppliedTags.IsSpecified)
                 payload["applied_tags"] = AppliedTags.Value;
-            if (Flags.IsSpecified)
-                payload["flags"] = Flags.Value;
             if (Poll.IsSpecified)
                 payload["poll"] = Poll.Value;
 
diff --git a/src/Discord.Net.Rest/API/Rest/UploadInteractionFileParams.cs b/src/Discord.Net.Rest/API/Rest/UploadInteractionFileParams.cs
index 26697ecbe9..0497495dfa 100644
--- a/src/Discord.Net.Rest/API/Rest/UploadInteractionFileParams.cs
+++ b/src/Discord.Net.Rest/API/Rest/UploadInteractionFileParams.cs
@@ -44,8 +44,10 @@ public IReadOnlyDictionary<string, object> ToDictionary()
         {
             var d = new Dictionary<string, object>();
 
+            var extraFlags = MessageFlags.None;
+
             if (Files.Any(x => x.Waveform is not null && x.DurationSeconds is not null))
-                Flags = Flags.GetValueOrDefault(MessageFlags.None) | MessageFlags.VoiceMessage;
+                extraFlags |= MessageFlags.VoiceMessage;
 
             var payload = new Dictionary<string, object>();
             payload["type"] = Type;
@@ -55,14 +57,20 @@ public IReadOnlyDictionary<string, object> ToDictionary()
                 data["content"] = Content.Value;
             if (IsTTS.IsSpecified)
                 data["tts"] = IsTTS.Value;
-            if (MessageComponents.IsSpecified)
-                data["components"] = MessageComponents.Value;
             if (Embeds.IsSpecified)
                 data["embeds"] = Embeds.Value;
             if (AllowedMentions.IsSpecified)
                 data["allowed_mentions"] = AllowedMentions.Value;
-            if (Flags.IsSpecified)
-                data["flags"] = Flags.Value;
+
+            if (MessageComponents.IsSpecified)
+            {
+                data["components"] = MessageComponents.Value;
+                if (MessageComponents.Value.Any(x => x.Type is not ComponentType.ActionRow))
+                    extraFlags |= MessageFlags.ComponentsV2;
+            }
+
+            data["flags"] = Flags.GetValueOrDefault(MessageFlags.None) | extraFlags;
+
             if (Poll.IsSpecified)
                 data["poll"] = Poll.Value;
 
diff --git a/src/Discord.Net.Rest/API/Rest/UploadWebhookFileParams.cs b/src/Discord.Net.Rest/API/Rest/UploadWebhookFileParams.cs
index 6c404475cc..4da38e9dab 100644
--- a/src/Discord.Net.Rest/API/Rest/UploadWebhookFileParams.cs
+++ b/src/Discord.Net.Rest/API/Rest/UploadWebhookFileParams.cs
@@ -37,8 +37,10 @@ public IReadOnlyDictionary<string, object> ToDictionary()
         {
             var d = new Dictionary<string, object>();
 
+            var extraFlags = MessageFlags.None;
+
             if (Files.Any(x => x.Waveform is not null && x.DurationSeconds is not null))
-                Flags = Flags.GetValueOrDefault(MessageFlags.None) | MessageFlags.VoiceMessage;
+                extraFlags |= MessageFlags.VoiceMessage;
 
             var payload = new Dictionary<string, object>();
             if (Content.IsSpecified)
@@ -51,14 +53,20 @@ public IReadOnlyDictionary<string, object> ToDictionary()
                 payload["username"] = Username.Value;
             if (AvatarUrl.IsSpecified)
                 payload["avatar_url"] = AvatarUrl.Value;
-            if (MessageComponents.IsSpecified)
-                payload["components"] = MessageComponents.Value;
             if (Embeds.IsSpecified)
                 payload["embeds"] = Embeds.Value;
             if (AllowedMentions.IsSpecified)
                 payload["allowed_mentions"] = AllowedMentions.Value;
-            if (Flags.IsSpecified)
-                payload["flags"] = Flags.Value;
+
+            if (MessageComponents.IsSpecified)
+            {
+                payload["components"] = MessageComponents.Value;
+                if (MessageComponents.Value.Any(x => x.Type is not ComponentType.ActionRow))
+                    extraFlags |= MessageFlags.ComponentsV2;
+            }
+
+            payload["flags"] = Flags.GetValueOrDefault(MessageFlags.None) | extraFlags;
+
             if (ThreadName.IsSpecified)
                 payload["thread_name"] = ThreadName.Value;
             if (AppliedTags.IsSpecified)
diff --git a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
index 0c8e2395bb..9229bc73f5 100644
--- a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
+++ b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
@@ -308,10 +308,10 @@ public static async Task<RestUserMessage> SendMessageAsync(IMessageChannel chann
                 Preconditions.AtMost(stickers.Length, 3, nameof(stickers), "A max of 3 stickers are allowed.");
             }
 
-            if (flags is not MessageFlags.None and not MessageFlags.SuppressEmbeds and not MessageFlags.SuppressNotification)
-                throw new ArgumentException("The only valid MessageFlags are SuppressEmbeds, SuppressNotification and none.", nameof(flags));
+            if (components?.Components?.Any(x => x.Type != ComponentType.ActionRow) ?? false)
+                flags |= MessageFlags.ComponentsV2;
 
-            
+            Preconditions.ValidateMessageFlags(flags);
 
             var args = new CreateMessageParams
             {
@@ -434,8 +434,10 @@ public static async Task<RestUserMessage> SendFilesAsync(IMessageChannel channel
                 }
             }
 
-            if (flags is not MessageFlags.None and not MessageFlags.SuppressEmbeds and not MessageFlags.SuppressNotification)
-                throw new ArgumentException("The only valid MessageFlags are SuppressEmbeds, SuppressNotification and none.", nameof(flags));
+            if (components?.Components?.Any(x => x.Type != ComponentType.ActionRow) ?? false)
+                flags |= MessageFlags.ComponentsV2;
+
+            Preconditions.ValidateMessageFlags(flags);
 
             if (stickers != null)
             {
diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/MessageComponents/SocketMessageComponent.cs b/src/Discord.Net.WebSocket/Entities/Interaction/MessageComponents/SocketMessageComponent.cs
index 16bc37722f..984c898454 100644
--- a/src/Discord.Net.WebSocket/Entities/Interaction/MessageComponents/SocketMessageComponent.cs
+++ b/src/Discord.Net.WebSocket/Entities/Interaction/MessageComponents/SocketMessageComponent.cs
@@ -116,6 +116,13 @@ public override async Task RespondWithFilesAsync(
                 }
             }
 
+            if (components?.Components?.Any(x => x.Type != ComponentType.ActionRow) ?? false)
+                flags |= MessageFlags.ComponentsV2;
+            if (ephemeral)
+                flags |= MessageFlags.Ephemeral;
+
+            Preconditions.ValidateMessageFlags(flags);
+
             var response = new API.Rest.UploadInteractionFileParams(attachments?.ToArray())
             {
                 Type = InteractionResponseType.ChannelMessageWithSource,
@@ -124,11 +131,7 @@ public override async Task RespondWithFilesAsync(
                 Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified,
                 IsTTS = isTTS,
                 MessageComponents = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
-                Flags = ephemeral
-                    ? flags | MessageFlags.Ephemeral
-                    : flags == MessageFlags.None
-                        ? Optional<MessageFlags>.Unspecified
-                        : flags,
+                Flags = flags,
                 Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
             };
 
@@ -188,6 +191,13 @@ public override async Task RespondAsync(
                 }
             }
 
+            if (components?.Components?.Any(x => x.Type != ComponentType.ActionRow) ?? false)
+                flags |= MessageFlags.ComponentsV2;
+            if (ephemeral)
+                flags |= MessageFlags.Ephemeral;
+
+            Preconditions.ValidateMessageFlags(flags);
+
             var response = new API.InteractionResponse
             {
                 Type = InteractionResponseType.ChannelMessageWithSource,
@@ -197,11 +207,7 @@ public override async Task RespondAsync(
                     AllowedMentions = allowedMentions?.ToModel(),
                     Embeds = embeds.Select(x => x.ToModel()).ToArray(),
                     TTS = isTTS,
-                    Flags = ephemeral
-                        ? flags | MessageFlags.Ephemeral
-                        : flags == MessageFlags.None
-                            ? Optional<MessageFlags>.Unspecified
-                            : flags,
+                    Flags = flags,
                     Components = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
                     Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
                 }
@@ -346,6 +352,14 @@ public override Task<RestFollowupMessage> FollowupAsync(
             Preconditions.AtMost(embeds.Length, 10, nameof(embeds), "A max of 10 embeds are allowed.");
             Preconditions.ValidatePoll(poll);
 
+            if (components?.Components?.Any(x => x.Type != ComponentType.ActionRow) ?? false)
+                flags |= MessageFlags.ComponentsV2;
+            if (ephemeral)
+                flags |= MessageFlags.Ephemeral;
+
+            Preconditions.ValidateMessageFlags(flags);
+                
+
             var args = new API.Rest.CreateWebhookMessageParams
             {
                 Content = text,
@@ -354,11 +368,7 @@ public override Task<RestFollowupMessage> FollowupAsync(
                 Embeds = embeds.Select(x => x.ToModel()).ToArray(),
                 Components = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
                 Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified,
-                Flags = ephemeral
-                    ? flags | MessageFlags.Ephemeral
-                    : flags == MessageFlags.None
-                        ? Optional<MessageFlags>.Unspecified
-                        : flags,
+                Flags = flags,
             };
 
             return InteractionHelper.SendFollowupAsync(Discord.Rest, args, Token, Channel, options);
@@ -410,14 +420,16 @@ public override Task<RestFollowupMessage> FollowupWithFilesAsync(
                     throw new ArgumentException("The Roles flag is mutually exclusive with the list of Role Ids.", nameof(allowedMentions));
                 }
             }
+            if (components?.Components?.Any(x => x.Type != ComponentType.ActionRow) ?? false)
+                flags |= MessageFlags.ComponentsV2;
+            if (ephemeral)
+                flags |= MessageFlags.Ephemeral;
+
+            Preconditions.ValidateMessageFlags(flags);
 
             var args = new API.Rest.UploadWebhookFileParams(attachments.ToArray())
             {
-                Flags = ephemeral
-                    ? flags | MessageFlags.Ephemeral
-                    : flags == MessageFlags.None
-                        ? Optional<MessageFlags>.Unspecified
-                        : flags,
+                Flags = flags,
                 Content = text,
                 IsTTS = isTTS,
                 Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified,
diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/Modals/SocketModal.cs b/src/Discord.Net.WebSocket/Entities/Interaction/Modals/SocketModal.cs
index 90c85a260e..a6de28d332 100644
--- a/src/Discord.Net.WebSocket/Entities/Interaction/Modals/SocketModal.cs
+++ b/src/Discord.Net.WebSocket/Entities/Interaction/Modals/SocketModal.cs
@@ -112,6 +112,12 @@ public override async Task RespondWithFilesAsync(
                     throw new ArgumentException("The Roles flag is mutually exclusive with the list of Role Ids.", nameof(allowedMentions));
                 }
             }
+            if (components?.Components?.Any(x => x.Type != ComponentType.ActionRow) ?? false)
+                flags |= MessageFlags.ComponentsV2;
+            if (ephemeral)
+                flags |= MessageFlags.Ephemeral;
+
+            Preconditions.ValidateMessageFlags(flags);
 
             var response = new API.Rest.UploadInteractionFileParams(attachments?.ToArray())
             {
@@ -121,11 +127,7 @@ public override async Task RespondWithFilesAsync(
                 Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified,
                 IsTTS = isTTS,
                 MessageComponents = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
-                Flags = ephemeral
-                    ? flags | MessageFlags.Ephemeral
-                    : flags == MessageFlags.None
-                        ? Optional<MessageFlags>.Unspecified
-                        : flags,
+                Flags = flags,
                 Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
             };
 
@@ -141,7 +143,7 @@ public override async Task RespondWithFilesAsync(
             HasResponded = true;
         }
 
-        /// <inheritdoc/>
+        /// <inheritdoc cref="IDiscordInteraction.RespondAsync"/>
         public override async Task RespondAsync(
             string text = null,
             Embed[] embeds = null,
@@ -185,6 +187,13 @@ public override async Task RespondAsync(
                 }
             }
 
+            if (components?.Components?.Any(x => x.Type != ComponentType.ActionRow) ?? false)
+                flags |= MessageFlags.ComponentsV2;
+            if (ephemeral)
+                flags |= MessageFlags.Ephemeral;
+
+            Preconditions.ValidateMessageFlags(flags);
+
             var response = new API.InteractionResponse
             {
                 Type = InteractionResponseType.ChannelMessageWithSource,
@@ -194,11 +203,7 @@ public override async Task RespondAsync(
                     AllowedMentions = allowedMentions?.ToModel(),
                     Embeds = embeds.Select(x => x.ToModel()).ToArray(),
                     TTS = isTTS,
-                    Flags = ephemeral
-                        ? flags | MessageFlags.Ephemeral
-                        : flags == MessageFlags.None
-                            ? Optional<MessageFlags>.Unspecified
-                            : flags,
+                    Flags = flags,
                     Components = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
                     Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
                 }
@@ -345,6 +350,13 @@ public override Task<RestFollowupMessage> FollowupAsync(
             Preconditions.AtMost(embeds.Length, 10, nameof(embeds), "A max of 10 embeds are allowed.");
             Preconditions.ValidatePoll(poll);
 
+            if (components?.Components?.Any(x => x.Type != ComponentType.ActionRow) ?? false)
+                flags |= MessageFlags.ComponentsV2;
+            if (ephemeral)
+                flags |= MessageFlags.Ephemeral;
+
+            Preconditions.ValidateMessageFlags(flags);
+
             var args = new API.Rest.CreateWebhookMessageParams
             {
                 Content = text,
@@ -352,11 +364,7 @@ public override Task<RestFollowupMessage> FollowupAsync(
                 IsTTS = isTTS,
                 Embeds = embeds.Select(x => x.ToModel()).ToArray(),
                 Components = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
-                Flags = ephemeral
-                    ? flags | MessageFlags.Ephemeral
-                    : flags == MessageFlags.None
-                        ? Optional<MessageFlags>.Unspecified
-                        : flags,
+                Flags = flags,
                 Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
             };
 
@@ -410,13 +418,16 @@ public override Task<RestFollowupMessage> FollowupWithFilesAsync(
                 }
             }
 
+            if (components?.Components?.Any(x => x.Type != ComponentType.ActionRow) ?? false)
+                flags |= MessageFlags.ComponentsV2;
+            if (ephemeral)
+                flags |= MessageFlags.Ephemeral;
+
+            Preconditions.ValidateMessageFlags(flags);
+
             var args = new API.Rest.UploadWebhookFileParams(attachments.ToArray())
             {
-                Flags = ephemeral
-                    ? flags | MessageFlags.Ephemeral
-                    : flags == MessageFlags.None
-                        ? Optional<MessageFlags>.Unspecified
-                        : flags,
+                Flags = flags,
                 Content = text,
                 IsTTS = isTTS,
                 Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified,
diff --git a/src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketCommandBase.cs b/src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketCommandBase.cs
index 2d8dcf0509..075e4e31fa 100644
--- a/src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketCommandBase.cs
+++ b/src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketCommandBase.cs
@@ -111,6 +111,13 @@ public override async Task RespondAsync(
                 }
             }
 
+            if (components?.Components?.Any(x => x.Type != ComponentType.ActionRow) ?? false)
+                flags |= MessageFlags.ComponentsV2;
+            if (ephemeral)
+                flags |= MessageFlags.Ephemeral;
+
+            Preconditions.ValidateMessageFlags(flags);
+
             var response = new API.InteractionResponse
             {
                 Type = InteractionResponseType.ChannelMessageWithSource,
@@ -121,11 +128,7 @@ public override async Task RespondAsync(
                     Embeds = embeds.Select(x => x.ToModel()).ToArray(),
                     TTS = isTTS,
                     Components = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
-                    Flags = ephemeral
-                        ? flags | MessageFlags.Ephemeral
-                        : flags == MessageFlags.None
-                            ? Optional<MessageFlags>.Unspecified
-                            : flags,
+                    Flags = flags,
                     Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
                 }
             };
@@ -222,6 +225,13 @@ public override async Task RespondWithFilesAsync(
                 }
             }
 
+            if (components?.Components?.Any(x => x.Type != ComponentType.ActionRow) ?? false)
+                flags |= MessageFlags.ComponentsV2;
+            if (ephemeral)
+                flags |= MessageFlags.Ephemeral;
+
+            Preconditions.ValidateMessageFlags(flags);
+
             var response = new API.Rest.UploadInteractionFileParams(attachments?.ToArray())
             {
                 Type = InteractionResponseType.ChannelMessageWithSource,
@@ -229,11 +239,7 @@ public override async Task RespondWithFilesAsync(
                 AllowedMentions = allowedMentions != null ? allowedMentions?.ToModel() : Optional<API.AllowedMentions>.Unspecified,
                 Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified,
                 IsTTS = isTTS,
-                Flags = ephemeral
-                    ? flags | MessageFlags.Ephemeral
-                    : flags == MessageFlags.None
-                        ? Optional<MessageFlags>.Unspecified
-                        : flags,
+                Flags = flags,
                 MessageComponents = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
                 Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
             };
@@ -275,6 +281,13 @@ public override Task<RestFollowupMessage> FollowupAsync(
             Preconditions.AtMost(embeds.Length, 10, nameof(embeds), "A max of 10 embeds are allowed.");
             Preconditions.ValidatePoll(poll);
 
+            if (components?.Components?.Any(x => x.Type != ComponentType.ActionRow) ?? false)
+                flags |= MessageFlags.ComponentsV2;
+            if (ephemeral)
+                flags |= MessageFlags.Ephemeral;
+
+            Preconditions.ValidateMessageFlags(flags);
+
             var args = new API.Rest.CreateWebhookMessageParams
             {
                 Content = text ?? Optional<string>.Unspecified,
@@ -283,11 +296,7 @@ public override Task<RestFollowupMessage> FollowupAsync(
                 Embeds = embeds.Select(x => x.ToModel()).ToArray(),
                 Components = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
                 Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified,
-                Flags = ephemeral
-                    ? flags | MessageFlags.Ephemeral
-                    : flags == MessageFlags.None
-                        ? Optional<MessageFlags>.Unspecified
-                        : flags,
+                Flags = flags,
             };
 
             return InteractionHelper.SendFollowupAsync(Discord.Rest, args, Token, Channel, options);
@@ -340,13 +349,16 @@ public override Task<RestFollowupMessage> FollowupWithFilesAsync(
                 }
             }
 
+            if (components?.Components?.Any(x => x.Type != ComponentType.ActionRow) ?? false)
+                flags |= MessageFlags.ComponentsV2;
+            if (ephemeral)
+                flags |= MessageFlags.Ephemeral;
+
+            Preconditions.ValidateMessageFlags(flags);
+
             var args = new API.Rest.UploadWebhookFileParams(attachments.ToArray())
             {
-                Flags = ephemeral
-                    ? flags | MessageFlags.Ephemeral
-                    : flags == MessageFlags.None
-                        ? Optional<MessageFlags>.Unspecified
-                        : flags,
+                Flags = flags,
                 Content = text,
                 IsTTS = isTTS,
                 Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified,

From 31c9c5d28c0407df8cb13eaffe62809af7e3397b Mon Sep 17 00:00:00 2001
From: Misha133 <mihagribkov133@gmail.com>
Date: Wed, 5 Feb 2025 23:13:41 +0300
Subject: [PATCH 11/15] apparently it's also nullable

---
 src/Discord.Net.Rest/API/Common/ContainerComponent.cs        | 4 ++--
 src/Discord.Net.Rest/Extensions/MessageComponentExtension.cs | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/Discord.Net.Rest/API/Common/ContainerComponent.cs b/src/Discord.Net.Rest/API/Common/ContainerComponent.cs
index 50bd8b08d2..969906bcb0 100644
--- a/src/Discord.Net.Rest/API/Common/ContainerComponent.cs
+++ b/src/Discord.Net.Rest/API/Common/ContainerComponent.cs
@@ -13,7 +13,7 @@ internal class ContainerComponent : IMessageComponent
     public Optional<int> Id { get; set; }
 
     [JsonProperty("accent_color")]
-    public Optional<uint> AccentColor { get; set; }
+    public Optional<uint?> AccentColor { get; set; }
 
     [JsonProperty("spoiler")]
     public Optional<bool> IsSpoiler { get; set; }
@@ -27,7 +27,7 @@ public ContainerComponent(Discord.ContainerComponent component)
     {
         Type = component.Type;
         Id = component.Id ?? Optional<int>.Unspecified;
-        AccentColor = component.AccentColor ?? Optional<uint>.Unspecified;
+        AccentColor = component.AccentColor ?? Optional<uint?>.Unspecified;
         IsSpoiler = component.IsSpoiler ?? Optional<bool>.Unspecified;
         Components = component.Components.Select(x => x.ToModel()).ToArray();
     }
diff --git a/src/Discord.Net.Rest/Extensions/MessageComponentExtension.cs b/src/Discord.Net.Rest/Extensions/MessageComponentExtension.cs
index ad409bf64f..8918c9bffc 100644
--- a/src/Discord.Net.Rest/Extensions/MessageComponentExtension.cs
+++ b/src/Discord.Net.Rest/Extensions/MessageComponentExtension.cs
@@ -163,7 +163,7 @@ internal static IMessageComponent ToEntity(this IMessageComponent component)
             {
                 var parsed = (API.ContainerComponent)component;
                 return new ContainerComponent(parsed.Components.Select(x => x.ToEntity()).ToImmutableArray(),
-                    parsed.AccentColor.ToNullable(),
+                    parsed.AccentColor.GetValueOrDefault(null),
                     parsed.IsSpoiler.ToNullable(),
                     parsed.Id.ToNullable());
             }

From 2313a34c44285b7f6fa24a55a481e06014f3bae9 Mon Sep 17 00:00:00 2001
From: Misha133 <mihagribkov133@gmail.com>
Date: Sun, 9 Feb 2025 21:38:29 +0300
Subject: [PATCH 12/15] fix build

---
 .../MessageComponents/Builders/ComponentBuilderV2.cs        | 2 +-
 .../Interactions/MessageComponents/FileComponent.cs         | 4 ++--
 .../Entities/Interactions/Modals/ModalBuilder.cs            | 6 +++---
 3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderV2.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderV2.cs
index 9bc2905cca..d961ba4fd7 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderV2.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderV2.cs
@@ -4,7 +4,7 @@
 
 namespace Discord;
 
-public class ComponentBuilderV2 : IStaticComponentContainer
+public class ComponentBuilderV2 : IStaticComponentContainer, IInteractableComponentContainer
 {
     public ComponentBuilderV2() {}
 
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/FileComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/FileComponent.cs
index 40b89ba6e7..c1e6303105 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/FileComponent.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/FileComponent.cs
@@ -4,10 +4,10 @@ public class FileComponent : IMessageComponent
 {
     public ComponentType Type => ComponentType.File;
 
-    public UnfurledMediaItem File { get; }
-
     public int? Id { get; }
 
+    public UnfurledMediaItem File { get; }
+
     public bool? IsSpoiler { get; }
 
     internal FileComponent(UnfurledMediaItem file, bool? isSpoiler, int? id = null)
diff --git a/src/Discord.Net.Core/Entities/Interactions/Modals/ModalBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/Modals/ModalBuilder.cs
index 6e04e95d9e..0b5c9bb47a 100644
--- a/src/Discord.Net.Core/Entities/Interactions/Modals/ModalBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/Modals/ModalBuilder.cs
@@ -178,13 +178,13 @@ public ModalBuilder UpdateTextInput(string customId, object value)
         /// <summary>
         ///     Removes a component from this builder by the specified <paramref name="customId"/>.
         /// </summary>
-        /// <param name="customId">The <see cref="IMessageComponent.CustomId"/> of the component to remove.</param>
+        /// <param name="customId">The <see cref="IInteractableComponent.CustomId"/> of the component to remove.</param>
         /// <returns>The current builder.</returns>
         public ModalBuilder RemoveComponent(string customId)
         {
             Preconditions.NotNull(customId, nameof(customId));
 
-            Components.ActionRows?.ForEach(r => r.Components.RemoveAll(c => c is IInteractableComponent ic && ic.CustomId == customId));
+            Components.ActionRows?.ForEach(r => r.Components.RemoveAll(c => c is IInteractableComponentBuilder ic && ic.CustomId == customId));
             return this;
         }
 
@@ -225,7 +225,7 @@ public Modal Build()
     public class ModalComponentBuilder
     {
         /// <summary>
-        ///     The max length of a <see cref="IMessageComponent.CustomId"/>.
+        ///     The max length of a <see cref="IInteractableComponent.CustomId"/>.
         /// </summary>
         public const int MaxCustomIdLength = 100;
 

From 1f73870bb8909d14688e8ea0232a9bbee18a8d81 Mon Sep 17 00:00:00 2001
From: Misha133 <mihagribkov133@gmail.com>
Date: Tue, 18 Feb 2025 23:58:56 +0300
Subject: [PATCH 13/15] webhook component supports + builder validations + some
 xmldoc

---
 .../Builders/ActionRowBuilder.cs              |  10 +-
 .../Builders/ButtonBuilder.cs                 |   6 -
 .../Builders/ComponentBuilderExtensions.cs    |  17 +++
 .../Builders/ComponentBuilderV2.cs            |  37 ++++-
 .../Builders/ComponentContainerExtensions.cs  | 126 ++++++++++++++++++
 .../Builders/ContainerBuilder.cs              |  66 ++++++---
 .../Builders/FileComponentBuilder.cs          |  45 ++++++-
 .../Builders/IComponentContainer.cs           |  26 +++-
 .../Builders/IInteractableComponentBuilder.cs |   6 +
 .../IInteractableComponentContainer.cs        |   3 +
 .../Builders/IMessageComponentBuilder.cs      |   9 ++
 .../Builders/IStaticComponentContainer.cs     |   3 +
 .../Builders/MediaGalleryBuilder.cs           |  60 ++++++++-
 .../Builders/MediaGalleryItemProperties.cs    |  20 +++
 .../Builders/SectionBuilder.cs                |  64 +++++++--
 .../Builders/SelectMenuBuilder.cs             |  12 +-
 .../Builders/SeparatorBuilder.cs              |  28 +++-
 .../Builders/TextDisplayBuilder.cs            |  35 ++++-
 .../Builders/ThumbnailBuilder.cs              |  59 ++++++--
 .../Builders/UnfurledMediaItemProperties.cs   |  10 ++
 src/Discord.Net.Rest/DiscordRestApiClient.cs  |   2 +
 .../WebhookClientHelper.cs                    |  13 +-
 22 files changed, 565 insertions(+), 92 deletions(-)
 create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderExtensions.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
index f1780f9bd9..66fb654665 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ActionRowBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ActionRowBuilder.cs
@@ -42,7 +42,7 @@ public List<IMessageComponentBuilder> Components
     }
 
 
-    public ActionRowBuilder AddComponents(params IEnumerable<IMessageComponentBuilder> components)
+    public ActionRowBuilder AddComponents(params IMessageComponentBuilder[] components)
     {
         foreach (var component in components)
             AddComponent(component);
@@ -179,12 +179,6 @@ public ActionRowBuilder WithButton(ButtonBuilder button)
         return this;
     }
 
-    public ActionRowBuilder WithId(int? id)
-    {
-        Id = id;
-        return this;
-    }
-
     /// <summary>
     ///     Builds the current builder to a <see cref="ActionRowComponent"/> that can be used within a <see cref="ComponentBuilder"/>
     /// </summary>
@@ -220,7 +214,7 @@ internal bool CanTakeComponent(IMessageComponentBuilder component)
 
     IComponentContainer IComponentContainer.AddComponent(IMessageComponentBuilder component) => AddComponent(component);
 
-    IComponentContainer IComponentContainer.AddComponents(params IEnumerable<IMessageComponentBuilder> components) => AddComponents(components);
+    IComponentContainer IComponentContainer.AddComponents(params IMessageComponentBuilder[] components) => AddComponents(components);
 
     IComponentContainer IComponentContainer.WithComponents(IEnumerable<IMessageComponentBuilder> components) => WithComponents(components);
 }
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 e16a2258e6..e6c56afe99 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ButtonBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ButtonBuilder.cs
@@ -262,12 +262,6 @@ public ButtonBuilder WithSkuId(ulong? skuId)
         return this;
     }
 
-    public ButtonBuilder WithId(int? id)
-    {
-        Id = id;
-        return this;
-    }
-
     /// <summary>
     ///     Builds this builder into a <see cref="ButtonComponent"/> to be used in a <see cref="ComponentBuilder"/>.
     /// </summary>
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderExtensions.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderExtensions.cs
new file mode 100644
index 0000000000..2c930feb5c
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderExtensions.cs
@@ -0,0 +1,17 @@
+namespace Discord;
+
+public static class ComponentBuilderExtensions
+{
+    /// <summary>
+    ///     Sets the custom id for the component.
+    /// </summary>
+    /// <returns>
+    ///     The current builder.
+    /// </returns>
+    public static BuilderT WithId<BuilderT>(this BuilderT builder, int? id)
+        where BuilderT : IMessageComponentBuilder
+    {
+        builder.Id = id;
+        return builder;
+    }
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderV2.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderV2.cs
index d961ba4fd7..d752bde326 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderV2.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderV2.cs
@@ -6,10 +6,14 @@ namespace Discord;
 
 public class ComponentBuilderV2 : IStaticComponentContainer, IInteractableComponentContainer
 {
-    public ComponentBuilderV2() {}
+    /// <summary>
+    ///    Gets the maximum number of components that can be added to this container.
+    /// </summary>
+    public const int MaxComponents = 10;
 
     private List<IMessageComponentBuilder> _components = new();
 
+    /// <inheritdoc/>
     public List<IMessageComponentBuilder> Components
     {
         get => _components;
@@ -19,31 +23,58 @@ public List<IMessageComponentBuilder> Components
         }
     }
 
+    /// <summary>
+    ///     Initializes a new instance of <see cref="ComponentBuilderV2"/>.
+    /// </summary>
+    public ComponentBuilderV2() { }
+
+    /// <inheritdoc cref="IComponentContainer.AddComponent"/>
     public ComponentBuilderV2 AddComponent(IMessageComponentBuilder component)
     {
         Components.Add(component);
         return this;
     }
 
-    public ComponentBuilderV2 AddComponents(params IEnumerable<IMessageComponentBuilder> components)
+    /// <inheritdoc cref="IComponentContainer.AddComponents"/>
+    public ComponentBuilderV2 AddComponents(params IMessageComponentBuilder[] components)
     {
         foreach (var component in components)
             Components.Add(component);
         return this;
     }
 
+    /// <inheritdoc cref="IComponentContainer.WithComponents"/>
     public ComponentBuilderV2 WithComponents(IEnumerable<IMessageComponentBuilder> components)
     {
         Components = components.ToList();
         return this;
     }
 
+    /// <inheritdoc cref="IMessageComponentBuilder.Build" />
     public MessageComponent Build()
     {
+        if (_components.Count is 0 or >MaxComponents)
+            throw new InvalidOperationException($"The number of components must be between 1 and {MaxComponents}.");
+
+        if (_components.Any(x => 
+                x is not ActionRowBuilder
+                and not SectionBuilder
+                and not TextDisplayBuilder
+                and not MediaGalleryBuilder
+                and not FileComponentBuilder
+                and not SeparatorBuilder
+                and not ContainerBuilder))
+            throw new InvalidOperationException($"Only the following components can be at the top level: {nameof(ActionRowBuilder)}, {nameof(TextDisplayBuilder)}, {nameof(SectionBuilder)}, {nameof(MediaGalleryBuilder)}, {nameof(SeparatorBuilder)}, or {nameof(FileComponentBuilder)} components.");
+
         return new MessageComponent(Components.Select(x => x.Build()).ToList());
     }
 
+    /// <inheritdoc/>
     IComponentContainer IComponentContainer.AddComponent(IMessageComponentBuilder component) => AddComponent(component);
-    IComponentContainer IComponentContainer.AddComponents(params IEnumerable<IMessageComponentBuilder> components) => AddComponents(components);
+
+    /// <inheritdoc/>
+    IComponentContainer IComponentContainer.AddComponents(params IMessageComponentBuilder[] components) => AddComponents(components);
+
+    /// <inheritdoc/>
     IComponentContainer IComponentContainer.WithComponents(IEnumerable<IMessageComponentBuilder> components) => WithComponents(components);
 }
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentContainerExtensions.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentContainerExtensions.cs
index 797d4b4ece..bddf0137f1 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentContainerExtensions.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentContainerExtensions.cs
@@ -5,6 +5,12 @@ namespace Discord;
 
 public static class ComponentContainerExtensions
 {
+    /// <summary>
+    ///     Adds a <see cref="TextDisplayBuilder"/> to the container.
+    /// </summary>
+    /// <returns>
+    ///     The current container.
+    /// </returns>
     public static BuilderT WithTextDisplay<BuilderT>(this BuilderT container, TextDisplayBuilder textDisplay)
         where BuilderT : class, IStaticComponentContainer
     {
@@ -12,6 +18,12 @@ public static BuilderT WithTextDisplay<BuilderT>(this BuilderT container, TextDi
         return container;
     }
 
+    /// <summary>
+    ///     Adds a <see cref="TextDisplayBuilder"/> to the container.
+    /// </summary>
+    /// <returns>
+    ///     The current container.
+    /// </returns>
     public static BuilderT WithTextDisplay<BuilderT>(this BuilderT container,
         string content,
         int? id = null)
@@ -20,6 +32,12 @@ public static BuilderT WithTextDisplay<BuilderT>(this BuilderT container,
             .WithContent(content)
             .WithId(id));
 
+    /// <summary>
+    ///     Adds a <see cref="SectionBuilder"/> to the container.
+    /// </summary>
+    /// <returns>
+    ///     The current container.
+    /// </returns>
     public static BuilderT WithSection<BuilderT>(this BuilderT container, SectionBuilder section)
         where BuilderT : class, IStaticComponentContainer
     {
@@ -27,6 +45,12 @@ public static BuilderT WithSection<BuilderT>(this BuilderT container, SectionBui
         return container;
     }
 
+    /// <summary>
+    ///     Adds a <see cref="SectionBuilder"/> to the container.
+    /// </summary>
+    /// <returns>
+    ///     The current container.
+    /// </returns>
     public static BuilderT WithSection<BuilderT>(this BuilderT container,
         IEnumerable<TextDisplayBuilder> components,
         IMessageComponentBuilder accessory,
@@ -38,6 +62,12 @@ public static BuilderT WithSection<BuilderT>(this BuilderT container,
             .WithAccessory(accessory)
             .WithId(id));
 
+    /// <summary>
+    ///     Adds a <see cref="MediaGalleryBuilder"/> to the container.
+    /// </summary>
+    /// <returns>
+    ///     The current container.
+    /// </returns>
     public static BuilderT WithMediaGallery<BuilderT>(this BuilderT container, MediaGalleryBuilder mediaGallery)
         where BuilderT : class, IStaticComponentContainer
     {
@@ -45,6 +75,12 @@ public static BuilderT WithMediaGallery<BuilderT>(this BuilderT container, Media
         return container;
     }
 
+    /// <summary>
+    ///     Adds a <see cref="MediaGalleryBuilder"/> to the container.
+    /// </summary>
+    /// <returns>
+    ///     The current container.
+    /// </returns>
     public static BuilderT WithMediaGallery<BuilderT>(this BuilderT container,
         IEnumerable<MediaGalleryItemProperties> items,
         int? id = null) where BuilderT : class, IStaticComponentContainer
@@ -52,6 +88,12 @@ public static BuilderT WithMediaGallery<BuilderT>(this BuilderT container,
             .WithItems(items)
             .WithId(id));
 
+    /// <summary>
+    ///     Adds a <see cref="MediaGalleryBuilder"/> to the container.
+    /// </summary>
+    /// <returns>
+    ///     The current container.
+    /// </returns>
     public static BuilderT WithMediaGallery<BuilderT>(this BuilderT container,
         IEnumerable<string> urls,
         int? id = null)
@@ -60,6 +102,12 @@ public static BuilderT WithMediaGallery<BuilderT>(this BuilderT container,
             .WithItems(urls.Select(x => new MediaGalleryItemProperties(new UnfurledMediaItemProperties(x))))
             .WithId(id));
 
+    /// <summary>
+    ///     Adds a <see cref="SeparatorBuilder"/> to the container.
+    /// </summary>
+    /// <returns>
+    ///     The current container.
+    /// </returns>
     public static BuilderT WithSeparator<BuilderT>(this BuilderT container, SeparatorBuilder separator)
         where BuilderT : class, IStaticComponentContainer
     {
@@ -67,6 +115,12 @@ public static BuilderT WithSeparator<BuilderT>(this BuilderT container, Separato
         return container;
     }
 
+    /// <summary>
+    ///     Adds a <see cref="SeparatorBuilder"/> to the container.
+    /// </summary>
+    /// <returns>
+    ///     The current container.
+    /// </returns>
     public static BuilderT WithSeparator<BuilderT>(this BuilderT container,
         SeparatorSpacingSize spacing = SeparatorSpacingSize.Small,
         bool isDivider = true,
@@ -77,6 +131,12 @@ public static BuilderT WithSeparator<BuilderT>(this BuilderT container,
             .WithIsDivider(isDivider)
             .WithId(id));
 
+    /// <summary>
+    ///     Adds a <see cref="FileComponentBuilder"/> to the container.
+    /// </summary>
+    /// <returns>
+    ///     The current container.
+    /// </returns>
     public static BuilderT WithFile<BuilderT>(this BuilderT container, FileComponentBuilder file)
         where BuilderT : class, IStaticComponentContainer
     {
@@ -84,6 +144,12 @@ public static BuilderT WithFile<BuilderT>(this BuilderT container, FileComponent
         return container;
     }
 
+    /// <summary>
+    ///     Adds a <see cref="FileComponentBuilder"/> to the container.
+    /// </summary>
+    /// <returns>
+    ///     The current container.
+    /// </returns>
     public static BuilderT WithFile<BuilderT>(this BuilderT container,
         UnfurledMediaItemProperties file,
         bool isSpoiler = false,
@@ -94,6 +160,12 @@ public static BuilderT WithFile<BuilderT>(this BuilderT container,
             .WithIsSpoiler(isSpoiler)
             .WithId(id));
 
+    /// <summary>
+    ///     Adds a <see cref="ContainerBuilder"/> to the container.
+    /// </summary>
+    /// <returns>
+    ///     The current container.
+    /// </returns>
     public static BuilderT WithContainer<BuilderT>(this BuilderT container, ContainerBuilder containerComponent)
         where BuilderT : class, IStaticComponentContainer
     {
@@ -101,6 +173,12 @@ public static BuilderT WithContainer<BuilderT>(this BuilderT container, Containe
         return container;
     }
 
+    /// <summary>
+    ///     Adds a <see cref="ContainerBuilder"/> to the container.
+    /// </summary>
+    /// <returns>
+    ///     The current container.
+    /// </returns>
     public static BuilderT WithContainer<BuilderT>(this BuilderT container,
         IEnumerable<IMessageComponentBuilder> components,
         Color? accentColor = null,
@@ -113,6 +191,24 @@ public static BuilderT WithContainer<BuilderT>(this BuilderT container,
             .WithSpoiler(isSpoiler)
             .WithId(id));
 
+    /// <summary>
+    ///     Adds a <see cref="ContainerBuilder"/> to the container.
+    /// </summary>
+    /// <returns>
+    ///     The current container.
+    /// </returns>
+    public static BuilderT WithContainer<BuilderT>(this BuilderT container,
+        params IMessageComponentBuilder[] components)
+        where BuilderT : class, IStaticComponentContainer
+        => container.WithContainer(new ContainerBuilder()
+            .WithComponents(components));
+
+    /// <summary>
+    ///     Adds a <see cref="ButtonBuilder"/> to the container.
+    /// </summary>
+    /// <returns>
+    ///     The current container.
+    /// </returns>
     public static BuilderT WithButton<BuilderT>(this BuilderT container, ButtonBuilder button)
         where BuilderT : class, IInteractableComponentContainer
     {
@@ -120,6 +216,12 @@ public static BuilderT WithButton<BuilderT>(this BuilderT container, ButtonBuild
         return container;
     }
 
+    /// <summary>
+    ///     Adds a <see cref="ButtonBuilder"/> to the container.
+    /// </summary>
+    /// <returns>
+    ///     The current container.
+    /// </returns>
     public static BuilderT WithButton<BuilderT>(this BuilderT container,
         string label = null,
         string customId = null,
@@ -140,6 +242,12 @@ public static BuilderT WithButton<BuilderT>(this BuilderT container,
             .WithSkuId(skuId)
             .WithId(id));
 
+    /// <summary>
+    ///     Adds a <see cref="SelectMenuBuilder"/> to the container.
+    /// </summary>
+    /// <returns>
+    ///     The current container.
+    /// </returns>
     public static BuilderT WithSelectMenu<BuilderT>(this BuilderT container, SelectMenuBuilder selectMenu)
         where BuilderT : class, IInteractableComponentContainer
     {
@@ -147,6 +255,12 @@ public static BuilderT WithSelectMenu<BuilderT>(this BuilderT container, SelectM
         return container;
     }
 
+    /// <summary>
+    ///     Adds a <see cref="SelectMenuBuilder"/> to the container.
+    /// </summary>
+    /// <returns>
+    ///     The current container.
+    /// </returns>
     public static BuilderT WithSelectMenu<BuilderT>(this BuilderT container,
         string customId,
         List<SelectMenuOptionBuilder> options = null,
@@ -172,6 +286,12 @@ public static BuilderT WithSelectMenu<BuilderT>(this BuilderT container,
                 .WithDefaultValues(defaultValues)
                 .WithId(id));
 
+    /// <summary>
+    ///     Adds a <see cref="ActionRowBuilder"/> to the container.
+    /// </summary>
+    /// <returns>
+    ///     The current container.
+    /// </returns>
     public static BuilderT WithActionRow<BuilderT>(this BuilderT container, ActionRowBuilder actionRow)
         where BuilderT : class, IStaticComponentContainer
     {
@@ -179,6 +299,12 @@ public static BuilderT WithActionRow<BuilderT>(this BuilderT container, ActionRo
         return container;
     }
 
+    /// <summary>
+    ///     Adds a <see cref="ActionRowBuilder"/> to the container.
+    /// </summary>
+    /// <returns>
+    ///     The current container.
+    /// </returns>
     public static BuilderT WithActionRow<BuilderT>(this BuilderT container,
         IEnumerable<IMessageComponentBuilder> components,
         int? id = null)
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ContainerBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ContainerBuilder.cs
index a7bc9efa8b..fe179868ea 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ContainerBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ContainerBuilder.cs
@@ -5,74 +5,108 @@
 
 namespace Discord;
 
-public class ContainerBuilder : IMessageComponentBuilder, IStaticComponentContainer 
+public class ContainerBuilder : IMessageComponentBuilder, IStaticComponentContainer
 {
+    /// <summary>
+    ///     The maximum number of components allowed in a container.
+    /// </summary>
+    public const int MaxComponents = 10;
+
+    /// <inheritdoc />
     public ComponentType Type => ComponentType.Container;
 
+    /// <inheritdoc />
     public int? Id { get; set; }
 
     private List<IMessageComponentBuilder> _components = new();
 
+    /// <inheritdoc/>
     public List<IMessageComponentBuilder> Components
     {
         get => _components;
         set => _components = value ?? throw new ArgumentNullException(nameof(value), $"{nameof(Components)} cannot be null.");
     }
 
+    /// <summary>
+    ///     Gets or sets the accent color of this container.
+    /// </summary>
     public uint? AccentColor { get; set; }
 
+    /// <summary>
+    ///     Gets or sets whether this container is a spoiler.
+    /// </summary>
     public bool? IsSpoiler { get; set; }
 
-    public ContainerBuilder WithId(int? id)
-    {
-        Id = id;
-        return this;
-    }
-
+    /// <summary>
+    ///     Sets the accent color of this container.
+    /// </summary>
+    /// <returns>
+    ///     The current builder.
+    /// </returns>
     public ContainerBuilder WithAccentColor(Color? color)
     {
         AccentColor = color?.RawValue;
         return this;
     }
 
+    /// <summary>
+    ///     Sets whether this container is a spoiler.
+    /// </summary>
+    /// <returns>
+    ///     The current builder.
+    /// </returns>
     public ContainerBuilder WithSpoiler(bool isSpoiler)
     {
         IsSpoiler = isSpoiler;
         return this;
     }
 
+    /// <inheritdoc cref="IComponentContainer.AddComponent"/>
     public ContainerBuilder AddComponent(IMessageComponentBuilder component)
     {
         Components.Add(component);
         return this;
     }
 
-    public ContainerBuilder AddComponents(params IEnumerable<IMessageComponentBuilder> components)
+    /// <inheritdoc cref="IComponentContainer.AddComponents"/>
+    public ContainerBuilder AddComponents(params IMessageComponentBuilder[] components)
     {
         foreach (var component in components)
             Components.Add(component);
         return this;
     }
 
+    /// <inheritdoc cref="IComponentContainer.WithComponents"/>
     public ContainerBuilder WithComponents(IEnumerable<IMessageComponentBuilder> components)
     {
         Components = components.ToList();
         return this;
     }
 
-    public ContainerBuilder WithComponents(List<IMessageComponentBuilder> components)
-    {
-        Components = components;
-        return this;
-    }
-
+    /// <inheritdoc cref="IMessageComponentBuilder.Build"/>
     public ContainerComponent Build()
     {
+        if (_components.Count is 0 or > MaxComponents)
+            throw new InvalidOperationException($"A container must have between 1 and {MaxComponents} components.");
+
+        if (_components.Any(x => x
+                is not ActionRowBuilder
+                and not TextDisplayBuilder
+                and not SectionBuilder
+                and not MediaGalleryBuilder
+                and not SeparatorBuilder
+                and not FileComponentBuilder))
+            throw new InvalidOperationException($"A container can only contain {nameof(ActionRowBuilder)}, {nameof(TextDisplayBuilder)}, {nameof(SectionBuilder)}, {nameof(MediaGalleryBuilder)}, {nameof(SeparatorBuilder)}, or {nameof(FileComponentBuilder)} components.");
+
         return new(Components.ConvertAll(x => x.Build()).ToImmutableArray(), AccentColor, IsSpoiler, Id);
     }
-
+    
+    /// <inheritdoc />
     IMessageComponent IMessageComponentBuilder.Build() => Build();
+    /// <inheritdoc />
     IComponentContainer IComponentContainer.AddComponent(IMessageComponentBuilder component) => AddComponent(component);
-    IComponentContainer IComponentContainer.AddComponents(params IEnumerable<IMessageComponentBuilder> components) => AddComponents(components);
+    /// <inheritdoc />
+    IComponentContainer IComponentContainer.AddComponents(params IMessageComponentBuilder[] components) => AddComponents(components);
+    /// <inheritdoc />
     IComponentContainer IComponentContainer.WithComponents(IEnumerable<IMessageComponentBuilder> components) => WithComponents(components);
 }
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/FileComponentBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/FileComponentBuilder.cs
index 0322423ad2..d2851b7bef 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/FileComponentBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/FileComponentBuilder.cs
@@ -1,17 +1,36 @@
+using System;
+
 namespace Discord;
 
 public class FileComponentBuilder : IMessageComponentBuilder
 {
+    /// <inheritdoc />
     public ComponentType Type => ComponentType.File;
 
+    /// <inheritdoc />
     public int? Id { get; set; }
 
+    /// <summary>
+    ///     Gets or sets the file for the component.
+    /// </summary>
+    /// <remarks>
+    ///     Only attachment URLs are supported.
+    /// </remarks>
     public UnfurledMediaItemProperties File { get; set; }
 
+    /// <summary>
+    ///     Gets or sets whether the file is a spoiler.
+    /// </summary>
     public bool? IsSpoiler { get; set; }
 
+    /// <summary>
+    ///     Initializes a new instance of the <see cref="FileComponentBuilder"/>.
+    /// </summary>
     public FileComponentBuilder() {}
 
+    /// <summary>
+    ///     Initializes a new instance of the <see cref="FileComponentBuilder"/>.
+    /// </summary>
     public FileComponentBuilder(UnfurledMediaItemProperties media, bool isSpoiler = false, int? id = null)
     {
         File = media;
@@ -19,28 +38,42 @@ public FileComponentBuilder(UnfurledMediaItemProperties media, bool isSpoiler =
         IsSpoiler = isSpoiler;
     }
 
+    /// <summary>
+    ///     Sets the file for the component.
+    /// </summary>
+    /// <returns>
+    ///     The current builder.
+    /// </returns>
     public FileComponentBuilder WithFile(UnfurledMediaItemProperties file)
     {
         File = file;
         return this;
     }
 
+    /// <summary>
+    ///     Sets whether the file is a spoiler.
+    /// </summary>
+    /// <returns>
+    ///     The current builder.
+    /// </returns>
     public FileComponentBuilder WithIsSpoiler(bool? isSpoiler)
     {
         IsSpoiler = isSpoiler;
         return this;
     }
 
-    public FileComponentBuilder WithId(int? id)
-    {
-        Id = id;
-        return this;
-    }
-
+    /// <inheritdoc cref="IMessageComponentBuilder.Build" />
     public FileComponent Build()
     {
+        if (string.IsNullOrWhiteSpace(File.Url))
+            throw new InvalidOperationException("File URL must be set.");
+
+        if (!File.Url.StartsWith("attachment://"))
+            throw new InvalidOperationException("File URL must be an attachment URL.");
+
         return new(new UnfurledMediaItem(File.Url), IsSpoiler, Id);
     }
 
+    /// <inheritdoc />
     IMessageComponent IMessageComponentBuilder.Build() => Build();
 }
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IComponentContainer.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IComponentContainer.cs
index c8f28045b0..2c2734fb7c 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IComponentContainer.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IComponentContainer.cs
@@ -2,13 +2,37 @@
 
 namespace Discord;
 
+/// <summary>
+///    Represents a container with child components.
+/// </summary>
 public interface IComponentContainer
 {
+    /// <summary>
+    ///     Gets the components in the container.
+    /// </summary>
     List<IMessageComponentBuilder> Components { get; }
 
+    /// <summary>
+    ///     Adds a component to the container.
+    /// </summary>
+    /// <returns>
+    ///     The current container.
+    /// </returns>
     IComponentContainer AddComponent(IMessageComponentBuilder component);
 
-    IComponentContainer AddComponents(params IEnumerable<IMessageComponentBuilder> components);
+    /// <summary>
+    ///    Adds components to the container.
+    /// </summary>
+    /// <returns>
+    ///     The current container.
+    /// </returns>
+    IComponentContainer AddComponents(params IMessageComponentBuilder[] components);
 
+    /// <summary>
+    ///     Sets the components in the container.
+    /// </summary>
+    /// <returns>
+    ///     The current container.
+    /// </returns>
     IComponentContainer WithComponents(IEnumerable<IMessageComponentBuilder> components);
 }
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IInteractableComponentBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IInteractableComponentBuilder.cs
index 703d9950b7..242060fcdd 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IInteractableComponentBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IInteractableComponentBuilder.cs
@@ -1,6 +1,12 @@
 namespace Discord;
 
+/// <summary>
+///     Represents a builder for an interactable component.
+/// </summary>
 public interface IInteractableComponentBuilder : IMessageComponentBuilder
 {
+    /// <summary>
+    ///     Gets or sets the custom id for the component.
+    /// </summary>
     string CustomId { get; set; }
 }
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IInteractableComponentContainer.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IInteractableComponentContainer.cs
index 69c310a120..c86a189e79 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IInteractableComponentContainer.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IInteractableComponentContainer.cs
@@ -1,5 +1,8 @@
 namespace Discord;
 
+/// <summary>
+///     Represents a container for interactable components.
+/// </summary>
 public interface IInteractableComponentContainer : IComponentContainer
 {
     
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IMessageComponentBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IMessageComponentBuilder.cs
index cb5ab6e511..88305308c1 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IMessageComponentBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IMessageComponentBuilder.cs
@@ -2,9 +2,18 @@ namespace Discord;
 
 public interface IMessageComponentBuilder
 {
+    /// <summary>
+    ///     Gets the type of the component.
+    /// </summary>
     ComponentType Type { get; }
 
+    /// <summary>
+    ///     Gets or sets the id for the component. An autoincremented id will be assigned if not set.
+    /// </summary>
     int? Id { get; set;  }
 
+    /// <summary>
+    ///     Runs validation checks and builds the component.
+    /// </summary>
     IMessageComponent Build();
 }
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IStaticComponentContainer.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IStaticComponentContainer.cs
index aed2af1b54..829b77bd09 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IStaticComponentContainer.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/IStaticComponentContainer.cs
@@ -1,5 +1,8 @@
 namespace Discord;
 
+/// <summary>
+///     Represents a container for static components.
+/// </summary>
 public interface IStaticComponentContainer : IComponentContainer
 {
     
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 509cbe1387..b6121acc35 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/MediaGalleryBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/MediaGalleryBuilder.cs
@@ -1,3 +1,4 @@
+using System;
 using System.Collections.Generic;
 using System.Collections.Immutable;
 using System.Linq;
@@ -6,38 +7,72 @@ namespace Discord;
 
 public class MediaGalleryBuilder : IMessageComponentBuilder
 {
+    /// <summary>
+    ///     Gets the maximum number of items that can be added to a media gallery.
+    /// </summary>
+    public const int MaxItems = 10;
+
+    /// <inheritdoc/>
     public ComponentType Type => ComponentType.MediaGallery;
 
+    /// <inheritdoc/>
     public int? Id { get; set; }
 
     private List<MediaGalleryItemProperties> _items = new();
 
+    /// <summary>
+    ///     Initializes a new instance of the <see cref="MediaGalleryBuilder"/>.
+    /// </summary>
     public MediaGalleryBuilder() { }
 
+    /// <summary>
+    ///    Initializes a new instance of the <see cref="MediaGalleryBuilder"/>.
+    /// </summary>
     public MediaGalleryBuilder(IEnumerable<MediaGalleryItemProperties> items, int? id = null)
     {
         Items = items.ToList();
         Id = id;
     }
 
+    /// <summary>
+    ///     Gets or sets the items in this media gallery.
+    /// </summary>
     public List<MediaGalleryItemProperties> Items
     {
         get => _items;
         set => _items = value;
     }
 
+    /// <summary>
+    ///     Adds a new item to the media gallery.
+    /// </summary>
+    /// <returns>
+    ///     The current builder.
+    /// </returns>
     public MediaGalleryBuilder AddItem(MediaGalleryItemProperties item)
     {
         _items.Add(item);
         return this;
     }
 
+    /// <summary>
+    ///     Adds a new item to the media gallery.
+    /// </summary>
+    /// <returns>
+    ///     The current builder.
+    /// </returns>
     public MediaGalleryBuilder AddItem(string url, string description = null, bool isSpoiler = false)
     {
         _items.Add(new MediaGalleryItemProperties(new UnfurledMediaItemProperties(url), description, isSpoiler));
         return this;
     }
 
+    /// <summary>
+    ///     Adds a list of items to the media gallery.
+    /// </summary>
+    /// <returns>
+    ///     The current builder.
+    /// </returns>
     public MediaGalleryBuilder AddItems(params IEnumerable<MediaGalleryItemProperties> items)
     {
         foreach (var item in items)
@@ -45,22 +80,35 @@ public MediaGalleryBuilder AddItems(params IEnumerable<MediaGalleryItemPropertie
         return this;
     }
 
+    /// <summary>
+    ///     Sets the items in the media gallery.
+    /// </summary>
+    /// <returns>
+    ///     The current builder.
+    /// </returns>
     public MediaGalleryBuilder WithItems(IEnumerable<MediaGalleryItemProperties> items)
     {
         _items = items.ToList();
         return this;
     }
 
-    public MediaGalleryBuilder WithId(int? id)
-    {
-        Id = id;
-        return this;
-    }
-
+    /// <inheritdoc cref="IMessageComponentBuilder.Build"/>
     public MediaGalleryComponent Build()
     {
+        if (_items.Any(x => (x.Description?.Length ?? 0) > MediaGalleryItemProperties.MaxDescriptionLength))
+            throw new ArgumentException($"{nameof(MediaGalleryItemProperties)} description length cannot exceed {MediaGalleryItemProperties.MaxDescriptionLength} characters.");
+
+        if (_items.Any(x => !(x.Media.Url?.StartsWith("http://") ?? false)
+                            && !(x.Media.Url?.StartsWith("https://") ?? false)
+                            && !(x.Media.Url?.StartsWith("attachment://") ?? false)))
+            throw new ArgumentException($"{nameof(MediaGalleryItemProperties)} description must be a valid URL or attachment.");
+
+        if (_items.Count is 0 or > MaxItems)
+            throw new ArgumentOutOfRangeException(nameof(Items), $"Media gallery items count must be in range [1, {MaxItems}]");
+
         return new(_items.Select(x => new MediaGalleryItem(new UnfurledMediaItem(x.Media.Url), x.Description, x.IsSpoiler)).ToImmutableArray(), Id);
     }
 
+    /// <inheritdoc/>
     IMessageComponent IMessageComponentBuilder.Build() => Build();
 }
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/MediaGalleryItemProperties.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/MediaGalleryItemProperties.cs
index 9bbc8e02c4..32d7d97aa8 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/MediaGalleryItemProperties.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/MediaGalleryItemProperties.cs
@@ -2,14 +2,34 @@ namespace Discord;
 
 public struct MediaGalleryItemProperties
 {
+    /// <summary>
+    ///     The maximum length of the description.
+    /// </summary>
+    public const int MaxDescriptionLength = 256;
+
+    /// <summary>
+    ///     Gets or sets the media item to display.
+    /// </summary>
     public UnfurledMediaItemProperties Media { get; set; }
 
+    /// <summary>
+    ///     Gets or sets the description of the media item.
+    /// </summary>
     public string Description { get; set; }
 
+    /// <summary>
+    ///     Gets or sets whether the media item is a spoiler.
+    /// </summary>
     public bool IsSpoiler { get; set; }
 
+    /// <summary>
+    ///     Initializes a new instance of the <see cref="MediaGalleryItemProperties"/>.
+    /// </summary>
     public MediaGalleryItemProperties() { }
 
+    /// <summary>
+    ///     Initializes a new instance of the <see cref="MediaGalleryItemProperties"/>.
+    /// </summary>
     public MediaGalleryItemProperties(UnfurledMediaItemProperties media, string description = null, bool isSpoiler = false)
     {
         Media = media;
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SectionBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SectionBuilder.cs
index 43251fbfda..6c9727f974 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SectionBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SectionBuilder.cs
@@ -7,57 +7,101 @@ namespace Discord;
 
 public class SectionBuilder : IMessageComponentBuilder, IStaticComponentContainer
 {
+    /// <summary>
+    ///     Gets the maximum number of components allowed in this container.
+    /// </summary>
+    public const int MaxComponents = 3;
+
+    /// <inheritdoc/>
     public ComponentType Type => ComponentType.Section;
 
+    /// <inheritdoc/>
     public int? Id { get; set; }
 
+    /// <summary>
+    ///     Gets or sets the accessory component.
+    /// </summary>
+    /// <remarks>
+    ///     Only supports <see cref="ButtonBuilder"/> and <see cref="ThumbnailBuilder"/> currently.
+    /// </remarks>
+    public IMessageComponentBuilder Accessory { get; set; }
+
     private List<IMessageComponentBuilder> _components = new();
+
+    /// <inheritdoc/>
+    /// <remarks>
+    ///     Only <see cref="TextDisplayBuilder"/> is supported.
+    /// </remarks>
     public List<IMessageComponentBuilder> Components
     {
         get => _components;
         set => _components = value ?? throw new ArgumentNullException(nameof(value), $"{nameof(Components)} cannot be null.");
     }
 
+    /// <inheritdoc cref="IComponentContainer.AddComponent"/>
+    /// <remarks>
+    ///     Only <see cref="TextDisplayBuilder"/> is supported.
+    /// </remarks>
     public SectionBuilder AddComponent(IMessageComponentBuilder component)
     {
         Components.Add(component);
         return this;
     }
 
-    public SectionBuilder AddComponents(params IEnumerable<IMessageComponentBuilder> components)
+    /// <inheritdoc cref="IComponentContainer.AddComponents"/>
+    /// <remarks>
+    ///     Only <see cref="TextDisplayBuilder"/> is supported.
+    /// </remarks>
+    public SectionBuilder AddComponents(params IMessageComponentBuilder[] components)
     {
         foreach (var component in components)
             AddComponent(component);
         return this;
     }
 
+    /// <inheritdoc cref="IComponentContainer.WithComponents"/>
+    /// <remarks>
+    ///     Only <see cref="TextDisplayBuilder"/> is supported.
+    /// </remarks>
     public SectionBuilder WithComponents(IEnumerable<IMessageComponentBuilder> components)
     {
         Components = components.ToList();
         return this;
     }
 
-    public IMessageComponentBuilder Accessory { get; set; }
-
-    public SectionBuilder WithId(int? id)
-    {
-        Id = id;
-        return this;
-    }
-
+    /// <summary>
+    ///     Sets the accessory component.
+    /// </summary>
     public SectionBuilder WithAccessory(IMessageComponentBuilder accessory)
     {
         Accessory = accessory;
         return this;
     }
 
+    /// <inheritdoc cref="IMessageComponentBuilder.Build"/>
     public SectionComponent Build()
     {
+        if (_components.Count is 0 or > MaxComponents)
+            throw new InvalidOperationException($"Section component can only contain {MaxComponents} child components!");
+
+        if (_components.Any(x => x is not TextDisplayBuilder))
+            throw new InvalidOperationException($"Section component can only contain {nameof(TextDisplayBuilder)}!");
+
+        if (Accessory is null)
+            throw new ArgumentNullException(nameof(Accessory), "A section must have an accessory");
+
+        if (Accessory is not ButtonBuilder and not ThumbnailBuilder)
+            throw new InvalidOperationException($"Accessory component can only be {nameof(ButtonBuilder)} or {nameof(ThumbnailBuilder)}!");
+
         return new(Id, Components.Select(x => x.Build()).ToImmutableArray(), Accessory?.Build());
     }
 
+    /// <inheritdoc/>
     IMessageComponent IMessageComponentBuilder.Build() => Build();
+    /// <inheritdoc/>
     IComponentContainer IComponentContainer.AddComponent(IMessageComponentBuilder component) => AddComponent(component);
-    IComponentContainer IComponentContainer.AddComponents(params IEnumerable<IMessageComponentBuilder> components) => AddComponents(components);
+    /// <inheritdoc/>
+    IComponentContainer IComponentContainer.AddComponents(params IMessageComponentBuilder[] components) => AddComponents(components);
+    /// <inheritdoc/>
     IComponentContainer IComponentContainer.WithComponents(IEnumerable<IMessageComponentBuilder> components) => WithComponents(components.ToList());
 }
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SelectMenuBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SelectMenuBuilder.cs
index c68e8623a9..81f1def8a1 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SelectMenuBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SelectMenuBuilder.cs
@@ -136,15 +136,16 @@ public List<SelectMenuDefaultValue> DefaultValues
         }
     }
 
+    /// <inheritdoc/>
     public int? Id { get; set; }
 
-    private List<SelectMenuOptionBuilder> _options = new List<SelectMenuOptionBuilder>();
+    private List<SelectMenuOptionBuilder> _options = [];
     private int _minValues = 1;
     private int _maxValues = 1;
     private string _placeholder;
     private string _customId;
     private ComponentType _type = ComponentType.SelectMenu;
-    private List<SelectMenuDefaultValue> _defaultValues = new();
+    private List<SelectMenuDefaultValue> _defaultValues = [];
 
     /// <summary>
     ///     Creates a new instance of a <see cref="SelectMenuBuilder"/>.
@@ -397,12 +398,6 @@ public SelectMenuBuilder WithChannelTypes(params ChannelType[] channelTypes)
         return this;
     }
 
-    public SelectMenuBuilder WithId(int? id)
-    {
-        Id = id;
-        return this;
-    }
-
     /// <summary>
     ///     Builds a <see cref="SelectMenuComponent"/>
     /// </summary>
@@ -414,5 +409,6 @@ public SelectMenuComponent Build()
         return new SelectMenuComponent(CustomId, options, Placeholder, MinValues, MaxValues, IsDisabled, Type, Id, ChannelTypes, DefaultValues);
     }
 
+    /// <inheritdoc/>
     IMessageComponent IMessageComponentBuilder.Build() => Build();
 }
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SeparatorBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SeparatorBuilder.cs
index 085d18a960..d308eddc60 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SeparatorBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/SeparatorBuilder.cs
@@ -2,36 +2,52 @@ namespace Discord;
 
 public class SeparatorBuilder : IMessageComponentBuilder
 {
+    /// <inheritdoc/>
     public ComponentType Type => ComponentType.Separator;
 
+    /// <summary>
+    ///     Gets or sets whether the component is a divider.
+    /// </summary>
     public bool? IsDivider { get; set; }
 
+    /// <summary>
+    ///     Gets or sets the spacing of the separator.
+    /// </summary>
     public SeparatorSpacingSize? Spacing { get; set; }
 
+    /// <inheritdoc/>
     public int? Id { get; set; }
 
+    /// <summary>
+    ///     Sets whether the component is a divider.
+    /// </summary>
+    /// <returns>
+    ///     The current builder.
+    /// </returns>
     public SeparatorBuilder WithIsDivider(bool? isDivider)
     {
         IsDivider = isDivider;
         return this;
     }
 
+    /// <summary>
+    ///     Sets the spacing of the separator.
+    /// </summary>
+    /// <returns>
+    ///     The current builder.
+    /// </returns>
     public SeparatorBuilder WithSpacing(SeparatorSpacingSize? spacing)
     {
         Spacing = spacing;
         return this;
     }
 
-    public SeparatorBuilder WithId(int? id)
-    {
-        Id = id;
-        return this;
-    }
-
+    /// <inheritdoc cref="IMessageComponentBuilder.Build"/>
     public SeparatorComponent Build()
     {
         return new(IsDivider, Spacing, Id);
     }
 
+    /// <inheritdoc/>
     IMessageComponent IMessageComponentBuilder.Build() => Build();
 }
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextDisplayBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextDisplayBuilder.cs
index 484e85d94b..eb2234eb42 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextDisplayBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/TextDisplayBuilder.cs
@@ -1,37 +1,60 @@
+using System;
+
 namespace Discord;
 
 public class TextDisplayBuilder : IMessageComponentBuilder
 {
+    /// <summary>
+    ///     The maximum length of the content.
+    /// </summary>
+    public const int MaxContentLength = 4096;
+
+    /// <inheritdoc/>
     public ComponentType Type => ComponentType.ActionRow;
 
+    /// <inheritdoc/>
     public int? Id { get; set; }
 
+    /// <summary>
+    ///     Gets or sets the content of the text display.
+    /// </summary>
     public string Content { get; set; }
 
+    /// <summary>
+    ///     Initializes a new <see cref="TextDisplayBuilder"/>.
+    /// </summary>
     public TextDisplayBuilder() { }
 
+    /// <summary>
+    ///     Initializes a new <see cref="TextDisplayBuilder"/> with the specified content.
+    /// </summary>
     public TextDisplayBuilder(string content, int? id = null)
     {
         Content = content;
         Id = id;
     }
 
+    /// <summary>
+    ///     Sets the content of the text display.
+    /// </summary>
+    /// <returns>
+    ///     The current builder.
+    /// </returns>
     public TextDisplayBuilder WithContent(string content)
     {
         Content = content;
         return this;
     }
 
-    public TextDisplayBuilder WithId(int? id)
-    {
-        Id = id;
-        return this;
-    }
-
+    /// <inheritdoc cref="IMessageComponentBuilder.Build"/>
     public TextDisplayComponent Build()
     {
+        if (Content.Length > MaxContentLength)
+            throw new ArgumentException($"Content length must be less than or equal to {MaxContentLength}.", nameof(Content));
+
         return new(Content, Id);
     }
 
+    /// <inheritdoc/>
     IMessageComponent IMessageComponentBuilder.Build() => Build();
 }
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ThumbnailBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ThumbnailBuilder.cs
index b4da4bcfc4..79a5fa05fe 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ThumbnailBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ThumbnailBuilder.cs
@@ -1,19 +1,43 @@
+using System;
+
 namespace Discord;
 
 public class ThumbnailBuilder : IMessageComponentBuilder
 {
+    /// <summary>
+    ///     Gets the maximum length of the description.
+    /// </summary>
+    public const int MaxDescriptionLength = 1024;
+
+    /// <inheritdoc/>
     public ComponentType Type => ComponentType.Thumbnail;
 
+    /// <inheritdoc/>
     public int? Id { get; set; }
 
+    /// <summary>
+    ///     Gets or sets the media of the thumbnail.
+    /// </summary>
     public UnfurledMediaItemProperties Media { get; set; }
 
+    /// <summary>
+    ///     Gets or sets the description of the thumbnail.
+    /// </summary>
     public string Description { get; set; }
 
+    /// <summary>
+    ///     Gets or sets whether the thumbnail is a spoiler.
+    /// </summary>
     public bool IsSpoiler { get; set; }
 
+    /// <summary>
+    ///     Initializes a new instance of the <see cref="ThumbnailBuilder"/>.
+    /// </summary>
     public ThumbnailBuilder() { }
 
+    /// <summary>
+    ///     Initializes a new instance of the <see cref="ThumbnailBuilder"/>.
+    /// </summary>
     public ThumbnailBuilder(UnfurledMediaItemProperties media, string description = null, bool isSpoiler = false)
     {
         Media = media;
@@ -21,40 +45,51 @@ public ThumbnailBuilder(UnfurledMediaItemProperties media, string description =
         IsSpoiler = isSpoiler;
     }
 
+    /// <summary>
+    ///     Sets the media of the thumbnail.
+    /// </summary>
+    /// <returns>
+    ///     The current builder.
+    /// </returns>
     public ThumbnailBuilder WithMedia(UnfurledMediaItemProperties media)
     {
         Media = media;
         return this;
     }
 
-    public ThumbnailBuilder WithMedia(string url)
-    {
-        Media = new UnfurledMediaItemProperties(url);
-        return this;
-    }
-
+    /// <summary>
+    ///     Sets the description of the thumbnail.
+    /// </summary>
+    /// <returns>
+    ///     The current builder.
+    /// </returns>
     public ThumbnailBuilder WithDescription(string description)
     {
         Description = description;
         return this;
     }
 
-    public ThumbnailBuilder WithId(int id)
-    {
-        Id = id;
-        return this;
-    }
-
+    /// <summary>
+    ///     Sets whether the thumbnail is a spoiler.
+    /// </summary>
+    /// <returns>
+    ///     The current builder.
+    /// </returns>
     public ThumbnailBuilder WithSpoiler(bool isSpoiler)
     {
         IsSpoiler = isSpoiler;
         return this;
     }
 
+    /// <inheritdoc cref="IMessageComponentBuilder.Build"/>
     public ThumbnailComponent Build()
     {
+        if (Description is not null && Description.Length > MaxDescriptionLength)
+            throw new ArgumentException($"Description length must be less than or equal to {MaxDescriptionLength}.", nameof(Description));
+
         return new(Id, new UnfurledMediaItem(Media.Url), Description, IsSpoiler);
     }
 
+    /// <inheritdoc/>
     IMessageComponent IMessageComponentBuilder.Build() => Build();
 }
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/UnfurledMediaItemProperties.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/UnfurledMediaItemProperties.cs
index c9d2b85d30..1bb2f85eb0 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/UnfurledMediaItemProperties.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/UnfurledMediaItemProperties.cs
@@ -2,9 +2,19 @@ namespace Discord;
 
 public struct UnfurledMediaItemProperties
 {
+    /// <summary>
+    ///     Gets or sets the URL of the media item.
+    /// </summary>
     public string Url { get; set; }
 
+    /// <summary>
+    ///     Initializes a new instance of the <see cref="UnfurledMediaItemProperties"/>.
+    /// </summary>
     public UnfurledMediaItemProperties() {}
+
+    /// <summary>
+    ///     Initializes a new instance of the <see cref="UnfurledMediaItemProperties"/>.
+    /// </summary>
     public UnfurledMediaItemProperties(string url)
     {
         Url = url;
diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs
index 91d533a596..765b4cd2ce 100644
--- a/src/Discord.Net.Rest/DiscordRestApiClient.cs
+++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs
@@ -2779,6 +2779,8 @@ private static string WebhookQuery(bool wait = false, ulong? threadId = null)
             if (threadId.HasValue)
                 querys.Add($"thread_id={threadId}");
 
+            querys.Add("with_components=true");
+
             return $"{string.Join("&", querys)}";
         }
 
diff --git a/src/Discord.Net.Webhook/WebhookClientHelper.cs b/src/Discord.Net.Webhook/WebhookClientHelper.cs
index d6b62f8f2b..b8861de93c 100644
--- a/src/Discord.Net.Webhook/WebhookClientHelper.cs
+++ b/src/Discord.Net.Webhook/WebhookClientHelper.cs
@@ -29,6 +29,11 @@ public static async Task<ulong> SendMessageAsync(DiscordWebhookClient client,
             MessageFlags flags, ulong? threadId = null, string threadName = null, ulong[] appliedTags = null,
             PollProperties poll = null)
         {
+            if (components?.Components.Any(x => x.Type is not ComponentType.ActionRow) ?? false)
+                flags |= MessageFlags.ComponentsV2;
+
+            Preconditions.ValidateMessageFlags(flags);
+
             var args = new CreateWebhookMessageParams
             {
                 Content = text,
@@ -56,8 +61,6 @@ public static async Task<ulong> SendMessageAsync(DiscordWebhookClient client,
             if (poll != null)
                 args.Poll = poll.ToModel();
 
-            if (flags is not MessageFlags.None and not MessageFlags.SuppressEmbeds and not MessageFlags.SuppressNotification)
-                throw new ArgumentException("The only valid MessageFlags are SuppressEmbeds, SuppressNotification and none.", nameof(flags));
 
             var model = await client.ApiClient.CreateWebhookMessageAsync(client.Webhook.Id, args, options: options, threadId: threadId).ConfigureAwait(false);
             return model.Id;
@@ -193,8 +196,10 @@ public static async Task<ulong> SendFilesAsync(DiscordWebhookClient client,
                 }
             }
 
-            if (flags is not MessageFlags.None and not MessageFlags.SuppressEmbeds and not MessageFlags.SuppressNotification)
-                throw new ArgumentException("The only valid MessageFlags are SuppressEmbeds, SuppressNotification and none.", nameof(flags));
+            if (components?.Components.Any(x => x.Type is not ComponentType.ActionRow) ?? false)
+                flags |= MessageFlags.ComponentsV2;
+
+            Preconditions.ValidateMessageFlags(flags);
 
             var args = new UploadWebhookFileParams(attachments.ToArray())
             {

From 2d665980cbb6b695940838b3278f392c74396803 Mon Sep 17 00:00:00 2001
From: Misha133 <mihagribkov133@gmail.com>
Date: Wed, 19 Feb 2025 00:27:14 +0300
Subject: [PATCH 14/15] `ResolvedUnfurledMediaItem` + fix missing accessory on
 sections

---
 .../ResolvedUnfurledMediaItem.cs              | 23 ++++++++++++++++++
 .../MessageComponents/UnfurledMediaItem.cs    |  2 +-
 .../UnfurledMediaItemLoadingState.cs          | 24 +++++++++++++++++++
 .../API/Common/ThumbnailComponent.cs          |  3 ++-
 .../API/Common/UnfurledMediaItem.cs           | 17 ++++++++++++-
 .../Extensions/MessageComponentExtension.cs   | 17 ++++++++++---
 6 files changed, 80 insertions(+), 6 deletions(-)
 create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/ResolvedUnfurledMediaItem.cs
 create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/UnfurledMediaItemLoadingState.cs

diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ResolvedUnfurledMediaItem.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ResolvedUnfurledMediaItem.cs
new file mode 100644
index 0000000000..f75e8bdcd8
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ResolvedUnfurledMediaItem.cs
@@ -0,0 +1,23 @@
+namespace Discord;
+
+public class ResolvedUnfurledMediaItem : UnfurledMediaItem
+{
+    public string ProxyUrl { get; }
+
+    public int Height { get; }
+
+    public int Width { get; }
+
+    public string ContentType { get;}
+
+    public UnfurledMediaItemLoadingState LoadingState { get; }
+
+    internal ResolvedUnfurledMediaItem(string url, string proxyUrl, int height, int width, string contentType, UnfurledMediaItemLoadingState loadingState) : base(url)
+    {
+        ProxyUrl = proxyUrl;
+        Height = height;
+        Width = width;
+        ContentType = contentType;
+        LoadingState = loadingState;
+    }
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/UnfurledMediaItem.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/UnfurledMediaItem.cs
index d9cb7b9592..97bf2becd6 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/UnfurledMediaItem.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/UnfurledMediaItem.cs
@@ -1,6 +1,6 @@
 namespace Discord;
 
-public readonly struct UnfurledMediaItem
+public class UnfurledMediaItem
 {
     public string Url { get; }
 
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/UnfurledMediaItemLoadingState.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/UnfurledMediaItemLoadingState.cs
new file mode 100644
index 0000000000..f6df157c45
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/UnfurledMediaItemLoadingState.cs
@@ -0,0 +1,24 @@
+namespace Discord;
+
+public enum UnfurledMediaItemLoadingState
+{
+    /// <summary>
+    ///     The state of the media item is unknown.
+    /// </summary>
+    Unknown = 0,
+
+    /// <summary>
+    ///     The media item is currently loading.
+    /// </summary>
+    Loading = 1,
+
+    /// <summary>
+    ///     The media item was successfully loaded.
+    /// </summary>
+    LoadingSuccess = 2,
+
+    /// <summary>
+    ///     The media item was not found.
+    /// </summary>
+    LoadingNotFound = 3
+}
diff --git a/src/Discord.Net.Rest/API/Common/ThumbnailComponent.cs b/src/Discord.Net.Rest/API/Common/ThumbnailComponent.cs
index 8ee7314fcb..bc8b40dc38 100644
--- a/src/Discord.Net.Rest/API/Common/ThumbnailComponent.cs
+++ b/src/Discord.Net.Rest/API/Common/ThumbnailComponent.cs
@@ -1,3 +1,4 @@
+using Discord.Rest;
 using Newtonsoft.Json;
 
 namespace Discord.API;
@@ -25,7 +26,7 @@ public ThumbnailComponent(Discord.ThumbnailComponent component)
     {
         Type = component.Type;
         Id = component.Id ?? Optional<int>.Unspecified;
-        Media = new UnfurledMediaItem { Url = component.Media.Url };
+        Media = component.Media.ToModel();
         Description = component.Description;
         IsSpoiler = component.IsSpoiler;
     }
diff --git a/src/Discord.Net.Rest/API/Common/UnfurledMediaItem.cs b/src/Discord.Net.Rest/API/Common/UnfurledMediaItem.cs
index 60635c41a7..558d8d4141 100644
--- a/src/Discord.Net.Rest/API/Common/UnfurledMediaItem.cs
+++ b/src/Discord.Net.Rest/API/Common/UnfurledMediaItem.cs
@@ -2,8 +2,23 @@
 
 namespace Discord.API;
 
-public class UnfurledMediaItem
+internal class UnfurledMediaItem
 {
     [JsonProperty("url")]
     public string Url { get; set; }
+
+    [JsonProperty("proxy_url")]
+    public Optional<string> ProxyUrl { get; set; }
+
+    [JsonProperty("height")]
+    public Optional<int> Height { get; set; }
+
+    [JsonProperty("width")]
+    public Optional<int> Width { get; set; }
+
+    [JsonProperty("content_type")]
+    public Optional<string> ContentType { get; set; }
+
+    [JsonProperty("loading_state")]
+    public Optional<UnfurledMediaItemLoadingState> LoadingState { get; set; }
 }
diff --git a/src/Discord.Net.Rest/Extensions/MessageComponentExtension.cs b/src/Discord.Net.Rest/Extensions/MessageComponentExtension.cs
index 8918c9bffc..881b1f6c00 100644
--- a/src/Discord.Net.Rest/Extensions/MessageComponentExtension.cs
+++ b/src/Discord.Net.Rest/Extensions/MessageComponentExtension.cs
@@ -129,13 +129,18 @@ internal static IMessageComponent ToEntity(this IMessageComponent component)
             case ComponentType.Section:
             {
                 var parsed = (API.SectionComponent)component;
-                return new SectionComponent(parsed.Id.ToNullable(), parsed.Components.Select(x => x.ToEntity()).ToImmutableArray(), parsed.Accessory.ToModel());
+                return new SectionComponent(parsed.Id.ToNullable(),
+                    parsed.Components.Select(x => x.ToEntity()).ToImmutableArray(),
+                    parsed.Accessory.ToEntity());
             }
 
             case ComponentType.Thumbnail:
             {
                 var parsed = (API.ThumbnailComponent)component;
-                return new ThumbnailComponent(parsed.Id.ToNullable(), parsed.Media.ToEntity(), parsed.Description.GetValueOrDefault(null), parsed.IsSpoiler.ToNullable());
+                return new ThumbnailComponent(parsed.Id.ToNullable(),
+                    parsed.Media.ToEntity(),
+                    parsed.Description.GetValueOrDefault(null),
+                    parsed.IsSpoiler.ToNullable());
             }
 
             case ComponentType.MediaGallery:
@@ -175,8 +180,14 @@ internal static IMessageComponent ToEntity(this IMessageComponent component)
 
     internal static UnfurledMediaItem ToEntity(this API.UnfurledMediaItem mediaItem)
     {
-        return new UnfurledMediaItem(mediaItem.Url);
+        return new ResolvedUnfurledMediaItem(mediaItem.Url,
+            mediaItem.ProxyUrl.Value,
+            mediaItem.Height.Value,
+            mediaItem.Width.Value,
+            mediaItem.ContentType.Value,
+            mediaItem.LoadingState.Value);
     }
+
     internal static API.UnfurledMediaItem ToModel(this UnfurledMediaItem mediaItem)
     {
         return new API.UnfurledMediaItem

From c67aa77f2541e5aec2a0fd0129be1447db3a0b7e Mon Sep 17 00:00:00 2001
From: Misha133 <mihagribkov133@gmail.com>
Date: Sun, 9 Mar 2025 21:02:17 +0300
Subject: [PATCH 15/15] missing xmldoc

---
 .../Builders/ComponentBuilderV2.cs             |  2 +-
 .../MessageComponents/ContainerComponent.cs    | 14 ++++++++++++++
 .../MessageComponents/FileComponent.cs         | 11 +++++++++++
 .../IInteractableComponent.cs                  |  3 +++
 .../MessageComponents/MediaGalleryComponent.cs |  8 ++++++++
 .../MessageComponents/MediaGalleryItem.cs      | 12 ++++++++++++
 .../ResolvedUnfurledMediaItem.cs               | 18 ++++++++++++++++++
 .../MessageComponents/SectionComponent.cs      | 11 +++++++++++
 .../MessageComponents/SeparatorComponent.cs    | 11 +++++++++++
 .../MessageComponents/SeparatorSpacingSize.cs  |  9 +++++++++
 .../MessageComponents/TextDisplayComponent.cs  |  8 ++++++++
 .../MessageComponents/TextInputComponent.cs    |  2 --
 .../MessageComponents/TextInputStyle.cs        |  1 +
 .../MessageComponents/ThumbnailComponent.cs    | 14 ++++++++++++++
 .../MessageComponents/UnfurledMediaItem.cs     |  6 ++++++
 .../Entities/Messages/FileAttachment.cs        | 11 +++++++++++
 .../Entities/Messages/SocketMessage.cs         |  2 +-
 17 files changed, 139 insertions(+), 4 deletions(-)

diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderV2.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderV2.cs
index d752bde326..8c119a2810 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderV2.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentBuilderV2.cs
@@ -4,7 +4,7 @@
 
 namespace Discord;
 
-public class ComponentBuilderV2 : IStaticComponentContainer, IInteractableComponentContainer
+public class ComponentBuilderV2 : IStaticComponentContainer
 {
     /// <summary>
     ///    Gets the maximum number of components that can be added to this container.
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ContainerComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ContainerComponent.cs
index f434a0e021..79a40b153a 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ContainerComponent.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ContainerComponent.cs
@@ -2,16 +2,30 @@
 
 namespace Discord;
 
+/// <summary>
+///     Represents a container component.
+/// </summary>
 public class ContainerComponent : IMessageComponent
 {
+    /// <inheritdoc/>
     public ComponentType Type => ComponentType.Container;
 
+    /// <inheritdoc/>
     public int? Id { get; }
 
+    /// <summary>
+    ///     Gets the components in this container.
+    /// </summary>
     public IReadOnlyCollection<IMessageComponent> Components { get; }
 
+    /// <summary>
+    ///     Gets the accent color of this container.
+    /// </summary>
     public uint? AccentColor { get; }
 
+    /// <summary>
+    ///     Gets whether this container is a spoiler.
+    /// </summary>
     public bool? IsSpoiler { get; }
 
     internal ContainerComponent(IReadOnlyCollection<IMessageComponent> components, uint? accentColor, bool? isSpoiler, int? id = null)
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/FileComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/FileComponent.cs
index c1e6303105..91a3c75f8c 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/FileComponent.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/FileComponent.cs
@@ -1,13 +1,24 @@
 namespace Discord;
 
+/// <summary>
+///     Represents a file component.
+/// </summary>
 public class FileComponent : IMessageComponent
 {
+    /// <inheritdoc/>
     public ComponentType Type => ComponentType.File;
 
+    /// <inheritdoc/>
     public int? Id { get; }
 
+    /// <summary>
+    ///     Gets the file of this component.
+    /// </summary>
     public UnfurledMediaItem File { get; }
 
+    /// <summary>
+    ///      Gets whether this file is a spoiler.
+    /// </summary>
     public bool? IsSpoiler { get; }
 
     internal FileComponent(UnfurledMediaItem file, bool? isSpoiler, int? id = null)
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/IInteractableComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/IInteractableComponent.cs
index 61c2265889..013004908b 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/IInteractableComponent.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/IInteractableComponent.cs
@@ -1,5 +1,8 @@
 namespace Discord;
 
+/// <summary>
+///     Represents a message component that can be interacted with.
+/// </summary>
 public interface IInteractableComponent : IMessageComponent
 {
     /// <summary>
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/MediaGalleryComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/MediaGalleryComponent.cs
index d08ecaa545..ce3c398917 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/MediaGalleryComponent.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/MediaGalleryComponent.cs
@@ -2,12 +2,20 @@
 
 namespace Discord;
 
+/// <summary>
+///     Represents a media gallery component.
+/// </summary>
 public class MediaGalleryComponent : IMessageComponent
 {
+    /// <inheritdoc/>
     public ComponentType Type => ComponentType.MediaGallery;
 
+    /// <inheritdoc/>
     public int? Id { get; }
 
+    /// <summary>
+    ///     Gets the items in this media gallery.
+    /// </summary>
     public IReadOnlyCollection<MediaGalleryItem> Items { get; }
 
     internal MediaGalleryComponent(IReadOnlyCollection<MediaGalleryItem> items, int? id)
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/MediaGalleryItem.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/MediaGalleryItem.cs
index db1b4c0d17..da33f7c91c 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/MediaGalleryItem.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/MediaGalleryItem.cs
@@ -1,11 +1,23 @@
 namespace Discord;
 
+/// <summary>
+///     Represents a media gallery item.
+/// </summary>
 public readonly struct MediaGalleryItem
 {
+    /// <summary>
+    ///     Gets the media for this item.
+    /// </summary>
     public UnfurledMediaItem Media { get; }
 
+    /// <summary>
+    ///     Gets the description for this item.
+    /// </summary>
     public string Description { get; }
 
+    /// <summary>
+    ///     Gets whether this item is a spoiler.
+    /// </summary>
     public bool IsSpoiler { get; }
 
     internal MediaGalleryItem(UnfurledMediaItem media, string description, bool? isSpoiler)
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ResolvedUnfurledMediaItem.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ResolvedUnfurledMediaItem.cs
index f75e8bdcd8..d7cfd1b754 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ResolvedUnfurledMediaItem.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ResolvedUnfurledMediaItem.cs
@@ -1,15 +1,33 @@
 namespace Discord;
 
+/// <summary>
+///     Represents a media item that has been unfurled and resolved.
+/// </summary>
 public class ResolvedUnfurledMediaItem : UnfurledMediaItem
 {
+    /// <summary>
+    ///     Gets the proxy URL for this media item.
+    /// </summary>
     public string ProxyUrl { get; }
 
+    /// <summary>
+    ///     Gets the height of this media item.
+    /// </summary>
     public int Height { get; }
 
+    /// <summary>
+    ///     Gets the width of this media item.
+    /// </summary>
     public int Width { get; }
 
+    /// <summary>
+    ///     Gets the content type of this media item.
+    /// </summary>
     public string ContentType { get;}
 
+    /// <summary>
+    ///     Gets the loading state of this media item.
+    /// </summary>
     public UnfurledMediaItemLoadingState LoadingState { get; }
 
     internal ResolvedUnfurledMediaItem(string url, string proxyUrl, int height, int width, string contentType, UnfurledMediaItemLoadingState loadingState) : base(url)
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SectionComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SectionComponent.cs
index 90a7e12df7..52d71e3419 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SectionComponent.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SectionComponent.cs
@@ -2,14 +2,25 @@
 
 namespace Discord;
 
+/// <summary>
+///     Represents a section component.
+/// </summary>
 public class SectionComponent : IMessageComponent
 {
+    /// <inheritdoc/>
     public ComponentType Type => ComponentType.Section;
 
+    /// <inheritdoc/>
     public int? Id { get; }
 
+    /// <summary>
+    ///     Gets the components in this section.
+    /// </summary>
     public IReadOnlyCollection<IMessageComponent> Components { get; }
 
+    /// <summary>
+    ///     Gets the accessory of this section.
+    /// </summary>
     public IMessageComponent Accessory { get; }
 
     internal SectionComponent(int? id, IReadOnlyCollection<IMessageComponent> components, IMessageComponent accessory)
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SeparatorComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SeparatorComponent.cs
index b722683a3a..00c2e7cb9a 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SeparatorComponent.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SeparatorComponent.cs
@@ -1,13 +1,24 @@
 namespace Discord;
 
+/// <summary>
+///     Represents a separator component.
+/// </summary>
 public class SeparatorComponent : IMessageComponent
 {
+    /// <inheritdoc/>
     public ComponentType Type => ComponentType.Separator;
 
+    /// <inheritdoc/>
     public int? Id { get; }
 
+    /// <summary>
+    ///     Gets whether this component is a divider.
+    /// </summary>
     public bool? IsDivider { get; }
 
+    /// <summary>
+    ///     Gets the spacing of this component.
+    /// </summary>
     public SeparatorSpacingSize? Spacing { get; }
 
     internal SeparatorComponent(bool? isDivider, SeparatorSpacingSize? spacing, int? id = null)
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SeparatorSpacingSize.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SeparatorSpacingSize.cs
index 405b08c58d..0336eb747a 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SeparatorSpacingSize.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SeparatorSpacingSize.cs
@@ -1,8 +1,17 @@
 namespace Discord;
 
+/// <summary>
+///     Represents the spacing of a separator component.
+/// </summary>
 public enum SeparatorSpacingSize
 {
+    /// <summary>
+    ///     The separator has a small spacing.
+    /// </summary>
     Small = 1,
 
+    /// <summary>
+    ///     The separator has a large spacing.
+    /// </summary>
     Large = 2
 }
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/TextDisplayComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/TextDisplayComponent.cs
index e96bac84e9..e2aec7e858 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/TextDisplayComponent.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/TextDisplayComponent.cs
@@ -1,11 +1,19 @@
 namespace Discord;
 
+/// <summary>
+///     Represents a text display component.
+/// </summary>
 public class TextDisplayComponent : IMessageComponent
 {
+    /// <inheritdoc/>
     public ComponentType Type => ComponentType.TextDisplay;
 
+    /// <inheritdoc/>
     public int? Id { get; }
 
+    /// <summary>
+    ///     Gets the content of this component.
+    /// </summary>
     public string Content { get; }
 
     internal TextDisplayComponent(string content, int? id = null)
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/TextInputComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/TextInputComponent.cs
index c462d0c45c..d76014bc07 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/TextInputComponent.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/TextInputComponent.cs
@@ -1,5 +1,3 @@
-using Newtonsoft.Json;
-
 namespace Discord
 {
     /// <summary>
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/TextInputStyle.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/TextInputStyle.cs
index 9bbcf687f9..292f26b4fb 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/TextInputStyle.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/TextInputStyle.cs
@@ -6,6 +6,7 @@ public enum TextInputStyle
         ///     Intended for short, single-line text.
         /// </summary>
         Short = 1,
+
         /// <summary>
         ///     Intended for longer or multiline text.
         /// </summary>
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ThumbnailComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ThumbnailComponent.cs
index fbbaf34ecd..b42233923b 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ThumbnailComponent.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ThumbnailComponent.cs
@@ -1,15 +1,29 @@
 namespace Discord;
 
+/// <summary>
+///     Represents a thumbnail component.
+/// </summary>
 public class ThumbnailComponent : IMessageComponent
 {
+    /// <inheritdoc/>
     public ComponentType Type => ComponentType.Thumbnail;
 
+    /// <inheritdoc/>
     public int? Id { get; }
 
+    /// <summary>
+    ///     Gets the media of the component.
+    /// </summary>
     public UnfurledMediaItem Media { get; }
 
+    /// <summary>
+    ///     Gets the description of the component.
+    /// </summary>
     public string Description { get; }
 
+    /// <summary>
+    ///     Gets whether the component is a spoiler.
+    /// </summary>
     public bool IsSpoiler { get; }
 
     internal ThumbnailComponent(int? id, UnfurledMediaItem media, string description, bool? isSpoiler)
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/UnfurledMediaItem.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/UnfurledMediaItem.cs
index 97bf2becd6..52177794ed 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/UnfurledMediaItem.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/UnfurledMediaItem.cs
@@ -1,7 +1,13 @@
 namespace Discord;
 
+/// <summary>
+///     Represents a media item that has been unfurled.
+/// </summary>
 public class UnfurledMediaItem
 {
+    /// <summary>
+    ///    Gets the URL of this media item.
+    /// </summary>
     public string Url { get; }
 
     internal UnfurledMediaItem(string url)
diff --git a/src/Discord.Net.Core/Entities/Messages/FileAttachment.cs b/src/Discord.Net.Core/Entities/Messages/FileAttachment.cs
index 57bb8ba262..c43b3f3e8a 100644
--- a/src/Discord.Net.Core/Entities/Messages/FileAttachment.cs
+++ b/src/Discord.Net.Core/Entities/Messages/FileAttachment.cs
@@ -123,5 +123,16 @@ public void Dispose()
                 _isDisposed = true;
             }
         }
+
+        /// <summary>
+        ///    Gets the url formatted with <c>attachment://</c> protocol.
+        /// </summary>
+        /// <returns>
+        ///     The formatted url.
+        /// </returns>
+        public string GetAttachmentUrl()
+        {
+            return $"attachment://{FileName}";
+        }
     }
 }
diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs
index 882050789a..4cba1d73d8 100644
--- a/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs
+++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs
@@ -67,7 +67,7 @@ public abstract class SocketMessage : SocketEntity<ulong>, IMessage
         /// <inheritdoc />
         public MessageReference Reference { get; private set; }
 
-        /// <inheritdoc/>
+        /// <inheritdoc cref="IMessage.Components"/>
         public IReadOnlyCollection<IMessageComponent> Components { get; private set; }
 
         /// <summary>